bmad-plus 0.17.1 → 0.19.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.
@@ -31,7 +31,7 @@
31
31
  "intended": "It is intended.",
32
32
  "savedLocal": "Saved in this browser — remember to save the JSON at the end",
33
33
  "savedRemote": "Saved ·",
34
- "localMode": "This page is a local file: at the end, save the JSON and hand the file over — that is what the agent reads.",
34
+ "localMode": "Your answers are kept in this browser, on this device, at every tick, and come back when the page is reloaded. They can disappear if the site data is cleared or a private window is closed, and nobody receives them: at the end, save the JSON and hand the file over — that is what the agent reads.",
35
35
  "dbMode": "Your answers are saved as you go; the agent reads them directly, you have nothing to send.",
36
36
  "serveMode": "Your answers are saved straight into the project as you tick; you have nothing to send.",
37
37
  "startFirst": "Enter your first name and start a run first — nothing is recorded before that.",
@@ -41,7 +41,28 @@
41
41
  "finishedOn": "finished on {date}",
42
42
  "countSeen": "seen",
43
43
  "countNotSeen": "not seen",
44
- "countBlocked": "blocked"
44
+ "countBlocked": "blocked",
45
+ "countAnswered": "answered",
46
+ "progressLabel": "Lines answered",
47
+ "percentAnswered": "{n} % answered",
48
+ "saving": "Saving…",
49
+ "saveFailed": "Could not save in this browser — save or copy the JSON before leaving or reloading this page.",
50
+ "remoteFailed": "Saved in this browser only: the copy could not be sent. Keep going, and save the JSON at the end.",
51
+ "otherTab": "This run is also open in another tab or window: the latest change wins, whichever tab made it.",
52
+ "otherRevision": "answered another revision of this recipe",
53
+ "continueHere": "Continue on this revision — unchanged lines keep their answers, changed lines are asked again",
54
+ "saveThatJson": "Save its JSON",
55
+ "carried": "{kept} answer(s) kept, {left} line(s) to answer — the earlier run stays saved as it was.",
56
+ "draftUnreadable": "A run saved in this browser cannot be read. It was not deleted: save it and hand it to the agent.",
57
+ "saveRaw": "Save the unreadable data",
58
+ "another": "Start another run — this one stays saved",
59
+ "finishedNote": "Run finished. This is not an acceptance: the agent reads every answer and classifies each failure before anything is decided.",
60
+ "finishAnyway": "Finish anyway",
61
+ "skipped": "Skipped",
62
+ "blockedWhy": "What stopped you?",
63
+ "savedAs": "Saved as {file}",
64
+ "downloadFailed": "The file could not be saved — use \"Copy the JSON\" instead.",
65
+ "changedAfterFinish": "Changed after finishing: the run keeps its finish date, and the agent sees the later change."
45
66
  },
46
67
  "fr": {
47
68
  "name": "Français",
@@ -75,7 +96,7 @@
75
96
  "intended": "C'est voulu.",
76
97
  "savedLocal": "Enregistré dans ce navigateur — pense à « Enregistrer le JSON » à la fin",
77
98
  "savedRemote": "Enregistré ·",
78
- "localMode": "Cette page est un fichier local : à la fin, enregistre le JSON et transmets le fichier — c'est lui que l'agent lit.",
99
+ "localMode": "Tes réponses sont conservées dans ce navigateur, sur cet appareil, à chaque coche, et reviennent quand la page est rechargée. Elles peuvent disparaître si les données du site sont effacées ou si une fenêtre privée est fermée, et personne ne les reçoit : à la fin, enregistre le JSON et transmets le fichier — c'est lui que l'agent lit.",
79
100
  "dbMode": "Tes réponses sont enregistrées au fur et à mesure : l'agent les lit directement, tu n'as rien à envoyer.",
80
101
  "serveMode": "Tes réponses sont enregistrées directement dans le projet à chaque coche ; tu n'as rien à envoyer.",
81
102
  "startFirst": "Écris ton prénom et commence la recette d’abord — rien n’est enregistré avant.",
@@ -85,7 +106,28 @@
85
106
  "finishedOn": "terminée le {date}",
86
107
  "countSeen": "vus",
87
108
  "countNotSeen": "pas vus",
88
- "countBlocked": "bloqués"
109
+ "countBlocked": "bloqués",
110
+ "countAnswered": "renseignées",
111
+ "progressLabel": "Lignes renseignées",
112
+ "percentAnswered": "{n} % renseignées",
113
+ "saving": "Enregistrement…",
114
+ "saveFailed": "Impossible d'enregistrer dans ce navigateur — enregistre ou copie le JSON avant de quitter ou de recharger la page.",
115
+ "remoteFailed": "Enregistré dans ce navigateur seulement : la copie n'a pas pu être envoyée. Continue, et enregistre le JSON à la fin.",
116
+ "otherTab": "Cette recette est aussi ouverte dans un autre onglet ou une autre fenêtre : la dernière modification l'emporte, quel que soit l'onglet.",
117
+ "otherRevision": "a répondu à une autre version de cette recette",
118
+ "continueHere": "Continuer sur cette version — les lignes inchangées gardent leurs réponses, les lignes modifiées sont redemandées",
119
+ "saveThatJson": "Enregistrer son JSON",
120
+ "carried": "{kept} réponse(s) conservée(s), {left} ligne(s) à renseigner — la recette précédente reste enregistrée telle quelle.",
121
+ "draftUnreadable": "Une recette enregistrée dans ce navigateur ne peut pas être lue. Elle n'a pas été supprimée : enregistre-la et transmets-la à l'agent.",
122
+ "saveRaw": "Enregistrer les données illisibles",
123
+ "another": "Commencer une autre recette — celle-ci reste enregistrée",
124
+ "finishedNote": "Recette terminée. Ce n'est pas une validation : l'agent lit chaque réponse et classe chaque échec avant toute décision.",
125
+ "finishAnyway": "Terminer quand même",
126
+ "skipped": "Sautée",
127
+ "blockedWhy": "Qu'est-ce qui t'a bloqué ?",
128
+ "savedAs": "Enregistré sous {file}",
129
+ "downloadFailed": "Le fichier n'a pas pu être enregistré — utilise « Copier le JSON ».",
130
+ "changedAfterFinish": "Modifiée après la fin : la recette garde sa date de fin, et l'agent voit la modification."
89
131
  },
