primocode 7.0.1 → 7.0.3

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/bin/primocode.js CHANGED
@@ -69,11 +69,14 @@ async function sendPrompt(userText) {
69
69
 
70
70
  if (state.mode === 'agent') {
71
71
  await runAgent(promptWithAttach, userText);
72
- } else if (state.mode === 'act') {
72
+ } else if (state.mode === 'primocode') {
73
+ const projectDir = state.currentProject ? getProjectDir(state.currentProject) : process.cwd();
73
74
  const r = await runAct(promptWithAttach, {
74
75
  server: state.config.server,
75
76
  model: state.config.model,
76
77
  history: state.history,
78
+ projectDir: projectDir,
79
+ projectName: state.currentProject,
77
80
  });
78
81
  state.history.push({ role: 'user', content: userText });
79
82
  state.history.push({ role: 'assistant', content: r.assistantMessage.content });
@@ -354,7 +357,7 @@ async function main() {
354
357
  catch { console.log(c.err(`Não consegui ler: ${args[fileFlag+1]}`)); process.exit(1); }
355
358
  }
356
359
  if (isAgent) await runAgent(promptText, prompt);
357
- else { const r = await runAct(promptText, {server:state.config.server, model:state.config.model, history:[]}); console.log('\n'+c.white(r.assistantMessage.content)); }
360
+ else { const r = await runAct(promptText, {server:state.config.server, model:state.config.model, history:[], projectDir: process.cwd()}); console.log('\n'+c.white(r.assistantMessage.content)); }
358
361
  process.exit(0);
359
362
  }
360
363
  console.log(welcomeBox(VERSION, state.config.server));
package/lib/act.js CHANGED
@@ -1,10 +1,13 @@
1
- // Loop do agente autônomo — vai e volta entre server e execução local.
1
+ // Loop do agente autônomo — streaming via /api/act/stream
2
2
 
3
3
  'use strict';
4
4
 
5
+ const fs = require('fs');
6
+ const path = require('path');
5
7
  const { c, actionHeader, Spinner } = require('./ui');
6
8
  const tools = require('./tools');
7
9
  const api = require('./api');
10
+ const { ROOT_DIR } = require('./config');
8
11
 
9
12
  const MAX_ITERATIONS = 15;
10
13
 
@@ -28,45 +31,123 @@ function makeLogger() {
28
31
  };
29
32
  }
30
33
 
