vibetachyon 1.5.0 → 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/README.md +7 -7
- package/dist/commands-data.js +1 -1
- package/dist/core/persona/config.js +32 -0
- package/dist/core/security/cipher-audit.js +322 -0
- package/dist/index.js +134 -1
- package/dist/installer.js +3 -3
- package/dist/mcp-server.js +67 -40
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
> O sistema operacional que roda dentro do seu code agent.
|
|
4
4
|
|
|
5
|
-
Transforma qualquer agente de código (Claude Code, Cursor, Windsurf, Cline...) em uma máquina especializada em desenvolvimento de produtos digitais — com **
|
|
5
|
+
Transforma qualquer agente de código (Claude Code, Cursor, Windsurf, Cline...) em uma máquina especializada em desenvolvimento de produtos digitais — com **39 slash commands**, **50+ skills especializadas**, **8 personas** e **workflows estruturados** prontos para usar.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx vibetachyon
|
|
8
|
+
npx vibetachyon
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
## O que você ganha
|
|
14
14
|
|
|
15
|
-
- **
|
|
15
|
+
- **39 slash commands** — `/vibe-review`, `/vibe-test`, `/vibe-explain`, `/vibe-bug`, `/vibe-pr`, `/vibe-changelog`, `/vibe-spec`, `/vibe-dev` e mais
|
|
16
16
|
- **50+ skills especializadas** — busca de componentes, segurança, performance, ADR, métricas, backlog, geração de testes, code review
|
|
17
17
|
- **8 personas** — Dev, QA, Architect, PM, Data, UX, DevOps, Analyst
|
|
18
18
|
- **5 squads pré-configurados** — fullstack, qa-focused, minimal, design, release
|
|
@@ -27,7 +27,7 @@ npx vibetachyon-cli
|
|
|
27
27
|
## Instalação
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npx vibetachyon
|
|
30
|
+
npx vibetachyon
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
Selecione seu agente, cole o token VibeCodes MAX e pronto.
|
|
@@ -35,9 +35,9 @@ Selecione seu agente, cole o token VibeCodes MAX e pronto.
|
|
|
35
35
|
### Flags não-interativas
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
npx vibetachyon
|
|
39
|
-
npx vibetachyon
|
|
40
|
-
npx vibetachyon
|
|
38
|
+
npx vibetachyon install --agent claude --token vcf_...
|
|
39
|
+
npx vibetachyon install --agent cursor --token vcf_... --dry-run
|
|
40
|
+
npx vibetachyon doctor
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
**Agentes suportados:** `claude` | `cursor` | `windsurf` | `cline` | `copilot` | `gemini` | `codex`
|
package/dist/commands-data.js
CHANGED
|
@@ -281,7 +281,7 @@ Activate the Vibe Dev persona for focused implementation work.
|
|
|
281
281
|
1. Call \`vibe_persona_activate\` with persona="dev" and the current task as context
|
|
282
282
|
2. Review the task requirements and acceptance criteria
|
|
283
283
|
3. Use \`vibe_scan_local_architecture\` to understand the codebase context before writing any code
|
|
284
|
-
4. Use \`vibe_search_snippets\`
|
|
284
|
+
4. **OBRIGATÓRIO para qualquer elemento visual:** Use \`vibe_search_snippets\` ANTES de escrever qualquer componente, botão, card, seção ou layout. Nunca invente UI do zero quando a biblioteca tem 4800+ componentes prontos.
|
|
285
285
|
5. Implement with these non-negotiable rules:
|
|
286
286
|
- Zero TypeScript errors before committing
|
|
287
287
|
- Use \`vibe_self_heal_typescript\` if type errors occur
|
|
@@ -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/dist/installer.js
CHANGED
|
@@ -242,8 +242,8 @@ async function runInstallFlow(options = {}) {
|
|
|
242
242
|
throw new Error(verification.error || 'Authentication failed');
|
|
243
243
|
}
|
|
244
244
|
if (useSpinner) {
|
|
245
|
-
s.message(picocolors_1.default.green(`✅ Authenticated as ${verification.name}. [
|
|
246
|
-
s.message(picocolors_1.default.cyan(`
|
|
245
|
+
s.message(picocolors_1.default.green(`✅ Authenticated as ${verification.name}. [CODEAGENT OS ACTIVATED]`));
|
|
246
|
+
s.message(picocolors_1.default.cyan(`Connecting VibeTachyon CodeAgent OS to ${agent}...`));
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
249
|
else if (useSpinner) {
|
|
@@ -276,7 +276,7 @@ async function runInstallFlow(options = {}) {
|
|
|
276
276
|
(0, prompts_1.outro)(picocolors_1.default.yellow('🧪 Dry-run mode complete. Re-run without --dry-run to persist.'));
|
|
277
277
|
}
|
|
278
278
|
else {
|
|
279
|
-
(0, prompts_1.outro)(picocolors_1.default.green('🚀 VibeTachyon
|
|
279
|
+
(0, prompts_1.outro)(picocolors_1.default.green('🚀 VibeTachyon CodeAgent OS is active! Your agent is now supercharged with 39 commands and 50+ skills.'));
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
return {
|
package/dist/mcp-server.js
CHANGED
|
@@ -29,7 +29,7 @@ if (proxyUrl) {
|
|
|
29
29
|
const supabase = (0, supabase_js_1.createClient)(supabaseUrl, supabaseKey, supabaseOptions);
|
|
30
30
|
// --- VibeTachyon Shield: Local Privacy & Sanity Traps ---
|
|
31
31
|
const LOCAL_CACHE_DIR = path_1.default.join(process.env.HOME || process.env.USERPROFILE || '', '.vibetachyon', 'cache');
|
|
32
|
-
const CALL_LIMIT_PER_SESSION =
|
|
32
|
+
const CALL_LIMIT_PER_SESSION = 150; // Sanity Trap to prevent agent infinite loops
|
|
33
33
|
let sessionCallCount = 0;
|
|
34
34
|
async function checkSanity() {
|
|
35
35
|
sessionCallCount++;
|
|
@@ -144,10 +144,37 @@ function minifySnippet(code) {
|
|
|
144
144
|
// Collapse excess newlines but keep some structure for the LLM
|
|
145
145
|
return minified.replace(/\n\s*\n/g, '\n').trim();
|
|
146
146
|
}
|
|
147
|
+
async function validateTokenAtStartup() {
|
|
148
|
+
const token = process.env.VIBECODES_TOKEN;
|
|
149
|
+
if (!token) {
|
|
150
|
+
console.error('[VibeTachyon] ERRO: VIBECODES_TOKEN não encontrado. Execute: npx vibetachyon para configurar.');
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
const apiUrl = process.env.VIBETACHYON_API_URL || 'https://vibecodings.com.br/api/vibecodes/mcp/verify';
|
|
154
|
+
try {
|
|
155
|
+
const res = await fetch(apiUrl, {
|
|
156
|
+
method: 'POST',
|
|
157
|
+
headers: { 'Content-Type': 'application/json' },
|
|
158
|
+
body: JSON.stringify({ token }),
|
|
159
|
+
signal: AbortSignal.timeout(8000)
|
|
160
|
+
});
|
|
161
|
+
const data = await res.json();
|
|
162
|
+
if (!data.success) {
|
|
163
|
+
console.error(`[VibeTachyon] Token inválido: ${data.error || 'Acesso negado'}. Reconfigure com: npx vibetachyon`);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
console.error(`[VibeTachyon] Autenticado como ${data.name || 'VibeCodes MAX User'}. CodeAgent OS ativo.`);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// Network error — allow graceful degradation (offline mode)
|
|
170
|
+
console.error('[VibeTachyon] Aviso: Não foi possível verificar o token (modo offline). Continuando...');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
147
173
|
async function startMcpServer() {
|
|
174
|
+
await validateTokenAtStartup();
|
|
148
175
|
const server = new mcp_js_1.McpServer({
|
|
149
176
|
name: "VibeTachyon MCP",
|
|
150
|
-
version: "1.
|
|
177
|
+
version: "1.5.0"
|
|
151
178
|
});
|
|
152
179
|
/**
|
|
153
180
|
* SYSTEM_PROMPT INJECTION:
|
|
@@ -2474,14 +2501,14 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
2474
2501
|
}
|
|
2475
2502
|
// Performance
|
|
2476
2503
|
if (/\.forEach\(/.test(content) && /await/.test(content)) {
|
|
2477
|
-
issues.push(`[WARNING] ${currentFile}:${lineNum}\n await dentro de forEach
|
|
2504
|
+
issues.push(`[WARNING] ${currentFile}:${lineNum}\n await dentro de forEach não funciona como esperado — use Promise.all() com .map() para operacoes async paralelas.`);
|
|
2478
2505
|
}
|
|
2479
2506
|
// Style / maintainability
|
|
2480
2507
|
if (/: any\b/.test(content) || /as any\b/.test(content)) {
|
|
2481
|
-
issues.push(`[SUGGESTION] ${currentFile}:${lineNum}\n Uso de 'any' detectado — definir tipo
|
|
2508
|
+
issues.push(`[SUGGESTION] ${currentFile}:${lineNum}\n Uso de 'any' detectado — definir tipo específico melhora a seguranca em tempo de compilacao.`);
|
|
2482
2509
|
}
|
|
2483
2510
|
if (/\/\/ TODO|\/\/ FIXME|\/\/ HACK/.test(content)) {
|
|
2484
|
-
issues.push(`[SUGGESTION] ${currentFile}:${lineNum}\n ${content.trim()} — registrar no backlog com vibe_backlog_manage se
|
|
2511
|
+
issues.push(`[SUGGESTION] ${currentFile}:${lineNum}\n ${content.trim()} — registrar no backlog com vibe_backlog_manage se não for resolver agora.`);
|
|
2485
2512
|
}
|
|
2486
2513
|
}
|
|
2487
2514
|
else if (!line.startsWith('-') && !line.startsWith('---') && !line.startsWith('diff') && !line.startsWith('index')) {
|
|
@@ -2497,7 +2524,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
2497
2524
|
? '🔴 BLOQUEADO — corrija os itens CRITICAL antes de commitar'
|
|
2498
2525
|
: warnings.length > 0
|
|
2499
2526
|
? '🟡 REVISAO RECOMENDADA — warnings encontrados'
|
|
2500
|
-
: '🟢 APROVADO — nenhum problema
|
|
2527
|
+
: '🟢 APROVADO — nenhum problema crítico detectado';
|
|
2501
2528
|
const sections = [
|
|
2502
2529
|
`[VIBE CODE REVIEW]\nMudancas analisadas: +${addedLines} adicoes, -${removedLines} remocoes\n`,
|
|
2503
2530
|
`Veredicto: ${verdict}\n`,
|
|
@@ -2509,7 +2536,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
2509
2536
|
if (suggestions.length > 0)
|
|
2510
2537
|
sections.push(`## SUGGESTION (${suggestions.length})\n${suggestions.join('\n\n')}\n`);
|
|
2511
2538
|
if (issues.length === 0)
|
|
2512
|
-
sections.push('Nenhum problema
|
|
2539
|
+
sections.push('Nenhum problema automático detectado. Considere também uma revisao manual do fluxo de negocio.\n');
|
|
2513
2540
|
return { content: [{ type: "text", text: sections.join('\n') }] };
|
|
2514
2541
|
}
|
|
2515
2542
|
catch (err) {
|
|
@@ -2525,7 +2552,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
2525
2552
|
try {
|
|
2526
2553
|
const absolutePath = path_1.default.isAbsolute(file_path) ? file_path : path_1.default.join(process.cwd(), file_path);
|
|
2527
2554
|
if (!(await fs_extra_1.default.pathExists(absolutePath))) {
|
|
2528
|
-
return { content: [{ type: "text", text: `[VIBE TEST] Arquivo
|
|
2555
|
+
return { content: [{ type: "text", text: `[VIBE TEST] Arquivo não encontrado: ${absolutePath}` }] };
|
|
2529
2556
|
}
|
|
2530
2557
|
const fileContent = await fs_extra_1.default.readFile(absolutePath, 'utf8');
|
|
2531
2558
|
const fileName = path_1.default.basename(absolutePath);
|
|
@@ -2579,7 +2606,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
2579
2606
|
expect(container).toMatchSnapshot();
|
|
2580
2607
|
});
|
|
2581
2608
|
|
|
2582
|
-
// TODO: adicionar testes
|
|
2609
|
+
// TODO: adicionar testes específicos para as props e interacoes do componente
|
|
2583
2610
|
});`;
|
|
2584
2611
|
}
|
|
2585
2612
|
else if (isNextRoute) {
|
|
@@ -2646,7 +2673,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
2646
2673
|
? `describe('${fileName}', () => {\n${fnTests}\n});`
|
|
2647
2674
|
: `describe('${fileName}', () => {
|
|
2648
2675
|
it('deve ser importado sem erros', () => {
|
|
2649
|
-
expect(true).toBe(true); // placeholder — adicionar testes
|
|
2676
|
+
expect(true).toBe(true); // placeholder — adicionar testes específicos
|
|
2650
2677
|
});
|
|
2651
2678
|
});`;
|
|
2652
2679
|
}
|
|
@@ -2671,7 +2698,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
2671
2698
|
try {
|
|
2672
2699
|
const absolutePath = path_1.default.isAbsolute(file_path) ? file_path : path_1.default.join(process.cwd(), file_path);
|
|
2673
2700
|
if (!(await fs_extra_1.default.pathExists(absolutePath))) {
|
|
2674
|
-
return { content: [{ type: "text", text: `[VIBE EXPLAIN] Arquivo
|
|
2701
|
+
return { content: [{ type: "text", text: `[VIBE EXPLAIN] Arquivo não encontrado: ${absolutePath}` }] };
|
|
2675
2702
|
}
|
|
2676
2703
|
const content = await fs_extra_1.default.readFile(absolutePath, 'utf8');
|
|
2677
2704
|
const fileName = path_1.default.basename(absolutePath);
|
|
@@ -2692,7 +2719,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
2692
2719
|
: isReact ? 'Componente React'
|
|
2693
2720
|
: fileName.includes('types') || fileName.endsWith('.d.ts') ? 'Arquivo de tipos TypeScript'
|
|
2694
2721
|
: fileName.includes('utils') || fileName.includes('lib') || fileName.includes('helpers') ? 'Utilitario / Biblioteca'
|
|
2695
|
-
: fileName.includes('config') ? 'Arquivo de
|
|
2722
|
+
: fileName.includes('config') ? 'Arquivo de configuração'
|
|
2696
2723
|
: 'Modulo TypeScript';
|
|
2697
2724
|
const audienceNote = audience === 'beginner'
|
|
2698
2725
|
? '\n(Explicacao voltada para iniciantes — conceitos basicos incluidos)'
|
|
@@ -2738,7 +2765,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
2738
2765
|
const quickFixes = [];
|
|
2739
2766
|
if (/auth|login|session|token|cookie/.test(desc)) {
|
|
2740
2767
|
hypotheses.push('Token JWT expirado ou invalido (alta probabilidade)');
|
|
2741
|
-
hypotheses.push('Cookie
|
|
2768
|
+
hypotheses.push('Cookie não persistido corretamente (httpOnly, secure, sameSite)');
|
|
2742
2769
|
hypotheses.push('Middleware de autenticacao bloqueando a rota');
|
|
2743
2770
|
checklist.push('Verificar expiracao do token no DevTools (Application > Local Storage)');
|
|
2744
2771
|
checklist.push('Inspecionar cookies em Application > Cookies');
|
|
@@ -2748,8 +2775,8 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
2748
2775
|
}
|
|
2749
2776
|
else if (/null|undefined|cannot read|is not a function/.test(desc)) {
|
|
2750
2777
|
hypotheses.push('Objeto acessado antes de ser inicializado (undefined)');
|
|
2751
|
-
hypotheses.push('Async operation
|
|
2752
|
-
hypotheses.push('Prop
|
|
2778
|
+
hypotheses.push('Async operation não aguardada (await faltando)');
|
|
2779
|
+
hypotheses.push('Prop não passada para o componente filho');
|
|
2753
2780
|
checklist.push('Adicionar optional chaining (?.) no acesso ao objeto suspeito');
|
|
2754
2781
|
checklist.push('Verificar se todas as chamadas async tem await');
|
|
2755
2782
|
checklist.push('Checar se o dado chega corretamente via props ou API');
|
|
@@ -2758,9 +2785,9 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
2758
2785
|
}
|
|
2759
2786
|
else if (/api|500|400|fetch|request|response/.test(desc)) {
|
|
2760
2787
|
hypotheses.push('Corpo da requisicao em formato incorreto (JSON malformado)');
|
|
2761
|
-
hypotheses.push('Endpoint
|
|
2788
|
+
hypotheses.push('Endpoint não existe ou URL incorreta');
|
|
2762
2789
|
hypotheses.push('Erro de validacao do schema no servidor');
|
|
2763
|
-
hypotheses.push('Variavel de ambiente da API
|
|
2790
|
+
hypotheses.push('Variavel de ambiente da API não configurada');
|
|
2764
2791
|
checklist.push('Verificar a URL da requisicao no Network tab do DevTools');
|
|
2765
2792
|
checklist.push('Inspecionar o body da resposta de erro no Network tab');
|
|
2766
2793
|
checklist.push('Checar se todas as env vars da API estao definidas');
|
|
@@ -2778,14 +2805,14 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
2778
2805
|
quickFixes.push('Verificar se o componente recebe as props corretas com React DevTools');
|
|
2779
2806
|
}
|
|
2780
2807
|
else {
|
|
2781
|
-
hypotheses.push('Logica de negocio incorreta na
|
|
2808
|
+
hypotheses.push('Logica de negocio incorreta na função principal');
|
|
2782
2809
|
hypotheses.push('Estado inconsistente entre cliente e servidor');
|
|
2783
2810
|
hypotheses.push('Race condition em operacoes assincronas');
|
|
2784
|
-
checklist.push('Adicionar console.log nos pontos de entrada e saida da
|
|
2811
|
+
checklist.push('Adicionar console.log nos pontos de entrada e saida da função');
|
|
2785
2812
|
checklist.push('Verificar os valores das variaveis no momento do erro');
|
|
2786
|
-
checklist.push('Testar a
|
|
2813
|
+
checklist.push('Testar a função isoladamente com um caso simples');
|
|
2787
2814
|
quickFixes.push('Adicionar try/catch e logar o erro completo (err.stack)');
|
|
2788
|
-
quickFixes.push('Verificar se o bug ocorre em desenvolvimento mas
|
|
2815
|
+
quickFixes.push('Verificar se o bug ocorre em desenvolvimento mas não em producao (ou vice-versa)');
|
|
2789
2816
|
}
|
|
2790
2817
|
const result = [
|
|
2791
2818
|
`[VIBE DEBUG FLOW]`,
|
|
@@ -2855,7 +2882,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
2855
2882
|
hasApiChanges && '- [ ] Endpoints retornam erro correto com dados invalidos',
|
|
2856
2883
|
hasAuthChanges && '- [ ] Fluxo de autenticacao funciona end-to-end',
|
|
2857
2884
|
hasDBChanges && '- [ ] Migration aplicada sem erros em ambiente de teste',
|
|
2858
|
-
hasDBChanges && '- [ ] Dados existentes
|
|
2885
|
+
hasDBChanges && '- [ ] Dados existentes não foram corrompidos',
|
|
2859
2886
|
'- [ ] Testado manualmente no ambiente local',
|
|
2860
2887
|
'- [ ] Sem erros no console do browser',
|
|
2861
2888
|
'- [ ] TypeScript: zero erros (tsc --noEmit)',
|
|
@@ -3024,15 +3051,15 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
3024
3051
|
else {
|
|
3025
3052
|
sections.push(`\nArquivo de exemplo: ${exampleFile} (${exampleKeys.length} variaveis documentadas)`);
|
|
3026
3053
|
}
|
|
3027
|
-
sections.push(`Arquivo .env: ${actualKeys.length > 0 ? `${actualKeys.length} variaveis encontradas` : '
|
|
3054
|
+
sections.push(`Arquivo .env: ${actualKeys.length > 0 ? `${actualKeys.length} variaveis encontradas` : 'não encontrado'}`);
|
|
3028
3055
|
if (missing.length > 0) {
|
|
3029
|
-
sections.push(`\n## ⚠️ VARS FALTANDO (no ${exampleFile} mas
|
|
3056
|
+
sections.push(`\n## ⚠️ VARS FALTANDO (no ${exampleFile} mas não no .env)\n${missing.map(k => ` ⚠️ ${k}`).join('\n')}`);
|
|
3030
3057
|
}
|
|
3031
3058
|
else if (exampleFile) {
|
|
3032
3059
|
sections.push('\n## Vars requeridas: OK (todas presentes no .env)');
|
|
3033
3060
|
}
|
|
3034
3061
|
if (undocumented.length > 0) {
|
|
3035
|
-
sections.push(`\n## 📝 VARS NAO DOCUMENTADAS (no .env mas
|
|
3062
|
+
sections.push(`\n## 📝 VARS NAO DOCUMENTADAS (no .env mas não no ${exampleFile})\n${undocumented.map(k => ` 📝 ${k}`).join('\n')}\n → Adicionar ao ${exampleFile} para documentar`);
|
|
3036
3063
|
}
|
|
3037
3064
|
if (usedInCode.length > 0) {
|
|
3038
3065
|
sections.push(`\n## 🔍 USADAS NO CODIGO MAS NAO DECLARADAS\n${usedInCode.map(k => ` 🔍 ${k}`).join('\n')}\n → Adicionar ao .env e ao ${exampleFile || '.env.example'}`);
|
|
@@ -3054,7 +3081,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
3054
3081
|
try {
|
|
3055
3082
|
const pkgPath = path_1.default.join(cwd, 'package.json');
|
|
3056
3083
|
if (!(await fs_extra_1.default.pathExists(pkgPath))) {
|
|
3057
|
-
return { content: [{ type: "text", text: `[VIBE DEPS] package.json
|
|
3084
|
+
return { content: [{ type: "text", text: `[VIBE DEPS] package.json não encontrado em: ${cwd}` }] };
|
|
3058
3085
|
}
|
|
3059
3086
|
const pkg = await fs_extra_1.default.readJson(pkgPath);
|
|
3060
3087
|
const deps = Object.keys(pkg.dependencies || {});
|
|
@@ -3165,7 +3192,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
3165
3192
|
['Ja tem conta? Fazer login.', 'Link action direto'],
|
|
3166
3193
|
['Email em uso.', 'Ultra minimal'],
|
|
3167
3194
|
],
|
|
3168
|
-
}[t] || [['Copy
|
|
3195
|
+
}[t] || [['Copy não disponivel para esse padrão', 'N/A']]),
|
|
3169
3196
|
},
|
|
3170
3197
|
{
|
|
3171
3198
|
pattern: /cta|botao|button|upgrade|assinar|plano|premium/,
|
|
@@ -3173,12 +3200,12 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
3173
3200
|
friendly: [
|
|
3174
3201
|
['Comecar agora — e gratis!', 'Remove barreira com enfase no gratis'],
|
|
3175
3202
|
['Quero experimentar', 'Baixo compromisso, alta conversao'],
|
|
3176
|
-
['Dar o proximo passo', 'Progressivo,
|
|
3203
|
+
['Dar o proximo passo', 'Progressivo, não agressivo'],
|
|
3177
3204
|
],
|
|
3178
3205
|
professional: [
|
|
3179
3206
|
['Iniciar periodo de avaliacao', 'Claro e corporativo'],
|
|
3180
3207
|
['Fazer upgrade agora', 'Acao direta'],
|
|
3181
|
-
['Contratar plano Premium', '
|
|
3208
|
+
['Contratar plano Premium', 'Específico e formal'],
|
|
3182
3209
|
],
|
|
3183
3210
|
bold: [
|
|
3184
3211
|
['Quero isso agora!', 'Urgencia e desejo'],
|
|
@@ -3190,7 +3217,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
3190
3217
|
['Upgrade', 'Uma palavra'],
|
|
3191
3218
|
['Assinar', 'Objetivo'],
|
|
3192
3219
|
],
|
|
3193
|
-
}[t] || [['Copy
|
|
3220
|
+
}[t] || [['Copy não disponivel', 'N/A']]),
|
|
3194
3221
|
},
|
|
3195
3222
|
{
|
|
3196
3223
|
pattern: /empty state|lista vazia|sem item|nenhum resultado/,
|
|
@@ -3215,7 +3242,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
3215
3242
|
['Vazio', 'Minimalista'],
|
|
3216
3243
|
['Nada ainda', 'Simples e honesto'],
|
|
3217
3244
|
],
|
|
3218
|
-
}[t] || [['Copy
|
|
3245
|
+
}[t] || [['Copy não disponivel', 'N/A']]),
|
|
3219
3246
|
},
|
|
3220
3247
|
];
|
|
3221
3248
|
// Find matching pattern
|
|
@@ -3257,7 +3284,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
3257
3284
|
try {
|
|
3258
3285
|
const absolutePath = path_1.default.isAbsolute(file_path) ? file_path : path_1.default.join(process.cwd(), file_path);
|
|
3259
3286
|
if (!(await fs_extra_1.default.pathExists(absolutePath))) {
|
|
3260
|
-
return { content: [{ type: "text", text: `[VIBE REFACTOR] Arquivo
|
|
3287
|
+
return { content: [{ type: "text", text: `[VIBE REFACTOR] Arquivo não encontrado: ${absolutePath}` }] };
|
|
3261
3288
|
}
|
|
3262
3289
|
const content = await fs_extra_1.default.readFile(absolutePath, 'utf8');
|
|
3263
3290
|
const fileName = path_1.default.basename(absolutePath);
|
|
@@ -3291,9 +3318,9 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
3291
3318
|
suggestions.push({
|
|
3292
3319
|
priority: '🔴 ALTA',
|
|
3293
3320
|
title: `Funcao muito longa: ${longestFn.name}()`,
|
|
3294
|
-
problem: `A
|
|
3321
|
+
problem: `A função ${longestFn.name} tem aproximadamente ${longestFn.lines} linhas. Funcoes longas sao dificeis de testar e entender.`,
|
|
3295
3322
|
solution: `Quebre ${longestFn.name} em funcoes menores com nomes descritivos:\n Antes: ${longestFn.name}() com toda a logica\n Depois: validateInput() + processData() + formatOutput()`,
|
|
3296
|
-
impact: 'Cada
|
|
3323
|
+
impact: 'Cada função menor pode ser testada isoladamente'
|
|
3297
3324
|
});
|
|
3298
3325
|
}
|
|
3299
3326
|
// 3. Repeated patterns (duplicate code blocks)
|
|
@@ -3310,8 +3337,8 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
3310
3337
|
suggestions.push({
|
|
3311
3338
|
priority: '🟠 MEDIA',
|
|
3312
3339
|
title: 'Codigo duplicado detectado',
|
|
3313
|
-
problem: `${repeated.length}
|
|
3314
|
-
solution: 'Extraia o
|
|
3340
|
+
problem: `${repeated.length} padrão(oes) de codigo aparece(m) 3+ vezes:\n${repeated.map(([line, count]) => ` "${line.slice(0, 60)}..." (${count}x)`).join('\n')}`,
|
|
3341
|
+
solution: 'Extraia o padrão repetido para uma função ou constante compartilhada:\n Antes: mesmo bloco em 3 lugares\n Depois: const handleX = (params) => { /* logica unica */ }',
|
|
3315
3342
|
impact: 'DRY — qualquer bug so precisa ser corrigido em um lugar'
|
|
3316
3343
|
});
|
|
3317
3344
|
}
|
|
@@ -3321,9 +3348,9 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
3321
3348
|
suggestions.push({
|
|
3322
3349
|
priority: '🟡 BAIXA',
|
|
3323
3350
|
title: `${anyUsages} uso(s) de 'any' detectado(s)`,
|
|
3324
|
-
problem: `O tipo 'any' desliga a
|
|
3325
|
-
solution: 'Substitua any por tipos
|
|
3326
|
-
impact: 'Erros de tipo sao capturados em compile-time,
|
|
3351
|
+
problem: `O tipo 'any' desliga a verificação de tipos do TypeScript, criando buracos de seguranca silenciosos.`,
|
|
3352
|
+
solution: 'Substitua any por tipos específicos ou genericos:\n Antes: function process(data: any)\n Depois: function process<T extends Record<string, unknown>>(data: T)',
|
|
3353
|
+
impact: 'Erros de tipo sao capturados em compile-time, não em runtime'
|
|
3327
3354
|
});
|
|
3328
3355
|
}
|
|
3329
3356
|
// 5. Naming issues (single letter vars outside loops)
|
|
@@ -3343,7 +3370,7 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
3343
3370
|
return {
|
|
3344
3371
|
content: [{
|
|
3345
3372
|
type: "text",
|
|
3346
|
-
text: `[VIBE REFACTOR] ${fileName} (${lineCount} linhas)\n\n✅ Nenhum problema
|
|
3373
|
+
text: `[VIBE REFACTOR] ${fileName} (${lineCount} linhas)\n\n✅ Nenhum problema crítico de refatoracao detectado automaticamente.\n\nVerifique manualmente:\n- Acoplamento entre modulos\n- Abstrações faltando no domínio de negocio\n- Funcoes com muitos parametros (>4)`
|
|
3347
3374
|
}]
|
|
3348
3375
|
};
|
|
3349
3376
|
}
|
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",
|