primocode 4.3.1 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/ui.js CHANGED
@@ -1,104 +1,161 @@
1
- // UI PrimoCode — minimalista e profissional
1
+ /**
2
+ * UI components — estilo Claude Code
3
+ */
2
4
 
3
5
  'use strict';
4
6
 
5
- const os = require('os');
6
- const path = require('path');
7
+ const readline = require('readline');
7
8
 
8
- const supportsColor = process.stdout.isTTY && process.env.TERM !== 'dumb' && !process.env.NO_COLOR;
9
- const wrap = (open, close) => (s) => supportsColor ? `\x1b[${open}m${s}\x1b[${close}m` : String(s);
9
+ const PALETTE = {
10
+ brand: '\x1b[38;5;183m',
11
+ brandDim: '\x1b[38;5;146m',
12
+ white: '\x1b[38;5;253m',
13
+ muted: '\x1b[38;5;245m',
14
+ dim: '\x1b[38;5;240m',
15
+ ok: '\x1b[38;5;114m',
16
+ warn: '\x1b[38;5;180m',
17
+ err: '\x1b[38;5;174m',
18
+ magenta: '\x1b[38;5;176m',
19
+ cyan: '\x1b[38;5;152m',
20
+ bold: '\x1b[1m',
21
+ reset: '\x1b[0m',
22
+ };
10
23
 
11
24
  const c = {
12
- reset: '\x1b[0m',
13
- brand: wrap('38;5;51', '39'),
14
- brandDim: wrap('38;5;44', '39'),
15
- accent: wrap('38;5;123', '39'),
16
- bold: wrap('1', '22'),
17
- dim: wrap('2', '22'),
18
- italic: wrap('3', '23'),
19
- ok: wrap('38;5;42', '39'),
20
- warn: wrap('38;5;220', '39'),
21
- err: wrap('38;5;203', '39'),
22
- muted: wrap('38;5;244', '39'),
23
- white: wrap('97', '39'),
24
- magenta: wrap('38;5;177', '39'),
25
+ ...PALETTE,
26
+ brand: (s) => `${PALETTE.brand}${s}${PALETTE.reset}`,
27
+ brandDim: (s) => `${PALETTE.brandDim}${s}${PALETTE.reset}`,
28
+ white: (s) => `${PALETTE.white}${s}${PALETTE.reset}`,
29
+ muted: (s) => `${PALETTE.muted}${s}${PALETTE.reset}`,
30
+ dim: (s) => `${PALETTE.dim}${s}${PALETTE.reset}`,
31
+ ok: (s) => `${PALETTE.ok}${s}${PALETTE.reset}`,
32
+ warn: (s) => `${PALETTE.warn}${s}${PALETTE.reset}`,
33
+ err: (s) => `${PALETTE.err}${s}${PALETTE.reset}`,
34
+ magenta: (s) => `${PALETTE.magenta}${s}${PALETTE.reset}`,
35
+ cyan: (s) => `${PALETTE.cyan}${s}${PALETTE.reset}`,
36
+ bold: (s) => `${PALETTE.bold}${s}${PALETTE.reset}`,
25
37
  };
26
38
 