34
+ function readMemory(projectDir, projectName) {
35
+ let context = '';
36
+
37
+ // Ler you.md (memória geral)
38
+ const youMdPath = path.join(ROOT_DIR, 'you.md');
39
+ if (fs.existsSync(youMdPath)) {
40
+ try {
41
+ const youContent = fs.readFileSync(youMdPath, 'utf-8');
42
+ context += `[Memória Geral (you.md)]\n${youContent.slice(0, 2000)}\n\n`;
43
+ } catch {}
44
+ }
45
+
46
+ // Ler memory.md do projeto
47
+ if (projectDir && projectName) {
48
+ const memoryPath = path.join(projectDir, 'memory.md');
49
+ if (fs.existsSync(memoryPath)) {
50
+ try {
51
+ const memContent = fs.readFileSync(memoryPath, 'utf-8');
52
+ context += `[Memória do Projeto: ${projectName}]\n${memContent.slice(0, 2000)}\n\n`;
53
+ } catch {}
54
+ }
55
+ }
56
+
57
+ return context;
58
+ }
59
+
60
+ function updateMemory(projectDir, projectName, entry) {
61
+ if (!projectDir || !projectName) return;
62
+ const memoryPath = path.join(projectDir, 'memory.md');
63
+ try {
64
+ let content = '';
65
+ if (fs.existsSync(memoryPath)) {
66
+ content = fs.readFileSync(memoryPath, 'utf-8');
67
+ }
68
+ const timestamp = new Date().toISOString().split('T')[0];
69
+ content += `\n## ${timestamp}\n${entry}\n`;
70
+ fs.writeFileSync(memoryPath, content, 'utf-8');
71
+ } catch {}
72
+ }
73
+
31
74
  async function runAct(userPrompt, opts) {
32
75
  const { server, model, history, projectDir, projectName } = opts || {};
33
76
  const logger = makeLogger();
34
- const messages = [
35
- ...(Array.isArray(history) ? history.filter(m => m && m.role && m.content).slice(-20) : []),
36
- { role: 'user', content: userPrompt },
37
- ];
77
+
78
+ // Ler memórias
79
+ const memoryContext = readMemory(projectDir, projectName);
80
+
81
+ // Montar prompt com contexto de memória
82
+ const fullPrompt = memoryContext
83
+ ? `[Contexto de memória]\n${memoryContext}\n[Tarefa]\n${userPrompt}`
84
+ : userPrompt;
85
+
38
86
  const toolResults = [];
39
87
  let iteration = 0;
88
+ let plan = '';
89
+ let assistantContent = '';
40
90
 
41
91
  while (iteration < MAX_ITERATIONS) {
42
92
  iteration++;
43
- const sp = new Spinner('pensando').start();
44
- let response;
45
- try { response = await api.requestAct(server, messages, model); }
46
- catch (e) { sp.stop(); console.log(c.err(`\nErro no servidor: ${e.message||e}`)); throw e; }
47
- sp.stop();
48
-
49
- if (!response.tool_calls || !response.tool_calls.length) {
50
- const content = response.content || '';
51
- console.log('\n' + c.white(content));
52
- return { userMessage: { role: 'user', content: userPrompt }, assistantMessage: { role: 'assistant', content } };
53
- }
93
+ const sp = new Spinner('Primo Code pensando').start();
54
94
 
55
- for (const tc of response.tool_calls) {
56
- const name = tc.function?.name || tc.name;
57
- let args = {}; try { args = JSON.parse(tc.function?.arguments || tc.arguments || '{}'); } catch {}
58
- console.log(actionHeader(name, args));
59
- logger.event(name, null, null);
60
- let result; try { result = await tools.execute(name, args); } catch (e) { result = { ok: false, error: e.message || String(e) }; }
61
- logger.event(name + '_done', null, result);
62
- toolResults.push({ tool_call_id: tc.id, role: 'tool', name, content: JSON.stringify(result) });
63
- }
95
+ try {
96
+ await api.requestAct(server, fullPrompt, history, model, { projectDir, projectName, plan_cache: plan, tool_results: toolResults }, (evt) => {
97
+ if (evt.plan) {
98
+ plan = evt.plan;
99
+ }
100
+ if (evt.tool_call) {
101
+ sp.stop();
102
+ const tc = evt.tool_call;
103
+ console.log(actionHeader(tc.name, tc.arguments));
104
+ logger.event(tc.name, tc.arguments);
105
+
106
+ // Executar tool localmente
107
+ tools.execute(tc.name, tc.arguments).then(result => {
108
+ logger.event(tc.name + '_done', null, result);
109
+ toolResults.push({ tool_call_id: tc.id || 'tc_' + iteration, result });
110
+ });
111
+ sp.start();
112
+ }
113
+ if (evt.assistant_message) {
114
+ assistantContent = evt.assistant_message.content || '';
115
+ }
116
+ if (evt.delta) {
117
+ sp.stop();
118
+ process.stdout.write(evt.delta);
119
+ sp.start();
120
+ }
121
+ });
122
+ sp.stop();
64
123
 
65
- messages.push({ role: 'assistant', content: response.content || '', tool_calls: response.tool_calls });
66
- for (const tr of toolResults) messages.push({ role: 'tool', tool_call_id: tr.tool_call_id, name: tr.name, content: tr.content });
124
+ // Se não teve tool_calls, mostrar conteúdo
125
+ if (toolResults.length === 0 && assistantContent) {
126
+ console.log('\n' + c.white(assistantContent));
127
+ // Atualizar memória
128
+ updateMemory(projectDir, projectName, `Tarefa: ${userPrompt}\nResultado: ${assistantContent.slice(0, 500)}`);
129
+ return { userMessage: { role: 'user', content: userPrompt }, assistantMessage: { role: 'assistant', content: assistantContent } };
130
+ }
131
+
132
+ // Se teve tool_results, enviar de volta para próximo turno
133
+ if (toolResults.length > 0) {
134
+ // Limpar para próximo turno
135
+ toolResults.length = 0;
136
+ assistantContent = '';
137
+ continue;
138
+ }
139
+
140
+ break;
141
+ } catch (e) {
142
+ sp.stop();
143
+ console.log(c.err(`\nErro: ${e.message||e}`));
144
+ throw e;
145
+ }
67
146
  }
68
- console.log(c.err('\nLimite de iterações atingido.'));
69
- return { userMessage: { role: 'user', content: userPrompt }, assistantMessage: { role: 'assistant', content: 'Limite de iterações atingido.' } };
147
+
148
+ // Atualizar memória
149
+ updateMemory(projectDir, projectName, `Tarefa: ${userPrompt}\nResultado: Concluído`);
150
+ return { userMessage: { role: 'user', content: userPrompt }, assistantMessage: { role: 'assistant', content: assistantContent || 'Concluído.' } };
70
151
  }
71
152
 
72
153
  module.exports = { runAct };
package/lib/api.js CHANGED
@@ -30,8 +30,9 @@ function requestStream(serverUrl, endpoint, payload, onEvent) {
30
30
  });
31
31
  }
32
32
 
33
- function requestAct(serverUrl, messages, model, extras) {
34
- return request(serverUrl, '/api/act', { messages, model, ...(extras || {}) });
33
+ function requestAct(serverUrl, prompt, history, model, extras, onEvent) {
34
+ // Envia prompt (string) e history (array) separados para /api/act/stream
35
+ return requestStream(serverUrl, '/api/act/stream', { prompt, history, model, ...(extras || {}) }, onEvent);
35
36
  }
36
37
 
37
38
  function getHealth(serverUrl) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "primocode",
3
- "version": "7.0.1",
3
+ "version": "7.0.3",
4
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
6
  "bin": "./bin/primocode.js",