@vyuhlabs/dxkit 0.1.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.
Files changed (149) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +173 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/codebase-scanner.d.ts +33 -0
  6. package/dist/codebase-scanner.d.ts.map +1 -0
  7. package/dist/codebase-scanner.js +497 -0
  8. package/dist/codebase-scanner.js.map +1 -0
  9. package/dist/constants.d.ts +17 -0
  10. package/dist/constants.d.ts.map +1 -0
  11. package/dist/constants.js +80 -0
  12. package/dist/constants.js.map +1 -0
  13. package/dist/detect.d.ts +3 -0
  14. package/dist/detect.d.ts.map +1 -0
  15. package/dist/detect.js +264 -0
  16. package/dist/detect.js.map +1 -0
  17. package/dist/doctor.d.ts +2 -0
  18. package/dist/doctor.d.ts.map +1 -0
  19. package/dist/doctor.js +157 -0
  20. package/dist/doctor.js.map +1 -0
  21. package/dist/files.d.ts +17 -0
  22. package/dist/files.d.ts.map +1 -0
  23. package/dist/files.js +99 -0
  24. package/dist/files.js.map +1 -0
  25. package/dist/generator.d.ts +10 -0
  26. package/dist/generator.d.ts.map +1 -0
  27. package/dist/generator.js +405 -0
  28. package/dist/generator.js.map +1 -0
  29. package/dist/index.d.ts +3 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +9 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/logger.d.ts +9 -0
  34. package/dist/logger.d.ts.map +1 -0
  35. package/dist/logger.js +51 -0
  36. package/dist/logger.js.map +1 -0
  37. package/dist/prompts.d.ts +10 -0
  38. package/dist/prompts.d.ts.map +1 -0
  39. package/dist/prompts.js +125 -0
  40. package/dist/prompts.js.map +1 -0
  41. package/dist/template-engine.d.ts +22 -0
  42. package/dist/template-engine.d.ts.map +1 -0
  43. package/dist/template-engine.js +63 -0
  44. package/dist/template-engine.js.map +1 -0
  45. package/dist/types.d.ts +68 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +3 -0
  48. package/dist/types.js.map +1 -0
  49. package/dist/update.d.ts +2 -0
  50. package/dist/update.d.ts.map +1 -0
  51. package/dist/update.js +97 -0
  52. package/dist/update.js.map +1 -0
  53. package/package.json +43 -0
  54. package/templates/.ai/README.md +117 -0
  55. package/templates/.ai/prompts/execution-prompt.md +9 -0
  56. package/templates/.ai/prompts/planning-prompt.md +18 -0
  57. package/templates/.ai/prompts/session-end-template.md +182 -0
  58. package/templates/.ai/prompts/session-end.md +132 -0
  59. package/templates/.ai/prompts/session-start.md +109 -0
  60. package/templates/.ai/prompts/step-by-step.md +113 -0
  61. package/templates/.ai/sessions/.gitkeep +0 -0
  62. package/templates/.ai/templates/session-checkpoint-template.md +97 -0
  63. package/templates/.claude/agents/knowledge-bot.md +62 -0
  64. package/templates/.claude/agents/onboarding.md +61 -0
  65. package/templates/.claude/agents/quality-reviewer.md +85 -0
  66. package/templates/.claude/agents-available/code-reviewer.md +29 -0
  67. package/templates/.claude/agents-available/codebase-explorer.md +99 -0
  68. package/templates/.claude/agents-available/debugger.md +29 -0
  69. package/templates/.claude/agents-available/knowledge-bot.md +62 -0
  70. package/templates/.claude/agents-available/test-writer.md +34 -0
  71. package/templates/.claude/commands/ask.md +7 -0
  72. package/templates/.claude/commands/check.md.template +55 -0
  73. package/templates/.claude/commands/doctor.md +21 -0
  74. package/templates/.claude/commands/enable-agent.md +12 -0
  75. package/templates/.claude/commands/explore-codebase.md +12 -0
  76. package/templates/.claude/commands/fix-issue.md +12 -0
  77. package/templates/.claude/commands/fix.md.template +32 -0
  78. package/templates/.claude/commands/help.md +36 -0
  79. package/templates/.claude/commands/learn.md +48 -0
  80. package/templates/.claude/commands/onboarding.md +21 -0
  81. package/templates/.claude/commands/quality.md.template +42 -0
  82. package/templates/.claude/commands/session-end.md +40 -0
  83. package/templates/.claude/commands/session-start.md +30 -0
  84. package/templates/.claude/commands/setup-hooks.md +60 -0
  85. package/templates/.claude/commands/test.md.template +49 -0
  86. package/templates/.claude/rules/csharp.md +18 -0
  87. package/templates/.claude/rules/go.md +16 -0
  88. package/templates/.claude/rules/nextjs.md +17 -0
  89. package/templates/.claude/rules/python.md +19 -0
  90. package/templates/.claude/rules/rust.md +14 -0
  91. package/templates/.claude/skills/build/SKILL.md.template +98 -0
  92. package/templates/.claude/skills/deploy/SKILL.md.template +131 -0
  93. package/templates/.claude/skills/deploy/references/gotchas.md +5 -0
  94. package/templates/.claude/skills/doctor/SKILL.md +54 -0
  95. package/templates/.claude/skills/gcloud/SKILL.md +61 -0
  96. package/templates/.claude/skills/gcloud/references/gotchas.md +5 -0
  97. package/templates/.claude/skills/learned/SKILL.md +55 -0
  98. package/templates/.claude/skills/learned/references/conventions.md +11 -0
  99. package/templates/.claude/skills/learned/references/deny-recommendations.md +18 -0
  100. package/templates/.claude/skills/learned/references/gotchas.md +11 -0
  101. package/templates/.claude/skills/pulumi/SKILL.md +67 -0
  102. package/templates/.claude/skills/quality/SKILL.md.template +108 -0
  103. package/templates/.claude/skills/quality/references/gotchas.md +5 -0
  104. package/templates/.claude/skills/review/SKILL.md.template +73 -0
  105. package/templates/.claude/skills/scaffold/SKILL.md.template +123 -0
  106. package/templates/.claude/skills/secrets/SKILL.md +45 -0
  107. package/templates/.claude/skills/session/SKILL.md +43 -0
  108. package/templates/.claude/skills/test/SKILL.md.template +109 -0
  109. package/templates/.claude/skills/test/references/gotchas.md +5 -0
  110. package/templates/.devcontainer/Dockerfile.dev.template +89 -0
  111. package/templates/.devcontainer/devcontainer.json.template +184 -0
  112. package/templates/.devcontainer/docker-compose.yml.template +105 -0
  113. package/templates/.devcontainer/init-scripts/01-init.sql.template +12 -0
  114. package/templates/.devcontainer/post-create.sh.template +298 -0
  115. package/templates/.github/workflows/ci.yml.template +399 -0
  116. package/templates/.github/workflows/quality.yml.template +376 -0
  117. package/templates/.pre-commit-config.yaml.template +106 -0
  118. package/templates/.project/config/edit_config.py +275 -0
  119. package/templates/.project/config/project_config.py +894 -0
  120. package/templates/.project/scripts/codegen/generate-all.sh +20 -0
  121. package/templates/.project/scripts/codegen/validate-all.sh +17 -0
  122. package/templates/.project/scripts/docs/generate-all.sh +30 -0
  123. package/templates/.project/scripts/docs/serve.sh +20 -0
  124. package/templates/.project/scripts/quality/fix-all.sh +138 -0
  125. package/templates/.project/scripts/quality/lint-go.sh +34 -0
  126. package/templates/.project/scripts/quality/lint-python.sh +54 -0
  127. package/templates/.project/scripts/quality/run-all.sh +497 -0
  128. package/templates/.project/scripts/session/commit.sh +70 -0
  129. package/templates/.project/scripts/session/create-pr.sh +165 -0
  130. package/templates/.project/scripts/session/end.sh +207 -0
  131. package/templates/.project/scripts/session/start.sh +233 -0
  132. package/templates/.project/scripts/setup/doctor.sh +404 -0
  133. package/templates/.project/scripts/setup/interactive-setup.sh +581 -0
  134. package/templates/.project/scripts/sync/sync-template.sh +328 -0
  135. package/templates/.project/scripts/test/run-all.sh +179 -0
  136. package/templates/.project/scripts/test/run-quick.sh +25 -0
  137. package/templates/CLAUDE.md.template +228 -0
  138. package/templates/Makefile +514 -0
  139. package/templates/config/versions.yaml +57 -0
  140. package/templates/configs/go/.golangci.yml.template +172 -0
  141. package/templates/configs/go/go.mod.template +15 -0
  142. package/templates/configs/node/package.json.template +67 -0
  143. package/templates/configs/node/tsconfig.json.template +53 -0
  144. package/templates/configs/python/pyproject.toml.template +92 -0
  145. package/templates/configs/python/pytest.ini.template +64 -0
  146. package/templates/configs/python/ruff.toml.template +79 -0
  147. package/templates/configs/rust/Cargo.toml.template +51 -0
  148. package/templates/configs/shared/.editorconfig +67 -0
  149. package/templates/scripts/validate-templates.sh +449 -0
