feat(config): centralized configuration [IDE-1786]#1162
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
1 similar comment
This comment was marked as outdated.
This comment was marked as outdated.
|
/review |
This comment has been minimized.
This comment has been minimized.
|
/review |
This comment has been minimized.
This comment has been minimized.
|
/review |
PR Reviewer Guide 🔍
|
| // Save the migrated folder config back to storage | ||
| if err := storedconfig.UpdateFolderConfig(gafConfig, folderConfig, &logger); err != nil { | ||
| logger.Err(err).Msg("unable to save folder config") | ||
| applyChanged := storedFolderConfig == nil |
There was a problem hiding this comment.
if storedFolderConfig is nil it would panic in the line that is trying to clone
|
|
||
| // ReadFolderConfigSnapshot reads folder config values from configuration into a snapshot. | ||
| // An optional ConfigurationOptionsMetaData may be passed to populate UserOverrides for org-scoped flags. | ||
| func ReadFolderConfigSnapshot(conf configuration.Configuration, folderPath FilePath, fms ...workflow.ConfigurationOptionsMetaData) FolderConfigSnapshot { |
There was a problem hiding this comment.
this should use configresolver.Resolve func
| // config.displayName, config.description, and config.ideKey for framework integration. | ||
| func RegisterAllConfigurations(fs *pflag.FlagSet) { | ||
| // Machine-scope settings (14) | ||
| registerFlag(fs, SettingApiEndpoint, "", "API endpoint URL", map[string][]string{ |
There was a problem hiding this comment.
TODO: check which flags are persisted and which aren't
| engineConfig.Set(configresolver.UserGlobalKey(types.SettingAuthenticationMethod), string(types.OAuthAuthentication)) | ||
| engineConfig.Set(configresolver.UserGlobalKey(types.SettingToken), "") | ||
| engineConfig.Set(configresolver.UserGlobalKey(types.SettingCliPath), CliDefaultBinaryInstallPath()) | ||
| if _, ok := engineConfig.Get(configresolver.UserGlobalKey(types.SettingBinarySearchPaths)).([]string); !ok { |
There was a problem hiding this comment.
SettingBinarySearchPath doesn't have a flagset registration, is this intended ? when should a setting have a flagset registration and when it shouldn't?
| configresolver.AnnotationRemoteKey: {"publish_security_at_inception_rules"}, | ||
| configresolver.AnnotationDisplayName: {"Publish Security At Inception Rules"}, | ||
| configresolver.AnnotationDescription: {"Publish security rules at inception"}, | ||
| configresolver.AnnotationIdeKey: {"publishSecurityAtInceptionRules"}, |
There was a problem hiding this comment.
replace this with SettingSecureAtInceptionExecutionFreq and delete SettingPublishSecurityAtInceptionRules
| configresolver.AnnotationDescription: {"Publish security rules at inception"}, | ||
| configresolver.AnnotationIdeKey: {"publishSecurityAtInceptionRules"}, | ||
| }) | ||
| registerFlag(fs, SettingTrustEnabled, "", "Enable trusted folders feature", map[string][]string{ |
There was a problem hiding this comment.
trust is internal config. let's leave it internal and not expose it now.
| configresolver.AnnotationIdeKey: {"token"}, | ||
| }) | ||
| registerFlag(fs, SettingSendErrorReports, false, "Send error reports", map[string][]string{ | ||
| configresolver.AnnotationScope: {"machine"}, |
There was a problem hiding this comment.
nitpick: reuse the scope constant from GAF
There was a problem hiding this comment.
also we don't have this setting
| configresolver.AnnotationDescription: {"Enable sending error reports to Snyk"}, | ||
| configresolver.AnnotationIdeKey: {"sendErrorReports"}, | ||
| }) | ||
| registerFlag(fs, SettingEnableSnykLearnCodeActions, false, "Enable Snyk Learn code actions", map[string][]string{ |
| configresolver.AnnotationDescription: {"Enable Snyk Learn code actions"}, | ||
| configresolver.AnnotationIdeKey: {"enableSnykLearnCodeActions"}, | ||
| }) | ||
| registerFlag(fs, SettingEnableSnykOssQuickFixActions, false, "Enable Snyk OSS quick fix code actions", map[string][]string{ |
| }) | ||
| registerFlag(fs, SettingEnableSnykLearnCodeActions, false, "Enable Snyk Learn code actions", map[string][]string{ | ||
| configresolver.AnnotationScope: {"machine"}, | ||
| configresolver.AnnotationWriteOnly: {"true"}, |
There was a problem hiding this comment.
what is a WriteOnly annotation?
|
/describe |
|
/review |
|
Preparing review... |
9cf7693 to
fc16c58
Compare
|
PR Description updated to latest commit (9cf7693) |
|
/describe |
|
PR Description updated to latest commit (77a7d2b) |
rrama
left a comment
There was a problem hiding this comment.
Submitting my comments so far. I am still a long way off, but I want to at least feedback some of the bugs I spotted.
Concern:
- To me it was not immediately clear that there is special folder config paths for
OrgSetByUser,PreferredOrg, andAutoDeterminedOrgthat do not have wiring, but all other folder configs need it. I am wondering if we should switch to calling them something else to make it clearer. They could beFolderOrgSelectorsor something (not so keen on calling them "native"), then if those functions are renamed to follow suit it would be clear if you are getting wiring for full or not. Then could do similar for the Git enrichments (not so keen on calling them "metadata"). - You have to know a lot about a key to get its value, it would be easier if all the information about a setting and how to get it's value was all in one place and you just use that everywhere. Currently it is spread between files like
configuration.go,config.go,config_readers.go,ldx_sync_config.goandregister_configurations.go, so there is a bit of jumping back and forth to figure out that some user global keys are overridden by LDX-Sync, etc., but feels weird I have to know it is a user global key in some of the places instead of that being stored with the key definition.
Testing improvements:
- Maybe an additional test to test a bit further / more multi-step than just
Test_RefreshConfigFromLdxSync_PreservesNonLockedOverrides: A test which starts off with user set at folder risk score at200, then a remote admin locks it to300and user fetches, then the remote admin unlocks and unsets the setting plus the user fetches, then verify that we get back the default of0and not the stale200.
Personal things to still check:
- Do we need migration code?
- Look into the bug AI thought it found:
Tracing ApplyLspUpdate (IDE → LS folder config writes)
FolderConfig.ApplyLspUpdate (folder_config.go:255) → applyFolderScopeUpdates (:344):applyBasicFolderFields (:395): Handles SettingBaseBranch, SettingReferenceBranch, SettingLocalBranches, SettingAdditionalParameters, SettingAdditionalEnvironment, SettingReferenceFolder, SettingScanCommandConfig. All written to UserFolderKey as *LocalConfigField{Value, Changed:true} except SettingLocalBranches which goes to FolderMetadataKey.
applyPreferredOrg (:488): Sets both SettingPreferredOrg and SettingOrgSetByUser together via UserFolderKey. Marks both as handled.
applyOrgSetByUser (:520): Handles standalone SettingOrgSetByUser changes. Note: applyPreferredOrg already marks SettingOrgSetByUser as handled at line 490, but applyOrgSetByUser re-adds it at line 521 — this is fine since the handled map is only used by the generic PATCH loop.
Generic PATCH loop (:373-391): For any remaining folder-scoped settings not in handled, applies Changed:true values to UserFolderKey, or Unsets on Value:nil.
Issue: getSettingValue doesn't check Changed flag
getSettingValue (folder_config.go:264-276) does not check cs.Changed. This means applyBasicFolderFields and applyPreferredOrg will process settings even when Changed: false. The generic PATCH loop at line 374 correctly checks !cs.Changed, but the explicit handlers don't. This is inconsistent with the documented PATCH semantics. In practice, it's mitigated by equality checks (e.g., baseBranch != cur), but a setting sent with Changed:false and a different value than current would still be applied.
| - Folder Config Notification | ||
| - method: `$/snyk.folderConfigs` | ||
| - params: `types.FolderConfigsParam` | ||
| - Configuration Notification (protocol v25+) |
There was a problem hiding this comment.
Nitpick: Don't state the protocol version, if someone wants historic analysis, they can get it from the Git history.
| * limitations under the License. | ||
| */ | ||
|
|
||
| package types |
There was a problem hiding this comment.
Why is this whole file in the types package?
| // goroutine. SetEngineDefaults only sets defaults when the key is not yet present, | ||
| // so a pre-seeded configuration ensures the goroutine reads the test-specific paths | ||
| // rather than the system defaults (which in CI can include many unrelated Java installs). | ||
| conf := configuration.NewWithOpts(configuration.WithAutomaticEnv()) |
There was a problem hiding this comment.
Minor nitpick: Comment should also explain we use this technique to be more realistic, as in prod we have WithAutomaticEnv().
| func initEngineForClientSettingsTest(t *testing.T) workflow.Engine { | ||
| t.Helper() | ||
| e, _ := InitEngine(nil) | ||
| e.GetConfiguration().Set(configresolver.UserGlobalKey(types.SettingBinarySearchPaths), []string{}) |
| func initEngineForConfigTest(t *testing.T) (workflow.Engine, *TokenServiceImpl) { | ||
| t.Helper() | ||
| engine, ts := InitEngine(nil) | ||
| engine.GetConfiguration().Set(configresolver.UserGlobalKey(types.SettingBinarySearchPaths), []string{}) |
There was a problem hiding this comment.
Same, not meant to be keyed.
| modified := c.enableDeltaFindings != enabled | ||
| c.enableDeltaFindings = enabled | ||
| return modified | ||
| return getCustomEndpointUrlFromSnykApi(types.GetGlobalString(conf, types.SettingApiEndpoint), "deeproxy") |
There was a problem hiding this comment.
Nitpick: Again may be worth calling out in a comment that LDX-Sync will directly override the user global key for API endpoint.
| // AuthenticationMethodMatchesCredentials returns true if the token matches the configured authentication method. | ||
| func AuthenticationMethodMatchesCredentials(token string, method types.AuthenticationMethod, logger *zerolog.Logger) bool { | ||
| if method == types.FakeAuthentication { | ||
| return true // We allow any value for the token in unit tests which use FakeAuthentication. |
There was a problem hiding this comment.
Nitpick: Leave the comment in.
| if org != "" { | ||
| // Store the resolved org so that defaultFuncOrganization's UUID fast-path | ||
| // returns it directly next time, avoiding /rest/self. | ||
| conf.Set(configuration.ORGANIZATION, org) |
There was a problem hiding this comment.
Not needed. After a resolved defaultFunc GAF will cache it.
Although standalone and tests do not have the cache enabled... See https://snyksec.atlassian.net/browse/IDE-1727
So a future enhancement to remove this then.
There was a problem hiding this comment.
It is needed, when the non-UUID org is given, as this is not cached. Was a fix to reduce API calls.
There was a problem hiding this comment.
I disagree, the GetWithError function in GAF's pkg/configuration/configuration.go will cache it after the default value function runs if you have caching enabled. I am going to refactor this out in my PR.
| c.deviceId = deviceId | ||
| } | ||
| if engine == nil { | ||
| conf := configuration.NewWithOpts(configuration.WithAutomaticEnv()) |
There was a problem hiding this comment.
Future enhancement reminder: We should enable the cache for standalone / tests: See https://snyksec.atlassian.net/browse/IDE-1727
| } | ||
| if v, ok := settingBool(settings, types.SettingIssueViewIgnoredIssues); ok { | ||
| ivo.IgnoredIssues = v | ||
| } |
There was a problem hiding this comment.
If only one is set then the other will be defaulted back to `false. TODO - Check severity filters too.
| key := configresolver.FolderMetadataKey(dst, name) | ||
| conf.PersistInStorage(key) | ||
| conf.Set(key, v) | ||
| } |
There was a problem hiding this comment.
why would we need to persist config for base scan ?
| for _, name := range userSettings { | ||
| if v := conf.Get(configresolver.UserFolderKey(src, name)); v != nil { | ||
| key := configresolver.UserFolderKey(dst, name) | ||
| conf.PersistInStorage(key) |
There was a problem hiding this comment.
Persist is now being called in many places, it should be centralized, it can be done on init and maybe when a folder is trusted
There was a problem hiding this comment.
PersistInStorage needs to be called once per field per configuration (if a field should be written to storage). If it's not called, the field is ephemeral.
| userSettings := []string{ | ||
| SettingBaseBranch, SettingReferenceBranch, SettingAdditionalParameters, | ||
| SettingAdditionalEnvironment, SettingReferenceFolder, SettingScanCommandConfig, | ||
| SettingPreferredOrg, SettingOrgSetByUser, | ||
| } |
There was a problem hiding this comment.
we shouldn't do this, instead load config names via scope and iterate through them
| gafConfiguration := conf | ||
|
|
||
| fs := pflag.NewFlagSet("snyk-ls-config", pflag.ContinueOnError) | ||
| types.RegisterAllConfigurations(fs) |
There was a problem hiding this comment.
this is also called when we create a new config so it's duplicated.
| logger.Debug().Err(err).Str("method", "clientSettingsFromEnv").Msgf("couldn't parse oss config %s", oss) | ||
| } | ||
| c.SetSnykOssEnabled(parseBool) | ||
| conf.Set(configresolver.UserGlobalKey(types.SettingSnykOssEnabled), parseBool) |
There was a problem hiding this comment.
The API to set a config is very confusing, when setting a configuration we must know the the scope which is pretty much error prone.
Instead we should simply just call Set and it will determine what the scope of this setting is based on what we already registered.
There was a problem hiding this comment.
Nitpick: Random swap for no reason.
| FormatMd = "md" | ||
| snykCodeTimeoutKey = "SNYK_CODE_TIMEOUT" // timeout as duration (number + unit), e.g. 10m | ||
| DefaultSnykApiUrl = "https://api.snyk.io" | ||
| DefaultSnykApiUrl = types.DefaultSnykApiUrl |
There was a problem hiding this comment.
Future refactor: Just use the original const everywhere.
| folderConfig, err := folderconfig.GetOrCreateFolderConfig(conf, path, logger) | ||
| if err != nil { | ||
| logger.Err(err).Msg("unable to get or create folder config") | ||
| folderConfig = &types.FolderConfig{FolderPath: path} |
There was a problem hiding this comment.
Now handled in GetFolderConfigWithOptions.
EDIT: Actually I will re-do a lot of it as the error paths just don't exist anymore.
| } | ||
|
|
||
| // BatchUpdateFolderConfigs validates folder configs for batch update. | ||
| func BatchUpdateFolderConfigs(conf configuration.Configuration, folderConfigs []*types.FolderConfig, logger *zerolog.Logger) error { |
There was a problem hiding this comment.
This function now does nothing, as ValidatePathForStorage is a literal no-op with no possible error path.
|
|
||
| // RegisterAllConfigurations registers all snyk-ls configuration flags with their annotations | ||
| // into the given FlagSet. Flags are annotated with config.scope, config.remoteKey, | ||
| // config.displayName, config.description, and config.ideKey for framework integration. |
There was a problem hiding this comment.
config.ideKey is no longer a thing.
| }) | ||
| registerFlag(fs, SettingRiskScoreThreshold, 0, "Risk score threshold (0-1000)", map[string][]string{ | ||
| configresolver.AnnotationScope: {folderScope}, | ||
| configresolver.AnnotationRemoteKey: {"risk_score_threshold"}, |
There was a problem hiding this comment.
Big nitpick code smell: Should be using string(v20241015.RiskScoreThreshold) or even better create a wrapper in GAF so we aren't having to update all these when we change to a different version of the API.
|
|
||
| return len(inlineValues) == 1 && inlineValues[0].Range.Start.Line == 17 && inlineValues[0].Range.End.Line == 17 | ||
| }, time.Minute, 1*time.Second, "expected inline values to be served, but they were not") | ||
| }, time.Minute, time.Millisecond, "expected inline values to be served, but they were not") |
There was a problem hiding this comment.
Why? This is an incredibly frequent poll which will just spam logs and slow things down surely. Looking at when you changed this (b6e3f0a) it seems like you did it randomly as part of a merge commit. I think this may be causing the panic I am seeing in CI: https://github.com/snyk/snyk-ls/actions/runs/24986913711/job/73162609575?pr=1162#logs
| time.Sleep(300 * time.Millisecond) | ||
| l.Debug().Msg("waiting for lsp initialization to be finished...") | ||
| for !conf.GetBool(types.SettingIsLspInitialized) { | ||
| time.Sleep(time.Millisecond) |
There was a problem hiding this comment.
I understand 300ms was too long, but why 1ms? Seems like a lot of CPU spinning. Why not 25ms?
There was a problem hiding this comment.
because with thousands of tests it adds up. local cpu spinning is ok (imo)
| return globalOrg | ||
| fcConf := fc.Conf() | ||
| if fcConf == nil { | ||
| fcConf = conf |
There was a problem hiding this comment.
I believe this path will always be hit, as the resolver and config is always nil from folderconfig.GetFolderConfigWithOptions
There was a problem hiding this comment.
This whole function's existence is silly and should be removed in favour of using FolderOrganizationFromConfig only.
| for _, folder := range ws.Folders() { | ||
| folderPath := folder.Path() | ||
| snapshot := types.ReadFolderConfigSnapshot(conf, folderPath) | ||
| effectiveOrg := snapshot.AutoDeterminedOrg |
There was a problem hiding this comment.
Should not be auto-org, should be the preferred org if org set by user.
#1240) * fix: refresh LDX-Sync before populating SAST cache on login [IDE-1981] When LDX-Sync resolves a different org after login than before, the post-credential hook cached feature flags + SAST settings keyed by the pre-login org. The next workspace scan reads the wrong cache entry and reports "Snyk Code is not enabled for this organization". - Reorder loginCommand post-credential hook: RefreshConfigFromLdxSync runs before populateFolderFeatureFlagsAndSastSettings so populate sees the post-login org. - Rename populateAllFolderConfigs -> populateFolderFeatureFlagsAndSastSettings. - Add info logs around startup populate flow + SAST cache hit/miss/failure for diagnosing similar issues. * chore: remove intrusive debug logging from login flow [IDE-1981] Strip verbose breadcrumb logs added during investigation of the SAST cache race. Restore prior log messages and levels; keep the fix logic (LDX-Sync refresh before populate) intact. * docs: restore context.Background rationale comment [IDE-1981] Per PR review — keep the explanation that context.Background() prevents the LDX-Sync refresh from being canceled when the IDE cancels the snyk.login LSP request. * test: clear stale token before snyk.login in StartsAuthentication [IDE-1981] After moving the login-time RefreshConfigFromLdxSync call into the post-credential hook, this test failed because: - During `initialized`, scanner Init runs an auth check that stores the fake provider's fixed token in config. - snyk.login's Authenticate then returns the same token; updateCredentials early-returns on the no-op (oldToken == newToken), so the post-credential hook (where Refresh now lives) never fires. Clearing the token after `initialized` simulates the realistic "unauthenticated user clicks Login" precondition the test is meant to exercise. No production change.
chore: change channel sizes
chore: downgrade go to 1.25.8
| // which issues /rest/self synchronously. IsSet bypasses default-value functions, | ||
| // keeping this read network-free for hot paths like StateSnapshot. Auto-determination | ||
| // stays the responsibility of GetGlobalOrganization. | ||
| func (r *ConfigResolver) GlobalOrg() string { |
There was a problem hiding this comment.
Having both this and types.GetGlobalOrganization() is confusing. Either we should stick to one OR rename this one to be explicitly a GlobalOrgNoNetwork or something.
Two related fixes for log-level handling that conspired to silently downgrade test output to INFO regardless of what the user requested. 1. application/config/config.go (SetupLogging) The error branch for SNYK_LOG_LEVEL parsing was guarded by `if levelErr == nil`, so it printed "Can't set log level from flag. Setting to default (=info)" on parse SUCCESS. Cosmetic — level itself was applied — but the misleading stderr line hid the real issue and invalid env values were silently ignored. Flip to `if levelErr \!= nil` and warn only on actual parse failure. Bug introduced in fc92273 (HEAD-317, 2023-06-01). 2. application/server/server_smoke_test.go (ensureInitialized) The helper unconditionally forced INFO via: config.SetLogLevel(zerolog.LevelInfoValue) t.Setenv("SNYK_LOG_LEVEL", config.GetLogLevel()) which clobbered any SNYK_LOG_LEVEL the developer had set, then replaced the engine logger with an INFO-level instance. Result: pre- init DBG lines visible, post-init silent at INFO. Only force INFO when env var not preset, so `SNYK_LOG_LEVEL=debug go test ...` actually surfaces debug logs end-to-end.
User description
Summary
Phase 2 of the IDE-1786 configuration resolution refactor:
ConfigResolverto read settings from GAF prefix keys instead of struct fields. InjectConfigResolverinto context for scanner access.map[string]*ConfigSettingfor LSP configuration exchange. AddLspFolderConfigwith per-folder settings maps.SastSettingsfromFolderConfigstruct field to GAF configuration withGet/SetSastSettingshelpers.persistedUserFolderSettings,persistedFolderMetadataSettings) andRegisterFolderPersistence()withSetFolderUserSetting/SetFolderMetadataSettinghelpers that combineSet+PersistInStorage.legacyFolderConfigJSON,MigrateLegacyFolderConfig,MigrateFolderConfigOrgSettings,OrgMigratedFromGlobalConfig,settings_migration.go).CliSettingsstruct intoConfig, promoting all fields and methods directly. Remove back-pointer pattern and separate mutex.precedence_smoke_test.goandscanner_precedence_test.gofor config resolution precedence validation.Test plan
make lint— 0 issuesmake test— all unit tests passSMOKE_TESTS=1 make test— smoke tests pass (except pre-existingTest_InvalidExpiredCredentialsSendMessageRequestflake)INTEG_TESTS=1 make test— integration testsPR Type
Enhancement, Refactor
Description
Diagram Walkthrough
flowchart LR subgraph Previous System C["config.Config"] end subgraph New System GAF_Conf["configuration.Configuration (GAF)"] CR["ConfigResolver (types)"] LS["LS Core Services<br/>(codeaction, scanner, etc.)"] NewInterface["New Config Accessors"] end C -->|Data Loaded Into| GAF_Conf GAF_Conf -->|Resolution Logic| CR CR -->|Provides Unified Access| LS LS -->|Uses| NewInterface C -->|Removed| Deprecated ``` <details> <summary><h3> File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Additional files</strong></td><td><details><summary>101 files</summary><table> <tr> <td><strong>mcp.json</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-19eb5f2245594c429cc37560d082721fbabff68227984d8d40bad80fd3514b83">[link]</a> </td> </tr> <tr> <td><strong>coder.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-b89595e8329168380c8d0d806f10fe48e0c74d6480bdd36d5f52fb50e60b38e7">+112/-0</a> </td> </tr> <tr> <td><strong>planner.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-3da7d9f10c1d522c26d34593af5559179e560e5f132e48d11925d54bc7e3ae8c">+95/-0</a> </td> </tr> <tr> <td><strong>qa.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-4399ee622fcd198ee75d7ce0c8f8bf764146afab8534c4b73be17d1b0d2e83c4">+126/-0</a> </td> </tr> <tr> <td><strong>commit.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-8a7812887cf57f02817a41095c0bc96e39f12a86dcedc8b9c7717843fb9fecff">+217/-0</a> </td> </tr> <tr> <td><strong>create-implementation-plan.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-c1589a6ef920a2e344d943924041069ef37a00f0c35b10b665b908aa4c3fe12e">+126/-0</a> </td> </tr> <tr> <td><strong>implementation.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-b7db59ef37d8544cde3bd3cb4463d2753bbcd1ef3da3e4d743d6d469476ab938">+202/-0</a> </td> </tr> <tr> <td><strong>verification.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-e63ba9b85e4f0149745953751025c71f5b9b3afd6dfa43b55ece2d3b0bfa56e7">+270/-0</a> </td> </tr> <tr> <td><strong>upload-to-s3.sh</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-4ec24b2c614b7504e801da062ef7c0a0ea927a853af71949117f9be1416955bd">+0/-12</a> </td> </tr> <tr> <td><strong>.goreleaser.yaml</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-7326b55c062b0f46fe9e39aace0a25f4515cf206040fb91a6fd2cae839f5e826">+1/-4</a> </td> </tr> <tr> <td><strong>.tmp_ignore</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-3a301d08bc19d4e6a95a86d0f5f652932e70406c818acb7c1497c799dce6d2c7">[link]</a> </td> </tr> <tr> <td><strong>launch.json</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-bd5430ee7c51dc892a67b3f2829d1f5b6d223f0fd48b82322cfd45baf9f5e945">+0/-23</a> </td> </tr> <tr> <td><strong>tasks.json</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-7d76d7533653c23b753fc7ce638cf64bdb5e419927d276af836d3a03fdf1745a">+0/-24</a> </td> </tr> <tr> <td><strong>coder.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-54b2c28a8e7b934853b4ab315640e41e7d75915418749972b73660a73dd2b8b6">+71/-0</a> </td> </tr> <tr> <td><strong>planner.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-3de3f79204a2442206f81923eaf79f4784cd2a508dde3722261ae346b527b4ba">+70/-0</a> </td> </tr> <tr> <td><strong>qa.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-4b3ee0d066f86a40697b5a63f015144677bddd2e4afc190c20f1e5d4316b1dbd">+117/-0</a> </td> </tr> <tr> <td><strong>general.mdc</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-56f646b00fd9a6c0573b57c0974764abd7f601a2deb90984147b1f66f7b4e6af">+112/-0</a> </td> </tr> <tr> <td><strong>SKILL.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-3eee1259a01cb01a6e29a2472eefcf3975d16e88e4c4841d7cb954dcedc57bdb">+260/-0</a> </td> </tr> <tr> <td><strong>SKILL.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-1e09e3863ee46b9fdc6f65d71c688175b12b8a2e7f4b0a994925ce4f52095352">+116/-0</a> </td> </tr> <tr> <td><strong>SKILL.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-773a18b37f68a8d4a8d73eb1a67eb4a70fd56c90d24f00295e36778a199e53b3">+241/-0</a> </td> </tr> <tr> <td><strong>SKILL.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-01767e331487ad3b20729c5e0747a8f44b4395e7ffdf7038b2fb8eb622a23e9b">+333/-0</a> </td> </tr> <tr> <td><strong>CLAUDE.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-6ebdb617a8104a7756d0cf36578ab01103dc9f07e4dc6feb751296b9c402faf7">+114/-0</a> </td> </tr> <tr> <td><strong>Makefile</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52">+2/-2</a> </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5">+26/-22</a> </td> </tr> <tr> <td><strong>helpers.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-939d219344d7e0ee2fabad9bd93cf499c569729779d098d7d7da97fefc181d15">+1/-15</a> </td> </tr> <tr> <td><strong>configuration_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-e141faafb3f0507956b85e92ac3b584c7c706bd96e7f428c7ef95d0d8b2df564">+752/-756</a></td> </tr> <tr> <td><strong>ldx_sync_smoke_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-e86bba96f063a23a5407b87f1d43156fbf47ff435cf44fc7324550746bc13305">+134/-61</a></td> </tr> <tr> <td><strong>scan_notifier.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-1b61c83cfc491251abf25e52610132617ca8ea6b0c1d9347b948fde580eaae42">+2/-5</a> </td> </tr> <tr> <td><strong>scan_notifier_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-5fdc92093964521489fec0d3eb3f9384994e19496fc0d274e0d6a3c3086a975d">+32/-24</a> </td> </tr> <tr> <td><strong>notification_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-09b80ca28f0c9f8db60c941c24e92dfc65ded48c06249e70e2004faec30b9891">+26/-25</a> </td> </tr> <tr> <td><strong>parallelization_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-dfb264976fa1c23451dc8d19b018920487af818f524aa24ea49211bd8b6326e9">+30/-24</a> </td> </tr> <tr> <td><strong>precedence_smoke_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-1c743b9b799604d498e5a5290961ffc135dec375c44150ad820cba81f3926d39">+1077/-0</a></td> </tr> <tr> <td><strong>secrets_smoke_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-c1510bd6a7151e3624d073a590787bd9af09d36b87a53a56067ffc0f3c8603fd">+24/-23</a> </td> </tr> <tr> <td><strong>server_smoke_treeview_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-abf8d4beebabcdeea5250f1698dc768bfbd744c22da6c343b552b5ed02908147">+11/-10</a> </td> </tr> <tr> <td><strong>server_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-165bad981a0cc202462a2fea7f236eab9ca3a72bd47fe8bae6b3adad801351fb">+272/-231</a></td> </tr> <tr> <td><strong>trust_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-e429a057cdac11e281a6ee8603bf8a5ab6dfaf0daedaec873982c47b28d7d431">+51/-49</a> </td> </tr> <tr> <td><strong>parser_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-56f8b9f576282024cef366a3d2024e23c393bc424bd1a7af8917142efcf913e0">+4/-4</a> </td> </tr> <tr> <td><strong>configuration.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-17ed18489a956f326ec0fe4040850c5bc9261d4631fb42da4c52891d74a59180">+712/-0</a> </td> </tr> <tr> <td><strong>configuration-effective-org.mmd</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-973fec236ae811f6a90a055f6b7d0da65dc4d11596911ce8ad89250223805426">+13/-0</a> </td> </tr> <tr> <td><strong>configuration-ide-to-ls.mmd</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-d96f7a3876e2a3103d4016e855aeed2eaf4e094231824ade00f1d15863ca2117">+29/-0</a> </td> </tr> <tr> <td><strong>configuration-ldx-sync-triggers.mmd</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-df333a4abf24cf3d87db395e0abe48a2faa721094e4c5c85350a3e539f7f5136">+39/-0</a> </td> </tr> <tr> <td><strong>configuration-locked-fields.mmd</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-38952676823280ad4b068d0c895c67c0793a9f12baa5fafbc4272e24b92e2188">+23/-0</a> </td> </tr> <tr> <td><strong>configuration-ls-to-ide.mmd</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-e87e10816f9abd36bfa4484439e3525b9094a79520d362815a983bfedd8f9587">+35/-0</a> </td> </tr> <tr> <td><strong>configuration-precedence-folder.mmd</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-cf55942689f0076c1c0d5e590504f623b7d4a2163177bf6a9c38b5fe2abc61e9">+43/-0</a> </td> </tr> <tr> <td><strong>configuration-precedence-machine.mmd</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-1a8c5dae9ef9700a8d68771cf78fe1053bd3a2b0d08b7dc6392ae9bce67ddcb8">+27/-0</a> </td> </tr> <tr> <td><strong>configuration-precedence-org.mmd</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-45c665222948a0e96307bcec7893b2c2eb30a1940831992d8bd16c015a7dca50">+31/-0</a> </td> </tr> <tr> <td><strong>tree-view.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-539534acc1cc1dac4c4790bdca3d442787d7e99ae04349bee000213f08300a28">+2/-2</a> </td> </tr> <tr> <td><strong>ui-rendering.md</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-c5a817d72c0cce20d0ef1e30f346d1533d38bf440472d1a0944fb0ad0c82f98a">+3/-3</a> </td> </tr> <tr> <td><strong>clear_cache_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-f59e398f3890e4a5979f96e46822865c12c0a1c24a04488066e1110e28f217ee">+12/-9</a> </td> </tr> <tr> <td><strong>code_fix_diffs_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-48255cf8a46398666fde50497322859b28c595de794ffd6670ece78b57ae7f75">+5/-7</a> </td> </tr> <tr> <td><strong>code_fix_feedback_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-9ecc2e3d8ac8d287a04858a39ff2b791deda6c161b005159f1df9cd201561be2">+24/-22</a> </td> </tr> <tr> <td><strong>code_fix_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-b6e42c27f7465637e31a6b441bb21a325b705c2e602cc5208182658553a53e71">+28/-18</a> </td> </tr> <tr> <td><strong>command_factory.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-b7c8bb6d3fa17d1b86a8de859670a7d849bdcedceb8d9a27f4e7f1a74b5d340d">+35/-31</a> </td> </tr> <tr> <td><strong>command_service.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-f91e9940ae7491a26a9708f682296ad692d4044fb82fad0d67811bc13f50fa0f">+12/-6</a> </td> </tr> <tr> <td><strong>command_service_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-4e5012d752e62ca48321ab14a65a40b32718552b72d0510af51c9eccfb981154">+4/-3</a> </td> </tr> <tr> <td><strong>configuration_command_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-4e6fcd9606a6e827d54cddeaf143ea61e51516a7e24ebcee13d0968284546bb0">+6/-5</a> </td> </tr> <tr> <td><strong>connectivity_check.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-bd2f39aac9325739fa4bc8f0086a9dbeee25ae8965cfaaebb59ebaf24bfc5892">+6/-7</a> </td> </tr> <tr> <td><strong>connectivity_check_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-d099e6ed42cac1be0e693600339935f0954b0ee310f46436ccb1fcc9b7264c46">+7/-7</a> </td> </tr> <tr> <td><strong>directory_diagnostics.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-c063651ad364c31d1c991853bd8e83c7c7dcde73b16f929e878721e11d1df8a5">+12/-6</a> </td> </tr> <tr> <td><strong>directory_diagnostics_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-cf8506645d2be31c6eb3d3f8ad413863197d6b1793829408240eced9782744cb">+17/-11</a> </td> </tr> <tr> <td><strong>folder_handler.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-85f453996002ebf21ce07aa3db62fcd3d18a1861f517804721583decb421a90d">+103/-199</a></td> </tr> <tr> <td><strong>folder_handler_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-657ad7fa06e97a16b4e8eb7cddc64cd5e53343e6513ef874563e83ffd694416b">+181/-344</a></td> </tr> <tr> <td><strong>generate_issue_description.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-20aaf0cdc220b7a600d894877440f647f93c34bbc4e4f023e33422b4f2e77ccf">+17/-15</a> </td> </tr> <tr> <td><strong>get_active_user_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-c5b8060a5d409b868500ca069b1916a9a35584924e1e61be677148d12a44064d">+18/-16</a> </td> </tr> <tr> <td><strong>get_feature_flag_status_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-29de3939a8441248dd44aa2f2eb09f23a57909cf126d85840d0a1519580e25f3">+11/-6</a> </td> </tr> <tr> <td><strong>get_tree_view_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-443abf9c8f12ff271e64a1eed61e5ddc426eca805d6d5be05cffab1a0e176166">+6/-6</a> </td> </tr> <tr> <td><strong>ignores_integration_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-79ebbec5beb7c426863a36683370db366beb6d364890c7e7155c4a8936527277">+25/-31</a> </td> </tr> <tr> <td><strong>ignores_request.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-06098845100f93443eaa531928a39a803862d1031ccea9ecccc83b8658a58b52">+61/-51</a> </td> </tr> <tr> <td><strong>ignores_request_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-592f7e7bcfc9e8bde581d845d4ae40f18b2f5c70aeb9d9f9481c92439c3f0e74">+35/-46</a> </td> </tr> <tr> <td><strong>ldx_sync_service.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-96079acff1e919961c5c93b80089075c62ed6b1f044fcf18fa0021dcf86fd30b">+149/-119</a></td> </tr> <tr> <td><strong>ldx_sync_service_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-aca1dc6a8fa97db48aa613e9fe3d8e075ff47071ab18848a1369c1217bf8863e">+438/-208</a></td> </tr> <tr> <td><strong>login.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-21b93267e7967e84d95e104e3b17f130b4b47b4a37ac176c5bba7dd4c35cee31">+10/-6</a> </td> </tr> <tr> <td><strong>logout.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-22e316f4a260c54802f276ff85692d92b3c1aba01d3dfd7d73f313c567fb5aab">+5/-3</a> </td> </tr> <tr> <td><strong>logout_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-e3d549e46654c52dd2506b8941fc83546f129917fb37045d8e1bb4c8eb4d1c91">+12/-8</a> </td> </tr> <tr> <td><strong>ldx_sync_service_mock.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-3c7bf69a15757a5bbe63c8758c6596bee98f8b3d7d7b6a9664fc7502ee05f7ed">+6/-5</a> </td> </tr> <tr> <td><strong>navigate_to_range_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-1cde128b3e8be4bc13c1ae238266409330a5bcef310bee386343660b932056b8">+12/-12</a> </td> </tr> <tr> <td><strong>report_analytics.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-62743cd3ce4566b6111230201a62f4d1fea670f1fd60c593265bde16353d8585">+10/-6</a> </td> </tr> <tr> <td><strong>report_analytics_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-20363c3fc3625f9eba95434b9eac8946f2aad36f0ca47165d56a412a78da766a">+29/-29</a> </td> </tr> <tr> <td><strong>sast_enabled.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-66bf16df3a0885d711b00264fad42f006b0346950036f8b48dc7ae6478dc6df4">+1/-1</a> </td> </tr> <tr> <td><strong>toggle_tree_filter_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-b68b006463ac72f1f5bd3786814cdcd0b103cac0245c1f3fdc0efbd0266c6b63">+25/-24</a> </td> </tr> <tr> <td><strong>update_folder_config.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-a05ebb1468908293f4d3cfe250b1748079a0b150076d62ba0df69156131ea8c7">+28/-17</a> </td> </tr> <tr> <td><strong>update_folder_config_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-ded99b4b398d892e849ca2c76051626e1fec77a62c3b6d777e532cf09653f049">+37/-33</a> </td> </tr> <tr> <td><strong>workspace_folder_scan.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-6cb7a834c5afbe811a8145bd6d12892ca661ae68a983846d9f8077beabd6e757">+10/-6</a> </td> </tr> <tr> <td><strong>workspace_scan.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-7815afb61f4ee4d064ffc994582c7306f6ba37d1014c9db5d44ee109dbdc6682">+5/-3</a> </td> </tr> <tr> <td><strong>initializer.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-549679250b6998b23ae6754c4684adc9c107201a71db89c46e227fcc2b6c3a9d">+5/-3</a> </td> </tr> <tr> <td><strong>tree_builder.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-13a01423d69e8880eef4d1b357edd592e256151c20fe2e708922ec9f369d258e">+7/-3</a> </td> </tr> <tr> <td><strong>tree_html.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-47c0b3a66e860f79eb96752b6c2d226c640efecc95340807757164ca16cf1f85">+8/-7</a> </td> </tr> <tr> <td><strong>tree_html_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-a05e59c0bf8dd60c31ffcd4d99f9c41b4e14113da3327539b810a53f4f99c00d">+56/-56</a> </td> </tr> <tr> <td><strong>tree_node_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-914f0695e35f0166328ffb0e48acb6527945999894fce96f1292a00f29fabc64">+1/-1</a> </td> </tr> <tr> <td><strong>tree_scan_emitter.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-c86a1bb522364b4f14326b1a2915a724532c1d4f10d39ed3a51ca4a648715456">+13/-8</a> </td> </tr> <tr> <td><strong>tree_scan_emitter_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-406a88e6d83f47e407cb1bd5a26bd0967e5866aa11529d659a54afd480d356f0">+23/-19</a> </td> </tr> <tr> <td><strong>folder_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-bb03ee76aabde8be49355b245229584151a114e49ec791407738dcaeb631acaf">+138/-162</a></td> </tr> <tr> <td><strong>workspace_trust_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-ac8e52aa69f3b6fb4fd604078ddeef7df2cfe4814df32258a855a7e2254d9af1">+4/-2</a> </td> </tr> <tr> <td><strong>scan_state_aggregator_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-dacb10414b37097b40d2960a603ee1a3ec84ee95c6d096386bceb1c5aa893b42">+65/-51</a> </td> </tr> <tr> <td><strong>summary_html_external_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-f9eb5dd6ed9a5462d93db22db8c10746d506824a6f2927b6ef59869a6f53b592">+15/-7</a> </td> </tr> <tr> <td><strong>issues.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-4bdec45b5c8738e4bf742694b67c57c1102cfa09083bd638e325bfa650f8362a">+0/-3</a> </td> </tr> <tr> <td><strong>git_persistence_provider_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-d11404eaca9a26e07e5b47c9a72b80378bfadb2ba02238363b7a445faf6ad955">+80/-79</a> </td> </tr> <tr> <td><strong>base_scan.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-f37e6aa696f750af79d9ed24805074033bcea09bd38405ca384d14e4c63135f4">+43/-20</a> </td> </tr> <tr> <td><strong>base_scan_test.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-29ac0a06174ae18093e1504044ee8b0ef1574ab063ca1a5c9210e28cba07b6ea">+47/-39</a> </td> </tr> <tr> <td><strong>pre_scan_command.go</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-eb3aa1acbc37cb13f25f84053ef3e81f4f48f558536a7d5e99a3fe429e4878f8">+10/-4</a> </td> </tr> <tr> <td><strong>Additional files not shown</strong></td> <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-2f328e4cd8dbe3ad193e49d92bcf045f47a6b72b1e9487d366f6b8288589b4ca"></a></td> </tr> </table></details></td></tr></tr></tbody></table> </details> ___