Skip to content

Commit 23cf976

Browse files
committed
feat: Implement HygieneSkill and MemorySkill in core, integrate them into the server, and introduce a gita CLI command for project setup.
1 parent daac85f commit 23cf976

11 files changed

Lines changed: 191 additions & 10 deletions

File tree

apps/cli/src/commands/gita.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,35 @@ export async function gitaCommand() {
5252
`;
5353
await fs.writeFile(path.join(callmDir, 'ZEN.md'), zenContent);
5454

55-
// 3. Criar FRONTEND.md e BACKEND.md
56-
await fs.writeFile(path.join(callmDir, 'frontend', 'FRONTEND.md'), '# Configurações de Frontend\nIdentifique frameworks (React, Vue, Svelte) e suas versões aqui.');
57-
await fs.writeFile(path.join(callmDir, 'backend', 'BACKEND.md'), '# Configurações de Backend\nIdentifique stacks (Node, Python, PHP) e bancos de dados aqui.');
58-
await fs.writeFile(path.join(callmDir, 'hygiene', 'HYGIENE.md'), '# Higiene do Projeto\nLogs de limpeza e organização do projeto.');
55+
// 3. Detecção de Stack Inteligente
56+
const pkgPath = path.join(rootDir, 'package.json');
57+
let frontendInfo = 'Frontend: Desconhecido';
58+
let backendInfo = 'Backend: Desconhecido';
59+
60+
if (await fs.pathExists(pkgPath)) {
61+
const pkg = await fs.readJson(pkgPath);
62+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
63+
64+
if (deps['react']) frontendInfo = 'Frontend: React detected';
65+
if (deps['vue']) frontendInfo = 'Frontend: Vue detected';
66+
if (deps['next']) frontendInfo = 'Frontend: Next.js detected';
67+
68+
if (deps['express'] || deps['@nestjs/core']) backendInfo = 'Backend: Node.js detected';
69+
}
70+
71+
if (await fs.pathExists(path.join(rootDir, 'requirements.txt'))) {
72+
backendInfo = 'Backend: Python detected';
73+
}
74+
75+
// 4. Salvar arquivos de configuração
76+
await fs.writeFile(path.join(callmDir, 'frontend', 'FRONTEND.md'), `# FRONTEND.md - Configurações de UI/UX\n\n${frontendInfo}\n\n## Diretrizes Elite\n- Performance: Throttling e Debouncing.\n- UX: Micro-interações Framer Motion.\n- SEO: Semantic HTML único H1.`);
77+
78+
await fs.writeFile(path.join(callmDir, 'backend', 'BACKEND.md'), `# BACKEND.md - Configurações de API/DB\n\n${backendInfo}\n\n## Diretrizes Elite\n- Arquitetura: Hexagonal (Domain Driven).\n- Segurança: OWASP Top 10 Sanitization.\n- DB: Caching estratégico (Redis/SQLite).`);
79+
80+
await fs.writeFile(path.join(callmDir, 'hygiene', 'HYGIENE.md'), '# HYGIENE.md - Higiene do Projeto\n\nLogs de limpeza e organização do projeto para desembaraço de cadeias de contexto.');
5981

6082
console.log(chalk.green('✔ Diretório .callm inicializado com sucesso!'));
83+
console.log(chalk.blue(`Stack identificada: ${frontendInfo} | ${backendInfo}`));
6184
console.log(chalk.gray(`Localizado em: ${callmDir}`));
6285

6386
} catch (error) {

apps/server/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"main": "index.js",
66
"scripts": {
77
"test": "echo \"Error: no test specified\" && exit 1",
8-
"dev": "ts-node src/index.ts"
8+
"dev": "ts-node src/index.ts",
9+
"lint": "tsc --noEmit"
910
},
1011
"keywords": [],
1112
"author": "",

apps/server/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import express from 'express';
22
import cors from 'cors';
33
import bodyParser from 'body-parser';
44
import path from 'path';
5-
import { LlmService, SessionService, SkillLoader, FileSystemSkill, BrowserSkill, AgentService } from '@callm/core';
5+
import { LlmService, SessionService, SkillLoader, FileSystemSkill, BrowserSkill, MemorySkill, HygieneSkill, AgentService } from '@callm/core';
66

77
const app = express();
88
const port = process.env.PORT || 3001;
@@ -20,6 +20,8 @@ const agentService = new AgentService();
2020
// Registrar Skills Padrão
2121
skillLoader.registerSkill(new FileSystemSkill());
2222
skillLoader.registerSkill(new BrowserSkill());
23+
skillLoader.registerSkill(new MemorySkill());
24+
skillLoader.registerSkill(new HygieneSkill());
2325

2426
// Carregar Skills Dinâmicas
2527
const externalSkillsPath = path.resolve(process.cwd(), '.callm', 'skills');

apps/web/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
"scripts": {
77
"dev": "vite",
88
"build": "tsc -b && vite build",
9-
"lint": "eslint .",
10-
"preview": "vite preview"
9+
"preview": "vite preview",
10+
"lint": "tsc --noEmit"
1111
},
1212
"dependencies": {
1313
"clsx": "^2.1.1",

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
"scripts": {
1111
"cli:dev": "npm run dev -w apps/cli",
1212
"cli:build": "npm run build -w apps/cli",
13+
"build": "npm run build --workspaces --if-present",
14+
"lint": "npm run lint --workspaces --if-present",
1315
"test": "vitest run",
1416
"test:watch": "vitest"
1517
},

packages/browser/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"description": "caLLM Browser Automation with Playwright",
55
"main": "dist/index.js",
66
"scripts": {
7-
"build": "tsc"
7+
"build": "tsc",
8+
"lint": "tsc --noEmit"
89
},
910
"dependencies": {
1011
"playwright": "^1.40.0"

packages/core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",
77
"scripts": {
8-
"build": "tsc"
8+
"build": "tsc",
9+
"lint": "tsc --noEmit"
910
},
1011
"devDependencies": {
1112
"typescript": "^5.0.0"

packages/core/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ export * from './services/LlmService';
22
export * from './services/SessionService';
33
export * from './services/SkillLoader';
44
export * from './services/AgentService';
5+
export * from './services/MemoryService';
56
export * from './interfaces/ISkill';
67
export * from './skills/FileSystemSkill';
78
export * from './skills/BrowserSkill';
9+
export * from './skills/MemorySkill';
10+
export * from './skills/HygieneSkill';
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import fs from 'fs-extra';
2+
import path from 'path';
3+
4+
export interface Neuron {
5+
id: string;
6+
content: string;
7+
tags: string[];
8+
createdAt: string;
9+
}
10+
11+
export class MemoryService {
12+
private neuronDir: string;
13+
14+
constructor(basePath: string = '.callm/neurons') {
15+
this.neuronDir = path.resolve(process.cwd(), basePath);
16+
}
17+
18+
async init() {
19+
await fs.ensureDir(this.neuronDir);
20+
}
21+
22+
async learn(title: string, content: string, tags: string[] = []): Promise<void> {
23+
await this.init();
24+
const id = title.toLowerCase().replace(/[^a-z0-9]/g, '_');
25+
const neuron: Neuron = {
26+
id,
27+
content,
28+
tags,
29+
createdAt: new Date().toISOString()
30+
};
31+
32+
const filePath = path.join(this.neuronDir, `${id}.json`);
33+
await fs.writeJson(filePath, neuron, { spaces: 2 });
34+
console.log(`[MemoryService] Novo neurônio formado: ${id}`);
35+
}
36+
37+
async recall(query: string): Promise<Neuron[]> {
38+
await this.init();
39+
const files = await fs.readdir(this.neuronDir);
40+
const neurons: Neuron[] = [];
41+
42+
for (const file of files) {
43+
if (file.endsWith('.json')) {
44+
const neuron: Neuron = await fs.readJson(path.join(this.neuronDir, file));
45+
if (neuron.content.toLowerCase().includes(query.toLowerCase()) ||
46+
neuron.tags.some(t => t.toLowerCase().includes(query.toLowerCase()))) {
47+
neurons.push(neuron);
48+
}
49+
}
50+
}
51+
52+
return neurons;
53+
}
54+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { IBaseSkill, SkillDefinition } from '../interfaces/ISkill';
2+
import fs from 'fs-extra';
3+
import path from 'path';
4+
5+
export class HygieneSkill implements IBaseSkill {
6+
getDefinition(): SkillDefinition {
7+
return {
8+
name: 'project_hygiene',
9+
description: 'Realiza limpezas e organizações no projeto para manter o ambiente saudável e as cadeias de contexto limpas.',
10+
parameters: [
11+
{
12+
name: 'action',
13+
type: 'string',
14+
description: 'Ação: "clean_logs" ou "organize_temp"',
15+
required: true
16+
}
17+
]
18+
};
19+
}
20+
21+
async execute(params: { action: string }): Promise<any> {
22+
const { action } = params;
23+
const projectRoot = process.cwd();
24+
25+
try {
26+
if (action === 'clean_logs') {
27+
// Exemplo: Limpar arquivos de log fantasmas
28+
const logsDir = path.join(projectRoot, 'logs');
29+
if (await fs.pathExists(logsDir)) {
30+
await fs.emptyDir(logsDir);
31+
return { success: true, message: 'Diretório de logs limpo com sucesso.' };
32+
}
33+
return { success: true, message: 'Nenhum diretório de log encontrado para limpar.' };
34+
}
35+
return { success: false, error: 'Ação de higiene desconhecida.' };
36+
} catch (error: any) {
37+
return { success: false, error: error.message };
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)