projscan 1.6.2 → 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 (108) 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/cppAdapter.d.ts +2 -0
  8. package/dist/core/languages/cppAdapter.js +145 -0
  9. package/dist/core/languages/cppAdapter.js.map +1 -0
  10. package/dist/core/languages/cppCallSites.d.ts +13 -0
  11. package/dist/core/languages/cppCallSites.js +62 -0
  12. package/dist/core/languages/cppCallSites.js.map +1 -0
  13. package/dist/core/languages/cppCyclomatic.d.ts +25 -0
  14. package/dist/core/languages/cppCyclomatic.js +82 -0
  15. package/dist/core/languages/cppCyclomatic.js.map +1 -0
  16. package/dist/core/languages/cppExports.d.ts +13 -0
  17. package/dist/core/languages/cppExports.js +120 -0
  18. package/dist/core/languages/cppExports.js.map +1 -0
  19. package/dist/core/languages/cppFunctions.d.ts +30 -0
  20. package/dist/core/languages/cppFunctions.js +281 -0
  21. package/dist/core/languages/cppFunctions.js.map +1 -0
  22. package/dist/core/languages/cppImports.d.ts +25 -0
  23. package/dist/core/languages/cppImports.js +77 -0
  24. package/dist/core/languages/cppImports.js.map +1 -0
  25. package/dist/core/languages/cppManifests.d.ts +18 -0
  26. package/dist/core/languages/cppManifests.js +41 -0
  27. package/dist/core/languages/cppManifests.js.map +1 -0
  28. package/dist/core/languages/kotlinAdapter.d.ts +2 -0
  29. package/dist/core/languages/kotlinAdapter.js +141 -0
  30. package/dist/core/languages/kotlinAdapter.js.map +1 -0
  31. package/dist/core/languages/kotlinCallSites.d.ts +15 -0
  32. package/dist/core/languages/kotlinCallSites.js +62 -0
  33. package/dist/core/languages/kotlinCallSites.js.map +1 -0
  34. package/dist/core/languages/kotlinCyclomatic.d.ts +24 -0
  35. package/dist/core/languages/kotlinCyclomatic.js +54 -0
  36. package/dist/core/languages/kotlinCyclomatic.js.map +1 -0
  37. package/dist/core/languages/kotlinExports.d.ts +13 -0
  38. package/dist/core/languages/kotlinExports.js +103 -0
  39. package/dist/core/languages/kotlinExports.js.map +1 -0
  40. package/dist/core/languages/kotlinFunctions.d.ts +27 -0
  41. package/dist/core/languages/kotlinFunctions.js +147 -0
  42. package/dist/core/languages/kotlinFunctions.js.map +1 -0
  43. package/dist/core/languages/kotlinImports.d.ts +25 -0
  44. package/dist/core/languages/kotlinImports.js +74 -0
  45. package/dist/core/languages/kotlinImports.js.map +1 -0
  46. package/dist/core/languages/kotlinManifests.d.ts +20 -0
  47. package/dist/core/languages/kotlinManifests.js +46 -0
  48. package/dist/core/languages/kotlinManifests.js.map +1 -0
  49. package/dist/core/languages/registry.js +16 -1
  50. package/dist/core/languages/registry.js.map +1 -1
  51. package/dist/core/languages/swiftAdapter.d.ts +2 -0
  52. package/dist/core/languages/swiftAdapter.js +130 -0
  53. package/dist/core/languages/swiftAdapter.js.map +1 -0
  54. package/dist/core/languages/swiftCallSites.d.ts +14 -0
  55. package/dist/core/languages/swiftCallSites.js +60 -0
  56. package/dist/core/languages/swiftCallSites.js.map +1 -0
  57. package/dist/core/languages/swiftCyclomatic.d.ts +24 -0
  58. package/dist/core/languages/swiftCyclomatic.js +54 -0
  59. package/dist/core/languages/swiftCyclomatic.js.map +1 -0
  60. package/dist/core/languages/swiftExports.d.ts +13 -0
  61. package/dist/core/languages/swiftExports.js +95 -0
  62. package/dist/core/languages/swiftExports.js.map +1 -0
  63. package/dist/core/languages/swiftFunctions.d.ts +28 -0
  64. package/dist/core/languages/swiftFunctions.js +162 -0
  65. package/dist/core/languages/swiftFunctions.js.map +1 -0
  66. package/dist/core/languages/swiftImports.d.ts +26 -0
  67. package/dist/core/languages/swiftImports.js +45 -0
  68. package/dist/core/languages/swiftImports.js.map +1 -0
  69. package/dist/core/languages/swiftManifests.d.ts +17 -0
  70. package/dist/core/languages/swiftManifests.js +49 -0
  71. package/dist/core/languages/swiftManifests.js.map +1 -0
  72. package/dist/core/languages/treeSitterLoader.js +21 -1
  73. package/dist/core/languages/treeSitterLoader.js.map +1 -1
  74. package/dist/core/memory.d.ts +30 -0
  75. package/dist/core/memory.js +42 -0
  76. package/dist/core/memory.js.map +1 -1
  77. package/dist/core/session.d.ts +23 -4
  78. package/dist/core/session.js +28 -8
  79. package/dist/core/session.js.map +1 -1
  80. package/dist/core/taint.d.ts +17 -0
  81. package/dist/core/taint.js +19 -1
  82. package/dist/core/taint.js.map +1 -1
  83. package/dist/grammars/tree-sitter-cpp.wasm +0 -0
  84. package/dist/grammars/tree-sitter-kotlin.wasm +0 -0
  85. package/dist/grammars/tree-sitter-swift.wasm +0 -0
  86. package/dist/mcp/server.js +107 -11
  87. package/dist/mcp/server.js.map +1 -1
  88. package/dist/mcp/sessionTouchScanner.js +8 -2
  89. package/dist/mcp/sessionTouchScanner.js.map +1 -1
  90. package/dist/mcp/tools/_shared.d.ts +25 -1
  91. package/dist/mcp/tools/_shared.js.map +1 -1
  92. package/dist/mcp/tools/costSummary.d.ts +26 -0
  93. package/dist/mcp/tools/costSummary.js +152 -0
  94. package/dist/mcp/tools/costSummary.js.map +1 -0
  95. package/dist/mcp/tools/fixSuggest.js +23 -2
  96. package/dist/mcp/tools/fixSuggest.js.map +1 -1
  97. package/dist/mcp/tools/memory.js +37 -4
  98. package/dist/mcp/tools/memory.js.map +1 -1
  99. package/dist/mcp/tools/reviewWatch.d.ts +7 -0
  100. package/dist/mcp/tools/reviewWatch.js +228 -0
  101. package/dist/mcp/tools/reviewWatch.js.map +1 -0
  102. package/dist/mcp/tools.js +4 -0
  103. package/dist/mcp/tools.js.map +1 -1
  104. package/dist/tool-manifest.json +52 -4
  105. package/dist/utils/atomicWrite.d.ts +26 -0
  106. package/dist/utils/atomicWrite.js +69 -0
  107. package/dist/utils/atomicWrite.js.map +1 -0
  108. package/package.json +6 -2
