@memoring/cli 0.11.0 → 0.12.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/README.md CHANGED
@@ -56,7 +56,7 @@ Os itens enviados nascem **PENDING_REVIEW** — nada entra nas suas listagens ne
56
56
  | `memoring auth` | login / logout / status |
57
57
  | `memoring stage` | schema (o contrato + seus limites por tipo + as regras de qualidade e suas instruções custom — tudo que o agente deve seguir) / caps (mostra/grava os limites — valem para o app e para os agentes) / create (envia o lote, `--file` ou stdin, `--replace` esvazia a caixa antes; `--max-*` vence a preferência por lote) |
58
58
  | `memoring inbox` | list / accept `<id>` / accept-all / discard `<id>` / discard-all / update `<id>` (edita item PENDENTE — merge do `--file` parcial e/ou flags `--title --front --back --tags a,b --deck nome ...`) |
59
- | `memoring deck` | list (seus baralhos) / create `<nome>` (idempotente; `--parent` p/ sub-baralho) |
59
+ | `memoring deck` | list (seus baralhos, com status ACTIVE/PENDING) / create `<nome>` (idempotente; `--parent` p/ sub-baralho; `--pending` p/ estrutura de importação — o baralho só aparece no app quando o primeiro item é aceito) |
60
60
  | `memoring update` | atualiza a CLI para a última versão (via npm) |
61
61
  | `memoring skill` | imprime o guia completo da CLI para agentes de IA (o SKILL.md empacotado) |
62
62
  | `memoring version` | versão da CLI |
@@ -95,13 +95,15 @@ Cada item do lote (1 a 50, all-or-nothing):
95
95
  - `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`).
96
96
  - `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
97
  - `source`: origem do material — default `"Importação assistida (CLI)"`.
98
- - Limites por tipo: `memoring stage caps` mostra/grava sua preferência (`livre`/`-1` = à vontade — o padrão; `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.
98
+ - 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
99
  - 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
100
 
101
101
  ## Para agentes de IA
102
102
 
103
103
  Este pacote inclui uma skill em `skills/memoring-cli/SKILL.md` com o fluxo completo (auth → schema → gerar → stage → avisar o usuário). O jeito mais fácil de um agente aprender a CLI de cabo a rabo é rodar **`memoring skill`** — o comando imprime o guia completo empacotado. Ou simplesmente peça "use a cli do memoring" — os comandos são auto-descritivos (`--help` em todos).
104
104
 
