@orkastery/cli 0.5.1 → 0.5.6

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.
Files changed (260) hide show
  1. package/adapters/claude-code/README.md +158 -0
  2. package/adapters/claude-code/agents/ork-master.md +2 -0
  3. package/adapters/claude-code/commands/master.md +8 -0
  4. package/adapters/claude-code/commands/ork.md +9 -0
  5. package/adapters/claude-code/mods/autoridade.json +56 -0
  6. package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
  7. package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
  8. package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
  9. package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
  10. package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
  11. package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
  12. package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
  13. package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
  14. package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
  15. package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
  16. package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
  17. package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
  18. package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
  19. package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
  20. package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
  21. package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
  22. package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
  23. package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
  24. package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
  25. package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
  26. package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
  27. package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
  28. package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
  29. package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
  30. package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
  31. package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
  32. package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
  33. package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
  34. package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
  35. package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
  36. package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
  37. package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
  38. package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
  39. package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
  40. package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
  41. package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
  42. package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
  43. package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
  44. package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
  45. package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
  46. package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
  47. package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
  48. package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
  49. package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
  50. package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
  51. package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
  52. package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
  53. package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
  54. package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
  55. package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
  56. package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
  57. package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
  58. package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
  59. package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
  60. package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
  61. package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
  62. package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
  63. package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
  64. package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
  65. package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
  66. package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
  67. package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
  68. package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
  69. package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
  70. package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
  71. package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
  72. package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
  73. package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
  74. package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
  75. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
  76. package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
  77. package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
  78. package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
  79. package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
  80. package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
  81. package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
  82. package/adapters/codex/skills/ork/SKILL.md +18 -3
  83. package/adapters/hermes/README.md +1 -0
  84. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  85. package/adapters/hermes/hermes.plugin.json +1 -0
  86. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  87. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +7 -6
  88. package/adapters/openclaw/README.md +26 -5
  89. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  90. package/adapters/openclaw/dist/index.js +46 -11
  91. package/adapters/openclaw/openclaw.plugin.json +5 -1
  92. package/adapters/openclaw/package.json +2 -2
  93. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  94. package/adapters/openclaw/src/index.ts +49 -11
  95. package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
  96. package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
  97. package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
  98. package/assets/docs-en/produto/README.md +14 -0
  99. package/assets/docs-en/produto/_modelo-feature.md +62 -0
  100. package/assets/docs-en/roadmap/README.md +14 -0
  101. package/assets/docs-en/roadmap/_modelo-item.md +75 -0
  102. package/assets/orkmind_bridge.py +117 -9
  103. package/dist/adapters/claude-bg.js +116 -18
  104. package/dist/adapters/codex.js +14 -4
  105. package/dist/agents-md.js +24 -14
  106. package/dist/auditrun.js +22 -3
  107. package/dist/board.js +20 -17
  108. package/dist/branch-de-estado.js +88 -10
  109. package/dist/busca-semantica.js +52 -12
  110. package/dist/canarios-rm057.js +149 -0
  111. package/dist/canarios.js +2 -0
  112. package/dist/catalogo.js +33 -0
  113. package/dist/ci.js +21 -8
  114. package/dist/claims-conferir.js +342 -0
  115. package/dist/claims.js +41 -1
  116. package/dist/company-brain-client.js +36 -1
  117. package/dist/company-brain-context.js +158 -22
  118. package/dist/company-brain-journal.js +2 -1
  119. package/dist/company-brain-migration.js +36 -1
  120. package/dist/conducao-sentinela.js +388 -0
  121. package/dist/conducao-texto.js +12 -18
  122. package/dist/conducao.js +122 -30
  123. package/dist/conhecimento.js +490 -0
  124. package/dist/docs.js +70 -17
  125. package/dist/doctor-pulse-cron.js +349 -0
  126. package/dist/doctor-rede.js +150 -0
  127. package/dist/doctor.js +195 -111
  128. package/dist/entrega-pr.js +42 -29
  129. package/dist/escopo-escrita.js +10 -1
  130. package/dist/esgotamentos.js +181 -0
  131. package/dist/estado-thread.js +23 -1
  132. package/dist/experiencia-instalacao.js +45 -25
  133. package/dist/experiencia.js +12 -2
  134. package/dist/fabrica-batida.js +310 -0
  135. package/dist/fabrica-estado.js +52 -14
  136. package/dist/fabrica-publicar.js +9 -1
  137. package/dist/fechamento.js +20 -66
  138. package/dist/fila-pesada.js +444 -0
  139. package/dist/fix.js +3 -2
  140. package/dist/forja.js +31 -7
  141. package/dist/gates.js +3 -24
  142. package/dist/handoff.js +10 -6
  143. package/dist/hitl-contract.js +59 -4
  144. package/dist/hitl-curto.js +11 -6
  145. package/dist/hitl-gates.js +5 -0
  146. package/dist/hitl-local-receipt.js +2 -0
  147. package/dist/hitl-lote.js +5 -2
  148. package/dist/hitl-resumo.js +38 -3
  149. package/dist/hitl-tempo-parado.js +201 -0
  150. package/dist/hitl-texto-livre.js +5 -5
  151. package/dist/hitl.js +29 -33
  152. package/dist/horario.js +41 -7
  153. package/dist/hosts.js +104 -11
  154. package/dist/impedimento.js +299 -0
  155. package/dist/index.js +961 -542
  156. package/dist/indice-vetorial.js +87 -10
  157. package/dist/indice.js +9 -1
  158. package/dist/init.js +56 -43
  159. package/dist/instabilidade.js +144 -0
  160. package/dist/integracoes-locais.js +4 -0
  161. package/dist/intelligence-graph-cli.js +170 -16
  162. package/dist/intelligence-graph-contexto.js +254 -0
  163. package/dist/intelligence-graph-contract.js +2 -1
  164. package/dist/intelligence-graph-extract-md.js +56 -4
  165. package/dist/intelligence-graph-extract-ts.js +71 -3
  166. package/dist/intelligence-graph-parsers.js +6 -4
  167. package/dist/intelligence-graph-query.js +66 -19
  168. package/dist/leases.js +664 -87
  169. package/dist/ledger-stats.js +15 -1
  170. package/dist/ledger.js +18 -1
  171. package/dist/licoes.js +1 -0
  172. package/dist/liveness.js +6 -3
  173. package/dist/locale-ajuda.js +761 -0
  174. package/dist/locale-gerados.js +160 -0
  175. package/dist/locale-master.js +99 -0
  176. package/dist/locale-onboarding.js +92 -0
  177. package/dist/locale-phase.js +151 -0
  178. package/dist/locale-pulse.js +211 -0
  179. package/dist/locale-retry.js +385 -0
  180. package/dist/locale-roadmap.js +102 -0
  181. package/dist/locale-ship.js +494 -0
  182. package/dist/locale-thread.js +277 -0
  183. package/dist/locale-verify.js +125 -0
  184. package/dist/locale.js +714 -0
  185. package/dist/maestro-runtime.js +4 -1
  186. package/dist/manifest.js +149 -4
  187. package/dist/maquina.js +23 -4
  188. package/dist/master-digest.js +6 -1
  189. package/dist/master-nota.js +18 -11
  190. package/dist/master.js +145 -31
  191. package/dist/mcp-artifacts.js +43 -3
  192. package/dist/mcp-git.js +42 -8
  193. package/dist/mcp-grafo-worker.js +5 -3
  194. package/dist/mcp-grafo.js +55 -6
  195. package/dist/mcp-server.js +22 -3
  196. package/dist/mcp-ship.js +3 -1
  197. package/dist/mcp-verify.js +6 -1
  198. package/dist/memoria.js +48 -6
  199. package/dist/modos.js +3 -6
  200. package/dist/network-roadmap.js +196 -18
  201. package/dist/noctum-arco.js +911 -0
  202. package/dist/noctum-capacidade.js +141 -0
  203. package/dist/noctum-envelope.js +810 -0
  204. package/dist/noctum-execucao.js +304 -0
  205. package/dist/noctum-fila.js +280 -0
  206. package/dist/ocupacao.js +40 -5
  207. package/dist/onboarding.js +40 -21
  208. package/dist/orkmind.js +99 -7
  209. package/dist/orquestracao.js +24 -21
  210. package/dist/parado-no-condutor.js +219 -38
  211. package/dist/phase.js +160 -52
  212. package/dist/policies.js +56 -5
  213. package/dist/portfolio.js +8 -3
  214. package/dist/postura-local.js +234 -0
  215. package/dist/preflight.js +11 -10
  216. package/dist/procedencia.js +225 -0
  217. package/dist/projeto-alvo.js +132 -5
  218. package/dist/prompts.js +40 -1
  219. package/dist/prova-ativacao.js +75 -6
  220. package/dist/pulse-delivery.js +47 -4
  221. package/dist/pulse.js +98 -19
  222. package/dist/ratelimit.js +24 -6
  223. package/dist/recall.js +3 -1
  224. package/dist/redacao-saida.js +63 -0
  225. package/dist/rede-adesao.js +399 -0
  226. package/dist/rede-forja.js +289 -0
  227. package/dist/rede-projetos.js +239 -0
  228. package/dist/rede-status.js +337 -0
  229. package/dist/rede.js +980 -0
  230. package/dist/retry.js +291 -140
  231. package/dist/roadmap-reservas.js +20 -3
  232. package/dist/roadmap-status.js +150 -34
  233. package/dist/runtime-context.js +181 -3
  234. package/dist/runtime-profiles.js +22 -1
  235. package/dist/runtimes.js +2 -1
  236. package/dist/saida-segura.js +58 -0
  237. package/dist/session-events.js +7 -3
  238. package/dist/session-watcher-claude.js +120 -24
  239. package/dist/session-watcher.js +185 -33
  240. package/dist/sessoes-contas.js +187 -0
  241. package/dist/sessoes-inventario.js +6 -18
  242. package/dist/sessoes.js +83 -2
  243. package/dist/ship-publicar.js +800 -0
  244. package/dist/ship.js +157 -88
  245. package/dist/steal.js +77 -0
  246. package/dist/thread-close.js +3 -2
  247. package/dist/thread.js +299 -49
  248. package/dist/util.js +8 -1
  249. package/dist/verify.js +141 -52
  250. package/dist/vida-da-thread.js +254 -0
  251. package/dist/worktree-local.js +165 -0
  252. package/dist/worktree.js +25 -2
  253. package/dist/yaml.js +27 -8
  254. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  255. package/package.json +7 -3
  256. package/references/definition-of-done.md +1 -1
  257. package/schemas/code-artifact-graph.v1.schema.json +2 -1
  258. package/skills/README.md +1 -1
  259. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  260. package/skills/phases/master-metrics/SKILL.md +8 -3
