pi-usereq 0.4.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 (361) hide show
  1. package/.g.conf +20 -0
  2. package/.github/workflows/release-npm.yml +131 -0
  3. package/.gitignore +270 -0
  4. package/CHANGELOG.md +221 -0
  5. package/LICENSE +674 -0
  6. package/README.md +260 -0
  7. package/TODO.md +20 -0
  8. package/docs/pi.dev/agent-document-manifest.json +6399 -0
  9. package/docs/pi.dev/coding-agent-docs/compaction.md +394 -0
  10. package/docs/pi.dev/coding-agent-docs/custom-provider.md +596 -0
  11. package/docs/pi.dev/coding-agent-docs/development.md +71 -0
  12. package/docs/pi.dev/coding-agent-docs/extensions.md +2262 -0
  13. package/docs/pi.dev/coding-agent-docs/images/doom-extension.png +0 -0
  14. package/docs/pi.dev/coding-agent-docs/images/exy.png +0 -0
  15. package/docs/pi.dev/coding-agent-docs/images/interactive-mode.png +0 -0
  16. package/docs/pi.dev/coding-agent-docs/images/tree-view.png +0 -0
  17. package/docs/pi.dev/coding-agent-docs/json.md +82 -0
  18. package/docs/pi.dev/coding-agent-docs/keybindings.md +175 -0
  19. package/docs/pi.dev/coding-agent-docs/models.md +392 -0
  20. package/docs/pi.dev/coding-agent-docs/packages.md +218 -0
  21. package/docs/pi.dev/coding-agent-docs/prompt-templates.md +67 -0
  22. package/docs/pi.dev/coding-agent-docs/providers.md +195 -0
  23. package/docs/pi.dev/coding-agent-docs/rpc.md +1377 -0
  24. package/docs/pi.dev/coding-agent-docs/sdk.md +1124 -0
  25. package/docs/pi.dev/coding-agent-docs/session.md +412 -0
  26. package/docs/pi.dev/coding-agent-docs/settings.md +247 -0
  27. package/docs/pi.dev/coding-agent-docs/shell-aliases.md +13 -0
  28. package/docs/pi.dev/coding-agent-docs/skills.md +232 -0
  29. package/docs/pi.dev/coding-agent-docs/terminal-setup.md +106 -0
  30. package/docs/pi.dev/coding-agent-docs/termux.md +127 -0
  31. package/docs/pi.dev/coding-agent-docs/themes.md +295 -0
  32. package/docs/pi.dev/coding-agent-docs/tmux.md +61 -0
  33. package/docs/pi.dev/coding-agent-docs/tree.md +231 -0
  34. package/docs/pi.dev/coding-agent-docs/tui.md +887 -0
  35. package/docs/pi.dev/coding-agent-docs/windows.md +17 -0
  36. package/docs/pi.dev/mom-docs/artifacts-server.md +475 -0
  37. package/docs/pi.dev/mom-docs/events.md +307 -0
  38. package/docs/pi.dev/mom-docs/new.md +970 -0
  39. package/docs/pi.dev/mom-docs/sandbox.md +153 -0
  40. package/docs/pi.dev/mom-docs/slack-bot-minimal-guide.md +399 -0
  41. package/docs/pi.dev/mom-docs/v86.md +319 -0
  42. package/docs/pi.dev/pods-docs/gml-4.5.md +189 -0
  43. package/docs/pi.dev/pods-docs/gpt-oss.md +233 -0
  44. package/docs/pi.dev/pods-docs/implementation-plan.md +183 -0
  45. package/docs/pi.dev/pods-docs/kimi-k2.md +197 -0
  46. package/docs/pi.dev/pods-docs/models.md +116 -0
  47. package/docs/pi.dev/pods-docs/plan.md +166 -0
  48. package/docs/pi.dev/pods-docs/qwen3-coder.md +132 -0
  49. package/images/flowchart-bw.png +0 -0
  50. package/images/flowchart-bw.svg +102 -0
  51. package/images/flowchart.md +100 -0
  52. package/images/flowchart.png +0 -0
  53. package/images/flowchart.svg +3 -0
  54. package/package.json +46 -0
  55. package/req/docs/REFERENCES.md +4554 -0
  56. package/req/docs/REQUIREMENTS.md +475 -0
  57. package/req/docs/WORKFLOW.md +1059 -0
  58. package/scripts/debug-extension.ts +497 -0
  59. package/scripts/lib/extension-debug-harness.ts +450 -0
  60. package/scripts/lib/recording-extension-api.ts +786 -0
  61. package/scripts/lib/sdk-smoke.ts +503 -0
  62. package/scripts/pi-usereq-debug.sh +330 -0
  63. package/scripts/tool-args-to-params.ts +208 -0
  64. package/src/cli.ts +349 -0
  65. package/src/core/agent-tool-json.ts +621 -0
  66. package/src/core/compress-files.ts +72 -0
  67. package/src/core/compress-payload.ts +648 -0
  68. package/src/core/compress.ts +464 -0
  69. package/src/core/config.ts +272 -0
  70. package/src/core/doxygen-parser.ts +318 -0
  71. package/src/core/errors.ts +31 -0
  72. package/src/core/extension-status.ts +660 -0
  73. package/src/core/find-constructs.ts +319 -0
  74. package/src/core/find-payload.ts +915 -0
  75. package/src/core/generate-markdown.ts +120 -0
  76. package/src/core/path-context.ts +196 -0
  77. package/src/core/pi-notify.ts +430 -0
  78. package/src/core/pi-usereq-tools.ts +140 -0
  79. package/src/core/prompts.ts +184 -0
  80. package/src/core/reference-payload.ts +818 -0
  81. package/src/core/resources.ts +63 -0
  82. package/src/core/runtime-project-paths.ts +99 -0
  83. package/src/core/settings-menu.ts +233 -0
  84. package/src/core/source-analyzer.ts +1721 -0
  85. package/src/core/static-check.ts +674 -0
  86. package/src/core/token-counter.ts +729 -0
  87. package/src/core/tool-runner.ts +717 -0
  88. package/src/core/utils.ts +185 -0
  89. package/src/index.ts +2209 -0
  90. package/src/resources/guidelines/Google_C++_Style_Guide.md +3711 -0
  91. package/src/resources/guidelines/Google_Python_Style_Guide.md +3709 -0
  92. package/src/resources/prompts/analyze.md +130 -0
  93. package/src/resources/prompts/change.md +227 -0
  94. package/src/resources/prompts/check.md +139 -0
  95. package/src/resources/prompts/cover.md +219 -0
  96. package/src/resources/prompts/create.md +104 -0
  97. package/src/resources/prompts/fix.md +221 -0
  98. package/src/resources/prompts/flowchart.md +220 -0
  99. package/src/resources/prompts/implement.md +163 -0
  100. package/src/resources/prompts/new.md +226 -0
  101. package/src/resources/prompts/readme.md +182 -0
  102. package/src/resources/prompts/recreate.md +213 -0
  103. package/src/resources/prompts/refactor.md +213 -0
  104. package/src/resources/prompts/references.md +100 -0
  105. package/src/resources/prompts/renumber.md +119 -0
  106. package/src/resources/prompts/workflow.md +202 -0
  107. package/src/resources/prompts/write.md +99 -0
  108. package/src/resources/sounds/Machine-alert-beep-sound-effect.mp3 +0 -0
  109. package/src/resources/sounds/Soft-high-tech-notification-sound-effect.mp3 +0 -0
  110. package/src/resources/templates/Document_Source_Code_in_Doxygen_Style.md +130 -0
  111. package/src/resources/templates/HDT_Test_Authoring_Guide.md +318 -0
  112. package/src/resources/templates/Requirements_Template.md +78 -0
  113. package/tests/attended-results-scenarios.ts +758 -0
  114. package/tests/attended-results.test.ts +39 -0
  115. package/tests/cli-command-option-parity.test.ts +815 -0
  116. package/tests/debug-extension-harness.test.ts +463 -0
  117. package/tests/extension-registration.test.ts +2006 -0
  118. package/tests/fixtures/fixture_c.c +361 -0
  119. package/tests/fixtures/fixture_cpp.cpp +407 -0
  120. package/tests/fixtures/fixture_csharp.cs +411 -0
  121. package/tests/fixtures/fixture_elixir.ex +409 -0
  122. package/tests/fixtures/fixture_go.go +341 -0
  123. package/tests/fixtures/fixture_haskell.hs +250 -0
  124. package/tests/fixtures/fixture_java.java +430 -0
  125. package/tests/fixtures/fixture_javascript.js +383 -0
  126. package/tests/fixtures/fixture_kotlin.kt +451 -0
  127. package/tests/fixtures/fixture_lua.lua +276 -0
  128. package/tests/fixtures/fixture_perl.pl +310 -0
  129. package/tests/fixtures/fixture_php.php +433 -0
  130. package/tests/fixtures/fixture_python.py +502 -0
  131. package/tests/fixtures/fixture_ruby.rb +345 -0
  132. package/tests/fixtures/fixture_rust.rs +380 -0
  133. package/tests/fixtures/fixture_scala.scala +398 -0
  134. package/tests/fixtures/fixture_shell.sh +276 -0
  135. package/tests/fixtures/fixture_swift.swift +397 -0
  136. package/tests/fixtures/fixture_typescript.ts +434 -0
  137. package/tests/fixtures/fixture_zig.zig +295 -0
  138. package/tests/fixtures_attended_results/project/compress-line-numbers.json +5 -0
  139. package/tests/fixtures_attended_results/project/compress.json +5 -0
  140. package/tests/fixtures_attended_results/project/enable-static-check-invalid-command.json +5 -0
  141. package/tests/fixtures_attended_results/project/enable-static-check-valid.json +5 -0
  142. package/tests/fixtures_attended_results/project/files-static-check.json +5 -0
  143. package/tests/fixtures_attended_results/project/find-line-numbers.json +5 -0
  144. package/tests/fixtures_attended_results/project/find.json +5 -0
  145. package/tests/fixtures_attended_results/project/get-base-path.json +5 -0
  146. package/tests/fixtures_attended_results/project/git-check-clean.json +5 -0
  147. package/tests/fixtures_attended_results/project/git-check-dirty.json +5 -0
  148. package/tests/fixtures_attended_results/project/git-path.json +5 -0
  149. package/tests/fixtures_attended_results/project/git-wt-create-invalid.json +5 -0
  150. package/tests/fixtures_attended_results/project/git-wt-create-valid.json +5 -0
  151. package/tests/fixtures_attended_results/project/git-wt-delete-nonexistent.json +5 -0
  152. package/tests/fixtures_attended_results/project/git-wt-delete-valid.json +5 -0
  153. package/tests/fixtures_attended_results/project/git-wt-name.json +5 -0
  154. package/tests/fixtures_attended_results/project/references.json +5 -0
  155. package/tests/fixtures_attended_results/project/static-check.json +5 -0
  156. package/tests/fixtures_attended_results/project/tokens.json +5 -0
  157. package/tests/fixtures_attended_results/standalone/files-compress/fixture_c.c.json +5 -0
  158. package/tests/fixtures_attended_results/standalone/files-compress/fixture_cpp.cpp.json +5 -0
  159. package/tests/fixtures_attended_results/standalone/files-compress/fixture_csharp.cs.json +5 -0
  160. package/tests/fixtures_attended_results/standalone/files-compress/fixture_elixir.ex.json +5 -0
  161. package/tests/fixtures_attended_results/standalone/files-compress/fixture_go.go.json +5 -0
  162. package/tests/fixtures_attended_results/standalone/files-compress/fixture_haskell.hs.json +5 -0
  163. package/tests/fixtures_attended_results/standalone/files-compress/fixture_java.java.json +5 -0
  164. package/tests/fixtures_attended_results/standalone/files-compress/fixture_javascript.js.json +5 -0
  165. package/tests/fixtures_attended_results/standalone/files-compress/fixture_kotlin.kt.json +5 -0
  166. package/tests/fixtures_attended_results/standalone/files-compress/fixture_lua.lua.json +5 -0
  167. package/tests/fixtures_attended_results/standalone/files-compress/fixture_perl.pl.json +5 -0
  168. package/tests/fixtures_attended_results/standalone/files-compress/fixture_php.php.json +5 -0
  169. package/tests/fixtures_attended_results/standalone/files-compress/fixture_python.py.json +5 -0
  170. package/tests/fixtures_attended_results/standalone/files-compress/fixture_ruby.rb.json +5 -0
  171. package/tests/fixtures_attended_results/standalone/files-compress/fixture_rust.rs.json +5 -0
  172. package/tests/fixtures_attended_results/standalone/files-compress/fixture_scala.scala.json +5 -0
  173. package/tests/fixtures_attended_results/standalone/files-compress/fixture_shell.sh.json +5 -0
  174. package/tests/fixtures_attended_results/standalone/files-compress/fixture_swift.swift.json +5 -0
  175. package/tests/fixtures_attended_results/standalone/files-compress/fixture_typescript.ts.json +5 -0
  176. package/tests/fixtures_attended_results/standalone/files-compress/fixture_zig.zig.json +5 -0
  177. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_c.c.json +5 -0
  178. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_cpp.cpp.json +5 -0
  179. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_csharp.cs.json +5 -0
  180. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_elixir.ex.json +5 -0
  181. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_go.go.json +5 -0
  182. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_haskell.hs.json +5 -0
  183. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_java.java.json +5 -0
  184. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_javascript.js.json +5 -0
  185. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_kotlin.kt.json +5 -0
  186. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_lua.lua.json +5 -0
  187. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_perl.pl.json +5 -0
  188. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_php.php.json +5 -0
  189. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_python.py.json +5 -0
  190. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_ruby.rb.json +5 -0
  191. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_rust.rs.json +5 -0
  192. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_scala.scala.json +5 -0
  193. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_shell.sh.json +5 -0
  194. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_swift.swift.json +5 -0
  195. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_typescript.ts.json +5 -0
  196. package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_zig.zig.json +5 -0
  197. package/tests/fixtures_attended_results/standalone/files-find/fixture_c.c.json +5 -0
  198. package/tests/fixtures_attended_results/standalone/files-find/fixture_cpp.cpp.json +5 -0
  199. package/tests/fixtures_attended_results/standalone/files-find/fixture_csharp.cs.json +5 -0
  200. package/tests/fixtures_attended_results/standalone/files-find/fixture_elixir.ex.json +5 -0
  201. package/tests/fixtures_attended_results/standalone/files-find/fixture_go.go.json +5 -0
  202. package/tests/fixtures_attended_results/standalone/files-find/fixture_haskell.hs.json +5 -0
  203. package/tests/fixtures_attended_results/standalone/files-find/fixture_java.java.json +5 -0
  204. package/tests/fixtures_attended_results/standalone/files-find/fixture_javascript.js.json +5 -0
  205. package/tests/fixtures_attended_results/standalone/files-find/fixture_kotlin.kt.json +5 -0
  206. package/tests/fixtures_attended_results/standalone/files-find/fixture_lua.lua.json +5 -0
  207. package/tests/fixtures_attended_results/standalone/files-find/fixture_perl.pl.json +5 -0
  208. package/tests/fixtures_attended_results/standalone/files-find/fixture_php.php.json +5 -0
  209. package/tests/fixtures_attended_results/standalone/files-find/fixture_python.py.json +5 -0
  210. package/tests/fixtures_attended_results/standalone/files-find/fixture_ruby.rb.json +5 -0
  211. package/tests/fixtures_attended_results/standalone/files-find/fixture_rust.rs.json +5 -0
  212. package/tests/fixtures_attended_results/standalone/files-find/fixture_scala.scala.json +5 -0
  213. package/tests/fixtures_attended_results/standalone/files-find/fixture_shell.sh.json +5 -0
  214. package/tests/fixtures_attended_results/standalone/files-find/fixture_swift.swift.json +5 -0
  215. package/tests/fixtures_attended_results/standalone/files-find/fixture_typescript.ts.json +5 -0
  216. package/tests/fixtures_attended_results/standalone/files-find/fixture_zig.zig.json +5 -0
  217. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_c.c.json +5 -0
  218. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_cpp.cpp.json +5 -0
  219. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_csharp.cs.json +5 -0
  220. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_elixir.ex.json +5 -0
  221. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_go.go.json +5 -0
  222. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_haskell.hs.json +5 -0
  223. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_java.java.json +5 -0
  224. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_javascript.js.json +5 -0
  225. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_kotlin.kt.json +5 -0
  226. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_lua.lua.json +5 -0
  227. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_perl.pl.json +5 -0
  228. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_php.php.json +5 -0
  229. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_python.py.json +5 -0
  230. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_ruby.rb.json +5 -0
  231. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_rust.rs.json +5 -0
  232. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_scala.scala.json +5 -0
  233. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_shell.sh.json +5 -0
  234. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_swift.swift.json +5 -0
  235. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_typescript.ts.json +5 -0
  236. package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_zig.zig.json +5 -0
  237. package/tests/fixtures_attended_results/standalone/files-references/fixture_c.c.json +5 -0
  238. package/tests/fixtures_attended_results/standalone/files-references/fixture_cpp.cpp.json +5 -0
  239. package/tests/fixtures_attended_results/standalone/files-references/fixture_csharp.cs.json +5 -0
  240. package/tests/fixtures_attended_results/standalone/files-references/fixture_elixir.ex.json +5 -0
  241. package/tests/fixtures_attended_results/standalone/files-references/fixture_go.go.json +5 -0
  242. package/tests/fixtures_attended_results/standalone/files-references/fixture_haskell.hs.json +5 -0
  243. package/tests/fixtures_attended_results/standalone/files-references/fixture_java.java.json +5 -0
  244. package/tests/fixtures_attended_results/standalone/files-references/fixture_javascript.js.json +5 -0
  245. package/tests/fixtures_attended_results/standalone/files-references/fixture_kotlin.kt.json +5 -0
  246. package/tests/fixtures_attended_results/standalone/files-references/fixture_lua.lua.json +5 -0
  247. package/tests/fixtures_attended_results/standalone/files-references/fixture_perl.pl.json +5 -0
  248. package/tests/fixtures_attended_results/standalone/files-references/fixture_php.php.json +5 -0
  249. package/tests/fixtures_attended_results/standalone/files-references/fixture_python.py.json +5 -0
  250. package/tests/fixtures_attended_results/standalone/files-references/fixture_ruby.rb.json +5 -0
  251. package/tests/fixtures_attended_results/standalone/files-references/fixture_rust.rs.json +5 -0
  252. package/tests/fixtures_attended_results/standalone/files-references/fixture_scala.scala.json +5 -0
  253. package/tests/fixtures_attended_results/standalone/files-references/fixture_shell.sh.json +5 -0
  254. package/tests/fixtures_attended_results/standalone/files-references/fixture_swift.swift.json +5 -0
  255. package/tests/fixtures_attended_results/standalone/files-references/fixture_typescript.ts.json +5 -0
  256. package/tests/fixtures_attended_results/standalone/files-references/fixture_zig.zig.json +5 -0
  257. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_c.c.json +5 -0
  258. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_cpp.cpp.json +5 -0
  259. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_csharp.cs.json +5 -0
  260. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_elixir.ex.json +5 -0
  261. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_go.go.json +5 -0
  262. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_haskell.hs.json +5 -0
  263. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_java.java.json +5 -0
  264. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_javascript.js.json +5 -0
  265. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_kotlin.kt.json +5 -0
  266. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_lua.lua.json +5 -0
  267. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_perl.pl.json +5 -0
  268. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_php.php.json +5 -0
  269. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_python.py.json +5 -0
  270. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_ruby.rb.json +5 -0
  271. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_rust.rs.json +5 -0
  272. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_scala.scala.json +5 -0
  273. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_shell.sh.json +5 -0
  274. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_swift.swift.json +5 -0
  275. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_typescript.ts.json +5 -0
  276. package/tests/fixtures_attended_results/standalone/files-tokens/fixture_zig.zig.json +5 -0
  277. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_c.c.json +5 -0
  278. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_cpp.cpp.json +5 -0
  279. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_csharp.cs.json +5 -0
  280. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_elixir.ex.json +5 -0
  281. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_go.go.json +5 -0
  282. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_haskell.hs.json +5 -0
  283. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_java.java.json +5 -0
  284. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_javascript.js.json +5 -0
  285. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_kotlin.kt.json +5 -0
  286. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_lua.lua.json +5 -0
  287. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_perl.pl.json +5 -0
  288. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_php.php.json +5 -0
  289. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_python.py.json +5 -0
  290. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_ruby.rb.json +5 -0
  291. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_rust.rs.json +5 -0
  292. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_scala.scala.json +5 -0
  293. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_shell.sh.json +5 -0
  294. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_swift.swift.json +5 -0
  295. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_typescript.ts.json +5 -0
  296. package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_zig.zig.json +5 -0
  297. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_c.c.json +5 -0
  298. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_cpp.cpp.json +5 -0
  299. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_csharp.cs.json +5 -0
  300. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_elixir.ex.json +5 -0
  301. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_go.go.json +5 -0
  302. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_haskell.hs.json +5 -0
  303. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_java.java.json +5 -0
  304. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_javascript.js.json +5 -0
  305. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_kotlin.kt.json +5 -0
  306. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_lua.lua.json +5 -0
  307. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_perl.pl.json +5 -0
  308. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_php.php.json +5 -0
  309. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_python.py.json +5 -0
  310. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_ruby.rb.json +5 -0
  311. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_rust.rs.json +5 -0
  312. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_scala.scala.json +5 -0
  313. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_shell.sh.json +5 -0
  314. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_swift.swift.json +5 -0
  315. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_typescript.ts.json +5 -0
  316. package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_zig.zig.json +5 -0
  317. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_c.c.json +5 -0
  318. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_cpp.cpp.json +5 -0
  319. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_csharp.cs.json +5 -0
  320. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_elixir.ex.json +5 -0
  321. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_go.go.json +5 -0
  322. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_haskell.hs.json +5 -0
  323. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_java.java.json +5 -0
  324. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_javascript.js.json +5 -0
  325. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_kotlin.kt.json +5 -0
  326. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_lua.lua.json +5 -0
  327. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_perl.pl.json +5 -0
  328. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_php.php.json +5 -0
  329. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_python.py.json +5 -0
  330. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_ruby.rb.json +5 -0
  331. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_rust.rs.json +5 -0
  332. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_scala.scala.json +5 -0
  333. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_shell.sh.json +5 -0
  334. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_swift.swift.json +5 -0
  335. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_typescript.ts.json +5 -0
  336. package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_zig.zig.json +5 -0
  337. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_c.c.json +5 -0
  338. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_cpp.cpp.json +5 -0
  339. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_csharp.cs.json +5 -0
  340. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_elixir.ex.json +5 -0
  341. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_go.go.json +5 -0
  342. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_haskell.hs.json +5 -0
  343. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_java.java.json +5 -0
  344. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_javascript.js.json +5 -0
  345. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_kotlin.kt.json +5 -0
  346. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_lua.lua.json +5 -0
  347. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_perl.pl.json +5 -0
  348. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_php.php.json +5 -0
  349. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_python.py.json +5 -0
  350. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_ruby.rb.json +5 -0
  351. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_rust.rs.json +5 -0
  352. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_scala.scala.json +5 -0
  353. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_shell.sh.json +5 -0
  354. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_swift.swift.json +5 -0
  355. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_typescript.ts.json +5 -0
  356. package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_zig.zig.json +5 -0
  357. package/tests/helpers.ts +204 -0
  358. package/tests/oracle-project.test.ts +63 -0
  359. package/tests/oracle-standalone.test.ts +48 -0
  360. package/tests/prompt-rendering.test.ts +66 -0
  361. package/tests/release-workflow.test.ts +133 -0
