@orkastery/cli 0.2.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +86 -65
  3. package/adapters/README.md +30 -15
  4. package/adapters/claude-code/.claude-plugin/plugin.json +11 -5
  5. package/adapters/claude-code/README.md +110 -8
  6. package/adapters/claude-code/commands/check.md +4 -3
  7. package/adapters/claude-code/commands/goal.md +3 -2
  8. package/adapters/claude-code/commands/master.md +3 -2
  9. package/adapters/claude-code/commands/onboarding.md +21 -0
  10. package/adapters/claude-code/commands/ork.md +99 -27
  11. package/adapters/claude-code/hooks/hooks.json +6 -1
  12. package/adapters/claude-code/hooks/ork-guard.js +1 -1
  13. package/adapters/claude-code/hooks/ork-sensor.js +66 -0
  14. package/adapters/codex/skills/ork/SKILL.md +57 -0
  15. package/adapters/hermes/README.md +161 -0
  16. package/adapters/hermes/bin/ork-abrir-thread.sh +2 -0
  17. package/adapters/hermes/bin/ork-brain.sh +4 -0
  18. package/adapters/hermes/bin/ork-hitl-answer.py +82 -0
  19. package/adapters/hermes/bin/ork-maestro.sh +6 -0
  20. package/adapters/hermes/bin/ork-master-enviar.py +84 -0
  21. package/adapters/hermes/bin/ork-pulse-enviar.py +47 -0
  22. package/adapters/hermes/hermes.plugin.json +24 -4
  23. package/adapters/hermes/hitl-ingress/__init__.py +356 -0
  24. package/adapters/hermes/hitl-ingress/plugin.yaml +4 -0
  25. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +75 -22
  26. package/adapters/openclaw/README.md +136 -27
  27. package/adapters/openclaw/bin/ork-brain.sh +4 -0
  28. package/adapters/openclaw/construir.sh +19 -0
  29. package/adapters/openclaw/dist/hitl-ingress.js +199 -0
  30. package/adapters/openclaw/dist/index.js +437 -0
  31. package/adapters/openclaw/openclaw.plugin.json +39 -134
  32. package/adapters/openclaw/package.json +26 -0
  33. package/adapters/openclaw/src/hitl-ingress.ts +180 -0
  34. package/adapters/openclaw/src/index.ts +460 -0
  35. package/adapters/openclaw/src/tipos-openclaw.d.ts +58 -0
  36. package/adapters/openclaw/tsconfig.json +15 -0
  37. package/assets/docs/markdownlint-cli2.jsonc +22 -0
  38. package/assets/docs/padroes/documentacao-de-produto.md +133 -0
  39. package/assets/docs/padroes/roadmap-de-produto.md +98 -0
  40. package/assets/docs/produto/README.md +14 -0
  41. package/assets/docs/produto/_modelo-feature.md +59 -0
  42. package/assets/docs/roadmap/README.md +14 -0
  43. package/assets/docs/roadmap/_modelo-item.md +73 -0
  44. package/assets/orkmind-native-schema.json +32 -0
  45. package/assets/orkmind_bridge.py +357 -0
  46. package/assets/orkmind_fixture.py +67 -0
  47. package/assets/orkmind_prospective.py +138 -0
  48. package/assets/reference-tariffs-i07.json +23 -0
  49. package/dist/adapters/claude-bg.js +488 -39
  50. package/dist/adapters/codex-controller-sensor.js +449 -0
  51. package/dist/adapters/codex-controller-worker.js +361 -0
  52. package/dist/adapters/codex-controller.js +252 -0
  53. package/dist/adapters/codex-events.js +205 -0
  54. package/dist/adapters/codex-question.js +26 -0
  55. package/dist/adapters/codex-runner.js +133 -0
  56. package/dist/adapters/codex.js +394 -0
  57. package/dist/agents-md.js +84 -0
  58. package/dist/auditoria.js +12 -9
  59. package/dist/auditrun.js +5 -4
  60. package/dist/board.js +174 -23
  61. package/dist/branch-de-estado.js +136 -0
  62. package/dist/canarios-hitl.js +177 -0
  63. package/dist/canarios-i43.js +543 -0
  64. package/dist/canarios-pulse.js +147 -0
  65. package/dist/canarios-sensores.js +129 -0
  66. package/dist/canarios.js +111 -2
  67. package/dist/catalogo.js +9 -0
  68. package/dist/ci.js +223 -0
  69. package/dist/ciclos.js +2 -1
  70. package/dist/claim-lint.js +64 -0
  71. package/dist/claims.js +60 -0
  72. package/dist/company-brain-capture.js +195 -0
  73. package/dist/company-brain-cli.js +123 -0
  74. package/dist/company-brain-client.js +61 -0
  75. package/dist/company-brain-contract.js +166 -0
  76. package/dist/company-brain-journal.js +192 -0
  77. package/dist/company-brain-mcp.js +33 -0
  78. package/dist/company-brain-migration.js +60 -0
  79. package/dist/company-brain-source.js +177 -0
  80. package/dist/company-brain-worker.js +18 -0
  81. package/dist/conducao-texto.js +61 -0
  82. package/dist/conducao.js +876 -0
  83. package/dist/contrato-publico.js +39 -0
  84. package/dist/creation-operation-store.js +251 -0
  85. package/dist/creation-operation.js +148 -0
  86. package/dist/decisao-autonoma.js +183 -0
  87. package/dist/delegation.js +79 -0
  88. package/dist/demo.js +120 -0
  89. package/dist/docs.js +828 -0
  90. package/dist/doctor.js +184 -25
  91. package/dist/entrega-pr.js +117 -0
  92. package/dist/escopo-escrita.js +71 -0
  93. package/dist/estado-thread.js +222 -0
  94. package/dist/evalrunner.js +12 -0
  95. package/dist/fabrica-estado.js +329 -0
  96. package/dist/fabrica-publicar.js +76 -0
  97. package/dist/fix.js +22 -1
  98. package/dist/gates.js +59 -11
  99. package/dist/handoff.js +39 -23
  100. package/dist/hitl-canais.js +333 -0
  101. package/dist/hitl-classificacao.js +131 -0
  102. package/dist/hitl-contract.js +465 -0
  103. package/dist/hitl-estado.js +136 -0
  104. package/dist/hitl-gates.js +554 -0
  105. package/dist/hitl-ingress-receipt.js +382 -0
  106. package/dist/hitl-local-atestado.js +57 -0
  107. package/dist/hitl-local-receipt.js +328 -0
  108. package/dist/hitl-local.js +143 -0
  109. package/dist/hitl-lock.js +139 -0
  110. package/dist/hitl-lote.js +223 -0
  111. package/dist/hitl-native-offer.js +97 -0
  112. package/dist/hitl-native.js +65 -0
  113. package/dist/hitl-presentation.js +209 -0
  114. package/dist/hitl-public-receipt.js +176 -0
  115. package/dist/hitl-resumo.js +209 -0
  116. package/dist/hitl-sessions.js +475 -0
  117. package/dist/hitl.js +711 -0
  118. package/dist/horario.js +269 -0
  119. package/dist/hosts.js +198 -22
  120. package/dist/index.js +1741 -94
  121. package/dist/indice.js +99 -0
  122. package/dist/init.js +28 -3
  123. package/dist/integracoes-locais.js +17 -0
  124. package/dist/leases.js +65 -21
  125. package/dist/ledger-stats.js +272 -0
  126. package/dist/ledger.js +109 -2
  127. package/dist/licoes.js +221 -0
  128. package/dist/liveness.js +218 -0
  129. package/dist/maestro-actions.js +51 -0
  130. package/dist/maestro-authority.js +152 -0
  131. package/dist/maestro-cli.js +97 -0
  132. package/dist/maestro-contract.js +55 -0
  133. package/dist/maestro-discovery.js +132 -0
  134. package/dist/maestro-runtime.js +268 -0
  135. package/dist/maestro-snapshot.js +86 -0
  136. package/dist/maestro-sources.js +224 -0
  137. package/dist/manifest.js +165 -6
  138. package/dist/maquina.js +102 -0
  139. package/dist/master-audit.js +101 -0
  140. package/dist/master-batch.js +43 -0
  141. package/dist/master-digest.js +149 -0
  142. package/dist/master-migracao.js +155 -0
  143. package/dist/master.js +361 -77
  144. package/dist/mcp-artifacts.js +234 -0
  145. package/dist/mcp-git.js +433 -0
  146. package/dist/mcp-install.js +310 -0
  147. package/dist/mcp-maestro.js +25 -0
  148. package/dist/mcp-server.js +469 -0
  149. package/dist/mcp-ship.js +436 -0
  150. package/dist/mcp-verify.js +173 -0
  151. package/dist/memoria-humana.js +207 -0
  152. package/dist/memoria.js +314 -78
  153. package/dist/memory-migration.js +279 -0
  154. package/dist/memory-prospective.js +148 -0
  155. package/dist/modos-migracao.js +191 -0
  156. package/dist/modos.js +151 -15
  157. package/dist/monitor-lock.js +110 -0
  158. package/dist/objective.js +449 -0
  159. package/dist/ocupacao.js +267 -0
  160. package/dist/onboarding.js +303 -0
  161. package/dist/orkmind.js +389 -90
  162. package/dist/orquestracao.js +96 -69
  163. package/dist/phase.js +612 -151
  164. package/dist/playbook-capabilities.js +150 -0
  165. package/dist/playbook-contracts.js +188 -0
  166. package/dist/policies.js +72 -0
  167. package/dist/portfolio-context.js +61 -0
  168. package/dist/portfolio.js +180 -0
  169. package/dist/preflight.js +207 -0
  170. package/dist/process-audit.js +112 -0
  171. package/dist/project-state.js +93 -0
  172. package/dist/prompts.js +25 -5
  173. package/dist/prova-minima.js +112 -0
  174. package/dist/pulse-cadencia.js +163 -0
  175. package/dist/pulse-consentimento.js +260 -0
  176. package/dist/pulse-delivery.js +323 -0
  177. package/dist/pulse-resposta.js +542 -0
  178. package/dist/pulse.js +262 -0
  179. package/dist/ratelimit.js +35 -2
  180. package/dist/recall.js +80 -3
  181. package/dist/redacao-saida.js +45 -0
  182. package/dist/redacao-url.js +66 -0
  183. package/dist/retry.js +640 -118
  184. package/dist/roadmap-reservas.js +243 -0
  185. package/dist/runtime-ambiente.js +38 -0
  186. package/dist/runtime-context.js +215 -0
  187. package/dist/runtime-profiles.js +869 -0
  188. package/dist/runtimes.js +103 -0
  189. package/dist/sandbox.js +9 -0
  190. package/dist/session-events.js +203 -0
  191. package/dist/session-watcher-claude.js +637 -0
  192. package/dist/session-watcher.js +700 -0
  193. package/dist/sessoes-adopt.js +154 -0
  194. package/dist/sessoes-inventario.js +159 -0
  195. package/dist/sessoes.js +16 -35
  196. package/dist/setup.js +691 -0
  197. package/dist/ship.js +168 -19
  198. package/dist/slug.js +1 -1
  199. package/dist/thread-close.js +115 -0
  200. package/dist/thread.js +142 -28
  201. package/dist/tokens.js +1 -1
  202. package/dist/util.js +4 -2
  203. package/dist/verify-sandbox.js +265 -0
  204. package/dist/verify.js +270 -25
  205. package/dist/versao.js +60 -0
  206. package/dist/worktree.js +31 -2
  207. package/dist/write-activation.js +291 -0
  208. package/dist/yaml.js +5 -1
  209. package/eval/casos/master-metrics.json +1 -1
  210. package/eval/casos/onboarding.json +102 -0
  211. package/eval/casos/orkastery-bootstrap.json +37 -3
  212. package/eval/casos/scope-check-capability-map.json +45 -7
  213. package/eval/casos/ship-release.json +3 -3
  214. package/eval/fixtures/b0-slug-e-modos/caso.json +35 -22
  215. package/eval/fixtures/b2-master-log/caso.json +1 -1
  216. package/eval/fixtures/fx-adapter-editado-detectado/caso.json +19 -0
  217. package/eval/fixtures/fx-auto-quiet/caso.json +11 -0
  218. package/eval/fixtures/fx-blanket-approve/caso.json +10 -0
  219. package/eval/fixtures/fx-check-runtime-cruzado/caso.json +20 -0
  220. package/eval/fixtures/fx-codex-dry/caso.json +18 -0
  221. package/eval/fixtures/fx-donewhen-executavel/caso.json +15 -0
  222. package/eval/fixtures/fx-estado-dividido/caso.json +15 -0
  223. package/eval/fixtures/fx-fase-orfa/caso.json +116 -0
  224. package/eval/fixtures/fx-happy/caso.json +6 -4
  225. package/eval/fixtures/fx-hitl-latency/caso.json +20 -0
  226. package/eval/fixtures/fx-indice-reversao/caso.json +25 -0
  227. package/eval/fixtures/fx-listagem-abertas/caso.json +16 -0
  228. package/eval/fixtures/fx-maestro-bootstrap/caso.json +21 -0
  229. package/eval/fixtures/fx-modo-aposentado-escritor/caso.json +21 -0
  230. package/eval/fixtures/fx-modo-aposentado-leitor/caso.json +16 -0
  231. package/eval/fixtures/fx-objective-oscillation/caso.json +14 -0
  232. package/eval/fixtures/fx-omnicanal/caso.json +17 -0
  233. package/eval/fixtures/fx-sensores-runtime/caso.json +17 -0
  234. package/monitor/company-brain.cjs +17 -0
  235. package/monitor/pulse-scope.cjs +45 -0
  236. package/monitor/pulse.cron +18 -0
  237. package/monitor/varredura-pulse.sh +27 -0
  238. package/package.json +28 -7
  239. package/references/definition-of-done.md +2 -2
  240. package/schemas/claims.schema.json +25 -0
  241. package/schemas/company-brain.schema.json +1158 -0
  242. package/schemas/creation-operation.schema.json +365 -0
  243. package/schemas/maestro-snapshot.schema.json +2716 -0
  244. package/skills/README.md +3 -3
  245. package/skills/core/onboarding/SKILL.md +50 -0
  246. package/skills/core/orkastery-bootstrap/SKILL.md +95 -65
  247. package/skills/core/thread-state/SKILL.md +3 -1
  248. package/skills/governance/decision-triage/SKILL.md +4 -4
  249. package/skills/governance/narrative-guardian/SKILL.md +1 -1
  250. package/skills/governance/roadmap-keeper/SKILL.md +1 -1
  251. package/skills/governance/scope-check-capability-map/SKILL.md +17 -7
  252. package/skills/observability/thread-tracing/SKILL.md +1 -1
  253. package/skills/phases/check-quality/SKILL.md +2 -2
  254. package/skills/phases/goal-definition/SKILL.md +1 -1
  255. package/skills/phases/master-metrics/SKILL.md +9 -7
  256. package/skills/phases/plan-specification/SKILL.md +1 -1
  257. package/skills/phases/ship-release/SKILL.md +1 -1
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.raizDoEstado = raizDoEstado;
37
+ exports.estadoCanonico = estadoCanonico;
38
+ exports.vincularEstado = vincularEstado;
39
+ exports.comEstadoParaGit = comEstadoParaGit;
40
+ exports.auditarEstado = auditarEstado;
41
+ /** D1: a árvore principal é o domicílio do estado, mesmo em uma linked worktree. */
42
+ const fs = __importStar(require("node:fs"));
43
+ const path = __importStar(require("node:path"));
44
+ const node_crypto_1 = require("node:crypto");
45
+ const util_1 = require("./util");
46
+ function raizDoEstado(raiz) {
47
+ const local = path.resolve(raiz);
48
+ const git = path.join(local, '.git');
49
+ if (!fs.existsSync(git) || !fs.statSync(git).isFile())
50
+ return local;
51
+ const match = /^gitdir: (.+)\s*$/m.exec(fs.readFileSync(git, 'utf8'));
52
+ if (!match)
53
+ throw new Error(`.git inválido em ${local}`);
54
+ const gitdir = path.resolve(local, match[1].trim());
55
+ const commondir = path.join(gitdir, 'commondir');
56
+ if (!fs.existsSync(commondir))
57
+ return local; // submódulo, não linked worktree
58
+ const comum = path.resolve(gitdir, fs.readFileSync(commondir, 'utf8').trim());
59
+ const principal = path.dirname(comum);
60
+ if (!fs.existsSync(path.join(principal, '.git')) ||
61
+ fs.realpathSync(path.join(principal, '.git')) !== fs.realpathSync(comum)) {
62
+ throw new Error(`árvore principal não resolvida pelo git-common-dir: ${comum}`);
63
+ }
64
+ return principal;
65
+ }
66
+ function estadoCanonico(raiz, id) {
67
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(id))
68
+ throw new Error('id de thread inválido');
69
+ return path.join(raizDoEstado(raiz), '.orkastery', 'threads', id);
70
+ }
71
+ function mesmoArquivo(a, b) {
72
+ return fs.existsSync(a) && fs.existsSync(b) && fs.realpathSync(a) === fs.realpathSync(b);
73
+ }
74
+ function arquivos(dir, prefixo = '') {
75
+ return fs.readdirSync(path.join(dir, prefixo), { withFileTypes: true }).flatMap(e => {
76
+ const nome = path.join(prefixo, e.name);
77
+ if (e.isSymbolicLink())
78
+ throw new Error(`estado legado contém link: ${nome}`);
79
+ if (e.isDirectory())
80
+ return arquivos(dir, nome);
81
+ if (!e.isFile())
82
+ throw new Error(`estado legado contém arquivo especial: ${nome}`);
83
+ return [nome];
84
+ });
85
+ }
86
+ function ignorarEstadoLocal(raiz, id) {
87
+ const exclude = path.join(raizDoEstado(raiz), '.git', 'info', 'exclude');
88
+ fs.mkdirSync(path.dirname(exclude), { recursive: true });
89
+ const regras = [`/.orkastery/threads/${id}`, '/.orkastery/state-backups/'];
90
+ const atual = fs.existsSync(exclude) ? fs.readFileSync(exclude, 'utf8') : '';
91
+ for (const regra of regras) {
92
+ if (!atual.split('\n').includes(regra))
93
+ fs.appendFileSync(exclude, `\n${regra}\n`);
94
+ }
95
+ }
96
+ /** Preflight integral antes de copiar. Divergência preserva ambas as fontes. */
97
+ function vincularEstado(raiz, id, worktree, dryRun = false) {
98
+ const alvo = estadoCanonico(raiz, id);
99
+ const local = path.join(worktree, '.orkastery', 'threads', id);
100
+ if (path.resolve(local) === alvo)
101
+ return;
102
+ if (raizDoEstado(worktree) !== raizDoEstado(raiz))
103
+ throw new Error('worktree não pertence à árvore principal');
104
+ // O índice é próprio da linked worktree. A main continua versionando seu estado.
105
+ const rastreados = (0, util_1.exec)('git', ['ls-files', '-z', '--', `.orkastery/threads/${id}/`], worktree);
106
+ if (!rastreados.ok)
107
+ throw new Error(`índice da worktree indisponível: ${rastreados.stderr}`);
108
+ const nomes = rastreados.stdout.split('\0').filter(Boolean);
109
+ const prepararIndice = () => {
110
+ if (dryRun || nomes.length === 0)
111
+ return;
112
+ const r = (0, util_1.exec)('git', ['update-index', '--skip-worktree', '--', ...nomes], worktree);
113
+ if (!r.ok)
114
+ throw new Error(`não foi possível preservar o estado versionado no índice: ${r.stderr}`);
115
+ };
116
+ if (mesmoArquivo(local, alvo)) {
117
+ prepararIndice();
118
+ if (!dryRun)
119
+ ignorarEstadoLocal(raiz, id);
120
+ return;
121
+ }
122
+ const copiar = [];
123
+ const existe = fs.existsSync(local);
124
+ if (!existe && fs.lstatSync(path.dirname(local), { throwIfNoEntry: false })?.isSymbolicLink()) {
125
+ throw new Error('diretório de threads inesperadamente vinculado');
126
+ }
127
+ if (existe) {
128
+ if (fs.lstatSync(local).isSymbolicLink())
129
+ throw new Error(`link de estado aponta para outro destino: ${local}`);
130
+ for (const nome of arquivos(local)) {
131
+ const destino = path.join(alvo, nome);
132
+ if (!fs.existsSync(destino)) {
133
+ copiar.push(nome);
134
+ continue;
135
+ }
136
+ const a = fs.readFileSync(path.join(local, nome));
137
+ const b = fs.readFileSync(destino);
138
+ if (a.equals(b))
139
+ continue;
140
+ // Prefixo JSONL já incorporado na main não é conflito nem deve ser duplicado.
141
+ if (nome.endsWith('.jsonl') && a.toString().endsWith('\n') && b.subarray(0, a.length).equals(a))
142
+ continue;
143
+ throw new Error(`estado dividido: ${nome} diverge entre ${local} e ${alvo}; reconcilie antes do sync`);
144
+ }
145
+ }
146
+ else if (fs.lstatSync(local, { throwIfNoEntry: false })) {
147
+ throw new Error(`link de estado quebrado: ${local}`);
148
+ }
149
+ // O ensaio executa o mesmo preflight, sem tocar em arquivos ou no índice.
150
+ if (dryRun)
151
+ return;
152
+ fs.mkdirSync(alvo, { recursive: true });
153
+ for (const nome of copiar) {
154
+ const destino = path.join(alvo, nome);
155
+ fs.mkdirSync(path.dirname(destino), { recursive: true });
156
+ fs.copyFileSync(path.join(local, nome), destino, fs.constants.COPYFILE_EXCL);
157
+ }
158
+ prepararIndice();
159
+ if (existe) {
160
+ const backup = path.join(raizDoEstado(raiz), '.orkastery', 'state-backups', `${id}-${(0, node_crypto_1.randomUUID)()}`);
161
+ fs.mkdirSync(path.dirname(backup), { recursive: true });
162
+ fs.renameSync(local, backup);
163
+ }
164
+ fs.mkdirSync(path.dirname(local), { recursive: true });
165
+ fs.symlinkSync(path.relative(path.dirname(local), alvo), local, 'dir');
166
+ // O vínculo é estado desta máquina, nunca parte do commit de produto.
167
+ ignorarEstadoLocal(raiz, id);
168
+ }
169
+ /**
170
+ * Git precisa materializar arquivos rastreados ao rebasar. Durante essa operação a
171
+ * cópia local vem exclusivamente do índice; a fonte canônica permanece intocada na main.
172
+ * O chamador conclui ou aborta o rebase antes de devolver o controle para a restauração.
173
+ */
174
+ function comEstadoParaGit(raiz, id, worktree, executar) {
175
+ vincularEstado(raiz, id, worktree);
176
+ const local = path.join(worktree, '.orkastery', 'threads', id);
177
+ const prefixo = `.orkastery/threads/${id}/`;
178
+ const git = (args) => {
179
+ const r = (0, util_1.exec)('git', args, worktree);
180
+ if (!r.ok)
181
+ throw new Error(`preparação do estado para Git falhou: ${r.stderr || r.stdout}`);
182
+ return r.stdout;
183
+ };
184
+ const nomes = git(['ls-files', '-z', '--', prefixo]).split('\0').filter(Boolean);
185
+ fs.unlinkSync(local); // somente o alias; nunca o diretório canônico
186
+ fs.mkdirSync(local);
187
+ let materializado = false;
188
+ try {
189
+ if (nomes.length) {
190
+ git(['update-index', '--no-skip-worktree', '--', ...nomes]);
191
+ git(['checkout-index', '--force', '--', ...nomes]);
192
+ }
193
+ materializado = true;
194
+ return executar();
195
+ }
196
+ finally {
197
+ // Uma escrita externa durante a operação é conflito, não estado descartável.
198
+ const mudou = materializado && (!(0, util_1.exec)('git', ['diff', '--quiet', '--', prefixo], worktree).ok ||
199
+ git(['ls-files', '--others', '--', prefixo]).trim() !== '');
200
+ if (mudou)
201
+ throw new Error(`estado local alterado durante operação Git: ${local}; cópia e main preservadas para reconciliação`);
202
+ if (fs.existsSync(local)) {
203
+ const backup = path.join(raizDoEstado(raiz), '.orkastery', 'state-backups', `${id}-git-${(0, node_crypto_1.randomUUID)()}`);
204
+ fs.mkdirSync(path.dirname(backup), { recursive: true });
205
+ fs.renameSync(local, backup);
206
+ }
207
+ // A nova versão do índice pode ter incluído ou removido arquivos de estado.
208
+ vincularEstado(raiz, id, worktree);
209
+ }
210
+ }
211
+ /** Inspeciona o caminho físico, para o resolvedor não esconder uma cópia obsoleta. */
212
+ function auditarEstado(raiz, id, worktree) {
213
+ const main = estadoCanonico(raiz, id), local = path.join(worktree, '.orkastery', 'threads', id);
214
+ const contar = (dir) => {
215
+ const file = path.join(dir, 'ledger.jsonl');
216
+ return fs.existsSync(file) ? fs.readFileSync(file, 'utf8').split('\n').filter(Boolean).length : 0;
217
+ };
218
+ const ok = mesmoArquivo(main, local);
219
+ return { nome: 'estado-unico', nivel: ok ? 'ok' : 'fail',
220
+ detalhe: `${ok ? 'mesmo estado' : 'estado dividido'}: main=${contar(main)} eventos, worktree=${contar(local)} eventos; ${main}`,
221
+ ...(ok ? {} : { correcao: `ork worktree sync ${id}; divergências exigem reconciliação preservando evidências` }) };
222
+ }
@@ -276,6 +276,14 @@ function rodarCanarios(catalogo, filtro) {
276
276
  const falhas = [];
277
277
  const temGit = (0, canarios_1.gitDisponivel)();
278
278
  const nomes = fs.existsSync(dir) ? fs.readdirSync(dir).sort() : [];
279
+ if (filtro && filtro.length === 0) {
280
+ falhas.push({ codigo: 'fixture-invalida', detalhe: 'filtro de canários vazio: nenhuma prova foi executada' });
281
+ }
282
+ for (const id of new Set(filtro ?? [])) {
283
+ if (!nomes.includes(id) || !(0, canarios_1.canarioPorId)(id)) {
284
+ falhas.push({ codigo: 'canario-sem-executor', caso: id, detalhe: `canário solicitado ausente: ${id}` });
285
+ }
286
+ }
279
287
  for (const nome of nomes) {
280
288
  if (!nome.startsWith('fx-'))
281
289
  continue;
@@ -295,6 +303,10 @@ function rodarCanarios(catalogo, filtro) {
295
303
  continue;
296
304
  }
297
305
  const canario = (0, canarios_1.canarioPorId)(fixture.id ?? nome);
306
+ if (fixture.id !== nome) {
307
+ falhas.push({ codigo: 'fixture-invalida', caso: nome, detalhe: 'id da fixture diverge do canário solicitado' });
308
+ continue;
309
+ }
298
310
  if (!canario) {
299
311
  falhas.push({
300
312
  codigo: 'canario-sem-executor',
@@ -0,0 +1,329 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PULSACAO_MS = exports.BRANCH_DA_FABRICA = exports.CONTRATO_MAQUINA = void 0;
37
+ exports.arquivoDaMaquina = arquivoDaMaquina;
38
+ exports.entregasNaBase = entregasNaBase;
39
+ exports.retratoDaMaquina = retratoDaMaquina;
40
+ exports.assinaturaDoRetrato = assinaturaDoRetrato;
41
+ exports.lerFabrica = lerFabrica;
42
+ exports.publicarMaquina = publicarMaquina;
43
+ exports.removerMaquina = removerMaquina;
44
+ exports.registrarPublicacao = registrarPublicacao;
45
+ exports.resumoDasOutrasMaquinas = resumoDasOutrasMaquinas;
46
+ exports.textoDaFabrica = textoDaFabrica;
47
+ exports.textoDasOutrasMaquinas = textoDasOutrasMaquinas;
48
+ exports.painelDaFabricaEmMarkdown = painelDaFabricaEmMarkdown;
49
+ /**
50
+ * I-51 (RM-047, fatia 2): a fabrica vista de qualquer maquina.
51
+ *
52
+ * Cada maquina publica o retrato das proprias threads em `maquinas/<maquina>.json` (contrato
53
+ * `ork.fabrica-maquina/v1`) na branch `ork/fabrica-estado`, com um `FABRICA.md` legivel no
54
+ * GitHub. `ork board`, `ork fabrica` e o resumo do pulse leem as outras maquinas dali. Cada
55
+ * maquina so escreve o proprio arquivo: push recusado se resolve relendo a ponta.
56
+ *
57
+ * Publicar e empurrar para o remoto do projeto, entao so acontece sozinho com
58
+ * `fabrica.compartilhada: true` no manifesto; `ork fabrica publicar` e o caminho explicito. Nada de
59
+ * credencial, prompt, log ou caminho local sai daqui: so identificadores, fase, status e o assunto
60
+ * da pausa que espera o dono.
61
+ *
62
+ * O retrato conta a verdade do git, nao so a do `thread.json`: thread cujo `ship(<thread>)` ja esta
63
+ * na base e entregue, mesmo que ninguem tenha fechado o MASTER dela.
64
+ */
65
+ const node_crypto_1 = require("node:crypto");
66
+ const fs = __importStar(require("node:fs"));
67
+ const path = __importStar(require("node:path"));
68
+ const branch_de_estado_1 = require("./branch-de-estado");
69
+ const monitor_lock_1 = require("./monitor-lock");
70
+ const board_1 = require("./board");
71
+ const estado_thread_1 = require("./estado-thread");
72
+ const horario_1 = require("./horario");
73
+ const modos_1 = require("./modos");
74
+ const orquestracao_1 = require("./orquestracao");
75
+ const roadmap_reservas_1 = require("./roadmap-reservas");
76
+ const util_1 = require("./util");
77
+ const versao_1 = require("./versao");
78
+ exports.CONTRATO_MAQUINA = 'ork.fabrica-maquina/v1';
79
+ exports.BRANCH_DA_FABRICA = 'ork/fabrica-estado';
80
+ const DIR = 'maquinas';
81
+ const PAINEL = 'FABRICA.md';
82
+ const PREFIXO = 'fabrica';
83
+ const TENTATIVAS = 5;
84
+ /** Retrato igual ao ultimo publicado so volta ao remoto depois disto: e o sinal de vida da maquina. */
85
+ exports.PULSACAO_MS = 60 * 60 * 1000;
86
+ const ARQUIVO_DA_MARCA = 'fabrica-publicada.json';
87
+ const ARQUIVO_DO_LOG = 'fabrica.log';
88
+ const curto = (texto, teto = 120) => {
89
+ const limpo = String(texto ?? '').replace(/\s+/g, ' ').trim();
90
+ return limpo.length > teto ? `${limpo.slice(0, teto - 3)}...` : limpo;
91
+ };
92
+ /** O nome do arquivo da maquina na branch: so o que o git e o GitHub aceitam sem surpresa. */
93
+ function arquivoDaMaquina(maquina) {
94
+ const nome = maquina.trim().replace(/[^A-Za-z0-9._-]+/g, '-').replace(/^[.-]+/, '').slice(0, 64);
95
+ if (!nome)
96
+ throw new Error(`fabrica.maquina: nome de maquina invalido ("${maquina}"); defina ORK_MAQUINA`);
97
+ return `${nome}.json`;
98
+ }
99
+ /**
100
+ * As entregas que ja estao na base: `ship(<thread>): ...` e o assunto do merge que o fluxo de
101
+ * entrega usa, o mesmo fato que o `ork docs sincronizar` le. Um `git log` so, para todas.
102
+ */
103
+ function entregasNaBase(raiz, base, remoto = 'origin') {
104
+ const ref = (0, branch_de_estado_1.git)(raiz, ['rev-parse', '--verify', '--quiet', `refs/remotes/${remoto}/${base}`]).ok ? `${remoto}/${base}` : base;
105
+ // Regex basica do git: o `(` e literal.
106
+ const r = (0, branch_de_estado_1.git)(raiz, ['log', ref, '--format=%h%x09%s', '--grep=^ship(']);
107
+ const entregas = new Map();
108
+ if (!r.ok)
109
+ return entregas;
110
+ for (const linha of r.stdout.split('\n')) {
111
+ const m = /^([0-9a-f]+)\tship\(([A-Za-z0-9._-]+)\)/.exec(linha);
112
+ // O log vem do mais novo para o mais velho: a primeira entrega de cada thread e a que vale.
113
+ if (m && !entregas.has(m[2]))
114
+ entregas.set(m[2], m[1]);
115
+ }
116
+ return entregas;
117
+ }
118
+ /** O retrato desta maquina: as threads nao fechadas do projeto, com a verdade do git por cima. */
119
+ function retratoDaMaquina(carregado, opcoes = {}) {
120
+ const quando = opcoes.agora ?? (0, util_1.agora)();
121
+ const raiz = carregado.raiz;
122
+ const remoto = opcoes.remoto ?? carregado.manifesto.fabrica.remoto;
123
+ const itens = (0, board_1.threadsDeTodosOsPerfis)(carregado, false).filter((i) => i.thread.status !== 'fechada');
124
+ const monitor = (0, orquestracao_1.montarMonitor)(carregado, { semRuntime: true, agora: quando });
125
+ const linhas = new Map(monitor.linhas.map((l) => [l.thread, l]));
126
+ const entregas = entregasNaBase(raiz, carregado.manifesto.worktree.base_branch, remoto);
127
+ const itemDaThread = new Map((0, roadmap_reservas_1.reservasLocais)(raiz, remoto).filter((r) => r.thread).map((r) => [r.thread, r.item]));
128
+ const threads = itens.map(({ thread: t }) => {
129
+ const pausa = linhas.get(t.id)?.pausas[0];
130
+ const entregue = entregas.get(t.id) ?? null;
131
+ const esperaVoce = !entregue && linhas.get(t.id)?.precisaDeHumano === true;
132
+ return {
133
+ id: t.id, nome: curto(t.nome, 100), modo: (0, modos_1.tagDoModo)(t.modo), fase: t.faseAtual, status: t.status,
134
+ roadmap: t.roadmap ?? itemDaThread.get(t.id) ?? null, branch: t.base?.branch ?? null,
135
+ atualizadaEm: t.atualizadaEm ?? null, entregue,
136
+ esperaVoce, pergunta: esperaVoce && pausa ? curto(pausa.pausaSobre || pausa.detalhe) : null,
137
+ paradaDesde: esperaVoce && pausa ? pausa.desdeEm : null,
138
+ };
139
+ }).sort((a, b) => a.id.localeCompare(b.id));
140
+ const eu = (0, roadmap_reservas_1.quemSouEu)(raiz, { por: opcoes.por, maquina: opcoes.maquina });
141
+ return { contrato: exports.CONTRATO_MAQUINA, maquina: eu.maquina, por: eu.por, projeto: carregado.manifesto.project.name,
142
+ versaoOrk: versao_1.VERSAO_DO_ORK, publicadoEm: quando, threads };
143
+ }
144
+ /** O que muda o retrato (sem o carimbo de hora): retrato igual nao precisa de push. */
145
+ function assinaturaDoRetrato(e) {
146
+ const { publicadoEm: _publicadoEm, ...resto } = e;
147
+ return (0, node_crypto_1.createHash)('sha256').update(JSON.stringify(resto)).digest('hex');
148
+ }
149
+ function estadoValido(bruto) {
150
+ const e = bruto;
151
+ return !!e && e.contrato === exports.CONTRATO_MAQUINA && typeof e.maquina === 'string' && !!e.maquina.trim() &&
152
+ typeof e.publicadoEm === 'string' && Array.isArray(e.threads) &&
153
+ e.threads.every((t) => !!t && typeof t.id === 'string' && typeof t.fase === 'string');
154
+ }
155
+ function maquinasDaPonta(raiz, ponta) {
156
+ return (0, branch_de_estado_1.jsonsDaPonta)(raiz, ponta, DIR, PREFIXO).filter(estadoValido).sort((a, b) => a.maquina.localeCompare(b.maquina));
157
+ }
158
+ /** `ork fabrica` e `ork board`: todas as maquinas, lidas do remoto (ou da ultima copia, sem rede). */
159
+ function lerFabrica(raiz, opcoes = {}) {
160
+ const remoto = opcoes.remoto ?? 'origin';
161
+ if (opcoes.semRemoto) {
162
+ const ponta = (0, branch_de_estado_1.pontaLocal)(raiz, remoto, exports.BRANCH_DA_FABRICA);
163
+ return { maquinas: maquinasDaPonta(raiz, ponta), atualizado: false, ponta };
164
+ }
165
+ const { ponta, atualizado } = (0, branch_de_estado_1.buscarBranch)(raiz, remoto, exports.BRANCH_DA_FABRICA, PREFIXO, opcoes.timeoutMs ?? 15000);
166
+ return { maquinas: maquinasDaPonta(raiz, ponta), atualizado, ponta };
167
+ }
168
+ const dirDoMonitor = (raiz) => path.join((0, estado_thread_1.raizDoEstado)(raiz), '.orkastery', 'monitor');
169
+ function lerMarca(raiz) {
170
+ try {
171
+ return JSON.parse(fs.readFileSync(path.join(dirDoMonitor(raiz), ARQUIVO_DA_MARCA), 'utf8'));
172
+ }
173
+ catch {
174
+ return null;
175
+ }
176
+ }
177
+ function gravarMarca(raiz, marca) {
178
+ const dir = dirDoMonitor(raiz);
179
+ fs.mkdirSync(dir, { recursive: true });
180
+ fs.writeFileSync(path.join(dir, ARQUIVO_DA_MARCA), JSON.stringify(marca, null, 2) + '\n', { mode: 0o600 });
181
+ }
182
+ /**
183
+ * `ork fabrica publicar`: grava o retrato desta maquina na branch. Retrato igual ao ultimo, dentro
184
+ * da pulsacao, nem busca o remoto; `forcar` publica mesmo assim.
185
+ */
186
+ function publicarMaquina(carregado, opcoes = {}) {
187
+ const raiz = carregado.raiz;
188
+ const remoto = opcoes.remoto ?? carregado.manifesto.fabrica.remoto;
189
+ const estado = retratoDaMaquina(carregado, { ...opcoes, remoto });
190
+ const assinatura = assinaturaDoRetrato(estado);
191
+ const marca = lerMarca(raiz);
192
+ if (!opcoes.forcar && marca && marca.assinatura === assinatura && marca.remoto === remoto && marca.maquina === estado.maquina &&
193
+ Date.parse(estado.publicadoEm) - Date.parse(marca.em) < exports.PULSACAO_MS) {
194
+ return { acao: 'sem-mudanca', maquina: estado.maquina, threads: estado.threads.length, commit: marca.commit, tentativas: 0 };
195
+ }
196
+ const arquivo = `${DIR}/${arquivoDaMaquina(estado.maquina)}`;
197
+ // I-57: uma publicacao por vez nesta maquina. Rajada de eventos (lote, fases em paralelo) nao
198
+ // vira N pushes disputando a mesma branch; quem chega depois sai, e a proxima batida publica.
199
+ fs.mkdirSync(dirDoMonitor(raiz), { recursive: true });
200
+ const trava = (0, monitor_lock_1.adquirirLockMonitor)(path.join(dirDoMonitor(raiz), 'fabrica.lock'));
201
+ if (!trava.ok)
202
+ return { acao: 'ocupado', maquina: estado.maquina, threads: estado.threads.length, commit: null, tentativas: 0 };
203
+ try {
204
+ for (let tentativa = 1; tentativa <= TENTATIVAS; tentativa++) {
205
+ const { ponta, atualizado } = (0, branch_de_estado_1.buscarBranch)(raiz, remoto, exports.BRANCH_DA_FABRICA, PREFIXO, 30000);
206
+ if (!atualizado)
207
+ throw new Error(`fabrica.sem-remoto: nao consegui ler ${exports.BRANCH_DA_FABRICA} em ${remoto}; publicar exige rede`);
208
+ const todas = [...maquinasDaPonta(raiz, ponta).filter((m) => m.maquina !== estado.maquina), estado]
209
+ .sort((a, b) => a.maquina.localeCompare(b.maquina));
210
+ const commit = (0, branch_de_estado_1.gravarNaBranch)(raiz, remoto, exports.BRANCH_DA_FABRICA, ponta, [
211
+ { caminho: arquivo, conteudo: JSON.stringify(estado, null, 2) + '\n' },
212
+ { caminho: PAINEL, conteudo: painelDaFabricaEmMarkdown(todas) },
213
+ ], `fabrica: ${estado.maquina} publicou ${estado.threads.length} thread(s)`, PREFIXO);
214
+ if (commit) {
215
+ gravarMarca(raiz, { assinatura, em: estado.publicadoEm, commit, remoto, maquina: estado.maquina });
216
+ return { acao: 'publicou', maquina: estado.maquina, threads: estado.threads.length, commit, tentativas: tentativa };
217
+ }
218
+ }
219
+ throw new Error(`fabrica.concorrencia: ${TENTATIVAS} pushes recusados seguidos; tente de novo em instantes`);
220
+ }
221
+ finally {
222
+ trava.liberar();
223
+ }
224
+ }
225
+ /** `ork fabrica sair`: tira o retrato desta maquina da branch. `null` quando nao havia retrato. */
226
+ function removerMaquina(carregado, opcoes = {}) {
227
+ const raiz = carregado.raiz;
228
+ const remoto = opcoes.remoto ?? carregado.manifesto.fabrica.remoto;
229
+ const maquina = (0, roadmap_reservas_1.quemSouEu)(raiz, { maquina: opcoes.maquina }).maquina;
230
+ for (let tentativa = 1; tentativa <= TENTATIVAS; tentativa++) {
231
+ const { ponta, atualizado } = (0, branch_de_estado_1.buscarBranch)(raiz, remoto, exports.BRANCH_DA_FABRICA, PREFIXO, 30000);
232
+ if (!atualizado)
233
+ throw new Error(`fabrica.sem-remoto: nao consegui ler ${exports.BRANCH_DA_FABRICA} em ${remoto}; sair exige rede`);
234
+ const todas = maquinasDaPonta(raiz, ponta);
235
+ if (!todas.some((m) => m.maquina === maquina))
236
+ return null;
237
+ const restantes = todas.filter((m) => m.maquina !== maquina);
238
+ const commit = (0, branch_de_estado_1.gravarNaBranch)(raiz, remoto, exports.BRANCH_DA_FABRICA, ponta, [
239
+ { caminho: `${DIR}/${arquivoDaMaquina(maquina)}`, conteudo: null },
240
+ { caminho: PAINEL, conteudo: painelDaFabricaEmMarkdown(restantes) },
241
+ ], `fabrica: ${maquina} saiu`, PREFIXO);
242
+ if (commit) {
243
+ try {
244
+ fs.rmSync(path.join(dirDoMonitor(raiz), ARQUIVO_DA_MARCA), { force: true });
245
+ }
246
+ catch { /* marca local */ }
247
+ return commit;
248
+ }
249
+ }
250
+ throw new Error(`fabrica.concorrencia: ${TENTATIVAS} pushes recusados seguidos; tente de novo em instantes`);
251
+ }
252
+ /** Uma linha JSON por publicacao em segundo plano: quem publica sozinho precisa deixar rastro. */
253
+ function registrarPublicacao(raiz, registro) {
254
+ try {
255
+ const dir = dirDoMonitor(raiz);
256
+ fs.mkdirSync(dir, { recursive: true });
257
+ fs.appendFileSync(path.join(dir, ARQUIVO_DO_LOG), JSON.stringify({ ts: (0, util_1.agora)(), ...registro }) + '\n', { mode: 0o600 });
258
+ }
259
+ catch { /* o log e informativo */ }
260
+ }
261
+ const ativas = (e) => e.threads.filter((t) => !t.entregue);
262
+ /** O que o resumo do pulse mostra das outras maquinas: so ativas, e quem espera o dono. */
263
+ function resumoDasOutrasMaquinas(p, eu) {
264
+ return p.maquinas.filter((m) => m.maquina !== eu).map((m) => ({
265
+ maquina: m.maquina, publicadoEm: m.publicadoEm, ativas: ativas(m).length,
266
+ esperando: ativas(m).filter((t) => t.esperaVoce).map((t) => ({ thread: t.id, fase: t.fase, pergunta: t.pergunta,
267
+ desdeEm: t.paradaDesde })),
268
+ })).filter((m) => m.ativas > 0);
269
+ }
270
+ function tabela(cabecalho, linhas) {
271
+ const largura = cabecalho.map((c, i) => Math.max(c.length, ...linhas.map((l) => l[i].length)));
272
+ const linha = (l) => ' ' + l.map((c, i) => c.padEnd(largura[i])).join(' ').trimEnd();
273
+ return [linha(cabecalho), linha(largura.map((n) => '-'.repeat(n))), ...linhas.map(linha)];
274
+ }
275
+ function blocoDaMaquina(m, eu) {
276
+ const vivas = ativas(m), entregues = m.threads.length - vivas.length;
277
+ const cabeca = `${m.maquina}${m.maquina === eu ? ' (esta maquina)' : ''}, ${m.por}, publicado ${(0, horario_1.formatarDataHora)(m.publicadoEm)}: ` +
278
+ `${vivas.length} thread(s) ativa(s)` + (entregues ? `, ${entregues} entregue(s) sem MASTER` : '');
279
+ if (vivas.length === 0)
280
+ return [cabeca];
281
+ return [cabeca, ...tabela(['THREAD', 'MODO', 'FASE', 'ITEM', 'ESPERA VOCE'], vivas.map((t) => [
282
+ t.id, t.modo, t.fase, t.roadmap ?? '-', t.esperaVoce ? `sim: ${curto(t.pergunta ?? 'veredito', 40)}` : '-',
283
+ ]))];
284
+ }
285
+ /** `ork fabrica`: todas as maquinas. */
286
+ function textoDaFabrica(p, eu) {
287
+ const linhas = [];
288
+ if (!p.atualizado)
289
+ linhas.push('AVISO: sem leitura nova do remoto; esta e a ultima copia lida nesta maquina.', '');
290
+ if (p.maquinas.length === 0) {
291
+ linhas.push(`Nenhuma maquina publicou ainda em ${exports.BRANCH_DA_FABRICA}. Publique esta com: ork fabrica publicar`);
292
+ return linhas.join('\n');
293
+ }
294
+ for (const m of p.maquinas)
295
+ linhas.push(...blocoDaMaquina(m, eu), '');
296
+ linhas.push((0, horario_1.legendaDoFuso)());
297
+ return linhas.join('\n');
298
+ }
299
+ /** A secao de `ork board` com as outras maquinas. */
300
+ function textoDasOutrasMaquinas(p, eu) {
301
+ const outras = p.maquinas.filter((m) => m.maquina !== eu);
302
+ const origem = p.atualizado ? 'lido agora' : 'ultima copia local';
303
+ if (outras.length === 0)
304
+ return `Outras maquinas (${exports.BRANCH_DA_FABRICA}, ${origem}): nenhuma publicou ainda.`;
305
+ const linhas = [`Outras maquinas (${exports.BRANCH_DA_FABRICA}, ${origem}):`];
306
+ for (const m of outras)
307
+ linhas.push(...blocoDaMaquina(m, eu).map((l, i) => (i === 0 ? ` ${l}` : l)));
308
+ return linhas.join('\n');
309
+ }
310
+ /** O `FABRICA.md` da branch: o mesmo retrato, para quem abre o GitHub. */
311
+ function painelDaFabricaEmMarkdown(maquinas) {
312
+ const linhas = [
313
+ '# Fábrica',
314
+ '',
315
+ 'O que cada máquina está conduzindo agora. Gerado pelo `ork fabrica publicar`; não edite à mão.',
316
+ 'Antes de pegar um item do roadmap: `ork roadmap reservas`.',
317
+ '',
318
+ '| Máquina | Thread | Modo | Fase | Item | Espera você | Publicado |',
319
+ '| --- | --- | --- | --- | --- | --- | --- |',
320
+ ];
321
+ const vivas = maquinas.flatMap((m) => ativas(m).map((t) => ({ m, t })));
322
+ if (vivas.length === 0)
323
+ linhas.push('| — | nenhuma thread ativa | — | — | — | — | — |');
324
+ for (const { m, t } of vivas) {
325
+ const pergunta = t.esperaVoce ? `sim: ${curto(t.pergunta ?? 'veredito', 60).replace(/\|/g, '/')}` : '—';
326
+ linhas.push(`| ${m.maquina} | ${t.id} | ${t.modo} | ${t.fase} | ${t.roadmap ?? '—'} | ${pergunta} | ${(0, horario_1.formatarDataHora)(m.publicadoEm)} |`);
327
+ }
328
+ return [...linhas, '', (0, horario_1.legendaDoFuso)(), ''].join('\n');
329
+ }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.publicarEmSegundoPlano = publicarEmSegundoPlano;
37
+ /**
38
+ * I-51 (RM-047): a publicacao do estado da fabrica em segundo plano.
39
+ *
40
+ * Modulo a parte, com o minimo de dependencias, porque quem chama (despacho de fase, entrega,
41
+ * thread nova) nao pode esperar rede nem arrastar o retrato inteiro para dentro de si.
42
+ */
43
+ const node_child_process_1 = require("node:child_process");
44
+ const fs = __importStar(require("node:fs"));
45
+ const path = __importStar(require("node:path"));
46
+ const maquina_1 = require("./maquina");
47
+ const manifest_1 = require("./manifest");
48
+ /**
49
+ * Publica sem segurar quem chamou (thread nova, despacho de fase, entrega). So com a fabrica
50
+ * compartilhada nesta maquina (`ork fabrica entrar`) ou no projeto; `ORK_FABRICA_PUBLICAR=0`
51
+ * desliga (testes e `ork eval`).
52
+ */
53
+ function publicarEmSegundoPlano(raiz) {
54
+ if (process.env.ORK_FABRICA_PUBLICAR === '0')
55
+ return false;
56
+ let carregado;
57
+ try {
58
+ carregado = (0, manifest_1.carregarManifesto)(raiz);
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ if (!carregado || carregado.erros.length > 0 || !(0, maquina_1.fabricaCompartilhada)(carregado.manifesto))
64
+ return false;
65
+ const cli = path.join(__dirname, 'index.js');
66
+ if (!fs.existsSync(cli))
67
+ return false;
68
+ try {
69
+ const filho = (0, node_child_process_1.spawn)(process.execPath, [cli, 'fabrica', 'publicar', '--silencioso'], { cwd: carregado.raiz, detached: true, stdio: 'ignore', env: process.env });
70
+ filho.unref();
71
+ return true;
72
+ }
73
+ catch {
74
+ return false;
75
+ }
76
+ }