projscan 3.8.0 → 4.1.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 (61) hide show
  1. package/README.md +337 -38
  2. package/dist/cli/commands/route.js +1 -0
  3. package/dist/cli/commands/route.js.map +1 -1
  4. package/dist/cli/commands/semanticGraph.js +27 -0
  5. package/dist/cli/commands/semanticGraph.js.map +1 -1
  6. package/dist/cli/commands/start.js +74 -1
  7. package/dist/cli/commands/start.js.map +1 -1
  8. package/dist/cli/index.js +0 -2
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/core/dependencyAnalyzer.js +172 -0
  11. package/dist/core/dependencyAnalyzer.js.map +1 -1
  12. package/dist/core/fixSuggest.js +5 -5
  13. package/dist/core/fixSuggest.js.map +1 -1
  14. package/dist/core/graphQuery.d.ts +23 -0
  15. package/dist/core/graphQuery.js +48 -0
  16. package/dist/core/graphQuery.js.map +1 -0
  17. package/dist/core/intentRouter.d.ts +8 -1
  18. package/dist/core/intentRouter.js +2186 -22
  19. package/dist/core/intentRouter.js.map +1 -1
  20. package/dist/core/issueEngine.js +6 -7
  21. package/dist/core/issueEngine.js.map +1 -1
  22. package/dist/core/onboarding.d.ts +2 -2
  23. package/dist/core/onboarding.js +29 -5
  24. package/dist/core/onboarding.js.map +1 -1
  25. package/dist/core/start.d.ts +1 -0
  26. package/dist/core/start.js +2010 -10
  27. package/dist/core/start.js.map +1 -1
  28. package/dist/mcp/server.d.ts +1 -1
  29. package/dist/mcp/server.js +14 -5
  30. package/dist/mcp/server.js.map +1 -1
  31. package/dist/mcp/tools/costSummary.js +0 -2
  32. package/dist/mcp/tools/costSummary.js.map +1 -1
  33. package/dist/mcp/tools/semanticGraph.js +28 -3
  34. package/dist/mcp/tools/semanticGraph.js.map +1 -1
  35. package/dist/mcp/tools/start.js +6 -1
  36. package/dist/mcp/tools/start.js.map +1 -1
  37. package/dist/mcp/tools.js +0 -4
  38. package/dist/mcp/tools.js.map +1 -1
  39. package/dist/projscan-sbom.cdx.json +6 -6
  40. package/dist/reporters/consoleReporter.js +19 -0
  41. package/dist/reporters/consoleReporter.js.map +1 -1
  42. package/dist/reporters/markdownReporter.js +19 -0
  43. package/dist/reporters/markdownReporter.js.map +1 -1
  44. package/dist/tool-manifest.json +41 -60
  45. package/dist/types.d.ts +83 -0
  46. package/docs/GUIDE.md +1565 -0
  47. package/docs/ROADMAP.md +219 -0
  48. package/docs/demos/projscan-4-1-demo.html +648 -0
  49. package/docs/projscan-mission-control.png +0 -0
  50. package/docs/projscan-proof-router.png +0 -0
  51. package/package.json +8 -1
  52. package/scripts/capture-readme-assets.mjs +60 -0
  53. package/dist/cli/commands/explain.d.ts +0 -1
  54. package/dist/cli/commands/explain.js +0 -48
  55. package/dist/cli/commands/explain.js.map +0 -1
  56. package/dist/mcp/tools/explain.d.ts +0 -2
  57. package/dist/mcp/tools/explain.js +0 -34
  58. package/dist/mcp/tools/explain.js.map +0 -1
  59. package/dist/mcp/tools/graph.d.ts +0 -2
  60. package/dist/mcp/tools/graph.js +0 -79
  61. package/dist/mcp/tools/graph.js.map +0 -1
