vibetachyon 1.5.1 → 1.5.2
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/core/persona/config.js +32 -0
- package/dist/core/security/cipher-audit.js +322 -0
- package/dist/index.js +134 -1
- package/package.json +10 -2
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AGENT_BIN_MAP = void 0;
|
|
6
7
|
exports.getActivePersonaPath = getActivePersonaPath;
|
|
7
8
|
exports.listPersonas = listPersonas;
|
|
8
9
|
exports.isPersonaId = isPersonaId;
|
|
@@ -11,10 +12,23 @@ exports.saveActivePersona = saveActivePersona;
|
|
|
11
12
|
exports.loadActivePersona = loadActivePersona;
|
|
12
13
|
const fs_1 = __importDefault(require("fs"));
|
|
13
14
|
const path_1 = __importDefault(require("path"));
|
|
15
|
+
// Agent name → persona id map (for CLI bin detection)
|
|
16
|
+
exports.AGENT_BIN_MAP = {
|
|
17
|
+
quasar: 'architect',
|
|
18
|
+
lumen: 'frontend',
|
|
19
|
+
axiom: 'backend',
|
|
20
|
+
cipher: 'security',
|
|
21
|
+
flux: 'performance',
|
|
22
|
+
prism: 'reviewer',
|
|
23
|
+
zenith: 'growth',
|
|
24
|
+
};
|
|
14
25
|
const PERSONAS = {
|
|
15
26
|
architect: {
|
|
16
27
|
id: 'architect',
|
|
17
28
|
label: 'Architect',
|
|
29
|
+
agentName: 'QUASAR',
|
|
30
|
+
agentColor: '\x1b[38;5;220m',
|
|
31
|
+
agentTagline: 'Projeta sistemas que escalam infinitamente.',
|
|
18
32
|
description: 'Prioritizes structure, long-term shape, dependency boundaries, and execution sequencing.',
|
|
19
33
|
defaultChecks: ['context', 'spec', 'gate'],
|
|
20
34
|
riskTolerance: 'low',
|
|
@@ -24,6 +38,9 @@ const PERSONAS = {
|
|
|
24
38
|
frontend: {
|
|
25
39
|
id: 'frontend',
|
|
26
40
|
label: 'Frontend',
|
|
41
|
+
agentName: 'LUMEN',
|
|
42
|
+
agentColor: '\x1b[38;5;231m',
|
|
43
|
+
agentTagline: 'Interfaces que iluminam a experiência do usuário.',
|
|
27
44
|
description: 'Prioritizes UI quality, framework fit, and user-facing implementation details.',
|
|
28
45
|
defaultChecks: ['context', 'frontend', 'gate'],
|
|
29
46
|
riskTolerance: 'medium',
|
|
@@ -33,6 +50,9 @@ const PERSONAS = {
|
|
|
33
50
|
backend: {
|
|
34
51
|
id: 'backend',
|
|
35
52
|
label: 'Backend',
|
|
53
|
+
agentName: 'AXIOM',
|
|
54
|
+
agentColor: '\x1b[38;5;39m',
|
|
55
|
+
agentTagline: 'A verdade por trás de toda API e banco de dados.',
|
|
36
56
|
description: 'Prioritizes API contracts, services, persistence, and execution safety on the server side.',
|
|
37
57
|
defaultChecks: ['context', 'gate'],
|
|
38
58
|
riskTolerance: 'medium',
|
|
@@ -42,6 +62,9 @@ const PERSONAS = {
|
|
|
42
62
|
security: {
|
|
43
63
|
id: 'security',
|
|
44
64
|
label: 'Security',
|
|
65
|
+
agentName: 'CIPHER',
|
|
66
|
+
agentColor: '\x1b[38;5;46m',
|
|
67
|
+
agentTagline: 'Nenhuma vulnerabilidade escapa do Cipher.',
|
|
45
68
|
description: 'Prioritizes auth, access scope, unsafe patterns, secrets, and exploit surfaces.',
|
|
46
69
|
defaultChecks: ['context', 'gate', 'release-evidence'],
|
|
47
70
|
riskTolerance: 'low',
|
|
@@ -51,6 +74,9 @@ const PERSONAS = {
|
|
|
51
74
|
performance: {
|
|
52
75
|
id: 'performance',
|
|
53
76
|
label: 'Performance',
|
|
77
|
+
agentName: 'FLUX',
|
|
78
|
+
agentColor: '\x1b[38;5;51m',
|
|
79
|
+
agentTagline: 'Velocidade sem resistência.',
|
|
54
80
|
description: 'Prioritizes Lighthouse budgets, runtime weight, latency, and delivery efficiency.',
|
|
55
81
|
defaultChecks: ['context', 'frontend', 'perf'],
|
|
56
82
|
riskTolerance: 'medium',
|
|
@@ -60,6 +86,9 @@ const PERSONAS = {
|
|
|
60
86
|
reviewer: {
|
|
61
87
|
id: 'reviewer',
|
|
62
88
|
label: 'Reviewer',
|
|
89
|
+
agentName: 'PRISM',
|
|
90
|
+
agentColor: '\x1b[38;5;135m',
|
|
91
|
+
agentTagline: 'Enxerga seu código de todos os ângulos.',
|
|
63
92
|
description: 'Prioritizes findings, regressions, and missing validation before approval.',
|
|
64
93
|
defaultChecks: ['context', 'gate', 'release-evidence'],
|
|
65
94
|
riskTolerance: 'low',
|
|
@@ -69,6 +98,9 @@ const PERSONAS = {
|
|
|
69
98
|
growth: {
|
|
70
99
|
id: 'growth',
|
|
71
100
|
label: 'Growth',
|
|
101
|
+
agentName: 'ZENITH',
|
|
102
|
+
agentColor: '\x1b[38;5;208m',
|
|
103
|
+
agentTagline: 'Leva seu SaaS ao ponto mais alto.',
|
|
72
104
|
description: 'Prioritizes funnels, conversion, tracking, offers, and experiment surfaces.',
|
|
73
105
|
defaultChecks: ['context', 'frontend', 'gate'],
|
|
74
106
|
riskTolerance: 'high',
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runCipherAudit = runCipherAudit;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
// Padrões de secrets hardcoded
|
|
10
|
+
const SECRET_PATTERNS = [
|
|
11
|
+
{ pattern: /sk_live_[a-zA-Z0-9]{20,}/g, label: 'Stripe secret key (live)' },
|
|
12
|
+
{ pattern: /sk_test_[a-zA-Z0-9]{20,}/g, label: 'Stripe secret key (test)' },
|
|
13
|
+
{ pattern: /rk_live_[a-zA-Z0-9]{20,}/g, label: 'Stripe restricted key (live)' },
|
|
14
|
+
{ pattern: /AKIA[0-9A-Z]{16}/g, label: 'AWS Access Key ID' },
|
|
15
|
+
{ pattern: /ghp_[a-zA-Z0-9]{36}/g, label: 'GitHub Personal Access Token' },
|
|
16
|
+
{ pattern: /xoxb-[0-9]{11}-[0-9]{11}-[a-zA-Z0-9]{24}/g, label: 'Slack Bot Token' },
|
|
17
|
+
{ pattern: /AIza[0-9A-Za-z\-_]{35}/g, label: 'Google API Key' },
|
|
18
|
+
{ pattern: /eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, label: 'JWT Token hardcoded' },
|
|
19
|
+
{ pattern: /password\s*[:=]\s*["'][^"']{6,}["']/gi, label: 'Hardcoded password' },
|
|
20
|
+
{ pattern: /secret\s*[:=]\s*["'][^"']{8,}["']/gi, label: 'Hardcoded secret' },
|
|
21
|
+
{ pattern: /api[_-]?key\s*[:=]\s*["'][^"']{8,}["']/gi, label: 'Hardcoded API key' },
|
|
22
|
+
];
|
|
23
|
+
const SKIP_DIRS = new Set([
|
|
24
|
+
'node_modules', '.git', '.next', 'dist', 'build', '.turbo',
|
|
25
|
+
'coverage', '.cache', '__pycache__', 'vendor',
|
|
26
|
+
]);
|
|
27
|
+
const SCAN_EXTENSIONS = new Set([
|
|
28
|
+
'.ts', '.tsx', '.js', '.jsx', '.env', '.json', '.yaml', '.yml',
|
|
29
|
+
'.py', '.rb', '.go', '.php', '.java', '.cs', '.sh', '.bash',
|
|
30
|
+
]);
|
|
31
|
+
function walkFiles(dir, maxFiles = 2000) {
|
|
32
|
+
const results = [];
|
|
33
|
+
function recurse(current, depth) {
|
|
34
|
+
if (depth > 8 || results.length >= maxFiles)
|
|
35
|
+
return;
|
|
36
|
+
let entries;
|
|
37
|
+
try {
|
|
38
|
+
entries = fs_1.default.readdirSync(current, { withFileTypes: true });
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
if (SKIP_DIRS.has(entry.name))
|
|
45
|
+
continue;
|
|
46
|
+
const full = path_1.default.join(current, entry.name);
|
|
47
|
+
if (entry.isDirectory()) {
|
|
48
|
+
recurse(full, depth + 1);
|
|
49
|
+
}
|
|
50
|
+
else if (entry.isFile()) {
|
|
51
|
+
const ext = path_1.default.extname(entry.name).toLowerCase();
|
|
52
|
+
if (SCAN_EXTENSIONS.has(ext) || entry.name.startsWith('.env')) {
|
|
53
|
+
results.push(full);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
recurse(dir, 0);
|
|
59
|
+
return results;
|
|
60
|
+
}
|
|
61
|
+
function checkSecrets(files, projectRoot) {
|
|
62
|
+
const findings = [];
|
|
63
|
+
for (const file of files) {
|
|
64
|
+
// Pula arquivos de lock e binários
|
|
65
|
+
if (file.endsWith('.lock') || file.endsWith('.min.js'))
|
|
66
|
+
continue;
|
|
67
|
+
let content;
|
|
68
|
+
try {
|
|
69
|
+
content = fs_1.default.readFileSync(file, 'utf8');
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const lines = content.split('\n');
|
|
75
|
+
for (const { pattern, label } of SECRET_PATTERNS) {
|
|
76
|
+
pattern.lastIndex = 0;
|
|
77
|
+
for (let i = 0; i < lines.length; i++) {
|
|
78
|
+
const line = lines[i];
|
|
79
|
+
if (pattern.test(line)) {
|
|
80
|
+
// Pula se estiver em comentário ou exemplo óbvio
|
|
81
|
+
if (/\/\/.*example|\/\/.*sample|#.*example/i.test(line))
|
|
82
|
+
continue;
|
|
83
|
+
if (/your[_-]?key|placeholder|xxx|abc123/i.test(line))
|
|
84
|
+
continue;
|
|
85
|
+
findings.push({
|
|
86
|
+
severity: 'critical',
|
|
87
|
+
category: 'Secret Exposure',
|
|
88
|
+
message: `${label} encontrado hardcoded`,
|
|
89
|
+
file: path_1.default.relative(projectRoot, file),
|
|
90
|
+
line: i + 1,
|
|
91
|
+
snippet: line.trim().slice(0, 80),
|
|
92
|
+
});
|
|
93
|
+
pattern.lastIndex = 0;
|
|
94
|
+
break; // um finding por arquivo por padrão
|
|
95
|
+
}
|
|
96
|
+
pattern.lastIndex = 0;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return findings;
|
|
101
|
+
}
|
|
102
|
+
function checkEnvFiles(projectRoot) {
|
|
103
|
+
const findings = [];
|
|
104
|
+
const gitignorePath = path_1.default.join(projectRoot, '.gitignore');
|
|
105
|
+
const envPath = path_1.default.join(projectRoot, '.env');
|
|
106
|
+
const envLocalPath = path_1.default.join(projectRoot, '.env.local');
|
|
107
|
+
let gitignoreContent = '';
|
|
108
|
+
if (fs_1.default.existsSync(gitignorePath)) {
|
|
109
|
+
gitignoreContent = fs_1.default.readFileSync(gitignorePath, 'utf8');
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
findings.push({
|
|
113
|
+
severity: 'warning',
|
|
114
|
+
category: 'Git Security',
|
|
115
|
+
message: '.gitignore não encontrado — secrets podem ser commitados acidentalmente',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (fs_1.default.existsSync(envPath)) {
|
|
119
|
+
if (!gitignoreContent.includes('.env')) {
|
|
120
|
+
findings.push({
|
|
121
|
+
severity: 'critical',
|
|
122
|
+
category: 'Git Security',
|
|
123
|
+
message: '.env existe mas NÃO está no .gitignore — risco de exposição de secrets',
|
|
124
|
+
file: '.env',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
findings.push({
|
|
129
|
+
severity: 'info',
|
|
130
|
+
category: 'Git Security',
|
|
131
|
+
message: '.env presente e protegido pelo .gitignore',
|
|
132
|
+
file: '.env',
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (fs_1.default.existsSync(envLocalPath) && !gitignoreContent.includes('.env.local')) {
|
|
137
|
+
findings.push({
|
|
138
|
+
severity: 'warning',
|
|
139
|
+
category: 'Git Security',
|
|
140
|
+
message: '.env.local existe mas pode não estar no .gitignore',
|
|
141
|
+
file: '.env.local',
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return findings;
|
|
145
|
+
}
|
|
146
|
+
function checkDependencies(projectRoot) {
|
|
147
|
+
const findings = [];
|
|
148
|
+
const pkgPath = path_1.default.join(projectRoot, 'package.json');
|
|
149
|
+
if (!fs_1.default.existsSync(pkgPath))
|
|
150
|
+
return findings;
|
|
151
|
+
let pkg;
|
|
152
|
+
try {
|
|
153
|
+
pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf8'));
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return findings;
|
|
157
|
+
}
|
|
158
|
+
const allDeps = {
|
|
159
|
+
...pkg.dependencies,
|
|
160
|
+
...pkg.devDependencies,
|
|
161
|
+
};
|
|
162
|
+
// Pacotes conhecidamente problemáticos ou abandonados
|
|
163
|
+
const flagged = {
|
|
164
|
+
'event-stream': 'Pacote comprometido em supply chain attack (2018)',
|
|
165
|
+
'flatmap-stream': 'Pacote malicioso injetado via event-stream',
|
|
166
|
+
'node-uuid': 'Deprecated — use "uuid" em vez disso',
|
|
167
|
+
'request': 'Deprecated e não mantido desde 2020',
|
|
168
|
+
'lodash': 'Versões < 4.17.21 têm vulnerabilidade de prototype pollution',
|
|
169
|
+
};
|
|
170
|
+
for (const [dep, reason] of Object.entries(flagged)) {
|
|
171
|
+
if (allDeps[dep]) {
|
|
172
|
+
findings.push({
|
|
173
|
+
severity: dep === 'event-stream' || dep === 'flatmap-stream' ? 'critical' : 'warning',
|
|
174
|
+
category: 'Dependencies',
|
|
175
|
+
message: `${dep}@${allDeps[dep]}: ${reason}`,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Verifica se tem npm audit disponível
|
|
180
|
+
findings.push({
|
|
181
|
+
severity: 'info',
|
|
182
|
+
category: 'Dependencies',
|
|
183
|
+
message: `${Object.keys(allDeps).length} dependências encontradas. Rode "npm audit" para verificação completa.`,
|
|
184
|
+
});
|
|
185
|
+
return findings;
|
|
186
|
+
}
|
|
187
|
+
function checkAuthPatterns(files, projectRoot) {
|
|
188
|
+
const findings = [];
|
|
189
|
+
// Rotas sem middleware de auth em Next.js/Express
|
|
190
|
+
const routeFiles = files.filter(f => (f.includes('/api/') || f.includes('/routes/')) &&
|
|
191
|
+
(f.endsWith('.ts') || f.endsWith('.js')));
|
|
192
|
+
let unprotectedRoutes = 0;
|
|
193
|
+
for (const file of routeFiles) {
|
|
194
|
+
let content;
|
|
195
|
+
try {
|
|
196
|
+
content = fs_1.default.readFileSync(file, 'utf8');
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const hasAuth = content.includes('getSession') ||
|
|
202
|
+
content.includes('getServerSession') ||
|
|
203
|
+
content.includes('auth()') ||
|
|
204
|
+
content.includes('requireAuth') ||
|
|
205
|
+
content.includes('verifyToken') ||
|
|
206
|
+
content.includes('middleware') ||
|
|
207
|
+
content.includes('authenticate') ||
|
|
208
|
+
content.includes('authorize') ||
|
|
209
|
+
content.includes('clerkClient') ||
|
|
210
|
+
content.includes('currentUser');
|
|
211
|
+
const isPublicRoute = file.includes('/auth/') ||
|
|
212
|
+
file.includes('/public/') ||
|
|
213
|
+
file.includes('webhook') ||
|
|
214
|
+
file.includes('health') ||
|
|
215
|
+
file.includes('callback');
|
|
216
|
+
if (!hasAuth && !isPublicRoute) {
|
|
217
|
+
unprotectedRoutes++;
|
|
218
|
+
if (unprotectedRoutes <= 3) {
|
|
219
|
+
findings.push({
|
|
220
|
+
severity: 'warning',
|
|
221
|
+
category: 'Authentication',
|
|
222
|
+
message: 'Rota sem verificação de autenticação detectada',
|
|
223
|
+
file: path_1.default.relative(projectRoot, file),
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (unprotectedRoutes > 3) {
|
|
229
|
+
findings.push({
|
|
230
|
+
severity: 'warning',
|
|
231
|
+
category: 'Authentication',
|
|
232
|
+
message: `${unprotectedRoutes - 3} outras rotas sem autenticação detectadas`,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
return findings;
|
|
236
|
+
}
|
|
237
|
+
function calculateScore(findings) {
|
|
238
|
+
let score = 100;
|
|
239
|
+
for (const f of findings) {
|
|
240
|
+
if (f.severity === 'critical')
|
|
241
|
+
score -= 20;
|
|
242
|
+
else if (f.severity === 'warning')
|
|
243
|
+
score -= 5;
|
|
244
|
+
}
|
|
245
|
+
return Math.max(0, score);
|
|
246
|
+
}
|
|
247
|
+
function saveReport(result, projectRoot) {
|
|
248
|
+
const dir = path_1.default.join(projectRoot, '.vibetachyon');
|
|
249
|
+
if (!fs_1.default.existsSync(dir))
|
|
250
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
251
|
+
const reportPath = path_1.default.join(dir, 'cipher-audit.md');
|
|
252
|
+
const scoreEmoji = result.summary.score >= 80 ? '🟢' : result.summary.score >= 50 ? '🟡' : '🔴';
|
|
253
|
+
const lines = [
|
|
254
|
+
`# CIPHER — Security Audit Report`,
|
|
255
|
+
``,
|
|
256
|
+
`**Projeto:** \`${result.projectRoot}\``,
|
|
257
|
+
`**Data:** ${new Date(result.timestamp).toLocaleString('pt-BR')}`,
|
|
258
|
+
`**Score de Segurança:** ${scoreEmoji} ${result.summary.score}/100`,
|
|
259
|
+
``,
|
|
260
|
+
`## Resumo`,
|
|
261
|
+
``,
|
|
262
|
+
`| Severidade | Total |`,
|
|
263
|
+
`|------------|-------|`,
|
|
264
|
+
`| 🔴 Critical | ${result.summary.critical} |`,
|
|
265
|
+
`| 🟡 Warning | ${result.summary.warning} |`,
|
|
266
|
+
`| 🔵 Info | ${result.summary.info} |`,
|
|
267
|
+
``,
|
|
268
|
+
`## Findings`,
|
|
269
|
+
``,
|
|
270
|
+
];
|
|
271
|
+
const criticals = result.findings.filter(f => f.severity === 'critical');
|
|
272
|
+
const warnings = result.findings.filter(f => f.severity === 'warning');
|
|
273
|
+
const infos = result.findings.filter(f => f.severity === 'info');
|
|
274
|
+
for (const group of [
|
|
275
|
+
{ label: '🔴 Critical', items: criticals },
|
|
276
|
+
{ label: '🟡 Warning', items: warnings },
|
|
277
|
+
{ label: '🔵 Info', items: infos },
|
|
278
|
+
]) {
|
|
279
|
+
if (group.items.length === 0)
|
|
280
|
+
continue;
|
|
281
|
+
lines.push(`### ${group.label}`);
|
|
282
|
+
lines.push('');
|
|
283
|
+
for (const f of group.items) {
|
|
284
|
+
lines.push(`- **[${f.category}]** ${f.message}`);
|
|
285
|
+
if (f.file)
|
|
286
|
+
lines.push(` - Arquivo: \`${f.file}\`${f.line ? ` (linha ${f.line})` : ''}`);
|
|
287
|
+
if (f.snippet)
|
|
288
|
+
lines.push(` - \`${f.snippet}\``);
|
|
289
|
+
}
|
|
290
|
+
lines.push('');
|
|
291
|
+
}
|
|
292
|
+
lines.push(`---`);
|
|
293
|
+
lines.push(`*Gerado pelo CIPHER — VibeTachyon Security Agent*`);
|
|
294
|
+
fs_1.default.writeFileSync(reportPath, lines.join('\n'), 'utf8');
|
|
295
|
+
return reportPath;
|
|
296
|
+
}
|
|
297
|
+
async function runCipherAudit(projectRoot) {
|
|
298
|
+
const files = walkFiles(projectRoot);
|
|
299
|
+
const findings = [
|
|
300
|
+
...checkSecrets(files, projectRoot),
|
|
301
|
+
...checkEnvFiles(projectRoot),
|
|
302
|
+
...checkDependencies(projectRoot),
|
|
303
|
+
...checkAuthPatterns(files, projectRoot),
|
|
304
|
+
];
|
|
305
|
+
const critical = findings.filter(f => f.severity === 'critical').length;
|
|
306
|
+
const warning = findings.filter(f => f.severity === 'warning').length;
|
|
307
|
+
const info = findings.filter(f => f.severity === 'info').length;
|
|
308
|
+
const result = {
|
|
309
|
+
projectRoot,
|
|
310
|
+
timestamp: new Date().toISOString(),
|
|
311
|
+
findings,
|
|
312
|
+
summary: {
|
|
313
|
+
critical,
|
|
314
|
+
warning,
|
|
315
|
+
info,
|
|
316
|
+
total: findings.length,
|
|
317
|
+
score: calculateScore(findings),
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
saveReport(result, projectRoot);
|
|
321
|
+
return result;
|
|
322
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,7 @@ const update_js_1 = require("./core/spec/update.js");
|
|
|
50
50
|
const validate_js_1 = require("./core/spec/validate.js");
|
|
51
51
|
const personas_js_1 = require("./core/orchestrator/personas.js");
|
|
52
52
|
const config_js_3 = require("./core/persona/config.js");
|
|
53
|
+
const cipher_audit_js_1 = require("./core/security/cipher-audit.js");
|
|
53
54
|
const contract_js_1 = require("./core/persona/contract.js");
|
|
54
55
|
const inspect_js_1 = require("./core/persona/inspect.js");
|
|
55
56
|
const recommend_js_1 = require("./core/persona/recommend.js");
|
|
@@ -119,6 +120,70 @@ function printBanner() {
|
|
|
119
120
|
`;
|
|
120
121
|
console.log(banner);
|
|
121
122
|
}
|
|
123
|
+
const AGENT_ASCII = {
|
|
124
|
+
QUASAR: `
|
|
125
|
+
██████╗ ██╗ ██╗ █████╗ ███████╗ █████╗ ██████╗
|
|
126
|
+
██╔═══██╗██║ ██║██╔══██╗██╔════╝██╔══██╗██╔══██╗
|
|
127
|
+
██║ ██║██║ ██║███████║███████╗███████║██████╔╝
|
|
128
|
+
██║▄▄ ██║██║ ██║██╔══██║╚════██║██╔══██║██╔══██╗
|
|
129
|
+
╚██████╔╝╚██████╔╝██║ ██║███████║██║ ██║██║ ██║
|
|
130
|
+
╚══▀▀═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝`,
|
|
131
|
+
LUMEN: `
|
|
132
|
+
██╗ ██╗ ██╗███╗ ███╗███████╗███╗ ██╗
|
|
133
|
+
██║ ██║ ██║████╗ ████║██╔════╝████╗ ██║
|
|
134
|
+
██║ ██║ ██║██╔████╔██║█████╗ ██╔██╗ ██║
|
|
135
|
+
██║ ██║ ██║██║╚██╔╝██║██╔══╝ ██║╚██╗██║
|
|
136
|
+
███████╗╚██████╔╝██║ ╚═╝ ██║███████╗██║ ╚████║
|
|
137
|
+
╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝`,
|
|
138
|
+
AXIOM: `
|
|
139
|
+
█████╗ ██╗ ██╗██╗ ██████╗ ███╗ ███╗
|
|
140
|
+
██╔══██╗╚██╗██╔╝██║██╔═══██╗████╗ ████║
|
|
141
|
+
███████║ ╚███╔╝ ██║██║ ██║██╔████╔██║
|
|
142
|
+
██╔══██║ ██╔██╗ ██║██║ ██║██║╚██╔╝██║
|
|
143
|
+
██║ ██║██╔╝ ██╗██║╚██████╔╝██║ ╚═╝ ██║
|
|
144
|
+
╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═╝ ╚═╝`,
|
|
145
|
+
CIPHER: `
|
|
146
|
+
██████╗██╗██████╗ ██╗ ██╗███████╗██████╗
|
|
147
|
+
██╔════╝██║██╔══██╗██║ ██║██╔════╝██╔══██╗
|
|
148
|
+
██║ ██║██████╔╝███████║█████╗ ██████╔╝
|
|
149
|
+
██║ ██║██╔═══╝ ██╔══██║██╔══╝ ██╔══██╗
|
|
150
|
+
╚██████╗██║██║ ██║ ██║███████╗██║ ██║
|
|
151
|
+
╚═════╝╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝`,
|
|
152
|
+
FLUX: `
|
|
153
|
+
███████╗██╗ ██╗ ██╗██╗ ██╗
|
|
154
|
+
██╔════╝██║ ██║ ██║╚██╗██╔╝
|
|
155
|
+
█████╗ ██║ ██║ ██║ ╚███╔╝
|
|
156
|
+
██╔══╝ ██║ ██║ ██║ ██╔██╗
|
|
157
|
+
██║ ███████╗╚██████╔╝██╔╝ ██╗
|
|
158
|
+
╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝`,
|
|
159
|
+
PRISM: `
|
|
160
|
+
██████╗ ██████╗ ██╗███████╗███╗ ███╗
|
|
161
|
+
██╔══██╗██╔══██╗██║██╔════╝████╗ ████║
|
|
162
|
+
██████╔╝██████╔╝██║███████╗██╔████╔██║
|
|
163
|
+
██╔═══╝ ██╔══██╗██║╚════██║██║╚██╔╝██║
|
|
164
|
+
██║ ██║ ██║██║███████║██║ ╚═╝ ██║
|
|
165
|
+
╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝ ╚═╝`,
|
|
166
|
+
ZENITH: `
|
|
167
|
+
███████╗███████╗███╗ ██╗██╗████████╗██╗ ██╗
|
|
168
|
+
╚══███╔╝██╔════╝████╗ ██║██║╚══██╔══╝██║ ██║
|
|
169
|
+
███╔╝ █████╗ ██╔██╗ ██║██║ ██║ ███████║
|
|
170
|
+
███╔╝ ██╔══╝ ██║╚██╗██║██║ ██║ ██╔══██║
|
|
171
|
+
███████╗███████╗██║ ╚████║██║ ██║ ██║ ██║
|
|
172
|
+
╚══════╝╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═╝`,
|
|
173
|
+
};
|
|
174
|
+
function printAgentBanner(personaId) {
|
|
175
|
+
const persona = (0, config_js_3.getPersona)(personaId);
|
|
176
|
+
const ascii = AGENT_ASCII[persona.agentName] ?? '';
|
|
177
|
+
console.log(`${persona.agentColor}${ascii}\x1b[0m`);
|
|
178
|
+
console.log(`${persona.agentColor} ⚡ ${persona.agentName} — ${persona.label} Agent\x1b[0m`);
|
|
179
|
+
console.log(`\x1b[90m ${persona.agentTagline}\x1b[0m`);
|
|
180
|
+
console.log('');
|
|
181
|
+
}
|
|
182
|
+
// Detecta se foi invocado por nome de agente (ex: `cipher audit`)
|
|
183
|
+
function detectAgentBin() {
|
|
184
|
+
const binName = path_1.default.basename(process.argv[1] ?? '').replace(/\.js$/, '').toLowerCase();
|
|
185
|
+
return config_js_3.AGENT_BIN_MAP[binName] ?? null;
|
|
186
|
+
}
|
|
122
187
|
async function runInteractiveMode() {
|
|
123
188
|
printBanner();
|
|
124
189
|
(0, prompts_1.intro)(picocolors_1.default.yellow(`⚡ Orchestrating VibeTachyon CLI v${CLI_VERSION}...`));
|
|
@@ -5138,8 +5203,76 @@ async function main() {
|
|
|
5138
5203
|
console.log(result.message);
|
|
5139
5204
|
(0, session_state_js_1.recordSessionEvent)('mcp migrate', 'success', options.dryRun ? 'dry-run' : 'written');
|
|
5140
5205
|
});
|
|
5206
|
+
// Detecta se foi invocado por nome de agente (ex: `cipher audit`)
|
|
5207
|
+
const agentPersonaId = detectAgentBin();
|
|
5208
|
+
if (agentPersonaId) {
|
|
5209
|
+
printAgentBanner(agentPersonaId);
|
|
5210
|
+
// Auto-ativa a persona correspondente
|
|
5211
|
+
const projectRoot = process.cwd();
|
|
5212
|
+
(0, config_js_3.saveActivePersona)(projectRoot, agentPersonaId);
|
|
5213
|
+
}
|
|
5141
5214
|
if (process.argv.length <= 2) {
|
|
5142
|
-
|
|
5215
|
+
if (!agentPersonaId) {
|
|
5216
|
+
await runInteractiveMode();
|
|
5217
|
+
}
|
|
5218
|
+
else {
|
|
5219
|
+
const projectRoot = process.cwd();
|
|
5220
|
+
if (agentPersonaId === 'security') {
|
|
5221
|
+
const steps = [
|
|
5222
|
+
' Mapeando estrutura do projeto...',
|
|
5223
|
+
' Escaneando arquivos em busca de secrets...',
|
|
5224
|
+
' Verificando configurações de segurança...',
|
|
5225
|
+
' Analisando dependências...',
|
|
5226
|
+
' Verificando rotas e autenticação...',
|
|
5227
|
+
' Gerando relatório...',
|
|
5228
|
+
];
|
|
5229
|
+
const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
5230
|
+
let frameIdx = 0;
|
|
5231
|
+
let stepIdx = 0;
|
|
5232
|
+
const interval = setInterval(() => {
|
|
5233
|
+
const frame = spinnerFrames[frameIdx % spinnerFrames.length];
|
|
5234
|
+
const step = steps[Math.min(stepIdx, steps.length - 1)];
|
|
5235
|
+
process.stdout.write(`\r\x1b[38;5;46m ${frame}\x1b[0m\x1b[90m${step}\x1b[0m\x1b[K`);
|
|
5236
|
+
frameIdx++;
|
|
5237
|
+
if (frameIdx % 8 === 0 && stepIdx < steps.length - 1)
|
|
5238
|
+
stepIdx++;
|
|
5239
|
+
}, 80);
|
|
5240
|
+
const result = await (0, cipher_audit_js_1.runCipherAudit)(projectRoot);
|
|
5241
|
+
clearInterval(interval);
|
|
5242
|
+
process.stdout.write(`\r\x1b[K`);
|
|
5243
|
+
console.log(`\x1b[38;5;46m ✓ Auditoria concluída\x1b[0m\n`);
|
|
5244
|
+
const { summary } = result;
|
|
5245
|
+
const scoreColor = summary.score >= 80 ? '\x1b[38;5;46m' : summary.score >= 50 ? '\x1b[38;5;220m' : '\x1b[38;5;196m';
|
|
5246
|
+
console.log(`${scoreColor} Score de Segurança: ${summary.score}/100\x1b[0m`);
|
|
5247
|
+
console.log(`\x1b[38;5;196m 🔴 Critical: ${summary.critical}\x1b[0m`);
|
|
5248
|
+
console.log(`\x1b[38;5;220m 🟡 Warning: ${summary.warning}\x1b[0m`);
|
|
5249
|
+
console.log(`\x1b[38;5;39m 🔵 Info: ${summary.info}\x1b[0m`);
|
|
5250
|
+
console.log('');
|
|
5251
|
+
if (summary.critical > 0 || summary.warning > 0) {
|
|
5252
|
+
const criticals = result.findings.filter(f => f.severity === 'critical').slice(0, 3);
|
|
5253
|
+
const warnings = result.findings.filter(f => f.severity === 'warning').slice(0, 3);
|
|
5254
|
+
for (const f of criticals) {
|
|
5255
|
+
console.log(`\x1b[38;5;196m ❌ [${f.category}] ${f.message}\x1b[0m`);
|
|
5256
|
+
if (f.file)
|
|
5257
|
+
console.log(`\x1b[90m → ${f.file}${f.line ? `:${f.line}` : ''}\x1b[0m`);
|
|
5258
|
+
}
|
|
5259
|
+
for (const f of warnings) {
|
|
5260
|
+
console.log(`\x1b[38;5;220m ⚠️ [${f.category}] ${f.message}\x1b[0m`);
|
|
5261
|
+
if (f.file)
|
|
5262
|
+
console.log(`\x1b[90m → ${f.file}\x1b[0m`);
|
|
5263
|
+
}
|
|
5264
|
+
console.log('');
|
|
5265
|
+
}
|
|
5266
|
+
else {
|
|
5267
|
+
console.log(`\x1b[38;5;46m ✅ Nenhum problema crítico encontrado.\x1b[0m\n`);
|
|
5268
|
+
}
|
|
5269
|
+
console.log(`\x1b[90m Relatório completo salvo em .vibetachyon/cipher-audit.md\x1b[0m\n`);
|
|
5270
|
+
}
|
|
5271
|
+
else {
|
|
5272
|
+
const persona = (0, config_js_3.getPersona)(agentPersonaId);
|
|
5273
|
+
console.log(`\x1b[90m Persona ${persona.agentName} ativada. Use os comandos do VibeTachyon normalmente.\x1b[0m\n`);
|
|
5274
|
+
}
|
|
5275
|
+
}
|
|
5143
5276
|
return;
|
|
5144
5277
|
}
|
|
5145
5278
|
await program.parseAsync(process.argv);
|
package/package.json
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibetachyon",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "VibeCodes MCP CLI Installer and Server",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"vibetachyon": "dist/index.js"
|
|
7
|
+
"vibetachyon": "dist/index.js",
|
|
8
|
+
"quasar": "dist/index.js",
|
|
9
|
+
"lumen": "dist/index.js",
|
|
10
|
+
"axiom": "dist/index.js",
|
|
11
|
+
"cipher": "dist/index.js",
|
|
12
|
+
"flux": "dist/index.js",
|
|
13
|
+
"prism": "dist/index.js",
|
|
14
|
+
"zenith": "dist/index.js",
|
|
15
|
+
"nexus": "dist/index.js"
|
|
8
16
|
},
|
|
9
17
|
"scripts": {
|
|
10
18
|
"build": "tsc",
|