package/dist/thread.js CHANGED
@@ -39,6 +39,7 @@ var __importStar = (this && this.__importStar) || (function () {
39
39
  };
40
40
  })();
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.TETO_DO_PEDIDO = exports.ARQUIVO_DO_PEDIDO = void 0;
42
43
  exports.dirThreads = dirThreads;
43
44
  exports.dirThread = dirThread;
44
45
  exports.caminhoThread = caminhoThread;
@@ -47,7 +48,18 @@ exports.lerThread = lerThread;
47
48
  exports.gravarThread = gravarThread;
48
49
  exports.avisoDeThreadSemBase = avisoDeThreadSemBase;
49
50
  exports.idDisponivel = idDisponivel;
51
+ exports.pastaDaWorktree = pastaDaWorktree;
52
+ exports.alvoDaWorktree = alvoDaWorktree;
53
+ exports.worktreePrevista = worktreePrevista;
50
54
  exports.criarWorktree = criarWorktree;
55
+ exports.pedidoDeWorktree = pedidoDeWorktree;
56
+ exports.linhaDaWorktree = linhaDaWorktree;
57
+ exports.avisoDeThreadSemWorktree = avisoDeThreadSemWorktree;
58
+ exports.avisoDeWorktreeQueFalharia = avisoDeWorktreeQueFalharia;
59
+ exports.avisoDeChaveSemCommit = avisoDeChaveSemCommit;
60
+ exports.avisoDaWorktree = avisoDaWorktree;
61
+ exports.prepararPedido = prepararPedido;
62
+ exports.pedidoDaThread = pedidoDaThread;
51
63
  exports.novaThread = novaThread;
52
64
  exports.exigirVarianteCompativel = exigirVarianteCompativel;
