projscan 1.7.0 → 1.8.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 (56) hide show
  1. package/dist/core/applyFix.js +5 -46
  2. package/dist/core/applyFix.js.map +1 -1
  3. package/dist/core/embeddings.d.ts +9 -0
  4. package/dist/core/embeddings.js +45 -5
  5. package/dist/core/embeddings.js.map +1 -1
  6. package/dist/core/languages/LanguageAdapter.d.ts +1 -1
  7. package/dist/core/languages/cppFunctions.js +40 -1
  8. package/dist/core/languages/cppFunctions.js.map +1 -1
  9. package/dist/core/languages/registry.js +2 -0
  10. package/dist/core/languages/registry.js.map +1 -1
  11. package/dist/core/languages/swiftAdapter.d.ts +2 -0
  12. package/dist/core/languages/swiftAdapter.js +130 -0
  13. package/dist/core/languages/swiftAdapter.js.map +1 -0
  14. package/dist/core/languages/swiftCallSites.d.ts +14 -0
  15. package/dist/core/languages/swiftCallSites.js +60 -0
  16. package/dist/core/languages/swiftCallSites.js.map +1 -0
  17. package/dist/core/languages/swiftCyclomatic.d.ts +24 -0
  18. package/dist/core/languages/swiftCyclomatic.js +54 -0
  19. package/dist/core/languages/swiftCyclomatic.js.map +1 -0
  20. package/dist/core/languages/swiftExports.d.ts +13 -0
  21. package/dist/core/languages/swiftExports.js +95 -0
  22. package/dist/core/languages/swiftExports.js.map +1 -0
  23. package/dist/core/languages/swiftFunctions.d.ts +28 -0
  24. package/dist/core/languages/swiftFunctions.js +162 -0
  25. package/dist/core/languages/swiftFunctions.js.map +1 -0
  26. package/dist/core/languages/swiftImports.d.ts +26 -0
  27. package/dist/core/languages/swiftImports.js +45 -0
  28. package/dist/core/languages/swiftImports.js.map +1 -0
  29. package/dist/core/languages/swiftManifests.d.ts +17 -0
  30. package/dist/core/languages/swiftManifests.js +49 -0
  31. package/dist/core/languages/swiftManifests.js.map +1 -0
  32. package/dist/core/languages/treeSitterLoader.js +2 -0
  33. package/dist/core/languages/treeSitterLoader.js.map +1 -1
  34. package/dist/core/session.d.ts +18 -4
  35. package/dist/core/session.js +20 -5
  36. package/dist/core/session.js.map +1 -1
  37. package/dist/core/taint.d.ts +17 -0
  38. package/dist/core/taint.js +19 -1
  39. package/dist/core/taint.js.map +1 -1
  40. package/dist/grammars/tree-sitter-swift.wasm +0 -0
  41. package/dist/mcp/server.js +60 -1
  42. package/dist/mcp/server.js.map +1 -1
  43. package/dist/mcp/tools/_shared.d.ts +25 -1
  44. package/dist/mcp/tools/_shared.js.map +1 -1
  45. package/dist/mcp/tools/costSummary.js +12 -3
  46. package/dist/mcp/tools/costSummary.js.map +1 -1
  47. package/dist/mcp/tools/reviewWatch.d.ts +7 -0
  48. package/dist/mcp/tools/reviewWatch.js +228 -0
  49. package/dist/mcp/tools/reviewWatch.js.map +1 -0
  50. package/dist/mcp/tools.js +2 -0
  51. package/dist/mcp/tools.js.map +1 -1
  52. package/dist/tool-manifest.json +37 -3
  53. package/dist/utils/atomicWrite.d.ts +26 -0
  54. package/dist/utils/atomicWrite.js +69 -0
  55. package/dist/utils/atomicWrite.js.map +1 -0
  56. package/package.json +3 -2
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "projscan",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "mcpProtocolVersion": "2025-03-26",
5
- "generatedAt": "2026-05-07T21:35:24.182Z",
6
- "toolCount": 26,
5
+ "generatedAt": "2026-05-07T22:25:02.535Z",
6
+ "toolCount": 27,
7
7
  "tools": [
8
8
  {
9
9
  "name": "projscan_analyze",
@@ -655,6 +655,40 @@
655
655
  }
656
656
  }
657
657
  }
