@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
@@ -0,0 +1,289 @@
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.ORDEM_DAS_FORJAS = void 0;
37
+ exports.pastasDeBinarios = pastasDeBinarios;
38
+ exports.acharBinario = acharBinario;
39
+ exports.versaoDoBinario = versaoDoBinario;
40
+ exports.comGitIsolado = comGitIsolado;
41
+ exports.forjaPorNome = forjaPorNome;
42
+ exports.forjasDaMaquina = forjasDaMaquina;
43
+ exports.ehNomeDeForja = ehNomeDeForja;
44
+ /**
45
+ * RM-053: a forja da pessoa (GitHub pelo `gh`, GitLab pelo `glab`) atras de uma interface so.
46
+ *
47
+ * D4: a identidade vem da CLI da forja ja autenticada. Daqui so sai o LOGIN (o campo `login` de
48
+ * `gh api user`, o `username` de `glab api user`); plano, e-mail e qualquer outro campo da resposta
49
+ * sao descartados na leitura. O git autentica pelo helper de credencial da propria CLI
50
+ * (`gh auth git-credential`, `glab auth git-credential`), configurado so no cache bare da rede: o
51
+ * `ork` nunca le, copia nem guarda token.
52
+ *
53
+ * D11: binario e procurado no PATH e nas pastas de usuario comuns. O cron do pulse tem PATH curto,
54
+ * e sem isso o retrato da maquina oscilaria entre a batida e o terminal. `ORK_BINARIOS_EXTRA`
55
+ * substitui a lista de pastas extras (os testes zeram, para nunca achar a forja de verdade).
56
+ */
57
+ const node_child_process_1 = require("node:child_process");
58
+ const fs = __importStar(require("node:fs"));
59
+ const os = __importStar(require("node:os"));
60
+ const path = __importStar(require("node:path"));
61
+ const redacao_url_1 = require("./redacao-url");
62
+ exports.ORDEM_DAS_FORJAS = ['github', 'gitlab'];
63
+ const LOGIN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,254})$/;
64
+ const HOST = /^[A-Za-z0-9.-]+(?::\d+)?$/;
65
+ const VERSAO = /\b(\d+\.\d+(?:\.\d+)?(?:[-+][0-9A-Za-z.]{1,20})?)\b/;
66
+ const ambienteDe = (amb) => amb.env ?? process.env;
67
+ const homeDe = (amb) => amb.home ?? os.homedir();
68
+ /** As pastas onde a rede procura binarios: PATH primeiro, depois as de usuario (D11). */
69
+ function pastasDeBinarios(amb = {}) {
70
+ const env = ambienteDe(amb), home = homeDe(amb);
71
+ const doPath = (env.PATH ?? '').split(path.delimiter).filter(Boolean);
72
+ const extras = env.ORK_BINARIOS_EXTRA !== undefined
73
+ ? env.ORK_BINARIOS_EXTRA.split(path.delimiter).filter(Boolean)
74
+ : [...['.local/bin', '.npm-global/bin', 'bin', '.bun/bin', '.volta/bin', '.claude/local'].map((d) => path.join(home, d)),
75
+ '/usr/local/bin', '/opt/homebrew/bin'];
76
+ return [...new Set([...doPath, ...extras])];
77
+ }
78
+ /** O primeiro executavel com esse nome nas pastas da rede, ou `null`. */
79
+ function acharBinario(nome, amb = {}) {
80
+ for (const dir of pastasDeBinarios(amb)) {
81
+ const candidato = path.join(dir, nome);
82
+ try {
83
+ fs.accessSync(candidato, fs.constants.X_OK);
84
+ if (fs.statSync(candidato).isFile())
85
+ return candidato;
86
+ }
87
+ catch { /* proxima pasta */ }
88
+ }
89
+ return null;
90
+ }
91
+ /** Roda a CLI sem prompt nem cor; a saida de erro so volta curta e sem credencial em URL. */
92
+ function rodar(binario, args, amb, timeoutMs) {
93
+ const env = { ...ambienteDe(amb), GH_PROMPT_DISABLED: '1', GH_NO_UPDATE_NOTIFIER: '1', NO_PROMPT: '1', NO_COLOR: '1' };
94
+ const r = (0, node_child_process_1.spawnSync)(binario, args, { encoding: 'utf8', timeout: timeoutMs, env, maxBuffer: 1024 * 1024 });
95
+ return { ok: r.status === 0, stdout: (r.stdout ?? '').toString(), stderr: (r.stderr ?? '').toString() };
96
+ }
97
+ /** A versao do binario (`<bin> --version`), so quando casa o formato de versao; o resto vira `null`. */
98
+ function versaoDoBinario(binario, amb = {}) {
99
+ const r = rodar(binario, ['--version'], amb, 5000);
100
+ const m = VERSAO.exec(`${r.stdout}\n${r.stderr}`.slice(0, 400));
101
+ return m && m[1].length <= 40 ? m[1] : null;
102
+ }
103
+ function json(texto) {
104
+ try {
105
+ const v = JSON.parse(texto);
106
+ return v && typeof v === 'object' && !Array.isArray(v) ? v : null;
107
+ }
108
+ catch {
109
+ return null;
110
+ }
111
+ }
112
+ /** Variaveis que redirecionam o git para outro repositorio (um hook do git exporta GIT_DIR). */
113
+ const GIT_REDIRECIONA = ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_COMMON_DIR', 'GIT_OBJECT_DIRECTORY',
114
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES', 'GIT_NAMESPACE', 'GIT_QUARANTINE_PATH', 'GIT_PREFIX'];
115
+ /**
116
+ * Roda `f` com o git da rede isolado do ambiente de quem chamou (GO-FIX 1 do CHECK 1 e GO-FIX 2):
117
+ * - sem as variaveis que redirecionam o repositorio: chamado de um hook, o git do cache miraria o
118
+ * projeto e regravaria a config dele (A2);
119
+ * - sem a config que um hook injeta (`GIT_CONFIG_PARAMETERS`, `GIT_CONFIG_COUNT/KEY_n/VALUE_n`):
120
+ * um `-c credential.helper=` zeraria o helper da forja no cache (S5);
121
+ * - sem prompt: nem terminal, nem askpass (`GIT_ASKPASS` vazio desliga tambem `core.askPass` e
122
+ * `SSH_ASKPASS`), nem SSH interativo, porque a batida roda sem ninguem olhando (M5, S5);
123
+ * - em ingles, porque o nucleo interpreta o stderr do git (B5);
124
+ * - com autor e committer fixos na maquina, quando ha identidade: `GIT_AUTHOR_EMAIL` e cia. do
125
+ * ambiente poriam o e-mail real da pessoa nos commits da casa (B8).
126
+ * O ambiente volta como estava na saida, inclusive em chamada aninhada.
127
+ */
128
+ function comGitIsolado(f, identidade, opcoes = {}) {
129
+ const antes = new Map();
130
+ const trocar = (k, v) => {
131
+ if (!antes.has(k))
132
+ antes.set(k, process.env[k]);
133
+ if (v === undefined)
134
+ delete process.env[k];
135
+ else
136
+ process.env[k] = v;
137
+ };
138
+ for (const k of GIT_REDIRECIONA)
139
+ trocar(k, undefined);
140
+ for (const k of Object.keys(process.env))
141
+ if (/^GIT_CONFIG_(?:PARAMETERS|COUNT|KEY_\d+|VALUE_\d+)$/.test(k))
142
+ trocar(k, undefined);
143
+ trocar('GIT_TERMINAL_PROMPT', '0');
144
+ trocar('GIT_ASKPASS', '');
145
+ trocar('SSH_ASKPASS_REQUIRE', 'never');
146
+ // S4: `GIT_SSH_COMMAND` vence `core.sshCommand`; so entra quando ninguem disse como rodar o ssh.
147
+ if ((opcoes.ssh ?? 'lote') === 'lote' && !process.env.GIT_SSH_COMMAND && !process.env.GIT_SSH)
148
+ trocar('GIT_SSH_COMMAND', 'ssh -o BatchMode=yes');
149
+ trocar('LC_ALL', 'C');
150
+ trocar('LANGUAGE', 'C');
151
+ const quem = identidade ?? opcoes.identidade;
152
+ if (quem) {
153
+ for (const papel of ['AUTHOR', 'COMMITTER']) {
154
+ trocar(`GIT_${papel}_NAME`, quem.nome);
155
+ trocar(`GIT_${papel}_EMAIL`, quem.email);
156
+ }
157
+ }
158
+ try {
159
+ return f();
160
+ }
161
+ finally {
162
+ for (const [k, v] of antes) {
163
+ if (v === undefined)
164
+ delete process.env[k];
165
+ else
166
+ process.env[k] = v;
167
+ }
168
+ }
169
+ }
170
+ const naoEncontrado = (s) => /HTTP 404|404 Not Found|"status"\s*:\s*"?404/i.test(`${s.stdout}\n${s.stderr}`);
171
+ function falhou(cli, rota, s) {
172
+ const linha = (0, redacao_url_1.redigirCredenciaisUrl)(s.stderr.trim().split('\n').pop() ?? '').slice(0, 200);
173
+ return new Error(`rede.forja: ${cli} api ${rota} falhou${linha ? `: ${linha}` : ''}`);
174
+ }
175
+ /** A resposta de quem nao tem login (ou tem credencial recusada), no `gh` e no `glab`. */
176
+ const SEM_LOGIN = /auth login|not logged|not authenticated|authentication|HTTP 401|401 Unauthorized|Bad credentials|no token/i;
177
+ /** `usuario: null` so e falta de login quando a forja disse isso; o resto e falha da leitura. */
178
+ function falhaDoLogin(cli, s) {
179
+ if (!s.ok && SEM_LOGIN.test(`${s.stderr}\n${s.stdout}`))
180
+ return {};
181
+ if (s.ok)
182
+ return { falha: `${cli} api user devolveu uma resposta sem login legivel` };
183
+ return { falha: falhou(cli, 'user', s.stderr.trim() ? s : { ...s, stderr: 'sem resposta da forja no prazo' }).message.replace(/^rede\.forja: /, '') };
184
+ }
185
+ /** Caminho seguro para o shell que o git usa no helper: aspas simples, com `'` escapada (B10). */
186
+ const citar = (caminho) => /^[A-Za-z0-9_./+-]+$/.test(caminho) ? caminho : `'${caminho.replace(/'/g, `'\\''`)}'`;
187
+ const helper = (binario) => `!${citar(binario)} auth git-credential`;
188
+ /**
189
+ * Privado de verdade (B1): no GitHub, `internal` vem com `private: true`, e a empresa inteira le.
190
+ * Quando a forja diz a visibilidade, so `private` conta; sem ela, o booleano.
191
+ */
192
+ function privadoDe(v) {
193
+ if (typeof v.visibility === 'string')
194
+ return v.visibility === 'private';
195
+ return typeof v.private === 'boolean' ? v.private : null;
196
+ }
197
+ function github(binario, amb, host = 'github.com') {
198
+ // B2: a casa vai sempre por HTTPS, com o helper da propria CLI; SSH pediria agente, que o cron nao tem.
199
+ const repositorioDe = (v) => {
200
+ if (!v)
201
+ throw new Error('rede.forja: gh devolveu um repositorio ilegivel');
202
+ return { existe: true, privado: privadoDe(v), url: typeof v.clone_url === 'string' ? v.clone_url : null };
203
+ };
204
+ // B9: o host da casa vai em toda chamada; o cron nao tem o ambiente do terminal.
205
+ const api = (args, timeoutMs) => rodar(binario, ['api', '--hostname', host, ...args], amb, timeoutMs);
206
+ return {
207
+ nome: 'github', host, cli: 'gh', binario,
208
+ identidade() {
209
+ const r = api(['user'], 15000);
210
+ const login = r.ok ? json(r.stdout)?.login : null;
211
+ const usuario = typeof login === 'string' && LOGIN.test(login) ? login : null;
212
+ return { forja: 'github', host, cli: 'gh', versao: versaoDoBinario(binario, amb), usuario, ...(usuario ? {} : falhaDoLogin('gh', r)) };
213
+ },
214
+ repositorio(dono, nome) {
215
+ const rota = `repos/${dono}/${nome}`;
216
+ const r = api([rota], 15000);
217
+ if (!r.ok) {
218
+ if (naoEncontrado(r))
219
+ return { existe: false, privado: null, url: null };
220
+ throw falhou('gh', rota, r);
221
+ }
222
+ return repositorioDe(json(r.stdout));
223
+ },
224
+ criarPrivado(nome, descricao) {
225
+ const r = api(['-X', 'POST', 'user/repos', '-f', `name=${nome}`, '-F', 'private=true',
226
+ '-f', `description=${descricao}`, '-F', 'has_issues=false', '-F', 'has_wiki=false'], 30000);
227
+ if (!r.ok)
228
+ throw falhou('gh', 'user/repos', r);
229
+ return repositorioDe(json(r.stdout));
230
+ },
231
+ helperDeCredencial: () => helper(binario),
232
+ };
233
+ }
234
+ function hostDoGitlab(amb) {
235
+ const bruto = (ambienteDe(amb).GITLAB_HOST ?? '').trim().replace(/^https?:\/\//, '').replace(/\/.*$/, '');
236
+ return HOST.test(bruto) ? bruto : 'gitlab.com';
237
+ }
238
+ function gitlab(binario, amb, hostPedido) {
239
+ const host = hostPedido ?? hostDoGitlab(amb);
240
+ const repositorioDe = (v) => {
241
+ if (!v)
242
+ throw new Error('rede.forja: glab devolveu um projeto ilegivel');
243
+ return { existe: true, privado: privadoDe(v), url: typeof v.http_url_to_repo === 'string' ? v.http_url_to_repo : null };
244
+ };
245
+ const api = (args, timeoutMs) => rodar(binario, ['api', '--hostname', host, ...args], amb, timeoutMs);
246
+ return {
247
+ nome: 'gitlab', host, cli: 'glab', binario,
248
+ identidade() {
249
+ const r = api(['user'], 15000);
250
+ const login = r.ok ? json(r.stdout)?.username : null;
251
+ const usuario = typeof login === 'string' && LOGIN.test(login) ? login : null;
252
+ return { forja: 'gitlab', host, cli: 'glab', versao: versaoDoBinario(binario, amb), usuario, ...(usuario ? {} : falhaDoLogin('glab', r)) };
253
+ },
254
+ repositorio(dono, nome) {
255
+ const rota = `projects/${encodeURIComponent(`${dono}/${nome}`)}`;
256
+ const r = api([rota], 15000);
257
+ if (!r.ok) {
258
+ if (naoEncontrado(r))
259
+ return { existe: false, privado: null, url: null };
260
+ throw falhou('glab', rota, r);
261
+ }
262
+ return repositorioDe(json(r.stdout));
263
+ },
264
+ criarPrivado(nome, descricao) {
265
+ const r = api(['-X', 'POST', 'projects', '-f', `name=${nome}`, '-f', 'visibility=private',
266
+ '-f', `description=${descricao}`], 30000);
267
+ if (!r.ok)
268
+ throw falhou('glab', 'projects', r);
269
+ return repositorioDe(json(r.stdout));
270
+ },
271
+ helperDeCredencial: () => helper(binario),
272
+ };
273
+ }
274
+ const CLI_DA_FORJA = { github: 'gh', gitlab: 'glab' };
275
+ /** A forja pelo nome, quando a CLI dela existe nesta maquina; `host` e o da casa gravada, quando ha. */
276
+ function forjaPorNome(nome, amb = {}, host) {
277
+ const binario = acharBinario(CLI_DA_FORJA[nome], amb);
278
+ if (!binario)
279
+ return null;
280
+ const h = host && HOST.test(host) ? host : undefined;
281
+ return nome === 'github' ? github(binario, amb, h) : gitlab(binario, amb, h);
282
+ }
283
+ /** As forjas cuja CLI existe nesta maquina, GitHub primeiro. */
284
+ function forjasDaMaquina(amb = {}) {
285
+ return exports.ORDEM_DAS_FORJAS.map((n) => forjaPorNome(n, amb)).filter((f) => f !== null);
286
+ }
287
+ function ehNomeDeForja(v) {
288
+ return v === 'github' || v === 'gitlab';
289
+ }
@@ -0,0 +1,239 @@
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.ehNomeDeProjeto = exports.INVISIVEL = exports.emUmaLinha = exports.CONTRATO_DO_REGISTRO = void 0;
37
+ exports.limparRemoto = limparRemoto;
38
+ exports.lerRegistroDeProjetos = lerRegistroDeProjetos;
39
+ exports.projetoDoDiretorio = projetoDoDiretorio;
40
+ exports.projetosConhecidos = projetosConhecidos;
41
+ /**
42
+ * RM-053 (D7): os projetos conhecidos desta maquina, na fronteira com a RM-052.
43
+ *
44
+ * A RM-052 e a dona do registro `~/.orkastery/projetos.json` (`ork.projetos/v1`); a rede so o LE,
45
+ * por este adaptador, e nunca o grava. Campos lidos: `nome`, `raiz` (ou `caminho`) e `remoto` (ou
46
+ * `remotos`, com preferencia por `origin`); o resto e ignorado. Se a forma final da RM-052 mudar,
47
+ * so este arquivo muda.
48
+ *
49
+ * Enquanto o registro nao existe (ou e de outra versao), a lista cai na reserva: o projeto do
50
+ * diretorio atual e os projetos do ultimo retrato desta maquina cujo caminho ainda tem manifesto.
51
+ * A reserva do retrato e o que impede a lista de oscilar entre os crons de projetos diferentes.
52
+ *
53
+ * Remoto sai sem credencial: usuario e senha da URL somem inteiros, nao so redigidos.
54
+ */
55
+ const fs = __importStar(require("node:fs"));
56
+ const path = __importStar(require("node:path"));
57
+ const branch_de_estado_1 = require("./branch-de-estado");
58
+ const estado_thread_1 = require("./estado-thread");
59
+ const manifest_1 = require("./manifest");
60
+ const maquina_1 = require("./maquina");
61
+ const rede_forja_1 = require("./rede-forja");
62
+ const saida_segura_1 = require("./saida-segura");
63
+ Object.defineProperty(exports, "emUmaLinha", { enumerable: true, get: function () { return saida_segura_1.emUmaLinha; } });
64
+ Object.defineProperty(exports, "INVISIVEL", { enumerable: true, get: function () { return saida_segura_1.INVISIVEL; } });
65
+ exports.CONTRATO_DO_REGISTRO = 'ork.projetos/v1';
66
+ /**
67
+ * V3 da revisao 4 e W1 da revisao 5: a regra UNICA de nome de projeto. Cabe no leitor
68
+ * (`normalizarRetrato`: sem caractere invisivel, ate 80 unidades UTF-16) e pede mais: sem espaco
69
+ * nas pontas, com algo visivel e sem `://`. O registro e o ultimo retrato a aplicam na leitura; o
70
+ * escritor tira do retrato, com aviso, o projeto do diretorio atual que a descumpre. Nome de pasta
71
+ * comum (`c++-tools`, `app (1)`, `r&d`, `Orçamento`) passa: o que o Markdown executaria, a celula
72
+ * do `REDE.md` escapa.
73
+ */
74
+ const ehNomeDeProjeto = (nome) => typeof nome === 'string' && nome.length > 0 && nome.length <= 80 && nome === nome.trim() && !saida_segura_1.INVISIVEL.test(nome)
75
+ // X3 da revisao 6: ao menos uma letra, digito, pontuacao ou simbolo (um nome so de ZWJ nao se ve).
76
+ // X1: nada de `://`, que o GitHub e o GitLab transformam em link no `REDE.md`.
77
+ && /[\p{L}\p{N}\p{P}\p{S}]/u.test(nome) && !nome.includes('://');
78
+ exports.ehNomeDeProjeto = ehNomeDeProjeto;
79
+ /** Esquemas de remoto que o retrato mostra; outro esquema vira `null`. */
80
+ const ESQUEMAS = new Set(['https', 'http', 'ssh', 'git', 'git+ssh', 'ssh+git']);
81
+ /** Host: nome DNS ou IPv4 em conjunto fechado, ou IPv6 entre colchetes. */
82
+ const HOST_DO_REMOTO = /^(?:[A-Za-z0-9](?:[A-Za-z0-9.-]{0,251}[A-Za-z0-9])?|\[[0-9A-Fa-f:.]{2,45}\])$/;
83
+ /** Caminho: sem `@`, `:`, `\`, `?` ou `#`; `%` so fora de `%40` (@) e `%3A` (:). */
84
+ const CAMINHO_DO_REMOTO = /^\/(?:[A-Za-z0-9._~/+-]|%(?!40|3[Aa])[0-9A-Fa-f]{2})*$/;
85
+ /**
86
+ * O remoto como pode ir ao retrato, MONTADO a partir de partes validadas (GO-FIX 3, U1 da revisao 3).
87
+ *
88
+ * Tres rodadas de revisao acharam variantes de senha passando pela limpeza do texto de entrada
89
+ * (usuario:senha na forma scp; `\` num usuario de dominio, que o `URL` do WHATWG le como `/` e o
90
+ * git nao; a propria saida vazada de uma versao anterior). Aqui a saida so tem esquema conhecido,
91
+ * host em conjunto fechado, porta numerica e caminho sem `@`, `:` nem `\`: usuario, senha, query e
92
+ * fragmento nunca sao copiados. Na duvida, `null` (o projeto continua, sem remoto).
93
+ */
94
+ function limparRemoto(bruto) {
95
+ if (typeof bruto !== 'string')
96
+ return null;
97
+ const texto = bruto.trim();
98
+ // `\`: o WHATWG e o git discordam sobre onde ela termina o usuario; nao ha remoto legitimo com ela.
99
+ if (!texto || texto.length > 500 || /[\s\x00-\x1f\x7f\\]/.test(texto))
100
+ return null;
101
+ let esquema, host, porta = '', caminho;
102
+ const url = /^([A-Za-z][A-Za-z0-9+.-]*):\/\/(?:[^/?#]*@)?([^/?#:@[\]]+|\[[^\]/?#@]*\])(?::(\d{1,5}))?(\/[^?#]*)?([?#].*)?$/.exec(texto);
103
+ if (url) {
104
+ [esquema, host, porta, caminho] = [url[1].toLowerCase(), url[2], url[3] ?? '', url[4] ?? '/'];
105
+ // V1 da revisao 4: no ssh e no git://, o git nao corta a autoridade em `?` nem `#` (o host vai ate
106
+ // a primeira `/`, depois de decodificar a URL): la, `?` e `#` ainda sao usuario e senha.
107
+ if (!/^https?$/.test(esquema) && /[?#]/.test(texto))
108
+ return null;
109
+ // No http(s), query e fragmento saem; com `@` neles, nao ha remoto legitimo (defesa em profundidade).
110
+ if (url[5]?.includes('@'))
111
+ return null;
112
+ }
113
+ else {
114
+ if (/^[A-Za-z]:[/]/.test(texto))
115
+ return null;
116
+ // Forma scp (`git@host:dono/repo.git`): vira `ssh://host/dono/repo.git`, a forma usual das forjas.
117
+ const scp = /^(?:[^@\s:/]+@)?([A-Za-z0-9.-]{2,}):(?!\/\/)([^\s@]+)$/.exec(texto);
118
+ if (!scp)
119
+ return path.isAbsolute(texto) && CAMINHO_DO_REMOTO.test(texto) ? texto : null;
120
+ [esquema, host, caminho] = ['ssh', scp[1], `/${scp[2].replace(/^\/+/, '')}`];
121
+ }
122
+ if (!ESQUEMAS.has(esquema) || !HOST_DO_REMOTO.test(host) || !CAMINHO_DO_REMOTO.test(caminho))
123
+ return null;
124
+ if (porta && (Number(porta) < 1 || Number(porta) > 65535))
125
+ return null;
126
+ // O host fica como veio: em minusculas, a varredura de segredo (sensivel a caixa, como o `AKIA`) nao o veria.
127
+ return `${esquema}://${host}${porta ? `:${porta}` : ''}${caminho}`;
128
+ }
129
+ function temManifesto(dir) {
130
+ return [manifest_1.NOME_MANIFESTO, manifest_1.NOME_MANIFESTO_LEGADO].some((n) => fs.existsSync(path.join(dir, n)));
131
+ }
132
+ function remotoDe(v) {
133
+ if (typeof v.remoto === 'string')
134
+ return limparRemoto(v.remoto);
135
+ const remotos = v.remotos;
136
+ if (Array.isArray(remotos)) {
137
+ const lista = remotos.filter((r) => !!r && typeof r === 'object');
138
+ const escolhido = lista.find((r) => r.nome === 'origin' || r.name === 'origin') ?? lista[0];
139
+ return escolhido ? limparRemoto(escolhido.url) : null;
140
+ }
141
+ if (remotos && typeof remotos === 'object') {
142
+ const mapa = remotos;
143
+ return limparRemoto(mapa.origin ?? Object.values(mapa)[0]);
144
+ }
145
+ return null;
146
+ }
147
+ function projetoDoRegistro(bruto, nomeDaChave) {
148
+ if (!bruto || typeof bruto !== 'object' || Array.isArray(bruto))
149
+ return null;
150
+ const v = bruto;
151
+ const nome = typeof v.nome === 'string' ? v.nome : nomeDaChave;
152
+ const caminho = typeof v.raiz === 'string' ? v.raiz : typeof v.caminho === 'string' ? v.caminho : null;
153
+ if (!(0, exports.ehNomeDeProjeto)(nome) || !caminho || !path.isAbsolute(caminho) || caminho.length > 1024 || saida_segura_1.INVISIVEL.test(caminho))
154
+ return null;
155
+ const normal = path.normalize(caminho);
156
+ return { nome, remoto: remotoDe(v), caminho: normal, fonte: 'registro', presente: temManifesto(normal) };
157
+ }
158
+ /** O registro da RM-052, lido de forma tolerante. Nunca lanca: registro ruim vira `invalido`. */
159
+ function lerRegistroDeProjetos(arquivo = path.join((0, maquina_1.pastaDoUsuario)(), 'projetos.json')) {
160
+ let bruto;
161
+ try {
162
+ if (fs.statSync(arquivo).size > 1024 * 1024)
163
+ return { arquivo, estado: 'invalido', projetos: [] };
164
+ bruto = JSON.parse(fs.readFileSync(arquivo, 'utf8'));
165
+ }
166
+ catch (e) {
167
+ return { arquivo, estado: e.code === 'ENOENT' ? 'ausente' : 'invalido', projetos: [] };
168
+ }
169
+ if (!bruto || typeof bruto !== 'object' || Array.isArray(bruto))
170
+ return { arquivo, estado: 'invalido', projetos: [] };
171
+ const v = bruto;
172
+ if (v.contrato !== undefined && v.contrato !== exports.CONTRATO_DO_REGISTRO)
173
+ return { arquivo, estado: 'invalido', projetos: [] };
174
+ const lista = Array.isArray(v.projetos) ? v.projetos.map((p) => projetoDoRegistro(p))
175
+ : v.projetos && typeof v.projetos === 'object'
176
+ ? Object.entries(v.projetos).map(([nome, p]) => projetoDoRegistro(p, nome)) : null;
177
+ if (!lista)
178
+ return { arquivo, estado: 'invalido', projetos: [] };
179
+ return { arquivo, estado: 'lido', projetos: lista.filter((p) => p !== null) };
180
+ }
181
+ /** O projeto do diretorio atual, pela raiz canonica (a arvore principal, nunca a worktree). */
182
+ function projetoDoDiretorio(dir) {
183
+ let carregado;
184
+ try {
185
+ carregado = (0, manifest_1.carregarManifesto)(dir);
186
+ }
187
+ catch {
188
+ return null;
189
+ }
190
+ if (!carregado || carregado.erros.length > 0)
191
+ return null;
192
+ let raiz;
193
+ try {
194
+ raiz = (0, estado_thread_1.raizDoEstado)(carregado.raiz);
195
+ }
196
+ catch {
197
+ raiz = carregado.raiz;
198
+ }
199
+ // RM-047: o `fabrica.remoto` vem do manifesto versionado; fora do formato de remoto, nada vai ao git.
200
+ const nomeDoRemoto = carregado.manifesto.fabrica.remoto;
201
+ const remoto = (0, branch_de_estado_1.remotoValido)(nomeDoRemoto) ? (0, rede_forja_1.comGitIsolado)(() => (0, branch_de_estado_1.git)(carregado.raiz, ['remote', 'get-url', '--', nomeDoRemoto])) : null;
202
+ return { nome: carregado.manifesto.project.name, remoto: remoto?.ok ? limparRemoto(remoto.stdout.trim()) : null,
203
+ caminho: raiz, fonte: 'cwd', presente: true };
204
+ }
205
+ const chave = (caminho) => { try {
206
+ return fs.realpathSync(caminho);
207
+ }
208
+ catch {
209
+ return path.normalize(caminho);
210
+ } };
211
+ /**
212
+ * Os projetos conhecidos: registro > diretorio atual > ultimo retrato, sem repetir raiz. So entram
213
+ * do retrato os que ainda tem manifesto no caminho; do registro entram todos, com `presente`.
214
+ */
215
+ function projetosConhecidos(opcoes = {}) {
216
+ const registro = lerRegistroDeProjetos(opcoes.arquivo);
217
+ const vistos = new Map();
218
+ const somar = (p) => {
219
+ if (!p)
220
+ return;
221
+ const k = chave(p.caminho), atual = vistos.get(k);
222
+ if (!atual)
223
+ vistos.set(k, p);
224
+ else if (!atual.remoto && p.remoto)
225
+ vistos.set(k, { ...atual, remoto: p.remoto });
226
+ };
227
+ registro.projetos.forEach(somar);
228
+ if (opcoes.diretorio)
229
+ somar(projetoDoDiretorio(opcoes.diretorio));
230
+ for (const a of opcoes.anteriores ?? []) {
231
+ if (!a || !(0, exports.ehNomeDeProjeto)(a.nome) || typeof a.caminho !== 'string' || !path.isAbsolute(a.caminho) || saida_segura_1.INVISIVEL.test(a.caminho))
232
+ continue;
233
+ if (!temManifesto(a.caminho))
234
+ continue;
235
+ somar({ nome: a.nome, remoto: limparRemoto(a.remoto), caminho: path.normalize(a.caminho), fonte: 'retrato', presente: true });
236
+ }
237
+ const projetos = [...vistos.values()].sort((a, b) => a.nome.localeCompare(b.nome) || a.caminho.localeCompare(b.caminho));
238
+ return { registro, projetos };
239
+ }