53
65
  exports.blocoDaThread = blocoDaThread;
@@ -58,12 +70,14 @@ exports.resumoDaThread = resumoDaThread;
58
70
  exports.threadsDaListagem = threadsDaListagem;
59
71
  exports.tabelaDeThreads = tabelaDeThreads;
60
72
  exports.exigirFase = exigirFase;
73
+ const worktree_local_1 = require("./worktree-local");
61
74
  const fs = __importStar(require("node:fs"));
62
75
  const path = __importStar(require("node:path"));
63
76
  const ciclos_1 = require("./ciclos");
64
77
  const modos_1 = require("./modos");
65
78
  const manifest_1 = require("./manifest");
66
79
  const slug_1 = require("./slug");
80
+ const node_crypto_1 = require("node:crypto");
67
81
  const types_1 = require("./types");
68
82
  const util_1 = require("./util");
69
83
  const ledger_1 = require("./ledger");
@@ -72,6 +86,7 @@ const creation_operation_store_1 = require("./creation-operation-store");
72
86
  const horario_1 = require("./horario");
73
87
  const maquina_1 = require("./maquina");
74
88
  const conducao_texto_1 = require("./conducao-texto");
89
+ const locale_1 = require("./locale");
75
90
  /** Diretorio de threads do projeto. */
