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 +1 -1
- package/package.json +1 -1
- package/src/commands/init.js +56 -15
- package/src/commands/sync-ai.js +42 -10
- package/src/utils/prompts.js +7 -2
- package/agent-rules.md +0 -17
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
package/src/commands/init.js
CHANGED
|
@@ -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(
|
|
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('
|
|
192
|
-
|
|
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('
|
|
195
|
-
|
|
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(
|
|
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(
|
|
205
|
-
const cursorDir = path.join(
|
|
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(
|
|
210
|
-
|
|
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(
|
|
213
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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!');
|
package/src/commands/sync-ai.js
CHANGED
|
@@ -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('
|
|
52
|
-
const
|
|
53
|
-
if (!fsSync.existsSync(
|
|
54
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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));
|
package/src/utils/prompts.js
CHANGED
|
@@ -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: '
|
|
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: '
|
|
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.
|