infrawise 0.10.0 → 0.10.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.
@@ -365,35 +365,37 @@ export async function runAnalyze(options = {}) {
365
365
  });
366
366
  }
367
367
  // ── Output ────────────────────────────────────────────────────────────────────
368
- const displayFindings = minSeverity > 0
369
- ? findings.filter((f) => (SEVERITY_ORDER[f.severity] ?? 0) >= minSeverity)
370
- : findings;
371
- console.log('');
372
- if (displayFindings.length === 0) {
373
- const msg = minSeverity > 0
374
- ? `No ${options.severity} (or higher) severity issues found.`
375
- : 'Your infrastructure looks clean.';
376
- console.log(` ${chalk.green.bold(' No issues found!')} ${chalk.dim(msg)}`);
377
- }
378
- else {
379
- const filterNote = minSeverity > 0 ? chalk.dim(` (${options.severity}+ only)`) : '';
380
- console.log(chalk.bold(` Findings`) + chalk.dim(` ${displayFindings.length} total`) + filterNote);
381
- displayFindings.forEach((f, i) => printFinding(f, i));
382
- printSummaryBox(displayFindings);
383
- if (displayFindings.some((f) => f.severity === 'high')) {
384
- console.log(`\n ${chalk.red.bold('Action required:')} ${chalk.red('High severity issues detected.')}`);
368
+ if (!options.silent) {
369
+ const displayFindings = minSeverity > 0
370
+ ? findings.filter((f) => (SEVERITY_ORDER[f.severity] ?? 0) >= minSeverity)
371
+ : findings;
372
+ console.log('');
373
+ if (displayFindings.length === 0) {
374
+ const msg = minSeverity > 0
375
+ ? `No ${options.severity} (or higher) severity issues found.`
376
+ : 'Your infrastructure looks clean.';
377
+ console.log(` ${chalk.green.bold('✓ No issues found!')} ${chalk.dim(msg)}`);
385
378
  }
386
- }
387
- if (options.output) {
388
- const report = buildMarkdownReport(displayFindings, config.project);
389
- const outPath = path.resolve(options.output);
390
- fs.writeFileSync(outPath, report, 'utf8');
379
+ else {
380
+ const filterNote = minSeverity > 0 ? chalk.dim(` (${options.severity}+ only)`) : '';
381
+ console.log(chalk.bold(` Findings`) + chalk.dim(` ${displayFindings.length} total`) + filterNote);
382
+ displayFindings.forEach((f, i) => printFinding(f, i));
383
+ printSummaryBox(displayFindings);
384
+ if (displayFindings.some((f) => f.severity === 'high')) {
385
+ console.log(`\n ${chalk.red.bold('Action required:')} ${chalk.red('High severity issues detected.')}`);
386
+ }
387
+ }
388
+ if (options.output) {
389
+ const report = buildMarkdownReport(displayFindings, config.project);
390
+ const outPath = path.resolve(options.output);
391
+ fs.writeFileSync(outPath, report, 'utf8');
392
+ console.log('');
393
+ log.success('Report saved', outPath);
394
+ }
395
+ console.log('');
396
+ log.dim(`Results cached in .infrawise/cache/`);
391
397
  console.log('');
392
- log.success('Report saved', outPath);
393
398
  }
394
- console.log('');
395
- log.dim(`Results cached in .infrawise/cache/`);
396
- console.log('');
397
399
  }
398
400
  export async function runCodeRefresh(repoPath, config) {
399
401
  const cached = readCache('meta', 60 * 60 * 1000);
@@ -21,40 +21,73 @@ export async function runInit(options = {}) {
21
21
  log.success(`AWS profiles found`, String(profiles.length));
22
22
  console.log('');
23
23
  // ── Core settings ──────────────────────────────────────────────────────────
24
- const core = await inquirer.prompt([
24
+ const { project } = await inquirer.prompt([
25
25
  {
26
26
  type: 'input',
27
27
  name: 'project',
28
28
  message: 'Project name:',
29
29
  default: repoName,
30
30
  },
31
+ ]);
32
+ // ── Step 1: provider ───────────────────────────────────────────────────────
33
+ const { provider } = await inquirer.prompt([
31
34
  {
32
35
  type: 'select',
33
- name: 'awsProfile',
34
- message: 'AWS profile:',
36
+ name: 'provider',
37
+ message: 'Infrastructure:',
35
38
  choices: [
36
- new inquirer.Separator('── no profile ──'),
37
- { name: 'Environment variables (CI/CD, real AWS)', value: '__env__' },
38
- { name: 'LocalStack (local development)', value: '__localstack__' },
39
- new inquirer.Separator('── named profiles ──'),
40
- ...profiles,
39
+ { name: 'AWS', value: 'aws' },
40
+ { name: 'LocalStack (mimics AWS locally)', value: 'localstack' },
41
+ { name: 'Local (no cloud — databases, queues, self-hosted services)', value: 'local' },
41
42
  ],
42
- default: profiles[0],
43
- },
44
- {
45
- type: 'input',
46
- name: 'region',
47
- message: 'AWS region:',
48
- default: detectedRegion,
49
- },
50
- {
51
- type: 'input',
52
- name: 'endpoint',
53
- message: 'LocalStack endpoint:',
54
- default: 'http://localhost:4566',
55
- when: (a) => a.awsProfile === '__localstack__',
56
43
  },
57
44
  ]);
45
+ // ── Step 2: AWS profile (aws only) ────────────────────────────────────────
46
+ let awsProfile = '__env__';
47
+ if (provider === 'aws') {
48
+ const answer = await inquirer.prompt([
49
+ {
50
+ type: 'select',
51
+ name: 'awsProfile',
52
+ message: 'AWS profile:',
53
+ choices: [
54
+ { name: 'Environment variables (CI/CD)', value: '__env__' },
55
+ ...(profiles.length ? [new inquirer.Separator('── named profiles ──'), ...profiles] : []),
56
+ ],
57
+ default: profiles[0] ?? '__env__',
58
+ },
59
+ ]);
60
+ awsProfile = answer.awsProfile;
61
+ }
62
+ else if (provider === 'localstack') {
63
+ awsProfile = '__localstack__';
64
+ }
65
+ // ── Step 3: region + endpoint ─────────────────────────────────────────────
66
+ let region = detectedRegion;
67
+ let endpoint;
68
+ if (provider !== 'local') {
69
+ const regionAnswer = await inquirer.prompt([
70
+ {
71
+ type: 'input',
72
+ name: 'region',
73
+ message: 'AWS region:',
74
+ default: detectedRegion,
75
+ },
76
+ ]);
77
+ region = regionAnswer.region;
78
+ if (provider === 'localstack') {
79
+ const endpointAnswer = await inquirer.prompt([
80
+ {
81
+ type: 'input',
82
+ name: 'endpoint',
83
+ message: 'LocalStack endpoint:',
84
+ default: 'http://localhost:4566',
85
+ },
86
+ ]);
87
+ endpoint = endpointAnswer.endpoint;
88
+ }
89
+ }
90
+ const core = { project, awsProfile, region, endpoint };
58
91
  // ── Databases ──────────────────────────────────────────────────────────────
59
92
  console.log('\n ' + chalk.bold('Databases'));
60
93
  console.log(chalk.dim(' Self-hosted databases (PostgreSQL, MySQL, MongoDB).'));
@@ -99,89 +132,108 @@ export async function runInit(options = {}) {
99
132
  when: (a) => a.mongoEnabled,
100
133
  },
101
134
  ]);
102
- // ── AWS services ───────────────────────────────────────────────────────────
103
- console.log('\n ' + chalk.bold('AWS Services'));
104
- console.log(chalk.dim(' Infrawise will introspect these services using the credentials configured above.'));
105
- const services = await inquirer.prompt([
106
- {
107
- type: 'confirm',
108
- name: 'dynamoEnabled',
109
- message: 'Introspect DynamoDB?',
110
- default: true,
111
- },
112
- {
113
- type: 'input',
114
- name: 'dynamoTables',
115
- message: 'DynamoDB tables to include:',
116
- default: '',
117
- suffix: chalk.dim(' (comma-separated, blank = all)'),
118
- when: (a) => a.dynamoEnabled,
119
- },
120
- {
121
- type: 'confirm',
122
- name: 'sqsEnabled',
123
- message: 'Introspect SQS queues?',
124
- default: true,
125
- },
126
- {
127
- type: 'confirm',
128
- name: 'snsEnabled',
129
- message: 'Introspect SNS topics?',
130
- default: true,
131
- },
132
- {
133
- type: 'confirm',
134
- name: 'ssmEnabled',
135
- message: 'Introspect SSM Parameter Store? (metadata only, no values)',
136
- default: true,
137
- },
138
- {
139
- type: 'input',
140
- name: 'ssmPaths',
141
- message: 'SSM path prefixes to filter:',
142
- default: '',
143
- suffix: chalk.dim(' (comma-separated, blank = all e.g. /myapp/prod)'),
144
- when: (a) => a.ssmEnabled,
145
- },
146
- {
147
- type: 'confirm',
148
- name: 'secretsEnabled',
149
- message: 'Introspect Secrets Manager? (names & rotation only, no values)',
150
- default: true,
151
- },
152
- {
153
- type: 'confirm',
154
- name: 'lambdaEnabled',
155
- message: 'Introspect Lambda functions?',
156
- default: true,
157
- },
158
- {
159
- type: 'confirm',
160
- name: 'eventbridgeEnabled',
161
- message: 'Introspect EventBridge rules?',
162
- default: true,
163
- },
164
- {
165
- type: 'confirm',
166
- name: 'rdsEnabled',
167
- message: 'Introspect RDS instances?',
168
- default: true,
169
- },
170
- {
171
- type: 'confirm',
172
- name: 'logsEnabled',
173
- message: 'Sample CloudWatch Logs? (error patterns only, no raw logs)',
174
- default: false,
175
- },
176
- {
177
- type: 'input',
178
- name: 'logGroupPrefixes',
179
- message: 'CloudWatch log group prefixes:',
180
- default: '',
181
- suffix: chalk.dim(' (comma-separated, blank = all)'),
182
- when: (a) => a.logsEnabled,
183
- },
184
- ]);
135
+ // ── AWS services (skipped for local) ─────────────────────────────────────
136
+ let services;
137
+ if (provider === 'local') {
138
+ services = {
139
+ dynamoEnabled: false,
140
+ dynamoTables: '',
141
+ sqsEnabled: false,
142
+ snsEnabled: false,
143
+ ssmEnabled: false,
144
+ ssmPaths: '',
145
+ secretsEnabled: false,
146
+ lambdaEnabled: false,
147
+ eventbridgeEnabled: false,
148
+ rdsEnabled: false,
149
+ logsEnabled: false,
150
+ logGroupPrefixes: '',
151
+ };
152
+ }
153
+ else {
154
+ console.log('\n ' + chalk.bold('AWS Services'));
155
+ console.log(chalk.dim(' Infrawise will introspect these services using the credentials configured above.'));
156
+ services = await inquirer.prompt([
157
+ {
158
+ type: 'confirm',
159
+ name: 'dynamoEnabled',
160
+ message: 'Introspect DynamoDB?',
161
+ default: true,
162
+ },
163
+ {
164
+ type: 'input',
165
+ name: 'dynamoTables',
166
+ message: 'DynamoDB tables to include:',
167
+ default: '',
168
+ suffix: chalk.dim(' (comma-separated, blank = all)'),
169
+ when: (a) => a.dynamoEnabled,
170
+ },
171
+ {
172
+ type: 'confirm',
173
+ name: 'sqsEnabled',
174
+ message: 'Introspect SQS queues?',
175
+ default: true,
176
+ },
177
+ {
178
+ type: 'confirm',
179
+ name: 'snsEnabled',
180
+ message: 'Introspect SNS topics?',
181
+ default: true,
182
+ },
183
+ {
184
+ type: 'confirm',
185
+ name: 'ssmEnabled',
186
+ message: 'Introspect SSM Parameter Store? (metadata only, no values)',
187
+ default: true,
188
+ },
189
+ {
190
+ type: 'input',
191
+ name: 'ssmPaths',
192
+ message: 'SSM path prefixes to filter:',
193
+ default: '',
194
+ suffix: chalk.dim(' (comma-separated, blank = all e.g. /myapp/prod)'),
195
+ when: (a) => a.ssmEnabled,
196
+ },
197
+ {
198
+ type: 'confirm',
199
+ name: 'secretsEnabled',
200
+ message: 'Introspect Secrets Manager? (names & rotation only, no values)',
201
+ default: true,
202
+ },
203
+ {
204
+ type: 'confirm',
205
+ name: 'lambdaEnabled',
206
+ message: 'Introspect Lambda functions?',
207
+ default: true,
208
+ },
209
+ {
210
+ type: 'confirm',
211
+ name: 'eventbridgeEnabled',
212
+ message: 'Introspect EventBridge rules?',
213
+ default: true,
214
+ },
215
+ {
216
+ type: 'confirm',
217
+ name: 'rdsEnabled',
218
+ message: 'Introspect RDS instances?',
219
+ default: true,
220
+ },
221
+ {
222
+ type: 'confirm',
223
+ name: 'logsEnabled',
224
+ message: 'Sample CloudWatch Logs? (error patterns only, no raw logs)',
225
+ default: false,
226
+ },
227
+ {
228
+ type: 'input',
229
+ name: 'logGroupPrefixes',
230
+ message: 'CloudWatch log group prefixes:',
231
+ default: '',
232
+ suffix: chalk.dim(' (comma-separated, blank = all)'),
233
+ when: (a) => a.logsEnabled,
234
+ },
235
+ ]);
236
+ } // end else (provider !== 'local')
185
237
  // ── Build config ───────────────────────────────────────────────────────────
186
238
  const includeTables = services.dynamoTables
187
239
  ? services.dynamoTables
@@ -204,9 +256,13 @@ export async function runInit(options = {}) {
204
256
  const isLocalStack = core.awsProfile === '__localstack__';
205
257
  const isEnvVars = core.awsProfile === '__env__';
206
258
  const resolvedProfile = isLocalStack || isEnvVars ? '' : core.awsProfile;
207
- const resolvedEndpoint = isLocalStack ? core.endpoint || 'http://localhost:4566' : undefined;
259
+ const resolvedEndpoint = isLocalStack ? (core.endpoint ?? 'http://localhost:4566') : undefined;
208
260
  const configContent = generateDefaultConfig(core.project, {
209
- aws: { profile: resolvedProfile, region: core.region, endpoint: resolvedEndpoint },
261
+ aws: {
262
+ profile: resolvedProfile,
263
+ region: core.region ?? detectedRegion,
264
+ endpoint: resolvedEndpoint,
265
+ },
210
266
  dynamodb: { enabled: services.dynamoEnabled, includeTables },
211
267
  postgres: {
212
268
  enabled: databases.pgEnabled,
@@ -120,7 +120,7 @@ export async function runStart(options = {}) {
120
120
  const reason = (cachedGraph ?? cachedFindings) ? 'Cache is stale (>24h)' : 'No cache found';
121
121
  log.warn(`${reason} — running analysis...`);
122
122
  console.log('');
123
- await runAnalyze({ config: options.config });
123
+ await runAnalyze({ config: options.config, silent: true });
124
124
  console.log('');
125
125
  }
126
126
  // Step 3 — write editor MCP config files
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infrawise",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "mcpName": "io.github.Sidd27/infrawise",
5
5
  "description": "CLI-first infrastructure intelligence platform — analyzes DynamoDB, PostgreSQL, MySQL, MongoDB, SQS, SNS, SSM, Secrets Manager, Lambda, S3, CloudWatch Logs and exposes findings as an MCP server for Claude Code",
6
6
  "keywords": [