@@ -0,0 +1,815 @@
1
+ /**
2
+ * @file
3
+ * @brief Verifies imported CLI command-option regressions against the TypeScript implementation.
4
+ * @details Maintains an executable mapping from imported source regression identifiers to TypeScript parity targets, executes oracle-backed and direct-behavior command scenarios, and inspects persisted config side effects for CLI-only static-check enablement. Runtime is dominated by subprocess execution, git operations, and temporary filesystem setup. Side effects are limited to temporary files, temporary git repositories, and child processes.
5
+ */
6
+
7
+ import test, { type TestContext } from "node:test";
8
+ import assert from "node:assert/strict";
9
+ import fs from "node:fs";
10
+ import path from "node:path";
11
+ import { spawnSync } from "node:child_process";
12
+ import {
13
+ DEFAULT_DOCS_DIR,
14
+ getProjectConfigPath,
15
+ type UseReqConfig,
16
+ } from "../src/core/config.js";
17
+ import { formatRuntimePathForDisplay } from "../src/core/path-context.js";
18
+ import { detectLanguage } from "../src/core/generate-markdown.js";
19
+ import { LANGUAGE_TAGS } from "../src/core/find-constructs.js";
20
+ import {
21
+ createTempDir,
22
+ getFixtureFiles,
23
+ initFixtureRepo,
24
+ readProjectConfigJson,
25
+ runNodeCli,
26
+ runPythonCli,
27
+ saveFixtureConfigs,
28
+ writeExecutableScript,
29
+ } from "./helpers.js";
30
+
31
+ /**
32
+ * @brief Describes one executable TypeScript regression target.
33
+ * @details Each target exposes a stable identifier plus a runner that performs the imported command-option assertions. The interface is compile-time only and introduces no runtime side effects.
34
+ */
35
+ interface TargetParityCase {
36
+ id: string;
37
+ run: (t: TestContext) => Promise<void> | void;
38
+ }
39
+
40
+ /**
41
+ * @brief Maps one imported source regression identifier to a TypeScript target identifier.
42
+ * @details The mapping is curated to current TypeScript CLI scope so coverage verification can assert that every imported case has a maintained executable target. The interface is compile-time only and adds no runtime cost.
43
+ */
44
+ interface ImportedCaseMapping {
45
+ sourceId: string;
46
+ targetId: string;
47
+ }
48
+
49
+ /**
50
+ * @brief Compares one Node CLI invocation with the Python oracle.
51
+ * @details Executes both CLIs with the same arguments, cwd, and optional environment overrides, then asserts identical exit status, stdout, and stderr payloads. Runtime is dominated by subprocess execution. Side effects are limited to child-process creation.
52
+ * @param[in] args {string[]} CLI arguments forwarded to both implementations.
53
+ * @param[in] cwd {string} Working directory for both subprocesses.
54
+ * @param[in] envOverrides {NodeJS.ProcessEnv | undefined} Optional shared environment overrides.
55
+ * @return {void} No return value.
56
+ */
57
+ function assertCliParity(args: string[], cwd: string, envOverrides?: NodeJS.ProcessEnv): void {
58
+ const node = runNodeCli(args, cwd, envOverrides);
59
+ const python = runPythonCli(args, cwd, envOverrides);
60
+ assert.equal(node.status, python.status, `exit code mismatch for ${args.join(" ")}`);
61
+ assert.equal(node.stdout, python.stdout, `stdout mismatch for ${args.join(" ")}`);
62
+ assert.equal(node.stderr, python.stderr, `stderr mismatch for ${args.join(" ")}`);
63
+ }
64
+
65
+ /**
66
+ * @brief Creates one temporary file and registers recursive cleanup on the active test context.
67
+ * @details Allocates a unique directory under the system temp root, writes the requested file content, and schedules directory removal after the enclosing test completes. Runtime is O(n) in content size. Side effects include directory creation and file writes.
68
+ * @param[in] t {test.TestContext} Active Node test context used for cleanup.
69
+ * @param[in] fileName {string} File name to create inside the temporary directory.
70
+ * @param[in] content {string} UTF-8 file payload.
71
+ * @return {string} Absolute file path.
72
+ */
73
+ function createScratchFile(t: TestContext, fileName: string, content: string): string {
74
+ const tempDir = createTempDir("pi-usereq-cli-case-");
75
+ t.after(() => {
76
+ fs.rmSync(tempDir, { recursive: true, force: true });
77
+ });
78
+ const filePath = path.join(tempDir, fileName);
79
+ fs.writeFileSync(filePath, content, "utf8");
80
+ return filePath;
81
+ }
82
+
83
+ /**
84
+ * @brief Creates a fixture git repository and registers cleanup on the active test context.
85
+ * @details Delegates to `initFixtureRepo`, then removes the temporary repository after the enclosing test completes. Runtime is dominated by git initialization and fixture copying. Side effects include temporary repository creation and git subprocess execution.
86
+ * @param[in] t {test.TestContext} Active Node test context used for cleanup.
87
+ * @param[in] options {Parameters<typeof initFixtureRepo>[0]} Repository initialization options.
88
+ * @return {{ projectBase: string; config: UseReqConfig }} Fixture project descriptor.
89
+ */
90
+ function createFixtureRepo(
91
+ t: TestContext,
92
+ options: Parameters<typeof initFixtureRepo>[0] = {},
93
+ ): { projectBase: string; config: UseReqConfig } {
94
+ const repo = initFixtureRepo(options);
95
+ t.after(() => {
96
+ fs.rmSync(repo.projectBase, { recursive: true, force: true });
97
+ });
98
+ return repo;
99
+ }
100
+
101
+ /**
102
+ * @brief Resolves the broadest tag filter supported by one fixture file.
103
+ * @details Detects the file language, loads the language-specific supported tag set, and returns a stable pipe-delimited filter string for `--files-find`. Runtime is O(k) in tag count. No side effects occur.
104
+ * @param[in] filePath {string} Fixture file path.
105
+ * @return {string} Pipe-delimited tag filter.
106
+ */
107
+ function getFixtureTagFilter(filePath: string): string {
108
+ const language = detectLanguage(filePath);
109
+ assert.ok(language, `unsupported fixture language for ${filePath}`);
110
+ const tags = [...(LANGUAGE_TAGS[language!] ?? [])].sort();
111
+ assert.ok(tags.length > 0, `missing LANGUAGE_TAGS entry for ${language}`);
112
+ return tags.join("|");
113
+ }
114
+
115
+ /**
116
+ * @brief Returns the persisted static-check entries for one language.
117
+ * @details Reads the raw project config JSON so tests can assert entry order, duplicate suppression, and metadata preservation exactly as written. Runtime is O(n) in config file size. Side effects are limited to filesystem reads.
118
+ * @param[in] projectBase {string} Fixture project root.
119
+ * @param[in] language {string} Canonical language key.
120
+ * @return {Array<Record<string, unknown>>} Persisted entry array or an empty array.
121
+ */
122
+ function getStaticCheckEntries(projectBase: string, language: string): Array<Record<string, unknown>> {
123
+ const payload = readProjectConfigJson(projectBase);
124
+ const staticCheck = (payload["static-check"] ?? {}) as Record<string, Array<Record<string, unknown>>>;
125
+ return staticCheck[language] ?? [];
126
+ }
127
+
128
+ /**
129
+ * @brief Creates a command-logging shell script for static-check integration tests.
130
+ * @details Writes an executable script that appends one tagged invocation record per run into the provided log file. Runtime is O(n) in script size. Side effects include file writes and permission changes.
131
+ * @param[in] projectBase {string} Fixture project root containing the helper script.
132
+ * @param[in] scriptName {string} Script file name.
133
+ * @param[in] tag {string} Stable prefix emitted into the log file.
134
+ * @param[in] logPath {string} Absolute log-file destination.
135
+ * @return {string} Absolute script path.
136
+ */
137
+ function createLoggingScript(projectBase: string, scriptName: string, tag: string, logPath: string): string {
138
+ const escapedLogPath = logPath.replace(/'/g, "'\\''");
139
+ const escapedTag = tag.replace(/'/g, "'\\''");
140
+ return writeExecutableScript(
141
+ path.join(projectBase, "bin", scriptName),
142
+ `#!/usr/bin/env bash
143
+ set -eu
144
+ printf '%s|%s\n' '${escapedTag}' "$*" >> '${escapedLogPath}'
145
+ `,
146
+ );
147
+ }
148
+
149
+ /**
150
+ * @brief Lists the imported direct-CLI regression mappings maintained by this suite.
151
+ * @details Each mapping identifies one imported source regression and the TypeScript target that executes its analogous behavior. The array is treated as normative test metadata for TST-014. Access complexity is O(1).
152
+ */
153
+ const IMPORTED_CASE_MAPPINGS: ImportedCaseMapping[] = [
154
+ { sourceId: "test_files_commands.py::TestFilesTokensCommand::test_files_tokens_basic", targetId: "oracle::standalone::files-tokens-explicit-files" },
155
+ { sourceId: "test_files_commands.py::TestFilesTokensCommand::test_files_tokens_no_base_required", targetId: "oracle::standalone::files-tokens-explicit-files" },
156
+ { sourceId: "test_files_commands.py::TestFilesTokensCommand::test_files_tokens_skip_missing", targetId: "oracle::standalone::files-tokens-explicit-files" },
157
+ { sourceId: "test_files_commands.py::TestFilesTokensCommand::test_files_tokens_all_missing_errors", targetId: "oracle::standalone::files-tokens-explicit-files" },
158
+ { sourceId: "test_files_commands.py::TestFilesReferencesCommand::test_files_references_basic", targetId: "direct::files-references-explicit-files" },
159
+ { sourceId: "test_files_commands.py::TestFilesReferencesCommand::test_files_references_no_base_required", targetId: "direct::files-references-explicit-files" },
160
+ { sourceId: "test_files_commands.py::TestFilesReferencesCommand::test_files_references_verbose_outputs_progress", targetId: "direct::files-references-verbose" },
161
+ { sourceId: "test_files_commands.py::TestFilesCompressCommand::test_files_compress_basic", targetId: "oracle::standalone::files-compress-line-number-modes" },
162
+ { sourceId: "test_files_commands.py::TestFilesCompressCommand::test_files_compress_no_base_required", targetId: "oracle::standalone::files-compress-line-number-modes" },
163
+ { sourceId: "test_files_commands.py::TestFilesCompressCommand::test_files_compress_no_line_numbers_by_default", targetId: "oracle::standalone::files-compress-line-number-modes" },
164
+ { sourceId: "test_files_commands.py::TestFilesCompressCommand::test_files_compress_enable_line_numbers", targetId: "oracle::standalone::files-compress-line-number-modes" },
165
+ { sourceId: "test_files_commands.py::TestFilesCompressCommand::test_files_compress_verbose_outputs_progress", targetId: "oracle::standalone::files-compress-verbose" },
166
+ { sourceId: "test_files_commands.py::TestReferencesCommand::test_rejects_base", targetId: "direct::references-modes" },
167
+ { sourceId: "test_files_commands.py::TestReferencesCommand::test_references_with_here", targetId: "direct::references-modes" },
168
+ { sourceId: "test_files_commands.py::TestReferencesCommand::test_references_prepends_files_structure", targetId: "direct::references-modes" },
169
+ { sourceId: "test_files_commands.py::TestReferencesCommand::test_references_from_config", targetId: "direct::references-modes" },
170
+ { sourceId: "test_files_commands.py::TestCompressCommand::test_rejects_base", targetId: "oracle::project::compress-modes" },
171
+ { sourceId: "test_files_commands.py::TestCompressCommand::test_compress_with_here", targetId: "oracle::project::compress-modes" },
172
+ { sourceId: "test_files_commands.py::TestCompressCommand::test_compress_no_line_numbers_by_default", targetId: "oracle::project::compress-modes" },
173
+ { sourceId: "test_files_commands.py::TestCompressCommand::test_compress_enable_line_numbers", targetId: "oracle::project::compress-modes" },
174
+ { sourceId: "test_files_commands.py::TestTokensCommand::test_tokens_rejects_base", targetId: "oracle::project::tokens-doc-selection" },
175
+ { sourceId: "test_files_commands.py::TestTokensCommand::test_tokens_implies_here_and_uses_config_docs_dir", targetId: "oracle::project::tokens-doc-selection" },
176
+ { sourceId: "test_files_commands.py::TestTokensCommand::test_tokens_counts_docs_files", targetId: "oracle::project::tokens-doc-selection" },
177
+ { sourceId: "test_files_commands.py::TestFindCommandVerbose::test_files_find_default_suppresses_progress", targetId: "oracle::standalone::files-find-modes" },
178
+ { sourceId: "test_files_commands.py::TestFindCommandVerbose::test_files_find_verbose_outputs_progress", targetId: "oracle::standalone::files-find-modes" },
179
+ { sourceId: "test_files_commands.py::TestFindCommandVerbose::test_files_find_enable_line_numbers", targetId: "oracle::standalone::files-find-modes" },
180
+ { sourceId: "test_files_commands.py::TestFindCommandVerbose::test_find_verbose_outputs_progress", targetId: "oracle::project::find-modes" },
181
+ { sourceId: "test_files_commands.py::TestFindCommandVerbose::test_find_enable_line_numbers", targetId: "oracle::project::find-modes" },
182
+ { sourceId: "test_files_commands.py::TestFindCommandVerbose::test_find_rejects_base", targetId: "oracle::project::find-modes" },
183
+ { sourceId: "test_static_check.py::TestCLIIntegrationStaticCheck::test_main_dispatches_dummy", targetId: "oracle::standalone::test-static-check-dispatch" },
184
+ { sourceId: "test_static_check.py::TestCLIIntegrationStaticCheck::test_main_dispatches_unknown_subcommand", targetId: "oracle::standalone::test-static-check-dispatch" },
185
+ { sourceId: "test_static_check.py::TestCLIIntegrationStaticCheck::test_main_static_check_is_standalone", targetId: "oracle::standalone::test-static-check-dispatch" },
186
+ { sourceId: "test_static_check.py::TestFilesStaticCheckCLI::test_files_static_check_dummy_language", targetId: "direct::files-static-check-matrix" },
187
+ { sourceId: "test_static_check.py::TestFilesStaticCheckCLI::test_files_static_check_unknown_extension_skipped", targetId: "direct::files-static-check-matrix" },
188
+ { sourceId: "test_static_check.py::TestFilesStaticCheckCLI::test_files_static_check_lang_not_in_config_skipped", targetId: "direct::files-static-check-matrix" },
189
+ { sourceId: "test_static_check.py::TestFilesStaticCheckCLI::test_files_static_check_fail_propagates_exit_code", targetId: "direct::files-static-check-matrix" },
190
+ { sourceId: "test_static_check.py::TestFilesStaticCheckCLI::test_files_static_check_multiple_files_mixed", targetId: "direct::files-static-check-matrix" },
191
+ { sourceId: "test_static_check.py::TestFilesStaticCheckCLI::test_files_static_check_runs_all_entries_for_language_in_order", targetId: "direct::files-static-check-matrix" },
192
+ { sourceId: "test_static_check.py::TestFilesStaticCheckCLI::test_files_static_check_command_invokes_params_before_filename", targetId: "direct::files-static-check-matrix" },
193
+ { sourceId: "test_static_check.py::TestStaticCheckProjectScan::test_static_check_dummy_on_project_files", targetId: "direct::project-static-check-matrix" },
194
+ { sourceId: "test_static_check.py::TestStaticCheckProjectScan::test_static_check_skips_unconfigured_language", targetId: "direct::project-static-check-matrix" },
195
+ { sourceId: "test_static_check.py::TestStaticCheckProjectScan::test_static_check_fail_propagates_exit_code", targetId: "direct::project-static-check-matrix" },
196
+ { sourceId: "test_static_check.py::TestStaticCheckProjectScan::test_static_check_no_source_files_raises", targetId: "direct::project-static-check-matrix" },
197
+ { sourceId: "test_static_check.py::TestStaticCheckProjectScan::test_static_check_rejects_base", targetId: "direct::project-static-check-matrix" },
198
+ { sourceId: "test_static_check.py::TestStaticCheckProjectScan::test_static_check_uses_cwd_as_project_base_with_here", targetId: "direct::project-static-check-matrix" },
199
+ { sourceId: "test_static_check.py::TestStaticCheckProjectScan::test_static_check_runs_all_entries_for_language_in_order", targetId: "direct::project-static-check-matrix" },
200
+ { sourceId: "test_static_check.py::TestStaticCheckProjectScan::test_static_check_command_invokes_params_before_filename", targetId: "direct::project-static-check-matrix" },
201
+ { sourceId: "test_static_check.py::TestStaticCheckProjectScan::test_static_check_includes_tests_dir_files", targetId: "direct::project-static-check-matrix" },
202
+ { sourceId: "test_static_check.py::TestStaticCheckProjectScan::test_static_check_ignores_tests_fixtures_subtree", targetId: "direct::project-static-check-matrix" },
203
+ { sourceId: "test_static_check.py::TestStaticCheckProjectScan::test_static_check_tests_dir_only", targetId: "direct::project-static-check-matrix" },
204
+ { sourceId: "test_static_check.py::TestEnableStaticCheckExecutableValidation::test_non_update_command_requires_executable_before_config_write", targetId: "direct::enable-static-check-invalid-command" },
205
+ { sourceId: "test_static_check.py::TestEnableStaticCheckConfigPersistence::test_enable_static_check_saved_to_config_json", targetId: "direct::enable-static-check-persistence" },
206
+ { sourceId: "test_static_check.py::TestEnableStaticCheckConfigPersistence::test_enable_static_check_command_saves_cmd_and_params", targetId: "direct::enable-static-check-persistence" },
207
+ { sourceId: "test_static_check.py::TestEnableStaticCheckConfigPersistence::test_enable_static_check_multiple_langs", targetId: "direct::enable-static-check-persistence" },
208
+ { sourceId: "test_static_check.py::TestEnableStaticCheckConfigPersistence::test_enable_static_check_preserves_multiple_entries_same_lang", targetId: "direct::enable-static-check-persistence" },
209
+ { sourceId: "test_static_check.py::TestEnableStaticCheckConfigPersistence::test_enable_static_check_deduplicates_identical_entries_same_invocation", targetId: "direct::enable-static-check-deduplication" },
210
+ { sourceId: "test_static_check.py::TestEnableStaticCheckConfigPersistence::test_enable_static_check_preserves_different_params_same_module", targetId: "direct::enable-static-check-deduplication" },
211
+ { sourceId: "test_static_check.py::TestEnableStaticCheckConfigPersistence::test_enable_static_check_update_does_not_duplicate_existing_identical_entry", targetId: "direct::enable-static-check-merge" },
212
+ { sourceId: "test_static_check.py::TestEnableStaticCheckConfigPersistence::test_enable_static_check_update_preserves_existing_tools_when_adding_new", targetId: "direct::enable-static-check-merge" },
213
+ { sourceId: "test_static_check.py::TestEnableStaticCheckConfigPersistence::test_enable_static_check_update_adds_same_module_different_params", targetId: "direct::enable-static-check-merge" },
214
+ { sourceId: "test_static_check.py::TestEnableStaticCheckConfigPersistence::test_enable_static_check_update_ignores_non_identity_fields_for_duplicate_check", targetId: "direct::enable-static-check-merge" },
215
+ { sourceId: "test_static_check.py::TestEnableStaticCheckConfigPersistence::test_non_update_enable_static_check_preserves_existing_entries", targetId: "direct::enable-static-check-merge" },
216
+ { sourceId: "test_cli.py::TestGitCheckCommand::test_git_check_on_clean_repo_succeeds", targetId: "direct::git-check-matrix" },
217
+ { sourceId: "test_cli.py::TestGitCheckCommand::test_git_check_on_dirty_repo_fails", targetId: "direct::git-check-matrix" },
218
+ { sourceId: "test_cli.py::TestGitCheckCommand::test_git_check_rejects_base", targetId: "direct::git-check-matrix" },
219
+ { sourceId: "test_cli.py::TestDocsCheckCommand::test_docs_check_all_present_succeeds", targetId: "direct::docs-check-matrix" },
220
+ { sourceId: "test_cli.py::TestDocsCheckCommand::test_docs_check_missing_requirements", targetId: "direct::docs-check-matrix" },
221
+ { sourceId: "test_cli.py::TestDocsCheckCommand::test_docs_check_missing_workflow", targetId: "direct::docs-check-matrix" },
222
+ { sourceId: "test_cli.py::TestGitWtNameCommand::test_git_wt_name_format", targetId: "direct::git-worktree-matrix" },
223
+ { sourceId: "test_cli.py::TestGitWtCreateDeleteCommands::test_wt_create_invalid_name_fails", targetId: "direct::git-worktree-matrix" },
224
+ { sourceId: "test_cli.py::TestGitWtCreateDeleteCommands::test_wt_create_and_delete_roundtrip", targetId: "direct::git-worktree-matrix" },
225
+ { sourceId: "test_cli.py::TestGitWtCreateDeleteCommands::test_wt_delete_rejects_partial_name_matches", targetId: "direct::git-worktree-matrix" },
226
+ { sourceId: "test_cli.py::TestGitWtCreateDeleteCommands::test_wt_delete_nonexistent_fails", targetId: "direct::git-worktree-matrix" },
227
+ { sourceId: "test_cli.py::TestGitRepositoryPathCommands::test_git_path_prints_configured_git_path", targetId: "direct::git-path-and-base-path-matrix" },
228
+ { sourceId: "test_cli.py::TestGitRepositoryPathCommands::test_get_base_path_prints_configured_base_path", targetId: "direct::git-path-and-base-path-matrix" },
229
+ { sourceId: "test_cli.py::TestGitRepositoryPathCommands::test_git_path_rejects_base", targetId: "direct::git-path-and-base-path-matrix" },
230
+ { sourceId: "test_cli.py::TestGitRepositoryPathCommands::test_get_base_path_rejects_base", targetId: "direct::git-path-and-base-path-matrix" },
231
+ ];
232
+
233
+ /**
234
+ * @brief Lists the executable TypeScript regression targets maintained by this suite.
235
+ * @details Each target runs one or more command-option scenarios that collectively cover the imported mappings above. The array is executed by the Node test runner through nested subtests. Access complexity is O(1).
236
+ */
237
+ const TARGET_CASES: TargetParityCase[] = [
238
+ {
239
+ id: "oracle::standalone::files-tokens-explicit-files",
240
+ run(t) {
241
+ const filePath = createScratchFile(t, "sample.py", "def hello():\n return 'world'\n");
242
+ const missingOne = path.join(path.dirname(filePath), "missing-one.py");
243
+ const missingTwo = path.join(path.dirname(filePath), "missing-two.py");
244
+ const basic = runNodeCli(["--files-tokens", filePath], path.dirname(filePath));
245
+ assert.equal(basic.status, 0, basic.stderr);
246
+ assert.match(basic.stdout, /Pack Summary/);
247
+ assert.match(basic.stdout, /tokens/);
248
+ const mixed = runNodeCli(["--files-tokens", missingOne, filePath], path.dirname(filePath));
249
+ assert.equal(mixed.status, 0, mixed.stderr);
250
+ assert.match(mixed.stderr, /Warning/);
251
+ const missing = runNodeCli(["--files-tokens", missingOne, missingTwo], path.dirname(filePath));
252
+ assert.notEqual(missing.status, 0);
253
+ },
254
+ },
255
+ {
256
+ id: "direct::files-references-explicit-files",
257
+ run(t) {
258
+ const filePath = createScratchFile(
259
+ t,
260
+ "sample.ts",
261
+ "/**\n * @file\n * @brief Sample file.\n */\n\n/**\n * @brief Build sample value.\n * @param[in] input {string} Caller text.\n * @return {string} Upper-cased text.\n */\nexport function buildSample(input: string): string {\n return input.toUpperCase();\n}\n",
262
+ );
263
+ const cwd = path.dirname(filePath);
264
+ const result = runNodeCli(["--files-references", filePath], cwd);
265
+ assert.equal(result.status, 0, result.stderr);
266
+ const payload = JSON.parse(result.stdout) as {
267
+ summary: { analyzed_file_count: number };
268
+ repository: { file_canonical_paths: string[] };
269
+ files: Array<{
270
+ canonical_path: string;
271
+ file_doxygen?: { brief?: string[] };
272
+ symbols: Array<{
273
+ symbol_name: string;
274
+ line_range: [number, number];
275
+ doxygen?: { params?: Array<{ direction: string; parameter_name?: string; value_type?: string }> };
276
+ }>;
277
+ }>;
278
+ };
279
+ assert.equal(payload.summary.analyzed_file_count, 1);
280
+ assert.deepEqual(payload.repository.file_canonical_paths, ["sample.ts"]);
281
+ assert.deepEqual(payload.files[0]?.file_doxygen?.brief, ["Sample file."]);
282
+ assert.equal(payload.files[0]?.canonical_path, "sample.ts");
283
+ assert.equal(payload.files[0]?.symbols[0]?.symbol_name, "buildSample");
284
+ assert.deepEqual(payload.files[0]?.symbols[0]?.line_range, [11, 13]);
285
+ assert.deepEqual(payload.files[0]?.symbols[0]?.doxygen?.params, [
286
+ { direction: "in", parameter_name: "input", value_type: "string", description: "Caller text." },
287
+ ]);
288
+ assert.equal(result.stderr, "");
289
+ },
290
+ },
291
+ {
292
+ id: "direct::files-references-verbose",
293
+ run(t) {
294
+ const filePath = createScratchFile(t, "sample.py", "value = 1\n");
295
+ const result = runNodeCli(["--verbose", "--files-references", filePath], path.dirname(filePath));
296
+ assert.equal(result.status, 0, result.stderr);
297
+ assert.doesNotThrow(() => JSON.parse(result.stdout));
298
+ assert.match(result.stderr, /OK/);
299
+ },
300
+ },
301
+ {
302
+ id: "oracle::standalone::files-compress-line-number-modes",
303
+ run(t) {
304
+ const filePath = createScratchFile(t, "sample.py", "# comment\nvalue = 1\n");
305
+ const cwd = path.dirname(filePath);
306
+ assertCliParity(["--files-compress", filePath], cwd);
307
+ assertCliParity(["--files-compress", filePath, "--enable-line-numbers"], cwd);
308
+ },
309
+ },
310
+ {
311
+ id: "oracle::standalone::files-compress-verbose",
312
+ run(t) {
313
+ const filePath = createScratchFile(t, "sample.py", "value = 1\n");
314
+ assertCliParity(["--verbose", "--files-compress", filePath], path.dirname(filePath));
315
+ },
316
+ },
317
+ {
318
+ id: "direct::references-modes",
319
+ run(t) {
320
+ const { projectBase } = createFixtureRepo(t, { fixtures: [] });
321
+ fs.writeFileSync(path.join(projectBase, "src", "alpha.ts"), "export const ALPHA = 1;\n", "utf8");
322
+ fs.mkdirSync(path.join(projectBase, "src", "nested"), { recursive: true });
323
+ fs.writeFileSync(path.join(projectBase, "src", "nested", "beta.ts"), "export function beta(): number {\n return 2;\n}\n", "utf8");
324
+
325
+ const direct = runNodeCli(["--references"], projectBase);
326
+ assert.equal(direct.status, 0, direct.stderr);
327
+ const directPayload = JSON.parse(direct.stdout) as {
328
+ repository: {
329
+ source_directory_paths: string[];
330
+ file_canonical_paths: string[];
331
+ directory_tree: { children: Array<{ node_name: string; node_kind: string }> };
332
+ };
333
+ files: Array<{ canonical_path: string }>;
334
+ };
335
+ assert.deepEqual(directPayload.repository.source_directory_paths, ["src"]);
336
+ assert.deepEqual(directPayload.repository.file_canonical_paths, ["src/alpha.ts", "src/nested/beta.ts"]);
337
+ assert.equal(directPayload.repository.directory_tree.children[0]?.node_name, "src");
338
+ assert.equal(directPayload.repository.directory_tree.children[0]?.node_kind, "directory");
339
+ assert.deepEqual(directPayload.files.map((file) => file.canonical_path), ["src/alpha.ts", "src/nested/beta.ts"]);
340
+
341
+ const here = runNodeCli(["--here", "--references"], projectBase);
342
+ assert.equal(here.status, 0, here.stderr);
343
+ assert.deepEqual(JSON.parse(here.stdout), directPayload);
344
+
345
+ const baseRejected = runNodeCli(["--base", projectBase, "--references"], projectBase);
346
+ assert.notEqual(baseRejected.status, 0);
347
+ assert.match(baseRejected.stderr, /do not allow --base/);
348
+ },
349
+ },
350
+ {
351
+ id: "oracle::project::compress-modes",
352
+ run(t) {
353
+ const { projectBase } = createFixtureRepo(t);
354
+ assertCliParity(["--compress"], projectBase);
355
+ assertCliParity(["--here", "--compress"], projectBase);
356
+ assertCliParity(["--compress", "--enable-line-numbers"], projectBase);
357
+ assertCliParity(["--base", projectBase, "--compress"], projectBase);
358
+ },
359
+ },
360
+ {
361
+ id: "oracle::project::tokens-doc-selection",
362
+ run(t) {
363
+ const { projectBase, config } = createFixtureRepo(t, {
364
+ fixtures: [],
365
+ docs: {
366
+ "REQUIREMENTS.md": "# Requirements\n",
367
+ "WORKFLOW.md": "# Workflow\n",
368
+ "REFERENCES.md": "# References\n",
369
+ },
370
+ });
371
+ fs.mkdirSync(path.join(projectBase, "docs"), { recursive: true });
372
+ fs.writeFileSync(path.join(projectBase, "docs", "REQUIREMENTS.md"), "# Alt Requirements\n", "utf8");
373
+ fs.writeFileSync(path.join(projectBase, "docs", "WORKFLOW.md"), "# Alt Workflow\n", "utf8");
374
+ fs.writeFileSync(path.join(projectBase, "docs", "REFERENCES.md"), "# Alt References\n", "utf8");
375
+ fs.writeFileSync(path.join(projectBase, "docs", "IGNORED.md"), "# Ignored\n", "utf8");
376
+ const updatedConfig: UseReqConfig = { ...config, "docs-dir": "docs" };
377
+ saveFixtureConfigs(projectBase, updatedConfig);
378
+ assertCliParity(["--tokens"], projectBase);
379
+ assertCliParity(["--base", projectBase, "--tokens"], projectBase);
380
+ },
381
+ },
382
+ {
383
+ id: "oracle::standalone::files-find-modes",
384
+ run(t) {
385
+ const filePath = createScratchFile(t, "sample.py", "def foo():\n return 1\n");
386
+ const cwd = path.dirname(filePath);
387
+ const basic = runNodeCli(["--files-find", "FUNCTION", "foo", filePath], cwd);
388
+ assert.equal(basic.status, 0, basic.stderr);
389
+ assert.equal(basic.stderr, "");
390
+ assert.match(basic.stdout, /### FUNCTION: `foo`/);
391
+ const verbose = runNodeCli(["--verbose", "--files-find", "FUNCTION", "foo", filePath], cwd);
392
+ assert.equal(verbose.status, 0, verbose.stderr);
393
+ assert.match(verbose.stderr, /Found:/);
394
+ const numbered = runNodeCli(["--files-find", "FUNCTION", "foo", filePath, "--enable-line-numbers"], cwd);
395
+ assert.equal(numbered.status, 0, numbered.stderr);
396
+ assert.match(numbered.stdout, /1: def foo\(\):/);
397
+ },
398
+ },
399
+ {
400
+ id: "oracle::project::find-modes",
401
+ run(t) {
402
+ const { projectBase } = createFixtureRepo(t, { fixtures: [] });
403
+ fs.writeFileSync(path.join(projectBase, "src", "find_target.py"), "def foo():\n return 1\n", "utf8");
404
+ const basic = runNodeCli(["--find", "FUNCTION", "foo"], projectBase);
405
+ assert.equal(basic.status, 0, basic.stderr);
406
+ assert.match(basic.stdout, /### FUNCTION: `foo`/);
407
+ const verbose = runNodeCli(["--verbose", "--find", "FUNCTION", "foo"], projectBase);
408
+ assert.equal(verbose.status, 0, verbose.stderr);
409
+ assert.match(verbose.stderr, /Found:/);
410
+ const numbered = runNodeCli(["--find", "FUNCTION", "foo", "--enable-line-numbers"], projectBase);
411
+ assert.equal(numbered.status, 0, numbered.stderr);
412
+ assert.match(numbered.stdout, /1: def foo\(\):/);
413
+ const rejected = runNodeCli(["--base", projectBase, "--find", "FUNCTION", "foo"], projectBase);
414
+ assert.notEqual(rejected.status, 0);
415
+ },
416
+ },
417
+ {
418
+ id: "oracle::standalone::test-static-check-dispatch",
419
+ run(t) {
420
+ const filePath = createScratchFile(t, "sample.py", "value = 1\n");
421
+ const cwd = path.dirname(filePath);
422
+ const dummy = runNodeCli(["--test-static-check", "dummy", filePath], cwd);
423
+ assert.equal(dummy.status, 0, dummy.stderr);
424
+ const bad = runNodeCli(["--test-static-check", "badcmd"], cwd);
425
+ assert.notEqual(bad.status, 0);
426
+ assert.match(bad.stderr, /unknown --test-static-check subcommand/i);
427
+ },
428
+ },
429
+ {
430
+ id: "direct::files-static-check-matrix",
431
+ run(t) {
432
+ const { projectBase, config } = createFixtureRepo(t, { fixtures: [] });
433
+ const logPath = path.join(projectBase, "files-static-check.log");
434
+ const firstScript = createLoggingScript(projectBase, "check-first.sh", "first", logPath);
435
+ const secondScript = createLoggingScript(projectBase, "check-second.sh", "second", logPath);
436
+ const jsFile = path.join(projectBase, "src", "sample.js");
437
+ fs.writeFileSync(jsFile, "const value = 1;\n", "utf8");
438
+ const updatedConfig: UseReqConfig = {
439
+ ...config,
440
+ "static-check": {
441
+ JavaScript: [
442
+ { module: "Command", cmd: firstScript },
443
+ { module: "Command", cmd: secondScript, params: ["--check"] },
444
+ ],
445
+ Python: [{ module: "Dummy" }],
446
+ },
447
+ };
448
+ saveFixtureConfigs(projectBase, updatedConfig);
449
+
450
+ const success = runNodeCli(["--files-static-check", jsFile], projectBase);
451
+ assert.equal(success.status, 0, success.stderr);
452
+ const logLines = fs.readFileSync(logPath, "utf8").trim().split(/\r?\n/);
453
+ assert.deepEqual(logLines, [
454
+ `first|${path.resolve(jsFile)}`,
455
+ `second|--check ${path.resolve(jsFile)}`,
456
+ ]);
457
+
458
+ const unknownExtension = path.join(projectBase, "src", "ignored.txt");
459
+ fs.writeFileSync(unknownExtension, "ignored\n", "utf8");
460
+ const skipped = runNodeCli(["--files-static-check", unknownExtension], projectBase);
461
+ assert.equal(skipped.status, 0, skipped.stderr);
462
+ assert.equal(skipped.stdout, "");
463
+
464
+ const unconfiguredLanguage = path.join(projectBase, "src", "program.c");
465
+ fs.writeFileSync(unconfiguredLanguage, "int main(void){return 0;}\n", "utf8");
466
+ const notConfigured = runNodeCli(["--files-static-check", unconfiguredLanguage], projectBase);
467
+ assert.equal(notConfigured.status, 0, notConfigured.stderr);
468
+ assert.equal(notConfigured.stdout, "");
469
+
470
+ const failingScript = writeExecutableScript(
471
+ path.join(projectBase, "bin", "check-fail.sh"),
472
+ "#!/usr/bin/env bash\nset -eu\nprintf '%s\n' 'failing evidence'\nexit 1\n",
473
+ );
474
+ saveFixtureConfigs(projectBase, {
475
+ ...updatedConfig,
476
+ "static-check": { JavaScript: [{ module: "Command", cmd: failingScript }] },
477
+ });
478
+ const failing = runNodeCli(["--files-static-check", jsFile], projectBase);
479
+ assert.equal(failing.status, 1);
480
+ assert.match(failing.stdout, /failing evidence/);
481
+ },
482
+ },
483
+ {
484
+ id: "direct::project-static-check-matrix",
485
+ run(t) {
486
+ const { projectBase, config } = createFixtureRepo(t, { fixtures: [] });
487
+ const logPath = path.join(projectBase, "project-static-check.log");
488
+ const logScript = createLoggingScript(projectBase, "collect.sh", "collect", logPath);
489
+ fs.mkdirSync(path.join(projectBase, "tests", "fixtures"), { recursive: true });
490
+ const srcFile = path.join(projectBase, "src", "main.py");
491
+ const testFile = path.join(projectBase, "tests", "test_main.py");
492
+ const fixtureFile = path.join(projectBase, "tests", "fixtures", "fixture_c.c");
493
+ fs.writeFileSync(srcFile, "print('src')\n", "utf8");
494
+ fs.writeFileSync(testFile, "print('test')\n", "utf8");
495
+ fs.writeFileSync(fixtureFile, "int main(void){return 0;}\n", "utf8");
496
+ saveFixtureConfigs(projectBase, {
497
+ ...config,
498
+ "static-check": {
499
+ Python: [{ module: "Command", cmd: logScript }],
500
+ C: [{ module: "Command", cmd: logScript }],
501
+ },
502
+ });
503
+
504
+ const success = runNodeCli(["--static-check"], projectBase);
505
+ assert.equal(success.status, 0, success.stderr);
506
+ const dispatched = fs.readFileSync(logPath, "utf8").trim().split(/\r?\n/);
507
+ assert.ok(dispatched.some((line) => line.endsWith(`|${path.resolve(srcFile)}`)));
508
+ assert.ok(dispatched.some((line) => line.endsWith(`|${path.resolve(testFile)}`)));
509
+ assert.ok(!dispatched.some((line) => line.endsWith(`|${path.resolve(fixtureFile)}`)));
510
+
511
+ fs.truncateSync(logPath, 0);
512
+ saveFixtureConfigs(projectBase, {
513
+ ...config,
514
+ "static-check": { Python: [{ module: "Dummy" }] },
515
+ });
516
+ const skipped = runNodeCli(["--static-check"], projectBase);
517
+ assert.equal(skipped.status, 0, skipped.stderr);
518
+ assert.equal(skipped.stdout, "");
519
+
520
+ const baseRejected = runNodeCli(["--base", projectBase, "--static-check"], projectBase);
521
+ assert.notEqual(baseRejected.status, 0);
522
+
523
+ fs.truncateSync(logPath, 0);
524
+ const orderedLogPath = path.join(projectBase, "project-static-check-ordered.log");
525
+ const firstOrderedScript = createLoggingScript(projectBase, "ordered-first.sh", "first", orderedLogPath);
526
+ const secondOrderedScript = createLoggingScript(projectBase, "ordered-second.sh", "second", orderedLogPath);
527
+ saveFixtureConfigs(projectBase, {
528
+ ...config,
529
+ "static-check": {
530
+ Python: [
531
+ { module: "Command", cmd: firstOrderedScript },
532
+ { module: "Command", cmd: secondOrderedScript, params: ["--check"] },
533
+ ],
534
+ },
535
+ });
536
+ const ordered = runNodeCli(["--static-check"], projectBase);
537
+ assert.equal(ordered.status, 0, ordered.stderr);
538
+ const orderedLines = fs.readFileSync(orderedLogPath, "utf8").trim().split(/\r?\n/);
539
+ assert.deepEqual(orderedLines, [
540
+ `first|${path.resolve(srcFile)}`,
541
+ `second|--check ${path.resolve(srcFile)}`,
542
+ `first|${path.resolve(testFile)}`,
543
+ `second|--check ${path.resolve(testFile)}`,
544
+ ]);
545
+
546
+ const failingScript = writeExecutableScript(
547
+ path.join(projectBase, "bin", "collect-fail.sh"),
548
+ "#!/usr/bin/env bash\nset -eu\nprintf '%s\n' 'failing evidence'\nexit 1\n",
549
+ );
550
+ saveFixtureConfigs(projectBase, {
551
+ ...config,
552
+ "static-check": { Python: [{ module: "Command", cmd: failingScript }] },
553
+ });
554
+ const failing = runNodeCli(["--static-check"], projectBase);
555
+ assert.equal(failing.status, 1);
556
+ assert.match(failing.stdout, /failing evidence/);
557
+
558
+ const emptyRepo = createFixtureRepo(t, { fixtures: [] });
559
+ fs.writeFileSync(path.join(emptyRepo.projectBase, "src", "README.txt"), "not source\n", "utf8");
560
+ saveFixtureConfigs(emptyRepo.projectBase, {
561
+ ...emptyRepo.config,
562
+ "static-check": { Python: [{ module: "Dummy" }] },
563
+ });
564
+ const noSource = runNodeCli(["--static-check"], emptyRepo.projectBase);
565
+ assert.equal(noSource.status, 1);
566
+ assert.match(noSource.stderr, /no source files found/i);
567
+ },
568
+ },
569
+ {
570
+ id: "direct::enable-static-check-invalid-command",
571
+ run(t) {
572
+ const projectBase = createTempDir("pi-usereq-enable-static-check-invalid-");
573
+ t.after(() => {
574
+ fs.rmSync(projectBase, { recursive: true, force: true });
575
+ });
576
+ fs.mkdirSync(path.join(projectBase, "src"), { recursive: true });
577
+ fs.mkdirSync(path.join(projectBase, "tests"), { recursive: true });
578
+ fs.mkdirSync(path.join(projectBase, ...DEFAULT_DOCS_DIR.split("/")), { recursive: true });
579
+ const result = runNodeCli(
580
+ ["--base", projectBase, "--enable-static-check", "C=Command,nonexistent_tool_xyz_12345"],
581
+ projectBase,
582
+ );
583
+ assert.equal(result.status, 1);
584
+ assert.match(result.stderr, /not an executable program/);
585
+ assert.ok(!fs.existsSync(getProjectConfigPath(projectBase)));
586
+ },
587
+ },
588
+ {
589
+ id: "direct::enable-static-check-persistence",
590
+ run(t) {
591
+ const { projectBase } = createFixtureRepo(t, { fixtures: [] });
592
+ const persisted = runNodeCli(
593
+ [
594
+ "--base",
595
+ projectBase,
596
+ "--enable-static-check",
597
+ "Python=Pylance",
598
+ "--enable-static-check",
599
+ "C=Command,git,--version",
600
+ "--enable-static-check",
601
+ "Python=Ruff",
602
+ ],
603
+ projectBase,
604
+ );
605
+ assert.equal(persisted.status, 0, persisted.stderr);
606
+ assert.deepEqual(getStaticCheckEntries(projectBase, "Python"), [{ module: "Pylance" }, { module: "Ruff" }]);
607
+ assert.deepEqual(getStaticCheckEntries(projectBase, "C"), [{ module: "Command", cmd: "git", params: ["--version"] }]);
608
+ },
609
+ },
610
+ {
611
+ id: "direct::enable-static-check-deduplication",
612
+ run(t) {
613
+ const { projectBase } = createFixtureRepo(t, { fixtures: [] });
614
+ const result = runNodeCli(
615
+ [
616
+ "--base",
617
+ projectBase,
618
+ "--enable-static-check",
619
+ "Python=Ruff",
620
+ "--enable-static-check",
621
+ "Python=Ruff",
622
+ "--enable-static-check",
623
+ "C=Command,git,--version",
624
+ "--enable-static-check",
625
+ "C=Command,git,--help",
626
+ ],
627
+ projectBase,
628
+ );
629
+ assert.equal(result.status, 0, result.stderr);
630
+ assert.deepEqual(getStaticCheckEntries(projectBase, "Python"), [{ module: "Ruff" }]);
631
+ assert.deepEqual(getStaticCheckEntries(projectBase, "C"), [
632
+ { module: "Command", cmd: "git", params: ["--version"] },
633
+ { module: "Command", cmd: "git", params: ["--help"] },
634
+ ]);
635
+ },
636
+ },
637
+ {
638
+ id: "direct::enable-static-check-merge",
639
+ run(t) {
640
+ const { projectBase, config } = createFixtureRepo(t, { fixtures: [] });
641
+ const preloaded = {
642
+ ...config,
643
+ "static-check": {
644
+ Python: [{ module: "Pylance", meta: "legacy-note" } as unknown as Record<string, unknown>],
645
+ JavaScript: [{ module: "Command", cmd: "git", params: ["--version"] }],
646
+ } as unknown as UseReqConfig["static-check"],
647
+ } as UseReqConfig;
648
+ saveFixtureConfigs(projectBase, preloaded);
649
+ const result = runNodeCli(
650
+ [
651
+ "--base",
652
+ projectBase,
653
+ "--enable-static-check",
654
+ "Python=Pylance",
655
+ "--enable-static-check",
656
+ "Python=Ruff",
657
+ "--enable-static-check",
658
+ "JavaScript=Command,git,--help",
659
+ ],
660
+ projectBase,
661
+ );
662
+ assert.equal(result.status, 0, result.stderr);
663
+ assert.deepEqual(getStaticCheckEntries(projectBase, "Python"), [
664
+ { module: "Pylance", meta: "legacy-note" },
665
+ { module: "Ruff" },
666
+ ]);
667
+ assert.deepEqual(getStaticCheckEntries(projectBase, "JavaScript"), [
668
+ { module: "Command", cmd: "git", params: ["--version"] },
669
+ { module: "Command", cmd: "git", params: ["--help"] },
670
+ ]);
671
+ },
672
+ },
673
+ {
674
+ id: "direct::git-check-matrix",
675
+ run(t) {
676
+ const { projectBase } = createFixtureRepo(t, { fixtures: [] });
677
+ fs.rmSync(path.join(projectBase, ".venv"), { recursive: true, force: true });
678
+ const gitEnv = {
679
+ ...process.env,
680
+ GIT_AUTHOR_NAME: "pi-usereq",
681
+ GIT_AUTHOR_EMAIL: "pi-usereq@example.com",
682
+ GIT_COMMITTER_NAME: "pi-usereq",
683
+ GIT_COMMITTER_EMAIL: "pi-usereq@example.com",
684
+ };
685
+ const stage = spawnSync("git", ["add", "."], { cwd: projectBase, encoding: "utf8" });
686
+ assert.equal(stage.status, 0, stage.stderr);
687
+ const commit = spawnSync("git", ["commit", "-m", "normalize"], { cwd: projectBase, encoding: "utf8", env: gitEnv });
688
+ assert.equal(commit.status, 0, commit.stderr);
689
+ const clean = runNodeCli(["--git-check"], projectBase);
690
+ assert.equal(clean.status, 0, clean.stderr);
691
+ assert.equal(clean.stdout, "");
692
+ assert.equal(clean.stderr, "");
693
+ fs.writeFileSync(path.join(projectBase, "dirty.txt"), "dirty\n", "utf8");
694
+ const dirty = runNodeCli(["--git-check"], projectBase);
695
+ assert.equal(dirty.status, 1);
696
+ assert.equal(dirty.stdout, "");
697
+ assert.equal(dirty.stderr.trim(), "ERROR: Git status unclear!");
698
+ const rejected = runNodeCli(["--base", projectBase, "--git-check"], projectBase);
699
+ assert.notEqual(rejected.status, 0);
700
+ },
701
+ },
702
+ {
703
+ id: "direct::docs-check-matrix",
704
+ run(t) {
705
+ const { projectBase } = createFixtureRepo(t, { fixtures: [] });
706
+ const success = runNodeCli(["--docs-check"], projectBase);
707
+ assert.equal(success.status, 0, success.stderr);
708
+
709
+ fs.rmSync(path.join(projectBase, ...DEFAULT_DOCS_DIR.split("/"), "REQUIREMENTS.md"));
710
+ const missingRequirements = runNodeCli(["--docs-check"], projectBase);
711
+ assert.equal(missingRequirements.status, 1);
712
+
713
+ fs.writeFileSync(path.join(projectBase, ...DEFAULT_DOCS_DIR.split("/"), "REQUIREMENTS.md"), "# Requirements\n", "utf8");
714
+ fs.rmSync(path.join(projectBase, ...DEFAULT_DOCS_DIR.split("/"), "WORKFLOW.md"));
715
+ const missingWorkflow = runNodeCli(["--docs-check"], projectBase);
716
+ assert.equal(missingWorkflow.status, 1);
717
+ },
718
+ },
719
+ {
720
+ id: "direct::git-worktree-matrix",
721
+ run(t) {
722
+ const { projectBase } = createFixtureRepo(t, { fixtures: [] });
723
+ const named = runNodeCli(["--git-wt-name"], projectBase);
724
+ assert.equal(named.status, 0, named.stderr);
725
+ const nameParts = named.stdout.trim().split("-");
726
+ assert.ok(named.stdout.trim().startsWith("useReq-"));
727
+ assert.ok(nameParts.length >= 4);
728
+ assert.match(nameParts.at(-1) ?? "", /^\d{14}$/);
729
+
730
+ const invalid = runNodeCli(["--git-wt-create", "bad name with spaces"], projectBase);
731
+ assert.notEqual(invalid.status, 0);
732
+
733
+ const wtName = "pi-usereq-option-parity-wt";
734
+ const created = runNodeCli(["--git-wt-create", wtName], projectBase);
735
+ assert.equal(created.status, 0, created.stderr);
736
+ const worktreePath = path.join(path.dirname(projectBase), wtName);
737
+ assert.ok(fs.existsSync(worktreePath), "worktree path was not created");
738
+ assert.ok(fs.existsSync(getProjectConfigPath(worktreePath)), "config was not copied");
739
+
740
+ const partial = runNodeCli(["--git-wt-delete", "pi-usereq-option-parity"], projectBase);
741
+ assert.notEqual(partial.status, 0);
742
+ assert.ok(fs.existsSync(worktreePath), "partial delete removed the target worktree");
743
+
744
+ const deleted = runNodeCli(["--git-wt-delete", wtName], projectBase);
745
+ assert.equal(deleted.status, 0, deleted.stderr);
746
+ assert.ok(!fs.existsSync(worktreePath), "worktree path was not removed");
747
+
748
+ const missing = runNodeCli(["--git-wt-delete", "nonexistent-wt-branch-xyz"], projectBase);
749
+ assert.notEqual(missing.status, 0);
750
+ },
751
+ },
752
+ {
753
+ id: "direct::git-path-and-base-path-matrix",
754
+ run(t) {
755
+ const { projectBase } = createFixtureRepo(t, { fixtures: [] });
756
+ const resolvedGitPath = spawnSync("git", ["rev-parse", "--show-toplevel"], { cwd: projectBase, encoding: "utf8" }).stdout.trim();
757
+ const gitPath = runNodeCli(["--git-path"], projectBase);
758
+ assert.equal(gitPath.status, 0, gitPath.stderr);
759
+ assert.equal(gitPath.stdout.trim(), formatRuntimePathForDisplay(resolvedGitPath));
760
+
761
+ const basePath = runNodeCli(["--get-base-path"], projectBase);
762
+ assert.equal(basePath.status, 0, basePath.stderr);
763
+ assert.equal(basePath.stdout.trim(), formatRuntimePathForDisplay(projectBase));
764
+
765
+ const gitRejected = runNodeCli(["--base", projectBase, "--git-path"], projectBase);
766
+ assert.notEqual(gitRejected.status, 0);
767
+ const baseRejected = runNodeCli(["--base", projectBase, "--get-base-path"], projectBase);
768
+ assert.notEqual(baseRejected.status, 0);
769
+ },
770
+ },
771
+ {
772
+ id: "direct::fixtures::files-references-fixture-shape",
773
+ async run(t) {
774
+ for (const fixture of getFixtureFiles()) {
775
+ await t.test(path.basename(fixture), () => {
776
+ const result = runNodeCli(["--files-references", fixture], path.dirname(fixture));
777
+ assert.equal(result.status, 0, result.stderr);
778
+ const payload = JSON.parse(result.stdout) as {
779
+ summary: { analyzed_file_count: number };
780
+ files: Array<{ canonical_path: string; status: string }>;
781
+ };
782
+ assert.equal(payload.summary.analyzed_file_count, 1);
783
+ assert.equal(payload.files[0]?.status, "analyzed");
784
+ assert.equal(payload.files[0]?.canonical_path, path.basename(fixture));
785
+ });
786
+ }
787
+ },
788
+ },
789
+ {
790
+ id: "oracle::fixtures::files-find-fixture-parity",
791
+ async run(t) {
792
+ for (const fixture of getFixtureFiles()) {
793
+ await t.test(path.basename(fixture), () => {
794
+ assertCliParity(["--files-find", getFixtureTagFilter(fixture), ".*", fixture], path.dirname(fixture));
795
+ });
796
+ }
797
+ },
798
+ },
799
+ ];
800
+
801
+ test("imported CLI regression mappings resolve to executable TypeScript targets", () => {
802
+ const implementedTargetIds = new Set(TARGET_CASES.map((target) => target.id));
803
+ assert.ok(IMPORTED_CASE_MAPPINGS.length > 0, "expected imported CLI regression mappings");
804
+ for (const mapping of IMPORTED_CASE_MAPPINGS) {
805
+ assert.ok(implementedTargetIds.has(mapping.targetId), `missing target ${mapping.targetId} for ${mapping.sourceId}`);
806
+ }
807
+ });
808
+
809
+ test("imported CLI option parity targets execute successfully", async (t) => {
810
+ for (const target of TARGET_CASES) {
811
+ await t.test(target.id, async (subtest) => {
812
+ await target.run(subtest);
813
+ });
814
+ }
815
+ });