@side-quest/kit 0.0.0 → 0.2.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.
Files changed (127) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +54 -352
  3. package/dist/cli.d.ts +14 -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/index.d.ts +8 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +8 -2509
  10. package/dist/index.js.map +1 -0
  11. package/dist/lib/ast/index.d.ts +11 -0
  12. package/dist/lib/ast/index.d.ts.map +1 -0
  13. package/dist/lib/ast/index.js +15 -0
  14. package/dist/lib/ast/index.js.map +1 -0
  15. package/dist/lib/ast/languages.d.ts +55 -0
  16. package/dist/lib/ast/languages.d.ts.map +1 -0
  17. package/dist/lib/ast/languages.js +146 -0
  18. package/dist/lib/ast/languages.js.map +1 -0
  19. package/dist/lib/ast/pattern.d.ts +84 -0
  20. package/dist/lib/ast/pattern.d.ts.map +1 -0
  21. package/dist/lib/ast/pattern.js +268 -0
  22. package/dist/lib/ast/pattern.js.map +1 -0
  23. package/dist/lib/ast/searcher.d.ts +89 -0
  24. package/dist/lib/ast/searcher.d.ts.map +1 -0
  25. package/dist/lib/ast/searcher.js +316 -0
  26. package/dist/lib/ast/searcher.js.map +1 -0
  27. package/dist/lib/ast/types.d.ts +93 -0
  28. package/dist/lib/ast/types.d.ts.map +1 -0
  29. package/dist/lib/ast/types.js +23 -0
  30. package/dist/lib/ast/types.js.map +1 -0
  31. package/dist/lib/commands/callers.d.ts +20 -0
  32. package/dist/lib/commands/callers.d.ts.map +1 -0
  33. package/dist/lib/commands/callers.js +162 -0
  34. package/dist/lib/commands/callers.js.map +1 -0
  35. package/dist/lib/commands/find.d.ts +15 -0
  36. package/dist/lib/commands/find.d.ts.map +1 -0
  37. package/dist/lib/commands/find.js +113 -0
  38. package/dist/lib/commands/find.js.map +1 -0
  39. package/dist/lib/commands/overview.d.ts +6 -0
  40. package/dist/lib/commands/overview.d.ts.map +1 -0
  41. package/dist/lib/commands/overview.js +52 -0
  42. package/dist/lib/commands/overview.js.map +1 -0
  43. package/dist/lib/commands/prime.d.ts +16 -0
  44. package/dist/lib/commands/prime.d.ts.map +1 -0
  45. package/dist/lib/commands/prime.js +168 -0
  46. package/dist/lib/commands/prime.js.map +1 -0
  47. package/dist/lib/commands/search.d.ts +20 -0
  48. package/dist/lib/commands/search.d.ts.map +1 -0
  49. package/dist/lib/commands/search.js +111 -0
  50. package/dist/lib/commands/search.js.map +1 -0
  51. package/dist/lib/errors.d.ts +80 -0
  52. package/dist/lib/errors.d.ts.map +1 -0
  53. package/dist/lib/errors.js +189 -0
  54. package/dist/lib/errors.js.map +1 -0
  55. package/dist/lib/formatters/output.d.ts +5 -0
  56. package/dist/lib/formatters/output.d.ts.map +1 -0
  57. package/dist/lib/formatters/output.js +5 -0
  58. package/dist/lib/formatters/output.js.map +1 -0
  59. package/dist/lib/formatters.d.ts +29 -0
  60. package/dist/lib/formatters.d.ts.map +1 -0
  61. package/dist/lib/formatters.js +141 -0
  62. package/dist/lib/formatters.js.map +1 -0
  63. package/dist/lib/index-tools.d.ts +108 -0
  64. package/dist/lib/index-tools.d.ts.map +1 -0
  65. package/dist/lib/index-tools.js +311 -0
  66. package/dist/lib/index-tools.js.map +1 -0
  67. package/dist/lib/index.d.ts +21 -0
  68. package/dist/lib/index.d.ts.map +1 -0
  69. package/dist/lib/index.js +42 -0
  70. package/dist/lib/index.js.map +1 -0
  71. package/dist/lib/kit-wrapper.d.ts +70 -0
  72. package/dist/lib/kit-wrapper.d.ts.map +1 -0
  73. package/dist/lib/kit-wrapper.js +462 -0
  74. package/dist/lib/kit-wrapper.js.map +1 -0
  75. package/dist/lib/logger.d.ts +28 -0
  76. package/dist/lib/logger.d.ts.map +1 -0
  77. package/dist/lib/logger.js +39 -0
  78. package/dist/lib/logger.js.map +1 -0
  79. package/dist/lib/types.d.ts +179 -0
  80. package/dist/lib/types.d.ts.map +1 -0
  81. package/dist/lib/types.js +48 -0
  82. package/dist/lib/types.js.map +1 -0
  83. package/dist/lib/utils/args.d.ts +40 -0
  84. package/dist/lib/utils/args.d.ts.map +1 -0
  85. package/dist/lib/utils/args.js +58 -0
  86. package/dist/lib/utils/args.js.map +1 -0
  87. package/dist/lib/utils/git.d.ts +23 -0
  88. package/dist/lib/utils/git.d.ts.map +1 -0
  89. package/dist/lib/utils/git.js +50 -0
  90. package/dist/lib/utils/git.js.map +1 -0
  91. package/dist/lib/utils/index-parser.d.ts +155 -0
  92. package/dist/lib/utils/index-parser.d.ts.map +1 -0
  93. package/dist/lib/utils/index-parser.js +252 -0
  94. package/dist/lib/utils/index-parser.js.map +1 -0
  95. package/dist/lib/validators.d.ts +138 -0
  96. package/dist/lib/validators.d.ts.map +1 -0
  97. package/dist/lib/validators.js +302 -0
  98. package/dist/lib/validators.js.map +1 -0
  99. package/dist/mcp/index.d.ts +19 -0
  100. package/dist/mcp/index.d.ts.map +1 -0
  101. package/dist/mcp/index.js +769 -0
  102. package/dist/mcp/index.js.map +1 -0
  103. package/package.json +5 -2
  104. package/src/cli.ts +170 -0
  105. package/src/lib/ast/index.ts +32 -0
  106. package/src/lib/ast/languages.ts +172 -0
  107. package/src/lib/ast/pattern.ts +299 -0
  108. package/src/lib/ast/searcher.ts +381 -0
  109. package/src/lib/ast/types.ts +99 -0
  110. package/src/lib/commands/callers.ts +226 -0
  111. package/src/lib/commands/find.ts +159 -0
  112. package/src/lib/commands/overview.ts +73 -0
  113. package/src/lib/commands/prime.ts +271 -0
  114. package/src/lib/commands/search.ts +146 -0
  115. package/src/lib/errors.ts +221 -0
  116. package/src/lib/formatters/output.ts +9 -0
  117. package/src/lib/formatters.ts +189 -0
  118. package/src/lib/index-tools.ts +471 -0
  119. package/src/lib/index.ts +122 -0
  120. package/src/lib/kit-wrapper.ts +675 -0
  121. package/src/lib/logger.ts +57 -0
  122. package/src/lib/types.ts +228 -0
  123. package/src/lib/utils/args.ts +72 -0
  124. package/src/lib/utils/git.ts +65 -0
  125. package/src/lib/utils/index-parser.ts +350 -0
  126. package/src/lib/validators.ts +437 -0
  127. package/src/mcp/index.ts +144 -79
