@memoring/cli 0.12.0 → 0.13.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 +3 -1
- package/dist/commands/inbox.js +3 -1
- package/dist/commands/stage.js +66 -3
- package/dist/main.js +1 -1
- package/package.json +1 -1
- package/skills/memoring-cli/SKILL.md +9 -6
package/README.md
CHANGED
|
@@ -87,15 +87,17 @@ Cada item do lote (1 a 50, all-or-nothing):
|
|
|
87
87
|
"front": "Quais são os quatro objetivos fundamentais da República (art. 3º da CF/88)?",
|
|
88
88
|
"back": "I — construir uma sociedade livre, justa e solidária; II — garantir o desenvolvimento nacional; III — erradicar a pobreza e reduzir as desigualdades; IV — promover o bem de todos.",
|
|
89
89
|
"subjectName": "Direito Constitucional",
|
|
90
|
+
"conceptKey": "const-cf88-art-3",
|
|
90
91
|
"tags": ["cf88", "art-3"],
|
|
91
92
|
"source": "Apostila constitucional.pdf"
|
|
92
93
|
}
|
|
93
94
|
```
|
|
94
95
|
|
|
95
96
|
- `type`: `NOTE` (nota, exige `content` em markdown; `NORMAL` legado é aceito e mapeado para `NOTE`), `FLASHCARD` ou `ONE_SHOT` (exigem `front` + `back`), `QUESTION` (exige `question`: `kind` `MULTIPLA_ESCOLHA`/`CERTO_ERRADO`, `statementHtml`, `correctAlternative` 1-based — 1=Certo/2=Errado —, `alternatives` 2–5 só em múltipla escolha, `commentHtml`).
|
|
97
|
+
- `conceptKey`: conceito do item em slug minúsculo (letras, números e hífens, até 80 chars). Opcional por compatibilidade, mas **fortemente recomendado em FLASHCARD/ONE_SHOT/QUESTION** — cartões sobre o MESMO conceito compartilham a MESMA chave e conceitos DIFERENTES usam chaves diferentes (nunca reuse a chave). Ex.: um flashcard e uma questão sobre o art. 4º do CTN usam ambos `"trib-art-4"`. No app, o whiteboard do Rascunho agrupa por conceito: ângulos do mesmo conceito contam como uma frente só nas rodadas guiadas; sem chave, cada cartão é uma frente individual. No `inbox update <id>`, o `conceptKey` atual é preservado quando o campo não é informado.
|
|
96
98
|
- `deckName`: **obrigatório** (ou `--deck` no create) — cartão não fica sem baralho; o nome de um baralho existente (`memoring deck list`) é reutilizado, nome novo vira baralho criado no envio.
|
|
97
99
|
- `source`: origem do material — default `"Importação assistida (CLI)"`.
|
|
98
|
-
- Limites por tipo
|
|
100
|
+
- Limites por tipo são **tetos, nunca cotas**: `memoring stage caps` mostra/grava sua preferência (`livre`/`-1` = sem teto preferencial — o padrão, não um incentivo a inflar o lote; `0` = não gerar o tipo) — o `stage schema` a expõe ao agente (`userCaps`) e o `create` a impõe; flags `--max-*` no `create` vencem por lote. O 50 é apenas o limite técnico por requisição, não a meta: sem quantidade explícita, o agente deve selecionar apenas os pontos realmente cobráveis e manter uma carga de revisão sustentável.
|
|
99
101
|
- Sem sessão, o `stage schema --json` sai com `authenticated: false`, `userCaps`/`qualityRules`/`customInstructions` nulos e um `warning` — agentes devem logar (`auth login`) e reler o schema ANTES de gerar.
|
|
100
102
|
|
|
101
103
|
## Para agentes de IA
|
package/dist/commands/inbox.js
CHANGED
|
@@ -60,6 +60,7 @@ async function updateInboxItem(parsed, id) {
|
|
|
60
60
|
subtopicName: overrides.subtopicName ?? stringFlag(flags, 'subtopic') ?? current.subtopicName ?? null,
|
|
61
61
|
tags: overrides.tags ?? (tagsFlag !== undefined ? parseTagsFlag(tagsFlag) : current.tags ?? []),
|
|
62
62
|
source: overrides.source ?? stringFlag(flags, 'source') ?? current.source ?? null,
|
|
63
|
+
conceptKey: overrides.conceptKey ?? stringFlag(flags, 'concept') ?? current.conceptKey ?? null,
|
|
63
64
|
deckId: deckId ?? overrides.deckId ?? current.deckId ?? null,
|
|
64
65
|
question: overrides.question ?? current.question ?? null,
|
|
65
66
|
};
|
|
@@ -166,7 +167,8 @@ export async function inboxCommand(parsed) {
|
|
|
166
167
|
' discard-all descarta todos os itens pendentes\n' +
|
|
167
168
|
' update <id> edita um item PENDENTE (merge: --file parcial.json e/ou flags\n' +
|
|
168
169
|
' --title --content --front --back --subject --subtopic\n' +
|
|
169
|
-
' --tags a,b,c --source --type)\n'
|
|
170
|
+
' --tags a,b,c --source --concept trib-art-4 --type)\n' +
|
|
171
|
+
' (--concept preserva/define o conceptKey; sem ele, o valor atual é mantido)\n');
|
|
170
172
|
if (sub) {
|
|
171
173
|
process.exitCode = 1;
|
|
172
174
|
}
|
package/dist/commands/stage.js
CHANGED
|
@@ -10,6 +10,13 @@ const LEGACY_TYPE_ALIASES = { NORMAL: 'NOTE' };
|
|
|
10
10
|
const MAX_ITEMS = 50;
|
|
11
11
|
const DEFAULT_SOURCE = 'Importação assistida (CLI)';
|
|
12
12
|
const QUESTION_KINDS = ['MULTIPLA_ESCOLHA', 'CERTO_ERRADO'];
|
|
13
|
+
/**
|
|
14
|
+
* Slug minúsculo (letras, números, hífens, até 80 chars) que identifica o conceito
|
|
15
|
+
* do item. Segmentos alfanuméricos separados por hífen único — sem hífen no
|
|
16
|
+
* início/fim nem duplo, espelhando a validação do backend (`assignConceptKey`).
|
|
17
|
+
*/
|
|
18
|
+
const CONCEPT_KEY_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
19
|
+
const CONCEPT_KEY_MAX = 80;
|
|
13
20
|
const UNLIMITED = -1;
|
|
14
21
|
/**
|
|
15
22
|
* GET /notes/import/agent-guidance, fail-open como o fetchStoredCaps: sem sessão/rede
|
|
@@ -147,6 +154,7 @@ const SCHEMA_JSON = {
|
|
|
147
154
|
subjectName: 'string? — matéria (ex.: "Direito Constitucional")',
|
|
148
155
|
subtopicName: 'string? — subtópico',
|
|
149
156
|
tags: 'string[]?',
|
|
157
|
+
conceptKey: `string? — conceito do item em slug minúsculo (letras/números/hífens, até ${CONCEPT_KEY_MAX} chars). Opcional por compatibilidade, mas FORTEMENTE RECOMENDADO em FLASHCARD/ONE_SHOT/QUESTION: cartões sobre o MESMO conceito compartilham a chave (ex.: "trib-art-4" num flashcard E numa questão do art. 4º do CTN — o whiteboard do app agrupa por conceito) e conceitos DIFERENTES usam chaves diferentes — nunca reuse a chave para outro conceito`,
|
|
150
158
|
deckName: 'string (OBRIGATÓRIO — cartão não fica sem baralho: consulte `memoring deck list`, reutilize um existente ou crie com `memoring deck create`; nome inexistente é criado no envio como PENDING — oculto no app até o 1º item aceite, que ativa o baralho e os ancestrais pendentes. A flag --deck do create vale como padrão do lote)',
|
|
151
159
|
question: 'objeto? — snapshot da questão (obrigatório quando type=QUESTION; contrato em questionContract)',
|
|
152
160
|
source: `string? — origem do material (default: "${DEFAULT_SOURCE}")`,
|
|
@@ -161,6 +169,7 @@ const SCHEMA_JSON = {
|
|
|
161
169
|
subjectName: 'Direito Constitucional',
|
|
162
170
|
deckName: 'Direito Constitucional',
|
|
163
171
|
tags: ['cf88', 'art-3'],
|
|
172
|
+
conceptKey: 'const-cf88-art-3',
|
|
164
173
|
source: 'Apostila constitucional.pdf',
|
|
165
174
|
},
|
|
166
175
|
{
|
|
@@ -169,6 +178,7 @@ const SCHEMA_JSON = {
|
|
|
169
178
|
subjectName: 'Direito Tributário',
|
|
170
179
|
deckName: 'Direito Tributário',
|
|
171
180
|
tags: ['ctn', 'art-3'],
|
|
181
|
+
conceptKey: 'trib-ctn-art-3',
|
|
172
182
|
source: 'aula-00.pdf',
|
|
173
183
|
question: {
|
|
174
184
|
kind: 'CERTO_ERRADO',
|
|
@@ -177,8 +187,34 @@ const SCHEMA_JSON = {
|
|
|
177
187
|
commentHtml: 'É a definição do art. 3º do CTN (Lei 5.172/1966).',
|
|
178
188
|
},
|
|
179
189
|
},
|
|
190
|
+
{
|
|
191
|
+
type: 'FLASHCARD',
|
|
192
|
+
title: 'CTN art. 4º — territorialidade',
|
|
193
|
+
front: 'Qual a regra de incidência dos tributos quanto à aplicação da lei (art. 4º, caput, do CTN)?',
|
|
194
|
+
back: 'Regra geral: a lei que institui o tributo aplica-se a todos os fatos geradores ocorridos no território nacional.',
|
|
195
|
+
subjectName: 'Direito Tributário',
|
|
196
|
+
deckName: 'Direito Tributário',
|
|
197
|
+
tags: ['ctn', 'art-4'],
|
|
198
|
+
conceptKey: 'trib-art-4',
|
|
199
|
+
source: 'aula-00.pdf',
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
type: 'QUESTION',
|
|
203
|
+
title: 'CTN art. 4º — exceção à territorialidade',
|
|
204
|
+
subjectName: 'Direito Tributário',
|
|
205
|
+
deckName: 'Direito Tributário',
|
|
206
|
+
tags: ['ctn', 'art-4'],
|
|
207
|
+
conceptKey: 'trib-art-4',
|
|
208
|
+
source: 'aula-00.pdf',
|
|
209
|
+
question: {
|
|
210
|
+
kind: 'CERTO_ERRADO',
|
|
211
|
+
statementHtml: 'Nos termos do art. 4º do CTN, a lei tributária brasileira NUNCA se aplica a fatos geradores ocorridos no exterior, ainda que o fato tenha produzido efeitos no Brasil.',
|
|
212
|
+
correctAlternative: 2,
|
|
213
|
+
commentHtml: 'Parágrafo único do art. 4º: a lei brasileira pode reger fatos ocorridos no exterior quando a eles se refiram dispositivos expressos da Constituição.',
|
|
214
|
+
},
|
|
215
|
+
},
|
|
180
216
|
],
|
|
181
|
-
typeCaps: 'preferência por usuário (`memoring stage caps`, vale para app e CLI) — o
|
|
217
|
+
typeCaps: 'preferência por usuário (`memoring stage caps`, vale para app e CLI) — limites são TETOS, nunca cotas: 50 é só o limite técnico por requisição, não a meta do lote; userCaps -1 = sem teto preferencial (não é incentivo a inflar), 0 = não gerar o tipo. Sem quantidade explícita no pedido, selecione só pontos realmente cobráveis, com cobertura seletiva e carga de revisão sustentável. Flags do `stage create` (--max-notes N | --max-flashcards N | --max-one-shots N | --max-questions N, ou "livre") vencem a preferência por lote.',
|
|
182
218
|
};
|
|
183
219
|
const SCHEMA_TEXT = `Contrato dos itens da caixa de revisão (POST /api/notes/review-inbox):
|
|
184
220
|
|
|
@@ -199,6 +235,14 @@ const SCHEMA_TEXT = `Contrato dos itens da caixa de revisão (POST /api/notes/re
|
|
|
199
235
|
subjectName? string — matéria (ex.: "Direito Constitucional")
|
|
200
236
|
subtopicName? string — subtópico
|
|
201
237
|
tags? string[]
|
|
238
|
+
conceptKey? string — conceito do item em slug minúsculo (letras, números
|
|
239
|
+
e hífens, até 80 chars). Opcional por compatibilidade, mas
|
|
240
|
+
FORTEMENTE RECOMENDADO em FLASHCARD/ONE_SHOT/QUESTION: cartões
|
|
241
|
+
sobre o MESMO conceito compartilham a MESMA chave — ex.:
|
|
242
|
+
"trib-art-4" num flashcard E numa questão do art. 4º do CTN
|
|
243
|
+
(o whiteboard do app agrupa por conceito, não por cartão) —
|
|
244
|
+
e conceitos DIFERENTES usam chaves diferentes: nunca reuse
|
|
245
|
+
uma chave para outro conceito
|
|
202
246
|
source? string — origem do material (default: "${DEFAULT_SOURCE}")
|
|
203
247
|
|
|
204
248
|
Snapshot de QUESTION (questões manuais — sem identidade TEC):
|
|
@@ -294,6 +338,7 @@ async function assignDecks(items, created, deckIds) {
|
|
|
294
338
|
subtopicName: item.subtopicName ?? null,
|
|
295
339
|
tags: item.tags ?? [],
|
|
296
340
|
source: item.source ?? null,
|
|
341
|
+
conceptKey: item.conceptKey ?? null,
|
|
297
342
|
deckId,
|
|
298
343
|
question: item.question ?? null,
|
|
299
344
|
},
|
|
@@ -410,7 +455,25 @@ function validateItems(raw) {
|
|
|
410
455
|
if (it.tags !== undefined && (!Array.isArray(it.tags) || it.tags.some((t) => typeof t !== 'string'))) {
|
|
411
456
|
fail(`${where} (${it.title}): tags deve ser um array de strings.`);
|
|
412
457
|
}
|
|
413
|
-
|
|
458
|
+
let conceptKey = null;
|
|
459
|
+
if (it.conceptKey !== undefined && it.conceptKey !== null) {
|
|
460
|
+
if (typeof it.conceptKey !== 'string') {
|
|
461
|
+
fail(`${where} (${it.title}): conceptKey deve ser uma string (slug minúsculo) ou null.`);
|
|
462
|
+
}
|
|
463
|
+
const key = it.conceptKey.trim();
|
|
464
|
+
if (key !== '') {
|
|
465
|
+
if (key.length > CONCEPT_KEY_MAX || !CONCEPT_KEY_PATTERN.test(key)) {
|
|
466
|
+
fail(`${where} (${it.title}): conceptKey "${key}" inválido — use slug minúsculo com letras, ` +
|
|
467
|
+
`números e hífens, até ${CONCEPT_KEY_MAX} chars (ex.: "trib-art-4"). Mesmo conceito, mesma chave; conceito diferente, chave diferente.`);
|
|
468
|
+
}
|
|
469
|
+
conceptKey = key;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return {
|
|
473
|
+
...it,
|
|
474
|
+
conceptKey,
|
|
475
|
+
source: typeof it.source === 'string' && it.source.trim() !== '' ? it.source : DEFAULT_SOURCE,
|
|
476
|
+
};
|
|
414
477
|
});
|
|
415
478
|
}
|
|
416
479
|
/**
|
|
@@ -466,7 +529,7 @@ export async function stageCommand(parsed) {
|
|
|
466
529
|
process.stdout.write(`\nLimites por tipo configurados por você — o agente DEVE respeitá-los ao gerar o lote: ` +
|
|
467
530
|
`notas: ${capLabel(caps.maxNotes)}, flashcards: ${capLabel(caps.maxFlashcards)}, ` +
|
|
468
531
|
`1-shots: ${capLabel(caps.maxOneShots)}, questões: ${capLabel(caps.maxQuestions)}.\n` +
|
|
469
|
-
dim('("à vontade" =
|
|
532
|
+
dim('("à vontade" = sem teto preferencial, não uma cota: selecione só o que for realmente cobrável; 50 é só o limite técnico por envio, nunca a meta. Altere com `memoring stage caps`)\n'));
|
|
470
533
|
}
|
|
471
534
|
else {
|
|
472
535
|
process.stdout.write(dim('\n(limites por tipo indisponíveis — faça `memoring auth login` ou verifique a conexão)\n'));
|
package/dist/main.js
CHANGED
|
@@ -9,7 +9,7 @@ import { updateCommand } from './commands/update.js';
|
|
|
9
9
|
import { skillCommand } from './commands/skill.js';
|
|
10
10
|
import { parseArgs as parse } from './commands/shared.js';
|
|
11
11
|
import { newerVersionAvailable, scheduleUpdateCheck } from './update.js';
|
|
12
|
-
const VERSION = '0.
|
|
12
|
+
const VERSION = '0.13.0';
|
|
13
13
|
const HELP = `Memoring CLI — flashcards e notas por IA, direto do terminal.
|
|
14
14
|
|
|
15
15
|
Uso: memoring <comando> [subcomando] [flags]
|
package/package.json
CHANGED
|
@@ -27,7 +27,7 @@ O Memoring é um app de estudos com revisão espaçada (FSRS). A CLI `memoring`
|
|
|
27
27
|
| `memoring inbox list` | itens pendentes de revisão |
|
|
28
28
|
| `memoring inbox accept <id>\|accept-all` | aceita itens (entram nas listagens e na fila FSRS) — **decisão do usuário: só rode se ELE pedir explicitamente** |
|
|
29
29
|
| `memoring inbox discard <id>\|discard-all` | descarta item(ns) pendente(s) |
|
|
30
|
-
| `memoring inbox update <id> [--file parcial.json] [--title ...] [--front ...] [--back ...] [--tags a,b] ...` | edita um item AINDA PENDENTE (merge com o estado atual) |
|
|
30
|
+
| `memoring inbox update <id> [--file parcial.json] [--title ...] [--front ...] [--back ...] [--tags a,b] [--concept trib-art-4] ...` | edita um item AINDA PENDENTE (merge com o estado atual — `conceptKey` é preservado se não for informado) |
|
|
31
31
|
| `memoring skill` | imprime este guia |
|
|
32
32
|
| `memoring update` | atualiza a CLI (decisão do usuário — nunca rode por conta própria) |
|
|
33
33
|
|
|
@@ -51,16 +51,19 @@ Flags globais: `--json` (saída legível por máquina), `--no-color`, `--api-bas
|
|
|
51
51
|
- `deckName` **OBRIGATÓRIO** (ou `--deck` no create como padrão do lote): cartão não fica sem baralho. Use o nome de um baralho de `memoring deck list`; nome inexistente é criado no envio como **PENDING** (oculto no app até o 1º item aceite). Espelhe a hierarquia do material: matéria = baralho raiz; tema da aula/capítulo = sub-baralho criado ANTES com `deck create "<tema>" --parent "<matéria>" --pending` (o `stage create` só cria raiz pelo nome). O primeiro item aceito ativa o baralho e os ancestrais pendentes.
|
|
52
52
|
- `ONE_SHOT` = pergunta de resposta única (UMA palavra ou termo curto, texto puro). Ative o tipo sempre que o cobrável for recall de termo puro — nome de princípio/instituto, termo em latim, sigla, número de lei/artigo, data, prazo. Resposta cabe num termo → `ONE_SHOT`; exige frase ou explicação → `FLASHCARD`.
|
|
53
53
|
- `QUESTION` = questão objetiva para verificação: `question.kind` (`MULTIPLA_ESCOLHA` | `CERTO_ERRADO`), `question.statementHtml` (enunciado), `question.correctAlternative` (gabarito, int 1-based; em CERTO_ERRADO, 1 = Certo, 2 = Errado), `question.alternatives` (2 a 5 textos — só em MULTIPLA_ESCOLHA), `question.commentHtml` (explicação do gabarito). **Se o material contiver questões prontas (provas, simulados, listas C/E), APROVEITE-AS como QUESTION** preservando enunciado e gabarito — não as achate em flashcards.
|
|
54
|
+
- `conceptKey` = slug minúsculo (letras, números e hífens, até 80 chars) que identifica o CONCEITO do item. Opcional por compatibilidade, mas **fortemente recomendado em FLASHCARD/ONE_SHOT/QUESTION**. A regra é simples: cartões sobre o MESMO conceito compartilham a MESMA chave — ex.: um flashcard e uma questão sobre o art. 4º do CTN usam ambos `"trib-art-4"` — e conceitos DIFERENTES usam chaves diferentes, nunca reuse uma chave para outro conceito. No app, o whiteboard do Rascunho usa a chave para AGRUPAR por conceito: ângulos do mesmo conceito (flashcard, questão, 1-shot) contam como uma frente só nas rodadas guiadas — sem chave, cada cartão vira uma frente individual. Chaves curtas e estáveis, com contexto da matéria (ex.: `trib-ctn-art-3`, `const-cf88-art-3`), evitando chaves genéricas que fundam conceitos distintos.
|
|
54
55
|
- Preencha `source` com a origem real do material (nome do arquivo/pasta) — não use o default quando souber a origem.
|
|
55
56
|
- `subjectName`/`subtopicName` quando o material tiver matéria/subtópico claros; `tags` curtas e em minúsculas.
|
|
56
57
|
- NUNCA invente campos fora do contrato — o servidor rejeita o lote inteiro.
|
|
57
58
|
|
|
58
|
-
## Quantidades (limites
|
|
59
|
+
## Quantidades (limites são tetos, NUNCA cotas)
|
|
59
60
|
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
61
|
+
- **50 é apenas o limite técnico de itens por requisição, NÃO a meta do lote.** Nunca mire nesse número, não proponha faixas como “46–50” sem o usuário pedir e não acrescente itens para “fechar” um lote.
|
|
62
|
+
- **Sem quantidade explícita no pedido, não estabeleça um alvo numérico antes de analisar o material.** A quantidade é consequência da seleção: inclua conceitos centrais, exceções e confusões realmente cobráveis, além das questões prontas. Não transforme cada parágrafo, detalhe secundário, exemplo ou sigla em cartão — a cobertura é SELETIVA, não exaustiva. Em dúvida, prefira menos itens.
|
|
63
|
+
- **Considere a carga de revisão acumulada do usuário.** Milhares de páginas de estudo não podem virar dezenas de milhares de revisões mecânicas. Não use uma taxa de cartões por página (ex.: “50 a cada 10–15 páginas”): densidade e importância do assunto importam mais que o volume do PDF.
|
|
64
|
+
- `userCaps` com `-1` significa apenas **sem teto preferencial**, não “cresça à vontade”. Tetos numéricos são limites máximos, nunca cotas a preencher; `0` proíbe o tipo.
|
|
65
|
+
- Se o usuário pedir quantidades no chat (ex.: “quero 30 flashcards e 5 notas”), o pedido vence o `userCaps`: gere e valide com `--max-flashcards 30 --max-notes 5` no `stage create`.
|
|
66
|
+
- Se a seleção de pontos realmente úteis ultrapassar 50 itens, divida em vários arquivos de até 50 e envie sequencialmente — nunca infle um lote para usar a capacidade disponível.
|
|
64
67
|
|
|
65
68
|
## Qualidade dos flashcards (o padrão que o app espera)
|
|
66
69
|
|