658
+ },
659
+ {
660
+ "name": "projscan_review_watch",
661
+ "description": "Long-running PR review. Polls a base+head ref pair on an interval and emits a notifications/projscan/pr_changed notification whenever the review verdict, SHAs, flow count, or risky-function set changes. Pairs with projscan_review (one-shot) — use this when an agent wants to react to pushes on a PR without re-asking. Actions: start (returns initial review + watchId) / stop / list.",
662
+ "inputSchema": {
663
+ "type": "object",
664
+ "properties": {
665
+ "action": {
666
+ "type": "string",
667
+ "enum": [
668
+ "start",
669
+ "stop",
670
+ "list"
671
+ ],
672
+ "description": "\"start\" begins polling (returns initial review + watchId). \"stop\" cancels a watch by id. \"list\" enumerates active watches."
673
+ },
674
+ "base": {
675
+ "type": "string",
676
+ "description": "Base ref. Default: origin/main → main → origin/master → master → HEAD~1. (start only)"
677
+ },
678
+ "head": {
679
+ "type": "string",
680
+ "description": "Head ref. Default: HEAD. (start only)"
681
+ },
682
+ "interval_seconds": {
683
+ "type": "number",
684
+ "description": "Poll interval in seconds. Default: 30. Min: 5, max: 600. (start only)"
685
+ },
686
+ "watchId": {
687
+ "type": "string",
688
+ "description": "Watch identifier returned by a previous \"start\". (stop only)"
689
+ }
690
+ }
691
+ }
658
692
  }
659
693
  ]
660
694
  }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Crash-safe file write: write to a tmp path, fsync the data, rename