@@ -0,0 +1,581 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # Colors
5
+ CYAN='\033[36m'
6
+ GREEN='\033[32m'
7
+ YELLOW='\033[33m'
8
+ RED='\033[31m'
9
+ BOLD='\033[1m'
10
+ DIM='\033[2m'
11
+ RESET='\033[0m'
12
+
13
+ echo ""
14
+ echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
15
+ echo -e "${CYAN} PROJECT SETUP${RESET}"
16
+ echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
17
+ echo ""
18
+
19
+ # ============================================================================
20
+ # Helper Functions
21
+ # ============================================================================
22
+
23
+ # Read a value from .project.yaml
24
+ read_config() {
25
+ local path="$1"
26
+ local default="$2"
27
+ if [ -f ".project.yaml" ] && command -v python3 &> /dev/null; then
28
+ python3 -c "
29
+ import yaml
30
+ try:
31
+ with open('.project.yaml') as f:
32
+ c = yaml.safe_load(f)
33
+ keys = '$path'.split('.')
34
+ val = c
35
+ for k in keys:
36
+ val = val.get(k, {}) if isinstance(val, dict) else {}
37
+ print('true' if val is True else ('false' if val is False else (val if val else '$default')))
38
+ except:
39
+ print('$default')
40
+ " 2>/dev/null || echo "$default"
41
+ else
42
+ echo "$default"
43
+ fi
44
+ }
45
+
46
+ # ============================================================================
47
+ # Quality Tools Installation
48
+ # ============================================================================
49
+
50
+ echo -e "${BOLD}Installing Quality Tools${RESET}"
51
+ echo ""
52
+
53
+ # Check enabled languages from config
54
+ PYTHON_ENABLED=$(read_config "languages.python.enabled" "false")
55
+ GO_ENABLED=$(read_config "languages.go.enabled" "false")
56
+ NODE_ENABLED=$(read_config "languages.node.enabled" "false")
57
+ RUST_ENABLED=$(read_config "languages.rust.enabled" "false")
58
+
59
+ # Go tools
60
+ if [ "$GO_ENABLED" = "true" ] && command -v go &> /dev/null; then
61
+ echo -e "${CYAN}Go tools:${RESET}"
62
+ if ! command -v golangci-lint &> /dev/null; then
63
+ echo -e " ${CYAN}→${RESET} Installing golangci-lint..."
64
+ go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest > /dev/null 2>&1 && \
65
+ echo -e " ${GREEN}✓${RESET} golangci-lint installed"
66
+ else
67
+ echo -e " ${GREEN}✓${RESET} golangci-lint already installed"
68
+ fi
69
+
70
+ if ! command -v goimports &> /dev/null; then
71
+ echo -e " ${CYAN}→${RESET} Installing goimports..."
72
+ go install golang.org/x/tools/cmd/goimports@latest > /dev/null 2>&1 && \
73
+ echo -e " ${GREEN}✓${RESET} goimports installed"
74
+ else
75
+ echo -e " ${GREEN}✓${RESET} goimports already installed"
76
+ fi
77
+ echo ""
78
+ fi
79
+
80
+ # Python tools
81
+ if [ "$PYTHON_ENABLED" = "true" ] && command -v pip &> /dev/null; then
82
+ echo -e "${CYAN}Python tools:${RESET}"
83
+ if [ -f "pyproject.toml" ]; then
84
+ echo -e " ${CYAN}→${RESET} Installing Python dev dependencies..."
85
+ # Versions synced with .template/config/versions.yaml
86
+ pip install -q -e ".[dev]" 2>/dev/null || pip install -q "ruff>=0.8.0" "mypy>=1.8.0" "pytest>=8.0.0" pytest-cov 2>/dev/null
87
+ echo -e " ${GREEN}✓${RESET} Python tools installed"
88
+ else
89
+ # Versions synced with .template/config/versions.yaml
90
+ pip install -q "ruff>=0.8.0" "mypy>=1.8.0" "pytest>=8.0.0" pytest-cov 2>/dev/null
91
+ echo -e " ${GREEN}✓${RESET} Python tools installed"
92
+ fi
93
+ echo ""
94
+ fi
95
+
96
+ # Node.js tools
97
+ if [ "$NODE_ENABLED" = "true" ] && command -v npm &> /dev/null; then
98
+ echo -e "${CYAN}Node.js tools:${RESET}"
99
+ if [ -f "package.json" ]; then
100
+ echo -e " ${CYAN}→${RESET} Installing npm dependencies..."
101
+ npm install --silent 2>/dev/null
102
+ echo -e " ${GREEN}✓${RESET} npm dependencies installed"
103
+ fi
104
+ echo ""
105
+ fi
106
+
107
+ # Rust tools
108
+ if [ "$RUST_ENABLED" = "true" ] && command -v rustup &> /dev/null; then
109
+ echo -e "${CYAN}Rust tools:${RESET}"
110
+ rustup component add rustfmt clippy 2>/dev/null
111
+ echo -e " ${GREEN}✓${RESET} rustfmt and clippy installed"
112
+ echo ""
113
+ fi
114
+
115
+ # ============================================================================
116
+ # Cloud Tools Authentication
117
+ # ============================================================================
118
+
119
+ # Read tool configuration
120
+ CLAUDE_CODE_ENABLED=$(read_config "tools.claude_code" "false")
121
+ GITHUB_CLI_ENABLED=$(read_config "tools.github_cli" "false")
122
+ GCLOUD_ENABLED=$(read_config "tools.gcloud" "false")
123
+ PULUMI_ENABLED=$(read_config "tools.pulumi" "false")
124
+ INFISICAL_ENABLED=$(read_config "tools.infisical" "false")
125
+
126
+ # Check if any cloud tools are enabled
127
+ if [ "$CLAUDE_CODE_ENABLED" = "true" ] || [ "$GITHUB_CLI_ENABLED" = "true" ] || \
128
+ [ "$GCLOUD_ENABLED" = "true" ] || [ "$PULUMI_ENABLED" = "true" ] || \
129
+ [ "$INFISICAL_ENABLED" = "true" ]; then
130
+
131
+ echo ""
132
+ echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
133
+ echo -e "${CYAN} CLOUD TOOLS AUTHENTICATION${RESET}"
134
+ echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
135
+ echo ""
136
+ echo -e "${DIM}Set up authentication for your cloud tools.${RESET}"
137
+ echo -e "${DIM}You can skip any tool by pressing Enter or Ctrl+C.${RESET}"
138
+ echo ""
139
+ fi
140
+
141
+ # GitHub CLI
142
+ if [ "$GITHUB_CLI_ENABLED" = "true" ]; then
143
+ if command -v gh &> /dev/null; then
144
+ echo -e "${BOLD}GitHub CLI${RESET}"
145
+ if gh auth status &> /dev/null; then
146
+ echo -e " ${GREEN}✓${RESET} Already authenticated"
147
+ gh auth status 2>&1 | head -3 | sed 's/^/ /'
148
+ else
149
+ echo -e " ${YELLOW}!${RESET} Not authenticated"
150
+ read -p " Login to GitHub? [Y/n]: " -n 1 -r gh_login
151
+ echo ""
152
+ if [[ ! $gh_login =~ ^[Nn]$ ]]; then
153
+ gh auth login
154
+ fi
155
+ fi
156
+ echo ""
157
+ fi
158
+ fi
159
+
160
+ # Claude Code CLI
161
+ if [ "$CLAUDE_CODE_ENABLED" = "true" ]; then
162
+ if command -v claude &> /dev/null; then
163
+ echo -e "${BOLD}Claude Code CLI${RESET}"
164
+
165
+ # Check if API key is configured
166
+ API_KEY_SET=false
167
+ if [ -n "$ANTHROPIC_API_KEY" ] || ([ -f ".env" ] && grep -q "ANTHROPIC_API_KEY" .env 2>/dev/null); then
168
+ API_KEY_SET=true
169
+ fi
170
+
171
+ # Check if OAuth is configured (by checking if claude can run without prompting)
172
+ # We can't easily check OAuth status without running claude interactively,
173
+ # so we just check if credentials file exists
174
+ OAUTH_SET=false
175
+ if [ -f "$HOME/.claude/credentials.json" ] || [ -f "$HOME/.config/claude/credentials.json" ]; then
176
+ OAUTH_SET=true
177
+ fi
178
+
179
+ if [ "$API_KEY_SET" = "true" ]; then
180
+ echo -e " ${GREEN}✓${RESET} API key configured (for programmatic use)"
181
+ fi
182
+ if [ "$OAUTH_SET" = "true" ]; then
183
+ echo -e " ${GREEN}✓${RESET} OAuth login configured (for interactive use)"
184
+ fi
185
+
186
+ if [ "$API_KEY_SET" = "false" ] && [ "$OAUTH_SET" = "false" ]; then
187
+ echo -e " ${YELLOW}!${RESET} Not authenticated"
188
+ echo ""
189
+ echo -e " ${DIM}Claude Code supports two authentication methods:${RESET}"
190
+ echo -e " ${DIM} 1. OAuth login (recommended) - Login with Claude.ai/Console account${RESET}"
191
+ echo -e " ${DIM} 2. API key - For programmatic/CI use${RESET}"
192
+ echo ""
193
+ echo " Choose authentication method:"
194
+ echo " 1) OAuth login (recommended for interactive use)"
195
+ echo " 2) API key (for CI/programmatic use)"
196
+ echo " 3) Skip"
197
+ read -p " Enter choice [1-3]: " auth_choice
198
+
199
+ case $auth_choice in
200
+ 1)
201
+ echo ""
202
+ echo -e " ${CYAN}→${RESET} Starting Claude Code for OAuth login..."
203
+ echo -e " ${DIM}This will open a browser for authentication.${RESET}"
204
+ echo -e " ${DIM}After login, you can exit with Ctrl+C or /exit${RESET}"
205
+ echo ""
206
+ read -p " Press Enter to continue..."
207
+ claude || true
208
+ echo -e " ${GREEN}✓${RESET} OAuth setup complete"
209
+ ;;
210
+ 2)
211
+ echo ""
212
+ echo -e " ${DIM}Get your API key from: https://console.anthropic.com/settings/keys${RESET}"
213
+ read -p " Enter Anthropic API key: " api_key
214
+ if [ -n "$api_key" ]; then
215
+ # Add to .env file
216
+ if [ ! -f ".env" ]; then
217
+ echo "# Environment variables" > .env
218
+ echo "" >> .env
219
+ fi
220
+ # Remove existing key if present
221
+ if [ -f ".env" ]; then
222
+ grep -v "^ANTHROPIC_API_KEY=" .env > .env.tmp 2>/dev/null || true
223
+ mv .env.tmp .env
224
+ fi
225
+ echo "ANTHROPIC_API_KEY=$api_key" >> .env
226
+ echo -e " ${GREEN}✓${RESET} API key saved to .env"
227
+ echo -e " ${DIM}Run: source .env (or restart your shell)${RESET}"
228
+ fi
229
+ ;;
230
+ *)
231
+ echo -e " ${DIM}Skipped${RESET}"
232
+ ;;
233
+ esac
234
+ elif [ "$API_KEY_SET" = "false" ]; then
235
+ # OAuth is set but API key is not - offer to add API key for CI
236
+ read -p " Add API key for CI/programmatic use? [y/N]: " -n 1 -r add_api_key
237
+ echo ""
238
+ if [[ $add_api_key =~ ^[Yy]$ ]]; then
239
+ echo -e " ${DIM}Get your API key from: https://console.anthropic.com/settings/keys${RESET}"
240
+ read -p " Enter Anthropic API key: " api_key
241
+ if [ -n "$api_key" ]; then
242
+ if [ ! -f ".env" ]; then
243
+ echo "# Environment variables" > .env
244
+ echo "" >> .env
245
+ fi
246
+ echo "ANTHROPIC_API_KEY=$api_key" >> .env
247
+ echo -e " ${GREEN}✓${RESET} API key saved to .env"
248
+ fi
249
+ fi
250
+ fi
251
+ echo ""
252
+ fi
253
+ fi
254
+
255
+ # Google Cloud SDK
256
+ if [ "$GCLOUD_ENABLED" = "true" ]; then
257
+ echo -e "${BOLD}Google Cloud SDK${RESET}"
258
+
259
+ # Check if CLI is available
260
+ GCLOUD_CLI_AVAILABLE=false
261
+ if command -v gcloud &> /dev/null; then
262
+ GCLOUD_CLI_AVAILABLE=true
263
+ else
264
+ echo -e " ${DIM}CLI not installed yet (will be installed on container rebuild)${RESET}"
265
+ fi
266
+
267
+ # Check if project ID already in .env
268
+ GCLOUD_CONFIGURED=false
269
+ if [ -f ".env" ] && grep -q "GOOGLE_CLOUD_PROJECT" .env 2>/dev/null; then
270
+ GCLOUD_CONFIGURED=true
271
+ GCP_PROJECT=$(grep "GOOGLE_CLOUD_PROJECT" .env | cut -d= -f2)
272
+ echo -e " ${GREEN}✓${RESET} Project ID configured: $GCP_PROJECT"
273
+
274
+ read -p " Reconfigure GCP project? [y/N]: " -n 1 -r reconfig
275
+ echo ""
276
+ if [[ $reconfig =~ ^[Yy]$ ]]; then
277
+ GCLOUD_CONFIGURED=false
278
+ fi
279
+ fi
280
+
281
+ if [ "$GCLOUD_CLI_AVAILABLE" = "true" ]; then
282
+ # CLI is available - check auth status
283
+ if gcloud auth list --filter=status:ACTIVE --format="value(account)" 2>/dev/null | grep -q "@"; then
284
+ ACCOUNT=$(gcloud auth list --filter=status:ACTIVE --format="value(account)" 2>/dev/null | head -1)
285
+ echo -e " ${GREEN}✓${RESET} Authenticated as: $ACCOUNT"
286
+
287
+ # Sync project from gcloud config if not in .env
288
+ if [ "$GCLOUD_CONFIGURED" = "false" ]; then
289
+ PROJECT=$(gcloud config get-value project 2>/dev/null)
290
+ if [ -n "$PROJECT" ] && [ "$PROJECT" != "(unset)" ]; then
291
+ echo -e " ${GREEN}✓${RESET} Current gcloud project: $PROJECT"
292
+ read -p " Use this project? [Y/n]: " -n 1 -r use_current
293
+ echo ""
294
+ if [[ ! $use_current =~ ^[Nn]$ ]]; then
295
+ gcp_project="$PROJECT"
296
+ else
297
+ read -p " Enter GCP project ID: " gcp_project
298
+ fi
299
+ else
300
+ read -p " Enter GCP project ID (or press Enter to skip): " gcp_project
301
+ fi
302
+
303
+ if [ -n "$gcp_project" ]; then
304
+ gcloud config set project "$gcp_project"
305
+ # Save to .env
306
+ if [ ! -f ".env" ]; then
307
+ echo "# Environment variables" > .env
308
+ echo "" >> .env
309
+ fi
310
+ grep -v "^GOOGLE_CLOUD_PROJECT=" .env > .env.tmp 2>/dev/null || true
311
+ mv .env.tmp .env
312
+ echo "GOOGLE_CLOUD_PROJECT=$gcp_project" >> .env
313
+ echo -e " ${GREEN}✓${RESET} Project set to: $gcp_project"
314
+ fi
315
+ fi
316
+ else
317
+ echo -e " ${YELLOW}!${RESET} Not authenticated"
318
+ read -p " Login to Google Cloud? [Y/n]: " -n 1 -r gcloud_login
319
+ echo ""
320
+ if [[ ! $gcloud_login =~ ^[Nn]$ ]]; then
321
+ gcloud auth login
322
+ read -p " Enter GCP project ID (or press Enter to skip): " gcp_project
323
+ if [ -n "$gcp_project" ]; then
324
+ gcloud config set project "$gcp_project"
325
+ # Save to .env
326
+ if [ ! -f ".env" ]; then
327
+ echo "# Environment variables" > .env
328
+ echo "" >> .env
329
+ fi
330
+ grep -v "^GOOGLE_CLOUD_PROJECT=" .env > .env.tmp 2>/dev/null || true
331
+ mv .env.tmp .env
332
+ echo "GOOGLE_CLOUD_PROJECT=$gcp_project" >> .env
333
+ echo -e " ${GREEN}✓${RESET} Project saved to .env"
334
+ fi
335
+ fi
336
+ fi
337
+ else
338
+ # CLI not available - just prompt for project ID to save in .env
339
+ if [ "$GCLOUD_CONFIGURED" = "false" ]; then
340
+ echo ""
341
+ echo -e " ${DIM}Enter your GCP project ID to save for later use.${RESET}"
342
+ read -p " Enter GCP project ID (or press Enter to skip): " gcp_project
343
+
344
+ if [ -n "$gcp_project" ]; then
345
+ # Save to .env
346
+ if [ ! -f ".env" ]; then
347
+ echo "# Environment variables" > .env
348
+ echo "" >> .env
349
+ fi
350
+ grep -v "^GOOGLE_CLOUD_PROJECT=" .env > .env.tmp 2>/dev/null || true
351
+ mv .env.tmp .env
352
+ echo "GOOGLE_CLOUD_PROJECT=$gcp_project" >> .env
353
+ echo -e " ${GREEN}✓${RESET} Project ID saved to .env"
354
+ echo -e " ${DIM}Run 'gcloud auth login' after CLI is installed${RESET}"
355
+ else
356
+ echo -e " ${DIM}Skipped${RESET}"
357
+ fi
358
+ fi
359
+ fi
360
+ echo ""
361
+ fi
362
+
363
+ # Pulumi
364
+ if [ "$PULUMI_ENABLED" = "true" ]; then
365
+ echo -e "${BOLD}Pulumi${RESET}"
366
+
367
+ # Check if pulumi is in PATH or in ~/.pulumi/bin
368
+ PULUMI_CMD=""
369
+ if command -v pulumi &> /dev/null; then
370
+ PULUMI_CMD="pulumi"
371
+ elif [ -f "$HOME/.pulumi/bin/pulumi" ]; then
372
+ PULUMI_CMD="$HOME/.pulumi/bin/pulumi"
373
+ fi
374
+
375
+ if [ -n "$PULUMI_CMD" ]; then
376
+ if $PULUMI_CMD whoami &> /dev/null; then
377
+ PULUMI_USER=$($PULUMI_CMD whoami 2>/dev/null)
378
+ echo -e " ${GREEN}✓${RESET} Logged in as: $PULUMI_USER"
379
+ else
380
+ echo -e " ${YELLOW}!${RESET} Not logged in"
381
+ read -p " Login to Pulumi? [Y/n]: " -n 1 -r pulumi_login
382
+ echo ""
383
+ if [[ ! $pulumi_login =~ ^[Nn]$ ]]; then
384
+ $PULUMI_CMD login
385
+ fi
386
+ fi
387
+ else
388
+ echo -e " ${DIM}CLI not installed yet (will be installed on container rebuild)${RESET}"
389
+ echo -e " ${DIM}Run 'pulumi login' after CLI is installed${RESET}"
390
+ fi
391
+ echo ""
392
+ fi
393
+
394
+ # Infisical
395
+ if [ "$INFISICAL_ENABLED" = "true" ]; then
396
+ echo -e "${BOLD}Infisical (Secrets Management)${RESET}"
397
+
398
+ # Check if CLI is available
399
+ INFISICAL_CLI_AVAILABLE=false
400
+ if command -v infisical &> /dev/null; then
401
+ INFISICAL_CLI_AVAILABLE=true
402
+ else
403
+ echo -e " ${DIM}CLI not installed yet (will be installed on container rebuild)${RESET}"
404
+ fi
405
+
406
+ # Check if already configured in .env
407
+ INFISICAL_CONFIGURED=false
408
+ if [ -f ".env" ] && grep -q "INFISICAL_TOKEN" .env 2>/dev/null; then
409
+ INFISICAL_CONFIGURED=true
410
+ echo -e " ${GREEN}✓${RESET} Infisical token configured in .env"
411
+
412
+ # Show current config
413
+ if grep -q "INFISICAL_PROJECT_ID" .env 2>/dev/null; then
414
+ PROJECT_ID=$(grep "INFISICAL_PROJECT_ID" .env | cut -d= -f2)
415
+ echo -e " ${GREEN}✓${RESET} Project ID: $PROJECT_ID"
416
+ fi
417
+ if grep -q "INFISICAL_ENV" .env 2>/dev/null; then
418
+ ENV_NAME=$(grep "INFISICAL_ENV" .env | cut -d= -f2)
419
+ echo -e " ${GREEN}✓${RESET} Environment: $ENV_NAME"
420
+ fi
421
+
422
+ read -p " Reconfigure Infisical? [y/N]: " -n 1 -r reconfig
423
+ echo ""
424
+ if [[ ! $reconfig =~ ^[Yy]$ ]]; then
425
+ INFISICAL_CONFIGURED=true
426
+ else
427
+ INFISICAL_CONFIGURED=false
428
+ fi
429
+ fi
430
+
431
+ if [ "$INFISICAL_CONFIGURED" = "false" ]; then
432
+ echo ""
433
+ echo -e " ${DIM}Configure Infisical to pull secrets from a specific project/path.${RESET}"
434
+ echo -e " ${DIM}Get your service token from: https://app.infisical.com${RESET}"
435
+ echo ""
436
+
437
+ # Service Token
438
+ read -p " Enter Infisical Service Token (or press Enter to skip): " infisical_token
439
+
440
+ if [ -n "$infisical_token" ]; then
441
+ # Project ID
442
+ read -p " Enter Project ID: " project_id
443
+
444
+ # Environment
445
+ echo " Environment options: dev, staging, prod (or custom)"
446
+ read -p " Enter environment [dev]: " env_name
447
+ env_name="${env_name:-dev}"
448
+
449
+ # Save to .env
450
+ if [ ! -f ".env" ]; then
451
+ echo "# Environment variables" > .env
452
+ echo "" >> .env
453
+ fi
454
+
455
+ # Remove existing Infisical config if present
456
+ if [ -f ".env" ]; then
457
+ grep -v "^INFISICAL_" .env > .env.tmp 2>/dev/null || true
458
+ mv .env.tmp .env
459
+ fi
460
+
461
+ # Add Infisical config
462
+ cat >> .env << INFISICAL_EOF
463
+
464
+ # Infisical Configuration
465
+ INFISICAL_TOKEN=$infisical_token
466
+ INFISICAL_PROJECT_ID=$project_id
467
+ INFISICAL_ENV=$env_name
468
+ INFISICAL_EOF
469
+
470
+ echo -e " ${GREEN}✓${RESET} Infisical configuration saved to .env"
471
+
472
+ # Offer to pull secrets now (only if CLI is available)
473
+ if [ "$INFISICAL_CLI_AVAILABLE" = "true" ]; then
474
+ read -p " Pull secrets now? [Y/n]: " -n 1 -r pull_now
475
+ echo ""
476
+ if [[ ! $pull_now =~ ^[Nn]$ ]]; then
477
+ echo -e " ${CYAN}→${RESET} Pulling secrets (env: $env_name)..."
478
+
479
+ # Export secrets using the token
480
+ if INFISICAL_TOKEN="$infisical_token" infisical export \
481
+ --projectId="$project_id" \
482
+ --env="$env_name" \
483
+ --format=dotenv > .env.secrets 2>/dev/null; then
484
+
485
+ # Merge secrets (avoiding duplicates and Infisical config)
486
+ while IFS= read -r line; do
487
+ # Skip empty lines and comments
488
+ [[ -z "$line" || "$line" == \#* ]] && continue
489
+ key=$(echo "$line" | cut -d= -f1)
490
+ # Don't overwrite Infisical config vars
491
+ if [[ "$key" != INFISICAL_* ]] && ! grep -q "^$key=" .env 2>/dev/null; then
492
+ echo "$line" >> .env
493
+ fi
494
+ done < .env.secrets
495
+ rm .env.secrets
496
+
497
+ echo -e " ${GREEN}✓${RESET} Secrets pulled and merged into .env"
498
+ else
499
+ echo -e " ${YELLOW}!${RESET} Failed to pull secrets (check token/permissions)"
500
+ rm -f .env.secrets
501
+ fi
502
+ fi
503
+ else
504
+ echo -e " ${DIM}Run 'make secrets-pull' after CLI is installed to pull secrets${RESET}"
505
+ fi
506
+
507
+ echo ""
508
+ echo -e " ${DIM}To pull secrets later: make secrets-pull${RESET}"
509
+ else
510
+ echo -e " ${DIM}Skipped${RESET}"
511
+ fi
512
+ fi
513
+ echo ""
514
+ fi
515
+
516
+ # ============================================================================
517
+ # Create .env template if it doesn't exist
518
+ # ============================================================================
519
+
520
+ if [ ! -f ".env" ] && [ ! -f ".env.example" ]; then
521
+ echo -e "${BOLD}Environment Variables${RESET}"
522
+ echo ""
523
+
524
+ # Check what tools are enabled and create appropriate .env.example
525
+ cat > .env.example << 'ENVEOF'
526
+ # Environment Variables
527
+ # Copy this file to .env and fill in your values
528
+ # cp .env.example .env
529
+
530
+ ENVEOF
531
+
532
+ if [ "$CLAUDE_CODE_ENABLED" = "true" ]; then
533
+ echo "# Claude Code" >> .env.example
534
+ echo "ANTHROPIC_API_KEY=your-api-key-here" >> .env.example
535
+ echo "" >> .env.example
536
+ fi
537
+
538
+ if [ "$GCLOUD_ENABLED" = "true" ]; then
539
+ echo "# Google Cloud" >> .env.example
540
+ echo "GOOGLE_CLOUD_PROJECT=your-project-id" >> .env.example
541
+ echo "# GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json" >> .env.example
542
+ echo "" >> .env.example
543
+ fi
544
+
545
+ if [ "$INFISICAL_ENABLED" = "true" ]; then
546
+ echo "# Infisical - Get service token from https://app.infisical.com" >> .env.example
547
+ echo "INFISICAL_TOKEN=your-service-token" >> .env.example
548
+ echo "INFISICAL_PROJECT_ID=your-project-id" >> .env.example
549
+ echo "INFISICAL_ENV=dev" >> .env.example
550
+ echo "" >> .env.example
551
+ fi
552
+
553
+ if [ "$PULUMI_ENABLED" = "true" ]; then
554
+ echo "# Pulumi" >> .env.example
555
+ echo "# PULUMI_ACCESS_TOKEN=your-token-here" >> .env.example
556
+ echo "" >> .env.example
557
+ fi
558
+
559
+ echo -e " ${GREEN}✓${RESET} Created .env.example template"
560
+ echo -e " ${DIM}Copy to .env and fill in your values: cp .env.example .env${RESET}"
561
+ echo ""
562
+ fi
563
+
564
+ # ============================================================================
565
+ # Summary
566
+ # ============================================================================
567
+
568
+ echo ""
569
+ echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
570
+ echo -e "${GREEN} SETUP COMPLETE${RESET}"
571
+ echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
572
+ echo ""
573
+ echo -e "${BOLD}Next steps:${RESET}"
574
+ echo " • Run ${CYAN}make doctor${RESET} to verify your setup"
575
+ echo " • Run ${CYAN}make help${RESET} to see available commands"
576
+ echo " • Run ${CYAN}make check${RESET} to verify quality and tests"
577
+ echo ""
578
+ if [ -f ".env.example" ] && [ ! -f ".env" ]; then
579
+ echo -e "${YELLOW}Don't forget:${RESET} Copy .env.example to .env and add your secrets"
580
+ echo ""
581
+ fi