90
132
  "es": {
91
133
  "name": "Español",
@@ -119,7 +161,7 @@
119
161
  "intended": "Es intencionado.",
120
162
  "savedLocal": "Guardado en este navegador — acuérdate de guardar el JSON al final",
121
163
  "savedRemote": "Guardado ·",
122
- "localMode": "Esta página es un archivo local: al final, guarda el JSON y entrega el archivo — es lo que lee el agente.",
164
+ "localMode": "Tus respuestas se conservan en este navegador, en este dispositivo, con cada marca, y vuelven al recargar la página. Pueden desaparecer si se borran los datos del sitio o se cierra una ventana privada, y nadie las recibe: al final, guarda el JSON y entrega el archivo — es lo que lee el agente.",
123
165
  "dbMode": "Tus respuestas se guardan sobre la marcha; el agente las lee directamente, no tienes que enviar nada.",
124
166
  "serveMode": "Tus respuestas se guardan directamente en el proyecto con cada marca; no tienes que enviar nada.",
125
167
  "startFirst": "Escribe tu nombre y empieza la prueba primero: nada se guarda antes.",
@@ -129,7 +171,28 @@
129
171
  "finishedOn": "terminada el {date}",
130
172
  "countSeen": "vistos",
131
173
  "countNotSeen": "no vistos",
132
- "countBlocked": "bloqueados"
174
+ "countBlocked": "bloqueados",
175
+ "countAnswered": "respondidas",
176
+ "progressLabel": "Líneas respondidas",
177
+ "percentAnswered": "{n} % respondidas",
178
+ "saving": "Guardando…",
179
+ "saveFailed": "No se pudo guardar en este navegador: guarda o copia el JSON antes de salir o recargar la página.",
180
+ "remoteFailed": "Guardado solo en este navegador: la copia no pudo enviarse. Sigue adelante y guarda el JSON al final.",
181
+ "otherTab": "Esta prueba también está abierta en otra pestaña o ventana: el último cambio gana, sea cual sea la pestaña.",
182
+ "otherRevision": "respondió a otra versión de esta prueba",
183
+ "continueHere": "Continuar en esta versión: las líneas sin cambios conservan sus respuestas, las modificadas se preguntan de nuevo",
184
+ "saveThatJson": "Guardar su JSON",
185
+ "carried": "{kept} respuesta(s) conservada(s), {left} línea(s) por responder — la prueba anterior sigue guardada tal cual.",
186
+ "draftUnreadable": "Una prueba guardada en este navegador no se puede leer. No se ha borrado: guárdala y entrégasela al agente.",
187
+ "saveRaw": "Guardar los datos ilegibles",
188
+ "another": "Empezar otra prueba: esta queda guardada",
189
+ "finishedNote": "Prueba terminada. No es una aceptación: el agente lee cada respuesta y clasifica cada fallo antes de decidir nada.",
190
+ "finishAnyway": "Terminar de todos modos",
191
+ "skipped": "Omitido",
192
+ "blockedWhy": "¿Qué te lo impidió?",
193
+ "savedAs": "Guardado como {file}",
194
+ "downloadFailed": "No se pudo guardar el archivo: usa «Copiar el JSON».",
195
+ "changedAfterFinish": "Modificada después de terminar: la prueba conserva su fecha de fin y el agente ve el cambio posterior."
133
196
  },
