@justmpm/memory 0.2.1 → 0.2.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/src/index.ts CHANGED
@@ -20,10 +20,62 @@ import {
20
20
  writeFileSync,
21
21
  existsSync,
22
22
  mkdirSync,
23
- readdirSync,
24
23
  } from "fs";
25
24
  import { join, dirname } from "path";
26
25
  import { fileURLToPath } from "url";
26
+ import { z } from "zod";
27
+
28
+ // ═══════════════════════════════════════════════════════════════════════════
29
+ // SCHEMAS DE VALIDAÇÃO (Zod)
30
+ // ═══════════════════════════════════════════════════════════════════════════
31
+
32
+ /**
33
+ * Schema para tool memory_read
34
+ */
35
+ const MemoryReadSchema = z.object({
36
+ agent: z.string().optional().describe(
37
+ 'Seu nome de agent (ex: "sentinel", "fix-worker"). Opcional - usa "unknown" se não informado.'
38
+ ),
39
+ });
40
+
41
+ /**
42
+ * Schema para tool memory_append
43
+ */
44
+ const MemoryAppendSchema = z.object({
45
+ agent: z.string().optional().describe(
46
+ 'Seu nome de agent (ex: "sentinel", "fix-worker"). Opcional - usa "unknown" se não informado.'
47
+ ),
48
+ entry: z.string().min(1).describe(
49
+ 'Texto da informação a salvar. Ex: "Padrão: sempre use try/catch em funções async" ou "Bug: erro quando usuário clica 2x". Será adicionado com timestamp automaticamente.'
50
+ ),
51
+ });
52
+
53
+ /**
54
+ * Schema para tool memory_search
55
+ */
56
+ const MemorySearchSchema = z.object({
57
+ agent: z.string().optional().describe(
58
+ 'Seu nome de agent (ex: "sentinel", "fix-worker"). Opcional - usa "unknown" se não informado.'
59
+ ),
60
+ query: z.string().min(1).describe(
61
+ 'Palavra-chave para buscar na sua memória. Ex: "Zod", "bug", "Firebase". Retorna até 20 resultados com número da linha.'
62
+ ),
63
+ });
64
+
65
+ /**
66
+ * Schema para tool memory_write
67
+ */
68
+ const MemoryWriteSchema = z.object({
69
+ agent: z.string().optional().describe(
70
+ 'Seu nome de agent (ex: "sentinel", "fix-worker"). Opcional - usa "unknown" se não informado.'
71
+ ),
72
+ content: z.string().min(1).describe(
73
+ 'Conteúdo completo em markdown para substituir a memória existente. Use para reorganizar, limpar ou reconstruir memória do zero.'
74
+ ),
75
+ backup: z.boolean().optional().default(false).describe(
76
+ 'Se true, cria um backup do conteúdo atual antes de sobrescrever. O backup é salvo como MEMORY.md.backup no mesmo diretório.'
77
+ ),
78
+ });
27
79
 
28
80
  // ═══════════════════════════════════════════════════════════════════════════
29
81
  // VERSÃO DO PROJETO
@@ -109,23 +161,6 @@ function limitMemoryLines(content: string, maxLines = 200): string {
109
161
  ${lines.slice(-keepCount).join("\n")}`;
110
162
  }
111
163
 
112
- /**
113
- * Lista todos os agents que têm memória
114
- */
115
- function listAgentsWithMemory(): string[] {
116
- const memoryRoot = join(process.cwd(), ".claude", "agent-memory");
117
- if (!existsSync(memoryRoot)) return [];
118
-
119
- try {
120
- return readdirSync(memoryRoot).filter((name) => {
121
- const memoryPath = join(memoryRoot, name, "MEMORY.md");
122
- return existsSync(memoryPath);
123
- });
124
- } catch {
125
- return [];
126
- }
127
- }
128
-
129
164
  // ═══════════════════════════════════════════════════════════════════════════
130
165
  // HANDLERS DAS OPERAÇÕES
131
166
  // ═══════════════════════════════════════════════════════════════════════════
@@ -264,35 +299,6 @@ ${results}${more}`;
264
299
  }
265
300
  }
266
301
 
