@factiii/stack 0.1.200 → 0.1.203

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 (48) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +403 -403
  3. package/bin/stack +334 -334
  4. package/dist/cli/dev-sync.js +16 -16
  5. package/dist/plugins/addons/auth/index.js +7 -7
  6. package/dist/plugins/addons/vercel/index.js +9 -9
  7. package/dist/plugins/addons/vercel/scanfix/config.js +10 -10
  8. package/dist/plugins/addons/vercel/scanfix/token.js +15 -15
  9. package/dist/plugins/approved.json +13 -13
  10. package/dist/plugins/pipelines/aws/index.js +12 -12
  11. package/dist/plugins/pipelines/aws/policies/bootstrap-policy.json +135 -135
  12. package/dist/plugins/pipelines/aws/prod.js +1 -1
  13. package/dist/plugins/pipelines/factiii/index.d.ts.map +1 -1
  14. package/dist/plugins/pipelines/factiii/index.js +2 -14
  15. package/dist/plugins/pipelines/factiii/index.js.map +1 -1
  16. package/dist/plugins/pipelines/factiii/prod.js +21 -21
  17. package/dist/plugins/pipelines/factiii/scanfix/port-convention.d.ts.map +1 -1
  18. package/dist/plugins/pipelines/factiii/scanfix/port-convention.js +2 -4
  19. package/dist/plugins/pipelines/factiii/scanfix/port-convention.js.map +1 -1
  20. package/dist/plugins/pipelines/factiii/staging.js +23 -23
  21. package/dist/plugins/pipelines/factiii/workflows/stack-ci.yml +75 -75
  22. package/dist/plugins/pipelines/factiii/workflows/stack-cicd-prod.yml +73 -73
  23. package/dist/plugins/servers/amazon-linux/index.js +16 -16
  24. package/dist/plugins/servers/mac/index.js +12 -12
  25. package/dist/plugins/servers/mac/staging.js +2 -2
  26. package/dist/plugins/servers/ubuntu/index.js +23 -23
  27. package/dist/plugins/servers/windows/index.js +15 -15
  28. package/dist/scanfix/commands/mac.d.ts.map +1 -1
  29. package/dist/scanfix/commands/mac.js +5 -4
  30. package/dist/scanfix/commands/mac.js.map +1 -1
  31. package/dist/scanfix/fixes/certbot.d.ts.map +1 -1
  32. package/dist/scanfix/fixes/certbot.js +4 -18
  33. package/dist/scanfix/fixes/certbot.js.map +1 -1
  34. package/dist/scanfix/fixes/docker.d.ts.map +1 -1
  35. package/dist/scanfix/fixes/docker.js +5 -14
  36. package/dist/scanfix/fixes/docker.js.map +1 -1
  37. package/dist/scanfix/ssl-cert-helper.d.ts.map +1 -1
  38. package/dist/scanfix/ssl-cert-helper.js +18 -4
  39. package/dist/scanfix/ssl-cert-helper.js.map +1 -1
  40. package/dist/scripts/generate-all.js +73 -73
  41. package/dist/utils/deployment-report.js +2 -2
  42. package/dist/utils/secret-prompts.js +34 -34
  43. package/dist/utils/template-generator.js +74 -74
  44. package/package.json +100 -114
  45. package/dist/plugins/pipelines/factiii/scanfix/docker.d.ts +0 -20
  46. package/dist/plugins/pipelines/factiii/scanfix/docker.d.ts.map +0 -1
  47. package/dist/plugins/pipelines/factiii/scanfix/docker.js +0 -131
  48. package/dist/plugins/pipelines/factiii/scanfix/docker.js.map +0 -1
