@build-astron-co/nimbus 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +628 -0
- package/bin/nimbus +38 -0
- package/package.json +80 -0
- package/src/__tests__/app.test.ts +76 -0
- package/src/__tests__/audit.test.ts +877 -0
- package/src/__tests__/circuit-breaker.test.ts +116 -0
- package/src/__tests__/cli-run.test.ts +115 -0
- package/src/__tests__/context-manager.test.ts +502 -0
- package/src/__tests__/context.test.ts +242 -0
- package/src/__tests__/enterprise.test.ts +401 -0
- package/src/__tests__/generator.test.ts +433 -0
- package/src/__tests__/hooks.test.ts +582 -0
- package/src/__tests__/init.test.ts +436 -0
- package/src/__tests__/intent-parser.test.ts +229 -0
- package/src/__tests__/llm-router.test.ts +209 -0
- package/src/__tests__/lsp.test.ts +293 -0
- package/src/__tests__/modes.test.ts +336 -0
- package/src/__tests__/permissions.test.ts +338 -0
- package/src/__tests__/serve.test.ts +275 -0
- package/src/__tests__/sessions.test.ts +227 -0
- package/src/__tests__/sharing.test.ts +288 -0
- package/src/__tests__/snapshots.test.ts +581 -0
- package/src/__tests__/state-db.test.ts +334 -0
- package/src/__tests__/stream-with-tools.test.ts +732 -0
- package/src/__tests__/subagents.test.ts +176 -0
- package/src/__tests__/system-prompt.test.ts +169 -0
- package/src/__tests__/tool-converter.test.ts +256 -0
- package/src/__tests__/tool-schemas.test.ts +397 -0
- package/src/__tests__/tools.test.ts +143 -0
- package/src/__tests__/version.test.ts +49 -0
- package/src/agent/compaction-agent.ts +227 -0
- package/src/agent/context-manager.ts +435 -0
- package/src/agent/context.ts +427 -0
- package/src/agent/deploy-preview.ts +426 -0
- package/src/agent/index.ts +68 -0
- package/src/agent/loop.ts +717 -0
- package/src/agent/modes.ts +429 -0
- package/src/agent/permissions.ts +466 -0
- package/src/agent/subagents/base.ts +116 -0
- package/src/agent/subagents/cost.ts +51 -0
- package/src/agent/subagents/explore.ts +42 -0
- package/src/agent/subagents/general.ts +54 -0
- package/src/agent/subagents/index.ts +102 -0
- package/src/agent/subagents/infra.ts +59 -0
- package/src/agent/subagents/security.ts +69 -0
- package/src/agent/system-prompt.ts +436 -0
- package/src/app.ts +122 -0
- package/src/audit/activity-log.ts +290 -0
- package/src/audit/compliance-checker.ts +540 -0
- package/src/audit/cost-tracker.ts +318 -0
- package/src/audit/index.ts +23 -0
- package/src/audit/security-scanner.ts +596 -0
- package/src/auth/guard.ts +75 -0
- package/src/auth/index.ts +56 -0
- package/src/auth/oauth.ts +455 -0
- package/src/auth/providers.ts +470 -0
- package/src/auth/sso.ts +113 -0
- package/src/auth/store.ts +505 -0
- package/src/auth/types.ts +187 -0
- package/src/build.ts +141 -0
- package/src/cli/index.ts +16 -0
- package/src/cli/init.ts +854 -0
- package/src/cli/openapi-spec.ts +356 -0
- package/src/cli/run.ts +237 -0
- package/src/cli/serve-auth.ts +80 -0
- package/src/cli/serve.ts +462 -0
- package/src/cli/web.ts +67 -0
- package/src/cli.ts +1417 -0
- package/src/clients/core-engine-client.ts +227 -0
- package/src/clients/enterprise-client.ts +334 -0
- package/src/clients/generator-client.ts +351 -0
- package/src/clients/git-client.ts +627 -0
- package/src/clients/github-client.ts +410 -0
- package/src/clients/helm-client.ts +504 -0
- package/src/clients/index.ts +80 -0
- package/src/clients/k8s-client.ts +497 -0
- package/src/clients/llm-client.ts +161 -0
- package/src/clients/rest-client.ts +130 -0
- package/src/clients/service-discovery.ts +33 -0
- package/src/clients/terraform-client.ts +482 -0
- package/src/clients/tools-client.ts +1843 -0
- package/src/clients/ws-client.ts +115 -0
- package/src/commands/analyze/index.ts +352 -0
- package/src/commands/apply/helm.ts +473 -0
- package/src/commands/apply/index.ts +213 -0
- package/src/commands/apply/k8s.ts +454 -0
- package/src/commands/apply/terraform.ts +582 -0
- package/src/commands/ask.ts +167 -0
- package/src/commands/audit/index.ts +238 -0
- package/src/commands/auth-cloud.ts +294 -0
- package/src/commands/auth-list.ts +134 -0
- package/src/commands/auth-profile.ts +121 -0
- package/src/commands/auth-status.ts +141 -0
- package/src/commands/aws/ec2.ts +501 -0
- package/src/commands/aws/iam.ts +397 -0
- package/src/commands/aws/index.ts +133 -0
- package/src/commands/aws/lambda.ts +396 -0
- package/src/commands/aws/rds.ts +439 -0
- package/src/commands/aws/s3.ts +439 -0
- package/src/commands/aws/vpc.ts +393 -0
- package/src/commands/aws-discover.ts +649 -0
- package/src/commands/aws-terraform.ts +805 -0
- package/src/commands/azure/aks.ts +376 -0
- package/src/commands/azure/functions.ts +253 -0
- package/src/commands/azure/index.ts +116 -0
- package/src/commands/azure/storage.ts +478 -0
- package/src/commands/azure/vm.ts +355 -0
- package/src/commands/billing/index.ts +256 -0
- package/src/commands/chat.ts +314 -0
- package/src/commands/config.ts +346 -0
- package/src/commands/cost/cloud-cost-estimator.ts +266 -0
- package/src/commands/cost/estimator.ts +79 -0
- package/src/commands/cost/index.ts +594 -0
- package/src/commands/cost/parsers/terraform.ts +273 -0
- package/src/commands/cost/parsers/types.ts +25 -0
- package/src/commands/cost/pricing/aws.ts +544 -0
- package/src/commands/cost/pricing/azure.ts +499 -0
- package/src/commands/cost/pricing/gcp.ts +396 -0
- package/src/commands/cost/pricing/index.ts +40 -0
- package/src/commands/demo.ts +250 -0
- package/src/commands/doctor.ts +794 -0
- package/src/commands/drift/index.ts +439 -0
- package/src/commands/explain.ts +277 -0
- package/src/commands/feedback.ts +389 -0
- package/src/commands/fix.ts +324 -0
- package/src/commands/fs/index.ts +402 -0
- package/src/commands/gcp/compute.ts +325 -0
- package/src/commands/gcp/functions.ts +271 -0
- package/src/commands/gcp/gke.ts +438 -0
- package/src/commands/gcp/iam.ts +344 -0
- package/src/commands/gcp/index.ts +129 -0
- package/src/commands/gcp/storage.ts +284 -0
- package/src/commands/generate-helm.ts +1249 -0
- package/src/commands/generate-k8s.ts +1560 -0
- package/src/commands/generate-terraform.ts +1460 -0
- package/src/commands/gh/index.ts +863 -0
- package/src/commands/git/index.ts +1343 -0
- package/src/commands/helm/index.ts +1126 -0
- package/src/commands/help.ts +539 -0
- package/src/commands/history.ts +142 -0
- package/src/commands/import.ts +868 -0
- package/src/commands/index.ts +367 -0
- package/src/commands/init.ts +1046 -0
- package/src/commands/k8s/index.ts +1137 -0
- package/src/commands/login.ts +631 -0
- package/src/commands/logout.ts +83 -0
- package/src/commands/onboarding.ts +228 -0
- package/src/commands/plan/display.ts +279 -0
- package/src/commands/plan/index.ts +599 -0
- package/src/commands/preview.ts +452 -0
- package/src/commands/questionnaire.ts +1270 -0
- package/src/commands/resume.ts +55 -0
- package/src/commands/team/index.ts +346 -0
- package/src/commands/template.ts +232 -0
- package/src/commands/tf/index.ts +1034 -0
- package/src/commands/upgrade.ts +550 -0
- package/src/commands/usage/index.ts +134 -0
- package/src/commands/version.ts +170 -0
- package/src/compat/index.ts +2 -0
- package/src/compat/runtime.ts +12 -0
- package/src/compat/sqlite.ts +107 -0
- package/src/config/index.ts +17 -0
- package/src/config/manager.ts +530 -0
- package/src/config/safety-policy.ts +358 -0
- package/src/config/schema.ts +125 -0
- package/src/config/types.ts +527 -0
- package/src/context/context-db.ts +199 -0
- package/src/demo/index.ts +349 -0
- package/src/demo/scenarios/full-journey.ts +229 -0
- package/src/demo/scenarios/getting-started.ts +127 -0
- package/src/demo/scenarios/helm-release.ts +341 -0
- package/src/demo/scenarios/k8s-deployment.ts +194 -0
- package/src/demo/scenarios/terraform-vpc.ts +170 -0
- package/src/demo/types.ts +92 -0
- package/src/engine/cost-estimator.ts +438 -0
- package/src/engine/diagram-generator.ts +256 -0
- package/src/engine/drift-detector.ts +902 -0
- package/src/engine/executor.ts +1035 -0
- package/src/engine/index.ts +76 -0
- package/src/engine/orchestrator.ts +636 -0
- package/src/engine/planner.ts +720 -0
- package/src/engine/safety.ts +743 -0
- package/src/engine/verifier.ts +770 -0
- package/src/enterprise/audit.ts +348 -0
- package/src/enterprise/auth.ts +270 -0
- package/src/enterprise/billing.ts +822 -0
- package/src/enterprise/index.ts +17 -0
- package/src/enterprise/teams.ts +443 -0
- package/src/generator/best-practices.ts +1608 -0
- package/src/generator/helm.ts +630 -0
- package/src/generator/index.ts +37 -0
- package/src/generator/intent-parser.ts +514 -0
- package/src/generator/kubernetes.ts +976 -0
- package/src/generator/terraform.ts +1867 -0
- package/src/history/index.ts +8 -0
- package/src/history/manager.ts +322 -0
- package/src/history/types.ts +34 -0
- package/src/hooks/config.ts +432 -0
- package/src/hooks/engine.ts +391 -0
- package/src/hooks/index.ts +4 -0
- package/src/llm/auth-bridge.ts +198 -0
- package/src/llm/circuit-breaker.ts +140 -0
- package/src/llm/config-loader.ts +201 -0
- package/src/llm/cost-calculator.ts +171 -0
- package/src/llm/index.ts +8 -0
- package/src/llm/model-aliases.ts +115 -0
- package/src/llm/provider-registry.ts +63 -0
- package/src/llm/providers/anthropic.ts +433 -0
- package/src/llm/providers/bedrock.ts +477 -0
- package/src/llm/providers/google.ts +405 -0
- package/src/llm/providers/ollama.ts +767 -0
- package/src/llm/providers/openai-compatible.ts +340 -0
- package/src/llm/providers/openai.ts +328 -0
- package/src/llm/providers/openrouter.ts +338 -0
- package/src/llm/router.ts +1035 -0
- package/src/llm/types.ts +232 -0
- package/src/lsp/client.ts +298 -0
- package/src/lsp/languages.ts +116 -0
- package/src/lsp/manager.ts +278 -0
- package/src/mcp/client.ts +402 -0
- package/src/mcp/index.ts +5 -0
- package/src/mcp/manager.ts +133 -0
- package/src/nimbus.ts +214 -0
- package/src/plugins/index.ts +27 -0
- package/src/plugins/loader.ts +334 -0
- package/src/plugins/manager.ts +376 -0
- package/src/plugins/types.ts +284 -0
- package/src/scanners/cicd-scanner.ts +258 -0
- package/src/scanners/cloud-scanner.ts +466 -0
- package/src/scanners/framework-scanner.ts +469 -0
- package/src/scanners/iac-scanner.ts +388 -0
- package/src/scanners/index.ts +539 -0
- package/src/scanners/language-scanner.ts +276 -0
- package/src/scanners/package-manager-scanner.ts +277 -0
- package/src/scanners/types.ts +172 -0
- package/src/sessions/manager.ts +365 -0
- package/src/sessions/types.ts +44 -0
- package/src/sharing/sync.ts +296 -0
- package/src/sharing/viewer.ts +97 -0
- package/src/snapshots/index.ts +2 -0
- package/src/snapshots/manager.ts +530 -0
- package/src/state/artifacts.ts +147 -0
- package/src/state/audit.ts +137 -0
- package/src/state/billing.ts +240 -0
- package/src/state/checkpoints.ts +117 -0
- package/src/state/config.ts +67 -0
- package/src/state/conversations.ts +14 -0
- package/src/state/credentials.ts +154 -0
- package/src/state/db.ts +58 -0
- package/src/state/index.ts +26 -0
- package/src/state/messages.ts +115 -0
- package/src/state/projects.ts +123 -0
- package/src/state/schema.ts +236 -0
- package/src/state/sessions.ts +147 -0
- package/src/state/teams.ts +200 -0
- package/src/telemetry.ts +108 -0
- package/src/tools/aws-ops.ts +952 -0
- package/src/tools/azure-ops.ts +579 -0
- package/src/tools/file-ops.ts +593 -0
- package/src/tools/gcp-ops.ts +625 -0
- package/src/tools/git-ops.ts +773 -0
- package/src/tools/github-ops.ts +799 -0
- package/src/tools/helm-ops.ts +943 -0
- package/src/tools/index.ts +17 -0
- package/src/tools/k8s-ops.ts +819 -0
- package/src/tools/schemas/converter.ts +184 -0
- package/src/tools/schemas/devops.ts +612 -0
- package/src/tools/schemas/index.ts +73 -0
- package/src/tools/schemas/standard.ts +1144 -0
- package/src/tools/schemas/types.ts +705 -0
- package/src/tools/terraform-ops.ts +862 -0
- package/src/types/ambient.d.ts +193 -0
- package/src/types/config.ts +83 -0
- package/src/types/drift.ts +116 -0
- package/src/types/enterprise.ts +335 -0
- package/src/types/index.ts +20 -0
- package/src/types/plan.ts +44 -0
- package/src/types/request.ts +65 -0
- package/src/types/response.ts +54 -0
- package/src/types/service.ts +51 -0
- package/src/ui/App.tsx +997 -0
- package/src/ui/DeployPreview.tsx +169 -0
- package/src/ui/Header.tsx +68 -0
- package/src/ui/InputBox.tsx +350 -0
- package/src/ui/MessageList.tsx +585 -0
- package/src/ui/PermissionPrompt.tsx +151 -0
- package/src/ui/StatusBar.tsx +158 -0
- package/src/ui/ToolCallDisplay.tsx +409 -0
- package/src/ui/chat-ui.ts +853 -0
- package/src/ui/index.ts +33 -0
- package/src/ui/ink/index.ts +711 -0
- package/src/ui/streaming.ts +176 -0
- package/src/ui/types.ts +57 -0
- package/src/utils/analytics.ts +72 -0
- package/src/utils/cost-warning.ts +27 -0
- package/src/utils/env.ts +46 -0
- package/src/utils/errors.ts +69 -0
- package/src/utils/event-bus.ts +38 -0
- package/src/utils/index.ts +24 -0
- package/src/utils/logger.ts +171 -0
- package/src/utils/rate-limiter.ts +121 -0
- package/src/utils/service-auth.ts +49 -0
- package/src/utils/validation.ts +53 -0
- package/src/version.ts +4 -0
- package/src/watcher/index.ts +163 -0
- package/src/wizard/approval.ts +383 -0
- package/src/wizard/index.ts +25 -0
- package/src/wizard/prompts.ts +338 -0
- package/src/wizard/types.ts +171 -0
- package/src/wizard/ui.ts +556 -0
- package/src/wizard/wizard.ts +304 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System Prompt Builder for the Nimbus Agentic Loop
|
|
3
|
+
*
|
|
4
|
+
* Generates the complete system prompt that is injected as the first message
|
|
5
|
+
* in every LLM conversation. The prompt tells the model who it is, what mode
|
|
6
|
+
* it is operating in, which tools are available, and how to behave.
|
|
7
|
+
*
|
|
8
|
+
* The prompt is assembled from several composable sections:
|
|
9
|
+
*
|
|
10
|
+
* 1. **Base identity** -- who Nimbus is and its core behavioral rules.
|
|
11
|
+
* 2. **Mode instructions** -- what the current {@link AgentMode} allows.
|
|
12
|
+
* 3. **Tool-use guidelines** -- general best practices for tool invocation.
|
|
13
|
+
* 4. **Available tools** -- a summarized list built from {@link ToolDefinition}s.
|
|
14
|
+
* 5. **NIMBUS.md** -- optional per-project or per-user custom instructions.
|
|
15
|
+
* 6. **Subagent instructions** -- constraints when running as a spawned subagent.
|
|
16
|
+
* 7. **Environment context** -- working directory, platform, date, git status.
|
|
17
|
+
*
|
|
18
|
+
* @module agent/system-prompt
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import * as fs from 'node:fs';
|
|
22
|
+
import * as path from 'node:path';
|
|
23
|
+
import { homedir } from 'node:os';
|
|
24
|
+
import { execSync } from 'node:child_process';
|
|
25
|
+
import type { ToolDefinition } from '../tools/schemas/types';
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Agent Mode
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Agent modes that control tool availability and behavior.
|
|
33
|
+
*
|
|
34
|
+
* | Mode | Description |
|
|
35
|
+
* | -------- | ---------------------------------------------------------- |
|
|
36
|
+
* | `plan` | Read-only exploration, analysis, and proposal generation. |
|
|
37
|
+
* | `build` | File editing, code generation, and non-destructive DevOps. |
|
|
38
|
+
* | `deploy` | Full infrastructure mutation with approval gates. |
|
|
39
|
+
*/
|
|
40
|
+
export type AgentMode = 'plan' | 'build' | 'deploy';
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Ordered list of all agent modes from least permissive to most permissive.
|
|
44
|
+
* Useful for comparison and escalation logic.
|
|
45
|
+
*/
|
|
46
|
+
export const AGENT_MODES: readonly AgentMode[] = ['plan', 'build', 'deploy'] as const;
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// System Prompt Options
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Options for building the system prompt via {@link buildSystemPrompt}.
|
|
54
|
+
*/
|
|
55
|
+
export interface SystemPromptOptions {
|
|
56
|
+
/** Current agent mode -- controls which actions are permitted. */
|
|
57
|
+
readonly mode: AgentMode;
|
|
58
|
+
|
|
59
|
+
/** Available tools (already filtered by mode before being passed in). */
|
|
60
|
+
readonly tools: ToolDefinition[];
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Custom instructions loaded from a `NIMBUS.md` file. When provided this
|
|
64
|
+
* value is used directly; when omitted the builder will attempt to
|
|
65
|
+
* discover and load the file automatically via {@link loadNimbusMd}.
|
|
66
|
+
*/
|
|
67
|
+
readonly nimbusInstructions?: string;
|
|
68
|
+
|
|
69
|
+
/** Current working directory. Defaults to `process.cwd()`. */
|
|
70
|
+
readonly cwd?: string;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Active subagent name. When set, the prompt includes additional
|
|
74
|
+
* constraints that prevent recursive subagent spawning and encourage
|
|
75
|
+
* focused, scoped execution.
|
|
76
|
+
*/
|
|
77
|
+
readonly activeSubagent?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Public API
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Build the complete system prompt for the agentic loop.
|
|
86
|
+
*
|
|
87
|
+
* The returned string is intended to be used as the `system` message (or
|
|
88
|
+
* first `user`/`system` message, depending on the LLM provider) in a
|
|
89
|
+
* conversation with the model.
|
|
90
|
+
*
|
|
91
|
+
* @param options - Configuration that controls prompt assembly.
|
|
92
|
+
* @returns The fully assembled system prompt string.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* import { buildSystemPrompt } from './system-prompt';
|
|
97
|
+
*
|
|
98
|
+
* const prompt = buildSystemPrompt({
|
|
99
|
+
* mode: 'build',
|
|
100
|
+
* tools: registry.getAll(),
|
|
101
|
+
* cwd: '/home/user/project',
|
|
102
|
+
* });
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export function buildSystemPrompt(options: SystemPromptOptions): string {
|
|
106
|
+
const parts: string[] = [];
|
|
107
|
+
|
|
108
|
+
// 1. Base identity
|
|
109
|
+
parts.push(BASE_PROMPT);
|
|
110
|
+
|
|
111
|
+
// 2. Mode-specific instructions
|
|
112
|
+
parts.push(getModeInstructions(options.mode));
|
|
113
|
+
|
|
114
|
+
// 3. Tool-use guidelines
|
|
115
|
+
parts.push(TOOL_USE_GUIDELINES);
|
|
116
|
+
|
|
117
|
+
// 4. Available tools summary
|
|
118
|
+
const toolsSummary = buildToolsSummary(options.tools);
|
|
119
|
+
if (toolsSummary) {
|
|
120
|
+
parts.push(toolsSummary);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 5. NIMBUS.md content (if exists)
|
|
124
|
+
const nimbusContent = options.nimbusInstructions ?? loadNimbusMd(options.cwd);
|
|
125
|
+
if (nimbusContent) {
|
|
126
|
+
parts.push(`# Project Instructions (NIMBUS.md)\n\n${nimbusContent}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 6. Subagent instructions (if applicable)
|
|
130
|
+
if (options.activeSubagent) {
|
|
131
|
+
parts.push(getSubagentInstructions(options.activeSubagent));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 7. Environment context
|
|
135
|
+
parts.push(buildEnvironmentContext(options.cwd));
|
|
136
|
+
|
|
137
|
+
return parts.join('\n\n---\n\n');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Prompt Fragments
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Core identity and behavioral rules that apply regardless of mode.
|
|
146
|
+
* @internal
|
|
147
|
+
*/
|
|
148
|
+
const BASE_PROMPT = `You are Nimbus, an AI-powered DevOps engineering agent. You help developers build, deploy, and manage cloud infrastructure through natural conversation.
|
|
149
|
+
|
|
150
|
+
You have access to tools that let you read files, edit code, run shell commands, execute Terraform/Kubernetes/Helm operations, discover cloud resources, estimate costs, and detect infrastructure drift.
|
|
151
|
+
|
|
152
|
+
You work autonomously — reading files, making edits, running commands, and iterating until the task is complete. You use tools proactively rather than asking the user to run commands themselves.
|
|
153
|
+
|
|
154
|
+
Key behaviors:
|
|
155
|
+
- Read files before editing them to understand the current state
|
|
156
|
+
- Make precise, targeted edits rather than rewriting entire files
|
|
157
|
+
- Run tests after making changes to verify correctness
|
|
158
|
+
- Show deployment previews before executing destructive infrastructure changes
|
|
159
|
+
- Explain what you're doing and why at each step
|
|
160
|
+
- If a tool call fails, analyze the error and try a different approach`;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* General best-practice guidelines for tool invocation that are included in
|
|
164
|
+
* every prompt regardless of mode.
|
|
165
|
+
* @internal
|
|
166
|
+
*/
|
|
167
|
+
const TOOL_USE_GUIDELINES = `# Tool-Use Guidelines
|
|
168
|
+
|
|
169
|
+
- Use the most specific tool available. Prefer \`read_file\` over \`bash cat\`, \`glob\` over \`bash find\`, \`grep\` over \`bash grep\`.
|
|
170
|
+
- For file edits, use \`edit_file\` for single replacements and \`multi_edit\` for multiple replacements in the same file.
|
|
171
|
+
- Use \`write_file\` only for creating new files or complete rewrites.
|
|
172
|
+
- When running bash commands, prefer specific commands over broad ones. Avoid \`rm -rf\` or other destructive patterns.
|
|
173
|
+
- For infrastructure operations, always run validation (terraform validate, kubectl --dry-run) before apply.
|
|
174
|
+
- Use \`deploy_preview\` before any destructive infrastructure change.
|
|
175
|
+
- Use the \`task\` tool to spawn subagents for parallel or specialized work.
|
|
176
|
+
- When using \`terraform\`, always run \`terraform init\` before \`plan\` or \`apply\` if not already initialized.
|
|
177
|
+
- For Kubernetes operations, be namespace-aware. Default to the current namespace context.`;
|
|
178
|
+
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
// Mode Instructions
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Return the mode-specific instruction block for the given {@link AgentMode}.
|
|
185
|
+
*
|
|
186
|
+
* Each mode defines an explicit allow-list and deny-list so the model
|
|
187
|
+
* understands the boundaries of what it can do.
|
|
188
|
+
*
|
|
189
|
+
* @param mode - The active agent mode.
|
|
190
|
+
* @returns A markdown section describing the mode's rules.
|
|
191
|
+
* @internal
|
|
192
|
+
*/
|
|
193
|
+
function getModeInstructions(mode: AgentMode): string {
|
|
194
|
+
switch (mode) {
|
|
195
|
+
case 'plan':
|
|
196
|
+
return `# Mode: PLAN
|
|
197
|
+
|
|
198
|
+
You are in Plan mode. Your role is to analyze, explore, and propose — NOT to modify.
|
|
199
|
+
|
|
200
|
+
Allowed actions:
|
|
201
|
+
- Read files, search code, list directories
|
|
202
|
+
- Analyze infrastructure configurations
|
|
203
|
+
- Estimate costs and detect drift
|
|
204
|
+
- Propose changes and create task lists
|
|
205
|
+
- Fetch web content for research
|
|
206
|
+
|
|
207
|
+
NOT allowed:
|
|
208
|
+
- Editing or creating files
|
|
209
|
+
- Running destructive bash commands
|
|
210
|
+
- Executing terraform apply, kubectl apply, helm install
|
|
211
|
+
- Making any state-changing operations
|
|
212
|
+
|
|
213
|
+
Focus on understanding the codebase and infrastructure, then propose a clear action plan.`;
|
|
214
|
+
|
|
215
|
+
case 'build':
|
|
216
|
+
return `# Mode: BUILD
|
|
217
|
+
|
|
218
|
+
You are in Build mode. You can read, edit, create files, and run non-destructive commands.
|
|
219
|
+
|
|
220
|
+
Allowed actions:
|
|
221
|
+
- All Plan mode actions
|
|
222
|
+
- Edit and create files
|
|
223
|
+
- Run tests and linters
|
|
224
|
+
- Generate Terraform/K8s/Helm configurations
|
|
225
|
+
- Run terraform validate, terraform fmt, terraform plan
|
|
226
|
+
- Run kubectl get, kubectl describe, kubectl diff
|
|
227
|
+
|
|
228
|
+
NOT allowed:
|
|
229
|
+
- terraform apply, terraform destroy
|
|
230
|
+
- kubectl apply, kubectl delete
|
|
231
|
+
- helm install, helm upgrade, helm uninstall
|
|
232
|
+
- Any infrastructure-mutating operations
|
|
233
|
+
|
|
234
|
+
Focus on building and testing changes before deploying.`;
|
|
235
|
+
|
|
236
|
+
case 'deploy':
|
|
237
|
+
return `# Mode: DEPLOY
|
|
238
|
+
|
|
239
|
+
You are in Deploy mode. You have full access to all tools including infrastructure mutations.
|
|
240
|
+
|
|
241
|
+
Allowed actions:
|
|
242
|
+
- All Build mode actions
|
|
243
|
+
- terraform apply, terraform destroy
|
|
244
|
+
- kubectl apply, kubectl delete
|
|
245
|
+
- helm install, helm upgrade, helm uninstall
|
|
246
|
+
- Cloud resource mutations
|
|
247
|
+
|
|
248
|
+
REQUIRED:
|
|
249
|
+
- Always run deploy_preview before any destructive operation
|
|
250
|
+
- Show the user what will change before executing
|
|
251
|
+
- Wait for explicit approval on destructive changes
|
|
252
|
+
|
|
253
|
+
Focus on safe, verified deployments with minimal blast radius.`;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
// Tools Summary
|
|
259
|
+
// ---------------------------------------------------------------------------
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Build a markdown summary of the available tools from their definitions.
|
|
263
|
+
*
|
|
264
|
+
* Returns an empty string when the tool list is empty so callers can
|
|
265
|
+
* conditionally include the section.
|
|
266
|
+
*
|
|
267
|
+
* @param tools - The tool definitions to summarize.
|
|
268
|
+
* @returns A markdown section listing each tool, or `''` if none.
|
|
269
|
+
* @internal
|
|
270
|
+
*/
|
|
271
|
+
function buildToolsSummary(tools: ToolDefinition[]): string {
|
|
272
|
+
if (tools.length === 0) {
|
|
273
|
+
return '';
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const lines = tools.map(t => `- **${t.name}**: ${t.description}`);
|
|
277
|
+
return `# Available Tools (${tools.length})\n\n${lines.join('\n')}`;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ---------------------------------------------------------------------------
|
|
281
|
+
// NIMBUS.md Loader
|
|
282
|
+
// ---------------------------------------------------------------------------
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Search paths for `NIMBUS.md` files, in priority order.
|
|
286
|
+
* @internal
|
|
287
|
+
*/
|
|
288
|
+
function getNimbusMdSearchPaths(cwd?: string): string[] {
|
|
289
|
+
return [
|
|
290
|
+
cwd ? path.join(cwd, 'NIMBUS.md') : null,
|
|
291
|
+
cwd ? path.join(cwd, '.nimbus', 'NIMBUS.md') : null,
|
|
292
|
+
path.join(homedir(), '.nimbus', 'NIMBUS.md'),
|
|
293
|
+
].filter(Boolean) as string[];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Load `NIMBUS.md` from the project directory or the user's home directory.
|
|
298
|
+
*
|
|
299
|
+
* The search order is:
|
|
300
|
+
* 1. `<cwd>/NIMBUS.md`
|
|
301
|
+
* 2. `<cwd>/.nimbus/NIMBUS.md`
|
|
302
|
+
* 3. `~/.nimbus/NIMBUS.md`
|
|
303
|
+
*
|
|
304
|
+
* Returns `null` if no file is found or if all candidates are inaccessible.
|
|
305
|
+
*
|
|
306
|
+
* @param cwd - The working directory to search from. Defaults to `undefined`
|
|
307
|
+
* (skips cwd-relative paths).
|
|
308
|
+
* @returns The file contents as a string, or `null`.
|
|
309
|
+
*/
|
|
310
|
+
export function loadNimbusMd(cwd?: string): string | null {
|
|
311
|
+
const searchPaths = getNimbusMdSearchPaths(cwd);
|
|
312
|
+
|
|
313
|
+
for (const p of searchPaths) {
|
|
314
|
+
try {
|
|
315
|
+
if (fs.existsSync(p)) {
|
|
316
|
+
return fs.readFileSync(p, 'utf-8');
|
|
317
|
+
}
|
|
318
|
+
} catch {
|
|
319
|
+
// Skip inaccessible files silently
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// ---------------------------------------------------------------------------
|
|
327
|
+
// Subagent Instructions
|
|
328
|
+
// ---------------------------------------------------------------------------
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Return the instruction block appended when the agent is running as a
|
|
332
|
+
* spawned subagent.
|
|
333
|
+
*
|
|
334
|
+
* Subagents are constrained to prevent recursive spawning and to keep
|
|
335
|
+
* execution focused on a single task.
|
|
336
|
+
*
|
|
337
|
+
* @param agentName - The name of the active subagent.
|
|
338
|
+
* @returns A markdown section with subagent-specific rules.
|
|
339
|
+
* @internal
|
|
340
|
+
*/
|
|
341
|
+
function getSubagentInstructions(agentName: string): string {
|
|
342
|
+
return `# Subagent Mode: ${agentName}
|
|
343
|
+
|
|
344
|
+
You are running as a subagent spawned by the primary Nimbus agent. Your task is specific and focused.
|
|
345
|
+
- Complete the requested task and return a clear, concise result
|
|
346
|
+
- Do NOT spawn further subagents (no nesting)
|
|
347
|
+
- Stay focused on the assigned task — do not explore tangentially
|
|
348
|
+
- Return your findings as structured, actionable information`;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ---------------------------------------------------------------------------
|
|
352
|
+
// Environment Context
|
|
353
|
+
// ---------------------------------------------------------------------------
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Build a short environment-context block that gives the model awareness of
|
|
357
|
+
* the runtime environment (working directory, platform, date, git status).
|
|
358
|
+
*
|
|
359
|
+
* @param cwd - The working directory. Defaults to `process.cwd()`.
|
|
360
|
+
* @returns A markdown section with environment metadata.
|
|
361
|
+
* @internal
|
|
362
|
+
*/
|
|
363
|
+
function buildEnvironmentContext(cwd?: string): string {
|
|
364
|
+
const effectiveCwd = cwd ?? process.cwd();
|
|
365
|
+
|
|
366
|
+
const parts = [
|
|
367
|
+
'# Environment',
|
|
368
|
+
`- Working directory: ${effectiveCwd}`,
|
|
369
|
+
`- Platform: ${process.platform}`,
|
|
370
|
+
`- Date: ${new Date().toISOString().split('T')[0]}`,
|
|
371
|
+
];
|
|
372
|
+
|
|
373
|
+
// Check for git repo and gather context
|
|
374
|
+
const gitDir = path.join(effectiveCwd, '.git');
|
|
375
|
+
if (fs.existsSync(gitDir)) {
|
|
376
|
+
parts.push('- Git repository: yes');
|
|
377
|
+
|
|
378
|
+
const execOpts = {
|
|
379
|
+
cwd: effectiveCwd,
|
|
380
|
+
timeout: 1000,
|
|
381
|
+
encoding: 'utf-8' as const,
|
|
382
|
+
stdio: ['pipe', 'pipe', 'pipe'] as ['pipe', 'pipe', 'pipe'],
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
try {
|
|
386
|
+
const branch = execSync('git rev-parse --abbrev-ref HEAD', execOpts).trim();
|
|
387
|
+
parts.push(`- Git branch: ${branch}`);
|
|
388
|
+
} catch {
|
|
389
|
+
/* ignore */
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
try {
|
|
393
|
+
const log = execSync('git log --oneline -5 2>/dev/null', execOpts).trim();
|
|
394
|
+
if (log) {
|
|
395
|
+
parts.push(
|
|
396
|
+
`- Recent commits:\n${log
|
|
397
|
+
.split('\n')
|
|
398
|
+
.map((l: string) => ` ${l}`)
|
|
399
|
+
.join('\n')}`
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
} catch {
|
|
403
|
+
/* ignore */
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
try {
|
|
407
|
+
const staged = execSync('git diff --cached --stat 2>/dev/null', execOpts).trim();
|
|
408
|
+
if (staged) {
|
|
409
|
+
parts.push(
|
|
410
|
+
`- Staged changes:\n${staged
|
|
411
|
+
.split('\n')
|
|
412
|
+
.map((l: string) => ` ${l}`)
|
|
413
|
+
.join('\n')}`
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
} catch {
|
|
417
|
+
/* ignore */
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
try {
|
|
421
|
+
const unstaged = execSync('git diff --stat 2>/dev/null', execOpts).trim();
|
|
422
|
+
if (unstaged) {
|
|
423
|
+
parts.push(
|
|
424
|
+
`- Unstaged changes:\n${unstaged
|
|
425
|
+
.split('\n')
|
|
426
|
+
.map((l: string) => ` ${l}`)
|
|
427
|
+
.join('\n')}`
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
} catch {
|
|
431
|
+
/* ignore */
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return parts.join('\n');
|
|
436
|
+
}
|
package/src/app.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App Lifecycle
|
|
3
|
+
*
|
|
4
|
+
* Handles initialization and shutdown of the embedded Nimbus application.
|
|
5
|
+
* Lazily initializes the SQLite database and the LLM router.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { mkdirSync, existsSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { homedir } from 'node:os';
|
|
11
|
+
import type { Database } from './compat/sqlite';
|
|
12
|
+
import type { LLMRouter } from './llm/router';
|
|
13
|
+
|
|
14
|
+
/** The resolved path to ~/.nimbus */
|
|
15
|
+
const NIMBUS_DIR = join(homedir(), '.nimbus');
|
|
16
|
+
|
|
17
|
+
/** Holds the initialized app context, or null if not yet initialized. */
|
|
18
|
+
let appContext: AppContext | null = null;
|
|
19
|
+
|
|
20
|
+
/** The shape returned by initApp(). */
|
|
21
|
+
export interface AppContext {
|
|
22
|
+
/** The bun:sqlite Database instance for local state. */
|
|
23
|
+
readonly db: Database;
|
|
24
|
+
/** The LLM router for provider-agnostic completions. */
|
|
25
|
+
readonly router: LLMRouter;
|
|
26
|
+
/** The nimbus home directory path. */
|
|
27
|
+
readonly nimbusDir: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Initialize the Nimbus application.
|
|
32
|
+
*
|
|
33
|
+
* - Ensures the ~/.nimbus directory exists.
|
|
34
|
+
* - Opens (or creates) the local SQLite database via the state module.
|
|
35
|
+
* - Loads config and creates the LLM router instance.
|
|
36
|
+
*
|
|
37
|
+
* This function is lazy: calling it multiple times returns the same context.
|
|
38
|
+
*/
|
|
39
|
+
export async function initApp(): Promise<AppContext> {
|
|
40
|
+
if (appContext) {
|
|
41
|
+
return appContext;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Ensure ~/.nimbus directory exists
|
|
45
|
+
if (!existsSync(NIMBUS_DIR)) {
|
|
46
|
+
mkdirSync(NIMBUS_DIR, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Initialize the SQLite database
|
|
50
|
+
const { getDb } = await import('./state/db');
|
|
51
|
+
const db = getDb();
|
|
52
|
+
|
|
53
|
+
// Load LLM config and create router
|
|
54
|
+
const { loadLLMConfig } = await import('./llm/config-loader');
|
|
55
|
+
const { LLMRouter: LLMRouterClass } = await import('./llm/router');
|
|
56
|
+
const llmConfig = loadLLMConfig();
|
|
57
|
+
const router = new LLMRouterClass(llmConfig);
|
|
58
|
+
|
|
59
|
+
// Register all built-in tools into the global registry
|
|
60
|
+
const { defaultToolRegistry } = await import('./tools/schemas/types');
|
|
61
|
+
if (defaultToolRegistry.size === 0) {
|
|
62
|
+
const { standardTools } = await import('./tools/schemas/standard');
|
|
63
|
+
const { devopsTools } = await import('./tools/schemas/devops');
|
|
64
|
+
for (const tool of [...standardTools, ...devopsTools]) {
|
|
65
|
+
try {
|
|
66
|
+
defaultToolRegistry.register(tool);
|
|
67
|
+
} catch {
|
|
68
|
+
/* skip duplicates */
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Connect MCP servers and register their tools (non-critical)
|
|
74
|
+
try {
|
|
75
|
+
const { MCPManager } = await import('./mcp/manager');
|
|
76
|
+
const mcpManager = new MCPManager();
|
|
77
|
+
await mcpManager.loadConfig(process.cwd());
|
|
78
|
+
if (mcpManager.serverCount > 0) {
|
|
79
|
+
await mcpManager.connectAll();
|
|
80
|
+
mcpManager.registerTools(defaultToolRegistry);
|
|
81
|
+
}
|
|
82
|
+
} catch (mcpErr) {
|
|
83
|
+
// MCP is non-critical — tools work fine without it, but warn the user
|
|
84
|
+
const msg = mcpErr instanceof Error ? mcpErr.message : String(mcpErr);
|
|
85
|
+
if (process.stderr.isTTY) {
|
|
86
|
+
process.stderr.write(
|
|
87
|
+
`\x1b[33m Warning: MCP server loading failed: ${msg}. External tools from .mcp.json will not be available.\x1b[0m\n`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
appContext = { db, router, nimbusDir: NIMBUS_DIR };
|
|
93
|
+
return appContext;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get the current app context without initializing.
|
|
98
|
+
* Returns null if initApp() has not been called yet.
|
|
99
|
+
*/
|
|
100
|
+
export function getAppContext(): AppContext | null {
|
|
101
|
+
return appContext;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Gracefully shut down the Nimbus application.
|
|
106
|
+
*
|
|
107
|
+
* Closes the database connection and clears the cached context so that
|
|
108
|
+
* a subsequent call to initApp() will re-initialize from scratch.
|
|
109
|
+
*/
|
|
110
|
+
export async function shutdownApp(): Promise<void> {
|
|
111
|
+
if (!appContext) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
appContext.db.close();
|
|
117
|
+
} catch {
|
|
118
|
+
// Database may already be closed; ignore.
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
appContext = null;
|
|
122
|
+
}
|