@@ -0,0 +1,162 @@
1
+ const SWIFT_DECISION_NODES = new Set([
2
+ 'if_statement',
3
+ 'guard_statement',
4
+ 'for_statement',
5
+ 'while_statement',
6
+ 'repeat_while_statement',
7
+ 'do_statement',
8
+ 'catch_block',
9
+ 'catch_clause',
10
+ ]);
11
+ /**
12
+ * Per-function McCabe CC for Swift. Walks `function_declaration` nodes
13
+ * (top-level + class/struct/protocol/extension methods). Methods inside
14
+ * `class Foo { func m() }` are named `Foo.m`. Closures and trailing-closure
15
+ * blocks aren't extracted as separate functions; their decision points
16
+ * fold into the enclosing function (matches projscan's convention for
17
+ * Rust closures and Go func literals).
18
+ *
19
+ * `switch` cases each count as +1; the `default` arm does not. Optional
20
+ * chaining (`?.`) and nil-coalescing (`??`) are NOT counted.
21
+ */
22
+ export function extractSwiftFunctions(root) {
23
+ const out = [];
24
+ walk(root, null, out);
25
+ return out;
26
+ }
27
+ function walk(node, ownerName, out) {
28
+ if (node.type === 'class_declaration' ||
29
+ node.type === 'protocol_declaration' ||
30
+ node.type === 'extension_declaration') {
31
+ const name = nameOfDecl(node) ?? ownerName;
32
+ const body = bodyOf(node);
33
+ if (body) {
34
+ for (const child of body.namedChildren)
35
+ walk(child, name, out);
36
+ }
37
+ return;
38
+ }
39
+ if (node.type === 'function_declaration' ||
40
+ node.type === 'init_declaration' ||
41
+ node.type === 'deinit_declaration' ||
42
+ node.type === 'subscript_declaration') {
43
+ let baseName;
44
+ if (node.type === 'init_declaration')
45
+ baseName = 'init';
46
+ else if (node.type === 'deinit_declaration')
47
+ baseName = 'deinit';
48
+ else if (node.type === 'subscript_declaration')
49
+ baseName = 'subscript';
50
+ else
51
+ baseName = nameOfDecl(node) ?? '<anonymous>';
52
+ const fnName = ownerName ? `${ownerName}.${baseName}` : baseName;
53
+ const line = node.startPosition.row + 1;
54
+ const endLine = node.endPosition.row + 1;
55
+ const { cc, callSites } = analyzeBody(node);
56
+ out.push({ name: fnName, line, endLine, cyclomaticComplexity: cc, callSites });
57
+ return;
58
+ }
59
+ for (const child of node.namedChildren)
60
+ walk(child, ownerName, out);
61
+ }
62
+ function nameOfDecl(node) {
63
+ if (node.childForFieldName) {
64
+ const id = node.childForFieldName('name');
65
+ if (id && id.text)
66
+ return id.text;
67
+ }
68
+ for (const c of node.namedChildren) {
69
+ if (c.type === 'simple_identifier' || c.type === 'identifier' || c.type === 'type_identifier') {
70
+ return c.text;
71
+ }
72
+ }
73
+ return null;
74
+ }
75
+ function bodyOf(node) {
76
+ if (node.childForFieldName) {
77
+ const b = node.childForFieldName('body');
78
+ if (b)
79
+ return b;
80
+ }
81
+ for (const c of node.namedChildren) {
82
+ if (c.type === 'class_body' ||
83
+ c.type === 'protocol_body' ||
84
+ c.type === 'function_body' ||
85
+ c.type === 'extension_body') {
86
+ return c;
87
+ }
88
+ }
89
+ return null;
90
+ }
91
+ function analyzeBody(fnNode) {
92
+ let count = 0;
93
+ const calls = new Set();
94
+ const body = bodyOf(fnNode);
95
+ if (!body)
96
+ return { cc: 1, callSites: [] };
97
+ walkSkipNested(body, (n) => {
98
+ if (SWIFT_DECISION_NODES.has(n.type)) {
99
+ count++;
100
+ return;
101
+ }
102
+ if (n.type === 'switch_entry') {
103
+ // The default arm has no value child (no case_label patterns) — non-
104
+ // default arms always carry one or more case_label / case_pattern
105
+ // children. Mirrors the kotlin/cpp structural-detection approach.
106
+ const isDefault = !n.namedChildren.some((c) => c.type === 'switch_pattern' || c.type === 'case_label');
107
+ if (!isDefault)
108
+ count++;
109
+ return;
110
+ }
111
+ if (n.type === 'conjunction_expression' || n.type === 'disjunction_expression') {
112
+ count++;
113
+ return;
114
+ }
115
+ if (n.type === 'call_expression') {
116
+ const fn = n.childForFieldName ? n.childForFieldName('function') : n.namedChildren[0] ?? null;
117
+ const name = bareName(fn);
118
+ if (name)
119
+ calls.add(name);
120
+ }
121
+ });
122
+ return { cc: count + 1, callSites: [...calls] };
123
+ }
124
+ function bareName(node) {
125
+ if (!node)
126
+ return null;
127
+ switch (node.type) {
128
+ case 'simple_identifier':
129
+ case 'identifier':
130
+ case 'type_identifier':
131
+ return node.text;
132
+ case 'navigation_expression': {
133
+ const last = node.namedChildren[node.namedChildren.length - 1];
134
+ return last ? bareName(last) : null;
135
+ }
136
+ case 'navigation_suffix': {
137
+ for (const c of node.namedChildren) {
138
+ const n = bareName(c);
139
+ if (n)
140
+ return n;
141
+ }
142
+ return null;
143
+ }
144
+ default:
145
+ return null;
146
+ }
147
+ }
148
+ function walkSkipNested(node, visit) {
149
+ visit(node);
150
+ for (const child of node.namedChildren) {
151
+ if (child.type === 'function_declaration' ||
152
+ child.type === 'init_declaration' ||
153
+ child.type === 'class_declaration' ||
154
+ child.type === 'protocol_declaration' ||
155
+ child.type === 'lambda_literal' ||
156
+ child.type === 'closure_expression') {
157
+ continue;
158
+ }
159
+ walkSkipNested(child, visit);
160
+ }
161
+ }
162
+ //# sourceMappingURL=swiftFunctions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swiftFunctions.js","sourceRoot":"","sources":["../../../src/core/languages/swiftFunctions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { AstImport } from '../ast.js';
2
+ interface TsNode {
3
+ type: string;
4
+ text: string;
5
+ startPosition: {
6
+ row: number;
7
+ column: number;
8
+ };
9
+ namedChildren: TsNode[];
10
+ childForFieldName?: (name: string) => TsNode | null;
11
+ }
12
+ /**
13
+ * Extract Swift `import` statements from a tree-sitter-swift AST.
14
+ *
15
+ * Handled forms:
16
+ * import Foundation → source = "Foundation"
17
+ * import struct Foo.Bar → source = "Foo.Bar"
18
+ * import class Foo.Bar.Baz → source = "Foo.Bar.Baz"
19
+ * @testable import MyModule → source = "MyModule"
20
+ * @_implementationOnly import Private → source = "Private"
21
+ *
22
+ * Swift modules don't expose individual symbols at import time the way
23
+ * JS named imports do; the import binds the whole module / submodule.
24
+ */
25
+ export declare function extractSwiftImports(root: TsNode): AstImport[];
26
+ export {};
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Extract Swift `import` statements from a tree-sitter-swift AST.
3
+ *
4
+ * Handled forms:
5
+ * import Foundation → source = "Foundation"
6
+ * import struct Foo.Bar → source = "Foo.Bar"
7
+ * import class Foo.Bar.Baz → source = "Foo.Bar.Baz"
8
+ * @testable import MyModule → source = "MyModule"
9
+ * @_implementationOnly import Private → source = "Private"
10
+ *
11
+ * Swift modules don't expose individual symbols at import time the way
12
+ * JS named imports do; the import binds the whole module / submodule.
13
+ */
14
+ export function extractSwiftImports(root) {
15
+ const imports = [];
16
+ walk(root, (n) => {
17
+ if (n.type !== 'import_declaration')
18
+ return;
19
+ const source = readImportPath(n);
20
+ if (!source)
21
+ return;
22
+ imports.push({
23
+ source,
24
+ kind: 'static',
25
+ specifiers: [],
26
+ typeOnly: false,
27
+ line: n.startPosition.row + 1,
28
+ });
29
+ });
30
+ return imports;
31
+ }
32
+ function readImportPath(node) {
33
+ const text = node.text.trim();
34
+ let body = text.replace(/^\s*(@[A-Za-z_][\w]*\s*)+/, '');
35
+ body = body.replace(/^\s*import\b/, '').trim();
36
+ body = body.replace(/^(typealias|struct|class|enum|protocol|let|var|func)\b\s*/, '');
37
+ const m = /^[A-Za-z_][\w.]*/.exec(body);
38
+ return m ? m[0] : '';
39
+ }
40
+ function walk(node, visit) {
41
+ visit(node);
42
+ for (const child of node.namedChildren)
43
+ walk(child, visit);
44
+ }
45
+ //# sourceMappingURL=swiftImports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swiftImports.js","sourceRoot":"","sources":["../../../src/core/languages/swiftImports.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;QACf,IAAI,CAAC,CAAC,IAAI,KAAK,oBAAoB;YAAE,OAAO;QAC5C,MAAM,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,OAAO,CAAC,IAAI,CAAC;YACX,MAAM;YACN,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,EAAE;YACd,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC;SAC9B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;IAClC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC;IACzD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/C,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,2DAA2D,EAC3D,EAAE,CACH,CAAC;IACF,MAAM,CAAC,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACvB,CAAC;AAED,SAAS,IAAI,CAAC,IAAY,EAAE,KAA0B;IACpD,KAAK,CAAC,IAAI,CAAC,CAAC;IACZ,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa;QAAE,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { FileEntry } from '../../types.js';
2
+ export interface SwiftProjectInfo {
3
+ /**
4
+ * Detected Swift source roots. For SwiftPM projects this is typically
5
+ * `Sources/<Module>` per declared target; we fall back to the conventional
6
+ * `Sources/` and the repo root when no Package.swift exists.
7
+ */
8
+ packageRoots: string[];
9
+ /** Did we see a Package.swift / .xcodeproj manifest? Used for diagnostics only. */
10
+ hasManifest: boolean;
11
+ }
12
+ /**
13
+ * Detect Swift source roots. We don't parse Package.swift; instead we
14
+ * survey the .swift file paths and pick whichever conventional roots
15
+ * actually contain Swift sources.
16
+ */
17
+ export declare function detectSwiftProject(rootPath: string, files: FileEntry[]): Promise<SwiftProjectInfo | null>;
@@ -0,0 +1,49 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ const COMMON_SWIFT_ROOTS = ['Sources', 'Tests', '.'];
4
+ /**
5
+ * Detect Swift source roots. We don't parse Package.swift; instead we
6
+ * survey the .swift file paths and pick whichever conventional roots
7
+ * actually contain Swift sources.
8
+ */
9
+ export async function detectSwiftProject(rootPath, files) {
10
+ const swiftFiles = files.filter((f) => f.relativePath.endsWith('.swift'));
11
+ if (swiftFiles.length === 0)
12
+ return null;
13
+ const hasManifest = await hasSwiftManifest(rootPath);
14
+ const packageRoots = [];
15
+ for (const candidate of COMMON_SWIFT_ROOTS) {
16
+ if (candidate === '.') {
17
+ if (swiftFiles.some((f) => !f.relativePath.includes('/')))
18
+ packageRoots.push('.');
19
+ }
20
+ else if (swiftFiles.some((f) => f.relativePath.startsWith(candidate + '/'))) {
21
+ packageRoots.push(candidate);
22
+ }
23
+ }
24
+ if (packageRoots.length === 0)
25
+ packageRoots.push('.');
26
+ return { packageRoots, hasManifest };
27
+ }
28
+ async function hasSwiftManifest(rootPath) {
29
+ const candidates = ['Package.swift', 'Package.resolved'];
30
+ for (const c of candidates) {
31
+ try {
32
+ await fs.access(path.join(rootPath, c));
33
+ return true;
34
+ }
35
+ catch {
36
+ // try next
37
+ }
38
+ }
39
+ try {
40
+ const entries = await fs.readdir(rootPath, { withFileTypes: true });
41
+ if (entries.some((e) => e.isDirectory() && e.name.endsWith('.xcodeproj')))
42
+ return true;
43
+ }
44
+ catch {
45
+ // ignore
46
+ }
47
+ return false;
48
+ }
49
+ //# sourceMappingURL=swiftManifests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swiftManifests.js","sourceRoot":"","sources":["../../../src/core/languages/swiftManifests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAc7B,MAAM,kBAAkB,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;AAErD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,QAAgB,EAChB,KAAkB;IAElB,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC1E,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzC,MAAM,WAAW,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACrD,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,KAAK,MAAM,SAAS,IAAI,kBAAkB,EAAE,CAAC;QAC3C,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;YACtB,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBAAE,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpF,CAAC;aAAM,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;YAC9E,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;AACvC,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,QAAgB;IAC9C,MAAM,UAAU,GAAG,CAAC,eAAe,EAAE,kBAAkB,CAAC,CAAC;IACzD,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;YACxC,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,WAAW;QACb,CAAC;IACH,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;IACzF,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -28,7 +28,27 @@ function grammarDirs() {
28
28
  const nodeModulesRust = path.join(repoRoot, 'node_modules', 'tree-sitter-rust');