package/bin/stack CHANGED
@@ -1,334 +1,334 @@
1
- #!/usr/bin/env node
2
-
3
- const { program } = require('commander');
4
- const path = require('path');
5
-
6
- // Import plugin command registration
7
- const { registerPluginCommands } = require('../dist/cli/plugin-commands');
8
-
9
- // Import CLI commands from compiled TypeScript
10
- const { init } = require('../dist/cli/init');
11
- const { scan } = require('../dist/cli/scan');
12
- const { fix } = require('../dist/cli/fix');
13
- const { deploy } = require('../dist/cli/deploy');
14
- const { prCheck } = require('../dist/cli/pr-check');
15
- const { undeploy } = require('../dist/cli/undeploy');
16
- const { secrets } = require('../dist/cli/secrets');
17
- const { upgrade } = require('../dist/cli/upgrade');
18
- const { validate } = require('../dist/cli/validate');
19
- const { checkConfig } = require('../dist/cli/check-config');
20
- const { devSync } = require('../dist/cli/dev-sync');
21
- const { devReset } = require('../dist/cli/dev-reset');
22
-
23
- // Read version from package.json
24
- const packageJson = require('../package.json');
25
-
26
- program
27
- .name('stack')
28
- .description('Stack - Infrastructure management CLI')
29
- .version(packageJson.version);
30
-
31
- // Init command (primary setup command)
32
- program
33
- .command('init')
34
- .description('Initialize Stack in your project (run this first)')
35
- .option('-f, --force', 'Overwrite existing config files')
36
- .action(async (options) => {
37
- await init(options);
38
- });
39
-
40
- // Default action: run scan when no subcommand provided (self-bootstrapping)
41
- program
42
- .action(async (options, command) => {
43
- if (command.args.length === 0) {
44
- await scan({});
45
- }
46
- });
47
-
48
- // Scan command (explicit)
49
- program
50
- .command('scan')
51
- .description('Scan all environments for issues')
52
- .option('--dev', 'Scan dev environment only')
53
- .option('--staging', 'Scan staging environment only')
54
- .option('--prod', 'Scan production environment only')
55
- .option('--secrets', 'Scan secrets only')
56
- .option('--commit <hash>', 'Commit hash to verify (for deployment verification)')
57
- .action(async (options) => {
58
- await scan(options);
59
- });
60
-
61
- // Fix command
62
- program
63
- .command('fix')
64
- .description('Fix all environments including uploading secrets to GitHub and installing server dependencies')
65
- .option('--dev', 'Fix dev environment only')
66
- .option('--staging', 'Fix staging environment only')
67
- .option('--prod', 'Fix production environment only')
68
- .option('--secrets', 'Fix secrets only')
69
- .option('--token <token>', 'GitHub token (or set GITHUB_TOKEN env var)')
70
- .option('--continue-on-error', 'Continue even if some fixes fail')
71
- .action(async (options) => {
72
- await fix(options);
73
- });
74
-
75
- // PR Check command (validates builds before merge)
76
- program
77
- .command('pr-check')
78
- .description('Run server/client/mobile builds and report to GitHub (for PR validation)')
79
- .option('--staging', 'Run on staging server')
80
- .action(async (options) => {
81
- const result = await prCheck(options);
82
- if (!result.success) {
83
- process.exit(1);
84
- }
85
- });
86
-
87
- // Deploy command (also handles secrets management via --secrets [action])
88
- program
89
- .command('deploy [secretName]')
90
- .description('Deploy to environments, or manage secrets with --secrets <action>')
91
- .option('--dev', 'Deploy dev environment only (local containers)')
92
- .option('--staging', 'Deploy staging environment only')
93
- .option('--prod', 'Deploy production environment only')
94
- .option('-e, --environment <env>', 'Environment (staging|prod)')
95
- .option('-b, --branch <branch>', 'Branch to deploy from (defaults to current branch)')
96
- .option('--commit <hash>', 'Commit hash to deploy (passed by workflow)')
97
- .option('--token <token>', 'GitHub token (or set GITHUB_TOKEN env var)')
98
- .option('--secrets [action]', 'Manage or deploy secrets (list|set|get|delete|check|set-env|list-env|delete-env|deploy|write-ssh-keys)')
99
- .option('--value <value>', 'Secret value (for scripting)')
100
- .option('--restart', 'Restart containers after deploying secrets')
101
- .option('--dry-run', 'Show deployment plan without actually deploying')
102
- .action(async (secretName, options) => {
103
- // --secrets with an action string = vault management mode
104
- if (typeof options.secrets === 'string') {
105
- const action = options.secrets;
106
- const validActions = ['list', 'set', 'get', 'delete', 'check', 'set-env', 'list-env', 'delete-env', 'deploy', 'write-ssh-keys'];
107
-
108
- if (!validActions.includes(action)) {
109
- console.log('');
110
- console.log('Unknown secrets action: ' + action);
111
- console.log('');
112
- console.log('Usage: npx stack deploy --secrets <action> [name] [options]');
113
- console.log('');
114
- console.log('Actions:');
115
- console.log(' list List all secrets (SSH keys + env vars)');
116
- console.log(' set <name> Set a secret (STAGING_SSH, PROD_SSH, etc.)');
117
- console.log(' get <name> Show a secret value');
118
- console.log(' delete <name> Delete a secret from vault');
119
- console.log(' check [name] Check if secrets exist');
120
- console.log(' set-env <name> Set environment variable for a stage');
121
- console.log(' list-env List environment variable keys for a stage');
122
- console.log(' delete-env <name> Delete environment variable from a stage');
123
- console.log(' deploy Deploy secrets to staging/prod servers');
124
- console.log(' write-ssh-keys Write SSH keys to ~/.ssh/ (for workflows)');
125
- console.log('');
126
- console.log('Examples:');
127
- console.log(' npx stack deploy --secrets list');
128
- console.log(' npx stack deploy --secrets set STAGING_SSH');
129
- console.log(' npx stack deploy --secrets get AWS_SECRET_ACCESS_KEY');
130
- console.log(' npx stack deploy --secrets delete STAGING_SSH');
131
- console.log(' npx stack deploy --secrets set-env DATABASE_URL --staging');
132
- console.log(' npx stack deploy --secrets delete-env DATABASE_URL --staging');
133
- console.log(' npx stack deploy --secrets deploy --staging');
134
- console.log('');
135
- return;
136
- }
137
-
138
- await secrets(action, secretName, options);
139
- return;
140
- }
141
-
142
- // --secrets as boolean flag = deploy secrets before app deploy
143
- // Determine which environment to deploy
144
- let environment = options.environment;
145
- if (options.dev) environment = 'dev';
146
- if (options.staging) environment = 'staging';
147
- if (options.prod) environment = 'prod';
148
-
149
- if (!environment) {
150
- console.log('Please specify an environment: --dev, --staging, or --prod');
151
- process.exit(1);
152
- }
153
-
154
- // Map --secrets boolean flag to deploySecrets option
155
- if (options.secrets === true) {
156
- options.deploySecrets = true;
157
- }
158
-
159
- const result = await deploy(environment, options);
160
- if (!result.success) {
161
- process.exit(1);
162
- }
163
- });
164
-
165
- // Secrets command (top-level shortcut — same as deploy --secrets)
166
- program
167
- .command('secrets [action] [name]')
168
- .description('Manage Ansible Vault secrets (shortcut for deploy --secrets)')
169
- .option('--staging', 'Target staging environment')
170
- .option('--prod', 'Target production environment')
171
- .option('--value <value>', 'Secret value (for scripting)')
172
- .option('--restart', 'Restart containers after deploying secrets')
173
- .action(async (action, name, options) => {
174
- if (!action) {
175
- // No action = interactive mode
176
- const { secretsInteractive } = require('../dist/cli/secrets');
177
- await secretsInteractive(options);
178
- return;
179
- }
180
- const validActions = ['list', 'set', 'get', 'delete', 'check', 'set-env', 'list-env', 'delete-env', 'deploy', 'write-ssh-keys'];
181
- if (!validActions.includes(action)) {
182
- console.log('Unknown action: ' + action);
183
- console.log('Valid actions: ' + validActions.join(', '));
184
- return;
185
- }
186
- await secrets(action, name, options);
187
- });
188
-
189
- // Undeploy command
190
- program
191
- .command('undeploy')
192
- .description('Remove deployment from servers')
193
- .option('--dev', 'Undeploy dev environment only')
194
- .option('--staging', 'Undeploy staging environment only')
195
- .option('--prod', 'Undeploy production environment only')
196
- .option('-e, --environment <env>', 'Environment (staging|prod|all)', 'all')
197
- .action(async (options) => {
198
- // Determine which environment to undeploy
199
- let environment = options.environment;
200
- if (options.dev) environment = 'dev';
201
- if (options.staging) environment = 'staging';
202
- if (options.prod) environment = 'prod';
203
-
204
- await undeploy(environment, options);
205
- });
206
-
207
- // Generate workflows command
208
- program
209
- .command('generate-workflows')
210
- .description('Generate GitHub workflow files')
211
- .option('-o, --output <dir>', 'Output directory', '.github/workflows')
212
- .action(async (options) => {
213
- const FactiiiPipeline = require('../dist/plugins/pipelines/factiii').default;
214
- await FactiiiPipeline.generateWorkflows(process.cwd());
215
- });
216
-
217
- // Upgrade command
218
- program
219
- .command('upgrade')
220
- .description('Check for updates and regenerate configs for new version')
221
- .option('--check', 'Only check for updates, do not upgrade')
222
- .action(async (options) => {
223
- await upgrade(options);
224
- });
225
-
226
- // Dev sync command - only available in dev mode
227
- if (process.env.DEV_MODE === 'true') {
228
- program
229
- .command('dev-sync')
230
- .description('Sync locally built infrastructure to servers')
231
- .option('--staging', 'Sync to staging environment only')
232
- .option('--prod', 'Sync to production environment only')
233
- .option('--deploy', 'Deploy after syncing')
234
- .action(async (options) => {
235
- await devSync(options);
236
- });
237
- }
238
-
239
- // Dev reset command
240
- program
241
- .command('dev-reset')
242
- .description('Reset local config/secrets to test 0-to-deployed flow')
243
- .option('--dry-run', 'Show what would be deleted without doing it')
244
- .action(async (options) => {
245
- await devReset(options);
246
- });
247
-
248
- // Legacy commands (for backwards compatibility)
249
- program
250
- .command('validate')
251
- .description('[Legacy] Validate stack.yml config file')
252
- .option('-c, --config <path>', 'Path to config file', 'stack.yml')
253
- .action(async (options) => {
254
- await validate(options);
255
- });
256
-
257
- program
258
- .command('check-config')
259
- .description('[Legacy] Check and regenerate configs on servers')
260
- .option('-e, --environment <env>', 'Environment (staging|prod|all)', 'all')
261
- .action(async (options) => {
262
- await checkConfig(options);
263
- });
264
-
265
- // Register plugin commands (db, ops, backup)
266
- // Pipeline plugin provides these commands via static commands array
267
- try {
268
- const FactiiiPipeline = require('../dist/plugins/pipelines/factiii').default;
269
- if (FactiiiPipeline.commands && FactiiiPipeline.commands.length > 0) {
270
- registerPluginCommands(program, FactiiiPipeline);
271
- }
272
- } catch (e) {
273
- // Plugin commands not available - continue without them
274
- }
275
-
276
- // Add detailed help text after Commander's default output
277
- program.addHelpText('after', '\n' +
278
- 'CORE COMMANDS\n' +
279
- ' npx stack Scan all environments (default command)\n' +
280
- ' npx stack init First-time setup — creates stack.yml, configures vault\n' +
281
- ' npx stack scan [--stage] Detect issues across environments (read-only, never modifies files)\n' +
282
- ' npx stack fix [--stage] Auto-fix detected issues (installs tools, creates configs)\n' +
283
- ' npx stack deploy --<stage> Build and deploy to an environment\n' +
284
- ' npx stack undeploy --<stage> Remove deployment from a server\n' +
285
- ' npx stack dev-reset [--dry-run] Reset local config/secrets for fresh bootstrap\n' +
286
- '\n' +
287
- 'SECRETS — npx stack deploy --secrets <action>\n' +
288
- ' list Show all stored vault secrets\n' +
289
- ' set <name> Set a secret (STAGING_SSH, PROD_SSH, etc.)\n' +
290
- ' get <name> Show a secret value\n' +
291
- ' delete <name> Delete a secret from vault\n' +
292
- ' check [name] Check if secrets exist\n' +
293
- ' set-env <name> --<stage> Set environment variable for a stage\n' +
294
- ' list-env --<stage> List environment variable keys for a stage\n' +
295
- ' delete-env <name> --<stage> Delete environment variable from a stage\n' +
296
- ' deploy --<stage> Push secrets to server\n' +
297
- ' write-ssh-keys Write SSH keys to ~/.ssh/\n' +
298
- '\n' +
299
- 'DATABASE — npx stack db <command> --<stage>\n' +
300
- ' seed --staging Run db:seed script to populate initial data\n' +
301
- ' migrate --prod Apply pending Prisma migrations (safe, non-destructive)\n' +
302
- ' reset --staging Drop all tables, re-run all migrations (DESTRUCTIVE)\n' +
303
- ' status --prod List pending and applied migrations\n' +
304
- '\n' +
305
- 'OPERATIONS — npx stack ops <command> --<stage>\n' +
306
- ' logs --staging [-f] [-n 50] View container logs (-f to follow, -n for line count)\n' +
307
- ' restart --prod [-s service] Restart containers without rebuilding images\n' +
308
- ' shell --staging Open /bin/sh inside the app container\n' +
309
- ' status --staging Show docker compose container status\n' +
310
- ' change-vault-password Change the Ansible Vault encryption password\n' +
311
- '\n' +
312
- 'BACKUP — npx stack backup <command> --<stage>\n' +
313
- ' create --prod [-o backup.sql] Export database via pg_dump\n' +
314
- ' restore --staging -i backup.sql Restore database from SQL dump (DESTRUCTIVE)\n' +
315
- ' health --prod Check container and database connectivity\n' +
316
- '\n' +
317
- 'STAGES\n' +
318
- ' --dev Local development environment\n' +
319
- ' --secrets Ansible Vault secrets (SSH keys, credentials)\n' +
320
- ' --staging Staging server (accessed via SSH)\n' +
321
- ' --prod Production server (via SSH or AWS provisioning)\n' +
322
- '\n' +
323
- 'EXAMPLES\n' +
324
- ' npx stack Scan and bootstrap a new project\n' +
325
- ' npx stack fix --prod Provision AWS infrastructure or fix server issues\n' +
326
- ' npx stack deploy --staging Build and deploy to staging\n' +
327
- ' npx stack deploy --secrets list Show all stored vault secrets\n' +
328
- ' npx stack deploy --secrets deploy --prod Push secrets to production server\n' +
329
- ' npx stack db migrate --prod Run production database migrations\n' +
330
- ' npx stack backup create --prod Create production database backup\n' +
331
- ' npx stack ops logs --staging -f Follow staging container logs\n'
332
- );
333
-
334
- program.parse();
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const path = require('path');
5
+
6
+ // Import plugin command registration
7
+ const { registerPluginCommands } = require('../dist/cli/plugin-commands');
8
+
9
+ // Import CLI commands from compiled TypeScript
10
+ const { init } = require('../dist/cli/init');
11
+ const { scan } = require('../dist/cli/scan');
12
+ const { fix } = require('../dist/cli/fix');
13
+ const { deploy } = require('../dist/cli/deploy');
14
+ const { prCheck } = require('../dist/cli/pr-check');
15
+ const { undeploy } = require('../dist/cli/undeploy');
16
+ const { secrets } = require('../dist/cli/secrets');
17
+ const { upgrade } = require('../dist/cli/upgrade');
18
+ const { validate } = require('../dist/cli/validate');
19
+ const { checkConfig } = require('../dist/cli/check-config');
20
+ const { devSync } = require('../dist/cli/dev-sync');
21
+ const { devReset } = require('../dist/cli/dev-reset');
22
+
23
+ // Read version from package.json
24
+ const packageJson = require('../package.json');
25
+
26
+ program
27
+ .name('stack')
28
+ .description('Stack - Infrastructure management CLI')
29
+ .version(packageJson.version);
30
+
31
+ // Init command (primary setup command)
32
+ program
33
+ .command('init')
34
+ .description('Initialize Stack in your project (run this first)')
35
+ .option('-f, --force', 'Overwrite existing config files')
36
+ .action(async (options) => {
37
+ await init(options);
38
+ });
39
+
40
+ // Default action: run scan when no subcommand provided (self-bootstrapping)
41
+ program
42
+ .action(async (options, command) => {
43
+ if (command.args.length === 0) {
44
+ await scan({});
45
+ }
46
+ });
47
+
48
+ // Scan command (explicit)
49
+ program
50
+ .command('scan')
51
+ .description('Scan all environments for issues')
52
+ .option('--dev', 'Scan dev environment only')
53
+ .option('--staging', 'Scan staging environment only')
54
+ .option('--prod', 'Scan production environment only')
55
+ .option('--secrets', 'Scan secrets only')
56
+ .option('--commit <hash>', 'Commit hash to verify (for deployment verification)')
57
+ .action(async (options) => {
58
+ await scan(options);
59
+ });
60
+
61
+ // Fix command
62
+ program
63
+ .command('fix')
64
+ .description('Fix all environments including uploading secrets to GitHub and installing server dependencies')
65
+ .option('--dev', 'Fix dev environment only')
66
+ .option('--staging', 'Fix staging environment only')
67
+ .option('--prod', 'Fix production environment only')
68
+ .option('--secrets', 'Fix secrets only')
69
+ .option('--token <token>', 'GitHub token (or set GITHUB_TOKEN env var)')
70
+ .option('--continue-on-error', 'Continue even if some fixes fail')
71
+ .action(async (options) => {
72
+ await fix(options);
73
+ });
74
+
75
+ // PR Check command (validates builds before merge)
76
+ program
77
+ .command('pr-check')
78
+ .description('Run server/client/mobile builds and report to GitHub (for PR validation)')
79
+ .option('--staging', 'Run on staging server')
80
+ .action(async (options) => {
81
+ const result = await prCheck(options);
82
+ if (!result.success) {
83
+ process.exit(1);
84
+ }
85
+ });
86
+
87
+ // Deploy command (also handles secrets management via --secrets [action])
88
+ program
89
+ .command('deploy [secretName]')
90
+ .description('Deploy to environments, or manage secrets with --secrets <action>')
91
+ .option('--dev', 'Deploy dev environment only (local containers)')
92
+ .option('--staging', 'Deploy staging environment only')
93
+ .option('--prod', 'Deploy production environment only')
94
+ .option('-e, --environment <env>', 'Environment (staging|prod)')
95
+ .option('-b, --branch <branch>', 'Branch to deploy from (defaults to current branch)')
96
+ .option('--commit <hash>', 'Commit hash to deploy (passed by workflow)')
97
+ .option('--token <token>', 'GitHub token (or set GITHUB_TOKEN env var)')
98
+ .option('--secrets [action]', 'Manage or deploy secrets (list|set|get|delete|check|set-env|list-env|delete-env|deploy|write-ssh-keys)')
99
+ .option('--value <value>', 'Secret value (for scripting)')
100
+ .option('--restart', 'Restart containers after deploying secrets')
101
+ .option('--dry-run', 'Show deployment plan without actually deploying')
102
+ .action(async (secretName, options) => {
103
+ // --secrets with an action string = vault management mode
104
+ if (typeof options.secrets === 'string') {
105
+ const action = options.secrets;
106
+ const validActions = ['list', 'set', 'get', 'delete', 'check', 'set-env', 'list-env', 'delete-env', 'deploy', 'write-ssh-keys'];
107
+
108
+ if (!validActions.includes(action)) {
109
+ console.log('');
110
+ console.log('Unknown secrets action: ' + action);
111
+ console.log('');
112
+ console.log('Usage: npx stack deploy --secrets <action> [name] [options]');
113
+ console.log('');
114
+ console.log('Actions:');
115
+ console.log(' list List all secrets (SSH keys + env vars)');
116
+ console.log(' set <name> Set a secret (STAGING_SSH, PROD_SSH, etc.)');
117
+ console.log(' get <name> Show a secret value');
118
+ console.log(' delete <name> Delete a secret from vault');
119
+ console.log(' check [name] Check if secrets exist');
120
+ console.log(' set-env <name> Set environment variable for a stage');
121
+ console.log(' list-env List environment variable keys for a stage');
122
+ console.log(' delete-env <name> Delete environment variable from a stage');
123
+ console.log(' deploy Deploy secrets to staging/prod servers');
124
+ console.log(' write-ssh-keys Write SSH keys to ~/.ssh/ (for workflows)');
125
+ console.log('');
126
+ console.log('Examples:');
127
+ console.log(' npx stack deploy --secrets list');
128
+ console.log(' npx stack deploy --secrets set STAGING_SSH');
129
+ console.log(' npx stack deploy --secrets get AWS_SECRET_ACCESS_KEY');
130
+ console.log(' npx stack deploy --secrets delete STAGING_SSH');
131
+ console.log(' npx stack deploy --secrets set-env DATABASE_URL --staging');
132
+ console.log(' npx stack deploy --secrets delete-env DATABASE_URL --staging');
133
+ console.log(' npx stack deploy --secrets deploy --staging');
134
+ console.log('');
135
+ return;
136
+ }
137
+
138
+ await secrets(action, secretName, options);
139
+ return;
140
+ }
141
+
142
+ // --secrets as boolean flag = deploy secrets before app deploy
143
+ // Determine which environment to deploy
144
+ let environment = options.environment;
145
+ if (options.dev) environment = 'dev';
146
+ if (options.staging) environment = 'staging';
147
+ if (options.prod) environment = 'prod';
148
+
149
+ if (!environment) {
150
+ console.log('Please specify an environment: --dev, --staging, or --prod');
151
+ process.exit(1);
152
+ }
153
+
154
+ // Map --secrets boolean flag to deploySecrets option
155
+ if (options.secrets === true) {
156
+ options.deploySecrets = true;
157
+ }
158
+
159
+ const result = await deploy(environment, options);
160
+ if (!result.success) {
161
+ process.exit(1);
162
+ }
163
+ });
164
+
165
+ // Secrets command (top-level shortcut — same as deploy --secrets)
166
+ program
167
+ .command('secrets [action] [name]')
168
+ .description('Manage Ansible Vault secrets (shortcut for deploy --secrets)')
169
+ .option('--staging', 'Target staging environment')
170
+ .option('--prod', 'Target production environment')
171
+ .option('--value <value>', 'Secret value (for scripting)')
172
+ .option('--restart', 'Restart containers after deploying secrets')
173
+ .action(async (action, name, options) => {
174
+ if (!action) {
175
+ // No action = interactive mode
176
+ const { secretsInteractive } = require('../dist/cli/secrets');
177
+ await secretsInteractive(options);
178
+ return;
179
+ }
180
+ const validActions = ['list', 'set', 'get', 'delete', 'check', 'set-env', 'list-env', 'delete-env', 'deploy', 'write-ssh-keys'];
181
+ if (!validActions.includes(action)) {
182
+ console.log('Unknown action: ' + action);
183
+ console.log('Valid actions: ' + validActions.join(', '));
184
+ return;
185
+ }
186
+ await secrets(action, name, options);
187
+ });
188
+
189
+ // Undeploy command
190
+ program
191
+ .command('undeploy')
192
+ .description('Remove deployment from servers')
193
+ .option('--dev', 'Undeploy dev environment only')
194
+ .option('--staging', 'Undeploy staging environment only')
195
+ .option('--prod', 'Undeploy production environment only')
196
+ .option('-e, --environment <env>', 'Environment (staging|prod|all)', 'all')
197
+ .action(async (options) => {
198
+ // Determine which environment to undeploy
199
+ let environment = options.environment;
200
+ if (options.dev) environment = 'dev';
201
+ if (options.staging) environment = 'staging';
202
+ if (options.prod) environment = 'prod';
203
+
204
+ await undeploy(environment, options);
205
+ });
206
+
207
+ // Generate workflows command
208
+ program
209
+ .command('generate-workflows')
210
+ .description('Generate GitHub workflow files')
211
+ .option('-o, --output <dir>', 'Output directory', '.github/workflows')
212
+ .action(async (options) => {
213
+ const FactiiiPipeline = require('../dist/plugins/pipelines/factiii').default;
214
+ await FactiiiPipeline.generateWorkflows(process.cwd());
215
+ });
216
+
217
+ // Upgrade command
218
+ program
219
+ .command('upgrade')
220
+ .description('Check for updates and regenerate configs for new version')
221
+ .option('--check', 'Only check for updates, do not upgrade')
222
+ .action(async (options) => {
223
+ await upgrade(options);
224
+ });
225
+
226
+ // Dev sync command - only available in dev mode
227
+ if (process.env.DEV_MODE === 'true') {
228
+ program
229
+ .command('dev-sync')
230
+ .description('Sync locally built infrastructure to servers')
231
+ .option('--staging', 'Sync to staging environment only')
232
+ .option('--prod', 'Sync to production environment only')
233
+ .option('--deploy', 'Deploy after syncing')
234
+ .action(async (options) => {
235
+ await devSync(options);
236
+ });
237
+ }
238
+
239
+ // Dev reset command
240
+ program
241
+ .command('dev-reset')
242
+ .description('Reset local config/secrets to test 0-to-deployed flow')
243
+ .option('--dry-run', 'Show what would be deleted without doing it')
244
+ .action(async (options) => {
245
+ await devReset(options);
246
+ });
247
+
248
+ // Legacy commands (for backwards compatibility)
249
+ program
250
+ .command('validate')
251
+ .description('[Legacy] Validate stack.yml config file')
252
+ .option('-c, --config <path>', 'Path to config file', 'stack.yml')
253
+ .action(async (options) => {
254
+ await validate(options);
255
+ });
256
+
257
+ program
258
+ .command('check-config')
259
+ .description('[Legacy] Check and regenerate configs on servers')
260
+ .option('-e, --environment <env>', 'Environment (staging|prod|all)', 'all')
261
+ .action(async (options) => {
262
+ await checkConfig(options);
263
+ });
264
+
265
+ // Register plugin commands (db, ops, backup)
266
+ // Pipeline plugin provides these commands via static commands array
267
+ try {
268
+ const FactiiiPipeline = require('../dist/plugins/pipelines/factiii').default;
269
+ if (FactiiiPipeline.commands && FactiiiPipeline.commands.length > 0) {
270
+ registerPluginCommands(program, FactiiiPipeline);
271
+ }
272
+ } catch (e) {
273
+ // Plugin commands not available - continue without them
274
+ }
275
+
276
+ // Add detailed help text after Commander's default output
277
+ program.addHelpText('after', '\n' +
278
+ 'CORE COMMANDS\n' +
279
+ ' npx stack Scan all environments (default command)\n' +
280
+ ' npx stack init First-time setup — creates stack.yml, configures vault\n' +
281
+ ' npx stack scan [--stage] Detect issues across environments (read-only, never modifies files)\n' +
282
+ ' npx stack fix [--stage] Auto-fix detected issues (installs tools, creates configs)\n' +
283
+ ' npx stack deploy --<stage> Build and deploy to an environment\n' +
284
+ ' npx stack undeploy --<stage> Remove deployment from a server\n' +
285
+ ' npx stack dev-reset [--dry-run] Reset local config/secrets for fresh bootstrap\n' +
286
+ '\n' +
287
+ 'SECRETS — npx stack deploy --secrets <action>\n' +
288
+ ' list Show all stored vault secrets\n' +
289
+ ' set <name> Set a secret (STAGING_SSH, PROD_SSH, etc.)\n' +
290
+ ' get <name> Show a secret value\n' +
291
+ ' delete <name> Delete a secret from vault\n' +
292
+ ' check [name] Check if secrets exist\n' +
293
+ ' set-env <name> --<stage> Set environment variable for a stage\n' +
294
+ ' list-env --<stage> List environment variable keys for a stage\n' +
295
+ ' delete-env <name> --<stage> Delete environment variable from a stage\n' +
296
+ ' deploy --<stage> Push secrets to server\n' +
297
+ ' write-ssh-keys Write SSH keys to ~/.ssh/\n' +
298
+ '\n' +
299
+ 'DATABASE — npx stack db <command> --<stage>\n' +
300
+ ' seed --staging Run db:seed script to populate initial data\n' +
301
+ ' migrate --prod Apply pending Prisma migrations (safe, non-destructive)\n' +
302
+ ' reset --staging Drop all tables, re-run all migrations (DESTRUCTIVE)\n' +
303
+ ' status --prod List pending and applied migrations\n' +
304
+ '\n' +
305
+ 'OPERATIONS — npx stack ops <command> --<stage>\n' +
306
+ ' logs --staging [-f] [-n 50] View container logs (-f to follow, -n for line count)\n' +
307
+ ' restart --prod [-s service] Restart containers without rebuilding images\n' +
308
+ ' shell --staging Open /bin/sh inside the app container\n' +
309
+ ' status --staging Show docker compose container status\n' +
310
+ ' change-vault-password Change the Ansible Vault encryption password\n' +
311
+ '\n' +
312
+ 'BACKUP — npx stack backup <command> --<stage>\n' +
313
+ ' create --prod [-o backup.sql] Export database via pg_dump\n' +
314
+ ' restore --staging -i backup.sql Restore database from SQL dump (DESTRUCTIVE)\n' +
315
+ ' health --prod Check container and database connectivity\n' +
316
+ '\n' +
317
+ 'STAGES\n' +
318
+ ' --dev Local development environment\n' +
319
+ ' --secrets Ansible Vault secrets (SSH keys, credentials)\n' +
320
+ ' --staging Staging server (accessed via SSH)\n' +
321
+ ' --prod Production server (via SSH or AWS provisioning)\n' +
322
+ '\n' +
323
+ 'EXAMPLES\n' +
324
+ ' npx stack Scan and bootstrap a new project\n' +
325
+ ' npx stack fix --prod Provision AWS infrastructure or fix server issues\n' +
326
+ ' npx stack deploy --staging Build and deploy to staging\n' +
327
+ ' npx stack deploy --secrets list Show all stored vault secrets\n' +
328
+ ' npx stack deploy --secrets deploy --prod Push secrets to production server\n' +
329
+ ' npx stack db migrate --prod Run production database migrations\n' +
330
+ ' npx stack backup create --prod Create production database backup\n' +
331
+ ' npx stack ops logs --staging -f Follow staging container logs\n'
332
+ );
333
+
334
+ program.parse();