package/src/mcp/index.ts CHANGED
@@ -31,10 +31,13 @@ import {
31
31
  executeIndexFind,
32
32
  executeIndexOverview,
33
33
  executeIndexPrime,
34
+ executeKitGrep,
35
+ executeKitSemantic,
34
36
  executeKitUsages,
35
37
  formatIndexFindResults,
36
38
  formatIndexOverviewResults,
37
39
  formatIndexPrimeResults,
40
+ formatSemanticResults,
38
41
  ResponseFormat,
39
42
  SearchMode,
40
43
  validateAstSearchInputs,
@@ -42,6 +45,7 @@ import {
42
45
  validateSemanticInputs,
43
46
  validateUsagesInputs,
44
47
  } from '../lib/index.js'
48
+ import { findGitRootSync } from '../lib/utils/git.js'
45
49
 
46
50
  // ============================================================================
47
51
  // Logger Adapter
@@ -64,18 +68,6 @@ function createLoggerAdapter(subsystem: string) {
64
68
  }
65
69
  }
66
70
 
67
- // ============================================================================
68
- // Helper: get git root for Kit CLI path arg
69
- // ============================================================================
70
-
71
- function getGitRoot(): string | undefined {
72
- const result = spawnSyncCollect(['git', 'rev-parse', '--show-toplevel'])
73
- if (result.exitCode === 0 && result.stdout.trim()) {
74
- return result.stdout.trim()
75
- }
76
- return undefined
77
- }
78
-
79
71
  // ============================================================================
