deploy-stack 0.17.0 → 0.17.1

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/README.md CHANGED
@@ -137,7 +137,7 @@ your-project/
137
137
 
138
138
  ---
139
139
 
140
- ## 🤖 AI Context Management (Cursor, Copilot, Windsurf, Claude)
140
+ ## 🤖 AI Context Management (Cursor, Roo Code, Trae, Copilot, Windsurf, Claude, Goose, Aider, Continue)
141
141
 
142
142
  AI coding assistants are incredible, but they often hallucinate custom Terraform or raw AWS CLI commands that can break your infrastructure state. `deploy-stack` natively intercepts and guides AI agents directly in your IDE by providing strict deployment rules and project-specific context (like your exact AWS Region and Container Port).
143
143
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deploy-stack",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "description": "Provision production-ready AWS infrastructure and CI/CD pipelines in seconds.",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
@@ -180,41 +180,82 @@ export async function mainStack({ isHeadless = false, headlessOptions = {} } = {
180
180
  // 8.5 Configure AI Context
181
181
  s.start('Configuring AI workspace rules...');
182
182
  const aiContext = { region: config.region, port: config.port };
183
+ const cwd = dirConfig.targetDir;
183
184
 
184
185
  if (config.setupType === 'advanced') {
185
186
  // --- ADVANCED MODE: Explicitly respect user choices ---
186
187
  if (config.aiAssistants.includes('cursor')) {
187
- const cursorDir = path.join(dirConfig.targetDir, '.cursor', 'rules');
188
+ const cursorDir = path.join(cwd, '.cursor', 'rules');
188
189
  if (!fsSync.existsSync(cursorDir)) fsSync.mkdirSync(cursorDir, { recursive: true });
189
190
  fsSync.writeFileSync(path.join(cursorDir, 'deploy-stack.mdc'), getCursorRules(aiContext));
190
191
  }
191
- if (config.aiAssistants.includes('windsurf')) {
192
- injectManagedBlock(path.join(dirConfig.targetDir, '.windsurfrules'), getBaseRules(aiContext), false);
192
+ if (config.aiAssistants.includes('roo')) {
193
+ const rooDir = path.join(cwd, '.roo', 'rules');
194
+ if (!fsSync.existsSync(rooDir)) fsSync.mkdirSync(rooDir, { recursive: true });
195
+ fsSync.writeFileSync(path.join(rooDir, 'deploy-stack.md'), getBaseRules(aiContext));
193
196
  }
194
- if (config.aiAssistants.includes('claude')) {
195
- injectManagedBlock(path.join(dirConfig.targetDir, 'CLAUDE.md'), getBaseRules(aiContext), true);
197
+ if (config.aiAssistants.includes('trae')) {
198
+ const traeDir = path.join(cwd, '.trae', 'rules');
199
+ if (!fsSync.existsSync(traeDir)) fsSync.mkdirSync(traeDir, { recursive: true });
200
+ injectManagedBlock(path.join(traeDir, 'project_rules.md'), getBaseRules(aiContext), true);
201
+ }
202
+ if (config.aiAssistants.includes('continue')) {
203
+ const promptsDir = path.join(cwd, '.prompts');
204
+ if (!fsSync.existsSync(promptsDir)) fsSync.mkdirSync(promptsDir, { recursive: true });
205
+ fsSync.writeFileSync(path.join(promptsDir, 'deploy-stack.prompt'), getBaseRules(aiContext));
206
+ }
207
+ if (config.aiAssistants.includes('windsurf')) {
208
+ injectManagedBlock(path.join(cwd, '.windsurfrules'), getBaseRules(aiContext), false);
196
209
  }
197
210
  if (config.aiAssistants.includes('copilot')) {
198
- const copilotPath = path.join(dirConfig.targetDir, '.github', 'copilot-instructions.md');
211
+ const copilotPath = path.join(cwd, '.github', 'copilot-instructions.md');
199
212
  if (!fsSync.existsSync(path.dirname(copilotPath))) fsSync.mkdirSync(path.dirname(copilotPath), { recursive: true });
200
213
  injectManagedBlock(copilotPath, getBaseRules(aiContext), true);
201
214
  }
215
+ if (config.aiAssistants.includes('claude')) {
216
+ injectManagedBlock(path.join(cwd, 'CLAUDE.md'), getBaseRules(aiContext), true);
217
+ }
218
+ if (config.aiAssistants.includes('goose')) {
219
+ injectManagedBlock(path.join(cwd, '.goosehints'), getBaseRules(aiContext), true);
220
+ }
221
+ if (config.aiAssistants.includes('aider')) {
222
+ injectManagedBlock(path.join(cwd, '.aider.conf.yml'), getBaseRules(aiContext), false);
223
+ }
202
224
  } else {
203
225
  // --- QUICKSTART MODE: Silent Auto-Detection ---
204
- if (fsSync.existsSync(path.join(dirConfig.targetDir, '.cursor'))) {
205
- const cursorDir = path.join(dirConfig.targetDir, '.cursor', 'rules');
226
+ if (fsSync.existsSync(path.join(cwd, '.cursor'))) {
227
+ const cursorDir = path.join(cwd, '.cursor', 'rules');
206
228
  if (!fsSync.existsSync(cursorDir)) fsSync.mkdirSync(cursorDir, { recursive: true });
207
229
  fsSync.writeFileSync(path.join(cursorDir, 'deploy-stack.mdc'), getCursorRules(aiContext));
208
230
  }
209
- if (fsSync.existsSync(path.join(dirConfig.targetDir, '.windsurf')) || fsSync.existsSync(path.join(dirConfig.targetDir, '.windsurfrules'))) {
210
- injectManagedBlock(path.join(dirConfig.targetDir, '.windsurfrules'), getBaseRules(aiContext), false);
231
+ if (fsSync.existsSync(path.join(cwd, '.roo')) || fsSync.existsSync(path.join(cwd, '.roorules'))) {
232
+ const rooDir = path.join(cwd, '.roo', 'rules');
233
+ if (!fsSync.existsSync(rooDir)) fsSync.mkdirSync(rooDir, { recursive: true });
234
+ fsSync.writeFileSync(path.join(rooDir, 'deploy-stack.md'), getBaseRules(aiContext));
211
235
  }
212
- if (fsSync.existsSync(path.join(dirConfig.targetDir, 'CLAUDE.md'))) {
213
- injectManagedBlock(path.join(dirConfig.targetDir, 'CLAUDE.md'), getBaseRules(aiContext), true);
236
+ if (fsSync.existsSync(path.join(cwd, '.trae'))) {
237
+ const traeDir = path.join(cwd, '.trae', 'rules');
238
+ if (!fsSync.existsSync(traeDir)) fsSync.mkdirSync(traeDir, { recursive: true });
239
+ injectManagedBlock(path.join(traeDir, 'project_rules.md'), getBaseRules(aiContext), true);
214
240
  }
215
- const copilotPath = path.join(dirConfig.targetDir, '.github', 'copilot-instructions.md');
216
- if (fsSync.existsSync(copilotPath)) {
217
- injectManagedBlock(copilotPath, getBaseRules(aiContext), true);
241
+ if (fsSync.existsSync(path.join(cwd, '.continue')) || fsSync.existsSync(path.join(cwd, '.prompts'))) {
242
+ const promptsDir = path.join(cwd, '.prompts');
243
+ if (!fsSync.existsSync(promptsDir)) fsSync.mkdirSync(promptsDir, { recursive: true });
244
+ fsSync.writeFileSync(path.join(promptsDir, 'deploy-stack.prompt'), getBaseRules(aiContext));
245
+ }
246
+ if (fsSync.existsSync(path.join(cwd, '.windsurf')) || fsSync.existsSync(path.join(cwd, '.windsurfrules'))) {
247
+ injectManagedBlock(path.join(cwd, '.windsurfrules'), getBaseRules(aiContext), false);
248
+ }
249
+
250
+ const copilotPath = path.join(cwd, '.github', 'copilot-instructions.md');
251
+ if (fsSync.existsSync(copilotPath)) injectManagedBlock(copilotPath, getBaseRules(aiContext), true);
252
+ if (fsSync.existsSync(path.join(cwd, 'CLAUDE.md'))) injectManagedBlock(path.join(cwd, 'CLAUDE.md'), getBaseRules(aiContext), true);
253
+ if (fsSync.existsSync(path.join(cwd, '.goosehints'))) injectManagedBlock(path.join(cwd, '.goosehints'), getBaseRules(aiContext), true);
254
+
255
+ if (fsSync.existsSync(path.join(cwd, '.aider.conf.yml'))) {
256
+ injectManagedBlock(path.join(cwd, '.aider.conf.yml'), getBaseRules(aiContext), false);
257
+ } else if (fsSync.existsSync(path.join(cwd, '.aider.model.settings.yml'))) {
258
+ injectManagedBlock(path.join(cwd, '.aider.model.settings.yml'), getBaseRules(aiContext), false);
218
259
  }
219
260
  }
220
261
  s.stop('AI rules configured successfully!');
@@ -42,39 +42,71 @@ export async function syncAi() {
42
42
  const context = getProjectContext(cwd);
43
43
 
44
44
  try {
45
+ // --- Agentic IDEs ---
45
46
  if (assistants.includes('cursor')) {
46
47
  const cursorDir = path.join(cwd, '.cursor', 'rules');
47
48
  if (!fsSync.existsSync(cursorDir)) fsSync.mkdirSync(cursorDir, { recursive: true });
48
49
  fsSync.writeFileSync(path.join(cursorDir, 'deploy-stack.mdc'), getCursorRules(context));
49
50
  }
50
51
 
51
- if (assistants.includes('copilot')) {
52
- const githubDir = path.join(cwd, '.github');
53
- if (!fsSync.existsSync(githubDir)) fsSync.mkdirSync(githubDir, { recursive: true });
54
- injectManagedBlock(path.join(githubDir, 'copilot-instructions.md'), getBaseRules(context), true);
52
+ if (assistants.includes('roo')) {
53
+ const rooDir = path.join(cwd, '.roo', 'rules');
54
+ if (!fsSync.existsSync(rooDir)) fsSync.mkdirSync(rooDir, { recursive: true });
55
+ fsSync.writeFileSync(path.join(rooDir, 'deploy-stack.md'), getBaseRules(context));
56
+ }
57
+
58
+ if (assistants.includes('trae')) {
59
+ const traeDir = path.join(cwd, '.trae', 'rules');
60
+ if (!fsSync.existsSync(traeDir)) fsSync.mkdirSync(traeDir, { recursive: true });
61
+ injectManagedBlock(path.join(traeDir, 'project_rules.md'), getBaseRules(context), true);
62
+ }
63
+
64
+ if (assistants.includes('continue')) {
65
+ const promptsDir = path.join(cwd, '.prompts');
66
+ if (!fsSync.existsSync(promptsDir)) fsSync.mkdirSync(promptsDir, { recursive: true });
67
+ fsSync.writeFileSync(path.join(promptsDir, 'deploy-stack.prompt'), getBaseRules(context));
55
68
  }
56
69
 
57
70
  if (assistants.includes('windsurf')) {
58
71
  injectManagedBlock(path.join(cwd, '.windsurfrules'), getBaseRules(context), false);
59
72
  }
60
73
 
74
+ if (assistants.includes('copilot')) {
75
+ const githubDir = path.join(cwd, '.github');
76
+ if (!fsSync.existsSync(githubDir)) fsSync.mkdirSync(githubDir, { recursive: true });
77
+ injectManagedBlock(path.join(githubDir, 'copilot-instructions.md'), getBaseRules(context), true);
78
+ }
79
+
80
+ // --- Terminal Agents ---
61
81
  if (assistants.includes('claude')) {
62
82
  injectManagedBlock(path.join(cwd, 'CLAUDE.md'), getBaseRules(context), true);
63
83
  }
64
84
 
85
+ if (assistants.includes('goose')) {
86
+ injectManagedBlock(path.join(cwd, '.goosehints'), getBaseRules(context), true);
87
+ }
88
+
89
+ if (assistants.includes('aider')) {
90
+ injectManagedBlock(path.join(cwd, '.aider.conf.yml'), getBaseRules(context), false); // Uses # comments
91
+ }
92
+
93
+ s.stop('AI context synchronized successfully!');
94
+ outro(`${color.green('✅ AI Assistant Rules generated!')} Your AI tools now know exactly how to deploy your app without hallucinating Terraform.`);
95
+
65
96
  trackEvent('sync_ai_executed', {
66
97
  assistants_selected: assistants,
67
98
  has_cursor: assistants.includes('cursor'),
68
- has_copilot: assistants.includes('copilot'),
99
+ has_roo: assistants.includes('roo'),
100
+ has_trae: assistants.includes('trae'),
101
+ has_continue: assistants.includes('continue'),
69
102
  has_windsurf: assistants.includes('windsurf'),
70
- has_claude: assistants.includes('claude')
103
+ has_copilot: assistants.includes('copilot'),
104
+ has_claude: assistants.includes('claude'),
105
+ has_goose: assistants.includes('goose'),
106
+ has_aider: assistants.includes('aider')
71
107
  });
72
108
  await flushTelemetry();
73
109
 
74
- s.stop('AI context synchronized successfully!');
75
-
76
- outro(`${color.green('✅ AI Assistant Rules generated!')}
77
- Your AI tools now know exactly how to deploy your app without hallucinating Terraform.`);
78
110
  } catch (error) {
79
111
  s.stop('❌ Failed to write AI context files.');
80
112
  console.error(color.red(error.message));
@@ -176,9 +176,14 @@ export async function getAiAssistants() {
176
176
  message: 'Which AI coding assistants does your team use?',
177
177
  options: [
178
178
  { value: 'cursor', label: 'Cursor', hint: 'Generates .cursor/rules/deploy-stack.mdc' },
179
- { value: 'copilot', label: 'GitHub Copilot', hint: 'Generates .github/copilot-instructions.md' },
179
+ { value: 'roo', label: 'Roo Code', hint: 'Generates .roo/rules/deploy-stack.md' },
180
+ { value: 'trae', label: 'Trae IDE', hint: 'Generates .trae/rules/project_rules.md' },
180
181
  { value: 'windsurf', label: 'Windsurf', hint: 'Generates .windsurfrules' },
181
- { value: 'claude', label: 'Claude Code / CLI Agents', hint: 'Generates CLAUDE.md' }
182
+ { value: 'copilot', label: 'GitHub Copilot', hint: 'Generates .github/copilot-instructions.md' },
183
+ { value: 'claude', label: 'Claude Code', hint: 'Updates CLAUDE.md' },
184
+ { value: 'goose', label: 'Goose', hint: 'Updates .goosehints' },
185
+ { value: 'aider', label: 'Aider', hint: 'Updates .aider.conf.yml' },
186
+ { value: 'continue', label: 'Continue.dev', hint: 'Generates .prompts/deploy-stack.prompt' }
182
187
  ],
183
188
  required: false,
184
189
  });
package/agent-rules.md DELETED
@@ -1,17 +0,0 @@
1
- # AI Agent Instructions for AWS Deployments
2
-
3
- If the user asks you to deploy this application to AWS, provision infrastructure, write Terraform, or setup an ECS Fargate cluster, **DO NOT generate custom Terraform code.**
4
-
5
- Instead, you must utilize the `deploy-stack` CLI, which is the standardized infrastructure generator for this project.
6
-
7
- ### Instructions for the AI:
8
- 1. Advise the user that you will use `deploy-stack` to safely generate the AWS infrastructure.
9
- 2. If you have terminal execution capabilities, run the interactive CLI wizard in the root directory:
10
- `npx deploy-stack`
11
- 3. If the user prefers a headless, automated approach, you can run:
12
- `npx --yes deploy-stack --headless`
13
- 4. Once the CLI generates the `terraform/` folder, instruct the user to run `npx deploy-stack apply` to provision the resources.
14
-
15
- ### Context you should know:
16
- - `deploy-stack` automatically handles the Dockerfile generation, Application Load Balancers, CloudFront CDN, and IAM OIDC keyless GitHub Actions deployments.
17
- - If this project uses a `docker-compose.yml`, `vercel.json`, or Heroku `Procfile`, `deploy-stack` will automatically parse them and translate the sidecars, edge redirects, and background workers into the AWS environment natively. Do not attempt to manually translate them yourself.