134
197
  "de": {
135
198
  "name": "Deutsch",
@@ -163,7 +226,7 @@
163
226
  "intended": "Das ist so gewollt.",
164
227
  "savedLocal": "In diesem Browser gespeichert — denk am Ende an „JSON speichern“",
165
228
  "savedRemote": "Gespeichert ·",
166
- "localMode": "Diese Seite ist eine lokale Datei: speichere am Ende das JSON und gib die Datei weiter — sie ist es, die der Agent liest.",
229
+ "localMode": "Deine Antworten werden bei jedem Klick in diesem Browser auf diesem Gerät gespeichert und sind nach dem Neuladen der Seite wieder da. Sie können verschwinden, wenn die Websitedaten gelöscht werden oder ein privates Fenster geschlossen wird, und niemand erhält sie: Speichere am Ende das JSON und gib die Datei weiter — sie ist es, die der Agent liest.",
167
230
  "dbMode": "Deine Antworten werden laufend gespeichert; der Agent liest sie direkt, du musst nichts senden.",
168
231
  "serveMode": "Deine Antworten werden bei jedem Klick direkt im Projekt gespeichert; du musst nichts senden.",
169
232
  "startFirst": "Gib zuerst deinen Vornamen ein und starte den Durchlauf — vorher wird nichts gespeichert.",
@@ -173,7 +236,28 @@
173
236
  "finishedOn": "abgeschlossen am {date}",
174
237
  "countSeen": "gesehen",
175
238
  "countNotSeen": "nicht gesehen",
176
- "countBlocked": "blockiert"
239
+ "countBlocked": "blockiert",
240
+ "countAnswered": "beantwortet",
241
+ "progressLabel": "Beantwortete Zeilen",
242
+ "percentAnswered": "{n} % beantwortet",
243
+ "saving": "Wird gespeichert…",
244
+ "saveFailed": "Speichern in diesem Browser nicht möglich — speichere oder kopiere das JSON, bevor du die Seite verlässt oder neu lädst.",
245
+ "remoteFailed": "Nur in diesem Browser gespeichert: Die Kopie konnte nicht gesendet werden. Mach weiter und speichere am Ende das JSON.",
246
+ "otherTab": "Dieser Durchlauf ist auch in einem anderen Tab oder Fenster geöffnet: Die letzte Änderung gewinnt, egal aus welchem Tab.",
247
+ "otherRevision": "hat eine andere Fassung dieser Checkliste beantwortet",
248
+ "continueHere": "Mit dieser Fassung weitermachen — unveränderte Zeilen behalten ihre Antworten, geänderte werden erneut abgefragt",
249
+ "saveThatJson": "JSON dieses Durchlaufs speichern",
250
+ "carried": "{kept} Antwort(en) übernommen, {left} Zeile(n) zu beantworten — der frühere Durchlauf bleibt unverändert gespeichert.",
251
+ "draftUnreadable": "Ein in diesem Browser gespeicherter Durchlauf kann nicht gelesen werden. Er wurde nicht gelöscht: Speichere ihn und gib ihn an den Agenten weiter.",
252
+ "saveRaw": "Unlesbare Daten speichern",
253
+ "another": "Einen weiteren Durchlauf starten — dieser bleibt gespeichert",
254
+ "finishedNote": "Durchlauf abgeschlossen. Das ist keine Abnahme: Der Agent liest jede Antwort und ordnet jeden Fehlschlag ein, bevor etwas entschieden wird.",
255
+ "finishAnyway": "Trotzdem abschließen",
256
+ "skipped": "Übersprungen",
257
+ "blockedWhy": "Was hat dich gehindert?",
258
+ "savedAs": "Gespeichert als {file}",
259
+ "downloadFailed": "Die Datei konnte nicht gespeichert werden — nutze „JSON kopieren“.",
260
+ "changedAfterFinish": "Nach dem Abschluss geändert: Der Durchlauf behält sein Abschlussdatum, und der Agent sieht die spätere Änderung."
177
261
  },
178
262
  "pt-br": {
179
263
  "name": "Português (Brasil)",
@@ -207,7 +291,7 @@
207
291
  "intended": "É intencional.",
208
292
  "savedLocal": "Salvo neste navegador — lembre-se de salvar o JSON no final",
209
293
  "savedRemote": "Salvo ·",
210
- "localMode": "Esta página é um arquivo local: no final, salve o JSON e entregue o arquivo — é ele que o agente lê.",
294
+ "localMode": "Suas respostas ficam salvas neste navegador, neste dispositivo, a cada marcação, e voltam quando a página é recarregada. Elas podem desaparecer se os dados do site forem apagados ou uma janela anônima for fechada, e ninguém as recebe: no final, salve o JSON e entregue o arquivo — é ele que o agente lê.",
211
295
  "dbMode": "Suas respostas são salvas conforme você avança; o agente as lê diretamente, você não precisa enviar nada.",
212
296
  "serveMode": "Suas respostas são salvas direto no projeto a cada marcação; você não precisa enviar nada.",
213
297
  "startFirst": "Digite seu nome e comece a rodada primeiro — nada é salvo antes disso.",
@@ -217,7 +301,28 @@
217
301
  "finishedOn": "encerrada em {date}",
218
302
  "countSeen": "vistos",
219
303
  "countNotSeen": "não vistos",
220
- "countBlocked": "bloqueados"
304
+ "countBlocked": "bloqueados",
305
+ "countAnswered": "respondidas",
306
+ "progressLabel": "Linhas respondidas",
307
+ "percentAnswered": "{n}% respondidas",
308
+ "saving": "Salvando…",
309
+ "saveFailed": "Não foi possível salvar neste navegador — salve ou copie o JSON antes de sair ou recarregar a página.",
310
+ "remoteFailed": "Salvo apenas neste navegador: a cópia não pôde ser enviada. Continue e salve o JSON no final.",
311
+ "otherTab": "Esta rodada também está aberta em outra aba ou janela: a última alteração vence, seja qual for a aba.",
312
+ "otherRevision": "respondeu a outra revisão deste roteiro",
313
+ "continueHere": "Continuar nesta revisão — linhas inalteradas mantêm as respostas, linhas alteradas precisam ser respondidas de novo",
314
+ "saveThatJson": "Salvar o JSON dela",
315
+ "carried": "{kept} resposta(s) mantida(s), {left} linha(s) a responder — a rodada anterior continua salva como estava.",
316
+ "draftUnreadable": "Uma rodada salva neste navegador não pode ser lida. Ela não foi apagada: salve-a e entregue ao agente.",
317
+ "saveRaw": "Salvar os dados ilegíveis",
318
+ "another": "Começar outra rodada — esta continua salva",
319
+ "finishedNote": "Rodada encerrada. Isso não é uma aceitação: o agente lê cada resposta e classifica cada falha antes de qualquer decisão.",
320
+ "finishAnyway": "Encerrar mesmo assim",
321
+ "skipped": "Pulada",
322
+ "blockedWhy": "O que impediu você?",
323
+ "savedAs": "Salvo como {file}",
324
+ "downloadFailed": "Não foi possível salvar o arquivo — use “Copiar o JSON”.",
325
+ "changedAfterFinish": "Alterada após o encerramento: a rodada mantém a data de fim, e o agente vê a alteração posterior."
221
326
  },
