@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
package/dist/src/ui/App.js
CHANGED
|
@@ -142,18 +142,21 @@ export function App({ initialSession, onMessage, onAbort, onCompact, onContext,
|
|
|
142
142
|
addMessage: (msg) => {
|
|
143
143
|
setMessages(prev => [...prev, msg]);
|
|
144
144
|
// C1: Keep pinned to bottom when scroll is locked
|
|
145
|
-
if (scrollLockedRef.current)
|
|
145
|
+
if (scrollLockedRef.current) {
|
|
146
146
|
setScrollOffset(0);
|
|
147
|
+
}
|
|
147
148
|
},
|
|
148
149
|
updateMessage: (id, content) => {
|
|
149
|
-
if (content)
|
|
150
|
+
if (content) {
|
|
150
151
|
setCurrentTurnHasOutput(true);
|
|
152
|
+
}
|
|
151
153
|
setMessages(prev => prev.map(m => (m.id === id ? { ...m, content } : m)));
|
|
152
154
|
},
|
|
153
155
|
updateSession: (patch) => setSession(prev => ({ ...prev, ...patch })),
|
|
154
156
|
setToolCalls: (toolCalls) => {
|
|
155
|
-
if (toolCalls.length > 0)
|
|
157
|
+
if (toolCalls.length > 0) {
|
|
156
158
|
setCurrentTurnHasOutput(true);
|
|
159
|
+
}
|
|
157
160
|
setActiveToolCalls(toolCalls);
|
|
158
161
|
// M3: Auto-show terminal pane when long-running DevOps tools start
|
|
159
162
|
const LONG_RUNNING_TOOL_PATTERNS = [
|
|
@@ -975,15 +978,18 @@ export function App({ initialSession, onMessage, onAbort, onCompact, onContext,
|
|
|
975
978
|
const watcher = new FileWatcher(process.cwd());
|
|
976
979
|
watcher.start();
|
|
977
980
|
watcher.on('change', (filePath) => {
|
|
978
|
-
if (ac.signal.aborted)
|
|
981
|
+
if (ac.signal.aborted) {
|
|
979
982
|
return;
|
|
983
|
+
}
|
|
980
984
|
const ext = pattern.replace('**/', '').replace(/\*/g, '');
|
|
981
|
-
if (ext && !filePath.includes(ext))
|
|
985
|
+
if (ext && !filePath.includes(ext)) {
|
|
982
986
|
return;
|
|
987
|
+
}
|
|
983
988
|
const prompt = `File changed: ${filePath}. Analyze the change and report any issues or drift.`;
|
|
984
989
|
sysMsg(`[watch] Change detected: ${filePath}`);
|
|
985
|
-
if (!isProcessing)
|
|
990
|
+
if (!isProcessing) {
|
|
986
991
|
handleSubmit(prompt);
|
|
992
|
+
}
|
|
987
993
|
});
|
|
988
994
|
ac.signal.addEventListener('abort', () => watcher.stop());
|
|
989
995
|
}
|
|
@@ -1062,8 +1068,9 @@ export function App({ initialSession, onMessage, onAbort, onCompact, onContext,
|
|
|
1062
1068
|
setIsProcessing(true);
|
|
1063
1069
|
setCurrentTurnHasOutput(false);
|
|
1064
1070
|
setProcessingStartTime(Date.now());
|
|
1065
|
-
if (onMessage)
|
|
1071
|
+
if (onMessage) {
|
|
1066
1072
|
onMessage('List all available Kubernetes contexts and show the current one.');
|
|
1073
|
+
}
|
|
1067
1074
|
}
|
|
1068
1075
|
return;
|
|
1069
1076
|
}
|
|
@@ -1132,8 +1139,9 @@ export function App({ initialSession, onMessage, onAbort, onCompact, onContext,
|
|
|
1132
1139
|
setIsProcessing(true);
|
|
1133
1140
|
setCurrentTurnHasOutput(false);
|
|
1134
1141
|
setProcessingStartTime(Date.now());
|
|
1135
|
-
if (onMessage)
|
|
1142
|
+
if (onMessage) {
|
|
1136
1143
|
onMessage('List all Terraform workspaces and show the current one.');
|
|
1144
|
+
}
|
|
1137
1145
|
}
|
|
1138
1146
|
return;
|
|
1139
1147
|
}
|
|
@@ -1526,8 +1534,9 @@ export function App({ initialSession, onMessage, onAbort, onCompact, onContext,
|
|
|
1526
1534
|
if (key.downArrow || input === 'j') {
|
|
1527
1535
|
setScrollOffset(prev => {
|
|
1528
1536
|
const next = Math.max(0, prev - 1);
|
|
1529
|
-
if (next === 0)
|
|
1537
|
+
if (next === 0) {
|
|
1530
1538
|
setScrollLocked(true);
|
|
1539
|
+
}
|
|
1531
1540
|
return next;
|
|
1532
1541
|
});
|
|
1533
1542
|
return;
|
|
@@ -1542,8 +1551,9 @@ export function App({ initialSession, onMessage, onAbort, onCompact, onContext,
|
|
|
1542
1551
|
if (key.pageDown || input === 'f' || input === ' ') {
|
|
1543
1552
|
setScrollOffset(prev => {
|
|
1544
1553
|
const next = Math.max(0, prev - 10);
|
|
1545
|
-
if (next === 0)
|
|
1554
|
+
if (next === 0) {
|
|
1546
1555
|
setScrollLocked(true);
|
|
1556
|
+
}
|
|
1547
1557
|
return next;
|
|
1548
1558
|
});
|
|
1549
1559
|
return;
|
|
@@ -1593,13 +1603,15 @@ export function App({ initialSession, onMessage, onAbort, onCompact, onContext,
|
|
|
1593
1603
|
}, { isActive: !isProcessing && !permissionRequest && !deployPreview && !fileDiffRequest && !showHelp });
|
|
1594
1604
|
/* -- H3: Deploy mode confirmation input handler ----------------------- */
|
|
1595
1605
|
useInput((input, key) => {
|
|
1596
|
-
if (!pendingDeployConfirm)
|
|
1606
|
+
if (!pendingDeployConfirm) {
|
|
1597
1607
|
return;
|
|
1608
|
+
}
|
|
1598
1609
|
if (input === 'y' || input === 'Y') {
|
|
1599
1610
|
setPendingDeployConfirm(false);
|
|
1600
1611
|
setSession(prev => ({ ...prev, mode: 'deploy' }));
|
|
1601
|
-
if (onModeChange)
|
|
1612
|
+
if (onModeChange) {
|
|
1602
1613
|
onModeChange('deploy');
|
|
1614
|
+
}
|
|
1603
1615
|
try {
|
|
1604
1616
|
const { saveModeForCwd } = require('../config/mode-store');
|
|
1605
1617
|
saveModeForCwd(process.cwd(), 'deploy');
|
|
@@ -1643,7 +1655,7 @@ export function App({ initialSession, onMessage, onAbort, onCompact, onContext,
|
|
|
1643
1655
|
return (_jsxs(Box, { flexDirection: "column", width: "100%", height: "100%", children: [showApiKeySetup && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", padding: 1, marginBottom: 1, children: [_jsx(Text, { bold: true, color: "yellow", children: "Welcome to Nimbus! No API key configured." }), _jsx(Text, { dimColor: true, children: "Set ANTHROPIC_API_KEY environment variable, or run: nimbus login" }), _jsx(Text, { dimColor: true, children: "Press Enter to continue without API key (limited functionality)" }), _jsx(Text, { dimColor: true, children: "This banner will dismiss in 8 seconds or on your first message." })] })), _jsx(Header, { session: session }), _jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [_jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(MessageList, { messages: messages, mode: session.mode, scrollOffset: scrollOffset, searchQuery: searchQuery || undefined, columns: columns }) }), (showTerminalPane || terminalPaneAuto) && (_jsx(TerminalPane, { toolCalls: completedToolCalls, maxLines: 20 })), showTreePane && (_jsx(TreePane, { cwd: process.cwd(), onSelectFile: fp => {
|
|
1644
1656
|
// GAP-21: inject @filepath directly into InputBox via prefill state
|
|
1645
1657
|
const cwd = process.cwd();
|
|
1646
|
-
const rel = fp.startsWith(cwd
|
|
1658
|
+
const rel = fp.startsWith(`${cwd}/`) ? fp.slice(cwd.length + 1) : fp;
|
|
1647
1659
|
setInputPrefill(`@${rel} `);
|
|
1648
1660
|
} }))] }), isProcessing && !currentTurnHasOutput && (_jsxs(Box, { paddingX: 1, paddingY: 0, children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { color: "cyan", dimColor: true, children: [' ', "Thinking..."] })] })), visibleToolCalls.length > 0 && (_jsx(ToolCallDisplay, { toolCalls: visibleToolCalls, expanded: isProcessing })), permissionRequest && (_jsx(PermissionPrompt, { toolName: permissionRequest.tool, toolInput: permissionRequest.input, riskLevel: permissionRequest.riskLevel, onDecide: handlePermission })), pendingDeployConfirm && (_jsxs(Box, { flexDirection: "column", borderStyle: "double", borderColor: "red", paddingX: 2, paddingY: 1, children: [_jsx(Text, { bold: true, color: "red", children: "!! Switch to DEPLOY mode?" }), _jsx(Text, { children: " " }), _jsx(Text, { children: "DEPLOY mode enables destructive operations:" }), _jsx(Text, { dimColor: true, children: " terraform apply/destroy, kubectl delete, helm uninstall" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["Press ", _jsx(Text, { bold: true, color: "green", children: "y" }), " to confirm | ", _jsx(Text, { bold: true, color: "red", children: "n" }), " or Esc to cancel"] })] })), deployPreview && _jsx(DeployPreview, { preview: deployPreview, onDecide: handleDeployDecision }), fileDiffRequest && (_jsx(FileDiffModal, { request: {
|
|
1649
1661
|
...fileDiffRequest,
|
|
@@ -70,22 +70,30 @@ export function FileDiffModal({ request }) {
|
|
|
70
70
|
const total = diffLines.length;
|
|
71
71
|
const maxOffset = Math.max(0, total - VISIBLE_LINES);
|
|
72
72
|
useInput((input, key) => {
|
|
73
|
-
if (input === 'a')
|
|
73
|
+
if (input === 'a') {
|
|
74
74
|
request.onDecide('apply');
|
|
75
|
-
|
|
75
|
+
}
|
|
76
|
+
if (input === 'r') {
|
|
76
77
|
request.onDecide('reject');
|
|
77
|
-
|
|
78
|
+
}
|
|
79
|
+
if (input === 'A') {
|
|
78
80
|
request.onDecide('apply-all');
|
|
79
|
-
|
|
81
|
+
}
|
|
82
|
+
if (input === 'R') {
|
|
80
83
|
request.onDecide('reject-all');
|
|
81
|
-
|
|
84
|
+
}
|
|
85
|
+
if (key.upArrow) {
|
|
82
86
|
setScrollOffset(o => Math.max(0, o - 1));
|
|
83
|
-
|
|
87
|
+
}
|
|
88
|
+
if (key.downArrow) {
|
|
84
89
|
setScrollOffset(o => Math.min(maxOffset, o + 1));
|
|
85
|
-
|
|
90
|
+
}
|
|
91
|
+
if (key.pageUp) {
|
|
86
92
|
setScrollOffset(o => Math.max(0, o - VISIBLE_LINES));
|
|
87
|
-
|
|
93
|
+
}
|
|
94
|
+
if (key.pageDown) {
|
|
88
95
|
setScrollOffset(o => Math.min(maxOffset, o + VISIBLE_LINES));
|
|
96
|
+
}
|
|
89
97
|
});
|
|
90
98
|
const endLine = Math.min(scrollOffset + VISIBLE_LINES, total);
|
|
91
99
|
const displayLines = diffLines.slice(scrollOffset, endLine);
|
|
@@ -97,12 +105,15 @@ export function FileDiffModal({ request }) {
|
|
|
97
105
|
: '';
|
|
98
106
|
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [_jsxs(Text, { bold: true, color: "yellow", children: [request.toolName, progress, ": ", request.filePath, scrollIndicator] }), _jsx(Box, { flexDirection: "column", marginY: 1, children: displayLines.map((line, i) => {
|
|
99
107
|
let color;
|
|
100
|
-
if (line.startsWith('+') && !line.startsWith('+++'))
|
|
108
|
+
if (line.startsWith('+') && !line.startsWith('+++')) {
|
|
101
109
|
color = 'green';
|
|
102
|
-
|
|
110
|
+
}
|
|
111
|
+
else if (line.startsWith('-') && !line.startsWith('---')) {
|
|
103
112
|
color = 'red';
|
|
104
|
-
|
|
113
|
+
}
|
|
114
|
+
else if (line.startsWith('@@')) {
|
|
105
115
|
color = 'cyan';
|
|
116
|
+
}
|
|
106
117
|
return (_jsx(Text, { color: color, dimColor: !color, children: line }, i));
|
|
107
118
|
}) }), _jsx(Text, { dimColor: true, children: " [a] Apply [r] Reject [A] Apply all [R] Reject all [\u2191/\u2193] Scroll [PgUp/PgDn] Page" })] }));
|
|
108
119
|
}
|
package/dist/src/ui/HelpModal.js
CHANGED
|
@@ -2,8 +2,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Box, Text, useInput } from 'ink';
|
|
3
3
|
export function HelpModal({ onClose }) {
|
|
4
4
|
useInput((input, key) => {
|
|
5
|
-
if (key.escape || input === 'q' || input === '?')
|
|
5
|
+
if (key.escape || input === 'q' || input === '?') {
|
|
6
6
|
onClose();
|
|
7
|
+
}
|
|
7
8
|
});
|
|
8
9
|
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, marginY: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: " Nimbus DevOps Agent \u2014 Help " }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Slash Commands:" }), _jsx(Text, { children: " /help Show this help" }), _jsx(Text, { children: " /mode plan Switch to plan mode (read-only tools)" }), _jsx(Text, { children: " /mode build Switch to build mode (file + infra tools)" }), _jsx(Text, { children: " /mode deploy Switch to deploy mode (all tools + previews)" }), _jsx(Text, { children: " /clear Clear conversation history" }), _jsx(Text, { children: " /compact Compress context to free tokens" }), _jsx(Text, { children: " /context Show context window usage" }), _jsx(Text, { children: " /cost Show token usage and cost" }), _jsx(Text, { children: " /diff Show unstaged git diff" }), _jsx(Text, { children: " /init Regenerate NIMBUS.md project context" }), _jsx(Text, { children: " /model [name] Show or switch the active model" }), _jsx(Text, { children: " /models List all available provider models" }), _jsx(Text, { children: " /undo Undo last file change (snapshot)" }), _jsx(Text, { children: " /redo Redo last undone change" }), _jsx(Text, { children: " /sessions List active sessions" }), _jsx(Text, { children: " /new [name] Create a new session" }), _jsxs(Text, { children: [" /switch ", '<id>', " Switch to a different session"] }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "DevOps Tools Available:" }), _jsx(Text, { children: " terraform, kubectl, helm, cloud_discover, cost_estimate," }), _jsx(Text, { children: " drift_detect, deploy_preview, git, task (subagent)" }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Keyboard Shortcuts:" }), _jsx(Text, { children: " ? Open this help panel" }), _jsx(Text, { children: " Tab Cycle mode (plan \u2192 build \u2192 deploy)" }), _jsx(Text, { children: " Ctrl+R Search input history" }), _jsx(Text, { children: " Ctrl+C Interrupt or exit" }), _jsx(Text, { children: " Esc / q / ? Close this help" }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Press Esc, q, or ? to close" })] }));
|
|
9
10
|
}
|
package/dist/src/ui/InputBox.js
CHANGED
|
@@ -27,12 +27,14 @@ const HISTORY_FILE = join(homedir(), '.nimbus', 'input-history.json');
|
|
|
27
27
|
/** Load persisted input history from disk (returns [] on any error). */
|
|
28
28
|
function loadHistory() {
|
|
29
29
|
try {
|
|
30
|
-
if (!existsSync(HISTORY_FILE))
|
|
30
|
+
if (!existsSync(HISTORY_FILE)) {
|
|
31
31
|
return [];
|
|
32
|
+
}
|
|
32
33
|
const raw = readFileSync(HISTORY_FILE, 'utf-8');
|
|
33
34
|
const parsed = JSON.parse(raw);
|
|
34
|
-
if (Array.isArray(parsed))
|
|
35
|
+
if (Array.isArray(parsed)) {
|
|
35
36
|
return parsed.slice(-MAX_HISTORY);
|
|
37
|
+
}
|
|
36
38
|
}
|
|
37
39
|
catch { /* ignore */ }
|
|
38
40
|
return [];
|
|
@@ -41,8 +43,9 @@ function loadHistory() {
|
|
|
41
43
|
function saveHistory(entries) {
|
|
42
44
|
try {
|
|
43
45
|
const dir = join(homedir(), '.nimbus');
|
|
44
|
-
if (!existsSync(dir))
|
|
46
|
+
if (!existsSync(dir)) {
|
|
45
47
|
mkdirSync(dir, { recursive: true });
|
|
48
|
+
}
|
|
46
49
|
writeFileSync(HISTORY_FILE, JSON.stringify(entries.slice(-MAX_HISTORY)), 'utf-8');
|
|
47
50
|
}
|
|
48
51
|
catch { /* non-critical */ }
|
|
@@ -42,14 +42,16 @@ export const _parseContentCache = new Map();
|
|
|
42
42
|
const _PARSE_CACHE_MAX = 200;
|
|
43
43
|
function parseContent(raw) {
|
|
44
44
|
const cached = _parseContentCache.get(raw);
|
|
45
|
-
if (cached !== undefined)
|
|
45
|
+
if (cached !== undefined) {
|
|
46
46
|
return cached;
|
|
47
|
+
}
|
|
47
48
|
const result = _parseContentRaw(raw);
|
|
48
49
|
if (_parseContentCache.size >= _PARSE_CACHE_MAX) {
|
|
49
50
|
// FIFO eviction: delete the oldest entry
|
|
50
51
|
const firstKey = _parseContentCache.keys().next().value;
|
|
51
|
-
if (firstKey !== undefined)
|
|
52
|
+
if (firstKey !== undefined) {
|
|
52
53
|
_parseContentCache.delete(firstKey);
|
|
54
|
+
}
|
|
53
55
|
}
|
|
54
56
|
_parseContentCache.set(raw, result);
|
|
55
57
|
return result;
|
|
@@ -306,8 +308,9 @@ function tokenizeYamlLine(line) {
|
|
|
306
308
|
const keyMatch = line.match(/^(\s*)([\w\-./]+)(\s*:\s*)(.*)/);
|
|
307
309
|
if (keyMatch) {
|
|
308
310
|
const [, indent, key, colon, value] = keyMatch;
|
|
309
|
-
if (indent)
|
|
311
|
+
if (indent) {
|
|
310
312
|
tokens.push({ type: 'plain', text: indent });
|
|
313
|
+
}
|
|
311
314
|
tokens.push({ type: 'type', text: key });
|
|
312
315
|
tokens.push({ type: 'plain', text: colon });
|
|
313
316
|
if (value) {
|
|
@@ -328,8 +331,9 @@ function tokenizeYamlLine(line) {
|
|
|
328
331
|
const dashIdx = line.indexOf('-');
|
|
329
332
|
tokens.push({ type: 'plain', text: line.slice(0, dashIdx + 1) });
|
|
330
333
|
const rest = line.slice(dashIdx + 1);
|
|
331
|
-
if (rest.trim())
|
|
334
|
+
if (rest.trim()) {
|
|
332
335
|
tokens.push({ type: 'string', text: rest });
|
|
336
|
+
}
|
|
333
337
|
return tokens;
|
|
334
338
|
}
|
|
335
339
|
return [{ type: 'plain', text: line }];
|
|
@@ -343,8 +347,9 @@ function tokenizeJsonLine(line) {
|
|
|
343
347
|
if (line[i] === '"') {
|
|
344
348
|
let j = i + 1;
|
|
345
349
|
while (j < line.length && line[j] !== '"') {
|
|
346
|
-
if (line[j] === '\\')
|
|
350
|
+
if (line[j] === '\\') {
|
|
347
351
|
j++;
|
|
352
|
+
}
|
|
348
353
|
j++;
|
|
349
354
|
}
|
|
350
355
|
j = Math.min(j + 1, line.length);
|
|
@@ -363,8 +368,9 @@ function tokenizeJsonLine(line) {
|
|
|
363
368
|
// Numbers
|
|
364
369
|
if (/[0-9\-]/.test(line[i]) && (i === 0 || /[\s,\[{:]/.test(line[i - 1]))) {
|
|
365
370
|
let j = i;
|
|
366
|
-
while (j < line.length && /[0-9.\-eE+]/.test(line[j]))
|
|
371
|
+
while (j < line.length && /[0-9.\-eE+]/.test(line[j])) {
|
|
367
372
|
j++;
|
|
373
|
+
}
|
|
368
374
|
tokens.push({ type: 'number', text: line.slice(i, j) });
|
|
369
375
|
i = j;
|
|
370
376
|
continue;
|
|
@@ -372,8 +378,9 @@ function tokenizeJsonLine(line) {
|
|
|
372
378
|
// Keywords: true, false, null
|
|
373
379
|
if (/[a-z]/.test(line[i])) {
|
|
374
380
|
let j = i;
|
|
375
|
-
while (j < line.length && /[a-z]/.test(line[j]))
|
|
381
|
+
while (j < line.length && /[a-z]/.test(line[j])) {
|
|
376
382
|
j++;
|
|
383
|
+
}
|
|
377
384
|
const word = line.slice(i, j);
|
|
378
385
|
if (word === 'true' || word === 'false' || word === 'null') {
|
|
379
386
|
tokens.push({ type: 'type', text: word });
|
|
@@ -399,11 +406,13 @@ function tokenizeDockerfileLine(line) {
|
|
|
399
406
|
if (instrMatch && DOCKERFILE_INSTRUCTIONS.has(instrMatch[1])) {
|
|
400
407
|
const tokens = [];
|
|
401
408
|
const leadingSpaces = line.length - line.trimStart().length;
|
|
402
|
-
if (leadingSpaces > 0)
|
|
409
|
+
if (leadingSpaces > 0) {
|
|
403
410
|
tokens.push({ type: 'plain', text: line.slice(0, leadingSpaces) });
|
|
411
|
+
}
|
|
404
412
|
tokens.push({ type: 'keyword', text: instrMatch[1] });
|
|
405
|
-
if (instrMatch[2])
|
|
413
|
+
if (instrMatch[2]) {
|
|
406
414
|
tokens.push({ type: 'plain', text: instrMatch[2] });
|
|
415
|
+
}
|
|
407
416
|
return tokens;
|
|
408
417
|
}
|
|
409
418
|
return [{ type: 'plain', text: line }];
|
|
@@ -423,13 +432,15 @@ function tokenizeBashLine(line, keywords) {
|
|
|
423
432
|
let j = i + 1;
|
|
424
433
|
if (line[j] === '{') {
|
|
425
434
|
j++;
|
|
426
|
-
while (j < line.length && line[j] !== '}')
|
|
435
|
+
while (j < line.length && line[j] !== '}') {
|
|
427
436
|
j++;
|
|
437
|
+
}
|
|
428
438
|
j = Math.min(j + 1, line.length);
|
|
429
439
|
}
|
|
430
440
|
else {
|
|
431
|
-
while (j < line.length && /[a-zA-Z0-9_]/.test(line[j]))
|
|
441
|
+
while (j < line.length && /[a-zA-Z0-9_]/.test(line[j])) {
|
|
432
442
|
j++;
|
|
443
|
+
}
|
|
433
444
|
}
|
|
434
445
|
tokens.push({ type: 'number', text: line.slice(i, j) });
|
|
435
446
|
i = j;
|
|
@@ -440,8 +451,9 @@ function tokenizeBashLine(line, keywords) {
|
|
|
440
451
|
const quote = line[i];
|
|
441
452
|
let j = i + 1;
|
|
442
453
|
while (j < line.length && line[j] !== quote) {
|
|
443
|
-
if (line[j] === '\\')
|
|
454
|
+
if (line[j] === '\\') {
|
|
444
455
|
j++;
|
|
456
|
+
}
|
|
445
457
|
j++;
|
|
446
458
|
}
|
|
447
459
|
j = Math.min(j + 1, line.length);
|
|
@@ -452,8 +464,9 @@ function tokenizeBashLine(line, keywords) {
|
|
|
452
464
|
// Flags (words starting with -)
|
|
453
465
|
if (line[i] === '-' && i > 0 && /\s/.test(line[i - 1])) {
|
|
454
466
|
let j = i;
|
|
455
|
-
while (j < line.length && !/\s/.test(line[j]))
|
|
467
|
+
while (j < line.length && !/\s/.test(line[j])) {
|
|
456
468
|
j++;
|
|
469
|
+
}
|
|
457
470
|
tokens.push({ type: 'comment', text: line.slice(i, j) });
|
|
458
471
|
i = j;
|
|
459
472
|
continue;
|
|
@@ -461,8 +474,9 @@ function tokenizeBashLine(line, keywords) {
|
|
|
461
474
|
// Words / keywords
|
|
462
475
|
if (/[a-zA-Z_]/.test(line[i])) {
|
|
463
476
|
let j = i;
|
|
464
|
-
while (j < line.length && /[a-zA-Z0-9_]/.test(line[j]))
|
|
477
|
+
while (j < line.length && /[a-zA-Z0-9_]/.test(line[j])) {
|
|
465
478
|
j++;
|
|
479
|
+
}
|
|
466
480
|
const word = line.slice(i, j);
|
|
467
481
|
if (keywords.has(word)) {
|
|
468
482
|
tokens.push({ type: 'keyword', text: word });
|
|
@@ -485,14 +499,18 @@ function tokenizeLine(line, keywords, types, language) {
|
|
|
485
499
|
// Language-specific fast paths
|
|
486
500
|
if (language) {
|
|
487
501
|
const lang = language.toLowerCase();
|
|
488
|
-
if (lang === 'yaml' || lang === 'yml')
|
|
502
|
+
if (lang === 'yaml' || lang === 'yml') {
|
|
489
503
|
return tokenizeYamlLine(line);
|
|
490
|
-
|
|
504
|
+
}
|
|
505
|
+
if (lang === 'json') {
|
|
491
506
|
return tokenizeJsonLine(line);
|
|
492
|
-
|
|
507
|
+
}
|
|
508
|
+
if (lang === 'dockerfile' || lang === 'docker') {
|
|
493
509
|
return tokenizeDockerfileLine(line);
|
|
494
|
-
|
|
510
|
+
}
|
|
511
|
+
if (lang === 'bash' || lang === 'sh' || lang === 'shell' || lang === 'zsh') {
|
|
495
512
|
return tokenizeBashLine(line, keywords);
|
|
513
|
+
}
|
|
496
514
|
}
|
|
497
515
|
// Generic tokenizer below
|
|
498
516
|
/* falls through */
|
|
@@ -669,8 +687,9 @@ function MessageBody({ content }) {
|
|
|
669
687
|
* Returns a React node with matching portions highlighted.
|
|
670
688
|
*/
|
|
671
689
|
function highlightText(text, query) {
|
|
672
|
-
if (!query || !text.toLowerCase().includes(query.toLowerCase()))
|
|
690
|
+
if (!query || !text.toLowerCase().includes(query.toLowerCase())) {
|
|
673
691
|
return text;
|
|
692
|
+
}
|
|
674
693
|
const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
675
694
|
const parts = text.split(new RegExp(`(${escaped})`, 'gi'));
|
|
676
695
|
return (_jsx(_Fragment, { children: parts.map((p, i) => p.toLowerCase() === query.toLowerCase() ? (_jsx(Text, { color: "yellow", bold: true, children: p }, i)) : (_jsx(Text, { children: p }, i))) }));
|
|
@@ -682,8 +701,9 @@ function highlightText(text, query) {
|
|
|
682
701
|
*/
|
|
683
702
|
function parseSubagentTag(content) {
|
|
684
703
|
const match = content.match(/^\[subagent:(\w+)\]/);
|
|
685
|
-
if (match)
|
|
704
|
+
if (match) {
|
|
686
705
|
return match[1];
|
|
706
|
+
}
|
|
687
707
|
return null;
|
|
688
708
|
}
|
|
689
709
|
/**
|
|
@@ -16,8 +16,9 @@ export function TerminalPane({ toolCalls, maxLines = 20 }) {
|
|
|
16
16
|
}
|
|
17
17
|
continue;
|
|
18
18
|
}
|
|
19
|
-
if (tc.status !== 'completed' && tc.status !== 'failed')
|
|
19
|
+
if (tc.status !== 'completed' && tc.status !== 'failed') {
|
|
20
20
|
continue;
|
|
21
|
+
}
|
|
21
22
|
const output = tc.result?.output ?? '';
|
|
22
23
|
const isError = tc.result?.isError ?? false;
|
|
23
24
|
const lines = output.split('\n').filter(l => l.length > 0);
|
|
@@ -34,8 +34,9 @@ const LONG_RUNNING_TOOLS = new Set([
|
|
|
34
34
|
function StatusBadge({ status, startTime }) {
|
|
35
35
|
const [elapsed, setElapsed] = useState(0);
|
|
36
36
|
useEffect(() => {
|
|
37
|
-
if (status !== 'running' || !startTime)
|
|
37
|
+
if (status !== 'running' || !startTime) {
|
|
38
38
|
return;
|
|
39
|
+
}
|
|
39
40
|
const initial = Math.floor((Date.now() - startTime) / 1000);
|
|
40
41
|
setElapsed(initial);
|
|
41
42
|
const id = setInterval(() => {
|
|
@@ -200,8 +201,9 @@ function DockerBuildBody({ input, result, streamingOutput, }) {
|
|
|
200
201
|
if (result && !result.isError) {
|
|
201
202
|
return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: result.output.split('\n').slice(0, 20).map((line, i) => (_jsx(Text, { dimColor: true, children: line }, i))) }));
|
|
202
203
|
}
|
|
203
|
-
if (result?.isError)
|
|
204
|
+
if (result?.isError) {
|
|
204
205
|
return _jsx(Text, { color: "red", children: result.output });
|
|
206
|
+
}
|
|
205
207
|
return null;
|
|
206
208
|
}
|
|
207
209
|
// Parse step progress from streaming output or result
|
|
@@ -272,18 +274,22 @@ function ToolCallBox({ toolCall, expanded }) {
|
|
|
272
274
|
const windowSize = isTerraformOrKubectl ? 60 : 40;
|
|
273
275
|
const visibleLines = allLines.slice(-windowSize);
|
|
274
276
|
// Pad to minimum 4 lines so the live area is always visible
|
|
275
|
-
while (visibleLines.length < 4)
|
|
277
|
+
while (visibleLines.length < 4) {
|
|
276
278
|
visibleLines.push('');
|
|
279
|
+
}
|
|
277
280
|
const hiddenCount = Math.max(0, allLines.length - windowSize);
|
|
278
281
|
return (_jsxs(_Fragment, { children: [hiddenCount > 0 && (_jsxs(Text, { dimColor: true, children: ["... ", hiddenCount, " earlier lines"] })), visibleLines.map((line, i) => {
|
|
279
282
|
// M2: Color terraform/kubectl streaming output lines
|
|
280
283
|
let lineColor;
|
|
281
|
-
if (line.match(/^\s*\+/) || line.includes('will be created') || line.includes(' created'))
|
|
284
|
+
if (line.match(/^\s*\+/) || line.includes('will be created') || line.includes(' created')) {
|
|
282
285
|
lineColor = 'green';
|
|
283
|
-
|
|
286
|
+
}
|
|
287
|
+
else if (line.match(/^\s*-/) || line.includes('will be destroyed') || line.includes(' destroyed')) {
|
|
284
288
|
lineColor = 'red';
|
|
285
|
-
|
|
289
|
+
}
|
|
290
|
+
else if (line.match(/^\s*~/) || line.includes('will be updated') || line.includes(' modified')) {
|
|
286
291
|
lineColor = 'yellow';
|
|
292
|
+
}
|
|
287
293
|
return _jsx(Text, { color: lineColor ?? 'gray', dimColor: !lineColor, children: line }, i);
|
|
288
294
|
})] }));
|
|
289
295
|
})()] })), _jsx(Box, { marginTop: 1, children: renderBody() })] }));
|
package/dist/src/ui/TreePane.js
CHANGED
|
@@ -10,8 +10,9 @@ import { Box, Text, useInput } from 'ink';
|
|
|
10
10
|
import * as fs from 'node:fs';
|
|
11
11
|
import * as path from 'node:path';
|
|
12
12
|
function buildTree(dir, depth, maxDepth) {
|
|
13
|
-
if (depth > maxDepth)
|
|
13
|
+
if (depth > maxDepth) {
|
|
14
14
|
return [];
|
|
15
|
+
}
|
|
15
16
|
try {
|
|
16
17
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
17
18
|
return entries
|
package/dist/src/ui/ink/index.js
CHANGED
|
@@ -129,10 +129,11 @@ export async function startInkChat(options = {}) {
|
|
|
129
129
|
// NIMBUS.md live reload (M10): watch for changes to NIMBUS.md mid-session
|
|
130
130
|
// M5: Also notify on DevOps file changes (debounced 30s per file)
|
|
131
131
|
const devopsChangeDebounce = new Map();
|
|
132
|
-
watcher.on('change', (
|
|
133
|
-
|
|
132
|
+
watcher.on('change', (event) => {
|
|
133
|
+
const filePath = event.path;
|
|
134
|
+
if (filePath.endsWith('NIMBUS.md')) {
|
|
134
135
|
try {
|
|
135
|
-
nimbusInstructions = readFileSync(
|
|
136
|
+
nimbusInstructions = readFileSync(filePath, 'utf-8');
|
|
136
137
|
addMessage({
|
|
137
138
|
id: crypto.randomUUID(),
|
|
138
139
|
role: 'system',
|
|
@@ -145,15 +146,15 @@ export async function startInkChat(options = {}) {
|
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
148
|
// M5: Notify on DevOps file changes (debounced 30s per file)
|
|
148
|
-
const filePath = typeof changedPath === 'string' ? changedPath : changedPath?.path ?? '';
|
|
149
149
|
const isDevOps = /\.(tf|yaml|yml)$|Dockerfile|docker-compose/i.test(filePath);
|
|
150
150
|
if (isDevOps) {
|
|
151
151
|
const existing = devopsChangeDebounce.get(filePath);
|
|
152
|
-
if (existing)
|
|
152
|
+
if (existing) {
|
|
153
153
|
clearTimeout(existing);
|
|
154
|
+
}
|
|
154
155
|
const timer = setTimeout(() => {
|
|
155
156
|
devopsChangeDebounce.delete(filePath);
|
|
156
|
-
const relPath = filePath.replace(process.cwd()
|
|
157
|
+
const relPath = filePath.replace(`${process.cwd()}/`, '');
|
|
157
158
|
const hint = relPath.endsWith('.tf') ? '/plan' : relPath.includes('yaml') ? '/plan' : '/init';
|
|
158
159
|
addMessage({
|
|
159
160
|
id: crypto.randomUUID(),
|
|
@@ -383,8 +384,9 @@ export async function startInkChat(options = {}) {
|
|
|
383
384
|
* Covers terraform/kubectl/helm plus destructive bash cloud CLI commands.
|
|
384
385
|
*/
|
|
385
386
|
function requiresDeployPreview(toolName, toolInput) {
|
|
386
|
-
if (['terraform', 'kubectl', 'helm'].includes(toolName))
|
|
387
|
+
if (['terraform', 'kubectl', 'helm'].includes(toolName)) {
|
|
387
388
|
return true;
|
|
389
|
+
}
|
|
388
390
|
if (toolName === 'docker') {
|
|
389
391
|
const action = String(toolInput.action ?? '');
|
|
390
392
|
return ['build', 'push', 'stop', 'compose-up', 'compose-down', 'rm', 'prune'].includes(action);
|
|
@@ -447,12 +449,14 @@ export async function startInkChat(options = {}) {
|
|
|
447
449
|
function parseToolTimeouts(nimbusMd) {
|
|
448
450
|
const result = {};
|
|
449
451
|
const match = nimbusMd.match(/##\s+Tool Timeouts\s*\n([\s\S]*?)(?=##|$)/);
|
|
450
|
-
if (!match)
|
|
452
|
+
if (!match) {
|
|
451
453
|
return result;
|
|
454
|
+
}
|
|
452
455
|
for (const line of match[1].split('\n')) {
|
|
453
456
|
const m = line.match(/^\s*([a-z_]+)\s*:\s*(\d+)\s*$/);
|
|
454
|
-
if (m)
|
|
457
|
+
if (m) {
|
|
455
458
|
result[m[1]] = parseInt(m[2], 10);
|
|
459
|
+
}
|
|
456
460
|
}
|
|
457
461
|
return result;
|
|
458
462
|
}
|
|
@@ -476,8 +480,9 @@ export async function startInkChat(options = {}) {
|
|
|
476
480
|
if (userMessageCount === 1 && sessionManager && sessionId) {
|
|
477
481
|
try {
|
|
478
482
|
const semanticName = text.slice(0, 40).replace(/[^a-z0-9]+/gi, '-').toLowerCase().replace(/^-+|-+$/g, '');
|
|
479
|
-
if (semanticName)
|
|
483
|
+
if (semanticName) {
|
|
480
484
|
sessionManager.rename(sessionId, semanticName);
|
|
485
|
+
}
|
|
481
486
|
}
|
|
482
487
|
catch { /* non-critical */ }
|
|
483
488
|
}
|
|
@@ -839,16 +844,18 @@ export async function startInkChat(options = {}) {
|
|
|
839
844
|
const full = spawnSync('git', ['diff'], { encoding: 'utf-8', cwd: process.cwd() });
|
|
840
845
|
const statOut = stat.stdout?.trim() ?? '';
|
|
841
846
|
const fullOut = full.stdout?.trim() ?? '';
|
|
842
|
-
if (!statOut && !fullOut)
|
|
847
|
+
if (!statOut && !fullOut) {
|
|
843
848
|
return 'No unstaged changes.';
|
|
849
|
+
}
|
|
844
850
|
return [statOut, fullOut].filter(Boolean).join('\n\n');
|
|
845
851
|
};
|
|
846
852
|
/**
|
|
847
853
|
* Handle /cost command — show per-turn cost breakdown.
|
|
848
854
|
*/
|
|
849
855
|
const onCost = () => {
|
|
850
|
-
if (turnCostLog.length === 0)
|
|
856
|
+
if (turnCostLog.length === 0) {
|
|
851
857
|
return 'No turns yet.';
|
|
858
|
+
}
|
|
852
859
|
const rows = turnCostLog.map(t => ` Turn ${t.turn} ${t.tokens.toLocaleString()} tokens $${t.costUSD.toFixed(4)}`);
|
|
853
860
|
const total = turnCostLog.reduce((s, t) => s + t.costUSD, 0);
|
|
854
861
|
const totalTok = turnCostLog.reduce((s, t) => s + t.tokens, 0);
|
|
@@ -1089,26 +1096,35 @@ export async function startInkChat(options = {}) {
|
|
|
1089
1096
|
}
|
|
1090
1097
|
// GAP-17: context-aware suggestions based on detected infrastructure
|
|
1091
1098
|
const suggestions = [];
|
|
1092
|
-
if (currentInfraContext?.terraformWorkspace)
|
|
1099
|
+
if (currentInfraContext?.terraformWorkspace) {
|
|
1093
1100
|
suggestions.push(`"check for drift in workspace ${currentInfraContext.terraformWorkspace}"`);
|
|
1094
|
-
|
|
1101
|
+
}
|
|
1102
|
+
if (currentInfraContext?.kubectlContext) {
|
|
1095
1103
|
suggestions.push(`"show all pods in ${currentInfraContext.kubectlContext}"`);
|
|
1096
|
-
|
|
1104
|
+
}
|
|
1105
|
+
if (currentInfraContext?.awsAccount) {
|
|
1097
1106
|
suggestions.push(`"show AWS costs for this month"`);
|
|
1098
|
-
|
|
1107
|
+
}
|
|
1108
|
+
if ((currentInfraContext?.helmReleases?.length ?? 0) > 0) {
|
|
1099
1109
|
suggestions.push(`"show helm release history for ${currentInfraContext.helmReleases[0]}"`);
|
|
1110
|
+
}
|
|
1100
1111
|
// H5: Build one-line infra hint for cold start
|
|
1101
1112
|
const infraHintParts = [];
|
|
1102
|
-
if (currentInfraContext?.terraformWorkspace)
|
|
1113
|
+
if (currentInfraContext?.terraformWorkspace) {
|
|
1103
1114
|
infraHintParts.push(`tf:${currentInfraContext.terraformWorkspace}`);
|
|
1104
|
-
|
|
1115
|
+
}
|
|
1116
|
+
if (currentInfraContext?.kubectlContext) {
|
|
1105
1117
|
infraHintParts.push(`k8s:${currentInfraContext.kubectlContext}`);
|
|
1106
|
-
|
|
1118
|
+
}
|
|
1119
|
+
if (currentInfraContext?.awsAccount) {
|
|
1107
1120
|
infraHintParts.push(`aws:${currentInfraContext.awsAccount}`);
|
|
1108
|
-
|
|
1121
|
+
}
|
|
1122
|
+
if (currentInfraContext?.gcpProject) {
|
|
1109
1123
|
infraHintParts.push(`gcp:${currentInfraContext.gcpProject}`);
|
|
1110
|
-
|
|
1124
|
+
}
|
|
1125
|
+
if ((currentInfraContext?.helmReleases?.length ?? 0) > 0) {
|
|
1111
1126
|
infraHintParts.push(`${currentInfraContext.helmReleases.length} helm release${currentInfraContext.helmReleases.length > 1 ? 's' : ''}`);
|
|
1127
|
+
}
|
|
1112
1128
|
const infraHintLine = infraHintParts.length > 0 ? `Infra detected: ${infraHintParts.join(' | ')}` : '';
|
|
1113
1129
|
// G24: DevOps-specific quick-start examples
|
|
1114
1130
|
// M3: When no NIMBUS.md, show concrete DevOps prompt examples to reduce blank-prompt friction
|
package/dist/src/version.js
CHANGED