27
- function stripAnsi(s) { return String(s).replace(/\x1b\[[0-9;]*m/g, ''); }
28
-
29
- function welcomeBox(config) {
30
- const width = Math.min((process.stdout.columns || 80) - 2, 76);
31
- const inner = width - 4;
32
- const home = os.homedir();
33
- let cwd = process.cwd();
34
- if (cwd.startsWith(home)) cwd = '~' + cwd.slice(home.length);
35
- if (cwd.length > inner - 6) cwd = '...' + cwd.slice(-(inner - 7));
36
-
37
- const mode = config.mode || 'Primo Code';
38
-
39
- const lines = [
40
- `${c.brand('◆')} ${c.bold('PrimoCode')}`,
41
- ``,
42
- `${c.muted('/help · /mode Primo Code | Chat')}`,
43
- `${c.muted('Diretorio')} ${c.dim(cwd)}`,
44
- `${c.muted('Modo')} ${c.dim(mode)}`,
45
- ];
46
- const pad = (s) => {
47
- const raw = stripAnsi(s);
48
- return ' ' + s + ' '.repeat(Math.max(1, inner - raw.length + 1));
49
- };
50
- const top = c.brandDim(`╭${'─'.repeat(width - 2)}╮`);
51
- const bottom = c.brandDim(`╰${'─'.repeat(width - 2)}╯`);
52
- const body = lines.map(l => c.brandDim('│') + pad(l) + c.brandDim('│')).join('\n');
53
- return `\n${top}\n${body}\n${bottom}\n`;
54
- }
55
-
56
- function hr() {
57
- const w = Math.min(process.stdout.columns || 80, 72);
58
- return c.dim(c.muted('─'.repeat(w)));
39
+ function welcomeBox(version, server) {
40
+ const line = '─'.repeat(74);
41
+ const title = '🤖 PrimoCode';
42
+ const pad = Math.max(0, 72 - title.length);
43
+ const serverLine = server ? ` ${c.dim(server)}` : '';
44
+ return [
45
+ '',
46
+ ` ${c.brand('╭' + line + '╮')}`,
47
+ ` ${c.brand('│')} ${c.brand(c.bold(title))}${' '.repeat(pad)} ${c.brand('│')}`,
48
+ ` ${c.brand('│')} ${c.brand('│')}`,
49
+ ` ${c.brand('│')} ${c.muted('v' + version)}${' '.repeat(64 - version.length)}${c.brand('│')}`,
50
+ serverLine,
51
+ ` ${c.brand('│')} ${c.brand('│')}`,
52
+ ` ${c.brand('│')} ${c.magenta('●')} ${c.muted('Primo Code')} · ${c.brand('●')} ${c.muted('Chat')} ${c.brand('│')}`,
53
+ ` ${c.brand('│')} ${c.brand('│')}`,
54
+ ` ${c.brand('╰' + line + '╯')}`,
55
+ '',
56
+ ].join('\n');
59
57
  }
60
58
 
61
59
  class Spinner {
62
- constructor(text = '') {
63
- this.frames = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
64
- this.i = 0;
60
+ constructor(text='pensando') {
65
61
  this.text = text;
66
- this.timer = null;
67
- this.active = false;
62
+ this.frames = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
63
+ this.i = 0; this.timer = null;
68
64
  }
69
- start(text) {
70
- if (text !== undefined) this.text = text;
71
- if (!process.stdout.isTTY) {
72
- if (this.text) process.stdout.write(c.muted(` ${this.text}...\n`));
73
- return this;
74
- }
75
- this.active = true;
76
- process.stdout.write('\x1b[?25l');
65
+ start() {
77
66
  this.timer = setInterval(() => {
78
- process.stdout.write(`\r ${c.brand(this.frames[this.i])} ${c.muted(this.text)} `);
79
- this.i = (this.i + 1) % this.frames.length;
67
+ process.stdout.write(`\r ${c.muted(this.frames[this.i])} ${c.muted(this.text)}`);
68
+ this.i = (this.i+1) % this.frames.length;
80
69
  }, 80);
81
70
  return this;
82
71
  }
83
- update(text) { this.text = text; return this; }
84
- stop(finalText) {
85
- if (this.timer) clearInterval(this.timer);
72
+ stop() {
73
+ if (this.timer) { clearInterval(this.timer); this.timer = null; process.stdout.write(`\r${' '.repeat(80)}\r`); }
74
+ }
75
+ }
76
+
77
+ // Barra de progresso estilo Claude Code — silenciosa, elegante
78
+ class ProgressBar {
79
+ constructor(label = 'Instalando') {
80
+ this.label = label;
81
+ this.width = 30;
82
+ this.progress = 0;
86
83
  this.timer = null;
87
- if (this.active && process.stdout.isTTY) {
88
- process.stdout.write('\r\x1b[2K\x1b[?25h');
89
- }
90
- this.active = false;
91
- if (finalText) process.stdout.write(c.muted(` ${finalText}\n`));
84
+ this.done = false;
85
+ }
86
+ start() {
87
+ process.stdout.write(` ${c.muted(this.label)} [`);
88
+ this.timer = setInterval(() => {
89
+ if (this.done) return;
90
+ this.progress = Math.min(this.progress + 1, this.width);
91
+ const filled = '█'.repeat(this.progress);
92
+ const empty = '░'.repeat(this.width - this.progress);
93
+ process.stdout.write(`\r ${c.muted(this.label)} [${c.brand(filled)}${c.dim(empty)}] ${c.muted(Math.round((this.progress/this.width)*100) + '%')}`);
94
+ }, 200);
92
95
  return this;
93
96
  }
97
+ tick() {
98
+ this.progress = Math.min(this.progress + 2, this.width);
99
+ const filled = '█'.repeat(this.progress);
100
+ const empty = '░'.repeat(this.width - this.progress);
101
+ process.stdout.write(`\r ${c.muted(this.label)} [${c.brand(filled)}${c.dim(empty)}] ${c.muted(Math.round((this.progress/this.width)*100) + '%')}`);
102
+ }
103
+ stop() {
104
+ this.done = true;
105
+ if (this.timer) { clearInterval(this.timer); this.timer = null; }
106
+ process.stdout.write(`\r ${c.muted(this.label)} [${c.ok('██████████ DONE ██████████')}] ${c.ok('100%')}\n`);
107
+ }
108
+ }
109
+
110
+ function actionHeader(name, args) {
111
+ const nice = {
112
+ read_file:'Read', write_file:'Write', list_dir:'List', run_shell:'Bash',
113
+ run_in_new_terminal:'Terminal', open_in_browser:'Open', browser_open:'Browser',
114
+ browser_navigate:'Navigate', browser_click:'Click', browser_type:'Type',
115
+ browser_key:'Key', browser_wait_for:'Wait', browser_wait:'Wait',
116
+ browser_screenshot:'Screenshot', browser_eval:'Eval', browser_scroll:'Scroll',
117
+ browser_url:'URL', finish:'Finish',
118
+ desktop_click:'Click', desktop_move:'Move', desktop_drag:'Drag',
119
+ desktop_type:'Type', desktop_key:'Key', desktop_scroll:'Scroll',
120
+ desktop_screenshot:'Screenshot', desktop_screen_size:'Screen',
121
+ };
122
+ const label = nice[name] || name;
123
+ const argStr = args ? (args.path || args.command || args.target || args.url || args.selector || JSON.stringify(args).slice(0,60)) : '';
124
+ return ` ${c.dim('⎿')} ${c.brand(label)}${argStr ? ' '+c.muted(argStr) : ''}`;
125
+ }
126
+
127
+ function toolLine(text) { return ` ${c.dim('⎿')} ${c.muted(text)}`; }
128
+
129
+ function toolResult(result) {
130
+ if (!result) return '';
131
+ if (result.ok === false) return c.err(` ✗ ${result.error || 'failed'}`);
132
+ if (result.created !== undefined) return c.ok(` ${result.created?'created':'updated'} · ${fmtBytes(result.bytes||0)}`);
133
+ return '';
134
+ }
135
+
136
+ function fmtBytes(n) {
137
+ if (n < 1024) return `${n} B`;
138
+ if (n < 1024*1024) return `${(n/1024).toFixed(1)} KB`;
139
+ return `${(n/(1024*1024)).toFixed(1)} MB`;
94
140
  }
95
141
 
96
- function actionHeader(name, detail) {
97
- return `${c.brand('▶')} ${c.bold(name)} ${c.muted(detail || '')}`;
142
+ function renderMarkdown(text) {
143
+ return text.replace(/\*\*([^*]+)\*\*/g,'$1').replace(/__([^_]+)__/g,'$1').replace(/`([^`]+)`/g,'$1').replace(/^#{1,6}\s+/gm,'');
98
144
  }
99
145
 
100
- function toolLine(name, detail) {
101
- return `${c.brand('▶')} ${c.bold(name)} ${c.muted(detail || '')}`;
146
+ // Pergunta Yes/No — aguarda resposta real do usuário
147
+ function askYesNo(question) {
148
+ return new Promise((resolve) => {
149
+ const rl = readline.createInterface({
150
+ input: process.stdin,
151
+ output: process.stdout,
152
+ });
153
+ rl.question(`${c.brand('?')} ${question} ${c.muted('(Y/n)')} `, (answer) => {
154
+ rl.close();
155
+ const a = (answer || '').trim().toLowerCase();
156
+ resolve(a === 'y' || a === 'yes' || a === '');
157
+ });
158
+ });
102
159
  }
103
160
 
104
- module.exports = { c, welcomeBox, hr, Spinner, actionHeader, toolLine, stripAnsi };
161
+ module.exports = { c, welcomeBox, Spinner, ProgressBar, actionHeader, toolLine, toolResult, fmtBytes, renderMarkdown, askYesNo };
package/package.json CHANGED
@@ -1,37 +1,26 @@
1
1
  {
2
2
  "name": "primocode",
3
- "version": "4.3.1",
4
- "description": "PrimoCode v4.1 \u2014 assistente de engenharia com IA multi-agente. Funciona em Windows, macOS, Linux e Termux (Android).",
3
+ "version": "7.0.1",
4
+ "description": "PrimoCode \u2014 agente de engenharia com IA e cursor pr\u00f3prio. Modo primocode cria arquivos, controla o desktop e navegador. Modo chat \u00e9 conversa pura.",
5
5
  "main": "bin/primocode.js",
6
- "bin": {
7
- "primocode": "bin/primocode.js"
8
- },
6
+ "bin": "./bin/primocode.js",
9
7
  "scripts": {
10
8
  "start": "node bin/primocode.js"
11
9
  },
12
10
  "engines": {
13
11
  "node": ">=18.17.0"
14
12
  },
15
- "dependencies": {},
16
13
  "keywords": [
17
14
  "cli",
18
15
  "ai",
16
+ "groq",
19
17
  "coding-assistant",
20
- "agent",
21
18
  "primocode",
22
- "termux",
23
- "android"
19
+ "desktop-automation"
24
20
  ],
25
- "author": "ConectaPrimoAI <conectaprimoai@gmail.com>",
21
+ "author": "ConectaPrimoAI",
26
22
  "license": "MIT",
27
- "repository": {
28
- "type": "git",
29
- "url": "git+https://github.com/ConectaPrimoAI/primocode.git"
30
- },
31
- "bugs": {
32
- "url": "https://github.com/ConectaPrimoAI/primocode/issues"
33
- },
34
- "homepage": "https://github.com/ConectaPrimoAI/primocode#readme",
23
+ "dependencies": {},
35
24
  "files": [
36
25
  "bin/",
37
26
  "lib/",
package/lib/mascot.js DELETED
@@ -1,121 +0,0 @@
1
- // Mascote PrimoCode — animações ASCII conforme estado da IA
2
- // Baseado no anexo do usuário: IDLE, THINKING, CODING, HAPPY
3
-
4
- 'use strict';
5
-
6
- const c = require('./ui').c;
7
-
8
- // Frames do mascote (cada estado tem múltiplos frames para animação)
9
- const FRAMES = {
10
- idle: [
11
- ` ╭─────────╮
12
- │ ◠ ◠ │
13
- │ ‿ │
14
- ╰─────────╯`,
15
- ` ╭─────────╮
16
- │ ◠ ◠ │
17
- │ ‿ │
18
- ╰─────────╯`,
19
- ],
20
- thinking: [
21
- ` ╭─────────╮
22
- │ ◠ ◠ │
23
- │ ‿ │
24
- ╰────?────╯`,
25
- ` ╭─────────╮
26
- │ ◡ ◠ │
27
- │ ‿ │
28
- ╰────?────╯`,
29
- ` ╭─────────╮
30
- │ ◠ ◡ │
31
- │ ‿ │
32
- ╰────?────╯`,
33
- ],
34
- coding: [
35
- ` ╭─────────╮
36
- │ ◕ ◕ │
37
- │ ‿ │
38
- ╰──⌨️─────╯`,
39
- ` ╭─────────╮
40
- │ ◕ ◕ │
41
- │ ‿ │
42
- ╰───⌨️────╯`,
43
- ` ╭─────────╮
44
- │ ◕ ◕ │
45
- │ ‿ │
46
- ╰────⌨️───╯`,
47
- ],
48
- happy: [
49
- ` ╭─────────╮
50
- │ ◠ ◠ │
51
- │ ▽ │
52
- ╰─────────╯`,
53
- ` ╭─────────╮
54
- │ ◡ ◡ │
55
- │ ▽ │
56
- ╰─────────╯`,
57
- ],
58
- searching: [
59
- ` ╭─────────╮
60
- │ ◠ ◠ │
61
- │ ‿ │
62
- ╰──🔍─────╯`,
63
- ` ╭─────────╮
64
- │ ◠ ◠ │
65
- │ ‿ │
66
- ╰───🔍────╯`,
67
- ],
68
- mouse: [
69
- ` ╭─────────╮
70
- │ ◠ ◠ │
71
- │ ‿ │
72
- ╰──🖱️─────╯`,
73
- ` ╭─────────╮
74
- │ ◠ ◠ │
75
- │ ‿ │
76
- ╰────🖱️───╯`,
77
- ],
78
- };
79
-
80
- let currentState = 'idle';
81
- let currentFrame = 0;
82
- let animationInterval = null;
83
-
84
- function render(state = 'idle', message = '') {
85
- const frames = FRAMES[state] || FRAMES.idle;
86
- const frame = frames[currentFrame % frames.length];
87
- const label = message ? ` ${c.muted(message)}` : '';
88
- return `\n${c.brand(frame)}${label}\n`;
89
- }
90
-
91
- function setState(state, message = '') {
92
- currentState = state;
93
- currentFrame = 0;
94
- return render(state, message);
95
- }
96
-
97
- function startAnimation(state, message = '', intervalMs = 500) {
98
- currentState = state;
99
- currentFrame = 0;
100
- if (animationInterval) clearInterval(animationInterval);
101
- animationInterval = setInterval(() => {
102
- currentFrame++;
103
- process.stdout.write('\x1b[2A\x1b[0J'); // sobe 2 linhas e limpa
104
- process.stdout.write(render(currentState, message));
105
- }, intervalMs);
106
- }
107
-
108
- function stopAnimation() {
109
- if (animationInterval) {
110
- clearInterval(animationInterval);
111
- animationInterval = null;
112
- }
113
- }
114
-
115
- function nextFrame(state, message = '') {
116
- currentState = state;
117
- currentFrame++;
118
- return render(state, message);
119
- }
120
-
121
- module.exports = { render, setState, startAnimation, stopAnimation, nextFrame, FRAMES };
package/lib/memory.js DELETED
@@ -1,179 +0,0 @@
1
- // Sistema de Memória e Projetos do PrimoCode
2
- //
3
- // Estrutura de pastas:
4
- // Windows: C:\primocode\
5
- // Linux/Mac: ~/primocode/
6
- // Termux/Android: /storage/shared/primocode/ (acessível pelo usuário)
7
- // ├── memory.md ← Memória geral do usuário
8
- // ├── projeto-1\ ← Pasta de projeto
9
- // │ ├── index.html
10
- // │ ├── style.css
11
- // │ └── primocode-projeto-1.md ← Memória do projeto
12
- // └── projeto-2\
13
- // └── ...
14
-
15
- 'use strict';
16
-
17
- const fs = require('fs');
18
- const path = require('path');
19
- const platform = require('./platform');
20
-
21
- // Diretório principal de projetos (cross-platform)
22
- const PRIMOCODE_DIR = platform.getProjectsDir();
23
- const MEMORY_FILE = path.join(PRIMOCODE_DIR, 'memory.md');
24
-
25
- function ensureDir(dir) {
26
- try { fs.mkdirSync(dir, { recursive: true }); } catch {}
27
- }
28
-
29
- // Inicializa a estrutura de pastas
30
- function init() {
31
- ensureDir(PRIMOCODE_DIR);
32
- if (!fs.existsSync(MEMORY_FILE)) {
33
- fs.writeFileSync(MEMORY_FILE, `# Memória PrimoCode
34
-
35
- ## Sobre o Usuário
36
- - Nome:
37
- - Preferências:
38
-
39
- ## Estilo de Resposta Preferido
40
- - Código sempre em arquivos, nunca no chat
41
- - Abrir automaticamente no navegador quando aplicável
42
- - Pesquisar na web para garantir qualidade
43
-
44
- ## Projetos Anteriores
45
- `, 'utf8');
46
- }
47
- }
48
-
49
- // Carrega a memória geral
50
- function loadMemory() {
51
- init();
52
- try {
53
- return fs.readFileSync(MEMORY_FILE, 'utf8');
54
- } catch {
55
- return '';
56
- }
57
- }
58
-
59
- // Salva na memória geral
60
- function saveMemory(content) {
61
- init();
62
- fs.writeFileSync(MEMORY_FILE, content, 'utf8');
63
- }
64
-
65
- // Adiciona informação à memória
66
- function appendMemory(section, info) {
67
- const mem = loadMemory();
68
- const sectionHeader = `## ${section}`;
69
- const idx = mem.indexOf(sectionHeader);
70
- if (idx >= 0) {
71
- const before = mem.slice(0, idx + sectionHeader.length);
72
- const after = mem.slice(idx + sectionHeader.length);
73
- saveMemory(`${before}\n- ${info}${after}`);
74
- } else {
75
- saveMemory(`${mem}\n## ${section}\n- ${info}\n`);
76
- }
77
- }
78
-
79
- // Lista todos os projetos existentes
80
- function listProjects() {
81
- init();
82
- try {
83
- const entries = fs.readdirSync(PRIMOCODE_DIR, { withFileTypes: true });
84
- return entries
85
- .filter(e => e.isDirectory())
86
- .map(e => e.name);
87
- } catch {
88
- return [];
89
- }
90
- }
91
-
92
- // Cria um novo projeto (pasta + arquivo de memória)
93
- function createProject(projectName) {
94
- init();
95
- const safeName = projectName.replace(/[^a-zA-Z0-9\-_]/g, '-').toLowerCase();
96
- const projectDir = path.join(PRIMOCODE_DIR, safeName);
97
- ensureDir(projectDir);
98
-
99
- const projectMemoryFile = path.join(projectDir, `primocode-${safeName}.md`);
100
- if (!fs.existsSync(projectMemoryFile)) {
101
- fs.writeFileSync(projectMemoryFile, `# PrimoCode — ${projectName}
102
-
103
- ## Descrição
104
- Projeto iniciado em ${new Date().toLocaleString('pt-BR')}
105
-
106
- ## Contexto
107
-
108
- ## Arquivos
109
-
110
- ## Decisões Técnicas
111
-
112
- ## Notas
113
- `, 'utf8');
114
- }
115
-
116
- // Atualiza memória geral
117
- appendMemory('Projetos Anteriores', `${safeName} — ${projectName} (${new Date().toLocaleDateString('pt-BR')})`);
118
-
119
- return { dir: projectDir, name: safeName, memoryFile: projectMemoryFile };
120
- }
121
-
122
- // Carrega memória de um projeto
123
- function loadProjectMemory(projectName) {
124
- const safeName = projectName.replace(/[^a-zA-Z0-9\-_]/g, '-').toLowerCase();
125
- const projectMemoryFile = path.join(PRIMOCODE_DIR, safeName, `primocode-${safeName}.md`);
126
- try {
127
- return fs.readFileSync(projectMemoryFile, 'utf8');
128
- } catch {
129
- return '';
130
- }
131
- }
132
-
133
- // Salva memória de um projeto
134
- function saveProjectMemory(projectName, content) {
135
- const safeName = projectName.replace(/[^a-zA-Z0-9\-_]/g, '-').toLowerCase();
136
- const projectDir = path.join(PRIMOCODE_DIR, safeName);
137
- ensureDir(projectDir);
138
- const projectMemoryFile = path.join(projectDir, `primocode-${safeName}.md`);
139
- fs.writeFileSync(projectMemoryFile, content, 'utf8');
140
- }
141
-
142
- // Verifica se projeto existe
143
- function projectExists(projectName) {
144
- const safeName = projectName.replace(/[^a-zA-Z0-9\-_]/g, '-').toLowerCase();
145
- return fs.existsSync(path.join(PRIMOCODE_DIR, safeName));
146
- }
147
-
148
- // Salva arquivo em um projeto
149
- function saveProjectFile(projectName, filename, content) {
150
- const safeName = projectName.replace(/[^a-zA-Z0-9\-_]/g, '-').toLowerCase();
151
- const projectDir = path.join(PRIMOCODE_DIR, safeName);
152
- ensureDir(projectDir);
153
- const filePath = path.join(projectDir, filename);
154
- fs.writeFileSync(filePath, content, 'utf8');
155
-
156
- // Atualiza memória do projeto
157
- const mem = loadProjectMemory(safeName);
158
- if (!mem.includes(filename)) {
159
- const updated = mem.replace('## Arquivos', `## Arquivos\n- ${filename}`);
160
- saveProjectMemory(safeName, updated);
161
- }
162
-
163
- return filePath;
164
- }
165
-
166
- module.exports = {
167
- PRIMOCODE_DIR,
168
- MEMORY_FILE,
169
- init,
170
- loadMemory,
171
- saveMemory,
172
- appendMemory,
173
- listProjects,
174
- createProject,
175
- loadProjectMemory,
176
- saveProjectMemory,
177
- projectExists,
178
- saveProjectFile,
179
- };