222
327
  "ru": {
223
328
  "name": "Русский",
@@ -251,7 +356,7 @@
251
356
  "intended": "Так задумано.",
252
357
  "savedLocal": "Сохранено в этом браузере — в конце не забудьте сохранить JSON",
253
358
  "savedRemote": "Сохранено ·",
254
- "localMode": "Эта страница — локальный файл: в конце сохраните JSON и передайте файл — именно его читает агент.",
359
+ "localMode": "Ваши ответы сохраняются в этом браузере, на этом устройстве, при каждой отметке и возвращаются после перезагрузки страницы. Они могут исчезнуть при очистке данных сайта или закрытии приватного окна, и никто их не получает: в конце сохраните JSON и передайте файл — именно его читает агент.",
255
360
  "dbMode": "Ваши ответы сохраняются по ходу; агент читает их напрямую, отправлять ничего не нужно.",
256
361
  "serveMode": "Ваши ответы сохраняются прямо в проект при каждой отметке; отправлять ничего не нужно.",
257
362
  "startFirst": "Сначала введите имя и начните прогон — до этого ничего не сохраняется.",
@@ -261,7 +366,28 @@
261
366
  "finishedOn": "завершён {date}",
262
367
  "countSeen": "видно",
263
368
  "countNotSeen": "не видно",
264
- "countBlocked": "недоступно"
369
+ "countBlocked": "недоступно",
370
+ "countAnswered": "отвечено",
371
+ "progressLabel": "Отвеченные строки",
372
+ "percentAnswered": "{n} % отвечено",
373
+ "saving": "Сохранение…",
374
+ "saveFailed": "Не удалось сохранить в этом браузере — сохраните или скопируйте JSON, прежде чем уйти со страницы или перезагрузить её.",
375
+ "remoteFailed": "Сохранено только в этом браузере: копию не удалось отправить. Продолжайте и сохраните JSON в конце.",
376
+ "otherTab": "Этот прогон открыт и в другой вкладке или окне: побеждает последнее изменение, из какой бы вкладки оно ни пришло.",
377
+ "otherRevision": "ответы даны на другую редакцию этого сценария",
378
+ "continueHere": "Продолжить в этой редакции — неизменённые строки сохраняют ответы, на изменённые нужно ответить заново",
379
+ "saveThatJson": "Сохранить его JSON",
380
+ "carried": "Перенесено ответов: {kept}, осталось строк: {left} — прежний прогон остаётся сохранённым без изменений.",
381
+ "draftUnreadable": "Прогон, сохранённый в этом браузере, не читается. Он не удалён: сохраните его и передайте агенту.",
382
+ "saveRaw": "Сохранить нечитаемые данные",
383
+ "another": "Начать ещё один прогон — этот остаётся сохранённым",
384
+ "finishedNote": "Прогон завершён. Это не приёмка: агент читает каждый ответ и классифицирует каждое несоответствие, прежде чем что-либо решать.",
385
+ "finishAnyway": "Всё равно завершить",
386
+ "skipped": "Пропущен",
387
+ "blockedWhy": "Что вам помешало?",
388
+ "savedAs": "Сохранено как {file}",
389
+ "downloadFailed": "Не удалось сохранить файл — используйте «Скопировать JSON».",
390
+ "changedAfterFinish": "Изменено после завершения: прогон сохраняет дату завершения, а агент видит более позднее изменение."
265
391
  },