267
- /**
268
- * Handler: list - Lista todos os agents com memória
269
- */
270
- async function handleList(): Promise<string> {
271
- const agents = listAgentsWithMemory();
272
-
273
- if (agents.length === 0) {
274
- return `📝 Nenhum agent com memória neste projeto.
275
-
276
- Diretório: .claude/agent-memory/`;
277
- }
278
-
279
- const agentList = agents
280
- .map((name) => {
281
- const path = join(".claude", "agent-memory", name, "MEMORY.md");
282
- let lines = 0;
283
- try {
284
- lines = readFileSync(join(process.cwd(), path), "utf-8").split("\n").length;
285
- } catch {}
286
- return ` • ${name} (${lines} linhas)`;
287
- })
288
- .join("\n");
289
-
290
- return `📝 Agents com memória neste projeto (${agents.length}):
291
-
292
- ${agentList}
293
-
294
- Use o comando "read" de cada agent para ver o conteúdo.`;
295
- }
296
302
 
297
303
  // ═══════════════════════════════════════════════════════════════════════════
298
304
  // SERVIDOR MCP
@@ -317,273 +323,161 @@ const server = new Server(
317
323
  server.setRequestHandler(ListToolsRequestSchema, async () => {
318
324
  return {
319
325
  tools: [
326
+ // ────────────────────────────────────────────────────────────────────────
327
+ // TOOL: memory_read
328
+ // ────────────────────────────────────────────────────────────────────────
320
329
  {
321
- name: "memory",
322
- description: `
323
- Sua memória pessoal - salve e recupere seus aprendizados entre sessões.
330
+ name: "memory_read",
331
+ description: `Lê todo o conteúdo da sua memória persistente. Use sempre ao iniciar uma sessão para recuperar contexto anterior.
324
332
 
325
- **PARA QUE:** Guarde padrões de código, bugs encontrados, decisões tomadas e preferências do usuário. Tudo persiste entre sessões.
333
+ RETORNA: Conteúdo completo do MEMORY.md localizado em .claude/agent-memory/<agent-name>/MEMORY.md.
326
334
 
327
- **ONDE:** Seu arquivo fica em .claude/agent-memory/<seu-nome>/MEMORY.md
328
-
329
- **IMPORTANTE:** Máximo 200 linhas. Quando passar, as mais antigas são removidas (mantém últimas 160).
330
-
331
- ═══════════════════════════════════════════════════════════════
332
- COMANDOS DISPONÍVEIS:
333
- ═══════════════════════════════════════════════════════════════
334
-
335
- 1. **read** - Carrega sua memória
336
- Uso: Ao iniciar uma sessão para recuperar seu contexto anterior
337
- - agent: (opcional) Seu nome de agent. Se não fornecido, usa "unknown"
338
- - Retorna: Conteúdo completo do seu MEMORY.md com contagem de linhas
339
- - Se não existir: Mensagem de memória vazia com sugestão de usar 'append'
340
-
341
- 2. **append** - Adiciona nova informação à sua memória
342
- Uso: Quando aprender algo novo importante (padrão, bug, decisão)
343
- - entry: (OBRIGATÓRIO) Texto da informação a salvar
344
- - Formato automático: Adiciona timestamp "## [YYYY-MM-DD HH:MM:SS]"
345
- - Retorna: Confirmação com timestamp e preview da entrada
346
-
347
- 3. **search** - Procura informação específica na sua memória
348
- Uso: Quando precisar encontrar algo sem ler tudo
349
- - query: (OBRIGATÓRIO) Termo ou palavra-chave a buscar
350
- - Busca: Case-insensitive, máximo 20 resultados
351
- - Retorna: Ocorrências encontradas com número da linha
352
-
353
- 4. **write** - Reescreve sua memória reorganizando tudo
354
- Uso: Quando a memória ficar grande (perto de 200 linhas) ou precisar reestruturar
355
- - content: (OBRIGATÓRIO) Novo conteúdo completo em markdown
356
- - backup: (opcional) Se true, cria backup antes de sobrescrever
357
- - Retorna: Confirmação com contagem de linhas e alerta se atingir limite
358
-
359
- ═══════════════════════════════════════════════════════════════
360
- WORKFLOW RECOMENDADO:
361
- ═══════════════════════════════════════════════════════════════
362
-
363
- 1. **INICIAR SESSÃO:** { "command": "read" }
364
- → Carrega sua memória anterior para ter contexto
365
-
366
- 2. **DURANTE O TRABALHO:** { "command": "append", "entry": "Padrão: ..." }
367
- → Sempre que aprender algo importante, salve imediatamente
368
-
369
- 3. **PRECISAR DE ALGO:** { "command": "search", "query": "Zod" }
370
- → Procure na sua memória sem precisar ler tudo
371
-
372
- 4. **MEMÓRIA GRANDE:** { "command": "write", "content": "...", "backup": true }
373
- → Reestruture e use backup para segurança
374
-
375
- ═══════════════════════════════════════════════════════════════
376
- EXEMPLOS PRÁTICOS:
377
- ═══════════════════════════════════════════════════════════════
378
-
379
- Iniciar sessão (sempre comece assim):
380
- { "command": "read" }
381
-
382
- Salvar um padrão de código:
383
- { "command": "append", "entry": "Padrão: Sempre use Zod para validar inputs do usuário" }
384
-
385
- Salvar uma decisão importante:
386
- { "command": "append", "entry": "Decisão: Usar Zustand em vez de Redux (mais leve)" }
387
-
388
- Salvar um bug recorrente:
389
- { "command": "append", "entry": "Bug: Erro acontece quando array está vazio no submit" }
390
-
391
- Buscar informação sobre Zod:
392
- { "command": "search", "query": "Zod" }
393
-
394
- Reorganizar memória grande com backup:
395
- { "command": "write", "content": "# Memória Reorganizada\\n\\n## Padrões\\n- Padrão 1\\n- Padrão 2", "backup": true }
396
-
397
- ═══════════════════════════════════════════════════════════════
398
- O QUE SALVAR (exemplos):
399
- ═══════════════════════════════════════════════════════════════
400
-
401
- ✅ **Salve:** Padrões de código, bugs recorrentes, decisões arquiteturais, preferências do usuário, configurações importantes.
402
-
403
- ✅ **Organize com markdown:**
404
- ## Padrões - Convenções de código
405
- ## Bugs - Problemas e workarounds
406
- ## Decisões - Escolhas arquiteturais
407
-
408
- ❌ **Não salve:** Coisas triviais, informações temporárias, logs de conversa, coisas óbvias.
409
-
410
- ═══════════════════════════════════════════════════════════════
411
- DICAS IMPORTANTES:
412
- ═══════════════════════════════════════════════════════════════
413
-
414
- • Seu nome é normalizado: "Meu Agent" → "meu-agent"
415
- • Use search antes de salvar para evitar duplicatas
416
- • Memória é por projeto (pasta .claude/agent-memory/)
417
- • Ao atingir 200 linhas, as mais antigas são removidas automaticamente
418
- `.trim(),
419
- inputSchema: {
420
- type: "object",
421
- properties: {
422
- command: {
423
- type: "string",
424
- enum: ["read", "append", "search", "write"],
425
- description: "Comando a executar: 'read' carrega sua memória, 'append' adiciona nova informação, 'search' procura algo específico, 'write' reescreve reorganizando tudo.",
426
- },
427
- agent: {
428
- type: "string",
429
- description: 'Seu nome de agent (ex: "sentinel", "fix-worker"). Opcional - usa "unknown" se não informado. Será normalizado para minúsculas.',
430
- },
431
- content: {
432
- type: "string",
433
- description: 'Conteúdo completo em markdown para substituir a memória existente (OBRIGATÓRIO para "write"). Use para reorganizar, limpar ou reconstruir memória do zero.',
434
- },
435
- backup: {
436
- type: "boolean",
437
- description: 'Se true, cria um backup do conteúdo atual antes de sobrescrever. O backup é salvo como MEMORY.md.backup no mesmo diretório. Padrão: false.',
438
- },
439
- entry: {
440
- type: "string",
441
- description: 'Texto da informação a salvar (OBRIGATÓRIO para append). Ex: "Padrão: sempre use try/catch em funções async" ou "Bug: erro quando usuário clica 2x". Será adicionado com timestamp automaticamente.',
442
- },
443
- query: {
444
- type: "string",
445
- description: 'Palavra-chave para buscar na sua memória (OBRIGATÓRIO para search). Ex: "Zod", "bug", "Firebase". Retorna até 20 resultados com número da linha.',
446
- },
447
- },
448
- required: ["command"],
449
- },
335
+ Se a memória não existir, retorna mensagem de memória vazia com sugestão para usar memory_append.
336
+
337
+ Limite: Máximo 200 linhas. Ao atingir, mantém apenas as últimas 160 entradas.
338
+
339
+ Parâmetro "agent": Seu nome de agent (opcional, padrão: "unknown").`,
340
+ inputSchema: MemoryReadSchema,
450
341
  },
451
- ],
452
- };
453
- });
454
342
 
455
- /**
456
- * Handler: CallTool - Executa uma tool
457
- */
458
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
459
- const { name, arguments: args } = request.params;
343
+ // ────────────────────────────────────────────────────────────────────────
344
+ // TOOL: memory_append
345
+ // ────────────────────────────────────────────────────────────────────────
346
+ {
347
+ name: "memory_append",
348
+ description: `Adiciona uma entrada no final da sua memória com timestamp automático "## [YYYY-MM-DD HH:MM:SS]".
460
349
 
461
- if (name !== "memory") {
462
- throw new Error(`Tool desconhecida: ${name}`);
463
- }
350
+ USE QUANDO APRENDER algo importante: padrões de código, bugs recorrentes, decisões arquiteturais, preferências do usuário, configurações importantes.
464
351
 
465
- const command = args?.command as string;
466
- const agentName = (args?.agent as string) || "unknown";
352
+ EXEMPLOS DE ENTRADAS VÁLIDAS:
353
+ - "Padrão: Este projeto usa App Router com estrutura /features/[domain]/"
354
+ - "Decisão: Escolhemos Zustand em vez de Redux porque é mais leve"
355
+ - "Bug: Firestore update falha silenciosamente quando array está vazio"
467
356
 
468
- // Valida comando
469
- const validCommands = ["read", "append", "search", "write"];
470
- if (!validCommands.includes(command)) {
471
- return {
472
- content: [
473
- {
474
- type: "text",
475
- text: `❌ ERRO: Comando desconhecido "${command}"
357
+ NÃO salve: Coisas triviais, informações temporárias, logs de conversa.
476
358
 
477
- COMANDOS DISPONÍVEIS:
359
+ Parâmetro "agent": Seu nome de agent (opcional, padrão: "unknown").
360
+ Parâmetro "entry": OBRIGATÓRIO - Texto da informação a salvar.`,
361
+ inputSchema: MemoryAppendSchema,
362
+ },
478
363
 
479
- 1. read - Carrega sua memória (use ao iniciar)
480
- 2. append - Adiciona nova informação (mais usado)
481
- 3. search - Procura algo na sua memória
482
- 4. write - Reescreve reorganizando tudo (cuidado!)
364
+ // ────────────────────────────────────────────────────────────────────────
365
+ // TOOL: memory_search
366
+ // ────────────────────────────────────────────────────────────────────────
367
+ {
368
+ name: "memory_search",
369
+ description: `Busca um termo na sua memória sem precisar ler todo o conteúdo.
483
370
 
484
- PARA MAIS DETALHES, CONSULTE A DESCRIÇÃO DA TOOL "memory"
485
- `,
486
- },
487
- ],
488
- };
489
- }
371
+ RETORNA: Até 20 ocorrências encontradas com número da linha. Busca case-insensitive.
490
372
 
491
- // Executa comando específico
492
- let result: string;
373
+ EXEMPLOS DE BUSCA ÚTEIS:
374
+ - "Zod" → encontrar tudo sobre validação
375
+ - "bug" → encontrar bugs documentados
376
+ - "Firebase" → encontrar configurações
377
+ - "TypeScript" → encontrar padrões de tipagem
493
378
 
494
- switch (command) {
495
- case "read": {
496
- result = await handleRead(agentName);
497
- break;
498
- }
379
+ Se não encontrar, retorna mensagem de que nenhuma ocorrência foi encontrada.
499
380
 
500
- case "write": {
501
- const content = args?.content as string;
502
- if (!content) {
503
- result = `❌ ERRO: O parâmetro "content" é OBRIGATÓRIO para o comando "write".
381
+ Parâmetro "agent": Seu nome de agent (opcional, padrão: "unknown").
382
+ Parâmetro "query": OBRIGATÓRIO - Termo ou palavra-chave a buscar.`,
383
+ inputSchema: MemorySearchSchema,
384
+ },
504
385
 
505
- EXEMPLO DE USO CORRETO:
506
- {
507
- "command": "write",
508
- "agent": "sentinel",
509
- "content": "# Memória do Sentinel\\n\\n## Padrões\\n- Sempre use Zod para validação\\n\\n## Bugs\\n- Bug XYZ ocorre quando..."
510
- }
386
+ // ────────────────────────────────────────────────────────────────────────
387
+ // TOOL: memory_write
388
+ // ────────────────────────────────────────────────────────────────────────
389
+ {
390
+ name: "memory_write",
391
+ description: `SUBSTITUI TODO o conteúdo da sua memória. APAGA tudo que estava antes - use com cautela.
511
392
 
512
- DICA: Use "backup": true para criar uma cópia de segurança antes de sobrescrever:
513
- {
514
- "command": "write",
515
- "agent": "sentinel",
516
- "content": "# Nova memória...",
517
- "backup": true
518
- }
393
+ USE QUANDO: Memória está grande (~150+ linhas) e precisa reorganizar, ou quer remover entradas obsoletas.
519
394
 
520
- Use "write" para reorganizar memória grande ou reconstruir do zero.
521
- Para adicionar uma entrada preservando o histórico, use "append".
522
- `;
523
- break;
524
- }
525
- const backup = (args?.backup as boolean) || false;
526
- result = await handleWrite(agentName, content, backup);
527
- break;
528
- }
395
+ DICAS:
396
+ - Use "backup: true" para criar MEMORY.md.backup antes de sobrescrever
397
+ - Leia primeiro com memory_read para não perder conteúdo importante
398
+ - Organize em seções: ## Padrões, ## Bugs, ## Decisões, ## Configurações
529
399
 
530
- case "append": {
531
- const entry = args?.entry as string;
532
- if (!entry) {
533
- result = `❌ ERRO: O parâmetro "entry" é OBRIGATÓRIO para o comando "append".
400
+ EXEMPLO DE ESTRUTURA:
401
+ # Memória do Agent
534
402
 
535
- EXEMPLO DE USO CORRETO:
536
- {
537
- "command": "append",
538
- "agent": "sentinel",
539
- "entry": "Padrão descoberto: Sempre use Zod para validar inputs do usuário em todos os componentes de formulário"
540
- }
403
+ ## Padrões de Código
404
+ - Sempre use TypeScript estrito
541
405
 
542
- DICA: Use "append" para salvar aprendizados incrementais. O timestamp é adicionado automaticamente no formato:
543
- ## [2026-02-09 12:34:56]
544
- Sua entrada aqui
406
+ ## Bugs Conhecidos
407
+ - Bug XYZ: ocorre quando array vazio
408
+ Workaround: verificar length antes de usar
545
409
 
546
- Para salvar informações mais longas ou estruturadas, considere usar "write".
547
- `;
548
- break;
410
+ ## Decisões
411
+ - Zustand em vez de Redux (mais leve)
412
+
413
+ Parâmetro "agent": Seu nome de agent (opcional, padrão: "unknown").
414
+ Parâmetro "content": OBRIGATÓRIO - Novo conteúdo completo em markdown.
415
+ Parâmetro "backup": Opcional (padrão: false) - Se true, cria backup antes de sobrescrever.`,
416
+ inputSchema: MemoryWriteSchema,
417
+ },
418
+ ],
419
+ };
420
+ });
421
+
422
+ /**
423
+ * Handler: CallTool - Executa uma tool
424
+ */
425
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
426
+ const { name, arguments: args } = request.params;
427
+ const agentName = (args?.agent as string) || "unknown";
428
+
429
+ try {
430
+ switch (name) {
431
+ case "memory_read": {
432
+ const parsed = MemoryReadSchema.safeParse(args);
433
+ if (!parsed.success) {
434
+ throw new Error(`Parâmetros inválidos: ${parsed.error.issues.map((e: { message: string }) => e.message).join(", ")}`);
435
+ }
436
+ const result = await handleRead(agentName);
437
+ return { content: [{ type: "text", text: result }] };
549
438
  }
550
- result = await handleAppend(agentName, entry);
551
- break;
552
- }
553
439
 
554
- case "search": {
555
- const query = args?.query as string;
556
- if (!query) {
557
- result = `❌ ERRO: O parâmetro "query" é OBRIGATÓRIO para o comando "search".
440
+ case "memory_append": {
441
+ const parsed = MemoryAppendSchema.safeParse(args);
442
+ if (!parsed.success) {
443
+ throw new Error(`Parâmetros inválidos: ${parsed.error.issues.map((e: { message: string }) => e.message).join(", ")}`);
444
+ }
445
+ const result = await handleAppend(agentName, parsed.data.entry);
446
+ return { content: [{ type: "text", text: result }] };
447
+ }
558
448
 
559
- EXEMPLO DE USO CORRETO:
560
- {
561
- "command": "search",
562
- "agent": "sentinel",
563
- "query": "Zod"
564
- }
449
+ case "memory_search": {
450
+ const parsed = MemorySearchSchema.safeParse(args);
451
+ if (!parsed.success) {
452
+ throw new Error(`Parâmetros inválidos: ${parsed.error.issues.map((e: { message: string }) => e.message).join(", ")}`);
453
+ }
454
+ const result = await handleSearch(agentName, parsed.data.query);
455
+ return { content: [{ type: "text", text: result }] };
456
+ }
565
457
 
566
- DICA: A busca é case-insensitive e retorna até 20 ocorrências com o número da linha.
567
- Exemplos de busca úteis: "padrão", "bug", "TypeScript", "Firebase", "erro"
568
- `;
569
- break;
458
+ case "memory_write": {
459
+ const parsed = MemoryWriteSchema.safeParse(args);
460
+ if (!parsed.success) {
461
+ throw new Error(`Parâmetros inválidos: ${parsed.error.issues.map((e: { message: string }) => e.message).join(", ")}`);
462
+ }
463
+ const result = await handleWrite(agentName, parsed.data.content, parsed.data.backup);
464
+ return { content: [{ type: "text", text: result }] };
570
465
  }
571
- result = await handleSearch(agentName, query);
572
- break;
573
- }
574
466
 
575
- default:
576
- result = `❌ Comando desconhecido: ${command}`;
467
+ default:
468
+ throw new Error(`Tool desconhecida: ${name}`);
469
+ }
470
+ } catch (error) {
471
+ const errorMessage = error instanceof Error ? error.message : String(error);
472
+ return {
473
+ content: [
474
+ {
475
+ type: "text",
476
+ text: `❌ Erro ao executar tool "${name}": ${errorMessage}`,
477
+ },
478
+ ],
479
+ };
577
480
  }
578
-
579
- return {
580
- content: [
581
- {
582
- type: "text",
583
- text: result,
584
- },
585
- ],
586
- };
587
481
  });
588
482
 
589
483
  // ═══════════════════════════════════════════════════════════════════════════
@@ -612,12 +506,11 @@ USO:
612
506
  memory --version Mostra a versão do pacote
613
507
  memory --help Mostra esta mensagem de ajuda
614
508
 
615
- COMANDOS MCP:
616
- - read Lê a memória de um agent
617
- - write Substitui toda a memória
618
- - append Adiciona uma entrada ao final
619
- - search Busca texto na memória
620
- - list Lista todos os agents com memória
509
+ TOOLS MCP:
510
+ - memory_read Lê a memória de um agent
511
+ - memory_write Substitui toda a memória
512
+ - memory_append Adiciona uma entrada ao final
513
+ - memory_search Busca texto na memória
621
514
 
622
515
  DOCUMENTAÇÃO:
623
516
  Para mais detalhes, consulte: