project-graph-mcp 1.3.0 → 1.5.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/AGENT_ROLE.md +87 -30
- package/AGENT_ROLE_MINIMAL.md +23 -8
- package/README.md +100 -14
- package/package.json +1 -1
- package/src/.project-graph-cache.json +1 -0
- package/src/ai-context.js +113 -0
- package/src/analysis-cache.js +155 -0
- package/src/cli-handlers.js +131 -0
- package/src/cli.js +14 -2
- package/src/compact.js +207 -0
- package/src/complexity.js +21 -7
- package/src/compress.js +319 -0
- package/src/ctx-to-jsdoc.js +514 -0
- package/src/custom-rules.js +1 -0
- package/src/doc-dialect.js +716 -0
- package/src/full-analysis.js +307 -11
- package/src/instructions.js +3 -105
- package/src/jsdoc-checker.js +351 -0
- package/src/jsdoc-generator.js +0 -11
- package/src/large-files.js +1 -0
- package/src/mcp-server.js +208 -1
- package/src/mode-config.js +127 -0
- package/src/outdated-patterns.js +1 -0
- package/src/parser.js +223 -13
- package/src/similar-functions.js +1 -0
- package/src/test-annotations.js +203 -181
- package/src/tool-defs.js +270 -2
- package/src/tools.js +1 -1
- package/src/type-checker.js +188 -0
- package/src/undocumented.js +11 -12
- package/src/workspace.js +1 -1
- package/vendor/terser.mjs +49 -0
package/src/tool-defs.js
CHANGED
|
@@ -98,7 +98,7 @@ export const TOOLS = [
|
|
|
98
98
|
// Test Checklist Tools
|
|
99
99
|
{
|
|
100
100
|
name: 'get_pending_tests',
|
|
101
|
-
description: 'Get list of pending
|
|
101
|
+
description: 'Get list of pending tests from ## Tests sections in .ctx.md files.',
|
|
102
102
|
inputSchema: {
|
|
103
103
|
type: 'object',
|
|
104
104
|
properties: {
|
|
@@ -269,7 +269,7 @@ export const TOOLS = [
|
|
|
269
269
|
level: {
|
|
270
270
|
type: 'string',
|
|
271
271
|
enum: ['tests', 'params', 'all'],
|
|
272
|
-
description: 'Strictness: tests (default) =
|
|
272
|
+
description: 'Strictness: tests (default) = .ctx.md checklists, params = +@param/@returns, all = +description',
|
|
273
273
|
},
|
|
274
274
|
},
|
|
275
275
|
required: ['path'],
|
|
@@ -522,4 +522,272 @@ export const TOOLS = [
|
|
|
522
522
|
required: ['path'],
|
|
523
523
|
},
|
|
524
524
|
},
|
|
525
|
+
|
|
526
|
+
// AI Context Tools
|
|
527
|
+
{
|
|
528
|
+
name: 'get_compressed_file',
|
|
529
|
+
description: 'Get AI-optimized compressed version of a JS source file. Terser-minified with export legend. Saves 20-55% tokens (more with heavy JSDoc).',
|
|
530
|
+
inputSchema: {
|
|
531
|
+
type: 'object',
|
|
532
|
+
properties: {
|
|
533
|
+
path: {
|
|
534
|
+
type: 'string',
|
|
535
|
+
description: 'Path to JS/MJS file',
|
|
536
|
+
},
|
|
537
|
+
beautify: {
|
|
538
|
+
type: 'boolean',
|
|
539
|
+
description: 'Readable multi-line output (default: true). Set false for maximum compression.',
|
|
540
|
+
},
|
|
541
|
+
legend: {
|
|
542
|
+
type: 'boolean',
|
|
543
|
+
description: 'Include compact export legend header (default: true)',
|
|
544
|
+
},
|
|
545
|
+
},
|
|
546
|
+
required: ['path'],
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
name: 'get_project_docs',
|
|
551
|
+
description: 'Get compact project documentation in doc-dialect format. Returns architecture, patterns, edge cases. Merges auto-generated docs with manual .context/ files.',
|
|
552
|
+
inputSchema: {
|
|
553
|
+
type: 'object',
|
|
554
|
+
properties: {
|
|
555
|
+
path: {
|
|
556
|
+
type: 'string',
|
|
557
|
+
description: 'Project root path',
|
|
558
|
+
},
|
|
559
|
+
file: {
|
|
560
|
+
type: 'string',
|
|
561
|
+
description: 'Optional: get docs for a specific file only',
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
required: ['path'],
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
name: 'generate_context_docs',
|
|
569
|
+
description: 'Generate .context/ doc-dialect files from AST. Creates rich templates with {DESCRIBE} markers. Use agent-pool with doc-enricher skill to auto-fill descriptions. Templates include function signatures, call graphs, and DB access patterns extracted from AST.',
|
|
570
|
+
inputSchema: {
|
|
571
|
+
type: 'object',
|
|
572
|
+
properties: {
|
|
573
|
+
path: {
|
|
574
|
+
type: 'string',
|
|
575
|
+
description: 'Project root path',
|
|
576
|
+
},
|
|
577
|
+
overwrite: {
|
|
578
|
+
type: 'boolean',
|
|
579
|
+
description: 'Overwrite existing .ctx files (default: false). Existing descriptions are preserved via merge.',
|
|
580
|
+
},
|
|
581
|
+
scope: {
|
|
582
|
+
description: 'Scope filter: "all" (default), "focus" (git diff — recently changed files only), or array of specific file paths.',
|
|
583
|
+
oneOf: [
|
|
584
|
+
{ type: 'string', enum: ['all', 'focus'] },
|
|
585
|
+
{ type: 'array', items: { type: 'string' } },
|
|
586
|
+
],
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
required: ['path'],
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
name: 'check_stale_docs',
|
|
594
|
+
description: 'Check which .ctx documentation files are outdated. Compares AST signature hashes to detect structural changes (new functions, renamed exports). Use for CI/CD doc health audits.',
|
|
595
|
+
inputSchema: {
|
|
596
|
+
type: 'object',
|
|
597
|
+
properties: {
|
|
598
|
+
path: {
|
|
599
|
+
type: 'string',
|
|
600
|
+
description: 'Project root path',
|
|
601
|
+
},
|
|
602
|
+
},
|
|
603
|
+
required: ['path'],
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
name: 'get_ai_context',
|
|
608
|
+
description: 'Boot AI agent context: skeleton + doc-dialect + optional compressed files in one call. Call FIRST when starting work on a new project. Returns totalTokens and savings vs reading raw source.',
|
|
609
|
+
inputSchema: {
|
|
610
|
+
type: 'object',
|
|
611
|
+
properties: {
|
|
612
|
+
path: {
|
|
613
|
+
type: 'string',
|
|
614
|
+
description: 'Project root path',
|
|
615
|
+
},
|
|
616
|
+
includeFiles: {
|
|
617
|
+
type: 'array',
|
|
618
|
+
items: { type: 'string' },
|
|
619
|
+
description: 'Specific files to include compressed (e.g., ["parser.js", "tools.js"])',
|
|
620
|
+
},
|
|
621
|
+
includeDocs: {
|
|
622
|
+
type: 'boolean',
|
|
623
|
+
description: 'Include doc-dialect documentation (default: true)',
|
|
624
|
+
},
|
|
625
|
+
includeSkeleton: {
|
|
626
|
+
type: 'boolean',
|
|
627
|
+
description: 'Include project skeleton (default: true)',
|
|
628
|
+
},
|
|
629
|
+
},
|
|
630
|
+
required: ['path'],
|
|
631
|
+
},
|
|
632
|
+
},
|
|
633
|
+
|
|
634
|
+
// JSDoc Consistency
|
|
635
|
+
{
|
|
636
|
+
name: 'check_jsdoc_consistency',
|
|
637
|
+
description: 'Validate JSDoc annotations against actual function signatures. Finds param count/name mismatches, missing @returns, type inconsistencies.',
|
|
638
|
+
inputSchema: {
|
|
639
|
+
type: 'object',
|
|
640
|
+
properties: {
|
|
641
|
+
path: {
|
|
642
|
+
type: 'string',
|
|
643
|
+
description: 'Path to scan (e.g., "src/")',
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
required: ['path'],
|
|
647
|
+
},
|
|
648
|
+
},
|
|
649
|
+
|
|
650
|
+
// Type Checker (optional tsc)
|
|
651
|
+
{
|
|
652
|
+
name: 'check_types',
|
|
653
|
+
description: 'Run TypeScript type checking on JS files with JSDoc types. Requires tsc in PATH (npm i -g typescript). Returns structured diagnostics or graceful fallback if tsc not available.',
|
|
654
|
+
inputSchema: {
|
|
655
|
+
type: 'object',
|
|
656
|
+
properties: {
|
|
657
|
+
path: {
|
|
658
|
+
type: 'string',
|
|
659
|
+
description: 'Directory to check',
|
|
660
|
+
},
|
|
661
|
+
files: {
|
|
662
|
+
type: 'array',
|
|
663
|
+
items: { type: 'string' },
|
|
664
|
+
description: 'Specific files to check (optional)',
|
|
665
|
+
},
|
|
666
|
+
maxDiagnostics: {
|
|
667
|
+
type: 'number',
|
|
668
|
+
description: 'Max diagnostics to return (default: 50)',
|
|
669
|
+
},
|
|
670
|
+
},
|
|
671
|
+
required: ['path'],
|
|
672
|
+
},
|
|
673
|
+
},
|
|
674
|
+
|
|
675
|
+
// Monorepo & Performance Tools
|
|
676
|
+
{
|
|
677
|
+
name: 'discover_sub_projects',
|
|
678
|
+
description: 'Find sub-projects in a monorepo. Scans packages/, apps/, services/, modules/, libs/, plugins/ for package.json.',
|
|
679
|
+
inputSchema: {
|
|
680
|
+
type: 'object',
|
|
681
|
+
properties: {
|
|
682
|
+
path: {
|
|
683
|
+
type: 'string',
|
|
684
|
+
description: 'Root path to scan for sub-projects',
|
|
685
|
+
},
|
|
686
|
+
},
|
|
687
|
+
required: ['path'],
|
|
688
|
+
},
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
name: 'get_analysis_summary',
|
|
692
|
+
description: 'Quick health score — runs only cached per-file metrics (complexity, undocumented, JSDoc). Much faster than get_full_analysis for large codebases.',
|
|
693
|
+
inputSchema: {
|
|
694
|
+
type: 'object',
|
|
695
|
+
properties: {
|
|
696
|
+
path: {
|
|
697
|
+
type: 'string',
|
|
698
|
+
description: 'Path to scan',
|
|
699
|
+
},
|
|
700
|
+
},
|
|
701
|
+
required: ['path'],
|
|
702
|
+
},
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
name: 'compact_project',
|
|
706
|
+
description: 'Compact all JS files in a directory — strips comments, whitespace, dead code. Preserves all names (mangle: false). Use with .ctx docs for AI-first workflow. Irreversible without git — use --dry-run first.',
|
|
707
|
+
inputSchema: {
|
|
708
|
+
type: 'object',
|
|
709
|
+
properties: {
|
|
710
|
+
path: {
|
|
711
|
+
type: 'string',
|
|
712
|
+
description: 'Directory to compact (e.g., "src/")',
|
|
713
|
+
},
|
|
714
|
+
dryRun: {
|
|
715
|
+
type: 'boolean',
|
|
716
|
+
description: 'Preview savings without modifying files (default: false)',
|
|
717
|
+
},
|
|
718
|
+
},
|
|
719
|
+
required: ['path'],
|
|
720
|
+
},
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
name: 'beautify_project',
|
|
724
|
+
description: 'Beautify/expand all JS files in a directory — formats with proper indentation. Inverse of compact_project. Preserves all names.',
|
|
725
|
+
inputSchema: {
|
|
726
|
+
type: 'object',
|
|
727
|
+
properties: {
|
|
728
|
+
path: {
|
|
729
|
+
type: 'string',
|
|
730
|
+
description: 'Directory to beautify (e.g., "src/")',
|
|
731
|
+
},
|
|
732
|
+
dryRun: {
|
|
733
|
+
type: 'boolean',
|
|
734
|
+
description: 'Preview without modifying files (default: false)',
|
|
735
|
+
},
|
|
736
|
+
},
|
|
737
|
+
required: ['path'],
|
|
738
|
+
},
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
name: 'validate_ctx_contracts',
|
|
742
|
+
description: 'Validate .ctx contracts against actual source code AST. Checks param count/names, export status, and reports mismatches. Zero-dependency alternative to tsc.',
|
|
743
|
+
inputSchema: {
|
|
744
|
+
type: 'object',
|
|
745
|
+
properties: {
|
|
746
|
+
path: { type: 'string', description: 'Project root path' },
|
|
747
|
+
strict: { type: 'boolean', description: 'Also report functions missing from .ctx (default: false)' },
|
|
748
|
+
},
|
|
749
|
+
required: ['path'],
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
name: 'edit_compressed',
|
|
754
|
+
description: 'Edit a function or class in source code by symbol name. Agent sends new code (compressed or formatted); server finds the symbol via AST, replaces it, validates syntax, and optionally beautifies. Use with get_compressed_file for token-efficient editing workflow.',
|
|
755
|
+
inputSchema: {
|
|
756
|
+
type: 'object',
|
|
757
|
+
properties: {
|
|
758
|
+
path: { type: 'string', description: 'Path to JS/MJS file' },
|
|
759
|
+
symbol: { type: 'string', description: 'Function or class name to replace' },
|
|
760
|
+
code: { type: 'string', description: 'New code for the symbol (full function/class definition)' },
|
|
761
|
+
beautify: { type: 'boolean', description: 'Beautify result after editing (default: true)' },
|
|
762
|
+
dryRun: { type: 'boolean', description: 'Preview without writing (default: false)' },
|
|
763
|
+
},
|
|
764
|
+
required: ['path', 'symbol', 'code'],
|
|
765
|
+
},
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
name: 'get_mode',
|
|
769
|
+
description: 'Get current compact code mode configuration. Returns mode (1=compact, 2=full, 3=IDE), preferences, and recommended workflow for agent.',
|
|
770
|
+
inputSchema: {
|
|
771
|
+
type: 'object',
|
|
772
|
+
properties: {
|
|
773
|
+
path: { type: 'string', description: 'Project root path' },
|
|
774
|
+
},
|
|
775
|
+
required: ['path'],
|
|
776
|
+
},
|
|
777
|
+
},
|
|
778
|
+
{
|
|
779
|
+
name: 'set_mode',
|
|
780
|
+
description: 'Set compact code mode for a project. Mode 1: store minified, edit directly. Mode 2: store full, use get_compressed_file + edit_compressed. Mode 3: IDE integration (future).',
|
|
781
|
+
inputSchema: {
|
|
782
|
+
type: 'object',
|
|
783
|
+
properties: {
|
|
784
|
+
path: { type: 'string', description: 'Project root path' },
|
|
785
|
+
mode: { type: 'number', description: 'Mode number: 1, 2, or 3' },
|
|
786
|
+
beautify: { type: 'boolean', description: 'Beautify code after edits (default: true)' },
|
|
787
|
+
autoValidate: { type: 'boolean', description: 'Auto-run .ctx validation after edits (default: false)' },
|
|
788
|
+
stripJSDoc: { type: 'boolean', description: 'Auto-strip JSDoc when compacting (default: false)' },
|
|
789
|
+
},
|
|
790
|
+
required: ['path', 'mode'],
|
|
791
|
+
},
|
|
792
|
+
},
|
|
525
793
|
];
|
package/src/tools.js
CHANGED
|
@@ -81,7 +81,7 @@ function loadDiskCache(path) {
|
|
|
81
81
|
* @param {string} path
|
|
82
82
|
* @returns {Promise<import('./graph-builder.js').Graph>}
|
|
83
83
|
*/
|
|
84
|
-
async function getGraph(path) {
|
|
84
|
+
export async function getGraph(path) {
|
|
85
85
|
// Different path = full rebuild
|
|
86
86
|
if (cachedGraph && cachedPath === path) {
|
|
87
87
|
// Check for file changes via mtime
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional Type Checker (tsc wrapper)
|
|
3
|
+
* Provides JSDoc type validation via TypeScript compiler
|
|
4
|
+
*
|
|
5
|
+
* Requires `tsc` in PATH (npm i -g typescript)
|
|
6
|
+
* Graceful fallback if not available
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { execSync, spawn } from 'child_process';
|
|
10
|
+
import { existsSync } from 'fs';
|
|
11
|
+
import { resolve, join } from 'path';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} TypeDiagnostic
|
|
15
|
+
* @property {string} file
|
|
16
|
+
* @property {number} line
|
|
17
|
+
* @property {number} column
|
|
18
|
+
* @property {'error'|'warning'} severity
|
|
19
|
+
* @property {string} message
|
|
20
|
+
* @property {string} code - TS error code (e.g. "TS2345")
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Check if tsc is available
|
|
25
|
+
* @returns {{ available: boolean, version: string|null, path: string|null }}
|
|
26
|
+
*/
|
|
27
|
+
function detectTsc() {
|
|
28
|
+
try {
|
|
29
|
+
const version = execSync('tsc --version', { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
30
|
+
const tscPath = execSync('which tsc', { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
31
|
+
return { available: true, version, path: tscPath };
|
|
32
|
+
} catch (e) {
|
|
33
|
+
// Try npx
|
|
34
|
+
try {
|
|
35
|
+
const version = execSync('npx tsc --version', { encoding: 'utf-8', timeout: 15000 }).trim();
|
|
36
|
+
return { available: true, version, path: 'npx tsc' };
|
|
37
|
+
} catch (e2) {
|
|
38
|
+
return { available: false, version: null, path: null };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Parse tsc output line into structured diagnostic
|
|
45
|
+
* @param {string} line
|
|
46
|
+
* @param {string} baseDir
|
|
47
|
+
* @returns {TypeDiagnostic|null}
|
|
48
|
+
*/
|
|
49
|
+
function parseDiagnosticLine(line, baseDir) {
|
|
50
|
+
// Format: file.js(line,col): error TS1234: message
|
|
51
|
+
const match = line.match(/^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+(TS\d+):\s+(.+)$/);
|
|
52
|
+
if (!match) return null;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
file: match[1],
|
|
56
|
+
line: parseInt(match[2]),
|
|
57
|
+
column: parseInt(match[3]),
|
|
58
|
+
severity: match[4],
|
|
59
|
+
message: match[6],
|
|
60
|
+
code: match[5],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Build tsc arguments
|
|
66
|
+
* @param {string} dir
|
|
67
|
+
* @param {Object} options
|
|
68
|
+
* @returns {string[]}
|
|
69
|
+
*/
|
|
70
|
+
function buildArgs(dir, options = {}) {
|
|
71
|
+
const args = ['--noEmit'];
|
|
72
|
+
|
|
73
|
+
// Check for existing config
|
|
74
|
+
const tsconfig = join(dir, 'tsconfig.json');
|
|
75
|
+
const jsconfig = join(dir, 'jsconfig.json');
|
|
76
|
+
|
|
77
|
+
if (existsSync(tsconfig)) {
|
|
78
|
+
args.push('--project', tsconfig);
|
|
79
|
+
} else if (existsSync(jsconfig)) {
|
|
80
|
+
args.push('--project', jsconfig);
|
|
81
|
+
} else {
|
|
82
|
+
// No config — use sensible defaults for JS projects
|
|
83
|
+
args.push('--allowJs', '--checkJs');
|
|
84
|
+
args.push('--target', 'ESNext');
|
|
85
|
+
args.push('--module', 'NodeNext');
|
|
86
|
+
args.push('--moduleResolution', 'NodeNext');
|
|
87
|
+
args.push('--skipLibCheck');
|
|
88
|
+
|
|
89
|
+
// Include the directory
|
|
90
|
+
if (options.files?.length) {
|
|
91
|
+
args.push(...options.files);
|
|
92
|
+
} else {
|
|
93
|
+
args.push('--rootDir', dir);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return args;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Run type checking on a directory
|
|
102
|
+
* @param {string} dir - Directory to check
|
|
103
|
+
* @param {Object} [options]
|
|
104
|
+
* @param {string[]} [options.files] - Specific files to check
|
|
105
|
+
* @param {number} [options.maxDiagnostics=50] - Max diagnostics to return
|
|
106
|
+
* @returns {Promise<{ available: boolean, version: string|null, diagnostics: TypeDiagnostic[], summary: Object, hint: string|null }>}
|
|
107
|
+
*/
|
|
108
|
+
export async function checkTypes(dir, options = {}) {
|
|
109
|
+
const maxDiagnostics = options.maxDiagnostics || 50;
|
|
110
|
+
const resolvedDir = resolve(dir);
|
|
111
|
+
|
|
112
|
+
// Detect tsc
|
|
113
|
+
const tsc = detectTsc();
|
|
114
|
+
if (!tsc.available) {
|
|
115
|
+
return {
|
|
116
|
+
available: false,
|
|
117
|
+
version: null,
|
|
118
|
+
diagnostics: [],
|
|
119
|
+
summary: { total: 0, errors: 0, warnings: 0 },
|
|
120
|
+
hint: 'TypeScript not found. Install: npm i -g typescript',
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Build and run command
|
|
125
|
+
const args = buildArgs(resolvedDir, options);
|
|
126
|
+
const cmd = tsc.path.includes('npx') ? 'npx' : 'tsc';
|
|
127
|
+
const cmdArgs = tsc.path.includes('npx') ? ['tsc', ...args] : args;
|
|
128
|
+
|
|
129
|
+
const result = await new Promise((res) => {
|
|
130
|
+
const child = spawn(cmd, cmdArgs, {
|
|
131
|
+
cwd: resolvedDir,
|
|
132
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
let stdout = '';
|
|
136
|
+
let stderr = '';
|
|
137
|
+
child.stdout.on('data', (d) => { stdout += d; });
|
|
138
|
+
child.stderr.on('data', (d) => { stderr += d; });
|
|
139
|
+
|
|
140
|
+
const timer = setTimeout(() => {
|
|
141
|
+
child.kill('SIGTERM');
|
|
142
|
+
res({ stdout, stderr, killed: true });
|
|
143
|
+
}, 60000);
|
|
144
|
+
|
|
145
|
+
child.on('close', () => {
|
|
146
|
+
clearTimeout(timer);
|
|
147
|
+
res({ stdout, stderr, killed: false });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
child.on('error', (e) => {
|
|
151
|
+
clearTimeout(timer);
|
|
152
|
+
res({ stdout: '', stderr: e.message, killed: false });
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Parse output (tsc exits with 1 on errors, stdout has diagnostics)
|
|
157
|
+
const output = (result.stdout || '') + (result.stderr || '');
|
|
158
|
+
const lines = output.split('\n').filter(l => l.trim());
|
|
159
|
+
|
|
160
|
+
const diagnostics = [];
|
|
161
|
+
for (const line of lines) {
|
|
162
|
+
const diag = parseDiagnosticLine(line, resolvedDir);
|
|
163
|
+
if (diag && diagnostics.length < maxDiagnostics) {
|
|
164
|
+
diagnostics.push(diag);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const errors = diagnostics.filter(d => d.severity === 'error').length;
|
|
169
|
+
const warnings = diagnostics.filter(d => d.severity === 'warning').length;
|
|
170
|
+
|
|
171
|
+
const byFile = {};
|
|
172
|
+
for (const d of diagnostics) {
|
|
173
|
+
byFile[d.file] = (byFile[d.file] || 0) + 1;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
available: true,
|
|
178
|
+
version: tsc.version,
|
|
179
|
+
diagnostics,
|
|
180
|
+
summary: {
|
|
181
|
+
total: diagnostics.length,
|
|
182
|
+
errors,
|
|
183
|
+
warnings,
|
|
184
|
+
byFile,
|
|
185
|
+
},
|
|
186
|
+
hint: null,
|
|
187
|
+
};
|
|
188
|
+
}
|
package/src/undocumented.js
CHANGED
|
@@ -74,8 +74,8 @@ function extractComments(code) {
|
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* Find JSDoc comment before a target line
|
|
77
|
-
* @param {Array<{text: string, endLine: number}>} comments
|
|
78
|
-
* @param {number} targetLine
|
|
77
|
+
* @param {Array<{text: string, endLine: number}>} comments - Extracted JSDoc comments
|
|
78
|
+
* @param {number} targetLine - Line number to search before
|
|
79
79
|
* @returns {string|null}
|
|
80
80
|
*/
|
|
81
81
|
function findJSDocBefore(comments, targetLine) {
|
|
@@ -100,15 +100,9 @@ function checkMissing(jsdoc, level) {
|
|
|
100
100
|
if (!jsdoc) {
|
|
101
101
|
if (level === 'all') missing.push('description');
|
|
102
102
|
if (level === 'params' || level === 'all') missing.push('@param', '@returns');
|
|
103
|
-
if (level === 'tests' || level === 'params' || level === 'all') missing.push('@test', '@expect');
|
|
104
103
|
return missing;
|
|
105
104
|
}
|
|
106
105
|
|
|
107
|
-
if (level === 'tests' || level === 'params' || level === 'all') {
|
|
108
|
-
if (!jsdoc.includes('@test')) missing.push('@test');
|
|
109
|
-
if (!jsdoc.includes('@expect')) missing.push('@expect');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
106
|
if (level === 'params' || level === 'all') {
|
|
113
107
|
if (!jsdoc.includes('@param')) missing.push('@param');
|
|
114
108
|
if (!jsdoc.includes('@returns') && !jsdoc.includes('@return')) missing.push('@returns');
|
|
@@ -124,13 +118,13 @@ const SKIP_METHODS = [
|
|
|
124
118
|
];
|
|
125
119
|
|
|
126
120
|
/**
|
|
127
|
-
* Parse file using AST and find undocumented items
|
|
121
|
+
* Parse file using AST and find undocumented items (per-file export for cache integration)
|
|
128
122
|
* @param {string} code
|
|
129
123
|
* @param {string} filePath
|
|
130
124
|
* @param {'tests'|'params'|'all'} level
|
|
131
125
|
* @returns {UndocumentedItem[]}
|
|
132
126
|
*/
|
|
133
|
-
function
|
|
127
|
+
export function checkUndocumentedFile(code, filePath, level) {
|
|
134
128
|
const results = [];
|
|
135
129
|
|
|
136
130
|
let ast;
|
|
@@ -223,8 +217,13 @@ export function getUndocumented(dir, level = 'tests') {
|
|
|
223
217
|
const results = [];
|
|
224
218
|
|
|
225
219
|
for (const file of files) {
|
|
226
|
-
|
|
227
|
-
|
|
220
|
+
let content;
|
|
221
|
+
try {
|
|
222
|
+
content = readFileSync(file, 'utf-8');
|
|
223
|
+
} catch (e) {
|
|
224
|
+
continue; // File deleted between findJSFiles and read
|
|
225
|
+
}
|
|
226
|
+
const items = checkUndocumentedFile(content, relative(resolvedDir, file), level);
|
|
228
227
|
results.push(...items);
|
|
229
228
|
}
|
|
230
229
|
|
package/src/workspace.js
CHANGED
|
@@ -51,7 +51,7 @@ export function getWorkspaceRoot() {
|
|
|
51
51
|
* Resolve a path argument against workspace root.
|
|
52
52
|
* Absolute paths are returned as-is.
|
|
53
53
|
* Relative paths are resolved against the workspace root.
|
|
54
|
-
* @param {string}
|
|
54
|
+
* @param {string} inputPath
|
|
55
55
|
* @returns {string}
|
|
56
56
|
*/
|
|
57
57
|
export function resolvePath(inputPath) {
|