266
392
  "zh": {
267
393
  "name": "中文 (简体)",
@@ -295,7 +421,7 @@
295
421
  "intended": "这是有意为之。",
296
422
  "savedLocal": "已保存在此浏览器 — 结束时记得保存 JSON",
297
423
  "savedRemote": "已保存 ·",
298
- "localMode": "本页是本地文件:结束时请保存 JSON 并把文件交出去 — 代理读取的正是这个文件。",
424
+ "localMode": "你的回答会在每次勾选时保存在此浏览器、此设备中,重新加载页面后会恢复。清除站点数据或关闭无痕窗口可能使其消失,也没有人会收到它们:结束时请保存 JSON 并把文件交出去——代理读取的正是这个文件。",
299
425
  "dbMode": "你的回答会即时保存,代理直接读取,你无需发送任何东西。",
300
426
  "serveMode": "你的每次勾选都会直接保存到项目中,你无需发送任何东西。",
301
427
  "startFirst": "请先输入名字并开始验收——在此之前不会保存任何内容。",
@@ -305,7 +431,28 @@
305
431
  "finishedOn": "完成于 {date}",
306
432
  "countSeen": "看到",
307
433
  "countNotSeen": "没看到",
308
- "countBlocked": "无法进行"
434
+ "countBlocked": "无法进行",
435
+ "countAnswered": "已作答",
436
+ "progressLabel": "已作答的行",
437
+ "percentAnswered": "已作答 {n}%",
438
+ "saving": "保存中…",
439
+ "saveFailed": "无法保存在此浏览器中——离开或重新加载页面之前,请先保存或复制 JSON。",
440
+ "remoteFailed": "仅保存在此浏览器中:副本未能发送。请继续,并在结束时保存 JSON。",
441
+ "otherTab": "此验收也在另一个标签页或窗口中打开:无论来自哪个标签页,最后一次更改生效。",
442
+ "otherRevision": "回答的是此验收的另一个版本",
443
+ "continueHere": "在此版本上继续——未更改的行保留回答,已更改的行需重新作答",
444
+ "saveThatJson": "保存它的 JSON",
445
+ "carried": "已保留 {kept} 条回答,还有 {left} 行待作答——先前的验收原样保存。",
446
+ "draftUnreadable": "此浏览器中保存的一次验收无法读取。它没有被删除:请保存并交给代理。",
447
+ "saveRaw": "保存无法读取的数据",
448
+ "another": "开始另一次验收——本次会保留",
449
+ "finishedNote": "验收已结束。这不等于通过:代理会先读取每条回答并归类每个失败项,然后才做决定。",
450
+ "finishAnyway": "仍然结束",
451
+ "skipped": "已跳过",
452
+ "blockedWhy": "是什么阻止了你?",
453
+ "savedAs": "已保存为 {file}",
454
+ "downloadFailed": "无法保存文件——请改用“复制 JSON”。",
455
+ "changedAfterFinish": "结束后有修改:本次验收保留结束日期,代理会看到之后的修改。"
309
456
  },
310
457
  "he": {
311
458
  "name": "עברית",
@@ -339,7 +486,7 @@
339
486
  "intended": "זה מכוון.",
340
487
  "savedLocal": "נשמר בדפדפן הזה — זכור לשמור את ה‑JSON בסוף",
341
488
  "savedRemote": "נשמר ·",
342
- "localMode": "הדף הזה הוא קובץ מקומי: בסוף שמור את ה‑JSON והעבר את הקובץ — זה מה שהסוכן קורא.",
489
+ "localMode": "התשובות שלך נשמרות בדפדפן הזה, במכשיר הזה, בכל סימון, וחוזרות כשהדף נטען מחדש. הן עלולות להיעלם אם נתוני האתר נמחקים או חלון פרטי נסגר, ואף אחד לא מקבל אותן: בסוף שמור את ה‑JSON והעבר את הקובץ — זה מה שהסוכן קורא.",
343
490
  "dbMode": "התשובות נשמרות תוך כדי, והסוכן קורא אותן ישירות. אין מה לשלוח.",
344
491
  "serveMode": "כל סימון נשמר ישירות בפרויקט. אין מה לשלוח.",
345
492
  "startFirst": "קודם כתוב את שמך והתחל הרצה — לפני כן שום דבר לא נשמר.",
@@ -349,7 +496,28 @@
349
496
  "finishedOn": "הסתיימה ב-{date}",
350
497
  "countSeen": "נראו",
351
498
  "countNotSeen": "לא נראו",
352
- "countBlocked": "חסומים"
499
+ "countBlocked": "חסומים",
500
+ "countAnswered": "נענו",
501
+ "progressLabel": "שורות שנענו",
502
+ "percentAnswered": "{n}% נענו",
503
+ "saving": "שומר…",
504
+ "saveFailed": "לא ניתן לשמור בדפדפן הזה — שמור או העתק את ה‑JSON לפני שתעזוב או תטען מחדש את הדף.",
505
+ "remoteFailed": "נשמר בדפדפן הזה בלבד: לא ניתן היה לשלוח את העותק. המשך, ושמור את ה‑JSON בסוף.",
506
+ "otherTab": "ההרצה הזו פתוחה גם בלשונית או בחלון אחר: השינוי האחרון גובר, מאיזו לשונית שלא יגיע.",
507
+ "otherRevision": "הרצה על גרסה אחרת של התסריט הזה",
508
+ "continueHere": "להמשיך בגרסה הזו — שורות שלא השתנו שומרות את התשובות שלהן, שורות שהשתנו נשאלות שוב",
509
+ "saveThatJson": "לשמור את ה‑JSON שלה",
510
+ "carried": "{kept} תשובות הועברו, {left} שורות לענות — ההרצה הקודמת נשארת שמורה כפי שהייתה.",
511
+ "draftUnreadable": "הרצה שנשמרה בדפדפן הזה אינה ניתנת לקריאה. היא לא נמחקה: שמור אותה והעבר אותה לסוכן.",
512
+ "saveRaw": "לשמור את הנתונים הבלתי קריאים",
513
+ "another": "להתחיל הרצה נוספת — זו נשארת שמורה",
514
+ "finishedNote": "ההרצה הסתיימה. זה לא אישור: הסוכן קורא כל תשובה ומסווג כל כישלון לפני שמחליטים משהו.",
515
+ "finishAnyway": "לסיים בכל זאת",
516
+ "skipped": "דולג",
517
+ "blockedWhy": "מה עצר אותך?",
518
+ "savedAs": "נשמר בשם {file}",
519
+ "downloadFailed": "לא ניתן היה לשמור את הקובץ — השתמש ב‑\"להעתיק את ה‑JSON\".",
520
+ "changedAfterFinish": "שונה אחרי הסיום: ההרצה שומרת את תאריך הסיום שלה, והסוכן רואה את השינוי המאוחר."
353
521
  },