80
72
  // 1. kit_prime - Generate/refresh PROJECT_INDEX.json
81
73
  // ============================================================================
@@ -294,34 +286,92 @@ Requires Kit CLI: uv tool install cased-kit`,
294
286
  }
295
287
 
296
288
  if (mode === 'callers_only') {
297
- // Validate symbol for callers mode (simple check - non-empty, no shell chars)
289
+ // Validate symbol for callers mode (allowlist valid identifiers)
298
290
  const trimmed = symbol.trim()
299
291
  if (!trimmed) {
300
292
  throw new Error('symbol is required and cannot be empty')
301
293
  }
302
- if (/[;&|`$()]/.test(trimmed)) {
303
- throw new Error('symbol contains forbidden characters')
294
+ if (!/^[a-zA-Z_$][a-zA-Z0-9_$.<>#]*$/.test(trimmed)) {
295
+ throw new Error(
296
+ 'symbol must be a valid identifier (letters, numbers, _, $, ., <, >, #)',
297
+ )
304
298
  }
305
299
 
306
- // Use CLI callers command -- finds call sites only
307
- const formatStr = format === ResponseFormat.JSON ? 'json' : 'markdown'
308
- const result = spawnSyncCollect(
309
- [
310
- 'bun',
311
- 'run',
312
- `${__dirname}/../cli.ts`,
313
- 'callers',
314
- symbol,
315
- '--format',
316
- formatStr,
317
- ],
318
- { env: { PATH: buildEnhancedPath() } },
319
- )
300
+ // Call library function directly instead of spawning CLI
301
+ // Uses kit grep to find all occurrences, then filters to call sites only
302
+ const grepResult = executeKitGrep({
303
+ pattern: trimmed,
304
+ path,
305
+ caseSensitive: true,
306
+ maxResults: 500,
307
+ })
308
+
309
+ // Handle error result
310
+ if ('error' in grepResult) {
311
+ throw new Error(
312
+ `${grepResult.error}${grepResult.hint ? `\nHint: ${grepResult.hint}` : ''}`,
313
+ )
314
+ }
315
+
316
+ // Filter out definition patterns to show only call sites
317
+ // Heuristics: exclude lines that look like function declarations or assignments
318
+ const definitionPatterns = [
319
+ /^function\s+/, // function declarations
320
+ /^export\s+(async\s+)?function\s+/, // exported functions
321
+ /^(const|let|var)\s+\w+\s*=\s*function/, // function expressions
322
+ /^(const|let|var)\s+\w+\s*=\s*\(/, // arrow functions
323
+ /^(const|let|var)\s+\w+\s*=\s*async\s*\(/, // async arrow functions
324
+ /^async\s+function\s+/, // async function declarations
325
+ ]
326
+
327
+ const callSites = grepResult.matches.filter((match) => {
328
+ const content = match.content.trim()
329
+ return !definitionPatterns.some((pattern) => pattern.test(content))
330
+ })
331
+
332
+ // Build callers result in same format as CLI command
333
+ const callersResult = {
334
+ functionName: trimmed,
335
+ callSites: callSites.map((m) => ({
336
+ file: m.file,
337
+ line: m.line || 0,
338
+ context: m.content,
339
+ })),
340
+ count: callSites.length,
341
+ }
342
+
343
+ // Format output
344
+ if (format === ResponseFormat.JSON) {
345
+ return JSON.stringify(callersResult, null, 2)
346
+ }
320
347
 
321
- if (result.exitCode !== 0) {
322
- throw new Error(result.stderr || 'Failed to find callers')
348
+ // Markdown format
349
+ let markdown = `## Call Sites\n\n`
350
+ markdown += `**Function:** \`${trimmed}\`\n`
351
+ markdown += `**Call sites found:** ${callersResult.count}\n\n`
352
+
353
+ if (callersResult.count === 0) {
354
+ markdown += '_No call sites found_\n'
355
+ } else {
356
+ // Group by file
357
+ const byFile = new Map<string, typeof callSites>()
358
+ for (const site of callSites) {
359
+ if (!byFile.has(site.file)) {
360
+ byFile.set(site.file, [])
361
+ }
362
+ byFile.get(site.file)?.push(site)
363
+ }
364
+
365
+ for (const [file, sites] of byFile.entries()) {
366
+ markdown += `### ${file}\n\n`
367
+ for (const site of sites) {
368
+ markdown += `- Line ${site.line || '?'}: \`${site.content.trim()}\`\n`
369
+ }
370
+ markdown += '\n'
371
+ }
323
372
  }
324
- return result.stdout
373
+
374
+ return markdown
325
375
  }
326
376
 
327
377
  // Validate inputs for usages modes
@@ -455,37 +505,28 @@ To enable: uv tool install 'cased-kit[ml]'`,
455
505
  throw new Error(validation.errors.join('; '))
456
506
  }
457
507
 
458
- const formatStr = format === ResponseFormat.JSON ? 'json' : 'markdown'
459
-
460
- const cmd = [
461
- 'run',
462
- `${__dirname}/../cli.ts`,
463
- 'search',
464
- validation.validated!.query,
465
- '--format',
466
- formatStr,
467
- ]
468
-
469
- if (validation.validated!.path) {
470
- cmd.push('--path', validation.validated!.path)
471
- }
472
- cmd.push('--top-k', String(validation.validated!.topK))
473
- if (chunk_by) {
474
- cmd.push('--chunk-by', chunk_by)
475
- }
476
- if (build_index) {
477
- cmd.push('--build-index')
478
- }
479
-
480
- const result = spawnSyncCollect(['bun', ...cmd], {
481
- env: { PATH: buildEnhancedPath() },
508
+ // Call library function directly instead of spawning CLI
509
+ const result = executeKitSemantic({
510
+ query: validation.validated!.query,
511
+ path: validation.validated!.path,
512
+ topK: validation.validated!.topK,
513
+ chunkBy: chunk_by,
514
+ buildIndex: build_index,
482
515
  })
483
516
 
484
- if (result.exitCode !== 0) {
485
- throw new Error(result.stderr || 'Semantic search failed')
517
+ // Handle error result
518
+ if ('error' in result) {
519
+ throw new Error(
520
+ `${result.error}${result.hint ? `\nHint: ${result.hint}` : ''}`,
521
+ )
486
522
  }
487
523
 
488
- return result.stdout
524
+ // Format result using existing formatter
525
+ const responseFormat =
526
+ format === ResponseFormat.JSON
527
+ ? ResponseFormat.JSON
528
+ : ResponseFormat.MARKDOWN
529
+ return formatSemanticResults(result, responseFormat)
489
530
  },
490
531
  {
491
532
  toolName: 'kit_semantic',
@@ -676,12 +717,23 @@ Requires Kit CLI v3.0+: uv tool install cased-kit`,
676
717
  path?: string
677
718
  }
678
719
 
679
- // Validate file_path - no traversal, non-empty
720
+ // Validate file_path - no traversal, non-empty, no null bytes, relative only
680
721
  const fileTrimmed = file_path.trim()
681
- if (!fileTrimmed || fileTrimmed.includes('..')) {
682
- throw new Error(
683
- 'Invalid file_path: cannot be empty or contain path traversal (..)',
684
- )
722
+ if (!fileTrimmed) {
723
+ throw new Error('file_path is required')
724
+ }
725
+ // Reject null bytes
726
+ if (fileTrimmed.includes('\x00')) {
727
+ throw new Error('file_path contains invalid characters')
728
+ }
729
+ // Reject absolute paths
730
+ if (fileTrimmed.startsWith('/') || fileTrimmed.startsWith('\\')) {
731
+ throw new Error('file_path must be a relative path')
732
+ }
733
+ // Normalize and check for directory traversal
734
+ const normalized = fileTrimmed.replace(/\\/g, '/')
735
+ if (normalized.includes('..')) {
736
+ throw new Error('file_path must not contain directory traversal')
685
737
  }
686
738
  if (line < 1) {
687
739
  throw new Error('line must be a positive integer')
@@ -695,10 +747,10 @@ Requires Kit CLI v3.0+: uv tool install cased-kit`,
695
747
  }
696
748
  }
697
749
 
698
- const repoPath = path || getGitRoot() || process.cwd()
750
+ const repoPath = path || findGitRootSync() || process.cwd()
699
751
 
700
752
  const result = spawnSyncCollect(
701
- ['kit', 'context', repoPath, file_path, String(line)],
753
+ ['kit', 'context', repoPath, '--', file_path, String(line)],
702
754
  {
703
755
  env: { PATH: buildEnhancedPath() },
704
756
  },
@@ -817,12 +869,23 @@ Requires Kit CLI v3.0+: uv tool install cased-kit`,
817
869
  path?: string
818
870
  }
819
871
 
820
- // Validate file_path - no traversal, non-empty
872
+ // Validate file_path - no traversal, non-empty, no null bytes, relative only
821
873
  const fileTrimmed = file_path.trim()
822
- if (!fileTrimmed || fileTrimmed.includes('..')) {
823
- throw new Error(
824
- 'Invalid file_path: cannot be empty or contain path traversal (..)',
825
- )
874
+ if (!fileTrimmed) {
875
+ throw new Error('file_path is required')
876
+ }
877
+ // Reject null bytes
878
+ if (fileTrimmed.includes('\x00')) {
879
+ throw new Error('file_path contains invalid characters')
880
+ }
881
+ // Reject absolute paths
882
+ if (fileTrimmed.startsWith('/') || fileTrimmed.startsWith('\\')) {
883
+ throw new Error('file_path must be a relative path')
884
+ }
885
+ // Normalize and check for directory traversal
886
+ const normalized = fileTrimmed.replace(/\\/g, '/')
887
+ if (normalized.includes('..')) {
888
+ throw new Error('file_path must not contain directory traversal')
826
889
  }
827
890
 
828
891
  // Validate max_lines bounds (1-500)
@@ -838,15 +901,17 @@ Requires Kit CLI v3.0+: uv tool install cased-kit`,
838
901
  }
839
902
  }
840
903
 
841
- const repoPath = path || getGitRoot() || process.cwd()
904
+ const repoPath = path || findGitRootSync() || process.cwd()
842
905
 
843
- const cmd =
844
- strategy === 'symbols'
845
- ? ['kit', 'chunk-symbols', repoPath, file_path]
846
- : ['kit', 'chunk-lines', repoPath, file_path]
847
-
848
- if (strategy === 'lines' && max_lines) {
849
- cmd.push('-n', String(max_lines))
906
+ let cmd: string[]
907
+ if (strategy === 'symbols') {
908
+ cmd = ['kit', 'chunk-symbols', repoPath, '--', file_path]
909
+ } else {
910
+ cmd = ['kit', 'chunk-lines', repoPath]
911
+ if (max_lines) {
912
+ cmd.push('-n', String(max_lines))
913
+ }
914
+ cmd.push('--', file_path)
850
915
  }
851
916
 
852
917
  const result = spawnSyncCollect(cmd, {