29
29
  const nodeModulesPhp = path.join(repoRoot, 'node_modules', 'tree-sitter-php');
30
30
  const nodeModulesCSharp = path.join(repoRoot, 'node_modules', 'tree-sitter-c-sharp');
31
- return [distDir, nodeModulesWebTs, nodeModulesPy, nodeModulesGo, nodeModulesJava, nodeModulesRuby, nodeModulesRust, nodeModulesPhp, nodeModulesCSharp];
31
+ // 1.7+ Kotlin doesn't ship a prebuilt wasm in the npm tarball; we
32
+ // build it locally during `npm run build` (see scripts/copy-wasm.mjs's
33
+ // ensureBuiltWasm). At test time (vitest running off src/) the wasm
34
+ // sits inside the package directory, so probe it there too.
35
+ const nodeModulesKotlin = path.join(repoRoot, 'node_modules', 'tree-sitter-kotlin');
36
+ const nodeModulesSwift = path.join(repoRoot, 'node_modules', 'tree-sitter-swift');
37
+ const nodeModulesCpp = path.join(repoRoot, 'node_modules', 'tree-sitter-cpp');
38
+ return [
39
+ distDir,
40
+ nodeModulesWebTs,
41
+ nodeModulesPy,
42
+ nodeModulesGo,
43
+ nodeModulesJava,
44
+ nodeModulesRuby,
45
+ nodeModulesRust,
46
+ nodeModulesPhp,
47
+ nodeModulesCSharp,
48
+ nodeModulesKotlin,
49
+ nodeModulesSwift,
50
+ nodeModulesCpp,
51
+ ];
32
52
  }