354
522
  "ja": {
355
523
  "name": "日本語",
@@ -383,7 +551,7 @@
383
551
  "intended": "意図した動作です。",
384
552
  "savedLocal": "このブラウザに保存しました — 最後に JSON の保存を忘れずに",
385
553
  "savedRemote": "保存済み ·",
386
- "localMode": "このページはローカルファイルです。最後に JSON を保存してファイルを渡してください — エージェントが読むのはそれです。",
554
+ "localMode": "回答はチェックのたびにこのブラウザ、この端末に保存され、ページを再読み込みすると復元されます。サイトデータを消去したりプライベートウィンドウを閉じたりすると消えることがあり、誰にも送られません。最後に JSON を保存してファイルを渡してください。エージェントが読むのはそれです。",
387
555
  "dbMode": "回答は随時保存され、エージェントが直接読みます。送信は不要です。",
388
556
  "serveMode": "チェックするたびに回答がプロジェクトへ直接保存されます。送信は不要です。",
389
557
  "startFirst": "まず名前を入力して実施を開始してください。それまでは何も保存されません。",
@@ -393,7 +561,28 @@
393
561
  "finishedOn": "{date} に終了",
394
562
  "countSeen": "見えた",
395
563
  "countNotSeen": "見えない",
396
- "countBlocked": "実施不可"
564
+ "countBlocked": "実施不可",
565
+ "countAnswered": "回答済み",
566
+ "progressLabel": "回答済みの行",
567
+ "percentAnswered": "{n}% 回答済み",
568
+ "saving": "保存中…",
569
+ "saveFailed": "このブラウザに保存できません。ページを離れたり再読み込みしたりする前に、JSON を保存またはコピーしてください。",
570
+ "remoteFailed": "このブラウザにのみ保存されました。コピーを送信できませんでした。続行し、最後に JSON を保存してください。",
571
+ "otherTab": "この実施は別のタブまたはウィンドウでも開かれています。どのタブからでも、最後の変更が優先されます。",
572
+ "otherRevision": "この手順書の別の版に回答したものです",
573
+ "continueHere": "この版で続ける — 変更のない行は回答を引き継ぎ、変更された行は再度回答が必要です",
574
+ "saveThatJson": "その JSON を保存する",
575
+ "carried": "{kept} 件の回答を引き継ぎ、{left} 行が未回答です。以前の実施はそのまま保存されています。",
576
+ "draftUnreadable": "このブラウザに保存された実施を読み取れません。削除はしていません。保存してエージェントに渡してください。",
577
+ "saveRaw": "読み取れないデータを保存する",
578
+ "another": "別の実施を開始する — この実施は保存されたままです",
579
+ "finishedNote": "実施を終了しました。これで受け入れが確定するわけではありません。エージェントがすべての回答を読み、各失敗を分類してから判断します。",
580
+ "finishAnyway": "このまま終了する",
581
+ "skipped": "スキップ",
582
+ "blockedWhy": "何に妨げられましたか?",
583
+ "savedAs": "{file} として保存しました",
584
+ "downloadFailed": "ファイルを保存できませんでした。「JSON をコピーする」を使ってください。",
585
+ "changedAfterFinish": "終了後に変更されました。実施は終了日を保持し、エージェントは後の変更を確認します。"
397
586
  },
