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,245 @@
1
+ /**
2
+ * Autofix Integration Tests
3
+ *
4
+ * Tests for auto-format and auto-fix behavior.
5
+ * Validates that autofix runs by default and can be disabled.
6
+ */
7
+ import { describe, expect, it, vi } from "vitest";
8
+ // --- Mock Runners ---
9
+ const createAutofixRunner = (id) => ({
10
+ id,
11
+ appliesTo: ["jsts"],
12
+ priority: 10,
13
+ enabledByDefault: true,
14
+ when: async (ctx) => ctx.autofix,
15
+ async run(ctx) {
16
+ return {
17
+ status: "succeeded",
18
+ diagnostics: [
19
+ {
20
+ id: `${id}:fixed`,
21
+ message: `Fixed by ${id}`,
22
+ filePath: ctx.filePath,
23
+ line: 1,
24
+ column: 1,
25
+ severity: "info",
26
+ semantic: "fixed",
27
+ tool: id,
28
+ },
29
+ ],
30
+ semantic: "fixed",
31
+ };
32
+ },
33
+ });
34
+ const createNonAutofixRunner = (id) => ({
35
+ id,
36
+ appliesTo: ["jsts"],
37
+ priority: 20,
38
+ enabledByDefault: true,
39
+ async run(ctx) {
40
+ return {
41
+ status: "succeeded",
42
+ diagnostics: [
43
+ {
44
+ id: `${id}:info`,
45
+ message: `Info from ${id}`,
46
+ filePath: ctx.filePath,
47
+ line: 1,
48
+ column: 1,
49
+ severity: "info",
50
+ semantic: "silent",
51
+ tool: id,
52
+ },
53
+ ],
54
+ semantic: "none",
55
+ };
56
+ },
57
+ });
58
+ const createMockContext = (filePath = "test.ts", autofix = true) => ({
59
+ filePath,
60
+ cwd: "/test",
61
+ kind: "jsts",
62
+ pi: {
63
+ getFlag: vi.fn((flag) => {
64
+ if (flag === "no-autofix")
65
+ return !autofix;
66
+ return autofix;
67
+ }),
68
+ },
69
+ autofix,
70
+ deltaMode: false,
71
+ baselines: new Map(),
72
+ hasTool: vi.fn(() => Promise.resolve(false)),
73
+ log: vi.fn(),
74
+ });
75
+ const _createMockGroup = (runners) => ({
76
+ runnerIds: runners.map((r) => r.id),
77
+ mode: "all",
78
+ });
79
+ // --- Tests ---
80
+ describe("Autofix Integration", () => {
81
+ describe("Autofix by Default", () => {
82
+ it("should run autofix runners when enabled", async () => {
83
+ const autofixRunner = createAutofixRunner("biome-fix");
84
+ const ctx = createMockContext("test.ts", true);
85
+ // The runner should apply (autofix is true)
86
+ const shouldRun = await autofixRunner.when?.(ctx);
87
+ expect(shouldRun).toBe(true);
88
+ });
89
+ it("should skip autofix runners when disabled", async () => {
90
+ const autofixRunner = createAutofixRunner("biome-fix");
91
+ const ctx = createMockContext("test.ts", false);
92
+ // The runner should not apply (autofix is false)
93
+ const shouldRun = await autofixRunner.when?.(ctx);
94
+ expect(shouldRun).toBe(false);
95
+ });
96
+ });
97
+ describe("--no-autofix Flag", () => {
98
+ it("should disable autofix with --no-autofix flag", () => {
99
+ const mockPi = {
100
+ getFlag: vi.fn((flag) => flag === "no-autofix"),
101
+ };
102
+ // Simulate flag check in dispatch context
103
+ const autofixEnabled = !mockPi.getFlag("no-autofix");
104
+ expect(autofixEnabled).toBe(false);
105
+ });
106
+ it("should enable autofix without --no-autofix flag", () => {
107
+ const mockPi = {
108
+ getFlag: vi.fn((_flag) => false),
109
+ };
110
+ const autofixEnabled = !mockPi.getFlag("no-autofix");
111
+ expect(autofixEnabled).toBe(true);
112
+ });
113
+ });
114
+ describe("File Modification Warnings", () => {
115
+ it("should track when format changes files", async () => {
116
+ // Simulate format result
117
+ const formatResult = {
118
+ formatChanged: true,
119
+ fixedCount: 0,
120
+ };
121
+ // Should show warning
122
+ const shouldWarn = formatResult.formatChanged || formatResult.fixedCount > 0;
123
+ expect(shouldWarn).toBe(true);
124
+ });
125
+ it("should track when autofix changes files", async () => {
126
+ // Simulate autofix result
127
+ const fixResult = {
128
+ formatChanged: false,
129
+ fixedCount: 3,
130
+ };
131
+ // Should show warning
132
+ const shouldWarn = fixResult.formatChanged || fixResult.fixedCount > 0;
133
+ expect(shouldWarn).toBe(true);
134
+ });
135
+ it("should show warning message format", () => {
136
+ const warningMessage = "⚠️ **File modified by auto-format/fix. Re-read before next edit.**";
137
+ expect(warningMessage).toContain("File modified");
138
+ expect(warningMessage).toContain("auto-format");
139
+ expect(warningMessage).toContain("fix");
140
+ });
141
+ });
142
+ describe("Runner Categories", () => {
143
+ it("should have autofix runners with when conditions", () => {
144
+ const autofixRunner = createAutofixRunner("ruff-fix");
145
+ expect(autofixRunner.when).toBeDefined();
146
+ expect(typeof autofixRunner.when).toBe("function");
147
+ });
148
+ it("should have non-autofix runners without when conditions", () => {
149
+ const regularRunner = createNonAutofixRunner("ts-lsp");
150
+ // Regular runners run regardless of autofix setting
151
+ expect(regularRunner.when).toBeUndefined();
152
+ });
153
+ it("should categorize fixed diagnostics correctly", async () => {
154
+ const autofixRunner = createAutofixRunner("biome-fix");
155
+ const ctx = createMockContext("test.ts", true);
156
+ const result = await autofixRunner.run(ctx);
157
+ // Should have semantic: "fixed"
158
+ expect(result.semantic).toBe("fixed");
159
+ // Diagnostics should also be fixed
160
+ for (const diag of result.diagnostics || []) {
161
+ expect(diag.semantic).toBe("fixed");
162
+ }
163
+ });
164
+ });
165
+ describe("Format vs Autofix Coordination", () => {
166
+ it("should run format before lint", async () => {
167
+ const order = [];
168
+ const formatRunner = {
169
+ id: "format",
170
+ appliesTo: ["jsts"],
171
+ priority: 5, // Lower = runs first
172
+ enabledByDefault: true,
173
+ async run() {
174
+ order.push("format");
175
+ return { status: "succeeded", diagnostics: [], semantic: "none" };
176
+ },
177
+ };
178
+ const lintRunner = {
179
+ id: "lint",
180
+ appliesTo: ["jsts"],
181
+ priority: 10, // Higher = runs after
182
+ enabledByDefault: true,
183
+ async run() {
184
+ order.push("lint");
185
+ return { status: "succeeded", diagnostics: [], semantic: "none" };
186
+ },
187
+ };
188
+ // Verify priority ordering
189
+ expect(formatRunner.priority).toBeLessThan(lintRunner.priority);
190
+ });
191
+ it("should handle format and lint race conditions", async () => {
192
+ // Simulate format changing file while lint is running
193
+ const _ctx = createMockContext("test.ts", true);
194
+ // Format result
195
+ const formatChanged = true;
196
+ // Lint should still work
197
+ const lintResult = {
198
+ status: "succeeded",
199
+ diagnostics: [],
200
+ semantic: "none",
201
+ };
202
+ // Both should complete without error
203
+ expect(formatChanged).toBe(true);
204
+ expect(lintResult.status).toBe("succeeded");
205
+ });
206
+ });
207
+ describe("Real-world Scenarios", () => {
208
+ it("should handle ruff autofix flow", async () => {
209
+ const ruffRunner = createAutofixRunner("ruff");
210
+ const ctxWithAutofix = createMockContext("test.py", true);
211
+ const ctxNoAutofix = createMockContext("test.py", false);
212
+ // With autofix
213
+ const shouldRunWith = await ruffRunner.when?.(ctxWithAutofix);
214
+ expect(shouldRunWith).toBe(true);
215
+ // Without autofix
216
+ const shouldRunWithout = await ruffRunner.when?.(ctxNoAutofix);
217
+ expect(shouldRunWithout).toBe(false);
218
+ });
219
+ it("should handle biome autofix flow", async () => {
220
+ const biomeRunner = createAutofixRunner("biome-fix");
221
+ const ctxWithAutofix = createMockContext("test.ts", true);
222
+ const ctxNoAutofix = createMockContext("test.ts", false);
223
+ // With autofix
224
+ const shouldRunWith = await biomeRunner.when?.(ctxWithAutofix);
225
+ expect(shouldRunWith).toBe(true);
226
+ // Without autofix
227
+ const shouldRunWithout = await biomeRunner.when?.(ctxNoAutofix);
228
+ expect(shouldRunWithout).toBe(false);
229
+ });
230
+ it("should show file modification warning only when changes made", async () => {
231
+ // No changes
232
+ const noChanges = { formatChanged: false, fixedCount: 0 };
233
+ expect(noChanges.formatChanged || noChanges.fixedCount > 0).toBe(false);
234
+ // Format only
235
+ const formatOnly = { formatChanged: true, fixedCount: 0 };
236
+ expect(formatOnly.formatChanged || formatOnly.fixedCount > 0).toBe(true);
237
+ // Fix only
238
+ const fixOnly = { formatChanged: false, fixedCount: 5 };
239
+ expect(fixOnly.formatChanged || fixOnly.fixedCount > 0).toBe(true);
240
+ // Both
241
+ const both = { formatChanged: true, fixedCount: 3 };
242
+ expect(both.formatChanged || both.fixedCount > 0).toBe(true);
243
+ });
244
+ });
245
+ });
@@ -0,0 +1,300 @@
1
+ /**
2
+ * Autofix Integration Tests
3
+ *
4
+ * Tests for auto-format and auto-fix behavior.
5
+ * Validates that autofix runs by default and can be disabled.
6
+ */
7
+
8
+ import { describe, expect, it, vi } from "vitest";
9
+ import type {
10
+ DispatchContext,
11
+ RunnerDefinition,
12
+ RunnerGroup,
13
+ } from "../types.js";
14
+
15
+ // --- Mock Runners ---
16
+
17
+ const createAutofixRunner = (id: string): RunnerDefinition => ({
18
+ id,
19
+ appliesTo: ["jsts"],
20
+ priority: 10,
21
+ enabledByDefault: true,
22
+ when: async (ctx) => ctx.autofix,
23
+ async run(ctx: DispatchContext) {
24
+ return {
25
+ status: "succeeded",
26
+ diagnostics: [
27
+ {
28
+ id: `${id}:fixed`,
29
+ message: `Fixed by ${id}`,
30
+ filePath: ctx.filePath,
31
+ line: 1,
32
+ column: 1,
33
+ severity: "info",
34
+ semantic: "fixed",
35
+ tool: id,
36
+ },
37
+ ],
38
+ semantic: "fixed",
39
+ };
40
+ },
41
+ });
42
+
43
+ const createNonAutofixRunner = (id: string): RunnerDefinition => ({
44
+ id,
45
+ appliesTo: ["jsts"],
46
+ priority: 20,
47
+ enabledByDefault: true,
48
+ async run(ctx: DispatchContext) {
49
+ return {
50
+ status: "succeeded",
51
+ diagnostics: [
52
+ {
53
+ id: `${id}:info`,
54
+ message: `Info from ${id}`,
55
+ filePath: ctx.filePath,
56
+ line: 1,
57
+ column: 1,
58
+ severity: "info",
59
+ semantic: "silent",
60
+ tool: id,
61
+ },
62
+ ],
63
+ semantic: "none",
64
+ };
65
+ },
66
+ });
67
+
68
+ const createMockContext = (
69
+ filePath: string = "test.ts",
70
+ autofix: boolean = true,
71
+ ): DispatchContext => ({
72
+ filePath,
73
+ cwd: "/test",
74
+ kind: "jsts",
75
+ pi: {
76
+ getFlag: vi.fn((flag: string) => {
77
+ if (flag === "no-autofix") return !autofix;
78
+ return autofix;
79
+ }),
80
+ },
81
+ autofix,
82
+ deltaMode: false,
83
+ baselines: new Map(),
84
+ hasTool: vi.fn(() => Promise.resolve(false)),
85
+ log: vi.fn(),
86
+ });
87
+
88
+ const _createMockGroup = (runners: RunnerDefinition[]): RunnerGroup => ({
89
+ runnerIds: runners.map((r) => r.id),
90
+ mode: "all",
91
+ });
92
+
93
+ // --- Tests ---
94
+
95
+ describe("Autofix Integration", () => {
96
+ describe("Autofix by Default", () => {
97
+ it("should run autofix runners when enabled", async () => {
98
+ const autofixRunner = createAutofixRunner("biome-fix");
99
+ const ctx = createMockContext("test.ts", true);
100
+
101
+ // The runner should apply (autofix is true)
102
+ const shouldRun = await autofixRunner.when?.(ctx);
103
+ expect(shouldRun).toBe(true);
104
+ });
105
+
106
+ it("should skip autofix runners when disabled", async () => {
107
+ const autofixRunner = createAutofixRunner("biome-fix");
108
+ const ctx = createMockContext("test.ts", false);
109
+
110
+ // The runner should not apply (autofix is false)
111
+ const shouldRun = await autofixRunner.when?.(ctx);
112
+ expect(shouldRun).toBe(false);
113
+ });
114
+ });
115
+
116
+ describe("--no-autofix Flag", () => {
117
+ it("should disable autofix with --no-autofix flag", () => {
118
+ const mockPi = {
119
+ getFlag: vi.fn((flag: string) => flag === "no-autofix"),
120
+ };
121
+
122
+ // Simulate flag check in dispatch context
123
+ const autofixEnabled = !mockPi.getFlag("no-autofix");
124
+ expect(autofixEnabled).toBe(false);
125
+ });
126
+
127
+ it("should enable autofix without --no-autofix flag", () => {
128
+ const mockPi = {
129
+ getFlag: vi.fn((_flag: string) => false),
130
+ };
131
+
132
+ const autofixEnabled = !mockPi.getFlag("no-autofix");
133
+ expect(autofixEnabled).toBe(true);
134
+ });
135
+ });
136
+
137
+ describe("File Modification Warnings", () => {
138
+ it("should track when format changes files", async () => {
139
+ // Simulate format result
140
+ const formatResult = {
141
+ formatChanged: true,
142
+ fixedCount: 0,
143
+ };
144
+
145
+ // Should show warning
146
+ const shouldWarn =
147
+ formatResult.formatChanged || formatResult.fixedCount > 0;
148
+ expect(shouldWarn).toBe(true);
149
+ });
150
+
151
+ it("should track when autofix changes files", async () => {
152
+ // Simulate autofix result
153
+ const fixResult = {
154
+ formatChanged: false,
155
+ fixedCount: 3,
156
+ };
157
+
158
+ // Should show warning
159
+ const shouldWarn = fixResult.formatChanged || fixResult.fixedCount > 0;
160
+ expect(shouldWarn).toBe(true);
161
+ });
162
+
163
+ it("should show warning message format", () => {
164
+ const warningMessage =
165
+ "⚠️ **File modified by auto-format/fix. Re-read before next edit.**";
166
+ expect(warningMessage).toContain("File modified");
167
+ expect(warningMessage).toContain("auto-format");
168
+ expect(warningMessage).toContain("fix");
169
+ });
170
+ });
171
+
172
+ describe("Runner Categories", () => {
173
+ it("should have autofix runners with when conditions", () => {
174
+ const autofixRunner = createAutofixRunner("ruff-fix");
175
+
176
+ expect(autofixRunner.when).toBeDefined();
177
+ expect(typeof autofixRunner.when).toBe("function");
178
+ });
179
+
180
+ it("should have non-autofix runners without when conditions", () => {
181
+ const regularRunner = createNonAutofixRunner("ts-lsp");
182
+
183
+ // Regular runners run regardless of autofix setting
184
+ expect(regularRunner.when).toBeUndefined();
185
+ });
186
+
187
+ it("should categorize fixed diagnostics correctly", async () => {
188
+ const autofixRunner = createAutofixRunner("biome-fix");
189
+ const ctx = createMockContext("test.ts", true);
190
+
191
+ const result = await autofixRunner.run(ctx);
192
+
193
+ // Should have semantic: "fixed"
194
+ expect(result.semantic).toBe("fixed");
195
+
196
+ // Diagnostics should also be fixed
197
+ for (const diag of result.diagnostics || []) {
198
+ expect(diag.semantic).toBe("fixed");
199
+ }
200
+ });
201
+ });
202
+
203
+ describe("Format vs Autofix Coordination", () => {
204
+ it("should run format before lint", async () => {
205
+ const order: string[] = [];
206
+
207
+ const formatRunner: RunnerDefinition = {
208
+ id: "format",
209
+ appliesTo: ["jsts"],
210
+ priority: 5, // Lower = runs first
211
+ enabledByDefault: true,
212
+ async run() {
213
+ order.push("format");
214
+ return { status: "succeeded", diagnostics: [], semantic: "none" };
215
+ },
216
+ };
217
+
218
+ const lintRunner: RunnerDefinition = {
219
+ id: "lint",
220
+ appliesTo: ["jsts"],
221
+ priority: 10, // Higher = runs after
222
+ enabledByDefault: true,
223
+ async run() {
224
+ order.push("lint");
225
+ return { status: "succeeded", diagnostics: [], semantic: "none" };
226
+ },
227
+ };
228
+
229
+ // Verify priority ordering
230
+ expect(formatRunner.priority).toBeLessThan(lintRunner.priority!);
231
+ });
232
+
233
+ it("should handle format and lint race conditions", async () => {
234
+ // Simulate format changing file while lint is running
235
+ const _ctx = createMockContext("test.ts", true);
236
+
237
+ // Format result
238
+ const formatChanged = true;
239
+
240
+ // Lint should still work
241
+ const lintResult = {
242
+ status: "succeeded",
243
+ diagnostics: [],
244
+ semantic: "none",
245
+ };
246
+
247
+ // Both should complete without error
248
+ expect(formatChanged).toBe(true);
249
+ expect(lintResult.status).toBe("succeeded");
250
+ });
251
+ });
252
+
253
+ describe("Real-world Scenarios", () => {
254
+ it("should handle ruff autofix flow", async () => {
255
+ const ruffRunner = createAutofixRunner("ruff");
256
+ const ctxWithAutofix = createMockContext("test.py", true);
257
+ const ctxNoAutofix = createMockContext("test.py", false);
258
+
259
+ // With autofix
260
+ const shouldRunWith = await ruffRunner.when?.(ctxWithAutofix);
261
+ expect(shouldRunWith).toBe(true);
262
+
263
+ // Without autofix
264
+ const shouldRunWithout = await ruffRunner.when?.(ctxNoAutofix);
265
+ expect(shouldRunWithout).toBe(false);
266
+ });
267
+
268
+ it("should handle biome autofix flow", async () => {
269
+ const biomeRunner = createAutofixRunner("biome-fix");
270
+ const ctxWithAutofix = createMockContext("test.ts", true);
271
+ const ctxNoAutofix = createMockContext("test.ts", false);
272
+
273
+ // With autofix
274
+ const shouldRunWith = await biomeRunner.when?.(ctxWithAutofix);
275
+ expect(shouldRunWith).toBe(true);
276
+
277
+ // Without autofix
278
+ const shouldRunWithout = await biomeRunner.when?.(ctxNoAutofix);
279
+ expect(shouldRunWithout).toBe(false);
280
+ });
281
+
282
+ it("should show file modification warning only when changes made", async () => {
283
+ // No changes
284
+ const noChanges = { formatChanged: false, fixedCount: 0 };
285
+ expect(noChanges.formatChanged || noChanges.fixedCount > 0).toBe(false);
286
+
287
+ // Format only
288
+ const formatOnly = { formatChanged: true, fixedCount: 0 };
289
+ expect(formatOnly.formatChanged || formatOnly.fixedCount > 0).toBe(true);
290
+
291
+ // Fix only
292
+ const fixOnly = { formatChanged: false, fixedCount: 5 };
293
+ expect(fixOnly.formatChanged || fixOnly.fixedCount > 0).toBe(true);
294
+
295
+ // Both
296
+ const both = { formatChanged: true, fixedCount: 3 };
297
+ expect(both.formatChanged || both.fixedCount > 0).toBe(true);
298
+ });
299
+ });
300
+ });