105
+ > **Aceite é decisão do usuário.** O fluxo do agente termina no `stage create` + aviso para revisar em [memoring.com.br/cards/inbox](https://memoring.com.br/cards/inbox). `memoring inbox accept <id>` / `accept-all` existem para **você** rodar — um agente só os executa quando você pedir explicitamente.
106
+
105
107
  ## Dev (backend local)
106
108
 
107
109
  ```
@@ -1,7 +1,11 @@
1
1
  import { apiFetch, ApiError } from '../api.js';
2
- import { fail, green, printJson, printTable, stringFlag } from './shared.js';
2
+ import { fail, green, dim, printJson, printTable, stringFlag } from './shared.js';
3
+ /**
4
+ * Lista ACTIVE + PENDING (?includePending=true): create/update/stage reutilizam
5
+ * baralhos pendentes por nome de forma idempotente e a listagem mostra o estado.
6
+ */
3
7
  export async function listDecks() {
4
- return (await apiFetch('/notes/decks'));
8
+ return (await apiFetch('/notes/decks?includePending=true'));
5
9
  }
6
10
  /**
7
11
  * Resolve uma referência de baralho ("123" por id ou nome exato, case-insensitive)
@@ -16,10 +20,11 @@ export function resolveDeck(decks, ref) {
16
20
  const norm = ref.trim().toLowerCase();
17
21
  return decks.find((d) => d.name.trim().toLowerCase() === norm) ?? null;
18
22
  }
19
- /** Cria o baralho (parentId null = baralho raiz/matéria). */
20
- export async function createDeck(name, parentId = null) {
23
+ /** Cria o baralho (parentId null = baralho raiz/matéria). O boolean `pending` é
24
+ * sempre explícito no body — sem ele, a chamada seria ambígua quanto ao status. */
25
+ export async function createDeck(name, parentId, pending) {
21
26
  try {
22
- return (await apiFetch('/notes/decks', { method: 'POST', body: { name, parentId } }));
27
+ return (await apiFetch('/notes/decks', { method: 'POST', body: { name, parentId, pending } }));
23
28
  }
24
29
  catch (e) {
25
30
  if (e instanceof ApiError) {
@@ -157,6 +162,7 @@ export async function deckCommand(parsed) {
157
162
  printTable(decks.map((d) => ({
158
163
  id: `#${d.id}`,
159
164
  name: d.name,
165
+ status: d.status,
160
166
  parent: d.parentId !== null ? (nameById.get(d.parentId) ?? `#${d.parentId}`) : '—',
161
167
  due: d.dueCount,
162
168
  relevance: formatRelevance(d.relevance),
@@ -168,6 +174,7 @@ export async function deckCommand(parsed) {
168
174
  })), [
169
175
  { key: 'id', label: 'ID' },
170
176
  { key: 'name', label: 'NOME' },
177
+ { key: 'status', label: 'STATUS' },
171
178
  { key: 'parent', label: 'BARALHO-PAI' },
172
179
  { key: 'due', label: 'A REVISAR' },
173
180
  { key: 'relevance', label: 'RELEVÂNCIA' },
@@ -180,7 +187,7 @@ export async function deckCommand(parsed) {
180
187
  case 'create': {
181
188
  const name = rest.join(' ').trim();
182
189
  if (name === '') {
183
- fail('informe o nome: memoring deck create "Direito Tributário" [--parent <id>].');
190
+ fail('informe o nome: memoring deck create "Direito Tributário" [--parent <id>] [--pending].');
184
191
  }
185
192
  let parentId = null;
186
193
  const parentRef = stringFlag(parsed.flags, 'parent');
@@ -200,12 +207,15 @@ export async function deckCommand(parsed) {
200
207
  process.stdout.write(`Baralho "${existing.name}" já existe (#${existing.id}).\n`);
201
208
  return;
202
209
  }
203
- const deck = await createDeck(name, parentId);
210
+ const deck = await createDeck(name, parentId, parsed.flags.pending === true);
204
211
  if (parsed.flags.json) {
205
212
  printJson({ deck, alreadyExisted: false });
206
213
  return;
207
214
  }
208
- process.stdout.write(green(`Baralho "${deck.name}" criado (#${deck.id}).\n`));
215
+ process.stdout.write(green(`Baralho "${deck.name}" criado (#${deck.id}).\n`) +
216
+ (deck.status === 'PENDING'
217
+ ? dim(' PENDING — oculto no app até o primeiro item aceite (o aceite ativa o baralho e os ancestrais pendentes).\n')
218
+ : ''));
209
219
  return;
210
220
  }
211
221
  case 'update': {
@@ -237,9 +247,11 @@ export async function deckCommand(parsed) {
237
247
  }
238
248
  default:
239
249
  process.stdout.write('Uso: memoring deck <list|create|update> [id|nome] [flags]\n' +
240
- ' list lista seus baralhos (id, nome, pai, a revisar, relevância, meta, recorde)\n' +
250
+ ' list lista seus baralhos (id, nome, status, pai, a revisar, relevância, meta, recorde)\n' +
241
251
  ' create <nome> cria um baralho (idempotente: nome existente só retorna o atual)\n' +
242
252
  ' --parent <id|nome> cria como sub-baralho\n' +
253
+ ' --pending cria PENDING (estrutura de importação: oculto no app\n' +
254
+ ' até o primeiro item aceite, que ativa o baralho e os ancestrais)\n' +
243
255
  ' update <id|nome> define metadados do baralho\n' +
244
256
  ' --relevance 0-5 (1 casa) | --goal "txt" (<=160)\n' +
245
257
  ' --tone gray|orange|green|blue|purple\n' +
@@ -147,7 +147,7 @@ const SCHEMA_JSON = {
147
147
  subjectName: 'string? — matéria (ex.: "Direito Constitucional")',
148
148
  subtopicName: 'string? — subtópico',
149
149
  tags: 'string[]?',
150
- 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. A flag --deck do create vale como padrão do lote)',
150
+ 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
151
  question: 'objeto? — snapshot da questão (obrigatório quando type=QUESTION; contrato em questionContract)',
152
152
  source: `string? — origem do material (default: "${DEFAULT_SOURCE}")`,
153
153
  },
@@ -178,7 +178,7 @@ const SCHEMA_JSON = {
178
178
  },
179
179
  },
180
180
  ],
181
- typeCaps: 'preferência por usuário (`memoring stage caps`, vale para app e CLI) — o `stage schema` mostra os limites ao agente (userCaps: -1 = à vontade, 0 = não gerar o tipo); 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.',
181
+ 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
182
  };
