@maverick006/vibeguard 1.0.6 → 1.0.7

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/dist/formatter.js CHANGED
@@ -18,6 +18,8 @@ function visibleWidth(str) {
18
18
  let width = 0;
19
19
  for (const char of plain) {
20
20
  const code = char.codePointAt(0) || 0;
21
+ if (code === 0xFE0F || code === 0xFE0E)
22
+ continue; // ignore variation selectors
21
23
  // Emojis and certain symbols take 2 terminal columns
22
24
  if (code > 0x1F000 || (code >= 0x2600 && code <= 0x27BF)) {
23
25
  width += 2;
@@ -130,39 +132,32 @@ function renderDashboard(options) {
130
132
  const riskColor = stats.riskLevel === 'LOW RISK' ? green : stats.riskLevel === 'MEDIUM RISK' ? yellow : red;
131
133
  console.log('\n');
132
134
  // Print Top Row: Clock aligned right
133
- console.log(' '.repeat(58) + cyan.bold(timeStr));
134
- // Top Left Box lines (ASCII)
135
- const leftHeader = [
136
- `${cyan(shield[0])} ${cyan.bold(title[0])}`,
137
- `${cyan(shield[1])} ${cyan.bold(title[1])}`,
138
- `${cyan(shield[2])} ${cyan.bold(title[2])}`,
139
- `${cyan(shield[3])} ${cyan.bold(title[3])}`,
140
- `${cyan(shield[4])} ${cyan.bold(title[4])}`,
141
- `${cyan(shield[5])} ${gray('AI-Powered DevSecOps Orchestrator')}`,
142
- ` ${cyan('Scanning. Analyzing. Protecting.')}`
143
- ];
144
- // Top Right Risk Box lines
145
- const rightBox = [
146
- `${darkBorder('┌───────────────────────────────────────┐')}`,
147
- `${darkBorder('')} ${cyan('OVERALL RISK SCORE')} ${darkBorder('')}`,
148
- `${darkBorder('')} ${red('CRITICAL')} ${String(stats.critical).padStart(2, ' ')} ${darkBorder('')}`,
149
- `${darkBorder('│')} ${scoreColor.bold(String(stats.score))} ${gray('/100')} ${orange('HIGH')} ${String(stats.high).padStart(2, ' ')} ${darkBorder('│')}`,
150
- `${darkBorder('│')} ${yellow('MEDIUM')} ${String(stats.medium).padStart(2, ' ')} ${darkBorder('│')}`,
151
- `${darkBorder('│')} ${riskColor.bold(stats.riskLevel.padEnd(13, ' '))} ${cyan('LOW')} ${String(stats.low).padStart(2, ' ')} ${darkBorder('│')}`,
152
- `${darkBorder('└───────────────────────────────────────┘')}`
153
- ];
154
- for (let i = 0; i < leftHeader.length; i++) {
155
- const l = padVisible(leftHeader[i], 66);
156
- const r = rightBox[i] || '';
157
- console.log(`${l}${r}`);
158
- }
135
+ console.log(' '.repeat(65) + cyan.bold(timeStr));
136
+ // 1. Header (Shield + VIBEGUARD)
137
+ console.log(`${cyan(shield[0])} ${cyan.bold(title[0])}`);
138
+ console.log(`${cyan(shield[1])} ${cyan.bold(title[1])}`);
139
+ console.log(`${cyan(shield[2])} ${cyan.bold(title[2])}`);
140
+ console.log(`${cyan(shield[3])} ${cyan.bold(title[3])}`);
141
+ console.log(`${cyan(shield[4])} ${cyan.bold(title[4])}`);
142
+ console.log(`${cyan(shield[5])} ${gray('AI-Powered DevSecOps Orchestrator')}`);
143
+ console.log(` ${cyan('Scanning. Analyzing. Protecting.')}\n`);
144
+ // 2. Risk Score Box (Placed cleanly BELOW VIBEGUARD Header)
145
+ const boxWidth = 74;
146
+ console.log(darkBorder(`┌${'─'.repeat(boxWidth)}┐`));
147
+ console.log(`${darkBorder('│')} ${cyan.bold('OVERALL RISK SCORE')}${' '.repeat(boxWidth - 20)}${darkBorder('│')}`);
148
+ console.log(`${darkBorder('')}${' '.repeat(boxWidth)}${darkBorder('│')}`);
149
+ const scoreLine1 = ` ${scoreColor.bold(String(stats.score))} ${gray('/100')} ${red('CRITICAL')} ${String(stats.critical).padEnd(2, ' ')} ${orange('HIGH')} ${String(stats.high).padEnd(2, ' ')}`;
150
+ const scoreLine2 = ` ${riskColor.bold(stats.riskLevel.padEnd(13, ' '))} ${yellow('MEDIUM')} ${String(stats.medium).padEnd(2, ' ')} ${cyan('LOW')} ${String(stats.low).padEnd(2, ' ')}`;
151
+ console.log(`${darkBorder('│')}${padVisible(scoreLine1, boxWidth)}${darkBorder('│')}`);
152
+ console.log(`${darkBorder('│')}${padVisible(scoreLine2, boxWidth)}${darkBorder('│')}`);
153
+ console.log(darkBorder(`└${''.repeat(boxWidth)}┘`));
159
154
  console.log('');
160
- // 3-Column / Multi-panel Grid
155
+ // 3. Multi-panel Grid (Pipeline & Findings)
161
156
  const pipelineRows = [
162
- `${gray('</>')} SAST (Semgrep) ${green('✓ OK')}`,
157
+ `</> SAST (Semgrep) ${green('✓ OK')}`,
163
158
  `📦 Dependency Check (Trivy) ${green('✓ OK')}`,
164
159
  `🔑 Secrets Scan (Gitleaks) ${green('✓ OK')}`,
165
- `☁️ IaC Scan (Checkov) ${green('✓ OK')}`,
160
+ `🔒 IaC Scan (Checkov) ${green('✓ OK')}`,
166
161
  `🐳 Container Scan (Trivy) ${green('✓ OK')}`,
167
162
  `📑 Code Quality (ESLint) ${green('✓ OK')}`,
168
163
  ``,
@@ -175,7 +170,7 @@ function renderDashboard(options) {
175
170
  ];
176
171
  // Table of findings (top 10)
177
172
  const topFindings = findings.slice(0, 10);
178
- const tableHeader = `${dimGray(padVisible('ID', 12))} ${dimGray(padVisible('SEVERITY', 10))} ${dimGray(padVisible('TITLE', 28))} ${dimGray('FILE:LINE')}`;
173
+ const tableHeader = `${dimGray(padVisible('ID', 13))} ${dimGray(padVisible('SEVERITY', 10))} ${dimGray(padVisible('TITLE', 28))} ${dimGray('FILE:LINE')}`;
179
174
  const findingRows = [tableHeader];
180
175
  if (topFindings.length === 0) {
181
176
  findingRows.push(green(' ✓ No security vulnerabilities detected. Codebase is clean!'));
@@ -192,24 +187,24 @@ function renderDashboard(options) {
192
187
  else if (sev === 'MEDIUM')
193
188
  sevFormatted = yellow('MEDIUM ');
194
189
  const rawTitle = f.title || 'Security Finding';
195
- const titleTruncated = rawTitle.length > 25 ? rawTitle.slice(0, 23) + '..' : rawTitle;
190
+ const titleTruncated = rawTitle.length > 26 ? rawTitle.slice(0, 24) + '..' : rawTitle;
196
191
  const titleFormatted = padVisible(white(titleTruncated), 28);
197
192
  const fileLine = `${f.file || 'unknown'}:${f.line || 1}`;
198
- const idFormatted = padVisible(cyan(id), 12);
193
+ const idFormatted = padVisible(cyan(id), 13);
199
194
  findingRows.push(`${idFormatted} ${sevFormatted} ${titleFormatted} ${dimGray(fileLine)}`);
200
195
  }
201
196
  }
202
197
  // Print Section Headers with ANSI-safe padding
203
198
  const headerCol1 = padVisible(cyan('▶ SCANNER PIPELINE'), 36);
204
199
  const headerCol2 = cyan('▶ TOP FINDINGS');
205
- console.log(`\n${headerCol1} ${headerCol2}`);
200
+ console.log(`${headerCol1} ${headerCol2}`);
206
201
  const maxRows = Math.max(pipelineRows.length, findingRows.length);
207
202
  for (let i = 0; i < maxRows; i++) {
208
203
  const left = padVisible(pipelineRows[i] || '', 36);
209
204
  const right = findingRows[i] || '';
210
205
  console.log(`${left} ${right}`);
211
206
  }
212
- // AI Remediation Section
207
+ // 4. AI Remediation Section
213
208
  if (remediation) {
214
209
  console.log(`\n${cyan('▶ AI REMEDIATION (VG-AI)')}\n`);
215
210
  console.log(`${cyan('Finding:')} ${red.bold(remediation.findingId)}\n`);
@@ -235,7 +230,7 @@ function renderDashboard(options) {
235
230
  console.log(darkBorder('└────────────────────────────────────────────────────────────┘'));
236
231
  console.log(`\n${cyan('Confidence:')} ${green.bold(remediation.confidence + '%')}`);
237
232
  }
238
- // Summary Bar (bottom capsule)
233
+ // 5. Summary Bar (bottom capsule)
239
234
  console.log(`\n${cyan('▶ SUMMARY')}`);
240
235
  const summaryText = `🛡️ Scan completed in ${duration} │ ${stats.total} findings │ 6 passed`;
241
236
  const barTop = `┌${'─'.repeat(visibleWidth(summaryText) + 4)}┐`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maverick006/vibeguard",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "VibeGuard CLI",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/formatter.ts CHANGED
@@ -22,6 +22,7 @@ function visibleWidth(str: string): number {
22
22
  let width = 0;
23
23
  for (const char of plain) {
24
24
  const code = char.codePointAt(0) || 0;
25
+ if (code === 0xFE0F || code === 0xFE0E) continue; // ignore variation selectors
25
26
  // Emojis and certain symbols take 2 terminal columns
26
27
  if (code > 0x1F000 || (code >= 0x2600 && code <= 0x27BF)) {
27
28
  width += 2;
@@ -152,43 +153,37 @@ export function renderDashboard(options: {
152
153
  console.log('\n');
153
154
 
154
155
  // Print Top Row: Clock aligned right
155
- console.log(' '.repeat(58) + cyan.bold(timeStr));
156
-
157
- // Top Left Box lines (ASCII)
158
- const leftHeader = [
159
- `${cyan(shield[0])} ${cyan.bold(title[0])}`,
160
- `${cyan(shield[1])} ${cyan.bold(title[1])}`,
161
- `${cyan(shield[2])} ${cyan.bold(title[2])}`,
162
- `${cyan(shield[3])} ${cyan.bold(title[3])}`,
163
- `${cyan(shield[4])} ${cyan.bold(title[4])}`,
164
- `${cyan(shield[5])} ${gray('AI-Powered DevSecOps Orchestrator')}`,
165
- ` ${cyan('Scanning. Analyzing. Protecting.')}`
166
- ];
167
-
168
- // Top Right Risk Box lines
169
- const rightBox = [
170
- `${darkBorder('┌───────────────────────────────────────┐')}`,
171
- `${darkBorder('│')} ${cyan('OVERALL RISK SCORE')} ${darkBorder('│')}`,
172
- `${darkBorder('')} ${red('CRITICAL')} ${String(stats.critical).padStart(2, ' ')} ${darkBorder('')}`,
173
- `${darkBorder('│')} ${scoreColor.bold(String(stats.score))} ${gray('/100')} ${orange('HIGH')} ${String(stats.high).padStart(2, ' ')} ${darkBorder('')}`,
174
- `${darkBorder('│')} ${yellow('MEDIUM')} ${String(stats.medium).padStart(2, ' ')} ${darkBorder('│')}`,
175
- `${darkBorder('│')} ${riskColor.bold(stats.riskLevel.padEnd(13, ' '))} ${cyan('LOW')} ${String(stats.low).padStart(2, ' ')} ${darkBorder('│')}`,
176
- `${darkBorder('└───────────────────────────────────────┘')}`
177
- ];
156
+ console.log(' '.repeat(65) + cyan.bold(timeStr));
157
+
158
+ // 1. Header (Shield + VIBEGUARD)
159
+ console.log(`${cyan(shield[0])} ${cyan.bold(title[0])}`);
160
+ console.log(`${cyan(shield[1])} ${cyan.bold(title[1])}`);
161
+ console.log(`${cyan(shield[2])} ${cyan.bold(title[2])}`);
162
+ console.log(`${cyan(shield[3])} ${cyan.bold(title[3])}`);
163
+ console.log(`${cyan(shield[4])} ${cyan.bold(title[4])}`);
164
+ console.log(`${cyan(shield[5])} ${gray('AI-Powered DevSecOps Orchestrator')}`);
165
+ console.log(` ${cyan('Scanning. Analyzing. Protecting.')}\n`);
166
+
167
+ // 2. Risk Score Box (Placed cleanly BELOW VIBEGUARD Header)
168
+ const boxWidth = 74;
169
+ console.log(darkBorder(`┌${'─'.repeat(boxWidth)}┐`));
170
+ console.log(`${darkBorder('│')} ${cyan.bold('OVERALL RISK SCORE')}${' '.repeat(boxWidth - 20)}${darkBorder('│')}`);
171
+ console.log(`${darkBorder('')}${' '.repeat(boxWidth)}${darkBorder('│')}`);
172
+
173
+ const scoreLine1 = ` ${scoreColor.bold(String(stats.score))} ${gray('/100')} ${red('CRITICAL')} ${String(stats.critical).padEnd(2, ' ')} ${orange('HIGH')} ${String(stats.high).padEnd(2, ' ')}`;
174
+ const scoreLine2 = ` ${riskColor.bold(stats.riskLevel.padEnd(13, ' '))} ${yellow('MEDIUM')} ${String(stats.medium).padEnd(2, ' ')} ${cyan('LOW')} ${String(stats.low).padEnd(2, ' ')}`;
178
175
 
179
- for (let i = 0; i < leftHeader.length; i++) {
180
- const l = padVisible(leftHeader[i], 66);
181
- const r = rightBox[i] || '';
182
- console.log(`${l}${r}`);
183
- }
176
+ console.log(`${darkBorder('│')}${padVisible(scoreLine1, boxWidth)}${darkBorder('│')}`);
177
+ console.log(`${darkBorder('│')}${padVisible(scoreLine2, boxWidth)}${darkBorder('│')}`);
178
+ console.log(darkBorder(`└${''.repeat(boxWidth)}┘`));
184
179
  console.log('');
185
180
 
186
- // 3-Column / Multi-panel Grid
181
+ // 3. Multi-panel Grid (Pipeline & Findings)
187
182
  const pipelineRows = [
188
- `${gray('</>')} SAST (Semgrep) ${green('✓ OK')}`,
183
+ `</> SAST (Semgrep) ${green('✓ OK')}`,
189
184
  `📦 Dependency Check (Trivy) ${green('✓ OK')}`,
190
185
  `🔑 Secrets Scan (Gitleaks) ${green('✓ OK')}`,
191
- `☁️ IaC Scan (Checkov) ${green('✓ OK')}`,
186
+ `🔒 IaC Scan (Checkov) ${green('✓ OK')}`,
192
187
  `🐳 Container Scan (Trivy) ${green('✓ OK')}`,
193
188
  `📑 Code Quality (ESLint) ${green('✓ OK')}`,
194
189
  ``,
@@ -202,7 +197,7 @@ export function renderDashboard(options: {
202
197
 
203
198
  // Table of findings (top 10)
204
199
  const topFindings = findings.slice(0, 10);
205
- const tableHeader = `${dimGray(padVisible('ID', 12))} ${dimGray(padVisible('SEVERITY', 10))} ${dimGray(padVisible('TITLE', 28))} ${dimGray('FILE:LINE')}`;
200
+ const tableHeader = `${dimGray(padVisible('ID', 13))} ${dimGray(padVisible('SEVERITY', 10))} ${dimGray(padVisible('TITLE', 28))} ${dimGray('FILE:LINE')}`;
206
201
 
207
202
  const findingRows: string[] = [tableHeader];
208
203
 
@@ -218,11 +213,11 @@ export function renderDashboard(options: {
218
213
  else if (sev === 'MEDIUM') sevFormatted = yellow('MEDIUM ');
219
214
 
220
215
  const rawTitle = f.title || 'Security Finding';
221
- const titleTruncated = rawTitle.length > 25 ? rawTitle.slice(0, 23) + '..' : rawTitle;
216
+ const titleTruncated = rawTitle.length > 26 ? rawTitle.slice(0, 24) + '..' : rawTitle;
222
217
  const titleFormatted = padVisible(white(titleTruncated), 28);
223
218
 
224
219
  const fileLine = `${f.file || 'unknown'}:${f.line || 1}`;
225
- const idFormatted = padVisible(cyan(id), 12);
220
+ const idFormatted = padVisible(cyan(id), 13);
226
221
  findingRows.push(`${idFormatted} ${sevFormatted} ${titleFormatted} ${dimGray(fileLine)}`);
227
222
  }
228
223
  }
@@ -230,7 +225,7 @@ export function renderDashboard(options: {
230
225
  // Print Section Headers with ANSI-safe padding
231
226
  const headerCol1 = padVisible(cyan('▶ SCANNER PIPELINE'), 36);
232
227
  const headerCol2 = cyan('▶ TOP FINDINGS');
233
- console.log(`\n${headerCol1} ${headerCol2}`);
228
+ console.log(`${headerCol1} ${headerCol2}`);
234
229
 
235
230
  const maxRows = Math.max(pipelineRows.length, findingRows.length);
236
231
  for (let i = 0; i < maxRows; i++) {
@@ -239,7 +234,7 @@ export function renderDashboard(options: {
239
234
  console.log(`${left} ${right}`);
240
235
  }
241
236
 
242
- // AI Remediation Section
237
+ // 4. AI Remediation Section
243
238
  if (remediation) {
244
239
  console.log(`\n${cyan('▶ AI REMEDIATION (VG-AI)')}\n`);
245
240
  console.log(`${cyan('Finding:')} ${red.bold(remediation.findingId)}\n`);
@@ -265,7 +260,7 @@ export function renderDashboard(options: {
265
260
  console.log(`\n${cyan('Confidence:')} ${green.bold(remediation.confidence + '%')}`);
266
261
  }
267
262
 
268
- // Summary Bar (bottom capsule)
263
+ // 5. Summary Bar (bottom capsule)
269
264
  console.log(`\n${cyan('▶ SUMMARY')}`);
270
265
  const summaryText = `🛡️ Scan completed in ${duration} │ ${stats.total} findings │ 6 passed`;
271
266
  const barTop = `┌${'─'.repeat(visibleWidth(summaryText) + 4)}┐`;