@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,179 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Incident Command Tests — G14
|
|
3
|
-
*
|
|
4
|
-
* Tests parseIncidentInput (internal logic), PD_API_TOKEN guard,
|
|
5
|
-
* detectServiceName logic, and overall incidentCommand integration.
|
|
6
|
-
*
|
|
7
|
-
* Because parseIncidentInput and detectServiceName are not exported,
|
|
8
|
-
* we test via inline reproductions of the same logic and source-level
|
|
9
|
-
* assertions.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
13
|
-
import { readFileSync } from 'node:fs';
|
|
14
|
-
import { join } from 'node:path';
|
|
15
|
-
|
|
16
|
-
const INCIDENT_SRC = readFileSync(join(__dirname, '..', 'commands', 'incident.ts'), 'utf-8');
|
|
17
|
-
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// Source-level assertions
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
|
|
22
|
-
describe('incident.ts source structure (G14)', () => {
|
|
23
|
-
it('defines parseIncidentInput function', () => {
|
|
24
|
-
expect(INCIDENT_SRC).toContain('function parseIncidentInput');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('detects PagerDuty URLs via pagerduty.com regex', () => {
|
|
28
|
-
expect(INCIDENT_SRC).toContain('pagerduty\\.com');
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('detects Opsgenie URLs via opsgenie.com regex', () => {
|
|
32
|
-
expect(INCIDENT_SRC).toContain('opsgenie\\.com');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('reads PD_API_TOKEN from process.env before fetch', () => {
|
|
36
|
-
expect(INCIDENT_SRC).toContain('PD_API_TOKEN');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('defines detectServiceName function', () => {
|
|
40
|
-
expect(INCIDENT_SRC).toContain('function detectServiceName');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('exports incidentCommand', () => {
|
|
44
|
-
expect(INCIDENT_SRC).toContain('export async function incidentCommand');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('fetchPagerDutyDetails returns null immediately when no token is set', () => {
|
|
48
|
-
// Verify the guard is present in source
|
|
49
|
-
expect(INCIDENT_SRC).toContain('if (!token) return null');
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// ---------------------------------------------------------------------------
|
|
54
|
-
// Inline reproduction of parseIncidentInput to unit-test logic (G14)
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
|
|
57
|
-
type AlertSource = 'pagerduty' | 'opsgenie' | 'plain';
|
|
58
|
-
interface ParsedIncident { source: AlertSource; id: string; rawInput: string; }
|
|
59
|
-
|
|
60
|
-
function parseIncidentInput(input: string): ParsedIncident {
|
|
61
|
-
if (/pagerduty\.com/i.test(input)) {
|
|
62
|
-
const match = input.match(/incidents?\/(P[A-Z0-9]+)/i);
|
|
63
|
-
return { source: 'pagerduty', id: match?.[1] ?? input, rawInput: input };
|
|
64
|
-
}
|
|
65
|
-
if (/opsgenie\.com/i.test(input) || /app\.opsgenie\.com/i.test(input)) {
|
|
66
|
-
const match = input.match(/alert\/([a-f0-9-]{36})/i);
|
|
67
|
-
return { source: 'opsgenie', id: match?.[1] ?? input, rawInput: input };
|
|
68
|
-
}
|
|
69
|
-
return { source: 'plain', id: input, rawInput: input };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
describe('parseIncidentInput logic (G14)', () => {
|
|
73
|
-
it('returns source=pagerduty for a PagerDuty URL', () => {
|
|
74
|
-
const result = parseIncidentInput('https://acme.pagerduty.com/incidents/PABC123');
|
|
75
|
-
expect(result.source).toBe('pagerduty');
|
|
76
|
-
expect(result.id).toBe('PABC123');
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('returns source=opsgenie for an Opsgenie URL', () => {
|
|
80
|
-
const uuid = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
|
|
81
|
-
const result = parseIncidentInput(`https://app.opsgenie.com/alert/${uuid}`);
|
|
82
|
-
expect(result.source).toBe('opsgenie');
|
|
83
|
-
expect(result.id).toBe(uuid);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('returns source=plain for plain text', () => {
|
|
87
|
-
const result = parseIncidentInput('high CPU on api-service pod');
|
|
88
|
-
expect(result.source).toBe('plain');
|
|
89
|
-
expect(result.id).toBe('high CPU on api-service pod');
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// ---------------------------------------------------------------------------
|
|
94
|
-
// Inline reproduction of detectServiceName to unit-test logic (G14)
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
|
|
97
|
-
function detectServiceName(incident: ParsedIncident): string | null {
|
|
98
|
-
const match = incident.rawInput.match(/\b([\w-]+(?:service|worker|api|pod|deploy(?:ment)?|ingress|controller)[\w-]*)\b/i);
|
|
99
|
-
return match?.[1] ?? null;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
describe('detectServiceName logic (G14)', () => {
|
|
103
|
-
it('detects service name from plain text containing "service"', () => {
|
|
104
|
-
const incident = parseIncidentInput('high CPU on payment-service pod');
|
|
105
|
-
const name = detectServiceName(incident);
|
|
106
|
-
expect(name).not.toBeNull();
|
|
107
|
-
expect(name).toContain('service');
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('detects API service names', () => {
|
|
111
|
-
const incident = parseIncidentInput('error rate spike in user-api deployment');
|
|
112
|
-
const name = detectServiceName(incident);
|
|
113
|
-
expect(name).not.toBeNull();
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('returns null for an opaque string with no recognizable service pattern', () => {
|
|
117
|
-
const incident = parseIncidentInput('PABC123');
|
|
118
|
-
const name = detectServiceName(incident);
|
|
119
|
-
expect(name).toBeNull();
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// ---------------------------------------------------------------------------
|
|
124
|
-
// PD_API_TOKEN guard: fetchPagerDutyDetails skips when token absent
|
|
125
|
-
// ---------------------------------------------------------------------------
|
|
126
|
-
|
|
127
|
-
describe('PD_API_TOKEN guard (G14)', () => {
|
|
128
|
-
beforeEach(() => {
|
|
129
|
-
vi.resetModules();
|
|
130
|
-
delete process.env.PD_API_TOKEN;
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
afterEach(() => {
|
|
134
|
-
vi.restoreAllMocks();
|
|
135
|
-
delete process.env.PD_API_TOKEN;
|
|
136
|
-
vi.resetModules();
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('incidentCommand with plain text does not throw when chatCommand is mocked', async () => {
|
|
140
|
-
// Mock chatCommand so the test doesn't start the full TUI
|
|
141
|
-
vi.doMock('../commands/chat', () => ({
|
|
142
|
-
chatCommand: vi.fn().mockResolvedValue(undefined),
|
|
143
|
-
}));
|
|
144
|
-
|
|
145
|
-
// Also mock child_process to prevent any real shell calls
|
|
146
|
-
vi.doMock('node:child_process', async () => {
|
|
147
|
-
const actual = await vi.importActual<typeof import('node:child_process')>('node:child_process');
|
|
148
|
-
return {
|
|
149
|
-
...actual,
|
|
150
|
-
execSync: vi.fn(() => ''),
|
|
151
|
-
};
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
const { incidentCommand } = await import('../commands/incident');
|
|
155
|
-
// Plain text input — should not throw, PD fetch is not triggered
|
|
156
|
-
await expect(incidentCommand('high CPU on api-service', {})).resolves.not.toThrow();
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('incidentCommand with PagerDuty URL and no token skips PD API call', async () => {
|
|
160
|
-
vi.doMock('../commands/chat', () => ({
|
|
161
|
-
chatCommand: vi.fn().mockResolvedValue(undefined),
|
|
162
|
-
}));
|
|
163
|
-
|
|
164
|
-
// Prevent real helm/kubectl shell calls
|
|
165
|
-
vi.doMock('node:child_process', async () => {
|
|
166
|
-
const actual = await vi.importActual<typeof import('node:child_process')>('node:child_process');
|
|
167
|
-
return {
|
|
168
|
-
...actual,
|
|
169
|
-
execSync: vi.fn(() => ''),
|
|
170
|
-
};
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
const { incidentCommand } = await import('../commands/incident');
|
|
174
|
-
// PD_API_TOKEN is not set, so the PD API fetch should be skipped
|
|
175
|
-
await expect(
|
|
176
|
-
incidentCommand('https://acme.pagerduty.com/incidents/PABC123', {})
|
|
177
|
-
).resolves.not.toThrow();
|
|
178
|
-
});
|
|
179
|
-
});
|
|
@@ -1,487 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the Nimbus project initialization module (src/cli/init.ts).
|
|
3
|
-
*
|
|
4
|
-
* Each test creates a temporary directory with the appropriate marker files,
|
|
5
|
-
* exercises the detection and init functions, and cleans up afterward.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, test, expect, beforeEach, afterEach } from 'vitest';
|
|
9
|
-
import * as fs from 'node:fs';
|
|
10
|
-
import * as path from 'node:path';
|
|
11
|
-
import * as os from 'node:os';
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
detectProjectType,
|
|
15
|
-
detectInfrastructure,
|
|
16
|
-
detectCloudProviders,
|
|
17
|
-
detectPackageManager,
|
|
18
|
-
detectProject,
|
|
19
|
-
generateNimbusMd,
|
|
20
|
-
runInit,
|
|
21
|
-
} from '../cli/init';
|
|
22
|
-
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
// Shared helpers
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
|
|
27
|
-
function makeTmpDir(): string {
|
|
28
|
-
return fs.mkdtempSync(path.join(os.tmpdir(), 'nimbus-init-test-'));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function removeTmpDir(dir: string): void {
|
|
32
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// ============================================================================
|
|
36
|
-
// detectProjectType()
|
|
37
|
-
// ============================================================================
|
|
38
|
-
|
|
39
|
-
describe('detectProjectType()', () => {
|
|
40
|
-
let tmpDir: string;
|
|
41
|
-
|
|
42
|
-
beforeEach(() => {
|
|
43
|
-
tmpDir = makeTmpDir();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
afterEach(() => {
|
|
47
|
-
removeTmpDir(tmpDir);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('detects TypeScript (tsconfig.json)', () => {
|
|
51
|
-
fs.writeFileSync(path.join(tmpDir, 'tsconfig.json'), '{}');
|
|
52
|
-
expect(detectProjectType(tmpDir)).toBe('typescript');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('detects Go (go.mod)', () => {
|
|
56
|
-
fs.writeFileSync(path.join(tmpDir, 'go.mod'), 'module example.com/myapp\n\ngo 1.21\n');
|
|
57
|
-
expect(detectProjectType(tmpDir)).toBe('go');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test('detects Python (pyproject.toml)', () => {
|
|
61
|
-
fs.writeFileSync(path.join(tmpDir, 'pyproject.toml'), '[project]\nname = "myapp"\n');
|
|
62
|
-
expect(detectProjectType(tmpDir)).toBe('python');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('detects Rust (Cargo.toml)', () => {
|
|
66
|
-
fs.writeFileSync(
|
|
67
|
-
path.join(tmpDir, 'Cargo.toml'),
|
|
68
|
-
'[package]\nname = "myapp"\nversion = "0.1.0"\n'
|
|
69
|
-
);
|
|
70
|
-
expect(detectProjectType(tmpDir)).toBe('rust');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('detects Java (pom.xml)', () => {
|
|
74
|
-
fs.writeFileSync(
|
|
75
|
-
path.join(tmpDir, 'pom.xml'),
|
|
76
|
-
'<project><modelVersion>4.0.0</modelVersion></project>'
|
|
77
|
-
);
|
|
78
|
-
expect(detectProjectType(tmpDir)).toBe('java');
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test('returns "unknown" for empty dir', () => {
|
|
82
|
-
expect(detectProjectType(tmpDir)).toBe('unknown');
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// ============================================================================
|
|
87
|
-
// detectInfrastructure()
|
|
88
|
-
// ============================================================================
|
|
89
|
-
|
|
90
|
-
describe('detectInfrastructure()', () => {
|
|
91
|
-
let tmpDir: string;
|
|
92
|
-
|
|
93
|
-
beforeEach(() => {
|
|
94
|
-
tmpDir = makeTmpDir();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
afterEach(() => {
|
|
98
|
-
removeTmpDir(tmpDir);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test('detects Terraform (.tf files)', () => {
|
|
102
|
-
fs.writeFileSync(path.join(tmpDir, 'main.tf'), 'provider "aws" {\n region = "us-east-1"\n}\n');
|
|
103
|
-
|
|
104
|
-
const infra = detectInfrastructure(tmpDir);
|
|
105
|
-
expect(infra).toContain('terraform');
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test('detects Kubernetes (manifest with kind: Deployment)', () => {
|
|
109
|
-
fs.writeFileSync(
|
|
110
|
-
path.join(tmpDir, 'deployment.yaml'),
|
|
111
|
-
['apiVersion: apps/v1', 'kind: Deployment', 'metadata:', ' name: web'].join('\n')
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
const infra = detectInfrastructure(tmpDir);
|
|
115
|
-
expect(infra).toContain('kubernetes');
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test('detects Helm (Chart.yaml)', () => {
|
|
119
|
-
fs.writeFileSync(
|
|
120
|
-
path.join(tmpDir, 'Chart.yaml'),
|
|
121
|
-
'apiVersion: v2\nname: my-chart\nversion: 0.1.0\n'
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
const infra = detectInfrastructure(tmpDir);
|
|
125
|
-
expect(infra).toContain('helm');
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test('detects Docker (Dockerfile)', () => {
|
|
129
|
-
fs.writeFileSync(path.join(tmpDir, 'Dockerfile'), 'FROM node:20-alpine\nWORKDIR /app\n');
|
|
130
|
-
|
|
131
|
-
const infra = detectInfrastructure(tmpDir);
|
|
132
|
-
expect(infra).toContain('docker');
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test('detects CI/CD (.github/workflows/)', () => {
|
|
136
|
-
const workflowsDir = path.join(tmpDir, '.github', 'workflows');
|
|
137
|
-
fs.mkdirSync(workflowsDir, { recursive: true });
|
|
138
|
-
fs.writeFileSync(path.join(workflowsDir, 'ci.yml'), 'name: CI\non: push\n');
|
|
139
|
-
|
|
140
|
-
const infra = detectInfrastructure(tmpDir);
|
|
141
|
-
expect(infra).toContain('cicd');
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
test('returns empty array for bare directory', () => {
|
|
145
|
-
const infra = detectInfrastructure(tmpDir);
|
|
146
|
-
expect(infra).toEqual([]);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// ============================================================================
|
|
151
|
-
// detectCloudProviders()
|
|
152
|
-
// ============================================================================
|
|
153
|
-
|
|
154
|
-
describe('detectCloudProviders()', () => {
|
|
155
|
-
let tmpDir: string;
|
|
156
|
-
|
|
157
|
-
beforeEach(() => {
|
|
158
|
-
tmpDir = makeTmpDir();
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
afterEach(() => {
|
|
162
|
-
removeTmpDir(tmpDir);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test('detects AWS (provider "aws" in .tf)', () => {
|
|
166
|
-
fs.writeFileSync(
|
|
167
|
-
path.join(tmpDir, 'providers.tf'),
|
|
168
|
-
'provider "aws" {\n region = "us-east-1"\n}\n'
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
const providers = detectCloudProviders(tmpDir);
|
|
172
|
-
expect(providers).toContain('aws');
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
test('detects GCP (provider "google" in .tf)', () => {
|
|
176
|
-
fs.writeFileSync(
|
|
177
|
-
path.join(tmpDir, 'providers.tf'),
|
|
178
|
-
'provider "google" {\n project = "my-project"\n}\n'
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
const providers = detectCloudProviders(tmpDir);
|
|
182
|
-
expect(providers).toContain('gcp');
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
test('detects Azure (provider "azurerm" in .tf)', () => {
|
|
186
|
-
fs.writeFileSync(path.join(tmpDir, 'providers.tf'), 'provider "azurerm" {\n features {}\n}\n');
|
|
187
|
-
|
|
188
|
-
const providers = detectCloudProviders(tmpDir);
|
|
189
|
-
expect(providers).toContain('azure');
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
// ============================================================================
|
|
194
|
-
// detectPackageManager()
|
|
195
|
-
// ============================================================================
|
|
196
|
-
|
|
197
|
-
describe('detectPackageManager()', () => {
|
|
198
|
-
let tmpDir: string;
|
|
199
|
-
|
|
200
|
-
beforeEach(() => {
|
|
201
|
-
tmpDir = makeTmpDir();
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
afterEach(() => {
|
|
205
|
-
removeTmpDir(tmpDir);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
test('detects bun (bun.lock)', () => {
|
|
209
|
-
fs.writeFileSync(path.join(tmpDir, 'bun.lock'), '');
|
|
210
|
-
expect(detectPackageManager(tmpDir)).toBe('bun');
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
test('detects npm (package-lock.json)', () => {
|
|
214
|
-
fs.writeFileSync(path.join(tmpDir, 'package-lock.json'), '{}');
|
|
215
|
-
expect(detectPackageManager(tmpDir)).toBe('npm');
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
test('returns undefined when no lock file is present', () => {
|
|
219
|
-
expect(detectPackageManager(tmpDir)).toBeUndefined();
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// ============================================================================
|
|
224
|
-
// detectProject()
|
|
225
|
-
// ============================================================================
|
|
226
|
-
|
|
227
|
-
describe('detectProject()', () => {
|
|
228
|
-
let tmpDir: string;
|
|
229
|
-
|
|
230
|
-
beforeEach(() => {
|
|
231
|
-
tmpDir = makeTmpDir();
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
afterEach(() => {
|
|
235
|
-
removeTmpDir(tmpDir);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
test('aggregates all detection results', () => {
|
|
239
|
-
// TypeScript project
|
|
240
|
-
fs.writeFileSync(path.join(tmpDir, 'tsconfig.json'), '{}');
|
|
241
|
-
// Bun lock file
|
|
242
|
-
fs.writeFileSync(path.join(tmpDir, 'bun.lock'), '');
|
|
243
|
-
// Terraform
|
|
244
|
-
fs.writeFileSync(path.join(tmpDir, 'main.tf'), 'provider "aws" {\n region = "us-east-1"\n}\n');
|
|
245
|
-
// Docker
|
|
246
|
-
fs.writeFileSync(path.join(tmpDir, 'Dockerfile'), 'FROM node:20\n');
|
|
247
|
-
// Git
|
|
248
|
-
fs.mkdirSync(path.join(tmpDir, '.git'));
|
|
249
|
-
|
|
250
|
-
const detection = detectProject(tmpDir);
|
|
251
|
-
|
|
252
|
-
expect(detection.projectName).toBe(path.basename(tmpDir));
|
|
253
|
-
expect(detection.projectType).toBe('typescript');
|
|
254
|
-
expect(detection.packageManager).toBe('bun');
|
|
255
|
-
expect(detection.infraTypes).toContain('terraform');
|
|
256
|
-
expect(detection.infraTypes).toContain('docker');
|
|
257
|
-
expect(detection.cloudProviders).toContain('aws');
|
|
258
|
-
expect(detection.hasGit).toBe(true);
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
// ============================================================================
|
|
263
|
-
// generateNimbusMd()
|
|
264
|
-
// ============================================================================
|
|
265
|
-
|
|
266
|
-
describe('generateNimbusMd()', () => {
|
|
267
|
-
test('includes project name', () => {
|
|
268
|
-
const md = generateNimbusMd(
|
|
269
|
-
{
|
|
270
|
-
projectName: 'my-cool-app',
|
|
271
|
-
projectType: 'typescript',
|
|
272
|
-
infraTypes: [],
|
|
273
|
-
cloudProviders: [],
|
|
274
|
-
hasGit: true,
|
|
275
|
-
packageManager: 'bun',
|
|
276
|
-
},
|
|
277
|
-
'/tmp/my-cool-app'
|
|
278
|
-
);
|
|
279
|
-
|
|
280
|
-
expect(md).toContain('# my-cool-app');
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
test('includes detected infrastructure', () => {
|
|
284
|
-
const md = generateNimbusMd(
|
|
285
|
-
{
|
|
286
|
-
projectName: 'infra-project',
|
|
287
|
-
projectType: 'typescript',
|
|
288
|
-
infraTypes: ['terraform', 'kubernetes', 'docker'],
|
|
289
|
-
cloudProviders: ['aws', 'gcp'],
|
|
290
|
-
hasGit: true,
|
|
291
|
-
packageManager: 'npm',
|
|
292
|
-
},
|
|
293
|
-
'/tmp/infra-project'
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
expect(md).toContain('## Infrastructure');
|
|
297
|
-
expect(md).toContain('terraform');
|
|
298
|
-
expect(md).toContain('kubernetes');
|
|
299
|
-
expect(md).toContain('docker');
|
|
300
|
-
expect(md).toContain('aws');
|
|
301
|
-
expect(md).toContain('gcp');
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
test('includes safety rules section', () => {
|
|
305
|
-
const md = generateNimbusMd(
|
|
306
|
-
{
|
|
307
|
-
projectName: 'test-project',
|
|
308
|
-
projectType: 'go',
|
|
309
|
-
infraTypes: [],
|
|
310
|
-
cloudProviders: [],
|
|
311
|
-
hasGit: false,
|
|
312
|
-
},
|
|
313
|
-
'/tmp/test-project'
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
expect(md).toContain('## Safety Rules');
|
|
317
|
-
expect(md).toContain('Protected branches');
|
|
318
|
-
expect(md).toContain('Never store secrets');
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
test('includes package manager when present', () => {
|
|
322
|
-
const md = generateNimbusMd(
|
|
323
|
-
{
|
|
324
|
-
projectName: 'bun-app',
|
|
325
|
-
projectType: 'typescript',
|
|
326
|
-
infraTypes: [],
|
|
327
|
-
cloudProviders: [],
|
|
328
|
-
hasGit: true,
|
|
329
|
-
packageManager: 'bun',
|
|
330
|
-
},
|
|
331
|
-
'/tmp/bun-app'
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
expect(md).toContain('**Package Manager:** bun');
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
test('G5: includes Guardrails section when infra is detected', () => {
|
|
338
|
-
const md = generateNimbusMd(
|
|
339
|
-
{
|
|
340
|
-
projectName: 'infra-app',
|
|
341
|
-
projectType: 'typescript',
|
|
342
|
-
infraTypes: ['terraform', 'kubernetes'],
|
|
343
|
-
cloudProviders: ['aws'],
|
|
344
|
-
hasGit: true,
|
|
345
|
-
},
|
|
346
|
-
'/tmp/infra-app'
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
expect(md).toContain('## Guardrails');
|
|
350
|
-
expect(md).toContain('Never run `terraform destroy`');
|
|
351
|
-
expect(md).toContain('Protected Kubernetes namespaces');
|
|
352
|
-
expect(md).toContain('Always show terraform plan before apply');
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
test('G5: omits Guardrails section when no infra detected', () => {
|
|
356
|
-
const md = generateNimbusMd(
|
|
357
|
-
{
|
|
358
|
-
projectName: 'pure-code',
|
|
359
|
-
projectType: 'typescript',
|
|
360
|
-
infraTypes: [],
|
|
361
|
-
cloudProviders: [],
|
|
362
|
-
hasGit: false,
|
|
363
|
-
},
|
|
364
|
-
'/tmp/pure-code'
|
|
365
|
-
);
|
|
366
|
-
|
|
367
|
-
expect(md).not.toContain('## Guardrails');
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
test('G5: always includes Forbidden placeholder section', () => {
|
|
371
|
-
const md = generateNimbusMd(
|
|
372
|
-
{
|
|
373
|
-
projectName: 'any-app',
|
|
374
|
-
projectType: 'typescript',
|
|
375
|
-
infraTypes: [],
|
|
376
|
-
cloudProviders: [],
|
|
377
|
-
hasGit: false,
|
|
378
|
-
},
|
|
379
|
-
'/tmp/any-app'
|
|
380
|
-
);
|
|
381
|
-
|
|
382
|
-
expect(md).toContain('## Forbidden');
|
|
383
|
-
expect(md).toContain('<!-- List operations Nimbus must never perform');
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
// ============================================================================
|
|
388
|
-
// runInit()
|
|
389
|
-
// ============================================================================
|
|
390
|
-
|
|
391
|
-
describe('runInit()', () => {
|
|
392
|
-
let tmpDir: string;
|
|
393
|
-
|
|
394
|
-
beforeEach(() => {
|
|
395
|
-
tmpDir = makeTmpDir();
|
|
396
|
-
// Create a minimal project marker so detectProjectType returns something
|
|
397
|
-
fs.writeFileSync(path.join(tmpDir, 'tsconfig.json'), '{}');
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
afterEach(() => {
|
|
401
|
-
removeTmpDir(tmpDir);
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
test('creates NIMBUS.md file', async () => {
|
|
405
|
-
const result = await runInit({ cwd: tmpDir, quiet: true });
|
|
406
|
-
|
|
407
|
-
const nimbusmdPath = path.join(tmpDir, 'NIMBUS.md');
|
|
408
|
-
expect(fs.existsSync(nimbusmdPath)).toBe(true);
|
|
409
|
-
expect(result.nimbusmdPath).toBe(nimbusmdPath);
|
|
410
|
-
|
|
411
|
-
const content = fs.readFileSync(nimbusmdPath, 'utf-8');
|
|
412
|
-
expect(content).toContain(path.basename(tmpDir));
|
|
413
|
-
expect(content).toContain('typescript');
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
test('creates .nimbus/ directory', async () => {
|
|
417
|
-
await runInit({ cwd: tmpDir, quiet: true });
|
|
418
|
-
|
|
419
|
-
const nimbusDirPath = path.join(tmpDir, '.nimbus');
|
|
420
|
-
expect(fs.existsSync(nimbusDirPath)).toBe(true);
|
|
421
|
-
expect(fs.statSync(nimbusDirPath).isDirectory()).toBe(true);
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
test('creates .nimbus/config.yaml', async () => {
|
|
425
|
-
const result = await runInit({ cwd: tmpDir, quiet: true });
|
|
426
|
-
|
|
427
|
-
const configPath = path.join(tmpDir, '.nimbus', 'config.yaml');
|
|
428
|
-
expect(fs.existsSync(configPath)).toBe(true);
|
|
429
|
-
expect(result.filesCreated).toContain(configPath);
|
|
430
|
-
|
|
431
|
-
const content = fs.readFileSync(configPath, 'utf-8');
|
|
432
|
-
expect(content).toContain('default_model:');
|
|
433
|
-
expect(content).toContain('permissions:');
|
|
434
|
-
expect(content).toContain('safety:');
|
|
435
|
-
expect(content).toContain('type: typescript');
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
test('skips if NIMBUS.md exists without --force (quiet mode)', async () => {
|
|
439
|
-
// First init
|
|
440
|
-
await runInit({ cwd: tmpDir, quiet: true });
|
|
441
|
-
expect(fs.existsSync(path.join(tmpDir, 'NIMBUS.md'))).toBe(true);
|
|
442
|
-
|
|
443
|
-
// Second init in quiet mode should skip without throwing
|
|
444
|
-
const result = await runInit({ cwd: tmpDir, quiet: true });
|
|
445
|
-
expect(result.filesCreated).toHaveLength(0);
|
|
446
|
-
expect(result.nimbusmdPath).toBe(path.join(tmpDir, 'NIMBUS.md'));
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
test('overwrites with --force', async () => {
|
|
450
|
-
// First init
|
|
451
|
-
await runInit({ cwd: tmpDir, quiet: true });
|
|
452
|
-
|
|
453
|
-
// Modify NIMBUS.md so we can tell if it's overwritten
|
|
454
|
-
const nimbusmdPath = path.join(tmpDir, 'NIMBUS.md');
|
|
455
|
-
fs.writeFileSync(nimbusmdPath, 'CUSTOM CONTENT THAT SHOULD BE REPLACED');
|
|
456
|
-
|
|
457
|
-
// Second init with force
|
|
458
|
-
await runInit({ cwd: tmpDir, force: true, quiet: true });
|
|
459
|
-
|
|
460
|
-
const content = fs.readFileSync(nimbusmdPath, 'utf-8');
|
|
461
|
-
expect(content).not.toContain('CUSTOM CONTENT THAT SHOULD BE REPLACED');
|
|
462
|
-
expect(content).toContain('Auto-generated by `nimbus init`');
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
test('creates hooks and agents subdirectories', async () => {
|
|
466
|
-
await runInit({ cwd: tmpDir, quiet: true });
|
|
467
|
-
|
|
468
|
-
expect(fs.existsSync(path.join(tmpDir, '.nimbus', 'hooks'))).toBe(true);
|
|
469
|
-
expect(fs.existsSync(path.join(tmpDir, '.nimbus', 'agents'))).toBe(true);
|
|
470
|
-
expect(fs.existsSync(path.join(tmpDir, '.nimbus', 'hooks', 'pre-commit.ts'))).toBe(true);
|
|
471
|
-
expect(fs.existsSync(path.join(tmpDir, '.nimbus', 'agents', 'default.yaml'))).toBe(true);
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
test('returns correct detection results', async () => {
|
|
475
|
-
// Add some extra markers
|
|
476
|
-
fs.writeFileSync(path.join(tmpDir, 'bun.lock'), '');
|
|
477
|
-
fs.writeFileSync(path.join(tmpDir, 'main.tf'), 'provider "aws" {\n region = "us-east-1"\n}\n');
|
|
478
|
-
|
|
479
|
-
const result = await runInit({ cwd: tmpDir, quiet: true });
|
|
480
|
-
|
|
481
|
-
expect(result.detection.projectType).toBe('typescript');
|
|
482
|
-
expect(result.detection.packageManager).toBe('bun');
|
|
483
|
-
expect(result.detection.infraTypes).toContain('terraform');
|
|
484
|
-
expect(result.detection.cloudProviders).toContain('aws');
|
|
485
|
-
expect(result.filesCreated.length).toBeGreaterThan(0);
|
|
486
|
-
});
|
|
487
|
-
});
|