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.
- package/.g.conf +20 -0
- package/.github/workflows/release-npm.yml +131 -0
- package/.gitignore +270 -0
- package/CHANGELOG.md +221 -0
- package/LICENSE +674 -0
- package/README.md +260 -0
- package/TODO.md +20 -0
- package/docs/pi.dev/agent-document-manifest.json +6399 -0
- package/docs/pi.dev/coding-agent-docs/compaction.md +394 -0
- package/docs/pi.dev/coding-agent-docs/custom-provider.md +596 -0
- package/docs/pi.dev/coding-agent-docs/development.md +71 -0
- package/docs/pi.dev/coding-agent-docs/extensions.md +2262 -0
- package/docs/pi.dev/coding-agent-docs/images/doom-extension.png +0 -0
- package/docs/pi.dev/coding-agent-docs/images/exy.png +0 -0
- package/docs/pi.dev/coding-agent-docs/images/interactive-mode.png +0 -0
- package/docs/pi.dev/coding-agent-docs/images/tree-view.png +0 -0
- package/docs/pi.dev/coding-agent-docs/json.md +82 -0
- package/docs/pi.dev/coding-agent-docs/keybindings.md +175 -0
- package/docs/pi.dev/coding-agent-docs/models.md +392 -0
- package/docs/pi.dev/coding-agent-docs/packages.md +218 -0
- package/docs/pi.dev/coding-agent-docs/prompt-templates.md +67 -0
- package/docs/pi.dev/coding-agent-docs/providers.md +195 -0
- package/docs/pi.dev/coding-agent-docs/rpc.md +1377 -0
- package/docs/pi.dev/coding-agent-docs/sdk.md +1124 -0
- package/docs/pi.dev/coding-agent-docs/session.md +412 -0
- package/docs/pi.dev/coding-agent-docs/settings.md +247 -0
- package/docs/pi.dev/coding-agent-docs/shell-aliases.md +13 -0
- package/docs/pi.dev/coding-agent-docs/skills.md +232 -0
- package/docs/pi.dev/coding-agent-docs/terminal-setup.md +106 -0
- package/docs/pi.dev/coding-agent-docs/termux.md +127 -0
- package/docs/pi.dev/coding-agent-docs/themes.md +295 -0
- package/docs/pi.dev/coding-agent-docs/tmux.md +61 -0
- package/docs/pi.dev/coding-agent-docs/tree.md +231 -0
- package/docs/pi.dev/coding-agent-docs/tui.md +887 -0
- package/docs/pi.dev/coding-agent-docs/windows.md +17 -0
- package/docs/pi.dev/mom-docs/artifacts-server.md +475 -0
- package/docs/pi.dev/mom-docs/events.md +307 -0
- package/docs/pi.dev/mom-docs/new.md +970 -0
- package/docs/pi.dev/mom-docs/sandbox.md +153 -0
- package/docs/pi.dev/mom-docs/slack-bot-minimal-guide.md +399 -0
- package/docs/pi.dev/mom-docs/v86.md +319 -0
- package/docs/pi.dev/pods-docs/gml-4.5.md +189 -0
- package/docs/pi.dev/pods-docs/gpt-oss.md +233 -0
- package/docs/pi.dev/pods-docs/implementation-plan.md +183 -0
- package/docs/pi.dev/pods-docs/kimi-k2.md +197 -0
- package/docs/pi.dev/pods-docs/models.md +116 -0
- package/docs/pi.dev/pods-docs/plan.md +166 -0
- package/docs/pi.dev/pods-docs/qwen3-coder.md +132 -0
- package/images/flowchart-bw.png +0 -0
- package/images/flowchart-bw.svg +102 -0
- package/images/flowchart.md +100 -0
- package/images/flowchart.png +0 -0
- package/images/flowchart.svg +3 -0
- package/package.json +46 -0
- package/req/docs/REFERENCES.md +4554 -0
- package/req/docs/REQUIREMENTS.md +475 -0
- package/req/docs/WORKFLOW.md +1059 -0
- package/scripts/debug-extension.ts +497 -0
- package/scripts/lib/extension-debug-harness.ts +450 -0
- package/scripts/lib/recording-extension-api.ts +786 -0
- package/scripts/lib/sdk-smoke.ts +503 -0
- package/scripts/pi-usereq-debug.sh +330 -0
- package/scripts/tool-args-to-params.ts +208 -0
- package/src/cli.ts +349 -0
- package/src/core/agent-tool-json.ts +621 -0
- package/src/core/compress-files.ts +72 -0
- package/src/core/compress-payload.ts +648 -0
- package/src/core/compress.ts +464 -0
- package/src/core/config.ts +272 -0
- package/src/core/doxygen-parser.ts +318 -0
- package/src/core/errors.ts +31 -0
- package/src/core/extension-status.ts +660 -0
- package/src/core/find-constructs.ts +319 -0
- package/src/core/find-payload.ts +915 -0
- package/src/core/generate-markdown.ts +120 -0
- package/src/core/path-context.ts +196 -0
- package/src/core/pi-notify.ts +430 -0
- package/src/core/pi-usereq-tools.ts +140 -0
- package/src/core/prompts.ts +184 -0
- package/src/core/reference-payload.ts +818 -0
- package/src/core/resources.ts +63 -0
- package/src/core/runtime-project-paths.ts +99 -0
- package/src/core/settings-menu.ts +233 -0
- package/src/core/source-analyzer.ts +1721 -0
- package/src/core/static-check.ts +674 -0
- package/src/core/token-counter.ts +729 -0
- package/src/core/tool-runner.ts +717 -0
- package/src/core/utils.ts +185 -0
- package/src/index.ts +2209 -0
- package/src/resources/guidelines/Google_C++_Style_Guide.md +3711 -0
- package/src/resources/guidelines/Google_Python_Style_Guide.md +3709 -0
- package/src/resources/prompts/analyze.md +130 -0
- package/src/resources/prompts/change.md +227 -0
- package/src/resources/prompts/check.md +139 -0
- package/src/resources/prompts/cover.md +219 -0
- package/src/resources/prompts/create.md +104 -0
- package/src/resources/prompts/fix.md +221 -0
- package/src/resources/prompts/flowchart.md +220 -0
- package/src/resources/prompts/implement.md +163 -0
- package/src/resources/prompts/new.md +226 -0
- package/src/resources/prompts/readme.md +182 -0
- package/src/resources/prompts/recreate.md +213 -0
- package/src/resources/prompts/refactor.md +213 -0
- package/src/resources/prompts/references.md +100 -0
- package/src/resources/prompts/renumber.md +119 -0
- package/src/resources/prompts/workflow.md +202 -0
- package/src/resources/prompts/write.md +99 -0
- package/src/resources/sounds/Machine-alert-beep-sound-effect.mp3 +0 -0
- package/src/resources/sounds/Soft-high-tech-notification-sound-effect.mp3 +0 -0
- package/src/resources/templates/Document_Source_Code_in_Doxygen_Style.md +130 -0
- package/src/resources/templates/HDT_Test_Authoring_Guide.md +318 -0
- package/src/resources/templates/Requirements_Template.md +78 -0
- package/tests/attended-results-scenarios.ts +758 -0
- package/tests/attended-results.test.ts +39 -0
- package/tests/cli-command-option-parity.test.ts +815 -0
- package/tests/debug-extension-harness.test.ts +463 -0
- package/tests/extension-registration.test.ts +2006 -0
- package/tests/fixtures/fixture_c.c +361 -0
- package/tests/fixtures/fixture_cpp.cpp +407 -0
- package/tests/fixtures/fixture_csharp.cs +411 -0
- package/tests/fixtures/fixture_elixir.ex +409 -0
- package/tests/fixtures/fixture_go.go +341 -0
- package/tests/fixtures/fixture_haskell.hs +250 -0
- package/tests/fixtures/fixture_java.java +430 -0
- package/tests/fixtures/fixture_javascript.js +383 -0
- package/tests/fixtures/fixture_kotlin.kt +451 -0
- package/tests/fixtures/fixture_lua.lua +276 -0
- package/tests/fixtures/fixture_perl.pl +310 -0
- package/tests/fixtures/fixture_php.php +433 -0
- package/tests/fixtures/fixture_python.py +502 -0
- package/tests/fixtures/fixture_ruby.rb +345 -0
- package/tests/fixtures/fixture_rust.rs +380 -0
- package/tests/fixtures/fixture_scala.scala +398 -0
- package/tests/fixtures/fixture_shell.sh +276 -0
- package/tests/fixtures/fixture_swift.swift +397 -0
- package/tests/fixtures/fixture_typescript.ts +434 -0
- package/tests/fixtures/fixture_zig.zig +295 -0
- package/tests/fixtures_attended_results/project/compress-line-numbers.json +5 -0
- package/tests/fixtures_attended_results/project/compress.json +5 -0
- package/tests/fixtures_attended_results/project/enable-static-check-invalid-command.json +5 -0
- package/tests/fixtures_attended_results/project/enable-static-check-valid.json +5 -0
- package/tests/fixtures_attended_results/project/files-static-check.json +5 -0
- package/tests/fixtures_attended_results/project/find-line-numbers.json +5 -0
- package/tests/fixtures_attended_results/project/find.json +5 -0
- package/tests/fixtures_attended_results/project/get-base-path.json +5 -0
- package/tests/fixtures_attended_results/project/git-check-clean.json +5 -0
- package/tests/fixtures_attended_results/project/git-check-dirty.json +5 -0
- package/tests/fixtures_attended_results/project/git-path.json +5 -0
- package/tests/fixtures_attended_results/project/git-wt-create-invalid.json +5 -0
- package/tests/fixtures_attended_results/project/git-wt-create-valid.json +5 -0
- package/tests/fixtures_attended_results/project/git-wt-delete-nonexistent.json +5 -0
- package/tests/fixtures_attended_results/project/git-wt-delete-valid.json +5 -0
- package/tests/fixtures_attended_results/project/git-wt-name.json +5 -0
- package/tests/fixtures_attended_results/project/references.json +5 -0
- package/tests/fixtures_attended_results/project/static-check.json +5 -0
- package/tests/fixtures_attended_results/project/tokens.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_c.c.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_cpp.cpp.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_csharp.cs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_elixir.ex.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_go.go.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_haskell.hs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_java.java.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_javascript.js.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_kotlin.kt.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_lua.lua.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_perl.pl.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_php.php.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_python.py.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_ruby.rb.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_rust.rs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_scala.scala.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_shell.sh.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_swift.swift.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_typescript.ts.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress/fixture_zig.zig.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_c.c.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_cpp.cpp.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_csharp.cs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_elixir.ex.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_go.go.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_haskell.hs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_java.java.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_javascript.js.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_kotlin.kt.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_lua.lua.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_perl.pl.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_php.php.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_python.py.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_ruby.rb.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_rust.rs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_scala.scala.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_shell.sh.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_swift.swift.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_typescript.ts.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-compress-line-numbers/fixture_zig.zig.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_c.c.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_cpp.cpp.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_csharp.cs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_elixir.ex.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_go.go.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_haskell.hs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_java.java.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_javascript.js.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_kotlin.kt.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_lua.lua.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_perl.pl.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_php.php.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_python.py.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_ruby.rb.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_rust.rs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_scala.scala.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_shell.sh.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_swift.swift.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_typescript.ts.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find/fixture_zig.zig.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_c.c.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_cpp.cpp.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_csharp.cs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_elixir.ex.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_go.go.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_haskell.hs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_java.java.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_javascript.js.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_kotlin.kt.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_lua.lua.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_perl.pl.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_php.php.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_python.py.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_ruby.rb.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_rust.rs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_scala.scala.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_shell.sh.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_swift.swift.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_typescript.ts.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-find-line-numbers/fixture_zig.zig.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_c.c.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_cpp.cpp.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_csharp.cs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_elixir.ex.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_go.go.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_haskell.hs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_java.java.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_javascript.js.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_kotlin.kt.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_lua.lua.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_perl.pl.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_php.php.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_python.py.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_ruby.rb.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_rust.rs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_scala.scala.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_shell.sh.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_swift.swift.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_typescript.ts.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-references/fixture_zig.zig.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_c.c.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_cpp.cpp.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_csharp.cs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_elixir.ex.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_go.go.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_haskell.hs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_java.java.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_javascript.js.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_kotlin.kt.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_lua.lua.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_perl.pl.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_php.php.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_python.py.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_ruby.rb.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_rust.rs.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_scala.scala.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_shell.sh.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_swift.swift.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_typescript.ts.json +5 -0
- package/tests/fixtures_attended_results/standalone/files-tokens/fixture_zig.zig.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_c.c.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_cpp.cpp.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_csharp.cs.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_elixir.ex.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_go.go.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_haskell.hs.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_java.java.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_javascript.js.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_kotlin.kt.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_lua.lua.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_perl.pl.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_php.php.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_python.py.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_ruby.rb.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_rust.rs.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_scala.scala.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_shell.sh.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_swift.swift.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_typescript.ts.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-command/fixture_zig.zig.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_c.c.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_cpp.cpp.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_csharp.cs.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_elixir.ex.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_go.go.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_haskell.hs.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_java.java.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_javascript.js.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_kotlin.kt.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_lua.lua.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_perl.pl.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_php.php.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_python.py.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_ruby.rb.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_rust.rs.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_scala.scala.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_shell.sh.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_swift.swift.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_typescript.ts.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-dummy/fixture_zig.zig.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_c.c.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_cpp.cpp.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_csharp.cs.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_elixir.ex.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_go.go.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_haskell.hs.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_java.java.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_javascript.js.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_kotlin.kt.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_lua.lua.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_perl.pl.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_php.php.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_python.py.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_ruby.rb.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_rust.rs.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_scala.scala.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_shell.sh.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_swift.swift.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_typescript.ts.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_zig.zig.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_c.c.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_cpp.cpp.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_csharp.cs.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_elixir.ex.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_go.go.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_haskell.hs.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_java.java.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_javascript.js.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_kotlin.kt.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_lua.lua.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_perl.pl.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_php.php.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_python.py.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_ruby.rb.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_rust.rs.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_scala.scala.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_shell.sh.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_swift.swift.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_typescript.ts.json +5 -0
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_zig.zig.json +5 -0
- package/tests/helpers.ts +204 -0
- package/tests/oracle-project.test.ts +63 -0
- package/tests/oracle-standalone.test.ts +48 -0
- package/tests/prompt-rendering.test.ts +66 -0
- package/tests/release-workflow.test.ts +133 -0
|
@@ -0,0 +1,915 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file
|
|
3
|
+
* @brief Builds agent-oriented JSON payloads for `files-find` and `find`.
|
|
4
|
+
* @details Converts construct-search results into deterministic JSON sections ordered for LLM traversal, including request metadata, repository scope, file statuses, structured matches, structured Doxygen fields, typed line ranges, and normalized stripped code lines. Runtime is O(F log F + S + M) where F is file count, S is analyzed source size, and M is matched construct count. Side effects are limited to filesystem reads and optional stderr logging.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import {
|
|
10
|
+
LANGUAGE_TAGS,
|
|
11
|
+
parseTagFilter,
|
|
12
|
+
buildStrippedConstructLineEntries,
|
|
13
|
+
type StrippedConstructLineEntry,
|
|
14
|
+
} from "./find-constructs.js";
|
|
15
|
+
import { detectLanguage } from "./compress.js";
|
|
16
|
+
import {
|
|
17
|
+
SourceAnalyzer,
|
|
18
|
+
ElementType,
|
|
19
|
+
SourceElement,
|
|
20
|
+
collectElementDoxygenFields,
|
|
21
|
+
collectFileLevelDoxygenFields,
|
|
22
|
+
} from "./source-analyzer.js";
|
|
23
|
+
import {
|
|
24
|
+
countDoxygenFieldValues,
|
|
25
|
+
structureDoxygenFields,
|
|
26
|
+
type StructuredDoxygenFields,
|
|
27
|
+
} from "./doxygen-parser.js";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @brief Enumerates supported find-payload scopes.
|
|
31
|
+
* @details Distinguishes explicit-file requests from configured project scans while preserving one stable JSON contract. The alias is compile-time only and introduces no runtime cost.
|
|
32
|
+
*/
|
|
33
|
+
export type FindToolScope = "explicit-files" | "configured-source-directories";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @brief Enumerates supported per-file find-entry statuses.
|
|
37
|
+
* @details Separates matched files, analyzed no-match files, analysis failures, and skipped inputs so downstream agents can branch without reparsing stderr text. The alias is compile-time only and introduces no runtime cost.
|
|
38
|
+
*/
|
|
39
|
+
export type FindFileStatus = "matched" | "no_match" | "error" | "skipped";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @brief Enumerates request-validation statuses for tag-filter and regex fields.
|
|
43
|
+
* @details Distinguishes validated search inputs from invalid request parameters without requiring stderr parsing. The alias is compile-time only and introduces no runtime cost.
|
|
44
|
+
*/
|
|
45
|
+
export type FindRequestStatus = "valid" | "invalid";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @brief Enumerates rendered line-number modes for find output.
|
|
49
|
+
* @details Distinguishes payloads whose display strings include original source line prefixes from payloads whose display strings contain plain stripped code only. The alias is compile-time only and introduces no runtime cost.
|
|
50
|
+
*/
|
|
51
|
+
export type FindLineNumberMode = "enabled" | "disabled";
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @brief Enumerates top-level find execution outcomes.
|
|
55
|
+
* @details Separates successful match delivery from invalid request states and valid no-match searches so agents can branch on one canonical field. The alias is compile-time only and introduces no runtime cost.
|
|
56
|
+
*/
|
|
57
|
+
export type FindSearchStatus = "matched" | "no_matches" | "invalid_tag_filter" | "invalid_regex";
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @brief Describes one numeric source line range.
|
|
61
|
+
* @details Exposes start and end line numbers plus the same inclusive range as a numeric tuple for direct agent access. The interface is compile-time only and introduces no runtime cost.
|
|
62
|
+
*/
|
|
63
|
+
export interface FindLineRange {
|
|
64
|
+
start_line_number: number;
|
|
65
|
+
end_line_number: number;
|
|
66
|
+
line_range: [number, number];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @brief Describes one structured stripped-code line.
|
|
71
|
+
* @details Preserves output order, original source coordinates, normalized stripped text, and rendered display text so agents can choose between direct-access facts and human-visible rendering without reparsing strings. The interface is compile-time only and introduces no runtime cost.
|
|
72
|
+
*/
|
|
73
|
+
export interface FindToolCodeLineEntry {
|
|
74
|
+
output_line_number: number;
|
|
75
|
+
source_line_number: number;
|
|
76
|
+
text: string;
|
|
77
|
+
display_text: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @brief Describes one structured matched construct.
|
|
82
|
+
* @details Orders direct-access identity fields before hierarchy, locations, Doxygen metadata, and stripped code so agents can branch without reparsing monolithic markdown. The interface is compile-time only and introduces no runtime cost.
|
|
83
|
+
*/
|
|
84
|
+
export interface FindToolMatchEntry extends FindLineRange {
|
|
85
|
+
match_index: number;
|
|
86
|
+
declaration_order_index: number;
|
|
87
|
+
symbol_name: string;
|
|
88
|
+
qualified_name: string;
|
|
89
|
+
symbol_kind: string;
|
|
90
|
+
type_label: string;
|
|
91
|
+
signature_text?: string;
|
|
92
|
+
visibility?: string;
|
|
93
|
+
parent_symbol_name?: string;
|
|
94
|
+
parent_qualified_name?: string;
|
|
95
|
+
depth: number;
|
|
96
|
+
inherits_text?: string;
|
|
97
|
+
brief_text?: string;
|
|
98
|
+
doxygen_field_count: number;
|
|
99
|
+
doxygen?: StructuredDoxygenFields;
|
|
100
|
+
code_line_count: number;
|
|
101
|
+
code_lines: FindToolCodeLineEntry[];
|
|
102
|
+
stripped_source_text?: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @brief Describes one per-file find payload entry.
|
|
107
|
+
* @details Stores path identity, file status, supported-tag metadata, line metrics, file-level Doxygen metadata, and matched-construct records while keeping failure facts structured. The interface is compile-time only and introduces no runtime cost.
|
|
108
|
+
*/
|
|
109
|
+
export interface FindToolFileEntry extends FindLineRange {
|
|
110
|
+
request_index: number;
|
|
111
|
+
input_path: string;
|
|
112
|
+
canonical_path: string;
|
|
113
|
+
absolute_path: string;
|
|
114
|
+
file_name: string;
|
|
115
|
+
file_extension: string;
|
|
116
|
+
language_id?: string;
|
|
117
|
+
language_name?: string;
|
|
118
|
+
status: FindFileStatus;
|
|
119
|
+
exists: boolean;
|
|
120
|
+
is_file: boolean;
|
|
121
|
+
supported_tag_count: number;
|
|
122
|
+
supported_tags: string[];
|
|
123
|
+
line_count: number;
|
|
124
|
+
match_count: number;
|
|
125
|
+
doxygen_field_count: number;
|
|
126
|
+
file_description_text?: string;
|
|
127
|
+
file_doxygen?: StructuredDoxygenFields;
|
|
128
|
+
matches: FindToolMatchEntry[];
|
|
129
|
+
error_reason?: string;
|
|
130
|
+
error_message?: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @brief Describes the request section of the find payload.
|
|
135
|
+
* @details Captures tool identity, scope, base directory, line-number mode, tag filter, regex, validation statuses, and requested path inventory so agents can reason about how the search was executed. The interface is compile-time only and introduces no runtime cost.
|
|
136
|
+
*/
|
|
137
|
+
export interface FindToolRequestSection {
|
|
138
|
+
tool_name: string;
|
|
139
|
+
scope: FindToolScope;
|
|
140
|
+
base_dir_path: string;
|
|
141
|
+
line_number_mode: FindLineNumberMode;
|
|
142
|
+
tag_filter_text: string;
|
|
143
|
+
tag_filter_values: string[];
|
|
144
|
+
tag_filter_status: FindRequestStatus;
|
|
145
|
+
tag_filter_error_message?: string;
|
|
146
|
+
name_regex_text: string;
|
|
147
|
+
regex_engine: string;
|
|
148
|
+
regex_status: FindRequestStatus;
|
|
149
|
+
regex_error_message?: string;
|
|
150
|
+
source_directory_count: number;
|
|
151
|
+
source_directory_paths: string[];
|
|
152
|
+
requested_file_count: number;
|
|
153
|
+
requested_input_paths: string[];
|
|
154
|
+
requested_canonical_paths: string[];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @brief Describes the summary section of the find payload.
|
|
159
|
+
* @details Exposes aggregate file, match, line, and Doxygen counts as numeric fields plus one stable search-status discriminator. The interface is compile-time only and introduces no runtime cost.
|
|
160
|
+
*/
|
|
161
|
+
export interface FindToolSummarySection {
|
|
162
|
+
search_status: FindSearchStatus;
|
|
163
|
+
processable_file_count: number;
|
|
164
|
+
matched_file_count: number;
|
|
165
|
+
no_match_file_count: number;
|
|
166
|
+
error_file_count: number;
|
|
167
|
+
skipped_file_count: number;
|
|
168
|
+
total_match_count: number;
|
|
169
|
+
total_code_line_count: number;
|
|
170
|
+
total_doxygen_field_count: number;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @brief Describes the repository section of the find payload.
|
|
175
|
+
* @details Stores the base path, configured source-directory scope, canonical file list, and supported-tag matrix needed to specialize later searches without rereading tool descriptions. The interface is compile-time only and introduces no runtime cost.
|
|
176
|
+
*/
|
|
177
|
+
export interface FindToolRepositorySection {
|
|
178
|
+
root_directory_path: string;
|
|
179
|
+
source_directory_paths: string[];
|
|
180
|
+
file_count: number;
|
|
181
|
+
file_canonical_paths: string[];
|
|
182
|
+
supported_tags_by_language: Record<string, string[]>;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* @brief Describes the full agent-oriented find payload.
|
|
187
|
+
* @details Orders the top-level sections as request, summary, repository, and files so execution metadata can be appended deterministically by the tool wrapper. The interface is compile-time only and introduces no runtime cost.
|
|
188
|
+
*/
|
|
189
|
+
export interface FindToolPayload {
|
|
190
|
+
request: FindToolRequestSection;
|
|
191
|
+
summary: FindToolSummarySection;
|
|
192
|
+
repository: FindToolRepositorySection;
|
|
193
|
+
files: FindToolFileEntry[];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @brief Describes the options required to build one find payload.
|
|
198
|
+
* @details Supplies tool identity, scope, base directory, tag filter, regex, requested paths, line-number mode, and optional configured source directories while keeping payload construction deterministic. The interface is compile-time only and introduces no runtime cost.
|
|
199
|
+
*/
|
|
200
|
+
export interface BuildFindToolPayloadOptions {
|
|
201
|
+
toolName: string;
|
|
202
|
+
scope: FindToolScope;
|
|
203
|
+
baseDir: string;
|
|
204
|
+
tagFilter: string;
|
|
205
|
+
pattern: string;
|
|
206
|
+
requestedPaths: string[];
|
|
207
|
+
includeLineNumbers: boolean;
|
|
208
|
+
sourceDirectoryPaths?: string[];
|
|
209
|
+
verbose?: boolean;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @brief Describes the result of validating one regex pattern.
|
|
214
|
+
* @details Separates valid compiled regex instances from invalid user input while preserving a stable machine-readable status and error message. The interface is compile-time only and introduces no runtime cost.
|
|
215
|
+
*/
|
|
216
|
+
interface ValidatedRegex {
|
|
217
|
+
status: FindRequestStatus;
|
|
218
|
+
regex?: RegExp;
|
|
219
|
+
errorMessage?: string;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* @brief Describes the result of validating one tag filter.
|
|
224
|
+
* @details Separates normalized tag values from invalid or empty filters while preserving a stable status and error message. The interface is compile-time only and introduces no runtime cost.
|
|
225
|
+
*/
|
|
226
|
+
interface ValidatedTagFilter {
|
|
227
|
+
status: FindRequestStatus;
|
|
228
|
+
tagValues: string[];
|
|
229
|
+
tagSet: Set<string>;
|
|
230
|
+
errorMessage?: string;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* @brief Canonicalizes one filesystem path relative to the payload base directory.
|
|
235
|
+
* @details Emits a slash-normalized relative path when the target is under the base directory; otherwise emits the normalized absolute path. Runtime is O(p) in path length. No side effects occur.
|
|
236
|
+
* @param[in] targetPath {string} Absolute or relative filesystem path.
|
|
237
|
+
* @param[in] baseDir {string} Base directory used for relative canonicalization.
|
|
238
|
+
* @return {string} Canonicalized path string.
|
|
239
|
+
*/
|
|
240
|
+
function canonicalizeFindPath(targetPath: string, baseDir: string): string {
|
|
241
|
+
const absolutePath = path.resolve(targetPath);
|
|
242
|
+
const absoluteBaseDir = path.resolve(baseDir);
|
|
243
|
+
const relativePath = path.relative(absoluteBaseDir, absolutePath).split(path.sep).join("/");
|
|
244
|
+
if (relativePath !== "" && relativePath !== "." && !relativePath.startsWith("../") && relativePath !== "..") {
|
|
245
|
+
return relativePath;
|
|
246
|
+
}
|
|
247
|
+
return absolutePath.split(path.sep).join("/");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* @brief Builds one structured line-range record.
|
|
252
|
+
* @details Duplicates the inclusive range as start, end, and tuple fields so callers can address whichever shape is most convenient. Runtime is O(1). No side effects occur.
|
|
253
|
+
* @param[in] startLineNumber {number} Inclusive start line number.
|
|
254
|
+
* @param[in] endLineNumber {number} Inclusive end line number.
|
|
255
|
+
* @return {FindLineRange} Structured line-range record.
|
|
256
|
+
*/
|
|
257
|
+
function buildLineRange(startLineNumber: number, endLineNumber: number): FindLineRange {
|
|
258
|
+
return {
|
|
259
|
+
start_line_number: startLineNumber,
|
|
260
|
+
end_line_number: endLineNumber,
|
|
261
|
+
line_range: [startLineNumber, endLineNumber],
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* @brief Returns the supported-tag matrix ordered for deterministic JSON emission.
|
|
267
|
+
* @details Sorts languages alphabetically and tag arrays lexicographically so downstream agents can reuse the matrix without reparsing human prose. Runtime is O(l * t log t). No side effects occur.
|
|
268
|
+
* @return {Record<string, string[]>} Supported tags keyed by canonical language identifier.
|
|
269
|
+
*/
|
|
270
|
+
function buildSupportedTagsByLanguage(): Record<string, string[]> {
|
|
271
|
+
return Object.fromEntries(
|
|
272
|
+
Object.entries(LANGUAGE_TAGS)
|
|
273
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
274
|
+
.map(([language, tagSet]) => [language, [...tagSet].sort()]),
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* @brief Resolves one stable symbol name from an analyzed element.
|
|
280
|
+
* @details Prefers explicit analyzer name metadata, then falls back to the derived signature or the first source line so every matched construct retains a direct-access identifier. Runtime is O(1). No side effects occur.
|
|
281
|
+
* @param[in] element {SourceElement} Source element.
|
|
282
|
+
* @return {string} Stable symbol name.
|
|
283
|
+
*/
|
|
284
|
+
function resolveSymbolName(element: SourceElement): string {
|
|
285
|
+
return element.name ?? element.signature ?? ((element.extract.split("\n")[0] ?? "").trim() || "?");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* @brief Resolves the direct parent definition for one source element.
|
|
290
|
+
* @details Matches by parent name plus inclusive line containment and chooses the deepest enclosing definition. Runtime is O(n) in definition count. No side effects occur.
|
|
291
|
+
* @param[in] definitions {SourceElement[]} Sorted definition elements.
|
|
292
|
+
* @param[in] element {SourceElement} Candidate child element.
|
|
293
|
+
* @return {SourceElement | undefined} Matched parent definition when available.
|
|
294
|
+
*/
|
|
295
|
+
function resolveParentElement(definitions: SourceElement[], element: SourceElement): SourceElement | undefined {
|
|
296
|
+
if (!element.parentName) {
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
return definitions
|
|
300
|
+
.filter(
|
|
301
|
+
(candidate) => candidate.name === element.parentName && candidate.lineStart <= element.lineStart && candidate.lineEnd >= element.lineEnd,
|
|
302
|
+
)
|
|
303
|
+
.sort((left, right) => right.depth - left.depth || right.lineStart - left.lineStart)[0];
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* @brief Converts stripped-code line entries into the payload line-entry contract.
|
|
308
|
+
* @details Performs a shallow field copy so the payload remains decoupled from the lower-level strip helper type. Runtime is O(n) in stripped line count. No side effects occur.
|
|
309
|
+
* @param[in] lineEntries {StrippedConstructLineEntry[]} Normalized stripped-code line entries.
|
|
310
|
+
* @return {FindToolCodeLineEntry[]} Payload line entries.
|
|
311
|
+
*/
|
|
312
|
+
function mapCodeLines(lineEntries: StrippedConstructLineEntry[]): FindToolCodeLineEntry[] {
|
|
313
|
+
return lineEntries.map((entry) => ({
|
|
314
|
+
output_line_number: entry.output_line_number,
|
|
315
|
+
source_line_number: entry.source_line_number,
|
|
316
|
+
text: entry.text,
|
|
317
|
+
display_text: entry.display_text,
|
|
318
|
+
} satisfies FindToolCodeLineEntry));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* @brief Joins stripped-code line entries into one optional monolithic text field.
|
|
323
|
+
* @details Preserves rendered display strings in line order so agents that need a contiguous excerpt can read one field without losing access to the structured line array. Runtime is O(n) in stripped line count. No side effects occur.
|
|
324
|
+
* @param[in] lineEntries {FindToolCodeLineEntry[]} Structured stripped-code line entries.
|
|
325
|
+
* @return {string | undefined} Joined stripped-source text, or `undefined` when no lines remain.
|
|
326
|
+
*/
|
|
327
|
+
function buildStrippedSourceText(lineEntries: FindToolCodeLineEntry[]): string | undefined {
|
|
328
|
+
if (lineEntries.length === 0) {
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
return lineEntries.map((entry) => entry.display_text).join("\n");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* @brief Validates and normalizes one tag filter.
|
|
336
|
+
* @details Parses the raw pipe-delimited filter, sorts the resulting unique tag values, and marks the filter invalid when no recognized tag remains after normalization. Runtime is O(n log n) in requested tag count. No side effects occur.
|
|
337
|
+
* @param[in] tagFilter {string} Raw pipe-delimited tag filter.
|
|
338
|
+
* @return {ValidatedTagFilter} Validation result containing the normalized tag set and status.
|
|
339
|
+
*/
|
|
340
|
+
function validateTagFilter(tagFilter: string): ValidatedTagFilter {
|
|
341
|
+
const tagSet = parseTagFilter(tagFilter);
|
|
342
|
+
const tagValues = [...tagSet].sort();
|
|
343
|
+
if (tagValues.length === 0) {
|
|
344
|
+
return {
|
|
345
|
+
status: "invalid",
|
|
346
|
+
tagValues,
|
|
347
|
+
tagSet,
|
|
348
|
+
errorMessage: "no valid tags specified",
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
status: "valid",
|
|
353
|
+
tagValues,
|
|
354
|
+
tagSet,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* @brief Validates and compiles one construct-name regex pattern.
|
|
360
|
+
* @details Uses the JavaScript `RegExp` engine with search-style `.test(...)` evaluation and records a stable error message when compilation fails. Runtime is O(n) in pattern length. No side effects occur.
|
|
361
|
+
* @param[in] pattern {string} Raw user pattern.
|
|
362
|
+
* @return {ValidatedRegex} Validation result containing the compiled regex when valid.
|
|
363
|
+
*/
|
|
364
|
+
function validateRegexPattern(pattern: string): ValidatedRegex {
|
|
365
|
+
try {
|
|
366
|
+
return {
|
|
367
|
+
status: "valid",
|
|
368
|
+
regex: new RegExp(pattern),
|
|
369
|
+
};
|
|
370
|
+
} catch (error) {
|
|
371
|
+
return {
|
|
372
|
+
status: "invalid",
|
|
373
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* @brief Tests whether one element matches a validated tag filter and regex.
|
|
380
|
+
* @details Rejects unnamed elements and elements outside the requested tag set before applying the precompiled regex to the construct name. Runtime is O(1) plus regex evaluation. No side effects occur.
|
|
381
|
+
* @param[in] element {SourceElement} Candidate source element.
|
|
382
|
+
* @param[in] tagSet {Set<string>} Normalized requested tag set.
|
|
383
|
+
* @param[in] regex {RegExp} Precompiled construct-name regex.
|
|
384
|
+
* @return {boolean} `true` when the element matches both filters.
|
|
385
|
+
*/
|
|
386
|
+
function elementMatches(element: SourceElement, tagSet: Set<string>, regex: RegExp): boolean {
|
|
387
|
+
if (!tagSet.has(element.typeLabel)) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
if (!element.name) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
return regex.test(element.name);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* @brief Counts logical source lines from one file content string.
|
|
398
|
+
* @details Preserves the repository's line-count convention that excludes the terminal empty split produced by trailing newlines. Runtime is O(n) in content length. No side effects occur.
|
|
399
|
+
* @param[in] fileContent {string} Raw file content.
|
|
400
|
+
* @return {number} Logical source-line count.
|
|
401
|
+
*/
|
|
402
|
+
function countLogicalLines(fileContent: string): number {
|
|
403
|
+
if (fileContent === "") {
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
return fileContent.split(/\r?\n/).length - (fileContent.endsWith("\n") ? 1 : 0);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* @brief Builds one skipped file entry for a request path.
|
|
411
|
+
* @details Preserves path identity, filesystem status, language metadata when detectable, supported tags for the language, and a stable skip reason without attempting search analysis. Runtime is O(t) in supported-tag count. No side effects occur.
|
|
412
|
+
* @param[in] inputPath {string} Caller-supplied path.
|
|
413
|
+
* @param[in] absolutePath {string} Absolute path resolved against the payload base directory.
|
|
414
|
+
* @param[in] requestIndex {number} Caller-order index.
|
|
415
|
+
* @param[in] baseDir {string} Base directory used for canonical path derivation.
|
|
416
|
+
* @param[in] errorReason {string} Stable skip reason identifier.
|
|
417
|
+
* @param[in] errorMessage {string} Human-readable skip reason.
|
|
418
|
+
* @param[in] exists {boolean} Filesystem existence flag.
|
|
419
|
+
* @param[in] isFile {boolean} Filesystem file-kind flag.
|
|
420
|
+
* @return {FindToolFileEntry} Structured skipped file entry.
|
|
421
|
+
*/
|
|
422
|
+
function buildSkippedFileEntry(
|
|
423
|
+
inputPath: string,
|
|
424
|
+
absolutePath: string,
|
|
425
|
+
requestIndex: number,
|
|
426
|
+
baseDir: string,
|
|
427
|
+
errorReason: string,
|
|
428
|
+
errorMessage: string,
|
|
429
|
+
exists: boolean,
|
|
430
|
+
isFile: boolean,
|
|
431
|
+
): FindToolFileEntry {
|
|
432
|
+
const canonicalPath = canonicalizeFindPath(absolutePath, baseDir);
|
|
433
|
+
const fileExtension = path.extname(absolutePath).toLowerCase();
|
|
434
|
+
const languageId = detectLanguage(absolutePath);
|
|
435
|
+
const supportedTags = languageId ? [...(LANGUAGE_TAGS[languageId] ?? [])].sort() : [];
|
|
436
|
+
return {
|
|
437
|
+
...buildLineRange(0, 0),
|
|
438
|
+
request_index: requestIndex,
|
|
439
|
+
input_path: inputPath,
|
|
440
|
+
canonical_path: canonicalPath,
|
|
441
|
+
absolute_path: absolutePath,
|
|
442
|
+
file_name: path.basename(absolutePath),
|
|
443
|
+
file_extension: fileExtension,
|
|
444
|
+
language_id: languageId,
|
|
445
|
+
status: "skipped",
|
|
446
|
+
exists,
|
|
447
|
+
is_file: isFile,
|
|
448
|
+
supported_tag_count: supportedTags.length,
|
|
449
|
+
supported_tags: supportedTags,
|
|
450
|
+
line_count: 0,
|
|
451
|
+
match_count: 0,
|
|
452
|
+
doxygen_field_count: 0,
|
|
453
|
+
matches: [],
|
|
454
|
+
error_reason: errorReason,
|
|
455
|
+
error_message: errorMessage,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* @brief Builds one matched construct entry from an analyzed element.
|
|
461
|
+
* @details Resolves symbol identity, hierarchy hints, structured Doxygen fields, numeric declaration lines, and stripped code excerpts while keeping monolithic source text optional. Runtime is O(n) in construct span length plus Doxygen size. No side effects occur.
|
|
462
|
+
* @param[in] element {SourceElement} Matched element.
|
|
463
|
+
* @param[in] matchIndex {number} Match index within the file.
|
|
464
|
+
* @param[in] definitions {SourceElement[]} Sorted definition elements used for parent resolution.
|
|
465
|
+
* @param[in] qualifiedNameByLineStart {Map<number, string>} Precomputed qualified-name map for definitions.
|
|
466
|
+
* @param[in] sourceLines {string[]} Full file content split into line-preserving entries.
|
|
467
|
+
* @param[in] languageId {string} Canonical language identifier.
|
|
468
|
+
* @param[in] includeLineNumbers {boolean} When `true`, rendered display strings include absolute source line prefixes.
|
|
469
|
+
* @return {FindToolMatchEntry} Structured match entry.
|
|
470
|
+
*/
|
|
471
|
+
function buildMatchEntry(
|
|
472
|
+
element: SourceElement,
|
|
473
|
+
matchIndex: number,
|
|
474
|
+
definitions: SourceElement[],
|
|
475
|
+
qualifiedNameByLineStart: Map<number, string>,
|
|
476
|
+
sourceLines: string[],
|
|
477
|
+
languageId: string,
|
|
478
|
+
includeLineNumbers: boolean,
|
|
479
|
+
): FindToolMatchEntry {
|
|
480
|
+
const symbolName = resolveSymbolName(element);
|
|
481
|
+
const parentElement = resolveParentElement(definitions, element);
|
|
482
|
+
const parentSymbolName = parentElement ? resolveSymbolName(parentElement) : undefined;
|
|
483
|
+
const parentQualifiedName = parentElement ? qualifiedNameByLineStart.get(parentElement.lineStart) : undefined;
|
|
484
|
+
const qualifiedName = qualifiedNameByLineStart.get(element.lineStart)
|
|
485
|
+
?? (parentQualifiedName ? `${parentQualifiedName}.${symbolName}` : symbolName);
|
|
486
|
+
const aggregateDoxygen = collectElementDoxygenFields(element);
|
|
487
|
+
const structuredDoxygen = structureDoxygenFields(aggregateDoxygen);
|
|
488
|
+
const codeLines = sourceLines.slice(element.lineStart - 1, element.lineEnd);
|
|
489
|
+
const strippedCodeLines = mapCodeLines(
|
|
490
|
+
buildStrippedConstructLineEntries(codeLines, languageId, element.lineStart, includeLineNumbers),
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
return {
|
|
494
|
+
...buildLineRange(element.lineStart, element.lineEnd),
|
|
495
|
+
match_index: matchIndex,
|
|
496
|
+
declaration_order_index: matchIndex,
|
|
497
|
+
symbol_name: symbolName,
|
|
498
|
+
qualified_name: qualifiedName,
|
|
499
|
+
symbol_kind: element.elementType,
|
|
500
|
+
type_label: element.typeLabel,
|
|
501
|
+
signature_text: element.signature,
|
|
502
|
+
visibility: element.visibility,
|
|
503
|
+
parent_symbol_name: parentSymbolName,
|
|
504
|
+
parent_qualified_name: parentQualifiedName,
|
|
505
|
+
depth: element.depth,
|
|
506
|
+
inherits_text: element.inherits,
|
|
507
|
+
brief_text: structuredDoxygen.brief?.[0],
|
|
508
|
+
doxygen_field_count: countDoxygenFieldValues(aggregateDoxygen),
|
|
509
|
+
doxygen: Object.keys(structuredDoxygen).length > 0 ? structuredDoxygen : undefined,
|
|
510
|
+
code_line_count: strippedCodeLines.length,
|
|
511
|
+
code_lines: strippedCodeLines,
|
|
512
|
+
stripped_source_text: buildStrippedSourceText(strippedCodeLines),
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* @brief Analyzes one path into a structured find file entry.
|
|
518
|
+
* @details Resolves path identity, validates language and tag support, parses the file with `SourceAnalyzer`, builds structured match entries, and preserves stable status facts for no-match or failure outcomes. Runtime is dominated by file I/O and analyzer cost. Side effects are limited to filesystem reads and optional stderr logging.
|
|
519
|
+
* @param[in] analyzer {SourceAnalyzer} Shared analyzer instance.
|
|
520
|
+
* @param[in] inputPath {string} Caller-supplied path.
|
|
521
|
+
* @param[in] absolutePath {string} Absolute path resolved against the payload base directory.
|
|
522
|
+
* @param[in] requestIndex {number} Caller-order index.
|
|
523
|
+
* @param[in] baseDir {string} Base directory used for canonical path derivation.
|
|
524
|
+
* @param[in] tagSet {Set<string>} Validated requested tag set.
|
|
525
|
+
* @param[in] regex {RegExp} Validated construct-name regex.
|
|
526
|
+
* @param[in] includeLineNumbers {boolean} When `true`, rendered stripped-source text includes original line prefixes.
|
|
527
|
+
* @param[in] verbose {boolean} When `true`, emit per-file diagnostics to stderr.
|
|
528
|
+
* @return {FindToolFileEntry} Structured file entry.
|
|
529
|
+
*/
|
|
530
|
+
function analyzeFindFile(
|
|
531
|
+
analyzer: SourceAnalyzer,
|
|
532
|
+
inputPath: string,
|
|
533
|
+
absolutePath: string,
|
|
534
|
+
requestIndex: number,
|
|
535
|
+
baseDir: string,
|
|
536
|
+
tagSet: Set<string>,
|
|
537
|
+
regex: RegExp,
|
|
538
|
+
includeLineNumbers: boolean,
|
|
539
|
+
verbose: boolean,
|
|
540
|
+
): FindToolFileEntry {
|
|
541
|
+
const canonicalPath = canonicalizeFindPath(absolutePath, baseDir);
|
|
542
|
+
const fileName = path.basename(absolutePath);
|
|
543
|
+
const fileExtension = path.extname(absolutePath).toLowerCase();
|
|
544
|
+
const languageId = detectLanguage(absolutePath);
|
|
545
|
+
if (!languageId) {
|
|
546
|
+
if (verbose) {
|
|
547
|
+
console.error(` SKIP ${inputPath} (unsupported extension)`);
|
|
548
|
+
}
|
|
549
|
+
return {
|
|
550
|
+
...buildLineRange(0, 0),
|
|
551
|
+
request_index: requestIndex,
|
|
552
|
+
input_path: inputPath,
|
|
553
|
+
canonical_path: canonicalPath,
|
|
554
|
+
absolute_path: absolutePath,
|
|
555
|
+
file_name: fileName,
|
|
556
|
+
file_extension: fileExtension,
|
|
557
|
+
status: "skipped",
|
|
558
|
+
exists: true,
|
|
559
|
+
is_file: true,
|
|
560
|
+
supported_tag_count: 0,
|
|
561
|
+
supported_tags: [],
|
|
562
|
+
line_count: 0,
|
|
563
|
+
match_count: 0,
|
|
564
|
+
doxygen_field_count: 0,
|
|
565
|
+
matches: [],
|
|
566
|
+
error_reason: "unsupported_extension",
|
|
567
|
+
error_message: "unsupported extension",
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const supportedTags = [...(LANGUAGE_TAGS[languageId] ?? [])].sort();
|
|
572
|
+
if (![...tagSet].some((tag) => supportedTags.includes(tag))) {
|
|
573
|
+
if (verbose) {
|
|
574
|
+
console.error(` SKIP ${inputPath} (language ${languageId} does not support requested tags)`);
|
|
575
|
+
}
|
|
576
|
+
return {
|
|
577
|
+
...buildLineRange(0, 0),
|
|
578
|
+
request_index: requestIndex,
|
|
579
|
+
input_path: inputPath,
|
|
580
|
+
canonical_path: canonicalPath,
|
|
581
|
+
absolute_path: absolutePath,
|
|
582
|
+
file_name: fileName,
|
|
583
|
+
file_extension: fileExtension,
|
|
584
|
+
language_id: languageId,
|
|
585
|
+
language_name: analyzer.specs[languageId]?.name,
|
|
586
|
+
status: "skipped",
|
|
587
|
+
exists: true,
|
|
588
|
+
is_file: true,
|
|
589
|
+
supported_tag_count: supportedTags.length,
|
|
590
|
+
supported_tags: supportedTags,
|
|
591
|
+
line_count: 0,
|
|
592
|
+
match_count: 0,
|
|
593
|
+
doxygen_field_count: 0,
|
|
594
|
+
matches: [],
|
|
595
|
+
error_reason: "unsupported_tags_for_language",
|
|
596
|
+
error_message: "language does not support any requested tags",
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
try {
|
|
601
|
+
const fileContent = fs.readFileSync(absolutePath, "utf8");
|
|
602
|
+
const lineCount = countLogicalLines(fileContent);
|
|
603
|
+
const sourceLines = fileContent.split(/(?<=\n)/);
|
|
604
|
+
const elements = analyzer.analyze(absolutePath, languageId);
|
|
605
|
+
analyzer.enrich(elements, languageId, absolutePath);
|
|
606
|
+
const fileLevelDoxygen = collectFileLevelDoxygenFields(elements);
|
|
607
|
+
const structuredFileDoxygen = structureDoxygenFields(fileLevelDoxygen);
|
|
608
|
+
const definitions = elements
|
|
609
|
+
.filter((element) => ![ElementType.COMMENT_SINGLE, ElementType.COMMENT_MULTI, ElementType.IMPORT, ElementType.DECORATOR].includes(element.elementType))
|
|
610
|
+
.sort((left, right) => left.lineStart - right.lineStart);
|
|
611
|
+
const qualifiedNameByLineStart = new Map<number, string>();
|
|
612
|
+
definitions.forEach((element) => {
|
|
613
|
+
const symbolName = resolveSymbolName(element);
|
|
614
|
+
const parentElement = resolveParentElement(definitions, element);
|
|
615
|
+
const parentQualifiedName = parentElement ? qualifiedNameByLineStart.get(parentElement.lineStart) : undefined;
|
|
616
|
+
const qualifiedName = parentQualifiedName ? `${parentQualifiedName}.${symbolName}` : symbolName;
|
|
617
|
+
qualifiedNameByLineStart.set(element.lineStart, qualifiedName);
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
const matchedElements = elements
|
|
621
|
+
.filter((element) => elementMatches(element, tagSet, regex))
|
|
622
|
+
.sort((left, right) => left.lineStart - right.lineStart || left.lineEnd - right.lineEnd);
|
|
623
|
+
const matches = matchedElements.map((element, matchIndex) => buildMatchEntry(
|
|
624
|
+
element,
|
|
625
|
+
matchIndex,
|
|
626
|
+
definitions,
|
|
627
|
+
qualifiedNameByLineStart,
|
|
628
|
+
sourceLines,
|
|
629
|
+
languageId,
|
|
630
|
+
includeLineNumbers,
|
|
631
|
+
));
|
|
632
|
+
const fileDoxygenFieldCount = countDoxygenFieldValues(fileLevelDoxygen);
|
|
633
|
+
const matchDoxygenFieldCount = matches.reduce((sum, match) => sum + match.doxygen_field_count, 0);
|
|
634
|
+
|
|
635
|
+
if (matches.length > 0) {
|
|
636
|
+
if (verbose) {
|
|
637
|
+
console.error(` OK ${absolutePath} (${matches.length} matches)`);
|
|
638
|
+
}
|
|
639
|
+
return {
|
|
640
|
+
...buildLineRange(lineCount > 0 ? 1 : 0, lineCount),
|
|
641
|
+
request_index: requestIndex,
|
|
642
|
+
input_path: inputPath,
|
|
643
|
+
canonical_path: canonicalPath,
|
|
644
|
+
absolute_path: absolutePath,
|
|
645
|
+
file_name: fileName,
|
|
646
|
+
file_extension: fileExtension,
|
|
647
|
+
language_id: languageId,
|
|
648
|
+
language_name: analyzer.specs[languageId]?.name,
|
|
649
|
+
status: "matched",
|
|
650
|
+
exists: true,
|
|
651
|
+
is_file: true,
|
|
652
|
+
supported_tag_count: supportedTags.length,
|
|
653
|
+
supported_tags: supportedTags,
|
|
654
|
+
line_count: lineCount,
|
|
655
|
+
match_count: matches.length,
|
|
656
|
+
doxygen_field_count: fileDoxygenFieldCount + matchDoxygenFieldCount,
|
|
657
|
+
file_description_text: structuredFileDoxygen.brief?.[0] ?? structuredFileDoxygen.details?.[0],
|
|
658
|
+
file_doxygen: Object.keys(structuredFileDoxygen).length > 0 ? structuredFileDoxygen : undefined,
|
|
659
|
+
matches,
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
if (verbose) {
|
|
664
|
+
console.error(` SKIP ${absolutePath} (no matches)`);
|
|
665
|
+
}
|
|
666
|
+
return {
|
|
667
|
+
...buildLineRange(lineCount > 0 ? 1 : 0, lineCount),
|
|
668
|
+
request_index: requestIndex,
|
|
669
|
+
input_path: inputPath,
|
|
670
|
+
canonical_path: canonicalPath,
|
|
671
|
+
absolute_path: absolutePath,
|
|
672
|
+
file_name: fileName,
|
|
673
|
+
file_extension: fileExtension,
|
|
674
|
+
language_id: languageId,
|
|
675
|
+
language_name: analyzer.specs[languageId]?.name,
|
|
676
|
+
status: "no_match",
|
|
677
|
+
exists: true,
|
|
678
|
+
is_file: true,
|
|
679
|
+
supported_tag_count: supportedTags.length,
|
|
680
|
+
supported_tags: supportedTags,
|
|
681
|
+
line_count: lineCount,
|
|
682
|
+
match_count: 0,
|
|
683
|
+
doxygen_field_count: fileDoxygenFieldCount,
|
|
684
|
+
file_description_text: structuredFileDoxygen.brief?.[0] ?? structuredFileDoxygen.details?.[0],
|
|
685
|
+
file_doxygen: Object.keys(structuredFileDoxygen).length > 0 ? structuredFileDoxygen : undefined,
|
|
686
|
+
matches: [],
|
|
687
|
+
};
|
|
688
|
+
} catch (error) {
|
|
689
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
690
|
+
if (verbose) {
|
|
691
|
+
console.error(` FAIL ${absolutePath} (${errorMessage})`);
|
|
692
|
+
}
|
|
693
|
+
return {
|
|
694
|
+
...buildLineRange(0, 0),
|
|
695
|
+
request_index: requestIndex,
|
|
696
|
+
input_path: inputPath,
|
|
697
|
+
canonical_path: canonicalPath,
|
|
698
|
+
absolute_path: absolutePath,
|
|
699
|
+
file_name: fileName,
|
|
700
|
+
file_extension: fileExtension,
|
|
701
|
+
language_id: languageId,
|
|
702
|
+
language_name: analyzer.specs[languageId]?.name,
|
|
703
|
+
status: "error",
|
|
704
|
+
exists: true,
|
|
705
|
+
is_file: true,
|
|
706
|
+
supported_tag_count: supportedTags.length,
|
|
707
|
+
supported_tags: supportedTags,
|
|
708
|
+
line_count: 0,
|
|
709
|
+
match_count: 0,
|
|
710
|
+
doxygen_field_count: 0,
|
|
711
|
+
matches: [],
|
|
712
|
+
error_reason: "analysis_failed",
|
|
713
|
+
error_message: errorMessage,
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* @brief Builds the full agent-oriented find payload.
|
|
720
|
+
* @details Validates request parameters, analyzes requested files in caller order when the request is valid, preserves skipped and no-match outcomes in structured file entries, computes aggregate numeric totals, and emits a structured supported-tag matrix. Runtime is O(F log F + S + M). Side effects are limited to filesystem reads and optional stderr logging.
|
|
721
|
+
* @param[in] options {BuildFindToolPayloadOptions} Payload-construction options.
|
|
722
|
+
* @return {FindToolPayload} Structured find payload ordered as request, summary, repository, and files.
|
|
723
|
+
* @satisfies REQ-089, REQ-090, REQ-091, REQ-092, REQ-093, REQ-094, REQ-096, REQ-098
|
|
724
|
+
*/
|
|
725
|
+
export function buildFindToolPayload(options: BuildFindToolPayloadOptions): FindToolPayload {
|
|
726
|
+
const {
|
|
727
|
+
toolName,
|
|
728
|
+
scope,
|
|
729
|
+
baseDir,
|
|
730
|
+
tagFilter,
|
|
731
|
+
pattern,
|
|
732
|
+
requestedPaths,
|
|
733
|
+
includeLineNumbers,
|
|
734
|
+
sourceDirectoryPaths = [],
|
|
735
|
+
verbose = false,
|
|
736
|
+
} = options;
|
|
737
|
+
const absoluteBaseDir = path.resolve(baseDir);
|
|
738
|
+
const lineNumberMode: FindLineNumberMode = includeLineNumbers ? "enabled" : "disabled";
|
|
739
|
+
const canonicalRequestedPaths = requestedPaths.map((requestedPath) => canonicalizeFindPath(requestedPath, absoluteBaseDir));
|
|
740
|
+
const tagValidation = validateTagFilter(tagFilter);
|
|
741
|
+
const regexValidation = validateRegexPattern(pattern);
|
|
742
|
+
const analyzer = new SourceAnalyzer();
|
|
743
|
+
|
|
744
|
+
let files: FindToolFileEntry[] = [];
|
|
745
|
+
let searchStatus: FindSearchStatus = "matched";
|
|
746
|
+
|
|
747
|
+
if (tagValidation.status === "invalid") {
|
|
748
|
+
searchStatus = "invalid_tag_filter";
|
|
749
|
+
files = requestedPaths.map((requestedPath, requestIndex) => {
|
|
750
|
+
const absolutePath = path.resolve(absoluteBaseDir, requestedPath);
|
|
751
|
+
const exists = fs.existsSync(absolutePath);
|
|
752
|
+
const isFile = exists && fs.statSync(absolutePath).isFile();
|
|
753
|
+
return buildSkippedFileEntry(
|
|
754
|
+
requestedPath,
|
|
755
|
+
absolutePath,
|
|
756
|
+
requestIndex,
|
|
757
|
+
absoluteBaseDir,
|
|
758
|
+
"invalid_tag_filter",
|
|
759
|
+
tagValidation.errorMessage ?? "invalid tag filter",
|
|
760
|
+
exists,
|
|
761
|
+
isFile,
|
|
762
|
+
);
|
|
763
|
+
});
|
|
764
|
+
} else if (regexValidation.status === "invalid") {
|
|
765
|
+
searchStatus = "invalid_regex";
|
|
766
|
+
files = requestedPaths.map((requestedPath, requestIndex) => {
|
|
767
|
+
const absolutePath = path.resolve(absoluteBaseDir, requestedPath);
|
|
768
|
+
const exists = fs.existsSync(absolutePath);
|
|
769
|
+
const isFile = exists && fs.statSync(absolutePath).isFile();
|
|
770
|
+
return buildSkippedFileEntry(
|
|
771
|
+
requestedPath,
|
|
772
|
+
absolutePath,
|
|
773
|
+
requestIndex,
|
|
774
|
+
absoluteBaseDir,
|
|
775
|
+
"invalid_regex",
|
|
776
|
+
regexValidation.errorMessage ?? "invalid regex",
|
|
777
|
+
exists,
|
|
778
|
+
isFile,
|
|
779
|
+
);
|
|
780
|
+
});
|
|
781
|
+
} else {
|
|
782
|
+
files = requestedPaths.map((requestedPath, requestIndex) => {
|
|
783
|
+
const absolutePath = path.resolve(absoluteBaseDir, requestedPath);
|
|
784
|
+
if (!fs.existsSync(absolutePath)) {
|
|
785
|
+
if (verbose) {
|
|
786
|
+
console.error(` SKIP ${requestedPath} (file not found)`);
|
|
787
|
+
}
|
|
788
|
+
return buildSkippedFileEntry(
|
|
789
|
+
requestedPath,
|
|
790
|
+
absolutePath,
|
|
791
|
+
requestIndex,
|
|
792
|
+
absoluteBaseDir,
|
|
793
|
+
"not_found",
|
|
794
|
+
"not found",
|
|
795
|
+
false,
|
|
796
|
+
false,
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
const stats = fs.statSync(absolutePath);
|
|
800
|
+
if (!stats.isFile()) {
|
|
801
|
+
if (verbose) {
|
|
802
|
+
console.error(` SKIP ${requestedPath} (not a file)`);
|
|
803
|
+
}
|
|
804
|
+
return buildSkippedFileEntry(
|
|
805
|
+
requestedPath,
|
|
806
|
+
absolutePath,
|
|
807
|
+
requestIndex,
|
|
808
|
+
absoluteBaseDir,
|
|
809
|
+
"not_file",
|
|
810
|
+
"not a file",
|
|
811
|
+
true,
|
|
812
|
+
false,
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
return analyzeFindFile(
|
|
816
|
+
analyzer,
|
|
817
|
+
requestedPath,
|
|
818
|
+
absolutePath,
|
|
819
|
+
requestIndex,
|
|
820
|
+
absoluteBaseDir,
|
|
821
|
+
tagValidation.tagSet,
|
|
822
|
+
regexValidation.regex!,
|
|
823
|
+
includeLineNumbers,
|
|
824
|
+
verbose,
|
|
825
|
+
);
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
const matchedFileCount = files.filter((file) => file.status === "matched").length;
|
|
829
|
+
if (matchedFileCount === 0) {
|
|
830
|
+
searchStatus = "no_matches";
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const matchedFiles = files.filter((file) => file.status === "matched");
|
|
835
|
+
const processableFiles = files.filter((file) => file.status !== "skipped");
|
|
836
|
+
const repositoryFileCanonicalPaths = files.map((file) => file.canonical_path);
|
|
837
|
+
|
|
838
|
+
return {
|
|
839
|
+
request: {
|
|
840
|
+
tool_name: toolName,
|
|
841
|
+
scope,
|
|
842
|
+
base_dir_path: absoluteBaseDir,
|
|
843
|
+
line_number_mode: lineNumberMode,
|
|
844
|
+
tag_filter_text: tagFilter,
|
|
845
|
+
tag_filter_values: tagValidation.tagValues,
|
|
846
|
+
tag_filter_status: tagValidation.status,
|
|
847
|
+
tag_filter_error_message: tagValidation.errorMessage,
|
|
848
|
+
name_regex_text: pattern,
|
|
849
|
+
regex_engine: "javascript-regexp-search",
|
|
850
|
+
regex_status: regexValidation.status,
|
|
851
|
+
regex_error_message: regexValidation.errorMessage,
|
|
852
|
+
source_directory_count: sourceDirectoryPaths.length,
|
|
853
|
+
source_directory_paths: sourceDirectoryPaths.map((sourceDirectoryPath) => canonicalizeFindPath(sourceDirectoryPath, absoluteBaseDir)),
|
|
854
|
+
requested_file_count: requestedPaths.length,
|
|
855
|
+
requested_input_paths: [...requestedPaths],
|
|
856
|
+
requested_canonical_paths: canonicalRequestedPaths,
|
|
857
|
+
},
|
|
858
|
+
summary: {
|
|
859
|
+
search_status: searchStatus,
|
|
860
|
+
processable_file_count: processableFiles.length,
|
|
861
|
+
matched_file_count: matchedFiles.length,
|
|
862
|
+
no_match_file_count: files.filter((file) => file.status === "no_match").length,
|
|
863
|
+
error_file_count: files.filter((file) => file.status === "error").length,
|
|
864
|
+
skipped_file_count: files.filter((file) => file.status === "skipped").length,
|
|
865
|
+
total_match_count: matchedFiles.reduce((sum, file) => sum + file.match_count, 0),
|
|
866
|
+
total_code_line_count: matchedFiles.reduce(
|
|
867
|
+
(sum, file) => sum + file.matches.reduce((fileSum, match) => fileSum + match.code_line_count, 0),
|
|
868
|
+
0,
|
|
869
|
+
),
|
|
870
|
+
total_doxygen_field_count: files.reduce((sum, file) => sum + file.doxygen_field_count, 0),
|
|
871
|
+
},
|
|
872
|
+
repository: {
|
|
873
|
+
root_directory_path: absoluteBaseDir,
|
|
874
|
+
source_directory_paths: sourceDirectoryPaths.map((sourceDirectoryPath) => canonicalizeFindPath(sourceDirectoryPath, absoluteBaseDir)),
|
|
875
|
+
file_count: repositoryFileCanonicalPaths.length,
|
|
876
|
+
file_canonical_paths: repositoryFileCanonicalPaths,
|
|
877
|
+
supported_tags_by_language: buildSupportedTagsByLanguage(),
|
|
878
|
+
},
|
|
879
|
+
files,
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* @brief Builds deterministic stderr diagnostics from a find payload.
|
|
885
|
+
* @details Serializes invalid request states, skipped inputs, no-match files, and analysis failures into stable newline-delimited diagnostics while leaving successful matched files silent. Runtime is O(n) in file-entry count. No side effects occur.
|
|
886
|
+
* @param[in] payload {FindToolPayload} Structured find payload.
|
|
887
|
+
* @return {string} Newline-delimited diagnostics.
|
|
888
|
+
* @satisfies REQ-096
|
|
889
|
+
*/
|
|
890
|
+
export function buildFindToolExecutionStderr(payload: FindToolPayload): string {
|
|
891
|
+
const diagnostics: string[] = [];
|
|
892
|
+
if (payload.request.tag_filter_status === "invalid") {
|
|
893
|
+
diagnostics.push(`error: tag_filter: ${payload.request.tag_filter_error_message ?? "invalid tag filter"}`);
|
|
894
|
+
}
|
|
895
|
+
if (payload.request.regex_status === "invalid") {
|
|
896
|
+
diagnostics.push(`error: name_regex: ${payload.request.regex_error_message ?? "invalid regex"}`);
|
|
897
|
+
}
|
|
898
|
+
payload.files.forEach((file) => {
|
|
899
|
+
if (file.status === "skipped") {
|
|
900
|
+
diagnostics.push(`skipped: ${file.canonical_path}: ${file.error_reason ?? "skipped"}`);
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
if (file.status === "no_match") {
|
|
904
|
+
diagnostics.push(`info: no_match: ${file.canonical_path}`);
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
if (file.status === "error") {
|
|
908
|
+
diagnostics.push(`error: ${file.canonical_path}: ${file.error_reason ?? "analysis_failed"}: ${file.error_message ?? "unknown error"}`);
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
if (payload.summary.search_status === "no_matches") {
|
|
912
|
+
diagnostics.push("info: no constructs found matching the requested tag filter and regex");
|
|
913
|
+
}
|
|
914
|
+
return diagnostics.join("\n");
|
|
915
|
+
}
|