@memoring/cli 0.4.0 → 0.6.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 +4 -3
- package/dist/commands/skill.js +20 -0
- package/dist/commands/stage.js +79 -34
- package/dist/main.js +6 -1
- package/package.json +1 -1
- package/skills/memoring-cli/SKILL.md +37 -13
package/README.md
CHANGED
|
@@ -54,9 +54,10 @@ Os itens enviados nascem **PENDING_REVIEW** — nada entra nas suas listagens ne
|
|
|
54
54
|
| Command | Purpose |
|
|
55
55
|
| --- | --- |
|
|
56
56
|
| `memoring auth` | login / logout / status |
|
|
57
|
-
| `memoring stage` | schema (o contrato + seus limites por tipo) / 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) |
|
|
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>` |
|
|
59
59
|
| `memoring update` | atualiza a CLI para a última versão (via npm) |
|
|
60
|
+
| `memoring skill` | imprime o guia completo da CLI para agentes de IA (o SKILL.md empacotado) |
|
|
60
61
|
| `memoring version` | versão da CLI |
|
|
61
62
|
|
|
62
63
|
## Flags
|
|
@@ -92,11 +93,11 @@ Cada item do lote (1 a 50, all-or-nothing):
|
|
|
92
93
|
|
|
93
94
|
- `type`: `NOTE` (nota, exige `content` em markdown; `NORMAL` legado é aceito e mapeado para `NOTE`), `FLASHCARD` ou `ONE_SHOT` (exigem `front` + `back`).
|
|
94
95
|
- `source`: origem do material — default `"Importação assistida (CLI)"`.
|
|
95
|
-
- Limites por tipo: `memoring stage caps` mostra/grava sua preferência (0 = não gerar o tipo) — o `stage schema` a expõe ao agente e o `create` a impõe; flags `--max-*` no `create` vencem por lote.
|
|
96
|
+
- 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.
|
|
96
97
|
|
|
97
98
|
## Para agentes de IA
|
|
98
99
|
|
|
99
|
-
Este pacote inclui uma skill em `skills/memoring-cli/SKILL.md` com o fluxo completo (auth → schema → gerar → stage → avisar o usuário).
|
|
100
|
+
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).
|
|
100
101
|
|
|
101
102
|
## Dev (backend local)
|
|
102
103
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fail } from './shared.js';
|
|
3
|
+
/**
|
|
4
|
+
* `memoring skill` — imprime o SKILL.md empacotado no npm (guia completo da CLI para
|
|
5
|
+
* agentes de IA: fluxo, contrato, limites por tipo, armadilhas). É como um agente
|
|
6
|
+
* aprende a CLI de cabo a rabo sem precisar caçar o arquivo no node_modules.
|
|
7
|
+
*/
|
|
8
|
+
export function skillCommand() {
|
|
9
|
+
let content;
|
|
10
|
+
try {
|
|
11
|
+
content = readFileSync(new URL('../../skills/memoring-cli/SKILL.md', import.meta.url), 'utf8');
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
fail('não encontrei o SKILL.md empacotado — reinstale com `npm i -g @memoring/cli@latest`.');
|
|
15
|
+
}
|
|
16
|
+
process.stdout.write(content);
|
|
17
|
+
if (!content.endsWith('\n')) {
|
|
18
|
+
process.stdout.write('\n');
|
|
19
|
+
}
|
|
20
|
+
}
|
package/dist/commands/stage.js
CHANGED
|
@@ -8,6 +8,43 @@ const TYPES = ['NOTE', 'FLASHCARD', 'ONE_SHOT'];
|
|
|
8
8
|
const LEGACY_TYPE_ALIASES = { NORMAL: 'NOTE' };
|
|
9
9
|
const MAX_ITEMS = 50;
|
|
10
10
|
const DEFAULT_SOURCE = 'Importação assistida (CLI)';
|
|
11
|
+
const UNLIMITED = -1;
|
|
12
|
+
/**
|
|
13
|
+
* GET /notes/import/agent-guidance, fail-open como o fetchStoredCaps: sem sessão/rede
|
|
14
|
+
* devolve null e o schema sai só com o contrato.
|
|
15
|
+
*/
|
|
16
|
+
async function fetchAgentGuidance() {
|
|
17
|
+
if (!hasSession()) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
return (await apiFetch('/notes/import/agent-guidance'));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Rótulo de exibição de um teto: -1 → "à vontade". */
|
|
28
|
+
function capLabel(max) {
|
|
29
|
+
return max === UNLIMITED ? 'à vontade' : `até ${max}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Teto vindo de flag ou preferência: inteiro -1..50, ou a palavra "livre" (= -1, à vontade).
|
|
33
|
+
* `null` quando a flag está ausente.
|
|
34
|
+
*/
|
|
35
|
+
function parseCapFlag(raw, flagName) {
|
|
36
|
+
if (raw === undefined) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
if (raw.trim().toLowerCase() === 'livre') {
|
|
40
|
+
return UNLIMITED;
|
|
41
|
+
}
|
|
42
|
+
const value = Number(raw);
|
|
43
|
+
if (!Number.isInteger(value) || value < UNLIMITED || value > 50) {
|
|
44
|
+
fail(`--${flagName} inválido: use "livre" ou um inteiro entre -1 (à vontade) e 50 (veio ${JSON.stringify(raw)}).`);
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
11
48
|
/**
|
|
12
49
|
* GET /notes/import/caps, fail-open: sem sessão ou sem rede devolve null (o schema
|
|
13
50
|
* continua útil deslogado e o create só perde a validação extra). Só chama a API
|
|
@@ -44,23 +81,19 @@ const CAPS_FLAG_BY_FIELD = {
|
|
|
44
81
|
};
|
|
45
82
|
/**
|
|
46
83
|
* Teto por tipo no lote (validação local): a flag --max-<tipo> vence; sem flag, vale a
|
|
47
|
-
* preferência gravada do usuário (`memoring stage caps` / app).
|
|
48
|
-
* preferência fica livre.
|
|
84
|
+
* preferência gravada do usuário (`memoring stage caps` / app). "livre"/-1 (à vontade)
|
|
85
|
+
* não limita; tipo sem flag nem preferência fica livre.
|
|
49
86
|
*/
|
|
50
87
|
function enforceTypeCaps(items, flags, stored) {
|
|
51
88
|
const storedByType = stored
|
|
52
89
|
? { NOTE: stored.maxNotes, FLASHCARD: stored.maxFlashcards, ONE_SHOT: stored.maxOneShots }
|
|
53
90
|
: {};
|
|
54
91
|
for (const [type, flag] of Object.entries(MAX_FLAG_BY_TYPE)) {
|
|
55
|
-
const
|
|
92
|
+
const fromFlag = parseCapFlag(stringFlag(flags, flag), flag);
|
|
56
93
|
let max;
|
|
57
94
|
let source;
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
61
|
-
fail(`--${flag} inválido: use um inteiro >= 0 (veio ${JSON.stringify(raw)}).`);
|
|
62
|
-
}
|
|
63
|
-
max = parsed;
|
|
95
|
+
if (fromFlag !== null) {
|
|
96
|
+
max = fromFlag;
|
|
64
97
|
source = `--${flag}`;
|
|
65
98
|
}
|
|
66
99
|
else if (storedByType[type] !== undefined) {
|
|
@@ -70,9 +103,12 @@ function enforceTypeCaps(items, flags, stored) {
|
|
|
70
103
|
else {
|
|
71
104
|
continue;
|
|
72
105
|
}
|
|
106
|
+
if (max === UNLIMITED) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
73
109
|
const count = items.filter((item) => item.type === type).length;
|
|
74
110
|
if (count > max) {
|
|
75
|
-
fail(`lote tem ${count} ${TYPE_LABEL[type]} e o limite é ${max} (${source}). Ajuste o lote ou o limite. ` +
|
|
111
|
+
fail(`lote tem ${count} ${TYPE_LABEL[type]} e o limite é ${max} (${source}). Ajuste o lote ou o limite ("livre" = à vontade). ` +
|
|
76
112
|
'Se um agente gerou este lote, peça a ele para respeitar os limites por tipo.');
|
|
77
113
|
}
|
|
78
114
|
}
|
|
@@ -101,7 +137,7 @@ const SCHEMA_JSON = {
|
|
|
101
137
|
source: 'Apostila constitucional.pdf',
|
|
102
138
|
},
|
|
103
139
|
],
|
|
104
|
-
typeCaps: 'preferência por usuário (`memoring stage caps`, vale para app e CLI) — o `stage schema` mostra os limites ao agente; flags do `stage create` (--max-notes N | --max-flashcards N | --max-one-shots N) vencem a preferência por lote.',
|
|
140
|
+
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, ou "livre") vencem a preferência por lote.',
|
|
105
141
|
};
|
|
106
142
|
const SCHEMA_TEXT = `Contrato dos itens da caixa de revisão (POST /api/notes/review-inbox):
|
|
107
143
|
|
|
@@ -118,7 +154,7 @@ Lote: 1 a ${MAX_ITEMS} itens, all-or-nothing — qualquer item inválido rejeita
|
|
|
118
154
|
Os itens nascem PENDING_REVIEW: o usuário aceita, edita ou recusa em /cards/inbox.
|
|
119
155
|
Dica: FLASHCARD entra na fila de revisão espaçada (FSRS); ONE_SHOT é pergunta de resposta única.
|
|
120
156
|
Limites por tipo: a preferência do usuário (abaixo, quando logado) vale por padrão;
|
|
121
|
-
--max-notes N | --max-flashcards N | --max-one-shots N no create vencem por lote.`;
|
|
157
|
+
--max-notes N | --max-flashcards N | --max-one-shots N no create vencem por lote ("livre" = à vontade).`;
|
|
122
158
|
async function readStdin() {
|
|
123
159
|
const chunks = [];
|
|
124
160
|
for await (const chunk of process.stdin) {
|
|
@@ -185,29 +221,42 @@ export async function stageCommand(parsed) {
|
|
|
185
221
|
const [sub] = parsed._;
|
|
186
222
|
switch (sub) {
|
|
187
223
|
case 'schema': {
|
|
188
|
-
const caps = await fetchStoredCaps();
|
|
224
|
+
const [caps, guidance] = await Promise.all([fetchStoredCaps(), fetchAgentGuidance()]);
|
|
189
225
|
if (parsed.flags.json) {
|
|
190
|
-
printJson(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
226
|
+
printJson({
|
|
227
|
+
...SCHEMA_JSON,
|
|
228
|
+
...(caps
|
|
229
|
+
? {
|
|
230
|
+
userCaps: {
|
|
231
|
+
maxNotes: caps.maxNotes,
|
|
232
|
+
maxFlashcards: caps.maxFlashcards,
|
|
233
|
+
maxOneShots: caps.maxOneShots,
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
: {}),
|
|
237
|
+
...(guidance ?? {}),
|
|
238
|
+
});
|
|
200
239
|
}
|
|
201
240
|
else {
|
|
202
241
|
process.stdout.write(SCHEMA_TEXT + '\n');
|
|
203
242
|
if (caps) {
|
|
204
243
|
process.stdout.write(`\nLimites por tipo configurados por você — o agente DEVE respeitá-los ao gerar o lote: ` +
|
|
205
|
-
`
|
|
206
|
-
dim('(altere com `memoring stage caps`; QUESTION não é estagiável pela CLI)\n'));
|
|
244
|
+
`notas: ${capLabel(caps.maxNotes)}, flashcards: ${capLabel(caps.maxFlashcards)}, 1-shots: ${capLabel(caps.maxOneShots)}.\n` +
|
|
245
|
+
dim('("à vontade" = crie quantos o material justificar; altere com `memoring stage caps`; QUESTION não é estagiável pela CLI)\n'));
|
|
207
246
|
}
|
|
208
247
|
else {
|
|
209
248
|
process.stdout.write(dim('\n(limites por tipo indisponíveis — faça `memoring auth login` ou verifique a conexão)\n'));
|
|
210
249
|
}
|
|
250
|
+
if (guidance) {
|
|
251
|
+
process.stdout.write('\nRegras de qualidade — SEMPRE aplicadas ao gerar (as mesmas que o app envia à LLM):\n' +
|
|
252
|
+
guidance.qualityRules.trim() +
|
|
253
|
+
'\n');
|
|
254
|
+
if (guidance.customInstructions) {
|
|
255
|
+
process.stdout.write('\nInstruções adicionais do usuário — também SEMPRE aplicadas:\n' +
|
|
256
|
+
guidance.customInstructions.trim() +
|
|
257
|
+
'\n');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
211
260
|
}
|
|
212
261
|
return;
|
|
213
262
|
}
|
|
@@ -216,14 +265,10 @@ export async function stageCommand(parsed) {
|
|
|
216
265
|
const current = (await apiFetch('/notes/import/caps'));
|
|
217
266
|
const updates = {};
|
|
218
267
|
for (const [field, flag] of Object.entries(CAPS_FLAG_BY_FIELD)) {
|
|
219
|
-
const
|
|
220
|
-
if (
|
|
268
|
+
const value = parseCapFlag(stringFlag(parsed.flags, flag), flag);
|
|
269
|
+
if (value === null) {
|
|
221
270
|
continue;
|
|
222
271
|
}
|
|
223
|
-
const value = Number(raw);
|
|
224
|
-
if (!Number.isInteger(value) || value < 0 || value > 50) {
|
|
225
|
-
fail(`--${flag} inválido: use um inteiro entre 0 e 50 (veio ${JSON.stringify(raw)}).`);
|
|
226
|
-
}
|
|
227
272
|
updates[field] = value;
|
|
228
273
|
}
|
|
229
274
|
const hasUpdates = Object.keys(updates).length > 0;
|
|
@@ -236,10 +281,10 @@ export async function stageCommand(parsed) {
|
|
|
236
281
|
}
|
|
237
282
|
process.stdout.write((hasUpdates ? green('Limites gravados') : 'Limites por tipo') +
|
|
238
283
|
' — valem para a importação do app e para os agentes via CLI:\n');
|
|
239
|
-
process.stdout.write(` notas:
|
|
240
|
-
`1-shots:
|
|
284
|
+
process.stdout.write(` notas: ${capLabel(effective.maxNotes)} flashcards: ${capLabel(effective.maxFlashcards)} ` +
|
|
285
|
+
`1-shots: ${capLabel(effective.maxOneShots)} questões: ${capLabel(effective.maxQuestions)}\n`);
|
|
241
286
|
if (!hasUpdates) {
|
|
242
|
-
process.stdout.write(dim('Altere com: memoring stage caps --max-flashcards 30 (0 = não gerar o tipo)\n'));
|
|
287
|
+
process.stdout.write(dim('Altere com: memoring stage caps --max-flashcards 30 (0 = não gerar o tipo; "livre" = à vontade)\n'));
|
|
243
288
|
}
|
|
244
289
|
return;
|
|
245
290
|
}
|
package/dist/main.js
CHANGED
|
@@ -5,9 +5,10 @@ import { authCommand } from './commands/auth.js';
|
|
|
5
5
|
import { inboxCommand } from './commands/inbox.js';
|
|
6
6
|
import { stageCommand } from './commands/stage.js';
|
|
7
7
|
import { updateCommand } from './commands/update.js';
|
|
8
|
+
import { skillCommand } from './commands/skill.js';
|
|
8
9
|
import { fail, parseArgs as parse } from './commands/shared.js';
|
|
9
10
|
import { newerVersionAvailable, scheduleUpdateCheck } from './update.js';
|
|
10
|
-
const VERSION = '0.
|
|
11
|
+
const VERSION = '0.6.0';
|
|
11
12
|
const HELP = `Memoring CLI — flashcards e notas por IA, direto do terminal.
|
|
12
13
|
|
|
13
14
|
Uso: memoring <comando> [subcomando] [flags]
|
|
@@ -22,6 +23,7 @@ Comandos:
|
|
|
22
23
|
inbox accept <id>|accept-all aceita itens (entram nas listagens e na fila FSRS)
|
|
23
24
|
inbox discard <id> descarta um item pendente
|
|
24
25
|
update atualiza a CLI para a versão mais recente (via npm)
|
|
26
|
+
skill guia completo da CLI para agentes de IA (imprime o SKILL.md)
|
|
25
27
|
version versão da CLI
|
|
26
28
|
|
|
27
29
|
Flags globais:
|
|
@@ -85,6 +87,9 @@ async function main() {
|
|
|
85
87
|
case 'update':
|
|
86
88
|
await updateCommand(VERSION);
|
|
87
89
|
return;
|
|
90
|
+
case 'skill':
|
|
91
|
+
skillCommand();
|
|
92
|
+
return;
|
|
88
93
|
case 'version':
|
|
89
94
|
process.stdout.write(`memoring-cli ${VERSION}\n`);
|
|
90
95
|
return;
|
package/package.json
CHANGED
|
@@ -3,38 +3,62 @@ name: memoring-cli
|
|
|
3
3
|
description: Gere flashcards e notas a partir de materiais de estudo locais (PDFs, textos, anotações) e envie para a caixa de revisão do Memoring usando a CLI `memoring`. Use quando o usuário pedir para "usar a cli do memoring", criar flashcards/notas de uma pasta ou arquivo, ou enviar material de estudo para o Memoring.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Memoring CLI —
|
|
6
|
+
# Memoring CLI — guia completo para agentes
|
|
7
7
|
|
|
8
|
-
O Memoring é um app de estudos com revisão espaçada (FSRS). A CLI `memoring` é a ponte entre você (o agente, que lê o material e gera o conteúdo) e a conta do usuário no app. Você NÃO chama a API HTTP diretamente — use sempre a CLI.
|
|
8
|
+
O Memoring é um app de estudos com revisão espaçada (FSRS). A CLI `memoring` é a ponte entre você (o agente, que lê o material e gera o conteúdo) e a conta do usuário no app. Você NÃO chama a API HTTP diretamente — use sempre a CLI. Este guia está sempre disponível via `memoring skill`.
|
|
9
9
|
|
|
10
10
|
## Pré-requisitos
|
|
11
11
|
|
|
12
12
|
1. CLI instalada: `memoring version` deve responder. Se não, peça ao usuário: `npm install -g @memoring/cli`.
|
|
13
13
|
2. Sessão ativa: `memoring auth status --json` → `authenticated: true`. Se não, peça ao usuário para rodar `memoring auth login` (device flow — ele aprova no browser; você não consegue fazer isso por ele).
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Mapa dos comandos
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
| Comando | O que faz |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `memoring auth login\|logout\|status` | sessão (device flow — sem senha no terminal) |
|
|
20
|
+
| `memoring stage schema [--json]` | O CONTRATO dos itens + os limites por tipo do usuário — leia SEMPRE antes de gerar |
|
|
21
|
+
| `memoring stage caps [--max-* N\|livre]` | limites por tipo do usuário (preferência DELE — nunca escreva sem ele pedir) |
|
|
22
|
+
| `memoring stage create --file f [--replace] [--max-* N]` | envia o lote (1 a 50 itens) para a caixa de revisão |
|
|
23
|
+
| `memoring inbox list` | itens pendentes de revisão |
|
|
24
|
+
| `memoring inbox accept <id>\|accept-all` | aceita itens (entram nas listagens e na fila FSRS) |
|
|
25
|
+
| `memoring inbox discard <id>` | descarta um item pendente |
|
|
26
|
+
| `memoring skill` | imprime este guia |
|
|
27
|
+
| `memoring update` | atualiza a CLI (decisão do usuário — nunca rode por conta própria) |
|
|
28
|
+
|
|
29
|
+
Flags globais: `--json` (saída legível por máquina), `--no-color`, `--api-base`.
|
|
30
|
+
|
|
31
|
+
## Fluxo padrão
|
|
32
|
+
|
|
33
|
+
1. **Entenda o pedido** — qual pasta/arquivo/material vira flashcards/notas e se o usuário pediu quantidades.
|
|
34
|
+
2. **Leia o contrato, os limites e as regras**: `memoring stage schema --json`. 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.
|
|
19
35
|
3. **Leia os materiais** e gere os itens no contrato, num arquivo JSON (array ou `{ "items": [...] }`).
|
|
20
|
-
4. **Envie**: `memoring stage create --file <arquivo> --json`.
|
|
36
|
+
4. **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.
|
|
21
37
|
5. **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`.
|
|
22
38
|
|
|
23
|
-
##
|
|
39
|
+
## O contrato (a fonte da verdade é `stage schema`)
|
|
24
40
|
|
|
25
41
|
- `type`: `NOTE` (nota, exige `content` markdown; `NORMAL` legado é aceito e mapeado para `NOTE`), `FLASHCARD` ou `ONE_SHOT` (exigem `front` + `back`).
|
|
26
|
-
- `title` sempre obrigatório; lote de 1 a 50 itens, all-or-nothing.
|
|
27
|
-
-
|
|
28
|
-
- O `stage schema` mostra os **limites por tipo do usuário** (`userCaps` no JSON; texto no fim da saída em modo humano) — respeite-os ao gerar, mesmo sem pedido explícito no chat. Sem limites visíveis, o create ainda pode rejeitar pela preferência gravada: ajuste o lote e reenvie. Para MUDAR a preferência permanente, o usuário roda `memoring stage caps` — não rode por conta própria.
|
|
29
|
-
- `ONE_SHOT` = pergunta de resposta única; prefira `FLASHCARD` para memorização.
|
|
42
|
+
- `title` sempre obrigatório; lote de 1 a 50 itens, all-or-nothing — qualquer item inválido rejeita o lote inteiro.
|
|
43
|
+
- `ONE_SHOT` = pergunta de resposta única (uma palavra ou termo curto); prefira `FLASHCARD` para memorização.
|
|
30
44
|
- Preencha `source` com a origem real do material (nome do arquivo/pasta) — não use o default quando souber a origem.
|
|
31
45
|
- `subjectName`/`subtopicName` quando o material tiver matéria/subtópico claros; `tags` curtas e em minúsculas.
|
|
32
46
|
- NUNCA invente campos fora do contrato — o servidor rejeita o lote inteiro.
|
|
33
47
|
|
|
48
|
+
## Quantidades (limites por tipo)
|
|
49
|
+
|
|
50
|
+
- 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.
|
|
51
|
+
- Se `userCaps` trouxer tetos numéricos, distribua a geração para caber neles; `0` proíbe o tipo.
|
|
52
|
+
- 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`.
|
|
53
|
+
- Lotes grandes: divida em vários arquivos de até 50 itens e envie sequencialmente.
|
|
54
|
+
|
|
55
|
+
## Qualidade dos flashcards (o padrão que o app espera)
|
|
56
|
+
|
|
57
|
+
Cartões atômicos: UMA ideia por cartão — se um conceito tem 3 partes cobráveis, crie 3 cartões, nunca um cartão com lista como resposta. A frente é uma pergunta direta que força LEMBRAR (não reconhecer). A resposta é auto-contida (faz sentido sem o material) e curta. Pares confundíveis (prazos, siglas, exceções) levam na frente o traço que os diferencia. Não invente conteúdo que não esteja no material.
|
|
58
|
+
|
|
34
59
|
## Armadilhas
|
|
35
60
|
|
|
36
61
|
- **Nunca** use `--replace` sem o usuário pedir explicitamente — ele apaga os itens pendentes existentes da caixa.
|
|
37
62
|
- 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.
|
|
63
|
+
- **Não** altere os limites do usuário (`stage caps`) por conta própria — é preferência dele, compartilhada com o app.
|
|
38
64
|
- Se `stage create` falhar com erro de validação do servidor, corrija os itens indicados e reenvie — não relaxe o contrato.
|
|
39
|
-
- Lotes grandes: divida em vários arquivos de até 50 itens e envie sequencialmente.
|
|
40
|
-
- Flashcards bons são atômicos: uma ideia por cartão, pergunta direta, resposta completa mas curta.
|