@@ -0,0 +1,60 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath, pathToFileURL } from 'node:url';
5
+
6
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
7
+ const demoPath = path.join(repoRoot, 'docs', 'demos', 'projscan-4-1-demo.html');
8
+
9
+ if (!existsSync(demoPath)) {
10
+ console.error(`Missing capture source: ${path.relative(repoRoot, demoPath)}`);
11
+ process.exit(1);
12
+ }
13
+
14
+ const captures = [
15
+ {
16
+ name: 'Mission Control hero',
17
+ url: pathToFileURL(demoPath).href,
18
+ output: path.join(repoRoot, 'docs', 'projscan-mission-control.png'),
19
+ viewport: '1440,960',
20
+ },
21
+ {
22
+ name: 'Intent and proof workflow',
23
+ url: `${pathToFileURL(demoPath).href}#proof`,
24
+ output: path.join(repoRoot, 'docs', 'projscan-proof-router.png'),
25
+ viewport: '1440,760',
26
+ },
27
+ ];
28
+
29
+ for (const capture of captures) {
30
+ console.log(`Capturing ${capture.name} -> ${path.relative(repoRoot, capture.output)}`);
31
+ const result = spawnSync(
32
+ 'npx',
33
+ [
34
+ '--yes',
35
+ 'playwright',
36
+ 'screenshot',
37
+ '--browser',
38
+ 'chromium',
39
+ '--viewport-size',
40
+ capture.viewport,
41
+ '--wait-for-selector',
42
+ '[data-ready="true"]',
43
+ capture.url,
44
+ capture.output,
45
+ ],
46
+ {
47
+ cwd: repoRoot,
48
+ stdio: 'inherit',
49
+ },
50
+ );
51
+
52
+ if (result.status !== 0) {
53
+ console.error(
54
+ 'Playwright screenshot capture failed. If Chromium is missing locally, run: npx --yes playwright install chromium',
55
+ );
56
+ process.exit(result.status ?? 1);
57
+ }
58
+ }
59
+
60
+ console.log('README screenshots captured.');
@@ -1 +0,0 @@
1
- export declare function registerExplain(): void;
@@ -1,48 +0,0 @@
1
- import chalk from 'chalk';
2
- import path from 'node:path';
3
- import { program, setupLogLevel, maybeCompactBanner, analyzeFile, assertFormatSupported } from '../_shared.js';
4
- import { reportExplanation } from '../../reporters/consoleReporter.js';
5
- import { reportExplanationJson } from '../../reporters/jsonReporter.js';
6
- import { reportExplanationMarkdown } from '../../reporters/markdownReporter.js';
7
- import { formatCliDeprecationNotice } from '../../core/deprecations.js';
8
- export function registerExplain() {
9
- program
10
- .command('explain <file>')
11
- .description('Explain a file - its purpose, dependencies, and exports')
12
- .action(async (filePath) => {
13
- setupLogLevel();
14
- maybeCompactBanner();
15
- console.error(chalk.yellow(formatCliDeprecationNotice('explain', {
16
- since: '3.8.0',
17
- replacedBy: 'projscan file',
18
- note: 'projscan file is a strict superset (adds churn, risk, ownership, and related health).',
19
- })));
20
- const format = assertFormatSupported('explain');
21
- const absolutePath = path.resolve(filePath);
22
- try {
23
- const explanation = await analyzeFile(absolutePath);
24
- switch (format) {
25
- case 'json':
26
- reportExplanationJson(explanation);
27
- break;
28
- case 'markdown':
29
- reportExplanationMarkdown(explanation);
30
- break;
31
- default:
32
- reportExplanation(explanation);
33
- }
34
- }
35
- catch (error) {
36
- const message = error instanceof Error ? error.message : String(error);
37
- if (error.code === 'ENOENT' || /not found/i.test(message)) {
38
- console.error(chalk.red(`File not found: ${filePath}`));
39
- console.error(chalk.dim(` Tip: paths are repo-relative. Run \`projscan structure\` to see the file tree.`));
40
- }
41
- else {
42
- console.error(chalk.red(message));
43
- }
44
- process.exit(1);
45
- }
46
- });
47
- }
48
- //# sourceMappingURL=explain.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"explain.js","sourceRoot":"","sources":["../../../src/cli/commands/explain.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC/G,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAChF,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAExE,MAAM,UAAU,eAAe;IAC7B,OAAO;SACJ,OAAO,CAAC,gBAAgB,CAAC;SACzB,WAAW,CAAC,yDAAyD,CAAC;SACtE,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,EAAE;QACjC,aAAa,EAAE,CAAC;QAChB,kBAAkB,EAAE,CAAC;QACrB,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,MAAM,CACV,0BAA0B,CAAC,SAAS,EAAE;YACpC,KAAK,EAAE,OAAO;YACd,UAAU,EAAE,eAAe;YAC3B,IAAI,EAAE,uFAAuF;SAC9F,CAAC,CACH,CACF,CAAC;QACF,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;QAChD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAE5C,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,CAAC;YAEpD,QAAQ,MAAM,EAAE,CAAC;gBACf,KAAK,MAAM;oBACT,qBAAqB,CAAC,WAAW,CAAC,CAAC;oBACnC,MAAM;gBACR,KAAK,UAAU;oBACb,yBAAyB,CAAC,WAAW,CAAC,CAAC;oBACvC,MAAM;gBACR;oBACE,iBAAiB,CAAC,WAAW,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACrF,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC,CAAC;gBACxD,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,GAAG,CAAC,kFAAkF,CAAC,CAC9F,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;YACpC,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -1,2 +0,0 @@
1
- import { type McpTool } from './_shared.js';
2
- export declare const explainTool: McpTool;
@@ -1,34 +0,0 @@
1
- import path from 'node:path';
2
- import { explainFile } from './_shared.js';
3
- export const explainTool = {
4
- name: 'projscan_explain',
5
- deprecated: {
6
- since: '3.8.0',
7
- replacedBy: 'projscan_file',
8
- note: 'projscan_file is a strict superset (same purpose/imports/exports plus churn, risk, ownership, and related health).',
9
- },
10
- description: 'Explain a single file: purpose, imports, exports, and potential issues. Useful for understanding unfamiliar code before editing.',
11
- inputSchema: {
12
- type: 'object',
13
- properties: {
14
- file: {
15
- type: 'string',
16
- description: 'Path to the file relative to the project root.',
17
- },
18
- },
19
- required: ['file'],
20
- },
21
- handler: async (args, rootPath) => {
22
- const rel = typeof args.file === 'string' ? args.file : '';
23
- if (!rel) {
24
- throw new Error('file argument is required: pass a repo-relative path (e.g. "src/auth.ts").');
25
- }
26
- const absolutePath = path.resolve(rootPath, rel);
27
- const resolvedRoot = path.resolve(rootPath);
28
- if (!absolutePath.startsWith(resolvedRoot + path.sep) && absolutePath !== resolvedRoot) {
29
- throw new Error(`file must be inside the project root (got "${rel}"; absolute or "../" paths are rejected for security).`);
30
- }
31
- return await explainFile(absolutePath, rootPath);
32
- },
33
- };
34
- //# sourceMappingURL=explain.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"explain.js","sourceRoot":"","sources":["../../../src/mcp/tools/explain.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAgB,MAAM,cAAc,CAAC;AAEzD,MAAM,CAAC,MAAM,WAAW,GAAY;IAClC,IAAI,EAAE,kBAAkB;IACxB,UAAU,EAAE;QACV,KAAK,EAAE,OAAO;QACd,UAAU,EAAE,eAAe;QAC3B,IAAI,EAAE,oHAAoH;KAC3H;IACD,WAAW,EACT,kIAAkI;IACpI,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,gDAAgD;aAC9D;SACF;QACD,QAAQ,EAAE,CAAC,MAAM,CAAC;KACnB;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAChC,MAAM,GAAG,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CACb,4EAA4E,CAC7E,CAAC;QACJ,CAAC;QACD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACjD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,KAAK,YAAY,EAAE,CAAC;YACvF,MAAM,IAAI,KAAK,CACb,8CAA8C,GAAG,wDAAwD,CAC1G,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,WAAW,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IACnD,CAAC;CACF,CAAC"}
@@ -1,2 +0,0 @@
1
- import type { McpTool } from './_shared.js';
2
- export declare const graphTool: McpTool;
@@ -1,79 +0,0 @@
1
- import { scanRepository } from '../../core/repositoryScanner.js';
2
- import { buildCodeGraph, filesImportingFile, filesImportingPackage, filesDefiningSymbol, exportsOf, importsOf, } from '../../core/codeGraph.js';
3
- import { loadCachedGraph, saveCachedGraph } from '../../core/indexCache.js';
4
- export const graphTool = {
5
- name: 'projscan_graph',
6
- deprecated: {
7
- since: '3.8.0',
8
- replacedBy: 'projscan_semantic_graph',
9
- note: 'projscan_semantic_graph is the stable v3 successor (same nodes/edges with a versioned, supported contract).',
10
- },
11
- description: 'Query the AST-based code graph directly. Returns imports, exports, importers, or symbol definitions for a file or symbol. Agents should prefer this over analyze/doctor/explain for targeted structural questions - it is much cheaper and more accurate.',
12
- inputSchema: {
13
- type: 'object',
14
- properties: {
15
- file: {
16
- type: 'string',
17
- description: 'File path (relative to project root) to query.',
18
- },
19
- symbol: {
20
- type: 'string',
21
- description: 'Symbol name to query (e.g. a function or class). Use instead of `file` to find where a symbol is defined.',
22
- },
23
- direction: {
24
- type: 'string',
25
- description: 'What to return: "imports" (what the file imports), "exports" (what the file exports), "importers" (who imports the file), "symbol_defs" (files defining the symbol), "package_importers" (files importing a package by name).',
26
- enum: ['imports', 'exports', 'importers', 'symbol_defs', 'package_importers'],
27
- },
28
- limit: { type: 'number', description: 'Max entries returned (default 50).' },
29
- max_tokens: { type: 'number', description: 'Cap the response to roughly this many tokens.' },
30
- },
31
- required: ['direction'],
32
- },
33
- handler: async (args, rootPath) => {
34
- const scan = await scanRepository(rootPath);
35
- const cached = await loadCachedGraph(rootPath);
36
- const graph = await buildCodeGraph(rootPath, scan.files, cached);
37
- await saveCachedGraph(rootPath, graph);
38
- const direction = String(args.direction);
39
- const file = typeof args.file === 'string' ? args.file : undefined;
40
- const symbol = typeof args.symbol === 'string' ? args.symbol : undefined;
41
- const limit = Math.max(1, Math.min(500, typeof args.limit === 'number' ? args.limit : 50));
42
- switch (direction) {
43
- case 'imports': {
44
- if (!file) {
45
- throw new Error('direction=imports requires a `file` argument (repo-relative path, e.g. "src/auth.ts").');
46
- }
47
- return { file, imports: importsOf(graph, file).slice(0, limit) };
48
- }
49
- case 'exports': {
50
- if (!file) {
51
- throw new Error('direction=exports requires a `file` argument (repo-relative path).');
52
- }
53
- return { file, exports: exportsOf(graph, file).slice(0, limit) };
54
- }
55
- case 'importers': {
56
- if (!file) {
57
- throw new Error('direction=importers requires a `file` argument (repo-relative path).');
58
- }
59
- return { file, importers: filesImportingFile(graph, file).slice(0, limit) };
60
- }
61
- case 'symbol_defs': {
62
- if (!symbol) {
63
- throw new Error('direction=symbol_defs requires a `symbol` argument (the exported name to look up, e.g. "authenticate").');
64
- }
65
- return { symbol, definedIn: filesDefiningSymbol(graph, symbol).slice(0, limit) };
66
- }
67
- case 'package_importers': {
68
- const pkg = symbol ?? file;
69
- if (!pkg) {
70
- throw new Error('direction=package_importers requires either `symbol` or `file` arg (the npm package name, e.g. "chalk").');
71
- }
72
- return { package: pkg, importers: filesImportingPackage(graph, pkg).slice(0, limit) };
73
- }
74
- default:
75
- throw new Error(`unknown direction "${direction}". Valid: imports, exports, importers, symbol_defs, package_importers.`);
76
- }
77
- },
78
- };
79
- //# sourceMappingURL=graph.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"graph.js","sourceRoot":"","sources":["../../../src/mcp/tools/graph.ts"],"names":[],"mappings":"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"}