398
587
  "it": {
399
588
  "name": "Italiano",
@@ -427,7 +616,7 @@
427
616
  "intended": "È voluto.",
428
617
  "savedLocal": "Salvato in questo browser — ricordati di salvare il JSON alla fine",
429
618
  "savedRemote": "Salvato ·",
430
- "localMode": "Questa pagina è un file locale: alla fine salva il JSON e consegna il file — è quello che legge l'agente.",
619
+ "localMode": "Le tue risposte restano in questo browser, su questo dispositivo, a ogni spunta, e tornano quando la pagina viene ricaricata. Possono sparire se i dati del sito vengono cancellati o si chiude una finestra privata, e nessuno le riceve: alla fine salva il JSON e consegna il file — è quello che legge l'agente.",
431
620
  "dbMode": "Le tue risposte vengono salvate man mano; l'agente le legge direttamente, non devi inviare nulla.",
432
621
  "serveMode": "Le tue risposte vengono salvate direttamente nel progetto a ogni spunta; non devi inviare nulla.",
433
622
  "startFirst": "Scrivi il tuo nome e inizia prima la sessione: prima non viene salvato nulla.",
@@ -437,6 +626,27 @@
437
626
  "finishedOn": "terminata il {date}",
438
627
  "countSeen": "visti",
439
628
  "countNotSeen": "non visti",
440
- "countBlocked": "bloccati"
629
+ "countBlocked": "bloccati",
630
+ "countAnswered": "con risposta",
631
+ "progressLabel": "Righe con risposta",
632
+ "percentAnswered": "{n}% con risposta",
633
+ "saving": "Salvataggio…",
634
+ "saveFailed": "Impossibile salvare in questo browser: salva o copia il JSON prima di lasciare o ricaricare la pagina.",
635
+ "remoteFailed": "Salvato solo in questo browser: la copia non è stata inviata. Continua e salva il JSON alla fine.",
636
+ "otherTab": "Questa sessione è aperta anche in un'altra scheda o finestra: vince l'ultima modifica, da qualunque scheda arrivi.",
637
+ "otherRevision": "ha risposto a un'altra versione di questa procedura",
638
+ "continueHere": "Continuare su questa versione: le righe invariate conservano le risposte, quelle modificate vengono chieste di nuovo",
639
+ "saveThatJson": "Salvare il suo JSON",
640
+ "carried": "{kept} risposta/e conservata/e, {left} riga/righe da compilare — la sessione precedente resta salvata com'era.",
641
+ "draftUnreadable": "Una sessione salvata in questo browser non può essere letta. Non è stata cancellata: salvala e consegnala all'agente.",
642
+ "saveRaw": "Salvare i dati illeggibili",
643
+ "another": "Iniziare un'altra sessione: questa resta salvata",
644
+ "finishedNote": "Sessione terminata. Non è un'accettazione: l'agente legge ogni risposta e classifica ogni anomalia prima di qualsiasi decisione.",
645
+ "finishAnyway": "Termina comunque",
646
+ "skipped": "Saltato",
647
+ "blockedWhy": "Che cosa ti ha bloccato?",
648
+ "savedAs": "Salvato come {file}",
649
+ "downloadFailed": "Impossibile salvare il file: usa «Copiare il JSON».",
650
+ "changedAfterFinish": "Modificata dopo la fine: la sessione conserva la data di fine e l'agente vede la modifica successiva."
441
651
  }
442
652
  }