76
91
  function dirThreads(raiz) {
77
92
  return path.join((0, manifest_1.dirEstado)((0, estado_thread_1.raizDoEstado)(raiz)), 'threads');
@@ -99,7 +114,7 @@ function listarIds(raiz) {
99
114
  function lerThread(raiz, id) {
100
115
  const caminho = caminhoThread(raiz, id);
101
116
  if (!fs.existsSync(caminho)) {
102
- throw new Error(`thread "${id}" nao encontrada em ${dirThreads(raiz)}`);
117
+ throw new locale_1.ErroDoCatalogo((m) => m.thread.naoEncontrada(id, dirThreads(raiz)));
103
118
  }
104
119
  return (0, util_1.lerJson)(caminho);
105
120
  }
@@ -124,11 +139,7 @@ function carimbarBase(raiz) {
124
139
  function avisoDeThreadSemBase(thread, gravada) {
125
140
  if (thread.base.commit !== util_1.COMMIT_DESCONHECIDO)
126
141
  return null;
127
- return gravada
128
- ? `Aviso: o repositório ainda não tem commit, e a thread ${thread.id} nasceu sem base: o ship não tem de onde partir. ` +
129
- `Faça o primeiro commit, feche esta com ork thread close ${thread.id} --motivo engano --por <quem> ` +
130
- '--justificativa "aberta antes do primeiro commit" e abra outra.'
131
- : 'Aviso: o repositório ainda não tem commit, e a thread nasceria sem base: faça o primeiro commit antes de abrir a primeira thread.';
142
+ return (0, locale_1.msg)().thread.semBase(thread.id, gravada);
132
143
  }
133
144
  /** Resolve um id livre a partir do assunto normalizado (`<abbrev>-<assunto>`). */
134
145
  function idDisponivel(raiz, abbrev, assunto) {
@@ -141,6 +152,43 @@ function idDisponivel(raiz, abbrev, assunto) {
141
152
  }
142
153
  return { id: `${abbrev}-${candidato}`, assunto: candidato };
143
154
  }
155
+ /**
156
+ * Pasta da worktree de uma thread: `worktree.dir` resolvido na arvore principal, nunca na worktree de onde o comando
157
+ * roda. P4 do ensaio da 0.5.0 (CHECK): com a chave valendo, o `ork thread new` rodado de dentro da worktree de outra
158
+ * thread aninhava a nova na pasta da mae, e o release da mae levava a da filha junto.
159
+ */
160
+ function pastaDaWorktree(carregado, id) {
161
+ return path.resolve((0, estado_thread_1.raizDoEstado)(carregado.raiz), carregado.manifesto.worktree.dir, id);
162
+ }
163
+ /** Pasta e branch da worktree de uma thread: o que `criarWorktree` cria e o `--dry-run` preve. */
164
+ function alvoDaWorktree(carregado, id, slug, opcoes = {}) {
165
+ return { dir: pastaDaWorktree(carregado, id), branch: opcoes.branchExistente ?? `ork/${slug}` };
166
+ }
167
+ /**
168
+ * P4 do ensaio da 0.5.0: a worktree que `criarWorktree` criaria para este id, sem criar nada. Mesma pasta, mesma
169
+ * branch e o commit de onde ela partiria: a branch existente (ciclo `merge-branch`), senao a base do manifesto,
170
+ * senao o HEAD. Diz tambem o que faria a criacao recusar: a pasta que ja existe, o repositorio sem commit ou a
171
+ * branch nova que ja existe.
172
+ */
173
+ function worktreePrevista(carregado, id, slug, opcoes = {}) {
174
+ const alvo = alvoDaWorktree(carregado, id, slug, opcoes);
175
+ const refs = opcoes.branchExistente
176
+ ? [opcoes.branchExistente]
177
+ : [opcoes.base ?? carregado.manifesto.worktree.base_branch, 'HEAD'];
178
+ let commit = null;
179
+ for (const ref of refs) {
180
+ const r = (0, util_1.exec)('git', ['rev-parse', '--verify', ref], carregado.raiz);
181
+ if (r.ok) {
182
+ commit = r.stdout.trim();
183
+ break;
184
+ }
185
+ }
186
+ const branchJaExiste = !opcoes.branchExistente &&
187
+ (0, util_1.exec)('git', ['rev-parse', '--verify', '--quiet', `refs/heads/${alvo.branch}`], carregado.raiz).ok;
188
+ const falha = fs.existsSync(alvo.dir) ? 'pasta-existe'
189
+ : commit === null ? 'sem-commit' : branchJaExiste ? 'branch-existe' : null;
190
+ return { ...alvo, commit, falha };
191
+ }
144
192
  /**
145
193
  * Cria a worktree isolada da thread (paralelismo sem colisao entre threads).
146
194
  *
@@ -150,8 +198,9 @@ function idDisponivel(raiz, abbrev, assunto) {
150
198
  */
151
199
  function criarWorktree(carregado, id, slug, opcoes = {}) {
152
200
  const { raiz, manifesto } = carregado;
153
- const dir = path.resolve(raiz, manifesto.worktree.dir, id);
154
- const branch = opcoes.branchExistente ?? `ork/${slug}`;
201
+ // RM-047 (P3): worktree.dir do manifesto fora da raiz só cria checkout com a confirmação desta máquina.
202
+ (0, worktree_local_1.exigirPastaDasWorktrees)(raiz, manifesto);
203
+ const { dir, branch } = alvoDaWorktree(carregado, id, slug, opcoes);
155
204
  if (fs.existsSync(dir)) {
156
205
  throw new Error(`worktree ja existe em ${dir}`);
157
206
  }
@@ -170,7 +219,7 @@ function criarWorktree(carregado, id, slug, opcoes = {}) {
170
219
  throw new Error(`git worktree add falhou: ${(r.stderr || r.stdout).trim()}`);
171
220
  }
172
221
  if (pastaNova)
173
- (0, util_1.ignorarPastaNoGit)(pastaDasWorktrees, raiz);
222
+ (0, util_1.ignorarPastaNoGit)(pastaDasWorktrees, (0, estado_thread_1.raizDoEstado)(raiz));
174
223
  const lista = (0, util_1.exec)('git', ['worktree', 'list', '--porcelain'], raiz);
175
224
  const criada = lista.ok && lista.stdout.split('\n').some((l) => l.trim() === `worktree ${dir}`);
176
225
  if (!criada) {
@@ -179,6 +228,161 @@ function criarWorktree(carregado, id, slug, opcoes = {}) {
179
228
  (0, estado_thread_1.vincularEstado)(raiz, id, dir);
180
229
  return { dir, branch };
181
230
  }
231
+ /**
232
+ * Resolve `--worktree`, `--sem-worktree` e `worktree.por_thread`. As flags vencem a chave; as duas juntas, ou
233
+ * `--sem-worktree` com valor (o parser leva o argumento seguinte), sao erro de uso. `--worktree` sozinho ou `auto`
234
+ * cria a worktree; outro texto e um diretorio que ja existe, como antes.
235
+ */
236
+ function pedidoDeWorktree(flags, porThread) {
237
+ const worktree = flags.worktree === false ? undefined : flags.worktree;
238
+ if (typeof worktree === 'string' && !worktree.trim())
239
+ throw new Error((0, locale_1.msg)().thread.worktreeVazia);
240
+ if (typeof flags.semWorktree === 'string')
241
+ throw new Error((0, locale_1.msg)().thread.semWorktreeComValor(flags.semWorktree));
242
+ const semWorktree = flags.semWorktree === true;
243
+ if (semWorktree && worktree !== undefined)
244
+ throw new Error((0, locale_1.msg)().thread.worktreeESemWorktree);
245
+ if (semWorktree)
246
+ return { criar: false, dir: null, origem: 'sem-worktree' };
247
+ if (worktree === true || worktree === 'auto')
248
+ return { criar: true, dir: null, origem: 'flag' };
249
+ if (typeof worktree === 'string')
250
+ return { criar: false, dir: worktree, origem: 'diretorio' };
251
+ return porThread ? { criar: true, dir: null, origem: 'chave' } : { criar: false, dir: null, origem: 'nenhuma' };
252
+ }
253
+ /**
254
+ * P4 do ensaio da 0.5.0: a linha que diz de onde veio a worktree da thread nova. Na criacao, so quando ela veio da
255
+ * chave (quem passou `--worktree auto` sabe de onde ela veio); no `--dry-run`, para qualquer origem.
256
+ */
257
+ function linhaDaWorktree(por, gravada, variante) {
258
+ if (!por || (gravada && por !== 'chave'))
259
+ return null;
260
+ const m = (0, locale_1.msg)().thread;
261
+ if (por === 'chave')
262
+ return m.worktreeChave(gravada);
263
+ if (por === 'flag')
264
+ return m.worktreeFlag(gravada);
265
+ return m.worktreeCiclo(gravada, variante ?? null);
266
+ }
267
+ /**
268
+ * P4 do ensaio da 0.5.0: o que acontece no SHIP com a thread criada por `--sem-worktree`. O `ork ship` entrega a
269
+ * branch em que a raiz estava na criacao (`thread.base.branch`), e a policy `push_direto_na_base` do manifesto barra
270
+ * (`block`), avisa (outro valor) ou deixa passar (sem ela, ou `off`) a entrega de uma branch para ela mesma ou da
271
+ * base. Raiz fora de branch (HEAD destacado ou repositorio sem commit) nao tem branch de origem.
272
+ */
273
+ function avisoDeThreadSemWorktree(thread, manifesto, gravada = true) {
274
+ const base = manifesto.worktree.base_branch;
275
+ const branch = thread.base.branch;
276
+ const aviso = (c) => (0, locale_1.msg)().thread.avisoSemWorktree(thread.id, gravada, c);
277
+ // Sem commit, nem o `ork worktree ensure` tem de onde partir: o caminho e o do aviso de thread sem base.
278
+ if (thread.base.commit === util_1.COMMIT_DESCONHECIDO)
279
+ return aviso({ caso: 'sem-commit' });
280
+ if (branch === 'HEAD')
281
+ return aviso({ caso: 'head' });
282
+ if (branch !== base)
283
+ return aviso({ caso: 'outra', branch });
284
+ const policy = manifesto.policies?.push_direto_na_base;
285
+ return aviso({ caso: 'base', base, policy: policy === undefined ? undefined : String(policy) });
286
+ }
287
+ /**
288
+ * P4 do ensaio da 0.5.0 (CHECK): no `--dry-run`, por que a criacao de verdade recusaria a worktree, em vez de preve-la.
289
+ * O ciclo que exige worktree nao aceita `--sem-worktree`, entao a saida so e sugerida para a chave e a flag.
290
+ */
291
+ function avisoDeWorktreeQueFalharia(f) {
292
+ return (0, locale_1.msg)().thread.worktreeFalharia(f.motivo, f.dir, f.branch, f.por === 'ciclo');
293
+ }
294
+ /** P4 do ensaio da 0.5.0: a worktree que a chave pede nao tem de onde partir num repositorio sem commit. */
295
+ function avisoDeChaveSemCommit(gravada) {
296
+ return (0, locale_1.msg)().thread.chaveSemCommit(gravada);
297
+ }
298
+ /** P4: o aviso de worktree do `ork thread new`, para o stderr, ou `null` quando nao ha o que avisar. */
299
+ function avisoDaWorktree(origem, resultado, manifesto) {
300
+ if (origem === 'sem-worktree')
301
+ return avisoDeThreadSemWorktree(resultado.thread, manifesto, resultado.gravada);
302
+ if (origem === 'chave' && !resultado.worktreePor)
303
+ return avisoDeChaveSemCommit(resultado.gravada);
304
+ return null;
305
+ }
306
+ /** RM-059 (T6.0): o arquivo do pedido guardado, na pasta da thread. */
307
+ exports.ARQUIVO_DO_PEDIDO = 'pedido.md';
308
+ /**
309
+ * O prompt da fase vai inteiro como UM argumento do `claude --bg`, e o Linux recusa argumento acima de
310
+ * 128 KiB (MAX_ARG_STRLEN); o pedido fica com a metade, para o template caber junto.
311
+ */
312
+ exports.TETO_DO_PEDIDO = 64 * 1024;
313
+ /** O pedido como ele vai ser guardado: sem espaco nas pontas, nao vazio e dentro do teto. */
314
+ function prepararPedido(bruto) {
315
+ const texto = bruto.trim();
316
+ if (!texto)
317
+ throw new locale_1.ErroDoCatalogo((m) => m.thread.pedidoVazio);
318
+ const bytes = Buffer.byteLength(texto, 'utf8');
319
+ if (bytes > exports.TETO_DO_PEDIDO)
320
+ throw new locale_1.ErroDoCatalogo((m) => m.thread.pedidoGrande(bytes, exports.TETO_DO_PEDIDO));
321
+ return { texto, meta: { arquivo: exports.ARQUIVO_DO_PEDIDO, sha256: (0, node_crypto_1.createHash)('sha256').update(texto, 'utf8').digest('hex'), bytes } };
322
+ }
323
+ /**
324
+ * RM-059 (T6.0): o pedido guardado na criacao da thread, conferido contra o sha256 gravado; `null` na
325
+ * thread criada sem pedido. Arquivo que falta ou que nao bate com o sha256 (editado fora do nucleo) e
326
+ * erro tipado: o render nunca usa um pedido sem a prova de que e o da criacao.
327
+ */
328
+ function pedidoDaThread(raiz, thread) {
329
+ const meta = thread.pedido;
330
+ if (!meta)
331
+ return null;
332
+ const restaurar = 'passe --pedido no render ou restaure o arquivo';
333
+ // O nome do arquivo e fixo: um thread.json editado nao aponta o render para outro arquivo do projeto.
334
+ if (meta.arquivo !== exports.ARQUIVO_DO_PEDIDO || !/^[a-f0-9]{64}$/.test(String(meta.sha256))) {
335
+ throw new Error(`thread.pedido.divergente: o pedido da thread ${thread.id} no thread.json nao e o que a criacao grava; ${restaurar}`);
336
+ }
337
+ const arquivo = path.join(dirThread(raiz, thread.id), exports.ARQUIVO_DO_PEDIDO);
338
+ let fd;
339
+ try {
340
+ fd = fs.openSync(arquivo, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
341
+ }
342
+ catch (e) {
343
+ if (e.code === 'ENOENT')
344
+ throw new Error(`thread.pedido.ausente: a thread ${thread.id} guardou o pedido em ${meta.arquivo} e ele sumiu; ${restaurar}`);
345
+ throw new Error(`thread.pedido.divergente: ${meta.arquivo} da thread ${thread.id} nao abre como arquivo comum; ${restaurar}`);
346
+ }
347
+ try {
348
+ const st = fs.fstatSync(fd);
349
+ if (!st.isFile() || st.size > exports.TETO_DO_PEDIDO)
350
+ throw new Error(`thread.pedido.divergente: ${meta.arquivo} da thread ${thread.id} nao e o arquivo guardado; ${restaurar}`);
351
+ const bytes = fs.readFileSync(fd);
352
+ const sha256 = (0, node_crypto_1.createHash)('sha256').update(bytes).digest('hex');
353
+ if (sha256 !== meta.sha256) {
354
+ throw new Error(`thread.pedido.divergente: ${meta.arquivo} da thread ${thread.id} nao bate com o sha256 gravado na criacao ` +
355
+ `(${(0, util_1.shaCurto)(meta.sha256)}): foi editado fora do nucleo; ${restaurar}`);
356
+ }
357
+ return { texto: bytes.toString('utf8'), sha256 };
358
+ }
359
+ finally {
360
+ fs.closeSync(fd);
361
+ }
362
+ }
363
+ /**
364
+ * Grava o pedido sem meio-termo: escreve num temporario com fsync e o liga ao nome final, que nao pode existir
365
+ * (o `link` recusa sobrescrever). Uma falha no meio nao deixa `pedido.md` pela metade.
366
+ */
367
+ function gravarPedido(dir, texto) {
368
+ fs.mkdirSync(dir, { recursive: true });
369
+ const alvo = path.join(dir, exports.ARQUIVO_DO_PEDIDO);
370
+ const temporario = path.join(dir, `.${exports.ARQUIVO_DO_PEDIDO}.${process.pid}.${Date.now()}.tmp`);
371
+ const fd = fs.openSync(temporario, 'wx', 0o600);
372
+ try {
373
+ try {
374
+ fs.writeFileSync(fd, texto, 'utf8');
375
+ fs.fsyncSync(fd);
376
+ }
377
+ finally {
378
+ fs.closeSync(fd);
379
+ }
380
+ fs.linkSync(temporario, alvo);
381
+ }
382
+ finally {
383
+ fs.rmSync(temporario, { force: true });
384
+ }
385
+ }
182
386
  /** Cria a thread: valida modo contra o manifesto, gera o slug e carimba a base. */
183
387
  function novaThread(carregado, opcoes) {
184
388
  if (opcoes.dryRun)
@@ -198,21 +402,23 @@ function exigirVarianteCompativel(variante, def) {
198
402
  return;
199
403
  const [unico, ...resto] = def.blocos;
200
404
  if (unico && resto.length === 0 && unico.fases.length < types_1.FASES.length) {
201
- throw new Error(`o ciclo ${variante} redesenha os blocos e o modo ${def.tag} tem um bloco so ` +
202
- `(${unico.fases.join('-')}): crie a thread sem --ciclo ou escolha um modo de ciclo completo`);
405
+ throw new locale_1.ErroDoCatalogo((m) => m.thread.varianteIncompativel(variante, def.tag, unico.fases.join('-')));
203
406
  }
204
407
  }
205
408
  function criarThreadSerializada(carregado, opcoes) {
206
409
  const { raiz, manifesto } = carregado;
207
410
  const permitidos = manifesto.conduction.allowed_modes;
208
411
  if (!permitidos.includes(opcoes.modo)) {
209
- throw new Error(`modo ${modos_1.MODOS[opcoes.modo].tag} fora de conduction.allowed_modes ` +
210
- `(permitidos: ${permitidos.map((m) => modos_1.MODOS[m].tag).join(', ')})`);
412
+ const tag = modos_1.MODOS[opcoes.modo].tag, lista = permitidos.map((p) => modos_1.MODOS[p].tag).join(', ');
413
+ throw new locale_1.ErroDoCatalogo((m) => m.thread.modoNaoPermitido(tag, lista));
211
414
  }
212
415
  const assuntoBruto = (0, slug_1.normalizarAssunto)(opcoes.assunto ?? opcoes.nome);
213
416
  if (!assuntoBruto) {
214
- throw new Error(`nome "${opcoes.nome}" nao produz assunto valido (esperado [a-z0-9])`);
417
+ const nome = opcoes.nome;
418
+ throw new locale_1.ErroDoCatalogo((m) => m.thread.nomeSemAssunto(nome));
215
419
  }
420
+ // RM-059 (T6.0): o pedido e conferido antes de qualquer gravacao, tambem no ensaio.
421
+ const pedido = opcoes.pedido !== undefined ? prepararPedido(opcoes.pedido) : null;
216
422
  const abbrev = manifesto.project.abbrev;
217
423
  const def = (0, modos_1.definicaoDoModo)(opcoes.modo);
218
424
  const variante = opcoes.variante ?? null;
@@ -224,17 +430,25 @@ function criarThreadSerializada(carregado, opcoes) {
224
430
  if (variante) {
225
431
  const dv = (0, ciclos_1.definicaoDaVariante)(variante);
226
432
  if (dv.exigeBranch && !opcoes.branch) {
227
- throw new Error(`o ciclo ${variante} parte de uma branch que ja existe: informe --branch <branch>`);
433
+ throw new locale_1.ErroDoCatalogo((m) => m.thread.cicloExigeBranch(variante));
228
434
  }
229
- if (opcoes.branch) {
230
- const existe = (0, util_1.exec)('git', ['rev-parse', '--verify', `refs/heads/${opcoes.branch}`], raiz);
435
+ const branch = opcoes.branch;
436
+ if (branch) {
437
+ const existe = (0, util_1.exec)('git', ['rev-parse', '--verify', `refs/heads/${branch}`], raiz);
231
438
  if (!existe.ok) {
232
- throw new Error(`branch "${opcoes.branch}" nao existe neste repositorio`);
439
+ throw new locale_1.ErroDoCatalogo((m) => m.thread.branchInexistente(branch));
233
440
  }
234
441
  }
235
442
  }
236
443
  else if (opcoes.branch) {
237
- throw new Error('--branch so vale com --ciclo merge-branch');
444
+ throw new locale_1.ErroDoCatalogo((m) => m.thread.branchSemCiclo);
445
+ }
446
+ // `greenfield`, `merge-branch` e `feature-xl-faseada` exigem worktree isolada: sao os
447
+ // ciclos que escrevem codigo em paralelo com outras threads.
448
+ const exigeWorktree = variante ? (0, ciclos_1.definicaoDaVariante)(variante).exigeWorktree : false;
449
+ // P4 do ensaio da 0.5.0 (D4): o ciclo que exige worktree nao nasce sem ela, e a recusa sai antes de gravar.
450
+ if (exigeWorktree && opcoes.origemDaWorktree === 'sem-worktree') {
451
+ throw new Error(`o ciclo ${variante} exige worktree isolada: crie a thread sem --sem-worktree ou escolha outro ciclo`);
238
452
  }
239
453
  const reservation = opcoes.reservation;
240
454
  const operation = reservation ? (0, creation_operation_store_1.readCreationOperation)(raiz, reservation.operationId, reservation.principal) : null;
@@ -260,15 +474,15 @@ function criarThreadSerializada(carregado, opcoes) {
260
474
  && fs.readdirSync(dirThread(raiz, reservation.threadId)).some((name) => !/^\.thread\.json\..+\.tmp$/.test(name))) {
261
475
  throw new Error('creation.conflict: diretório reservado sem origem confirmada');
262
476
  }
477
+ // P4 do ensaio da 0.5.0 (CHECK): o ensaio usa o mesmo id livre da criacao (o `idDisponivel` so le o disco), para a
478
+ // worktree que ele mostra ser a que a criacao usaria.
263
479
  const { id, assunto } = reservation
264
480
  ? { id: reservation.threadId, assunto: reservation.threadId.slice(abbrev.length + 1) }
265
- : opcoes.dryRun
266
- ? { id: `${abbrev}-${assuntoBruto}`, assunto: assuntoBruto }
267
- : idDisponivel(raiz, abbrev, assuntoBruto);
481
+ : idDisponivel(raiz, abbrev, assuntoBruto);
268
482
  const slug = opcoes.slug ?? (0, slug_1.montarSlug)(abbrev, assunto, blocos[0].slugFases);
269
483
  // O override `--slug` do builder tambem passa pela regex canonica.
270
484
  if (!(0, slug_1.slugValido)(slug)) {
271
- throw new Error(`slug invalido: "${slug}" (regex canonica: ${slug_1.REGEX_SLUG.source})`);
485
+ throw new locale_1.ErroDoCatalogo((m) => m.thread.slugInvalido(slug, slug_1.REGEX_SLUG.source));
272
486
  }
273
487
  const thread = {
274
488
  ...(origin ? { creationOrigin: origin } : {}),
@@ -291,6 +505,7 @@ function criarThreadSerializada(carregado, opcoes) {
291
505
  sessoes: [],
292
506
  ...(opcoes.exigeRuntimeDiferente === true ? { exigeRuntimeDiferente: true } : {}),
293
507
  ...(opcoes.doneWhen && opcoes.doneWhen.length > 0 ? { doneWhen: opcoes.doneWhen } : {}),
508
+ ...(pedido ? { pedido: pedido.meta } : {}),
294
509
  decisoes: [],
295
510
  claims: [],
296
511
  leases: [],
@@ -300,13 +515,39 @@ function criarThreadSerializada(carregado, opcoes) {
300
515
  };
301
516
  if (variante === 'feature-xl-faseada')
302
517
  thread.fatias = opcoes.fatias ?? 3;
303
- if (opcoes.dryRun)
304
- return { thread, gravada: false };
518
+ // P4 do ensaio da 0.5.0: por que a thread ganha a worktree. O ensaio e a chave preveem a worktree antes; a pedida
519
+ // por flag ou por ciclo segue direto ao git, como era. A pedida so pela chave espera o primeiro commit (D3): sem
520
+ // commit, a thread fica na raiz, como antes. O ciclo que exige worktree vence a chave e a flag na origem: a worktree
521
+ // e dele, e o `--sem-worktree` que a linha da chave sugeriria ele recusa (CHECK, rodada 2).
522
+ const pedeWorktree = opcoes.criarWorktree === true || exigeWorktree;
523
+ const pelaChave = opcoes.criarWorktree === true && opcoes.origemDaWorktree === 'chave';
524
+ const prevista = pedeWorktree && (opcoes.dryRun || pelaChave)
525
+ ? worktreePrevista(carregado, id, slug, { branchExistente: opcoes.branch }) : null;
526
+ const worktreePor = !pedeWorktree || (pelaChave && !exigeWorktree && prevista?.falha === 'sem-commit') ? null
527
+ : exigeWorktree ? 'ciclo' : pelaChave ? 'chave' : 'flag';
528
+ if (opcoes.dryRun) {
529
+ // P4 (D8): o ensaio mostra a worktree que a criacao usaria; quando ela recusaria, diz por que, em vez de preve-la.
530
+ if (worktreePor && prevista?.falha) {
531
+ return { thread, gravada: false, worktreePor: null,
532
+ worktreeFalharia: { motivo: prevista.falha, dir: prevista.dir, branch: prevista.branch, por: worktreePor } };
533
+ }
534
+ if (worktreePor && prevista?.commit) {
535
+ thread.worktree = prevista.dir;
536
+ thread.base = { branch: prevista.branch, commit: prevista.commit };
537
+ }
538
+ return { thread, gravada: false, worktreePor };
539
+ }
540
+ // P4 (CHECK): a worktree pedida so pela chave, com a pasta ou a branch ja ocupadas, recusa dizendo de onde veio o
541
+ // pedido e qual e a saida.
542
+ const ocupadaPelaChave = pelaChave && !exigeWorktree && prevista &&
543
+ (prevista.falha === 'pasta-existe' || prevista.falha === 'branch-existe') ? prevista : null;
544
+ if (ocupadaPelaChave) {
545
+ const pasta = ocupadaPelaChave.falha === 'pasta-existe';
546
+ const alvo = pasta ? ocupadaPelaChave.dir : ocupadaPelaChave.branch;
547
+ throw new locale_1.ErroDoCatalogo((m) => m.thread.ocupadaPelaChave(pasta, alvo));
548
+ }
305
549
  let worktreeCriada = null;
306
- // `greenfield`, `merge-branch` e `feature-xl-faseada` exigem worktree isolada: sao os
307
- // ciclos que escrevem codigo em paralelo com outras threads.
308
- const exigeWorktree = variante ? (0, ciclos_1.definicaoDaVariante)(variante).exigeWorktree : false;
309
- if (opcoes.criarWorktree || exigeWorktree) {
550
+ if (worktreePor) {
310
551
  worktreeCriada = criarWorktree(carregado, id, slug, {
311
552
  branchExistente: opcoes.branch,
312
553
  });
@@ -317,6 +558,10 @@ function criarThreadSerializada(carregado, opcoes) {
317
558
  commit: commitDaBranch.ok ? commitDaBranch.stdout.trim() : thread.base.commit,
318
559
  };
319
560
  }
561
+ // RM-059 (T6.0): o pedido vai ao lado do thread.json, so com permissao do dono, como ele, e antes dele: o
562
+ // thread.json nunca aponta para um pedido que nao foi gravado.
563
+ if (pedido)
564
+ gravarPedido(dirThread(raiz, id), pedido.texto);
320
565
  gravarThread(raiz, thread);
321
566
  (0, ledger_1.registrar)(dirThread(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.threadCriada, {
322
567
  ...(origin ? { creationOrigin: origin } : {}),
@@ -328,20 +573,24 @@ function criarThreadSerializada(carregado, opcoes) {
328
573
  base: thread.base,
329
574
  projeto: manifesto.project.name,
330
575
  worktree: thread.worktree,
576
+ // P4 do ensaio da 0.5.0 (D9): quem abriu a thread sem worktree de proposito fica no rastro.
577
+ ...(opcoes.origemDaWorktree === 'sem-worktree' ? { semWorktree: true } : {}),
331
578
  variante,
332
579
  branchDeOrigem: opcoes.branch ?? null,
333
580
  maquina: thread.maquina,
334
581
  ...(thread.roadmap ? { roadmap: thread.roadmap } : {}),
582
+ ...(pedido ? { pedido: pedido.meta } : {}),
335
583
  });
336
584
  if (worktreeCriada) {
337
585
  (0, ledger_1.registrar)(dirThread(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.worktreeCriada, {
338
586
  dir: worktreeCriada.dir,
339
587
  branch: worktreeCriada.branch,
340
588
  base: manifesto.worktree.base_branch,
589
+ origem: worktreePor,
341
590
  fonte: 'git worktree list --porcelain',
342
591
  });
343
592
  }
344
- return { thread, gravada: true };
593
+ return { thread, gravada: true, worktreePor };
345
594
  }
346
595
  /**
347
596
  * O bloco de loop que conduz a fase DESTA thread.
@@ -373,38 +622,39 @@ function canalDaSessao(s) {
373
622
  }
374
623
  function resumoDaThread(thread, conducao) {
375
624
  const def = (0, modos_1.definicaoDoModo)(thread.modo);
625
+ const m = (0, locale_1.msg)().thread, r = m.rotulos;
626
+ // RM-061 (F2a): o rotulo vem do catalogo, na largura do locale; o valor gravado (nome, criterio) sai como esta.
627
+ const campo = (rotulo, valor) => ` ${rotulo.padEnd(m.largura)} ${valor}`;
376
628
  const linhas = [];
377
- linhas.push(` id ${thread.id}`);
378
- linhas.push(` slug ${thread.slug}`);
379
- linhas.push(` nome ${thread.nome}`);
380
- linhas.push(` modo ${def.tag} (${pausasDaThread(thread)} pausas humanas previstas)`);
381
- linhas.push(` ciclo ${thread.variante ?? 'padrao do modo'}`);
382
- linhas.push(` fase ${thread.faseAtual}`);
383
- linhas.push(` status ${thread.status}`);
384
- linhas.push(` base ${thread.base.branch} @ ${(0, util_1.shaCurto)(thread.base.commit)}`);
385
- linhas.push(` worktree ${thread.worktree ?? '(raiz do projeto)'}`);
629
+ linhas.push(campo(r.id, thread.id));
630
+ linhas.push(campo(r.slug, thread.slug));
631
+ linhas.push(campo(r.nome, thread.nome));
632
+ linhas.push(campo(r.modo, m.modo(def.tag, pausasDaThread(thread))));
633
+ linhas.push(campo(r.ciclo, thread.variante ?? m.cicloPadrao));
634
+ linhas.push(campo(r.fase, thread.faseAtual));
635
+ linhas.push(campo(r.status, m.status(thread.status)));
636
+ linhas.push(campo(r.base, `${thread.base.branch} @ ${(0, util_1.shaCurto)(thread.base.commit)}`));
637
+ linhas.push(campo(r.worktree, thread.worktree ?? m.raizDoProjeto));
386
638
  // I-36 (T17): quem passa a leitura da conducao ve a linha unica; sem ela o resumo fica como era.
387
639
  if (conducao !== undefined)
388
- linhas.push(` conducao ${conducao ? (0, conducao_texto_1.linhaDeConducao)(conducao) : 'ninguem conduz agora'}`);
640
+ linhas.push(campo(r.conducao, conducao ? (0, conducao_texto_1.linhaDeConducao)(conducao) : m.ninguemConduz));
389
641
  // I-43 (D4, viga a): a exigencia so aparece quando existe, para nao poluir o resumo
390
642
  // das threads que nao a declararam.
391
- if (thread.exigeRuntimeDiferente === true) {
392
- linhas.push(' validacao CHECK em runtime diferente do GO (exigido pela thread)');
393
- }
643
+ if (thread.exigeRuntimeDiferente === true)
644
+ linhas.push(m.validacao);
394
645
  // I-43 (D4, viga b): o criterio aparece COM o comando. Mostrar so o criterio seria
395
646
  // reproduzir a prosa do envelope numa tela nova.
396
647
  if (thread.doneWhen && thread.doneWhen.length > 0) {
397
- linhas.push(` pronto quando (${thread.doneWhen.length} criterio(s) executaveis)`);
648
+ linhas.push(m.prontoQuando(thread.doneWhen.length));
398
649
  for (const d of thread.doneWhen)
399
650
  linhas.push(` ${d.criterio}\n $ ${d.comando}`);
400
651
  }
401
652
  if (thread.score) {
402
- linhas.push(` score ${thread.score.valor}/5 por ${thread.score.avaliadoPor} ` +
403
- `(${thread.score.regime}) em ${(0, horario_1.formatarDataHoraRotulada)(thread.score.avaliadoEm)}`);
653
+ linhas.push(campo(r.score, m.score(thread.score.valor, thread.score.avaliadoPor, thread.score.regime, (0, horario_1.formatarDataHoraRotulada)(thread.score.avaliadoEm))));
404
654
  }
405
- linhas.push(' blocos previstos');
655
+ linhas.push(m.blocosPrevistos);
406
656
  for (const b of thread.blocos) {
407
- const marca = b.pausa ? `pausa prevista: ${b.pausaSobre}` : 'sem pausa humana prevista';
657
+ const marca = b.pausa ? m.pausaPrevista(m.pausaSobre(b.pausaSobre)) : m.semPausa;
408
658
  linhas.push(` ${b.slugFases.padEnd(6)} ${b.fases.join('-').padEnd(18)} ${marca}`);
409
659
  }
410
660
  return linhas.join('\n');
@@ -460,7 +710,7 @@ function tabelaDeThreads(raiz, opcoes = {}) {
460
710
  function exigirFase(thread, bruto) {
461
711
  const fase = bruto.toUpperCase();
462
712
  if (!thread.fases.includes(fase)) {
463
- throw new Error(`fase invalida: "${bruto}" (esperado uma de ${thread.fases.join(', ')})`);
713
+ throw new Error((0, locale_1.msg)().phase.faseInvalida(bruto, thread.fases.join(', ')));
464
714
  }
465
715
  return fase;
466
716
  }
package/dist/util.js CHANGED
@@ -197,7 +197,14 @@ function lerJsonl(caminho) {
197
197
  /** Anexa um registro ao armazem JSONL, criando o diretorio que faltar. */
198
198
  function anexarJsonl(caminho, valor) {
199
199
  fs.mkdirSync(path.dirname(caminho), { recursive: true });
200
- fs.appendFileSync(caminho, JSON.stringify(valor) + '\n', 'utf8');
200
+ // RM-047: O_NOFOLLOW; um armazém versionado como link (claims, board) não leva o append para fora da raiz.
201
+ const fd = fs.openSync(caminho, fs.constants.O_WRONLY | fs.constants.O_APPEND | fs.constants.O_CREAT | fs.constants.O_NOFOLLOW, 0o644);
202
+ try {
203
+ fs.writeFileSync(fd, JSON.stringify(valor) + '\n', 'utf8');
204
+ }
205
+ finally {
206
+ fs.closeSync(fd);
207
+ }
201
208
  }
202
209
  /** Proximo id sequencial de um armazem (`C1`, `C2`, ... / `F1`, `F2`, ...). */
203
210
  function proximoIdSequencial(ids, prefixo) {