3
+ * over the target, then best-effort fsync the parent directory.
4
+ *
5
+ * Three hardenings over a naive writeFile + rename:
6
+ *
7
+ * 1. Tmp filename uses randomUUID() so the path is unpredictable. A
8
+ * shared-system attacker can't pre-create a symlink at the tmp path
9
+ * before our write lands.
10
+ *
11
+ * 2. Open with the 'wx' flag (O_CREAT | O_EXCL). If the tmp path
12
+ * already exists for any reason (race, leftover, planted symlink),
13
+ * fs.open throws EEXIST instead of following the symlink. Belt-
14
+ * and-suspenders with #1.
15
+ *
16
+ * 3. fsync the file before rename, and best-effort fsync the parent
17
+ * dir after rename. Rename is atomic at the journal-record level
18
+ * on most filesystems, but without fsync the rename can survive a
19
+ * crash with empty tmp content (file metadata persists, data
20
+ * doesn't). Parent-dir sync is best-effort: Windows doesn't
21
+ * support it and some FUSE backends treat it as a no-op.
22
+ *
23
+ * Used by `applyFix` (1.6+) and `session.saveSession` (1.8+) to ensure
24
+ * that crashes mid-write never leave partially-written state on disk.
25
+ */
26
+ export declare function atomicWriteFile(absPath: string, content: string): Promise<void>;
@@ -0,0 +1,69 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ /**
5
+ * Crash-safe file write: write to a tmp path, fsync the data, rename
6
+ * over the target, then best-effort fsync the parent directory.
7
+ *
8
+ * Three hardenings over a naive writeFile + rename:
9
+ *
10
+ * 1. Tmp filename uses randomUUID() so the path is unpredictable. A
11
+ * shared-system attacker can't pre-create a symlink at the tmp path
12
+ * before our write lands.
13
+ *
14
+ * 2. Open with the 'wx' flag (O_CREAT | O_EXCL). If the tmp path
15
+ * already exists for any reason (race, leftover, planted symlink),
16
+ * fs.open throws EEXIST instead of following the symlink. Belt-
17
+ * and-suspenders with #1.
18
+ *
19
+ * 3. fsync the file before rename, and best-effort fsync the parent
20
+ * dir after rename. Rename is atomic at the journal-record level
21
+ * on most filesystems, but without fsync the rename can survive a
22
+ * crash with empty tmp content (file metadata persists, data
23
+ * doesn't). Parent-dir sync is best-effort: Windows doesn't
24
+ * support it and some FUSE backends treat it as a no-op.
25
+ *
26
+ * Used by `applyFix` (1.6+) and `session.saveSession` (1.8+) to ensure
27
+ * that crashes mid-write never leave partially-written state on disk.
28
+ */
29
+ export async function atomicWriteFile(absPath, content) {
30
+ const tmp = `${absPath}.projscan-tmp-${randomUUID()}`;
31
+ const handle = await fs.open(tmp, 'wx');
32
+ try {
33
+ await handle.writeFile(content, 'utf-8');
34
+ await handle.sync();
35
+ }
36
+ finally {
37
+ await handle.close();
38
+ }
39
+ // 1.8+ — clean up the tmp file if rename fails (e.g., the target is
40
+ // on a read-only filesystem, EACCES, or its parent dir was removed
41
+ // mid-write). Without this, retried writes leak abandoned tmp files
42
+ // forever — invisible until they pile up. Re-throw the original
43
+ // error so callers see the real cause; the unlink is best-effort.
44
+ try {
45
+ await fs.rename(tmp, absPath);
46
+ }
47
+ catch (err) {
48
+ try {
49
+ await fs.unlink(tmp);
50
+ }
51
+ catch {
52
+ // best-effort cleanup
53
+ }
54
+ throw err;
55
+ }
56
+ try {
57
+ const dir = await fs.open(path.dirname(absPath), 'r');
58
+ try {
59
+ await dir.sync();
60
+ }
61
+ finally {
62
+ await dir.close();
63
+ }
64
+ }
65
+ catch {
66
+ // ignore — best-effort
67
+ }
68
+ }
69
+ //# sourceMappingURL=atomicWrite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomicWrite.js","sourceRoot":"","sources":["../../src/utils/atomicWrite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAe,EAAE,OAAe;IACpE,MAAM,GAAG,GAAG,GAAG,OAAO,iBAAiB,UAAU,EAAE,EAAE,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACzC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IACD,oEAAoE;IACpE,mEAAmE;IACnE,oEAAoE;IACpE,gEAAgE;IAChE,kEAAkE;IAClE,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC;QACtD,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QACnB,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,uBAAuB;IACzB,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "projscan",
3
3
  "mcpName": "io.github.abhiyoheswaran1/projscan",
4
- "version": "1.7.0",
5
- "description": "Agent-first code intelligence. MCP server (2025-03-26) with AST parsing for JavaScript, TypeScript, Python, Go, Java, Ruby, Rust, PHP, C#, Kotlin, and C++; code graph, file + per-function AST cyclomatic complexity, per-function fan-in + fan-out, coupling + cycle detection, structural PR diff with HTML reporter, coverage report with HTML reporter, one-call PR review (projscan_review) with new-taint-flow detection, rule-driven fix suggestions + mechanical apply layer with rollback (projscan_apply_fix, projscan_fix_suggest, projscan_explain_issue), source-to-sink taint analysis (projscan_taint), transitive blast-radius analysis with cross-repo mode (projscan_impact for files and symbols), cross-repo workspace registration + intelligence (projscan_workspace_graph), per-function semantic search chunks (sub-file embeddings), per-rule confidence + cost-summary analytics (projscan_cost_summary), monorepo workspace awareness with cross-package import policy + per-package dependencies / outdated / audit, BM25 + optional semantic search, cursor pagination, progress notifications, context-budgeted output, and a stable-surface CI guard. CLI on the side.",
4
+ "version": "1.8.0",
5
+ "description": "Agent-first code intelligence. MCP server (2025-03-26) with AST parsing for JavaScript, TypeScript, Python, Go, Java, Ruby, Rust, PHP, C#, Kotlin, Swift, and C++; code graph, file + per-function AST cyclomatic complexity, per-function fan-in + fan-out, coupling + cycle detection, structural PR diff with HTML reporter, coverage report with HTML reporter, one-call PR review (projscan_review) and long-running PR-watch mode (projscan_review_watch), rule-driven fix suggestions + mechanical apply layer with rollback (projscan_apply_fix, projscan_fix_suggest, projscan_explain_issue), source-to-sink taint analysis (projscan_taint) with truncation reporting, transitive blast-radius analysis with cross-repo mode (projscan_impact for files and symbols), cross-repo workspace registration + intelligence (projscan_workspace_graph), per-function semantic search chunks (sub-file embeddings), per-rule confidence + cost-summary analytics (projscan_cost_summary), monorepo workspace awareness with cross-package import policy + per-package dependencies / outdated / audit, BM25 + optional semantic search, cursor pagination, progress notifications, context-budgeted output, and a stable-surface CI guard. CLI on the side.",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
@@ -87,6 +87,7 @@
87
87
  "tree-sitter-cli": "^0.26.8",
88
88
  "tree-sitter-cpp": "^0.23.4",
89
89
  "tree-sitter-kotlin": "^0.3.8",
90
+ "tree-sitter-swift": "^0.7.1",
90
91
  "typescript": "^5.6.0",
91
92
  "typescript-eslint": "^8.57.0",
92
93
  "vitest": "^2.1.0"