primocode 4.3.1 → 7.0.0
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 +91 -107
- package/bin/primocode.js +272 -477
- package/lib/act.js +43 -471
- package/lib/api.js +2 -2
- package/lib/config.js +4 -32
- package/lib/desktop.js +142 -0
- package/lib/tools.js +68 -467
- package/lib/ui.js +135 -78
- package/package.json +7 -18
- package/lib/mascot.js +0 -121
- package/lib/memory.js +0 -179
- package/lib/mouse.js +0 -144
- package/lib/platform.js +0 -245
package/lib/ui.js
CHANGED
|
@@ -1,104 +1,161 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* UI components — estilo Claude Code
|
|
3
|
+
*/
|
|
2
4
|
|
|
3
5
|
'use strict';
|
|
4
6
|
|
|
5
|
-
const
|
|
6
|
-
const path = require('path');
|
|
7
|
+
const readline = require('readline');
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
-
|
|
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
|
-
|
|
13
|
-
brand:
|
|
14
|
-
brandDim:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
dim:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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.
|
|
67
|
-
this.
|
|
62
|
+
this.frames = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
|
|
63
|
+
this.i = 0; this.timer = null;
|
|
68
64
|
}
|
|
69
|
-
start(
|
|
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.
|
|
79
|
-
this.i = (this.i
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
this.
|
|
91
|
-
|
|
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
|
|
97
|
-
return
|
|
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
|
-
|
|
101
|
-
|
|
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,
|
|
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
|
-
"description": "PrimoCode
|
|
3
|
+
"version": "7.0.0",
|
|
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
|
-
"
|
|
23
|
-
"android"
|
|
19
|
+
"desktop-automation"
|
|
24
20
|
],
|
|
25
|
-
"author": "ConectaPrimoAI
|
|
21
|
+
"author": "ConectaPrimoAI",
|
|
26
22
|
"license": "MIT",
|
|
27
|
-
"
|
|
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
|
-
};
|