183
183
  const SCHEMA_TEXT = `Contrato dos itens da caixa de revisão (POST /api/notes/review-inbox):
184
184
 
@@ -189,11 +189,13 @@ const SCHEMA_TEXT = `Contrato dos itens da caixa de revisão (POST /api/notes/re
189
189
  question objeto — snapshot da questão (obrigatório quando type=QUESTION)
190
190
  deckName string — baralho de destino (OBRIGATÓRIO — cartão não fica sem baralho:
191
191
  veja \`memoring deck list\`, reutilize ou crie
192
- com \`memoring deck create\`; --deck = padrão do lote.
192
+ com \`memoring deck create --pending\`; --deck = padrão do lote.
193
193
  Espelhe a hierarquia do material: matéria = baralho
194
194
  raiz; tema da aula/capítulo = sub-baralho criado
195
- ANTES com \`deck create "<tema>" --parent "<matéria>"\`
196
- — o create-por-nome só cria baralho raiz)
195
+ ANTES com \`deck create "<tema>" --parent "<matéria>" --pending\`
196
+ — o create-por-nome só cria baralho raiz. Baralhos
197
+ PENDING ficam ocultos no app até o 1º item aceite,
198
+ que ativa o baralho e os ancestrais pendentes)
197
199
  subjectName? string — matéria (ex.: "Direito Constitucional")
198
200
  subtopicName? string — subtópico
199
201
  tags? string[]
@@ -217,7 +219,8 @@ QUESTION treina verificação objetiva (aproveite questões prontas do material)
217
219
  Limites por tipo: a preferência do usuário (abaixo, quando logado) vale por padrão;
218
220
  --max-notes N | --max-flashcards N | --max-one-shots N | --max-questions N no create vencem por lote ("livre" = à vontade).`;
219
221
  const NO_DECK_HINT = 'cartão não fica sem baralho: informe deckName no item ou --deck "<nome>" no lote. ' +
220
- 'Veja `memoring deck list` e reutilize um existente, ou crie com `memoring deck create "<nome>"`.';
222
+ 'Veja `memoring deck list` e reutilize um existente, ou crie com `memoring deck create "<nome>" --pending` ' +
223
+ '(estrutura de importação fica oculta até o primeiro aceite).';
221
224
  /** Exige baralho em TODO o lote: deckName no item ou a flag --deck como padrão. */
222
225
  function enforceDeck(items, flags) {
223
226
  const batchDeck = stringFlag(flags, 'deck');
@@ -228,8 +231,10 @@ function enforceDeck(items, flags) {
228
231
  }
229
232
  /**
230
233
  * Resolve os baralhos do lote ANTES do POST: referências existentes são reutilizadas
231
- * (id numérico ou nome exato case-insensitive) e nomes novos viram baralhos criados
232
- * na hora — o agente nunca precisa sair do fluxo para criar estrutura.
234
+ * (id numérico ou nome exato case-insensitive, ativos E pendentes) e nomes novos viram
235
+ * baralhos PENDING criados na hora — invisíveis no app até o primeiro item aceite, que
236
+ * ativa o baralho e os ancestrais pendentes. O agente nunca precisa sair do fluxo
237
+ * para criar estrutura.
233
238
  */
234
239
  async function resolveBatchDecks(items, flags) {
235
240
  const batchDeck = stringFlag(flags, 'deck')?.trim();
@@ -256,7 +261,7 @@ async function resolveBatchDecks(items, flags) {
256
261
  if (Number.isInteger(Number(ref)) && Number(ref) > 0) {
257
262
  fail(`baralho #${ref} não existe — ${NO_DECK_HINT}`);
258
263
  }
259
- const deck = await createDeck(ref);
264
+ const deck = await createDeck(ref, null, true);
260
265
  decks.push(deck);
261
266
  idByRef.set(ref, deck.id);
262
267
  createdNames.push(ref);
@@ -461,7 +466,7 @@ export async function stageCommand(parsed) {
461
466
  process.stdout.write(`\nLimites por tipo configurados por você — o agente DEVE respeitá-los ao gerar o lote: ` +
462
467
  `notas: ${capLabel(caps.maxNotes)}, flashcards: ${capLabel(caps.maxFlashcards)}, ` +
463
468
  `1-shots: ${capLabel(caps.maxOneShots)}, questões: ${capLabel(caps.maxQuestions)}.\n` +
464
- dim('("à vontade" = crie quantos o material justificar; altere com `memoring stage caps`)\n'));
469
+ 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'));
465
470
  }
466
471
  else {
467
472
  process.stdout.write(dim('\n(limites por tipo indisponíveis — faça `memoring auth login` ou verifique a conexão)\n'));
@@ -540,7 +545,7 @@ export async function stageCommand(parsed) {
540
545
  }
541
546
  }
542
547
  if (batchDecks.createdNames.length > 0 && !parsed.flags.json) {
543
- process.stdout.write(dim(`Baralho(s) criado(s): ${batchDecks.createdNames.join(', ')}.\n`));
548
+ process.stdout.write(dim(`Baralho(s) criado(s) como PENDING (ocultos até o 1º aceite): ${batchDecks.createdNames.join(', ')}.\n`));
544
549
  }
545
550
  let created;
546
551
  try {
@@ -601,7 +606,8 @@ export async function stageCommand(parsed) {
601
606
  ' create envia o lote para a caixa de revisão (arquivo ou stdin)\n' +
602
607
  ' tipos: NOTE | FLASHCARD | ONE_SHOT | QUESTION\n' +
603
608
  ' baralho OBRIGATÓRIO: deckName por item ou --deck "<nome|id>" no lote\n' +
604
- ' (nomes inexistentes são criados; veja `memoring deck list`)\n' +
609
+ ' (nomes inexistentes são criados como PENDING — ocultos no app até o\n' +
610
+ ' primeiro aceite; veja `memoring deck list`)\n' +
605
611
  ' --replace esvazia a caixa antes de enviar\n' +
606
612
  ' --max-notes N | --max-flashcards N | --max-one-shots N | --max-questions N\n' +
607
613
  ' vencem a preferência gravada por lote (validação local)\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.11.0';
12
+ const VERSION = '0.12.1';
13
13
  const HELP = `Memoring CLI — flashcards e notas por IA, direto do terminal.
14
14
 
15
15
  Uso: memoring <comando> [subcomando] [flags]
@@ -28,8 +28,8 @@ Comandos:
28
28
  inbox discard-all descarta todos os itens pendentes
29
29
  inbox update <id> edita um item pendente (--file parcial e/ou flags,
30
30
  inclui --deck <id|nome>)
31
- deck list seus baralhos (id, nome, pai, a revisar, relevância, meta, recorde)
32
- deck create <nome> cria um baralho (--parent <id|nome> p/ sub-baralho)
31
+ deck list seus baralhos (id, nome, status, pai, a revisar, relevância, meta, recorde)
32
+ deck create <nome> cria um baralho (--parent <id|nome> p/ sub-baralho; --pending p/ importação)
33
33
  deck update <id|nome> metadados do baralho (--relevance/--goal/--tone;
34
34
  --clear-* limpa um campo)
35
35
  update atualiza a CLI para a versão mais recente (via npm)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memoring/cli",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "description": "CLI do Memoring — envie flashcards/notas gerados por IA para a sua caixa de revisão, direto do terminal.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,11 +21,11 @@ O Memoring é um app de estudos com revisão espaçada (FSRS). A CLI `memoring`
21
21
  | `memoring stage schema [--json]` | O CONTRATO dos itens + limites por tipo + regras de qualidade — leia SEMPRE, logado, antes de gerar |
22
22
  | `memoring stage caps [--max-* N\|livre]` | limites por tipo do usuário (preferência DELE — nunca escreva sem ele pedir) |
23
23
  | `memoring stage create --file f [--replace] [--max-* N] [--deck nome]` | envia o lote (1 a 50 itens) para a caixa de revisão — **baralho OBRIGATÓRIO** (deckName por item ou `--deck` no lote; nomes novos viram baralhos); `--json` devolve `created` + `ids` |
24
- | `memoring deck list` | seus baralhos (id, nome, pai, a revisar, relevância, meta, recorde) — consulte ANTES de gerar |
25
- | `memoring deck create <nome> [--parent id]` | cria baralho (idempotente) |
24
+ | `memoring deck list` | seus baralhos (id, nome, status, pai, a revisar, relevância, meta, recorde) — consulte ANTES de gerar |
25
+ | `memoring deck create <nome> [--parent id] [--pending]` | cria baralho (idempotente); `--pending` deixa oculto até o primeiro item aceite |
26
26
  | `memoring deck update <id\|nome> [--relevance <0-5>] [--goal "<txt>"] [--tone <gray\|orange\|green\|blue\|purple>] [--clear-*]` | define metadados do baralho (relevância, meta, tom de capa; `--clear-*` limpa o campo) |
27
27
  | `memoring inbox list` | itens pendentes de revisão |
28
- | `memoring inbox accept <id>\|accept-all` | aceita itens (entram nas listagens e na fila FSRS) |
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
30
  | `memoring inbox update <id> [--file parcial.json] [--title ...] [--front ...] [--back ...] [--tags a,b] ...` | edita um item AINDA PENDENTE (merge com o estado atual) |
31
31
  | `memoring skill` | imprime este guia |
@@ -38,29 +38,31 @@ Flags globais: `--json` (saída legível por máquina), `--no-color`, `--api-bas
38
38
  1. **Entenda o pedido** — qual pasta/arquivo/material vira flashcards/notas e se o usuário pediu quantidades.
39
39
  2. **Confira a sessão**: `memoring auth status --json`. Sem `authenticated: true`, PARE e peça o login (ver Pré-requisitos).
40
40
  3. **Leia o contrato, os limites e as regras**: `memoring stage schema --json` (logado). Além do contrato, a saída traz: `userCaps` — os limites por tipo DO USUÁRIO (`-1` = à vontade, `0` = não gerar o tipo, `N` = teto), respeite-os SEMPRE; `qualityRules` — as regras de qualidade INTEGRAIS (as mesmas que o app envia à LLM), aplique-as em TODOS os itens; `customInstructions` — instruções adicionais do usuário (quando não-nulas), também sempre aplicadas. Tudo isso vale mesmo sem pedido explícito no chat.
41
- 4. **Defina o baralho** — cartão NÃO fica sem baralho. Rode `memoring deck list`, escolha o baralho adequado ao material e reutilize o nome; se não existir, crie com `memoring deck create "<nome>"` (ou deixe o próprio `stage create` criar pelo nome). **Espelhe a hierarquia do material:** a matéria vira baralho raiz; o tema da aula/capítulo vira sub-baralho criado ANTES com `memoring deck create "<tema>" --parent "<matéria>"` — o create-por-nome do `stage create` só cria baralho raiz. Todo item do lote deve levar `deckName` — ou use `--deck "<nome>"` no create como padrão do lote. Lote sem baralho é REJEITADO pela validação local.
41
+ 4. **Defina o baralho** — cartão NÃO fica sem baralho. Rode `memoring deck list`, escolha o baralho adequado ao material e reutilize o nome; se não existir e a estrutura for destinada à importação, crie com `memoring deck create "<nome>" --pending` (ou deixe o próprio `stage create` criar pelo nome — nesse caso o baralho também nasce **PENDING**). **Espelhe a hierarquia do material:** a matéria vira baralho raiz; o tema da aula/capítulo vira sub-baralho criado ANTES com `memoring deck create "<tema>" --parent "<matéria>" --pending` — o create-por-nome do `stage create` só cria baralho raiz. Use `--pending` na estrutura criada para importação: baralhos PENDING ficam ocultos no app (árvore, filtros, fila, estatísticas) até o **primeiro item aceite** — o aceite ativa o baralho E os ancestrais pendentes de uma vez; descartar não ativa. Todo item do lote deve levar `deckName` — ou use `--deck "<nome>"` no create como padrão do lote. Lote sem baralho é REJEITADO pela validação local.
42
42
  5. **Leia os materiais** e gere os itens no contrato, num arquivo JSON (array ou `{ "items": [...] }`).
43
43
  6. **Envie**: `memoring stage create --file <arquivo> --json`. Se o lote for rejeitado por limite de tipo, ajuste o LOTE — não relaxe o limite do usuário.
44
44
  7. **Guarde os `ids` retornados** pelo create: enquanto os itens estiverem PENDING_REVIEW você pode corrigi-los com `memoring inbox update <id>` — sem precisar descartar e reenviar.
45
- 8. **Avise o usuário**: quantos itens foram para a caixa de revisão e onde aceitar — no app em `/cards/inbox` ou `memoring inbox list` / `memoring inbox accept-all`.
45
+ 8. **Avise o usuário e PARE**: informe quantos itens foram para a caixa de revisão, os `ids` e onde ele revisa — no app em `/cards/inbox` (ou `memoring inbox list` para ver o estado). O fluxo termina AQUI: aceitar (`memoring inbox accept <id>` / `memoring inbox accept-all`) é decisão do usuário e só acontece quando ELE pedir explicitamente — aponte o caminho, não execute.
46
46
 
47
47
  ## O contrato (a fonte da verdade é `stage schema`)
48
48
 
49
49
  - `type`: `NOTE` (nota, exige `content` markdown; `NORMAL` legado é aceito e mapeado para `NOTE`), `FLASHCARD` ou `ONE_SHOT` (exigem `front` + `back`) ou `QUESTION` (exige `question` — ver abaixo).
50
50
  - `title` sempre obrigatório; lote de 1 a 50 itens, all-or-nothing — qualquer item inválido rejeita o lote inteiro.
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. 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>"` (o `stage create` só cria raiz pelo nome).
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
54
  - Preencha `source` com a origem real do material (nome do arquivo/pasta) — não use o default quando souber a origem.
55
55
  - `subjectName`/`subtopicName` quando o material tiver matéria/subtópico claros; `tags` curtas e em minúsculas.
56
56
  - NUNCA invente campos fora do contrato — o servidor rejeita o lote inteiro.
57
57
 
58
- ## Quantidades (limites por tipo)
58
+ ## Quantidades (limites são tetos, NUNCA cotas)
59
59
 
60
- - O default do usuário costuma ser **à vontade** (`userCaps` com `-1`): crie QUANTOS itens o material justificar — qualidade acima de quantidade, sem encher cota e sem repetir o mesmo fato em variações triviais.
61
- - Se `userCaps` trouxer tetos numéricos, distribua a geração para caber neles; `0` proíbe o tipo.
62
- - 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`.
63
- - Lotes grandes: divida em vários arquivos de até 50 itens e envie sequencialmente.
60
+ - **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.
61
+ - **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.
62
+ - **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.
63
+ - `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.
64
+ - 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`.
65
+ - 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
66
 
65
67
  ## Qualidade dos flashcards (o padrão que o app espera)
66
68
 
@@ -69,6 +71,7 @@ Cartões atômicos: UMA ideia por cartão — se um conceito tem 3 partes cobrá
69
71
  ## Armadilhas
70
72
 
71
73
  - **Nunca** use `--replace` ou `inbox discard-all` sem o usuário pedir explicitamente — eles apagam os itens pendentes existentes da caixa.
74
+ - **Nunca** aceite itens em nome do usuário: `memoring inbox accept-all` e `memoring inbox accept <id>` só podem ser rodados quando o usuário pedir EXPLICITAMENTE a aceitação. Conferir o lote tecnicamente (JSON válido, gabarito correto, limites respeitados) é revisão SUA, não revisão do usuário — não conta como pedido de aceite. Sem pedido explícito, o fluxo termina no aviso para ele revisar em `/cards/inbox`.
72
75
  - Se aparecer um aviso de versão nova no stderr, ignore e siga o fluxo — **não** rode `memoring update` por conta própria; atualizar a CLI é decisão do usuário.
73
76
  - **Não** altere os limites do usuário (`stage caps`) por conta própria — é preferência dele, compartilhada com o app.
74
77
  - Se `stage create` falhar com erro de validação do servidor, corrija os itens indicados e reenvie — não relaxe o contrato.