33
53
  function findWasm(filename) {
34
54
  for (const dir of grammarDirs()) {
@@ -1 +1 @@
1
- {"version":3,"file":"treeSitterLoader.js","sourceRoot":"","sources":["../../../src/core/languages/treeSitterLoader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"treeSitterLoader.js","sourceRoot":"","sources":["../../../src/core/languages/treeSitterLoader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEnD;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,QAAQ,CAAC;AAEnD,IAAI,iBAAiB,GAAyB,IAAI,CAAC;AACnD,MAAM,aAAa,GAAG,IAAI,GAAG,EAA6B,CAAC;AAE3D;;;;;GAKG;AACH,SAAS,WAAW;IAClB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACtD,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,iBAAiB,CAAC,CAAC;IAChF,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,oBAAoB,CAAC,CAAC;IAChF,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,gBAAgB,CAAC,CAAC;IAC5E,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,kBAAkB,CAAC,CAAC;IAChF,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,kBAAkB,CAAC,CAAC;IAChF,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,kBAAkB,CAAC,CAAC;IAChF,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,iBAAiB,CAAC,CAAC;IAC9E,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,qBAAqB,CAAC,CAAC;IACrF,oEAAoE;IACpE,uEAAuE;IACvE,oEAAoE;IACpE,4DAA4D;IAC5D,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,oBAAoB,CAAC,CAAC;IACpF,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,mBAAmB,CAAC,CAAC;IAClF,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,iBAAiB,CAAC,CAAC;IAC9E,OAAO;QACL,OAAO;QACP,gBAAgB;QAChB,aAAa;QACb,aAAa;QACb,eAAe;QACf,eAAe;QACf,eAAe;QACf,cAAc;QACd,iBAAiB;QACjB,iBAAiB;QACjB,gBAAgB;QAChB,cAAc;KACf,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,QAAgB;IAChC,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,EAAE,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC3C,IAAI,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IAC9C,CAAC;IACD,MAAM,IAAI,KAAK,CACb,oBAAoB,QAAQ,eAAe,WAAW,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;QACrE,mDAAmD,CACtD,CAAC;AACJ,CAAC;AAED,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,iBAAiB;QAAE,OAAO,iBAAiB,CAAC;IAChD,iBAAiB,GAAG,MAAM,CAAC,IAAI,CAAC;QAC9B,UAAU,CAAC,IAAY;YACrB,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;YAClD,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC,CAAC;IACH,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,YAAoB;IACrD,MAAM,gBAAgB,EAAE,CAAC;IACzB,IAAI,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,CAAC,KAAK,IAAuB,EAAE;YACtC,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;YACxC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACrC,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC,CAAC,EAAE,CAAC;QACL,aAAa,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,yDAAyD;AACzD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,YAAoB;IACxD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;IAC5B,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,qBAAqB;IACnC,iBAAiB,GAAG,IAAI,CAAC;IACzB,aAAa,CAAC,KAAK,EAAE,CAAC;AACxB,CAAC"}
@@ -127,6 +127,36 @@ export declare function recordRun(memory: ProjectMemory, currentIds: Iterable<st
127
127
  * These are the suggestions to surface in the doctor's "Tip" line.
128
128
  */
129
129
  export declare function findStableRules(memory: ProjectMemory): RuleObservation[];
130
+ /**
131
+ * 1.7+ — Confidence label for a rule, derived from observed fix-rate.
132
+ *
133
+ * "high" — the user has actively fixed instances of this rule
134
+ * (`fixedCount > 0`). Suggestions for this rule should be
135
+ * surfaced prominently; the user has shown they care.
136
+ * "low" — the rule has surfaced across ≥ STABLE_RULE_RUN_COUNT runs
137
+ * over ≥ STABLE_RULE_DAYS days but has never been fixed
138
+ * (`fixedCount === 0`). The user is effectively tolerating
139
+ * it; suggestions should be deprioritized.
140
+ * "medium" — anything in between (insufficient signal, or recent rule).
141
+ *
142
+ * Returns "medium" if the memory has no observation for this rule yet.
143
+ *
144
+ * Project Memory's "loop #3" — rules with low confidence in a given repo
145
+ * stop crowding out rules the user actually acts on. The advisor stays
146
+ * useful instead of becoming background noise.
147
+ */
148
+ export type RuleConfidence = 'high' | 'medium' | 'low';
149
+ export declare function computeRuleConfidence(memory: ProjectMemory, ruleId: string): RuleConfidence;
150
+ /**
151
+ * 1.7+ — A simple numeric score in [0, 1] that ranks suggestions by how
152
+ * likely the user is to act on them. Pairs with `computeRuleConfidence`
153
+ * for callers that want a single comparable number for sorting.
154
+ *
155
+ * high → 1.0 + (small bonus for higher fix-rate)
156
+ * medium → 0.5 (neutral)
157
+ * low → 0.0 + (small penalty for run-count that ages further)
158
+ */
159
+ export declare function computeRuleConfidenceScore(memory: ProjectMemory, ruleId: string): number;
130
160
  /** Drop a single rule's history. Returns true if the rule existed. */
131
161
  export declare function forgetRule(memory: ProjectMemory, ruleId: string): boolean;
132
162
  /**
@@ -167,6 +167,48 @@ export function findStableRules(memory) {
167
167
  out.sort((a, b) => b.runCount - a.runCount);
168
168
  return out;
169
169
  }
170
+ export function computeRuleConfidence(memory, ruleId) {
171
+ const obs = memory.rules[ruleId];
172
+ if (!obs)
173
+ return 'medium';
174
+ if (obs.fixedCount > 0)
175
+ return 'high';
176
+ if (obs.runCount < STABLE_RULE_RUN_COUNT)
177
+ return 'medium';
178
+ const ageMs = Date.now() - Date.parse(obs.firstSeenAt);
179
+ const stableMs = STABLE_RULE_DAYS * MS_PER_DAY;
180
+ // Guard against future-dated timestamps (clock skew, corrupt memory):
181
+ // a negative age must NOT trip the "low" branch — fall back to medium
182
+ // until the wall clock catches up or the memory is regenerated.
183
+ if (Number.isFinite(ageMs) && ageMs > 0 && ageMs >= stableMs)
184
+ return 'low';
185
+ return 'medium';
186
+ }
187
+ /**
188
+ * 1.7+ — A simple numeric score in [0, 1] that ranks suggestions by how
189
+ * likely the user is to act on them. Pairs with `computeRuleConfidence`
190
+ * for callers that want a single comparable number for sorting.
191
+ *
192
+ * high → 1.0 + (small bonus for higher fix-rate)
193
+ * medium → 0.5 (neutral)
194
+ * low → 0.0 + (small penalty for run-count that ages further)
195
+ */
196
+ export function computeRuleConfidenceScore(memory, ruleId) {
197
+ const obs = memory.rules[ruleId];
198
+ if (!obs)
199
+ return 0.5;
200
+ const label = computeRuleConfidence(memory, ruleId);
201
+ if (label === 'high') {
202
+ const fixRate = obs.runCount > 0 ? obs.fixedCount / obs.runCount : 0;
203
+ return Math.min(1, 0.6 + 0.4 * fixRate);
204
+ }
205
+ if (label === 'low') {
206
+ // The longer a rule has been ignored, the lower its score.
207
+ const decay = Math.min(1, obs.runCount / 10);
208
+ return Math.max(0, 0.2 - 0.2 * decay);
209
+ }
210
+ return 0.5;
211
+ }
170
212
  /** Drop a single rule's history. Returns true if the rule existed. */
171
213
  export function forgetRule(memory, ruleId) {
172
214
  if (!memory.rules[ruleId])
@@ -1 +1 @@
1
- {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/core/memory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/core/memory.ts"],"names":[],"mappings":"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"}
@@ -75,16 +75,35 @@ export declare function recordTouch(session: Session, file: string, source: Touc
75
75
  /**
76
76
  * Append an event to the session's bounded log. The log keeps the last
77
77
  * MAX_EVENTS entries and drops the oldest when over capacity.
78
+ *
79
+ * 1.7+ — bound BEFORE append (rather than after) so that two interleaved
80
+ * recordEvent calls can't both observe `length === MAX_EVENTS`, both push,
81
+ * and leave the array one-over. Drops the single oldest entry on overflow,
82
+ * which keeps the steady-state size at exactly MAX_EVENTS.
78
83
  */
79
84
  export declare function recordEvent(session: Session, kind: string, data?: Record<string, unknown>): void;
80
85
  /**
81
86
  * Persist the session to disk. Best-effort; failures are swallowed so
82
87
  * a transient write error doesn't break the calling tool.
83
88
  *
84
- * Last-write-wins semantics for now. If two MCP servers run against the
85
- * same repo concurrently, the later writer overwrites the earlier; this
86
- * is acceptable for the v1 scope (the multi-process case is rare and the
87
- * downside is just a slightly stale touched-file count, not corruption).
89
+ * 1.8+ uses atomicWriteFile (tmp + fsync + rename + parent-dir fsync)
90
+ * instead of a naive fs.writeFile. Two reasons:
91
+ *
92
+ * 1. Crash safety. A naive writeFile can leave the file truncated to
93
+ * zero bytes if the process is killed mid-write — and on next
94
+ * startup the corrupt file is treated as a new session, losing the
95
+ * touched-file map and event log.
96
+ *
97
+ * 2. Concurrent processes. The previous "last-write-wins" comment was
98
+ * true at the *file* level: two writers could clobber each other.
99
+ * With atomic rename the in-flight tmp paths are unique (random
100
+ * UUID suffix), so the only collision point is the rename — which
101
+ * is atomic at the journal-record level on every supported FS.
102
+ * Either A wins or B wins; neither sees a half-written read.
103
+ *
104
+ * The session payload is JSON; we still don't merge concurrent writes
105
+ * (one writer's view may shadow another's recent touches by a few
106
+ * seconds), but corruption is no longer possible.
88
107
  */
89
108
  export declare function saveSession(rootPath: string, session: Session): Promise<void>;
90
109
  /**
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
+ import { atomicWriteFile } from '../utils/atomicWrite.js';
4
5
  /**
5
6
  * Session — durable cross-invocation state for projscan (1.4+).
6
7
  *
@@ -99,6 +100,11 @@ export function recordTouch(session, file, source) {
99
100
  /**
100
101
  * Append an event to the session's bounded log. The log keeps the last
101
102
  * MAX_EVENTS entries and drops the oldest when over capacity.
103
+ *
104
+ * 1.7+ — bound BEFORE append (rather than after) so that two interleaved
105
+ * recordEvent calls can't both observe `length === MAX_EVENTS`, both push,
106
+ * and leave the array one-over. Drops the single oldest entry on overflow,
107
+ * which keeps the steady-state size at exactly MAX_EVENTS.
102
108
  */
103
109
  export function recordEvent(session, kind, data) {
104
110
  const event = {
@@ -106,27 +112,41 @@ export function recordEvent(session, kind, data) {
106
112
  kind,
107
113
  ...(data ? { data } : {}),
108
114
  };
109
- session.events.push(event);
110
- if (session.events.length > MAX_EVENTS) {
111
- session.events = session.events.slice(-MAX_EVENTS);
115
+ if (session.events.length >= MAX_EVENTS) {
116
+ session.events = session.events.slice(-(MAX_EVENTS - 1));
112
117
  }
118
+ session.events.push(event);
113
119
  session.lastActivityAt = event.at;
114
120
  }
115
121
  /**
116
122
  * Persist the session to disk. Best-effort; failures are swallowed so
117
123
  * a transient write error doesn't break the calling tool.
118
124
  *
119
- * Last-write-wins semantics for now. If two MCP servers run against the
120
- * same repo concurrently, the later writer overwrites the earlier; this
121
- * is acceptable for the v1 scope (the multi-process case is rare and the
122
- * downside is just a slightly stale touched-file count, not corruption).
125
+ * 1.8+ uses atomicWriteFile (tmp + fsync + rename + parent-dir fsync)
126
+ * instead of a naive fs.writeFile. Two reasons:
127
+ *
128
+ * 1. Crash safety. A naive writeFile can leave the file truncated to
129
+ * zero bytes if the process is killed mid-write — and on next
130
+ * startup the corrupt file is treated as a new session, losing the
131
+ * touched-file map and event log.
132
+ *
133
+ * 2. Concurrent processes. The previous "last-write-wins" comment was
134
+ * true at the *file* level: two writers could clobber each other.
135
+ * With atomic rename the in-flight tmp paths are unique (random
136
+ * UUID suffix), so the only collision point is the rename — which
137
+ * is atomic at the journal-record level on every supported FS.
138
+ * Either A wins or B wins; neither sees a half-written read.
139
+ *
140
+ * The session payload is JSON; we still don't merge concurrent writes
141
+ * (one writer's view may shadow another's recent touches by a few
142
+ * seconds), but corruption is no longer possible.
123
143
  */
124
144
  export async function saveSession(rootPath, session) {
125
145
  try {
126
146
  const dir = path.join(rootPath, SESSION_DIR);
127
147
  await fs.mkdir(dir, { recursive: true });
128
148
  const filePath = sessionFilePath(rootPath);
129
- await fs.writeFile(filePath, JSON.stringify(session, null, 2), 'utf-8');
149
+ await atomicWriteFile(filePath, JSON.stringify(session, null, 2));
130
150
  }
131
151
  catch {
132
152
  // Swallow — sessions are best-effort.
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"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"}