@@ -119,6 +119,7 @@ for (const modulePath of [
119
119
  './commands/studio',
120
120
  './commands/nexus',
121
121
  './commands/uat',
122
+ './commands/review',
122
123
  ]) {
123
124
  const command = require(modulePath);
124
125
  const configured = program.command(command.command).description(command.description);
@@ -0,0 +1,152 @@
1
+ /** Code review evidence: seal the scope, anchor the findings, derive the verdict from coverage. */
2
+ 'use strict';
3
+
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+ const review = require('../lib/review');
7
+
8
+ const ID = /^[a-z0-9][a-z0-9.-]{0,80}$/;
9
+
10
+ function fail(message) {
11
+ throw new Error(message);
12
+ }
13
+
14
+ function readJson(file, what) {
15
+ if (!fs.existsSync(file)) return null;
16
+ try {
17
+ // PowerShell 5.1 writes UTF-8 with a byte-order mark; JSON.parse refuses it.
18
+ return JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, ''));
19
+ } catch (error) {
20
+ fail(`${what} is not valid JSON (${error.message})`);
21
+ return null;
22
+ }
23
+ }
24
+
25
+ function print(json, payload, lines) {
26
+ if (json) console.log(JSON.stringify({ schemaVersion: 1, ...payload }, null, 2));
27
+ else for (const line of lines) console.log(line);
28
+ }
29
+
30
+ module.exports = {
31
+ command: 'review <action> [id]',
32
+ description: 'Code review evidence: scope, anchor, gate',
33
+ options: [
34
+ ['-d, --directory <path>', 'Project directory'],
35
+ ['--dir <path>', 'Review folder inside the project', review.DEFAULT_DIR],
36
+ ['--base <ref>', 'Base commit or branch (default HEAD~1)'],
37
+ ['--head <ref>', 'Head commit or branch (default HEAD)'],
38
+ ['--workspace', 'Review the working tree, untracked files included, against the base'],
39
+ ['--include <glob>', 'Only review matching paths (repeatable)', (v, all) => [...all, v], []],
40
+ ['--exclude <glob>', 'Never review matching paths (repeatable)', (v, all) => [...all, v], []],
41
+ ['--json', 'Machine-readable output'],
42
+ ],
43
+ action: (action, id, options = {}) => {
44
+ const projectDir = path.resolve(options.directory || process.cwd());
45
+ const json = Boolean(options.json);
46
+ try {
47
+ if (!id || !ID.test(id))
48
+ fail('a review id is required: lowercase letters, digits, dots and dashes');
49
+ if (!['scope', 'anchor', 'gate'].includes(action))
50
+ fail(`unknown action "${action}" (scope, anchor, gate)`);
51
+ const paths = review.layout(projectDir, options.dir || review.DEFAULT_DIR, id);
52
+
53
+ if (action === 'scope') {
54
+ const scope = review.buildScope(projectDir, {
55
+ id,
56
+ base: options.base,
57
+ head: options.head,
58
+ workspace: Boolean(options.workspace),
59
+ include: options.include,
60
+ exclude: options.exclude,
61
+ });
62
+ fs.mkdirSync(paths.root, { recursive: true });
63
+ fs.writeFileSync(paths.scope, `${JSON.stringify(scope, null, 2)}\n`);
64
+ const reasons = {};
65
+ for (const item of scope.excluded) reasons[item.reason] = (reasons[item.reason] || 0) + 1;
66
+ print(
67
+ json,
68
+ {
69
+ action,
70
+ id,
71
+ file: paths.scope,
72
+ totals: scope.totals,
73
+ units: scope.units.length,
74
+ excluded: reasons,
75
+ sha256: scope.sha256,
76
+ },
77
+ [
78
+ `${paths.scope}`,
79
+ `${scope.totals.selected} file(s) to review, ${scope.totals.lines} changed lines, ${scope.units.length} unit(s) — scope ${scope.sha256.slice(0, 12)}`,
80
+ ...Object.entries(reasons).map(([reason, n]) => ` excluded ${n} × ${reason}`),
81
+ 'Every selected file must end in coverage.json as completed, failed or waived (with a reason).',
82
+ ]
83
+ );
84
+ return;
85
+ }
86
+
87
+ const scope = readJson(paths.scope, 'scope.json');
88
+ if (!scope) fail(`no scope at ${paths.scope} — run "bmad-plus review scope ${id}" first`);
89
+
90
+ if (action === 'anchor') {
91
+ const findings = readJson(paths.findings, 'findings.json');
92
+ if (!findings) fail(`no findings at ${paths.findings}`);
93
+ const errors = review.validateFindings(findings, scope);
94
+ if (errors.length) {
95
+ print(
96
+ json,
97
+ { action, id, status: 'error', errors },
98
+ errors.map((e) => ` error ${e}`)
99
+ );
100
+ process.exitCode = 1;
101
+ return;
102
+ }
103
+ const anchored = review.anchorFindings(projectDir, scope, findings);
104
+ fs.writeFileSync(
105
+ paths.anchored,
106
+ `${JSON.stringify({ schema: review.FINDINGS_SCHEMA, scopeSha256: scope.sha256, ...anchored }, null, 2)}\n`
107
+ );
108
+ const loose = anchored.findings.filter((f) => f.location.status !== 'located');
109
+ print(json, { action, id, file: paths.anchored, counts: anchored.counts }, [
110
+ `${paths.anchored} — ${anchored.counts.located} located, ${anchored.counts.ambiguous} ambiguous, ${anchored.counts.unlocated} unlocated`,
111
+ ...loose.map(
112
+ (f) =>
113
+ ` ${f.location.status.padEnd(9)} ${f.id} ${f.path}: quote a longer, exact excerpt`
114
+ ),
115
+ ]);
116
+ process.exitCode = loose.length ? 1 : 0;
117
+ return;
118
+ }
119
+
120
+ if (action === 'gate') {
121
+ const findings = readJson(paths.findings, 'findings.json');
122
+ const coverage = readJson(paths.coverage, 'coverage.json');
123
+ let anchored = null;
124
+ if (findings && !review.validateFindings(findings, scope).length)
125
+ anchored = review.anchorFindings(projectDir, scope, findings);
126
+ const verdict = review.reviewGate({ scope, findings, coverage, anchored });
127
+ print(
128
+ json,
129
+ { action, id, ...verdict },
130
+ [
131
+ `${id}: ${verdict.status} — coverage ${verdict.coverage.completed}/${verdict.coverage.selected} completed (${verdict.coverage.waived} waived), ${verdict.findings.open} open finding(s), ${verdict.findings.refuted} refuted`,
132
+ ...verdict.reasons.map((reason) => ` - ${reason}`),
133
+ verdict.status === 'clean'
134
+ ? ' clean: no open finding within a fully covered scope. It does not prove the code correct.'
135
+ : '',
136
+ ].filter(Boolean)
137
+ );
138
+ process.exitCode = { clean: 0, findings: 1, incomplete: 2 }[verdict.status];
139
+ return;
140
+ }
141
+
142
+ fail(`unknown action "${action}" (scope, anchor, gate)`);
143
+ } catch (error) {
144
+ if (json)
145
+ console.log(
146
+ JSON.stringify({ schemaVersion: 1, status: 'error', message: error.message }, null, 2)
147
+ );
148
+ else console.error(`review: ${error.message}`);
149
+ process.exitCode = 3;
150
+ }
151
+ },
152
+ };