@build-astron-co/nimbus 0.4.1 → 0.4.3
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/CHANGELOG.md +268 -89
- package/README.md +26 -567
- package/dist/src/agent/compaction-agent.js +24 -12
- package/dist/src/agent/context-manager.js +2 -1
- package/dist/src/agent/expand-files.js +2 -1
- package/dist/src/agent/loop.js +71 -33
- package/dist/src/agent/permissions.js +4 -2
- package/dist/src/agent/system-prompt.js +34 -17
- package/dist/src/app.js +1 -1
- package/dist/src/auth/keychain.js +8 -4
- package/dist/src/auth/store.js +70 -107
- package/dist/src/cli/init.js +35 -19
- package/dist/src/cli/run.js +18 -10
- package/dist/src/cli/serve.js +4 -2
- package/dist/src/cli.js +52 -11
- package/dist/src/commands/alias.js +5 -3
- package/dist/src/commands/audit/index.js +2 -1
- package/dist/src/commands/aws-terraform.js +36 -18
- package/dist/src/commands/completions.js +1 -1
- package/dist/src/commands/config.js +3 -2
- package/dist/src/commands/connect-github.js +92 -0
- package/dist/src/commands/cost/index.js +3 -2
- package/dist/src/commands/deploy.js +15 -10
- package/dist/src/commands/doctor.js +9 -6
- package/dist/src/commands/drift/index.js +2 -1
- package/dist/src/commands/export.js +5 -3
- package/dist/src/commands/generate-terraform.js +110 -2
- package/dist/src/commands/import.js +3 -3
- package/dist/src/commands/incident.js +10 -5
- package/dist/src/commands/login.js +8 -93
- package/dist/src/commands/logs.js +16 -8
- package/dist/src/commands/onboarding.js +6 -4
- package/dist/src/commands/pipeline.js +6 -3
- package/dist/src/commands/plugin.js +3 -2
- package/dist/src/commands/profile.js +27 -14
- package/dist/src/commands/questionnaire.js +1 -1
- package/dist/src/commands/rollback.js +3 -2
- package/dist/src/commands/rollout.js +5 -3
- package/dist/src/commands/runbook.js +17 -10
- package/dist/src/commands/schedule.js +10 -5
- package/dist/src/commands/status.js +2 -1
- package/dist/src/commands/team-context.js +12 -7
- package/dist/src/commands/template.js +1 -1
- package/dist/src/commands/tf/index.js +6 -3
- package/dist/src/commands/upgrade.js +5 -3
- package/dist/src/commands/version.js +6 -3
- package/dist/src/commands/watch.js +6 -3
- package/dist/src/compat/sqlite.js +5 -3
- package/dist/src/config/mode-store.js +2 -1
- package/dist/src/config/profiles.js +4 -2
- package/dist/src/config/types.js +2 -1
- package/dist/src/engine/executor.js +8 -4
- package/dist/src/engine/planner.js +9 -5
- package/dist/src/llm/providers/anthropic.js +6 -3
- package/dist/src/llm/providers/ollama.js +1 -1
- package/dist/src/llm/router.js +22 -7
- package/dist/src/nimbus.js +1 -0
- package/dist/src/sessions/manager.js +6 -3
- package/dist/src/sharing/viewer.js +2 -1
- package/dist/src/tools/file-ops.js +1 -2
- package/dist/src/tools/schemas/devops.js +197 -108
- package/dist/src/tools/schemas/standard.js +1 -1
- package/dist/src/ui/App.js +25 -13
- package/dist/src/ui/FileDiffModal.js +22 -11
- package/dist/src/ui/HelpModal.js +2 -1
- package/dist/src/ui/InputBox.js +6 -3
- package/dist/src/ui/MessageList.js +40 -20
- package/dist/src/ui/TerminalPane.js +2 -1
- package/dist/src/ui/ToolCallDisplay.js +12 -6
- package/dist/src/ui/TreePane.js +2 -1
- package/dist/src/ui/ink/index.js +37 -21
- package/dist/src/version.js +1 -1
- package/dist/src/watcher/index.js +8 -4
- package/package.json +3 -5
- package/src/__tests__/alias.test.ts +0 -133
- package/src/__tests__/app.test.ts +0 -76
- package/src/__tests__/audit.test.ts +0 -877
- package/src/__tests__/circuit-breaker.test.ts +0 -116
- package/src/__tests__/cli-run.test.ts +0 -351
- package/src/__tests__/compat-sqlite.test.ts +0 -68
- package/src/__tests__/context-manager.test.ts +0 -632
- package/src/__tests__/context.test.ts +0 -242
- package/src/__tests__/devops-terminal-gaps.test.ts +0 -718
- package/src/__tests__/doctor.test.ts +0 -48
- package/src/__tests__/enterprise.test.ts +0 -401
- package/src/__tests__/export.test.ts +0 -236
- package/src/__tests__/gap-11-18-20.test.ts +0 -958
- package/src/__tests__/generator.test.ts +0 -433
- package/src/__tests__/helm-streaming.test.ts +0 -127
- package/src/__tests__/hooks.test.ts +0 -582
- package/src/__tests__/incident.test.ts +0 -179
- package/src/__tests__/init.test.ts +0 -487
- package/src/__tests__/intent-parser.test.ts +0 -229
- package/src/__tests__/llm-router.test.ts +0 -209
- package/src/__tests__/logs.test.ts +0 -107
- package/src/__tests__/loop-errors.test.ts +0 -244
- package/src/__tests__/lsp.test.ts +0 -293
- package/src/__tests__/modes.test.ts +0 -336
- package/src/__tests__/perf-optimizations.test.ts +0 -847
- package/src/__tests__/permissions.test.ts +0 -338
- package/src/__tests__/pipeline.test.ts +0 -50
- package/src/__tests__/polish-phase3.test.ts +0 -340
- package/src/__tests__/profile.test.ts +0 -237
- package/src/__tests__/rollback.test.ts +0 -83
- package/src/__tests__/runbook.test.ts +0 -219
- package/src/__tests__/schedule.test.ts +0 -206
- package/src/__tests__/serve.test.ts +0 -275
- package/src/__tests__/sessions.test.ts +0 -322
- package/src/__tests__/sharing.test.ts +0 -340
- package/src/__tests__/snapshots.test.ts +0 -581
- package/src/__tests__/standalone-migration.test.ts +0 -199
- package/src/__tests__/state-db.test.ts +0 -334
- package/src/__tests__/status.test.ts +0 -158
- package/src/__tests__/stream-with-tools.test.ts +0 -778
- package/src/__tests__/subagents.test.ts +0 -176
- package/src/__tests__/system-prompt.test.ts +0 -248
- package/src/__tests__/terminal-gap-v2.test.ts +0 -395
- package/src/__tests__/terminal-parity.test.ts +0 -393
- package/src/__tests__/tf-apply.test.ts +0 -187
- package/src/__tests__/tool-converter.test.ts +0 -256
- package/src/__tests__/tool-schemas.test.ts +0 -602
- package/src/__tests__/tools.test.ts +0 -144
- package/src/__tests__/version-json.test.ts +0 -184
- package/src/__tests__/version.test.ts +0 -49
- package/src/__tests__/watch.test.ts +0 -129
- package/src/agent/compaction-agent.ts +0 -266
- package/src/agent/context-manager.ts +0 -499
- package/src/agent/context.ts +0 -427
- package/src/agent/deploy-preview.ts +0 -487
- package/src/agent/expand-files.ts +0 -108
- package/src/agent/index.ts +0 -68
- package/src/agent/loop.ts +0 -1998
- package/src/agent/modes.ts +0 -429
- package/src/agent/permissions.ts +0 -513
- package/src/agent/subagents/base.ts +0 -116
- package/src/agent/subagents/cost.ts +0 -51
- package/src/agent/subagents/explore.ts +0 -42
- package/src/agent/subagents/general.ts +0 -54
- package/src/agent/subagents/index.ts +0 -102
- package/src/agent/subagents/infra.ts +0 -59
- package/src/agent/subagents/security.ts +0 -69
- package/src/agent/system-prompt.ts +0 -990
- package/src/app.ts +0 -180
- package/src/audit/activity-log.ts +0 -290
- package/src/audit/compliance-checker.ts +0 -540
- package/src/audit/cost-tracker.ts +0 -318
- package/src/audit/index.ts +0 -23
- package/src/audit/security-scanner.ts +0 -641
- package/src/auth/guard.ts +0 -75
- package/src/auth/index.ts +0 -56
- package/src/auth/keychain.ts +0 -82
- package/src/auth/oauth.ts +0 -465
- package/src/auth/providers.ts +0 -470
- package/src/auth/sso.ts +0 -113
- package/src/auth/store.ts +0 -505
- package/src/auth/types.ts +0 -187
- package/src/build.ts +0 -141
- package/src/cli/index.ts +0 -16
- package/src/cli/init.ts +0 -1227
- package/src/cli/openapi-spec.ts +0 -356
- package/src/cli/run.ts +0 -628
- package/src/cli/serve-auth.ts +0 -80
- package/src/cli/serve.ts +0 -539
- package/src/cli/web.ts +0 -71
- package/src/cli.ts +0 -1728
- package/src/clients/core-engine-client.ts +0 -227
- package/src/clients/enterprise-client.ts +0 -334
- package/src/clients/generator-client.ts +0 -351
- package/src/clients/git-client.ts +0 -627
- package/src/clients/github-client.ts +0 -410
- package/src/clients/helm-client.ts +0 -504
- package/src/clients/index.ts +0 -80
- package/src/clients/k8s-client.ts +0 -497
- package/src/clients/llm-client.ts +0 -161
- package/src/clients/rest-client.ts +0 -130
- package/src/clients/service-discovery.ts +0 -38
- package/src/clients/terraform-client.ts +0 -482
- package/src/clients/tools-client.ts +0 -1843
- package/src/clients/ws-client.ts +0 -115
- package/src/commands/alias.ts +0 -100
- package/src/commands/analyze/index.ts +0 -352
- package/src/commands/apply/helm.ts +0 -473
- package/src/commands/apply/index.ts +0 -213
- package/src/commands/apply/k8s.ts +0 -454
- package/src/commands/apply/terraform.ts +0 -582
- package/src/commands/ask.ts +0 -167
- package/src/commands/audit/index.ts +0 -357
- package/src/commands/auth-cloud.ts +0 -407
- package/src/commands/auth-list.ts +0 -134
- package/src/commands/auth-profile.ts +0 -121
- package/src/commands/auth-refresh.ts +0 -187
- package/src/commands/auth-status.ts +0 -141
- package/src/commands/aws/ec2.ts +0 -501
- package/src/commands/aws/iam.ts +0 -397
- package/src/commands/aws/index.ts +0 -133
- package/src/commands/aws/lambda.ts +0 -396
- package/src/commands/aws/rds.ts +0 -439
- package/src/commands/aws/s3.ts +0 -439
- package/src/commands/aws/vpc.ts +0 -393
- package/src/commands/aws-discover.ts +0 -542
- package/src/commands/aws-terraform.ts +0 -755
- package/src/commands/azure/aks.ts +0 -376
- package/src/commands/azure/functions.ts +0 -253
- package/src/commands/azure/index.ts +0 -116
- package/src/commands/azure/storage.ts +0 -478
- package/src/commands/azure/vm.ts +0 -355
- package/src/commands/billing/index.ts +0 -256
- package/src/commands/chat.ts +0 -320
- package/src/commands/completions.ts +0 -268
- package/src/commands/config.ts +0 -372
- package/src/commands/cost/cloud-cost-estimator.ts +0 -266
- package/src/commands/cost/estimator.ts +0 -79
- package/src/commands/cost/index.ts +0 -810
- package/src/commands/cost/parsers/terraform.ts +0 -273
- package/src/commands/cost/parsers/types.ts +0 -25
- package/src/commands/cost/pricing/aws.ts +0 -544
- package/src/commands/cost/pricing/azure.ts +0 -499
- package/src/commands/cost/pricing/gcp.ts +0 -396
- package/src/commands/cost/pricing/index.ts +0 -40
- package/src/commands/demo.ts +0 -250
- package/src/commands/deploy.ts +0 -260
- package/src/commands/doctor.ts +0 -1386
- package/src/commands/drift/index.ts +0 -787
- package/src/commands/explain.ts +0 -277
- package/src/commands/export.ts +0 -146
- package/src/commands/feedback.ts +0 -389
- package/src/commands/fix.ts +0 -324
- package/src/commands/fs/index.ts +0 -402
- package/src/commands/gcp/compute.ts +0 -325
- package/src/commands/gcp/functions.ts +0 -271
- package/src/commands/gcp/gke.ts +0 -438
- package/src/commands/gcp/iam.ts +0 -344
- package/src/commands/gcp/index.ts +0 -129
- package/src/commands/gcp/storage.ts +0 -284
- package/src/commands/generate-helm.ts +0 -1249
- package/src/commands/generate-k8s.ts +0 -1508
- package/src/commands/generate-terraform.ts +0 -1202
- package/src/commands/gh/index.ts +0 -863
- package/src/commands/git/index.ts +0 -1343
- package/src/commands/helm/index.ts +0 -1126
- package/src/commands/help.ts +0 -715
- package/src/commands/history.ts +0 -149
- package/src/commands/import.ts +0 -868
- package/src/commands/incident.ts +0 -166
- package/src/commands/index.ts +0 -367
- package/src/commands/init.ts +0 -1051
- package/src/commands/k8s/index.ts +0 -1137
- package/src/commands/login.ts +0 -716
- package/src/commands/logout.ts +0 -83
- package/src/commands/logs.ts +0 -167
- package/src/commands/onboarding.ts +0 -405
- package/src/commands/pipeline.ts +0 -186
- package/src/commands/plan/display.ts +0 -279
- package/src/commands/plan/index.ts +0 -599
- package/src/commands/plugin.ts +0 -398
- package/src/commands/preview.ts +0 -452
- package/src/commands/profile.ts +0 -342
- package/src/commands/questionnaire.ts +0 -1172
- package/src/commands/resume.ts +0 -47
- package/src/commands/rollback.ts +0 -315
- package/src/commands/rollout.ts +0 -88
- package/src/commands/runbook.ts +0 -346
- package/src/commands/schedule.ts +0 -236
- package/src/commands/status.ts +0 -252
- package/src/commands/team/index.ts +0 -346
- package/src/commands/team-context.ts +0 -220
- package/src/commands/template.ts +0 -233
- package/src/commands/tf/index.ts +0 -1093
- package/src/commands/upgrade.ts +0 -607
- package/src/commands/usage/index.ts +0 -134
- package/src/commands/version.ts +0 -174
- package/src/commands/watch.ts +0 -153
- package/src/compat/index.ts +0 -2
- package/src/compat/runtime.ts +0 -12
- package/src/compat/sqlite.ts +0 -177
- package/src/config/index.ts +0 -17
- package/src/config/manager.ts +0 -530
- package/src/config/mode-store.ts +0 -62
- package/src/config/profiles.ts +0 -84
- package/src/config/safety-policy.ts +0 -358
- package/src/config/schema.ts +0 -125
- package/src/config/types.ts +0 -609
- package/src/config/workspace-state.ts +0 -53
- package/src/context/context-db.ts +0 -199
- package/src/demo/index.ts +0 -349
- package/src/demo/scenarios/full-journey.ts +0 -229
- package/src/demo/scenarios/getting-started.ts +0 -127
- package/src/demo/scenarios/helm-release.ts +0 -341
- package/src/demo/scenarios/k8s-deployment.ts +0 -194
- package/src/demo/scenarios/terraform-vpc.ts +0 -170
- package/src/demo/types.ts +0 -92
- package/src/engine/cost-estimator.ts +0 -480
- package/src/engine/diagram-generator.ts +0 -256
- package/src/engine/drift-detector.ts +0 -902
- package/src/engine/executor.ts +0 -1066
- package/src/engine/index.ts +0 -76
- package/src/engine/orchestrator.ts +0 -636
- package/src/engine/planner.ts +0 -787
- package/src/engine/safety.ts +0 -743
- package/src/engine/verifier.ts +0 -770
- package/src/enterprise/audit.ts +0 -348
- package/src/enterprise/auth.ts +0 -270
- package/src/enterprise/billing.ts +0 -822
- package/src/enterprise/index.ts +0 -17
- package/src/enterprise/teams.ts +0 -443
- package/src/generator/best-practices.ts +0 -1608
- package/src/generator/helm.ts +0 -630
- package/src/generator/index.ts +0 -37
- package/src/generator/intent-parser.ts +0 -514
- package/src/generator/kubernetes.ts +0 -976
- package/src/generator/terraform.ts +0 -1875
- package/src/history/index.ts +0 -8
- package/src/history/manager.ts +0 -250
- package/src/history/types.ts +0 -34
- package/src/hooks/config.ts +0 -432
- package/src/hooks/engine.ts +0 -392
- package/src/hooks/index.ts +0 -4
- package/src/llm/auth-bridge.ts +0 -198
- package/src/llm/circuit-breaker.ts +0 -140
- package/src/llm/config-loader.ts +0 -201
- package/src/llm/cost-calculator.ts +0 -171
- package/src/llm/index.ts +0 -8
- package/src/llm/model-aliases.ts +0 -115
- package/src/llm/provider-registry.ts +0 -63
- package/src/llm/providers/anthropic.ts +0 -462
- package/src/llm/providers/bedrock.ts +0 -477
- package/src/llm/providers/google.ts +0 -405
- package/src/llm/providers/ollama.ts +0 -767
- package/src/llm/providers/openai-compatible.ts +0 -340
- package/src/llm/providers/openai.ts +0 -328
- package/src/llm/providers/openrouter.ts +0 -338
- package/src/llm/router.ts +0 -1104
- package/src/llm/types.ts +0 -232
- package/src/lsp/client.ts +0 -298
- package/src/lsp/languages.ts +0 -119
- package/src/lsp/manager.ts +0 -294
- package/src/mcp/client.ts +0 -402
- package/src/mcp/index.ts +0 -5
- package/src/mcp/manager.ts +0 -133
- package/src/nimbus.ts +0 -233
- package/src/plugins/index.ts +0 -27
- package/src/plugins/loader.ts +0 -334
- package/src/plugins/manager.ts +0 -376
- package/src/plugins/types.ts +0 -284
- package/src/scanners/cicd-scanner.ts +0 -258
- package/src/scanners/cloud-scanner.ts +0 -466
- package/src/scanners/framework-scanner.ts +0 -469
- package/src/scanners/iac-scanner.ts +0 -388
- package/src/scanners/index.ts +0 -539
- package/src/scanners/language-scanner.ts +0 -276
- package/src/scanners/package-manager-scanner.ts +0 -277
- package/src/scanners/types.ts +0 -172
- package/src/sessions/manager.ts +0 -472
- package/src/sessions/types.ts +0 -44
- package/src/sharing/sync.ts +0 -300
- package/src/sharing/viewer.ts +0 -163
- package/src/snapshots/index.ts +0 -2
- package/src/snapshots/manager.ts +0 -530
- package/src/state/artifacts.ts +0 -147
- package/src/state/audit.ts +0 -137
- package/src/state/billing.ts +0 -240
- package/src/state/checkpoints.ts +0 -117
- package/src/state/config.ts +0 -67
- package/src/state/conversations.ts +0 -14
- package/src/state/credentials.ts +0 -154
- package/src/state/db.ts +0 -58
- package/src/state/index.ts +0 -26
- package/src/state/messages.ts +0 -115
- package/src/state/projects.ts +0 -123
- package/src/state/schema.ts +0 -236
- package/src/state/sessions.ts +0 -147
- package/src/state/teams.ts +0 -200
- package/src/telemetry.ts +0 -108
- package/src/tools/aws-ops.ts +0 -952
- package/src/tools/azure-ops.ts +0 -579
- package/src/tools/file-ops.ts +0 -615
- package/src/tools/gcp-ops.ts +0 -625
- package/src/tools/git-ops.ts +0 -773
- package/src/tools/github-ops.ts +0 -799
- package/src/tools/helm-ops.ts +0 -943
- package/src/tools/index.ts +0 -17
- package/src/tools/k8s-ops.ts +0 -819
- package/src/tools/schemas/converter.ts +0 -184
- package/src/tools/schemas/devops.ts +0 -3502
- package/src/tools/schemas/index.ts +0 -73
- package/src/tools/schemas/standard.ts +0 -1148
- package/src/tools/schemas/types.ts +0 -735
- package/src/tools/spawn-exec.ts +0 -148
- package/src/tools/terraform-ops.ts +0 -862
- package/src/types/ambient.d.ts +0 -193
- package/src/types/config.ts +0 -83
- package/src/types/drift.ts +0 -116
- package/src/types/enterprise.ts +0 -335
- package/src/types/index.ts +0 -20
- package/src/types/plan.ts +0 -44
- package/src/types/request.ts +0 -65
- package/src/types/response.ts +0 -54
- package/src/types/service.ts +0 -51
- package/src/ui/App.tsx +0 -2114
- package/src/ui/DeployPreview.tsx +0 -174
- package/src/ui/FileDiffModal.tsx +0 -162
- package/src/ui/Header.tsx +0 -131
- package/src/ui/HelpModal.tsx +0 -57
- package/src/ui/InputBox.tsx +0 -503
- package/src/ui/MessageList.tsx +0 -1032
- package/src/ui/PermissionPrompt.tsx +0 -163
- package/src/ui/StatusBar.tsx +0 -277
- package/src/ui/TerminalPane.tsx +0 -84
- package/src/ui/ToolCallDisplay.tsx +0 -643
- package/src/ui/TreePane.tsx +0 -132
- package/src/ui/chat-ui.ts +0 -850
- package/src/ui/index.ts +0 -33
- package/src/ui/ink/index.ts +0 -1444
- package/src/ui/streaming.ts +0 -176
- package/src/ui/theme.ts +0 -104
- package/src/ui/types.ts +0 -75
- package/src/utils/analytics.ts +0 -72
- package/src/utils/cost-warning.ts +0 -27
- package/src/utils/env.ts +0 -46
- package/src/utils/errors.ts +0 -69
- package/src/utils/event-bus.ts +0 -38
- package/src/utils/index.ts +0 -24
- package/src/utils/logger.ts +0 -171
- package/src/utils/rate-limiter.ts +0 -121
- package/src/utils/service-auth.ts +0 -49
- package/src/utils/validation.ts +0 -53
- package/src/version.ts +0 -4
- package/src/watcher/index.ts +0 -214
- package/src/wizard/approval.ts +0 -383
- package/src/wizard/index.ts +0 -25
- package/src/wizard/prompts.ts +0 -338
- package/src/wizard/types.ts +0 -172
- package/src/wizard/ui.ts +0 -556
- package/src/wizard/wizard.ts +0 -304
- package/tsconfig.json +0 -24
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent Loop Error Classification Tests — G3 + G24
|
|
3
|
-
*
|
|
4
|
-
* The `classifyDevOpsError` function is not exported from loop.ts, so we test:
|
|
5
|
-
* 1. The install-hint patterns by parsing the source directly and validating
|
|
6
|
-
* the INSTALL_HINTS mapping is present for expected CLI tools.
|
|
7
|
-
* 2. Network error handling (G24) — ECONNREFUSED + _nimbusNetworkError sentinel.
|
|
8
|
-
*
|
|
9
|
-
* For install-hint logic we also inline a minimal reproduction so we can
|
|
10
|
-
* verify the matching logic behaves correctly without coupling to internals.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { describe, it, expect } from 'vitest';
|
|
14
|
-
import { readFileSync } from 'node:fs';
|
|
15
|
-
import { join } from 'node:path';
|
|
16
|
-
|
|
17
|
-
const LOOP_SRC = readFileSync(join(__dirname, '..', 'agent', 'loop.ts'), 'utf-8');
|
|
18
|
-
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
// Inline reproduction of classifyDevOpsError install-hint logic (G3)
|
|
21
|
-
// This mirrors exactly what the source implements so we can unit-test it.
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
const INSTALL_HINTS: Record<string, string> = {
|
|
25
|
-
terraform: 'brew install terraform OR https://developer.hashicorp.com/terraform/install',
|
|
26
|
-
kubectl: 'brew install kubectl OR https://kubernetes.io/docs/tasks/tools/',
|
|
27
|
-
helm: 'brew install helm OR https://helm.sh/docs/intro/install/',
|
|
28
|
-
docker: 'brew install --cask docker OR https://docs.docker.com/get-docker/',
|
|
29
|
-
aws: 'brew install awscli OR pip install awscli',
|
|
30
|
-
gcloud: 'brew install --cask google-cloud-sdk',
|
|
31
|
-
az: 'brew install azure-cli',
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
function classifyInstallHint(toolName: string, errorOutput: string): string | null {
|
|
35
|
-
const e = errorOutput.toLowerCase();
|
|
36
|
-
if (/command not found|not found|no such file or directory/i.test(errorOutput)) {
|
|
37
|
-
for (const [cmd, hint] of Object.entries(INSTALL_HINTS)) {
|
|
38
|
-
if (toolName.includes(cmd) || e.includes(`'${cmd}'`) || e.includes(`"${cmd}"`)) {
|
|
39
|
-
return `\`${cmd}\` is not installed.\n\nInstall: ${hint}`;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
// G3: install-hint tests
|
|
48
|
-
// ---------------------------------------------------------------------------
|
|
49
|
-
|
|
50
|
-
describe('classifyDevOpsError install hints (G3)', () => {
|
|
51
|
-
it('returns brew install terraform for "terraform: command not found"', () => {
|
|
52
|
-
const result = classifyInstallHint('terraform', 'terraform: command not found');
|
|
53
|
-
expect(result).not.toBeNull();
|
|
54
|
-
expect(result).toContain('brew install terraform');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('returns brew install kubectl for kubectl not found', () => {
|
|
58
|
-
const result = classifyInstallHint('kubectl', 'kubectl: No such file or directory');
|
|
59
|
-
expect(result).not.toBeNull();
|
|
60
|
-
expect(result).toContain('brew install kubectl');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('returns brew install helm for helm not found', () => {
|
|
64
|
-
const result = classifyInstallHint('helm', 'helm not found');
|
|
65
|
-
expect(result).not.toBeNull();
|
|
66
|
-
expect(result).toContain('brew install helm');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('returns brew install docker for docker command not found', () => {
|
|
70
|
-
const result = classifyInstallHint('docker', 'docker: command not found');
|
|
71
|
-
expect(result).not.toBeNull();
|
|
72
|
-
expect(result).toContain('brew install');
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('returns null for a normal error that is not a "not found" error', () => {
|
|
76
|
-
const result = classifyInstallHint('kubectl', 'context deadline exceeded');
|
|
77
|
-
expect(result).toBeNull();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('returns null for an unrecognised tool with a random error', () => {
|
|
81
|
-
const result = classifyInstallHint('myapp', 'connection refused');
|
|
82
|
-
expect(result).toBeNull();
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// ---------------------------------------------------------------------------
|
|
87
|
-
// G24: network error sentinel in loop.ts source
|
|
88
|
-
// ---------------------------------------------------------------------------
|
|
89
|
-
|
|
90
|
-
describe('network error handling exists in loop.ts (G24)', () => {
|
|
91
|
-
it('source contains ECONNREFUSED detection', () => {
|
|
92
|
-
expect(LOOP_SRC).toContain('ECONNREFUSED');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('source contains _nimbusNetworkError sentinel property', () => {
|
|
96
|
-
expect(LOOP_SRC).toContain('_nimbusNetworkError');
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('source detects ETIMEDOUT in network errors', () => {
|
|
100
|
-
expect(LOOP_SRC).toContain('ETIMEDOUT');
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('source detects ENOTFOUND in network errors', () => {
|
|
104
|
-
expect(LOOP_SRC).toContain('ENOTFOUND');
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// ---------------------------------------------------------------------------
|
|
109
|
-
// G3: source-level assertions that INSTALL_HINTS map exists
|
|
110
|
-
// ---------------------------------------------------------------------------
|
|
111
|
-
|
|
112
|
-
describe('INSTALL_HINTS map present in loop.ts source (G3)', () => {
|
|
113
|
-
it('source defines INSTALL_HINTS with terraform entry', () => {
|
|
114
|
-
expect(LOOP_SRC).toContain('brew install terraform');
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('source defines INSTALL_HINTS with kubectl entry', () => {
|
|
118
|
-
expect(LOOP_SRC).toContain('brew install kubectl');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('source defines INSTALL_HINTS with helm entry', () => {
|
|
122
|
-
expect(LOOP_SRC).toContain('brew install helm');
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('source defines INSTALL_HINTS with docker entry', () => {
|
|
126
|
-
expect(LOOP_SRC).toContain('brew install --cask docker');
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
// ---------------------------------------------------------------------------
|
|
131
|
-
// L3: NIMBUS.md custom error hints
|
|
132
|
-
// ---------------------------------------------------------------------------
|
|
133
|
-
|
|
134
|
-
describe('NIMBUS.md custom error hints (L3)', () => {
|
|
135
|
-
// Inline a minimal reproduction of the custom hints parsing logic
|
|
136
|
-
function parseCustomHints(nimbusInstructions: string, errorOutput: string): string | null {
|
|
137
|
-
const hintsMatch = nimbusInstructions.match(/##\s*Custom Error Hints\s*\n([\s\S]*?)(?=\n##|\n$|$)/i);
|
|
138
|
-
if (!hintsMatch) return null;
|
|
139
|
-
const hintsSection = hintsMatch[1];
|
|
140
|
-
const hintLines = hintsSection.split('\n').filter((l: string) => l.trim().startsWith('-'));
|
|
141
|
-
for (const line of hintLines) {
|
|
142
|
-
const colonIdx = line.indexOf(':');
|
|
143
|
-
if (colonIdx > 0) {
|
|
144
|
-
const pattern = line.slice(1, colonIdx).trim();
|
|
145
|
-
const hint = line.slice(colonIdx + 1).trim();
|
|
146
|
-
if (pattern && hint && errorOutput.toLowerCase().includes(pattern.toLowerCase())) {
|
|
147
|
-
return `HINT: ${hint}`;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
it('returns custom hint when pattern matches error output', () => {
|
|
155
|
-
const instructions = `## Custom Error Hints\n- registry.internal: Run docker login registry.internal first\n`;
|
|
156
|
-
const result = parseCustomHints(instructions, 'Error: registry.internal: connection refused');
|
|
157
|
-
expect(result).toBe('HINT: Run docker login registry.internal first');
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('returns null when no matching pattern', () => {
|
|
161
|
-
const instructions = `## Custom Error Hints\n- registry.internal: Run docker login registry.internal first\n`;
|
|
162
|
-
const result = parseCustomHints(instructions, 'Error: some unrelated error');
|
|
163
|
-
expect(result).toBeNull();
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('returns null when no Custom Error Hints section', () => {
|
|
167
|
-
const instructions = `## Other Section\n- some content\n`;
|
|
168
|
-
const result = parseCustomHints(instructions, 'Error: registry.internal: connection refused');
|
|
169
|
-
expect(result).toBeNull();
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('pattern matching is case-insensitive', () => {
|
|
173
|
-
const instructions = `## Custom Error Hints\n- VPN Required: Connect to corporate VPN\n`;
|
|
174
|
-
const result = parseCustomHints(instructions, 'error: vpn required - connection blocked');
|
|
175
|
-
expect(result).toBe('HINT: Connect to corporate VPN');
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('loop.ts source contains custom error hints parsing', () => {
|
|
179
|
-
const { readFileSync } = require('node:fs');
|
|
180
|
-
const { join } = require('node:path');
|
|
181
|
-
const src = readFileSync(join(process.cwd(), 'src/agent/loop.ts'), 'utf-8');
|
|
182
|
-
expect(src).toContain('Custom Error Hints');
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it('multiple hints in section — first match wins', () => {
|
|
186
|
-
const instructions = [
|
|
187
|
-
'## Custom Error Hints',
|
|
188
|
-
'- alpha: Hint for alpha',
|
|
189
|
-
'- beta: Hint for beta',
|
|
190
|
-
'- gamma: Hint for gamma',
|
|
191
|
-
'',
|
|
192
|
-
].join('\n');
|
|
193
|
-
const result = parseCustomHints(instructions, 'Error: beta failure occurred');
|
|
194
|
-
expect(result).toBe('HINT: Hint for beta');
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it('hint with colon in hint text is preserved', () => {
|
|
198
|
-
const instructions = `## Custom Error Hints\n- timeout: Run: kubectl describe pod <name>\n`;
|
|
199
|
-
const result = parseCustomHints(instructions, 'Error: timeout waiting for pod');
|
|
200
|
-
expect(result).toBe('HINT: Run: kubectl describe pod <name>');
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('returns null when instructions is empty string', () => {
|
|
204
|
-
const result = parseCustomHints('', 'Error: timeout');
|
|
205
|
-
expect(result).toBeNull();
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('loop.ts source passes nimbusInstructions to classifyDevOpsError', () => {
|
|
209
|
-
const { readFileSync } = require('node:fs');
|
|
210
|
-
const { join } = require('node:path');
|
|
211
|
-
const src = readFileSync(join(process.cwd(), 'src/agent/loop.ts'), 'utf-8');
|
|
212
|
-
expect(src).toContain('nimbusInstructions');
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it('source contains READ_ONLY_TOOLS set', () => {
|
|
216
|
-
const { readFileSync } = require('node:fs');
|
|
217
|
-
const { join } = require('node:path');
|
|
218
|
-
const src = readFileSync(join(process.cwd(), 'src/agent/loop.ts'), 'utf-8');
|
|
219
|
-
expect(src).toContain('READ_ONLY_TOOLS');
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// ---------------------------------------------------------------------------
|
|
224
|
-
// C4: TUI error hint propagation — hint appears in result.output (not just LLM context)
|
|
225
|
-
// ---------------------------------------------------------------------------
|
|
226
|
-
|
|
227
|
-
describe('C4 — tool error hints propagated to TUI (result.output)', () => {
|
|
228
|
-
it('loop.ts source augments result.output with the install hint', () => {
|
|
229
|
-
expect(LOOP_SRC).toContain('result.output +=');
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it('loop.ts C4 comment exists to document the change', () => {
|
|
233
|
-
expect(LOOP_SRC).toContain('C4: Also show hint in TUI error output');
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('hint augmentation happens after toolContent augmentation', () => {
|
|
237
|
-
// The toolContent line should appear before result.output in source
|
|
238
|
-
const toolContentIdx = LOOP_SRC.indexOf('toolContent += `\\n\\n${hint}`');
|
|
239
|
-
const resultOutputIdx = LOOP_SRC.indexOf('result.output += `\\n\\n${hint}`');
|
|
240
|
-
expect(toolContentIdx).toBeGreaterThanOrEqual(0);
|
|
241
|
-
expect(resultOutputIdx).toBeGreaterThanOrEqual(0);
|
|
242
|
-
expect(toolContentIdx).toBeLessThan(resultOutputIdx);
|
|
243
|
-
});
|
|
244
|
-
});
|
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for LSP Manager, Client, Language configs, and Agent Loop integration.
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
5
|
-
import { getLanguageForFile, getLanguagePriority, LANGUAGE_CONFIGS } from '../lsp/languages';
|
|
6
|
-
import { LSPManager, resetLSPManager } from '../lsp/manager';
|
|
7
|
-
import { severityLabel } from '../lsp/client';
|
|
8
|
-
|
|
9
|
-
describe('Language Configs', () => {
|
|
10
|
-
describe('getLanguageForFile', () => {
|
|
11
|
-
it('should match TypeScript files', () => {
|
|
12
|
-
expect(getLanguageForFile('src/index.ts')?.id).toBe('typescript');
|
|
13
|
-
expect(getLanguageForFile('component.tsx')?.id).toBe('typescript');
|
|
14
|
-
expect(getLanguageForFile('index.js')?.id).toBe('typescript');
|
|
15
|
-
expect(getLanguageForFile('config.mjs')?.id).toBe('typescript');
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('should match Go files', () => {
|
|
19
|
-
expect(getLanguageForFile('main.go')?.id).toBe('go');
|
|
20
|
-
expect(getLanguageForFile('pkg/server/handler.go')?.id).toBe('go');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('should match Python files', () => {
|
|
24
|
-
expect(getLanguageForFile('app.py')?.id).toBe('python');
|
|
25
|
-
expect(getLanguageForFile('types.pyi')?.id).toBe('python');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should match Terraform files', () => {
|
|
29
|
-
expect(getLanguageForFile('main.tf')?.id).toBe('terraform');
|
|
30
|
-
expect(getLanguageForFile('variables.tfvars')?.id).toBe('terraform');
|
|
31
|
-
expect(getLanguageForFile('config.hcl')?.id).toBe('terraform');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should match YAML files', () => {
|
|
35
|
-
expect(getLanguageForFile('config.yaml')?.id).toBe('yaml');
|
|
36
|
-
expect(getLanguageForFile('deployment.yml')?.id).toBe('yaml');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should match Dockerfile', () => {
|
|
40
|
-
expect(getLanguageForFile('Dockerfile')?.id).toBe('docker');
|
|
41
|
-
expect(getLanguageForFile('app.dockerfile')?.id).toBe('docker');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should return undefined for unknown extensions', () => {
|
|
45
|
-
expect(getLanguageForFile('README.md')).toBeUndefined();
|
|
46
|
-
expect(getLanguageForFile('Makefile')).toBeUndefined();
|
|
47
|
-
expect(getLanguageForFile('data.csv')).toBeUndefined();
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe('getLanguagePriority', () => {
|
|
52
|
-
it('should return all configs in priority order', () => {
|
|
53
|
-
const priority = getLanguagePriority();
|
|
54
|
-
expect(priority).toHaveLength(LANGUAGE_CONFIGS.length);
|
|
55
|
-
expect(priority[0].id).toBe('typescript');
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe('LANGUAGE_CONFIGS', () => {
|
|
60
|
-
it('should have command and installHint for all configs', () => {
|
|
61
|
-
for (const config of LANGUAGE_CONFIGS) {
|
|
62
|
-
expect(config.command).toBeTruthy();
|
|
63
|
-
expect(config.installHint).toBeTruthy();
|
|
64
|
-
expect(config.extensions.length).toBeGreaterThan(0);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('severityLabel', () => {
|
|
71
|
-
it('should return correct labels', () => {
|
|
72
|
-
expect(severityLabel(1)).toBe('Error');
|
|
73
|
-
expect(severityLabel(2)).toBe('Warning');
|
|
74
|
-
expect(severityLabel(3)).toBe('Info');
|
|
75
|
-
expect(severityLabel(4)).toBe('Hint');
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
describe('LSPManager', () => {
|
|
80
|
-
let manager: LSPManager;
|
|
81
|
-
|
|
82
|
-
beforeEach(() => {
|
|
83
|
-
resetLSPManager();
|
|
84
|
-
manager = new LSPManager('/tmp/test-project');
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
afterEach(async () => {
|
|
88
|
-
await manager.stopAll();
|
|
89
|
-
resetLSPManager();
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
describe('setEnabled', () => {
|
|
93
|
-
it('should disable LSP integration', () => {
|
|
94
|
-
manager.setEnabled(false);
|
|
95
|
-
// touchFile should be a no-op when disabled
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('should re-enable LSP integration', () => {
|
|
99
|
-
manager.setEnabled(false);
|
|
100
|
-
manager.setEnabled(true);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
describe('getDiagnostics', () => {
|
|
105
|
-
it('should return empty array when disabled', async () => {
|
|
106
|
-
manager.setEnabled(false);
|
|
107
|
-
const diags = await manager.getDiagnostics('/tmp/test.ts');
|
|
108
|
-
expect(diags).toEqual([]);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('should return empty array for unsupported file types', async () => {
|
|
112
|
-
const diags = await manager.getDiagnostics('/tmp/file.csv');
|
|
113
|
-
expect(diags).toEqual([]);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('should return empty array when server is not running', async () => {
|
|
117
|
-
const diags = await manager.getDiagnostics('/tmp/test.ts', 100);
|
|
118
|
-
expect(diags).toEqual([]);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe('getErrors', () => {
|
|
123
|
-
it('should return empty array when no errors', async () => {
|
|
124
|
-
const errors = await manager.getErrors('/tmp/test.ts');
|
|
125
|
-
expect(errors).toEqual([]);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe('formatDiagnosticsForAgent', () => {
|
|
130
|
-
it('should return null for empty diagnostics', () => {
|
|
131
|
-
expect(manager.formatDiagnosticsForAgent([])).toBeNull();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should format error diagnostics', () => {
|
|
135
|
-
const diagnostics = [
|
|
136
|
-
{
|
|
137
|
-
file: '/src/server.ts',
|
|
138
|
-
line: 23,
|
|
139
|
-
column: 5,
|
|
140
|
-
severity: 1 as const,
|
|
141
|
-
message: "Property 'origin' does not exist on type 'CorsConfig'",
|
|
142
|
-
source: 'ts',
|
|
143
|
-
},
|
|
144
|
-
];
|
|
145
|
-
const result = manager.formatDiagnosticsForAgent(diagnostics);
|
|
146
|
-
expect(result).toContain('[LSP Diagnostics]');
|
|
147
|
-
expect(result).toContain('Error: /src/server.ts:23:5');
|
|
148
|
-
expect(result).toContain("Property 'origin'");
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('should truncate warnings after 5', () => {
|
|
152
|
-
const diagnostics = Array.from({ length: 10 }, (_, i) => ({
|
|
153
|
-
file: '/src/file.ts',
|
|
154
|
-
line: i + 1,
|
|
155
|
-
column: 1,
|
|
156
|
-
severity: 2 as const,
|
|
157
|
-
message: `Warning ${i + 1}`,
|
|
158
|
-
}));
|
|
159
|
-
const result = manager.formatDiagnosticsForAgent(diagnostics);
|
|
160
|
-
expect(result).toContain('5 more warnings');
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should return null for only info/hint diagnostics', () => {
|
|
164
|
-
const diagnostics = [
|
|
165
|
-
{ file: '/src/file.ts', line: 1, column: 1, severity: 3 as const, message: 'Info' },
|
|
166
|
-
{ file: '/src/file.ts', line: 2, column: 1, severity: 4 as const, message: 'Hint' },
|
|
167
|
-
];
|
|
168
|
-
expect(manager.formatDiagnosticsForAgent(diagnostics)).toBeNull();
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
describe('getStatus', () => {
|
|
173
|
-
it('should return status for all configured languages', async () => {
|
|
174
|
-
const statuses = await manager.getStatus();
|
|
175
|
-
expect(statuses.length).toBe(LANGUAGE_CONFIGS.length);
|
|
176
|
-
for (const status of statuses) {
|
|
177
|
-
expect(status.language).toBeTruthy();
|
|
178
|
-
expect(typeof status.active).toBe('boolean');
|
|
179
|
-
expect(typeof status.available).toBe('boolean');
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
describe('stopAll', () => {
|
|
185
|
-
it('should not throw when no clients running', async () => {
|
|
186
|
-
await expect(manager.stopAll()).resolves.toBeUndefined();
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// ---------------------------------------------------------------------------
|
|
192
|
-
// Agent Loop LSP Integration
|
|
193
|
-
// ---------------------------------------------------------------------------
|
|
194
|
-
|
|
195
|
-
describe('Agent Loop LSP Integration', () => {
|
|
196
|
-
it('should identify file-editing tools correctly', () => {
|
|
197
|
-
// Verify the tools that should trigger LSP diagnostics
|
|
198
|
-
const fileEditingTools = ['edit_file', 'multi_edit', 'write_file'];
|
|
199
|
-
const nonFileEditingTools = ['read_file', 'bash', 'glob', 'grep', 'terraform', 'kubectl'];
|
|
200
|
-
|
|
201
|
-
for (const tool of fileEditingTools) {
|
|
202
|
-
// These tools have a `path` parameter that LSP integration uses
|
|
203
|
-
expect(['edit_file', 'multi_edit', 'write_file']).toContain(tool);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
for (const tool of nonFileEditingTools) {
|
|
207
|
-
expect(['edit_file', 'multi_edit', 'write_file']).not.toContain(tool);
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it('should extract file path from edit_file input', () => {
|
|
212
|
-
const input = { path: '/src/server.ts', old_string: 'foo', new_string: 'bar' };
|
|
213
|
-
expect(input.path).toBe('/src/server.ts');
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it('should extract file path from write_file input', () => {
|
|
217
|
-
const input = { path: '/src/new-file.ts', content: 'console.log("hello")' };
|
|
218
|
-
expect(input.path).toBe('/src/new-file.ts');
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it('should extract file path from multi_edit input', () => {
|
|
222
|
-
const input = { path: '/src/app.ts', edits: [{ old_string: 'a', new_string: 'b' }] };
|
|
223
|
-
expect(input.path).toBe('/src/app.ts');
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it('should format diagnostics for agent conversation injection', () => {
|
|
227
|
-
const manager = new LSPManager('/tmp/test');
|
|
228
|
-
const diagnostics = [
|
|
229
|
-
{
|
|
230
|
-
file: '/src/server.ts',
|
|
231
|
-
line: 23,
|
|
232
|
-
column: 5,
|
|
233
|
-
severity: 1 as const,
|
|
234
|
-
message: "Property 'origin' does not exist on type 'CorsConfig'",
|
|
235
|
-
source: 'ts',
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
file: '/src/server.ts',
|
|
239
|
-
line: 45,
|
|
240
|
-
column: 10,
|
|
241
|
-
severity: 2 as const,
|
|
242
|
-
message: 'Unused variable',
|
|
243
|
-
source: 'ts',
|
|
244
|
-
},
|
|
245
|
-
];
|
|
246
|
-
|
|
247
|
-
const formatted = manager.formatDiagnosticsForAgent(diagnostics);
|
|
248
|
-
expect(formatted).not.toBeNull();
|
|
249
|
-
expect(formatted).toContain('[LSP Diagnostics]');
|
|
250
|
-
expect(formatted).toContain('Error:');
|
|
251
|
-
expect(formatted).toContain('Warning:');
|
|
252
|
-
// Verify the formatting includes enough info for the LLM to self-correct
|
|
253
|
-
expect(formatted).toContain('/src/server.ts:23:5');
|
|
254
|
-
expect(formatted).toContain("Property 'origin'");
|
|
255
|
-
resetLSPManager();
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it('should append diagnostics to tool output when errors exist', () => {
|
|
259
|
-
// Simulate the behavior of the agent loop's LSP injection
|
|
260
|
-
const originalOutput = 'File edited successfully.';
|
|
261
|
-
const diagnosticText = '[LSP Diagnostics]\n Error: /src/server.ts:23:5 — Type error (ts)';
|
|
262
|
-
|
|
263
|
-
// This mirrors the logic in executeToolCall
|
|
264
|
-
const combined = `${originalOutput}\n\n${diagnosticText}`;
|
|
265
|
-
expect(combined).toContain(originalOutput);
|
|
266
|
-
expect(combined).toContain('[LSP Diagnostics]');
|
|
267
|
-
expect(combined).toContain('Type error');
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it('should not append diagnostics when there are no errors', () => {
|
|
271
|
-
const manager = new LSPManager('/tmp/test');
|
|
272
|
-
const formatted = manager.formatDiagnosticsForAgent([]);
|
|
273
|
-
expect(formatted).toBeNull();
|
|
274
|
-
resetLSPManager();
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
it('should not inject diagnostics for non-file-editing tools', () => {
|
|
278
|
-
// read_file, bash, grep etc. should not trigger LSP
|
|
279
|
-
const nonEditTools = ['read_file', 'bash', 'glob', 'grep', 'list_dir', 'terraform'];
|
|
280
|
-
for (const tool of nonEditTools) {
|
|
281
|
-
// These should not have file path extraction attempted
|
|
282
|
-
const isFileEditing = ['edit_file', 'multi_edit', 'write_file'].includes(tool);
|
|
283
|
-
expect(isFileEditing).toBe(false);
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it('should gracefully handle missing path in input', () => {
|
|
288
|
-
// If somehow the input doesn't have a path field, extraction returns null
|
|
289
|
-
const input = { content: 'some content' };
|
|
290
|
-
const hasPath = 'path' in input;
|
|
291
|
-
expect(hasPath).toBe(false);
|
|
292
|
-
});
|
|
293
|
-
});
|