pi-lens 2.2.9 → 3.0.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 (304) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/README.md +709 -519
  3. package/clients/__tests__/file-time.test.js +216 -0
  4. package/clients/__tests__/file-time.test.ts +276 -0
  5. package/clients/__tests__/format-service.test.js +245 -0
  6. package/clients/__tests__/format-service.test.ts +339 -0
  7. package/clients/__tests__/formatters.test.js +271 -0
  8. package/clients/__tests__/formatters.test.ts +401 -0
  9. package/clients/amain-types.js +164 -0
  10. package/clients/amain-types.ts +165 -0
  11. package/clients/architect-client.js +56 -12
  12. package/clients/architect-client.ts +81 -16
  13. package/clients/ast-grep-client.js +2 -2
  14. package/clients/ast-grep-client.ts +14 -39
  15. package/clients/ast-grep-parser.ts +1 -1
  16. package/clients/ast-grep-rule-manager.js +8 -0
  17. package/clients/ast-grep-rule-manager.ts +10 -1
  18. package/clients/ast-grep-types.js +9 -0
  19. package/clients/ast-grep-types.ts +106 -0
  20. package/clients/auto-loop.js +10 -0
  21. package/clients/auto-loop.ts +14 -1
  22. package/clients/biome-client.js +81 -19
  23. package/clients/biome-client.ts +103 -22
  24. package/clients/bus/bus.js +191 -0
  25. package/clients/bus/bus.ts +251 -0
  26. package/clients/bus/events.js +214 -0
  27. package/clients/bus/events.ts +279 -0
  28. package/clients/bus/index.js +8 -0
  29. package/clients/bus/index.ts +9 -0
  30. package/clients/bus/integration.js +158 -0
  31. package/clients/bus/integration.ts +214 -0
  32. package/clients/complexity-client.js +13 -7
  33. package/clients/complexity-client.ts +13 -7
  34. package/clients/config-validator.js +465 -0
  35. package/clients/config-validator.ts +558 -0
  36. package/clients/dependency-checker.js +4 -10
  37. package/clients/dependency-checker.ts +4 -10
  38. package/clients/dispatch/__tests__/autofix-integration.test.js +245 -0
  39. package/clients/dispatch/__tests__/autofix-integration.test.ts +300 -0
  40. package/clients/dispatch/__tests__/runner-registration.test.js +236 -0
  41. package/clients/dispatch/__tests__/runner-registration.test.ts +282 -0
  42. package/clients/dispatch/bus-dispatcher.js +177 -0
  43. package/clients/dispatch/bus-dispatcher.ts +251 -0
  44. package/clients/dispatch/dispatcher.edge.test.js +82 -0
  45. package/clients/dispatch/dispatcher.edge.test.ts +100 -0
  46. package/clients/dispatch/dispatcher.format.test.js +46 -0
  47. package/clients/dispatch/dispatcher.format.test.ts +58 -0
  48. package/clients/dispatch/dispatcher.inline.test.js +74 -0
  49. package/clients/dispatch/dispatcher.inline.test.ts +93 -0
  50. package/clients/dispatch/dispatcher.js +19 -53
  51. package/clients/dispatch/dispatcher.ts +20 -67
  52. package/clients/dispatch/plan.js +9 -4
  53. package/clients/dispatch/plan.ts +9 -4
  54. package/clients/dispatch/runners/architect.js +21 -7
  55. package/clients/dispatch/runners/architect.test.js +138 -0
  56. package/clients/dispatch/runners/architect.test.ts +162 -0
  57. package/clients/dispatch/runners/architect.ts +22 -7
  58. package/clients/dispatch/runners/ast-grep-napi.js +462 -0
  59. package/clients/dispatch/runners/ast-grep-napi.test.js +111 -0
  60. package/clients/dispatch/runners/ast-grep-napi.test.ts +133 -0
  61. package/clients/dispatch/runners/ast-grep-napi.ts +506 -0
  62. package/clients/dispatch/runners/ast-grep.js +62 -19
  63. package/clients/dispatch/runners/ast-grep.ts +70 -18
  64. package/clients/dispatch/runners/biome.js +29 -53
  65. package/clients/dispatch/runners/biome.ts +29 -63
  66. package/clients/dispatch/runners/config-validation.js +67 -0
  67. package/clients/dispatch/runners/config-validation.ts +82 -0
  68. package/clients/dispatch/runners/go-vet.js +4 -28
  69. package/clients/dispatch/runners/go-vet.ts +4 -32
  70. package/clients/dispatch/runners/index.js +30 -10
  71. package/clients/dispatch/runners/index.ts +30 -10
  72. package/clients/dispatch/runners/oxlint.js +141 -0
  73. package/clients/dispatch/runners/oxlint.test.js +230 -0
  74. package/clients/dispatch/runners/oxlint.test.ts +303 -0
  75. package/clients/dispatch/runners/oxlint.ts +175 -0
  76. package/clients/dispatch/runners/pyright.js +40 -70
  77. package/clients/dispatch/runners/pyright.test.js +16 -2
  78. package/clients/dispatch/runners/pyright.test.ts +14 -2
  79. package/clients/dispatch/runners/pyright.ts +48 -91
  80. package/clients/dispatch/runners/python-slop.js +97 -0
  81. package/clients/dispatch/runners/python-slop.test.js +203 -0
  82. package/clients/dispatch/runners/python-slop.test.ts +298 -0
  83. package/clients/dispatch/runners/python-slop.ts +124 -0
  84. package/clients/dispatch/runners/ruff.js +18 -71
  85. package/clients/dispatch/runners/ruff.ts +19 -79
  86. package/clients/dispatch/runners/rust-clippy.js +28 -32
  87. package/clients/dispatch/runners/rust-clippy.ts +29 -31
  88. package/clients/dispatch/runners/scan_codebase.test.js +89 -0
  89. package/clients/dispatch/runners/scan_codebase.test.ts +105 -0
  90. package/clients/dispatch/runners/shellcheck.js +147 -0
  91. package/clients/dispatch/runners/shellcheck.test.js +98 -0
  92. package/clients/dispatch/runners/shellcheck.test.ts +129 -0
  93. package/clients/dispatch/runners/shellcheck.ts +188 -0
  94. package/clients/dispatch/runners/similarity.js +230 -0
  95. package/clients/dispatch/runners/similarity.ts +339 -0
  96. package/clients/dispatch/runners/spellcheck.js +106 -0
  97. package/clients/dispatch/runners/spellcheck.test.js +158 -0
  98. package/clients/dispatch/runners/spellcheck.test.ts +214 -0
  99. package/clients/dispatch/runners/spellcheck.ts +136 -0
  100. package/clients/dispatch/runners/tree-sitter.js +107 -0
  101. package/clients/dispatch/runners/tree-sitter.ts +135 -0
  102. package/clients/dispatch/runners/ts-lsp.js +104 -33
  103. package/clients/dispatch/runners/ts-lsp.ts +120 -38
  104. package/clients/dispatch/runners/ts-slop.js +113 -0
  105. package/clients/dispatch/runners/ts-slop.test.js +180 -0
  106. package/clients/dispatch/runners/ts-slop.test.ts +230 -0
  107. package/clients/dispatch/runners/ts-slop.ts +142 -0
  108. package/clients/dispatch/runners/utils/diagnostic-parsers.js +134 -0
  109. package/clients/dispatch/runners/utils/diagnostic-parsers.ts +186 -0
  110. package/clients/dispatch/runners/utils/runner-helpers.js +115 -0
  111. package/clients/dispatch/runners/utils/runner-helpers.ts +167 -0
  112. package/clients/dispatch/runners/utils.js +2 -4
  113. package/clients/dispatch/runners/utils.ts +2 -4
  114. package/clients/dispatch/types.ts +1 -1
  115. package/clients/dispatch/utils/format-utils.js +49 -0
  116. package/clients/dispatch/utils/format-utils.ts +60 -0
  117. package/clients/dogfood.test.js +201 -0
  118. package/clients/dogfood.test.ts +269 -0
  119. package/clients/file-time.js +152 -0
  120. package/clients/file-time.ts +208 -0
  121. package/clients/file-utils.js +40 -0
  122. package/clients/file-utils.ts +44 -0
  123. package/clients/fix-scanners.js +10 -20
  124. package/clients/fix-scanners.ts +10 -22
  125. package/clients/format-service.js +172 -0
  126. package/clients/format-service.ts +254 -0
  127. package/clients/formatters.js +435 -0
  128. package/clients/formatters.ts +508 -0
  129. package/clients/go-client.js +5 -14
  130. package/clients/go-client.ts +5 -13
  131. package/clients/installer/index.js +356 -0
  132. package/clients/installer/index.ts +426 -0
  133. package/clients/jscpd-client.js +11 -9
  134. package/clients/jscpd-client.ts +12 -8
  135. package/clients/knip-client.js +3 -7
  136. package/clients/knip-client.ts +3 -6
  137. package/clients/lsp/__tests__/client.test.js +325 -0
  138. package/clients/lsp/__tests__/client.test.ts +434 -0
  139. package/clients/lsp/__tests__/config.test.js +166 -0
  140. package/clients/lsp/__tests__/config.test.ts +209 -0
  141. package/clients/lsp/__tests__/error-recovery.test.js +213 -0
  142. package/clients/lsp/__tests__/error-recovery.test.ts +279 -0
  143. package/clients/lsp/__tests__/integration.test.js +127 -0
  144. package/clients/lsp/__tests__/integration.test.ts +160 -0
  145. package/clients/lsp/__tests__/launch.test.js +260 -0
  146. package/clients/lsp/__tests__/launch.test.ts +329 -0
  147. package/clients/lsp/__tests__/server.test.js +259 -0
  148. package/clients/lsp/__tests__/server.test.ts +332 -0
  149. package/clients/lsp/__tests__/service.test.js +417 -0
  150. package/clients/lsp/__tests__/service.test.ts +499 -0
  151. package/clients/lsp/client.js +235 -0
  152. package/clients/lsp/client.ts +328 -0
  153. package/clients/lsp/config.js +115 -0
  154. package/clients/lsp/config.ts +149 -0
  155. package/clients/lsp/index.js +222 -0
  156. package/clients/lsp/index.ts +280 -0
  157. package/clients/lsp/installer/index.js +391 -0
  158. package/clients/lsp/interactive-install.js +210 -0
  159. package/clients/lsp/interactive-install.ts +251 -0
  160. package/clients/lsp/language.js +170 -0
  161. package/clients/lsp/language.ts +216 -0
  162. package/clients/lsp/launch.js +174 -0
  163. package/clients/lsp/launch.ts +240 -0
  164. package/clients/lsp/lsp/launch.js +116 -0
  165. package/clients/lsp/lsp/server.js +532 -0
  166. package/clients/lsp/lsp-index.js +10 -0
  167. package/clients/lsp/lsp-index.ts +11 -0
  168. package/clients/lsp/path-utils.js +48 -0
  169. package/clients/lsp/path-utils.ts +52 -0
  170. package/clients/lsp/server.js +615 -0
  171. package/clients/lsp/server.ts +800 -0
  172. package/clients/lsp/test-py-spawn/requirements.txt +1 -0
  173. package/clients/lsp/test-py-spawn/test.py +3 -0
  174. package/clients/lsp/test-py-svc/requirements.txt +1 -0
  175. package/clients/lsp/test-py-svc/test.py +3 -0
  176. package/clients/lsp/test-python-project/requirements.txt +1 -0
  177. package/clients/lsp/test-python-project/test.py +5 -0
  178. package/clients/metrics-history.js +2 -2
  179. package/clients/metrics-history.ts +2 -2
  180. package/clients/production-readiness.js +522 -0
  181. package/clients/production-readiness.ts +556 -0
  182. package/clients/project-index.js +255 -0
  183. package/clients/project-index.ts +383 -0
  184. package/clients/project-metadata.js +531 -0
  185. package/clients/project-metadata.ts +624 -0
  186. package/clients/ruff-client.js +56 -16
  187. package/clients/ruff-client.ts +72 -15
  188. package/clients/runner-tracker.js +152 -0
  189. package/clients/runner-tracker.ts +213 -0
  190. package/clients/rust-client.js +4 -11
  191. package/clients/rust-client.ts +5 -11
  192. package/clients/safe-spawn.js +96 -0
  193. package/clients/safe-spawn.ts +128 -0
  194. package/clients/scan-architectural-debt.js +3 -6
  195. package/clients/scan-architectural-debt.ts +3 -6
  196. package/clients/scan-utils.js +5 -20
  197. package/clients/scan-utils.ts +5 -29
  198. package/clients/secrets-scanner.js +3 -17
  199. package/clients/secrets-scanner.ts +4 -20
  200. package/clients/services/__tests__/effect-integration.test.js +86 -0
  201. package/clients/services/__tests__/effect-integration.test.ts +111 -0
  202. package/clients/services/effect-integration.js +194 -0
  203. package/clients/services/effect-integration.ts +268 -0
  204. package/clients/services/index.js +7 -0
  205. package/clients/services/index.ts +8 -0
  206. package/clients/services/runner-service.js +105 -0
  207. package/clients/services/runner-service.ts +179 -0
  208. package/clients/sg-runner.js +87 -13
  209. package/clients/sg-runner.ts +97 -13
  210. package/clients/state-matrix.js +160 -0
  211. package/clients/state-matrix.ts +202 -0
  212. package/clients/subprocess-client.js +10 -9
  213. package/clients/subprocess-client.ts +10 -8
  214. package/clients/test-runner-client.js +3 -7
  215. package/clients/test-runner-client.ts +3 -6
  216. package/clients/tool-availability.js +4 -10
  217. package/clients/tool-availability.ts +4 -9
  218. package/clients/tree-sitter-client.js +564 -0
  219. package/clients/tree-sitter-client.ts +797 -0
  220. package/clients/tree-sitter-query-loader.js +355 -0
  221. package/clients/tree-sitter-query-loader.ts +425 -0
  222. package/clients/type-coverage-client.js +3 -7
  223. package/clients/type-coverage-client.ts +3 -6
  224. package/clients/typescript-client.codefix.test.js +157 -0
  225. package/clients/typescript-client.codefix.test.ts +186 -0
  226. package/clients/typescript-client.js +43 -0
  227. package/clients/typescript-client.ts +98 -0
  228. package/commands/booboo.js +799 -219
  229. package/commands/booboo.ts +1004 -225
  230. package/commands/clients/ast-grep-client.js +250 -0
  231. package/commands/clients/ast-grep-parser.js +86 -0
  232. package/commands/clients/ast-grep-rule-manager.js +91 -0
  233. package/commands/clients/ast-grep-types.js +9 -0
  234. package/commands/clients/biome-client.js +380 -0
  235. package/commands/clients/complexity-client.js +667 -0
  236. package/commands/clients/file-kinds.js +177 -0
  237. package/commands/clients/file-utils.js +40 -0
  238. package/commands/clients/jscpd-client.js +169 -0
  239. package/commands/clients/knip-client.js +211 -0
  240. package/commands/clients/ruff-client.js +297 -0
  241. package/commands/clients/safe-spawn.js +88 -0
  242. package/commands/clients/scan-utils.js +83 -0
  243. package/commands/clients/sg-runner.js +190 -0
  244. package/commands/clients/types.js +11 -0
  245. package/commands/clients/typescript-client.js +505 -0
  246. package/commands/fix-from-booboo.js +398 -0
  247. package/commands/fix-from-booboo.ts +485 -0
  248. package/commands/fix-simplified.js +618 -0
  249. package/commands/fix-simplified.ts +768 -0
  250. package/commands/rate.js +10 -14
  251. package/commands/rate.ts +9 -16
  252. package/default-architect.yaml +59 -15
  253. package/index.ts +342 -429
  254. package/package.json +16 -3
  255. package/rules/ast-grep-rules/rules/empty-catch.yml +38 -13
  256. package/rules/ast-grep-rules/rules/no-array-constructor.yml +1 -0
  257. package/rules/ast-grep-rules/rules/no-debugger.yml +2 -0
  258. package/rules/python-slop-rules/.sgconfig.yml +4 -0
  259. package/rules/python-slop-rules/rules/slop-rules.yml +647 -0
  260. package/rules/tree-sitter-queries/python/bare-except.yml +54 -0
  261. package/rules/tree-sitter-queries/python/eval-exec.yml +50 -0
  262. package/rules/tree-sitter-queries/python/is-vs-equals.yml +60 -0
  263. package/rules/tree-sitter-queries/python/mutable-default-arg.yml +57 -0
  264. package/rules/tree-sitter-queries/python/unreachable-except.yml +60 -0
  265. package/rules/tree-sitter-queries/python/wildcard-import.yml +46 -0
  266. package/rules/tree-sitter-queries/tsx/dangerously-set-inner-html.yml +63 -0
  267. package/rules/tree-sitter-queries/typescript/await-in-loop.yml +56 -0
  268. package/rules/tree-sitter-queries/typescript/console-statement.yml +47 -0
  269. package/rules/tree-sitter-queries/typescript/debugger.yml +47 -0
  270. package/rules/tree-sitter-queries/typescript/deep-nesting.yml +117 -0
  271. package/rules/tree-sitter-queries/typescript/deep-promise-chain.yml +73 -0
  272. package/rules/tree-sitter-queries/typescript/empty-catch.yml +64 -0
  273. package/rules/tree-sitter-queries/typescript/eval.yml +48 -0
  274. package/rules/tree-sitter-queries/typescript/hardcoded-secrets.yml +78 -0
  275. package/rules/tree-sitter-queries/typescript/long-parameter-list.yml +62 -0
  276. package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +49 -0
  277. package/rules/tree-sitter-queries/typescript/nested-ternary.yml +45 -0
  278. package/rules/ts-slop-rules/.sgconfig.yml +4 -0
  279. package/rules/ts-slop-rules/rules/in-correct-optional-input-type.yml +10 -0
  280. package/rules/ts-slop-rules/rules/jwt-no-verify.yml +13 -0
  281. package/rules/ts-slop-rules/rules/no-architecture-violation.yml +10 -0
  282. package/rules/ts-slop-rules/rules/no-case-declarations.yml +10 -0
  283. package/rules/ts-slop-rules/rules/no-dangerously-set-inner-html.yml +10 -0
  284. package/rules/ts-slop-rules/rules/no-debugger.yml +10 -0
  285. package/rules/ts-slop-rules/rules/no-dupe-args.yml +10 -0
  286. package/rules/ts-slop-rules/rules/no-dupe-class-members.yml +10 -0
  287. package/rules/ts-slop-rules/rules/no-dupe-keys.yml +10 -0
  288. package/rules/ts-slop-rules/rules/no-eval.yml +13 -0
  289. package/rules/ts-slop-rules/rules/no-hardcoded-secrets.yml +12 -0
  290. package/rules/ts-slop-rules/rules/no-implied-eval.yml +12 -0
  291. package/rules/ts-slop-rules/rules/no-inner-html.yml +13 -0
  292. package/rules/ts-slop-rules/rules/no-javascript-url.yml +10 -0
  293. package/rules/ts-slop-rules/rules/no-mutable-default.yml +10 -0
  294. package/rules/ts-slop-rules/rules/no-nested-links.yml +12 -0
  295. package/rules/ts-slop-rules/rules/no-new-symbol.yml +10 -0
  296. package/rules/ts-slop-rules/rules/no-new-wrappers.yml +13 -0
  297. package/rules/ts-slop-rules/rules/no-open-redirect.yml +16 -0
  298. package/rules/ts-slop-rules/rules/slop-rules.yml +455 -0
  299. package/rules/ts-slop-rules/rules/weak-rsa-key.yml +12 -0
  300. package/skills/ast-grep/SKILL.md +182 -0
  301. package/clients/dispatch/runners/secrets.js +0 -109
  302. package/commands/fix.js +0 -244
  303. package/commands/fix.ts +0 -373
  304. package/rules/ast-grep-rules/rules/no-lonely-if.yml +0 -13
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Amain State Types: 57 syntax types + 15 token types
3
+ *
4
+ * Adapted from Amain paper's Java AST structure for TypeScript.
5
+ * 57 syntax types (non-leaf AST nodes) + 15 token types (leaf categories) = 72 states
6
+ *
7
+ * Reference: https://github.com/CGCL-codes/Amain (ASE 2022)
8
+ */
9
+ import * as ts from "typescript";
10
+ // ============================================================================
11
+ // 57 Syntax Types (non-leaf AST nodes, indices 0-56)
12
+ // ============================================================================
13
+ export const SYNTAX_TYPES = [
14
+ // Declarations (0-9)
15
+ "FunctionDeclaration", // 0
16
+ "ArrowFunction", // 1
17
+ "FunctionExpression", // 2
18
+ "ClassDeclaration", // 3
19
+ "InterfaceDeclaration", // 4
20
+ "TypeAliasDeclaration", // 5
21
+ "EnumDeclaration", // 6
22
+ "MethodDeclaration", // 7
23
+ "Constructor", // 8
24
+ "GetAccessor", // 9
25
+ // More declarations (10-19)
26
+ "SetAccessor", // 10
27
+ "PropertyDeclaration", // 11
28
+ "Parameter", // 12
29
+ "VariableDeclaration", // 13
30
+ "ModuleDeclaration", // 14
31
+ "ImportDeclaration", // 15
32
+ "ExportDeclaration", // 16
33
+ "NamespaceExportDeclaration", // 17
34
+ "ImportClause", // 18
35
+ "NamespaceImport", // 19
36
+ // Statements (20-39)
37
+ "IfStatement", // 20
38
+ "ForStatement", // 21
39
+ "ForOfStatement", // 22
40
+ "ForInStatement", // 23
41
+ "WhileStatement", // 24
42
+ "DoWhileStatement", // 25
43
+ "SwitchStatement", // 26
44
+ "CaseClause", // 27
45
+ "DefaultClause", // 28
46
+ "TryStatement", // 29
47
+ "CatchClause", // 30
48
+ "ThrowStatement", // 31
49
+ "ReturnStatement", // 32
50
+ "BreakStatement", // 33
51
+ "ContinueStatement", // 34
52
+ "Block", // 35
53
+ "EmptyStatement", // 36
54
+ // More statements (37-39)
55
+ "DebuggerStatement", // 37
56
+ "LabeledStatement", // 38
57
+ "WithStatement", // 39
58
+ // Expressions (40-56)
59
+ "BinaryExpression", // 40
60
+ "UnaryExpression", // 41
61
+ "PrefixUnaryExpression", // 42
62
+ "PostfixUnaryExpression", // 43
63
+ "ConditionalExpression", // 44
64
+ "CallExpression", // 45
65
+ "PropertyAccessExpression", // 46
66
+ "ElementAccessExpression", // 47
67
+ "NewExpression", // 48
68
+ "ParenthesizedExpression", // 49
69
+ "TypeAssertionExpression", // 50
70
+ "AsExpression", // 51
71
+ "NonNullExpression", // 52
72
+ "TemplateExpression", // 53
73
+ "ArrayLiteralExpression", // 54
74
+ "ObjectLiteralExpression", // 55
75
+ "ExpressionStatement", // 56
76
+ ];
77
+ // ============================================================================
78
+ // 15 Token Types (leaf node categories, indices 57-71)
79
+ // ============================================================================
80
+ export const TOKEN_TYPES = [
81
+ "Identifier", // 57
82
+ "StringLiteral", // 58
83
+ "NumericLiteral", // 59
84
+ "TrueKeyword", // 60
85
+ "FalseKeyword", // 61
86
+ "NullKeyword", // 62
87
+ "UndefinedKeyword", // 63
88
+ "ThisKeyword", // 64
89
+ "SuperKeyword", // 65
90
+ "RegularExpressionLiteral", // 66
91
+ "NoSubstitutionTemplateLiteral", // 67
92
+ "TemplateHead", // 68
93
+ "TemplateMiddle", // 69
94
+ "TemplateTail", // 70
95
+ "ComputedPropertyName", // 71
96
+ ];
97
+ // ============================================================================
98
+ // Constants
99
+ // ============================================================================
100
+ export const NUM_SYNTAX = SYNTAX_TYPES.length; // 57
101
+ export const NUM_TOKEN = TOKEN_TYPES.length; // 15
102
+ export const NUM_STATES = NUM_SYNTAX + NUM_TOKEN; // 72
103
+ // ============================================================================
104
+ // State Index Mapping
105
+ // ============================================================================
106
+ /**
107
+ * Map a TypeScript AST node to its Amain state index (0-71)
108
+ */
109
+ export function getStateIndex(node) {
110
+ // Try syntax types first (0-56)
111
+ const kindName = ts.SyntaxKind[node.kind];
112
+ const syntaxIdx = SYNTAX_TYPES.indexOf(kindName);
113
+ if (syntaxIdx !== -1)
114
+ return syntaxIdx;
115
+ // Map to token types (57-71) based on node kind
116
+ if (ts.isIdentifier(node))
117
+ return 57;
118
+ if (ts.isStringLiteral(node))
119
+ return 58;
120
+ if (ts.isNumericLiteral(node))
121
+ return 59;
122
+ if (node.kind === ts.SyntaxKind.TrueKeyword)
123
+ return 60;
124
+ if (node.kind === ts.SyntaxKind.FalseKeyword)
125
+ return 61;
126
+ if (node.kind === ts.SyntaxKind.NullKeyword)
127
+ return 62;
128
+ if (node.kind === ts.SyntaxKind.UndefinedKeyword)
129
+ return 63;
130
+ if (node.kind === ts.SyntaxKind.ThisKeyword)
131
+ return 64;
132
+ if (node.kind === ts.SyntaxKind.SuperKeyword)
133
+ return 65;
134
+ if (node.kind === ts.SyntaxKind.RegularExpressionLiteral)
135
+ return 66;
136
+ if (node.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral)
137
+ return 67;
138
+ if (node.kind === ts.SyntaxKind.TemplateHead)
139
+ return 68;
140
+ if (node.kind === ts.SyntaxKind.TemplateMiddle)
141
+ return 69;
142
+ if (node.kind === ts.SyntaxKind.TemplateTail)
143
+ return 70;
144
+ if (node.kind === ts.SyntaxKind.ComputedPropertyName)
145
+ return 71;
146
+ // Default: treat as identifier for any other leaf node
147
+ return 57;
148
+ }
149
+ /**
150
+ * Check if a node is a syntax type (non-leaf) vs token type (leaf)
151
+ */
152
+ export function isSyntaxNode(node) {
153
+ return getStateIndex(node) < NUM_SYNTAX;
154
+ }
155
+ /**
156
+ * Get state name for debugging
157
+ */
158
+ export function getStateName(index) {
159
+ if (index < NUM_SYNTAX)
160
+ return SYNTAX_TYPES[index];
161
+ if (index < NUM_STATES)
162
+ return TOKEN_TYPES[index - NUM_SYNTAX];
163
+ return "Unknown";
164
+ }
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Amain State Types: 57 syntax types + 15 token types
3
+ *
4
+ * Adapted from Amain paper's Java AST structure for TypeScript.
5
+ * 57 syntax types (non-leaf AST nodes) + 15 token types (leaf categories) = 72 states
6
+ *
7
+ * Reference: https://github.com/CGCL-codes/Amain (ASE 2022)
8
+ */
9
+
10
+ import * as ts from "typescript";
11
+
12
+ // ============================================================================
13
+ // 57 Syntax Types (non-leaf AST nodes, indices 0-56)
14
+ // ============================================================================
15
+
16
+ export const SYNTAX_TYPES = [
17
+ // Declarations (0-9)
18
+ "FunctionDeclaration", // 0
19
+ "ArrowFunction", // 1
20
+ "FunctionExpression", // 2
21
+ "ClassDeclaration", // 3
22
+ "InterfaceDeclaration", // 4
23
+ "TypeAliasDeclaration", // 5
24
+ "EnumDeclaration", // 6
25
+ "MethodDeclaration", // 7
26
+ "Constructor", // 8
27
+ "GetAccessor", // 9
28
+
29
+ // More declarations (10-19)
30
+ "SetAccessor", // 10
31
+ "PropertyDeclaration", // 11
32
+ "Parameter", // 12
33
+ "VariableDeclaration", // 13
34
+ "ModuleDeclaration", // 14
35
+ "ImportDeclaration", // 15
36
+ "ExportDeclaration", // 16
37
+ "NamespaceExportDeclaration", // 17
38
+ "ImportClause", // 18
39
+ "NamespaceImport", // 19
40
+
41
+ // Statements (20-39)
42
+ "IfStatement", // 20
43
+ "ForStatement", // 21
44
+ "ForOfStatement", // 22
45
+ "ForInStatement", // 23
46
+ "WhileStatement", // 24
47
+ "DoWhileStatement", // 25
48
+ "SwitchStatement", // 26
49
+ "CaseClause", // 27
50
+ "DefaultClause", // 28
51
+ "TryStatement", // 29
52
+ "CatchClause", // 30
53
+ "ThrowStatement", // 31
54
+ "ReturnStatement", // 32
55
+ "BreakStatement", // 33
56
+ "ContinueStatement", // 34
57
+ "Block", // 35
58
+ "EmptyStatement", // 36
59
+
60
+ // More statements (37-39)
61
+ "DebuggerStatement", // 37
62
+ "LabeledStatement", // 38
63
+ "WithStatement", // 39
64
+
65
+ // Expressions (40-56)
66
+ "BinaryExpression", // 40
67
+ "UnaryExpression", // 41
68
+ "PrefixUnaryExpression", // 42
69
+ "PostfixUnaryExpression", // 43
70
+ "ConditionalExpression", // 44
71
+ "CallExpression", // 45
72
+ "PropertyAccessExpression", // 46
73
+ "ElementAccessExpression", // 47
74
+ "NewExpression", // 48
75
+ "ParenthesizedExpression", // 49
76
+ "TypeAssertionExpression", // 50
77
+ "AsExpression", // 51
78
+ "NonNullExpression", // 52
79
+ "TemplateExpression", // 53
80
+ "ArrayLiteralExpression", // 54
81
+ "ObjectLiteralExpression", // 55
82
+ "ExpressionStatement", // 56
83
+ ] as const;
84
+
85
+ // ============================================================================
86
+ // 15 Token Types (leaf node categories, indices 57-71)
87
+ // ============================================================================
88
+
89
+ export const TOKEN_TYPES = [
90
+ "Identifier", // 57
91
+ "StringLiteral", // 58
92
+ "NumericLiteral", // 59
93
+ "TrueKeyword", // 60
94
+ "FalseKeyword", // 61
95
+ "NullKeyword", // 62
96
+ "UndefinedKeyword", // 63
97
+ "ThisKeyword", // 64
98
+ "SuperKeyword", // 65
99
+ "RegularExpressionLiteral", // 66
100
+ "NoSubstitutionTemplateLiteral", // 67
101
+ "TemplateHead", // 68
102
+ "TemplateMiddle", // 69
103
+ "TemplateTail", // 70
104
+ "ComputedPropertyName", // 71
105
+ ] as const;
106
+
107
+ // ============================================================================
108
+ // Constants
109
+ // ============================================================================
110
+
111
+ export const NUM_SYNTAX = SYNTAX_TYPES.length; // 57
112
+ export const NUM_TOKEN = TOKEN_TYPES.length; // 15
113
+ export const NUM_STATES = NUM_SYNTAX + NUM_TOKEN; // 72
114
+
115
+ // ============================================================================
116
+ // State Index Mapping
117
+ // ============================================================================
118
+
119
+ /**
120
+ * Map a TypeScript AST node to its Amain state index (0-71)
121
+ */
122
+ export function getStateIndex(node: ts.Node): number {
123
+ // Try syntax types first (0-56)
124
+ const kindName = ts.SyntaxKind[node.kind];
125
+ const syntaxIdx = SYNTAX_TYPES.indexOf(
126
+ kindName as (typeof SYNTAX_TYPES)[number],
127
+ );
128
+ if (syntaxIdx !== -1) return syntaxIdx;
129
+
130
+ // Map to token types (57-71) based on node kind
131
+ if (ts.isIdentifier(node)) return 57;
132
+ if (ts.isStringLiteral(node)) return 58;
133
+ if (ts.isNumericLiteral(node)) return 59;
134
+ if (node.kind === ts.SyntaxKind.TrueKeyword) return 60;
135
+ if (node.kind === ts.SyntaxKind.FalseKeyword) return 61;
136
+ if (node.kind === ts.SyntaxKind.NullKeyword) return 62;
137
+ if (node.kind === ts.SyntaxKind.UndefinedKeyword) return 63;
138
+ if (node.kind === ts.SyntaxKind.ThisKeyword) return 64;
139
+ if (node.kind === ts.SyntaxKind.SuperKeyword) return 65;
140
+ if (node.kind === ts.SyntaxKind.RegularExpressionLiteral) return 66;
141
+ if (node.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) return 67;
142
+ if (node.kind === ts.SyntaxKind.TemplateHead) return 68;
143
+ if (node.kind === ts.SyntaxKind.TemplateMiddle) return 69;
144
+ if (node.kind === ts.SyntaxKind.TemplateTail) return 70;
145
+ if (node.kind === ts.SyntaxKind.ComputedPropertyName) return 71;
146
+
147
+ // Default: treat as identifier for any other leaf node
148
+ return 57;
149
+ }
150
+
151
+ /**
152
+ * Check if a node is a syntax type (non-leaf) vs token type (leaf)
153
+ */
154
+ export function isSyntaxNode(node: ts.Node): boolean {
155
+ return getStateIndex(node) < NUM_SYNTAX;
156
+ }
157
+
158
+ /**
159
+ * Get state name for debugging
160
+ */
161
+ export function getStateName(index: number): string {
162
+ if (index < NUM_SYNTAX) return SYNTAX_TYPES[index];
163
+ if (index < NUM_STATES) return TOKEN_TYPES[index - NUM_SYNTAX];
164
+ return "Unknown";
165
+ }
@@ -15,7 +15,6 @@ import { minimatch } from "minimatch";
15
15
  export class ArchitectClient {
16
16
  constructor(verbose = false) {
17
17
  this.config = null;
18
- this.configPath = null;
19
18
  this.isUserConfig = false;
20
19
  this.log = verbose
21
20
  ? (msg) => console.error(`[architect] ${msg}`)
@@ -47,18 +46,32 @@ export class ArchitectClient {
47
46
  }
48
47
  // Fall back to built-in default
49
48
  try {
49
+ // Try multiple possible locations for the default config
50
+ const possibleDefaultPaths = [
51
+ path.join(projectRoot, "default-architect.yaml"),
52
+ path.join(projectRoot, "..", "default-architect.yaml"),
53
+ path.join(process.cwd(), "default-architect.yaml"),
54
+ ];
50
55
  // Handle both CommonJS and ESM environments
51
- let currentDir = ".";
52
56
  if (typeof __dirname !== "undefined") {
53
- currentDir = __dirname;
57
+ possibleDefaultPaths.push(path.join(__dirname, "..", "default-architect.yaml"));
58
+ possibleDefaultPaths.push(path.join(__dirname, "..", "..", "default-architect.yaml"));
54
59
  }
55
- const defaultPath = path.join(currentDir, "..", "default-architect.yaml");
56
- const content = fs.readFileSync(defaultPath, "utf-8");
57
- this.config = this.parseYaml(content);
58
- this.configPath = defaultPath;
59
- this.isUserConfig = false;
60
- this.log("Using default architect rules (create .pi-lens/architect.yaml to customize)");
61
- return true;
60
+ for (const defaultPath of possibleDefaultPaths) {
61
+ try {
62
+ const content = fs.readFileSync(defaultPath, "utf-8");
63
+ this.config = this.parseYaml(content);
64
+ this.configPath = defaultPath;
65
+ this.isUserConfig = false;
66
+ this.log("Using default architect rules (create .pi-lens/architect.yaml to customize)");
67
+ return true;
68
+ }
69
+ catch {
70
+ // Try next path
71
+ }
72
+ }
73
+ this.log("No architect config available");
74
+ return false;
62
75
  }
63
76
  catch {
64
77
  this.log("No architect config available");
@@ -113,6 +126,8 @@ export class ArchitectClient {
113
126
  pattern: rule.pattern,
114
127
  message: check.message,
115
128
  line: lineNum,
129
+ fix: check.fix,
130
+ note: check.note,
116
131
  });
117
132
  // Prevent infinite loop on empty matches
118
133
  if (match.index === regex.lastIndex) {
@@ -209,9 +224,14 @@ export class ArchitectClient {
209
224
  section = "must";
210
225
  continue;
211
226
  }
212
- // Message for current violation
227
+ // Message for current violation (handle nested quotes)
213
228
  if (trimmed.startsWith("message:") && violation) {
214
- const match = trimmed.match(/message:\s*["'](.+?)["']/);
229
+ // Match "..." or '...' allowing the other quote type inside
230
+ const dquoteMatch = trimmed.match(/message:\s*"([^"]*)"/);
231
+ const squoteMatch = !dquoteMatch
232
+ ? trimmed.match(/message:\s*'([^']*)'/)
233
+ : null;
234
+ const match = dquoteMatch || squoteMatch;
215
235
  if (match) {
216
236
  violation.message = match[1];
217
237
  if (rule) {
@@ -222,6 +242,30 @@ export class ArchitectClient {
222
242
  }
223
243
  continue;
224
244
  }
245
+ // Fix guidance for current violation
246
+ if (trimmed.startsWith("fix:") && violation) {
247
+ const dquoteMatch = trimmed.match(/fix:\s*"([^"]*)"/);
248
+ const squoteMatch = !dquoteMatch
249
+ ? trimmed.match(/fix:\s*'([^']*)'/)
250
+ : null;
251
+ const match = dquoteMatch || squoteMatch;
252
+ if (match) {
253
+ violation.fix = match[1];
254
+ }
255
+ continue;
256
+ }
257
+ // Note guidance for current violation
258
+ if (trimmed.startsWith("note:") && violation) {
259
+ const dquoteMatch = trimmed.match(/note:\s*"([^"]*)"/);
260
+ const squoteMatch = !dquoteMatch
261
+ ? trimmed.match(/note:\s*'([^']*)'/)
262
+ : null;
263
+ const match = dquoteMatch || squoteMatch;
264
+ if (match) {
265
+ violation.note = match[1];
266
+ }
267
+ continue;
268
+ }
225
269
  // Must items (simple strings)
226
270
  if (section === "must" && trimmed.startsWith("- ") && rule) {
227
271
  const item = trimmed.slice(2).replace(/^["']|["']$/g, "");
@@ -19,11 +19,18 @@ export interface ArchitectViolation {
19
19
  pattern: string;
20
20
  message: string;
21
21
  line?: number;
22
+ fix?: string;
23
+ note?: string;
22
24
  }
23
25
 
24
26
  export interface ArchitectRule {
25
27
  pattern: string;
26
- must_not?: Array<{ pattern: string; message: string }>;
28
+ must_not?: Array<{
29
+ pattern: string;
30
+ message: string;
31
+ fix?: string;
32
+ note?: string;
33
+ }>;
27
34
  must?: string[];
28
35
  max_lines?: number;
29
36
  }
@@ -44,8 +51,8 @@ export interface FileArchitectResult {
44
51
 
45
52
  export class ArchitectClient {
46
53
  private config: ArchitectConfig | null = null;
47
- private configPath: string | null = null;
48
54
  private isUserConfig: boolean = false;
55
+ private configPath: string | undefined;
49
56
  private log: (msg: string) => void;
50
57
 
51
58
  constructor(verbose = false) {
@@ -81,20 +88,40 @@ export class ArchitectClient {
81
88
 
82
89
  // Fall back to built-in default
83
90
  try {
91
+ // Try multiple possible locations for the default config
92
+ const possibleDefaultPaths = [
93
+ path.join(projectRoot, "default-architect.yaml"),
94
+ path.join(projectRoot, "..", "default-architect.yaml"),
95
+ path.join(process.cwd(), "default-architect.yaml"),
96
+ ];
97
+
84
98
  // Handle both CommonJS and ESM environments
85
- let currentDir = ".";
86
99
  if (typeof __dirname !== "undefined") {
87
- currentDir = __dirname;
100
+ possibleDefaultPaths.push(
101
+ path.join(__dirname, "..", "default-architect.yaml"),
102
+ );
103
+ possibleDefaultPaths.push(
104
+ path.join(__dirname, "..", "..", "default-architect.yaml"),
105
+ );
88
106
  }
89
- const defaultPath = path.join(currentDir, "..", "default-architect.yaml");
90
- const content = fs.readFileSync(defaultPath, "utf-8");
91
- this.config = this.parseYaml(content);
92
- this.configPath = defaultPath;
93
- this.isUserConfig = false;
94
- this.log(
95
- "Using default architect rules (create .pi-lens/architect.yaml to customize)",
96
- );
97
- return true;
107
+
108
+ for (const defaultPath of possibleDefaultPaths) {
109
+ try {
110
+ const content = fs.readFileSync(defaultPath, "utf-8");
111
+ this.config = this.parseYaml(content);
112
+ this.configPath = defaultPath;
113
+ this.isUserConfig = false;
114
+ this.log(
115
+ "Using default architect rules (create .pi-lens/architect.yaml to customize)",
116
+ );
117
+ return true;
118
+ } catch {
119
+ // Try next path
120
+ }
121
+ }
122
+
123
+ this.log("No architect config available");
124
+ return false;
98
125
  } catch {
99
126
  this.log("No architect config available");
100
127
  return false;
@@ -154,6 +181,8 @@ export class ArchitectClient {
154
181
  pattern: rule.pattern,
155
182
  message: check.message,
156
183
  line: lineNum,
184
+ fix: check.fix,
185
+ note: check.note,
157
186
  });
158
187
 
159
188
  // Prevent infinite loop on empty matches
@@ -226,7 +255,12 @@ export class ArchitectClient {
226
255
  const lines = block.split("\n");
227
256
  let rule: ArchitectRule | null = null;
228
257
  let section: "must_not" | "must" | null = null;
229
- let violation: { pattern: string; message: string } | null = null;
258
+ let violation: {
259
+ pattern: string;
260
+ message: string;
261
+ fix?: string;
262
+ note?: string;
263
+ } | null = null;
230
264
 
231
265
  for (const line of lines) {
232
266
  const trimmed = line.trim();
@@ -269,9 +303,14 @@ export class ArchitectClient {
269
303
  continue;
270
304
  }
271
305
 
272
- // Message for current violation
306
+ // Message for current violation (handle nested quotes)
273
307
  if (trimmed.startsWith("message:") && violation) {
274
- const match = trimmed.match(/message:\s*["'](.+?)["']/);
308
+ // Match "..." or '...' allowing the other quote type inside
309
+ const dquoteMatch = trimmed.match(/message:\s*"([^"]*)"/);
310
+ const squoteMatch = !dquoteMatch
311
+ ? trimmed.match(/message:\s*'([^']*)'/)
312
+ : null;
313
+ const match = dquoteMatch || squoteMatch;
275
314
  if (match) {
276
315
  violation.message = match[1];
277
316
  if (rule) {
@@ -283,6 +322,32 @@ export class ArchitectClient {
283
322
  continue;
284
323
  }
285
324
 
325
+ // Fix guidance for current violation
326
+ if (trimmed.startsWith("fix:") && violation) {
327
+ const dquoteMatch = trimmed.match(/fix:\s*"([^"]*)"/);
328
+ const squoteMatch = !dquoteMatch
329
+ ? trimmed.match(/fix:\s*'([^']*)'/)
330
+ : null;
331
+ const match = dquoteMatch || squoteMatch;
332
+ if (match) {
333
+ violation.fix = match[1];
334
+ }
335
+ continue;
336
+ }
337
+
338
+ // Note guidance for current violation
339
+ if (trimmed.startsWith("note:") && violation) {
340
+ const dquoteMatch = trimmed.match(/note:\s*"([^"]*)"/);
341
+ const squoteMatch = !dquoteMatch
342
+ ? trimmed.match(/note:\s*'([^']*)'/)
343
+ : null;
344
+ const match = dquoteMatch || squoteMatch;
345
+ if (match) {
346
+ violation.note = match[1];
347
+ }
348
+ continue;
349
+ }
350
+
286
351
  // Must items (simple strings)
287
352
  if (section === "must" && trimmed.startsWith("- ") && rule) {
288
353
  const item = trimmed.slice(2).replace(/^["']|["']$/g, "");
@@ -182,7 +182,7 @@ message: found
182
182
  const result = spawnSync("npx", ["sg", "scan", "--config", configPath, "--json", absolutePath], {
183
183
  encoding: "utf-8",
184
184
  timeout: 15000,
185
- shell: true,
185
+ shell: process.platform === "win32",
186
186
  });
187
187
  // ast-grep exits 1 when it finds issues
188
188
  const output = result.stdout || result.stderr || "";
@@ -192,7 +192,7 @@ message: found
192
192
  return parser.parseOutput(output, absolutePath);
193
193
  }
194
194
  catch (err) {
195
- this.log(`Scan error: ${err.message}`);
195
+ this.log(`Scan error: ${err instanceof Error ? err.message : String(err)}`);
196
196
  return [];
197
197
  }
198
198
  }