@orkastery/cli 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -0
  3. package/adapters/README.md +22 -0
  4. package/adapters/claude-code/.claude-plugin/marketplace.json +15 -0
  5. package/adapters/claude-code/.claude-plugin/plugin.json +31 -0
  6. package/adapters/claude-code/README.md +102 -0
  7. package/adapters/claude-code/agents/ork-check.md +32 -0
  8. package/adapters/claude-code/agents/ork-go.md +32 -0
  9. package/adapters/claude-code/agents/ork-goal.md +33 -0
  10. package/adapters/claude-code/agents/ork-master.md +32 -0
  11. package/adapters/claude-code/agents/ork-plan.md +32 -0
  12. package/adapters/claude-code/agents/ork-ship.md +32 -0
  13. package/adapters/claude-code/commands/check.md +45 -0
  14. package/adapters/claude-code/commands/go.md +46 -0
  15. package/adapters/claude-code/commands/goal.md +54 -0
  16. package/adapters/claude-code/commands/master.md +42 -0
  17. package/adapters/claude-code/commands/ork.md +37 -0
  18. package/adapters/claude-code/commands/plan.md +47 -0
  19. package/adapters/claude-code/commands/ship.md +45 -0
  20. package/adapters/claude-code/hooks/hooks.json +17 -0
  21. package/adapters/claude-code/hooks/ork-guard.js +130 -0
  22. package/adapters/hermes/README.md +45 -0
  23. package/adapters/hermes/bin/ork-abrir-thread.sh +25 -0
  24. package/adapters/hermes/hermes.plugin.json +21 -0
  25. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +116 -0
  26. package/adapters/openclaw/README.md +63 -0
  27. package/adapters/openclaw/bin/ork-abrir-thread.sh +24 -0
  28. package/adapters/openclaw/openclaw.plugin.json +138 -0
  29. package/dist/adapters/claude-bg.js +308 -0
  30. package/dist/auditoria.js +848 -0
  31. package/dist/auditrun.js +976 -0
  32. package/dist/board.js +314 -0
  33. package/dist/canarios.js +271 -0
  34. package/dist/catalogo.js +124 -0
  35. package/dist/ciclos.js +156 -0
  36. package/dist/claims.js +226 -0
  37. package/dist/divida.js +374 -0
  38. package/dist/doctor.js +246 -0
  39. package/dist/evalrunner.js +458 -0
  40. package/dist/fix.js +450 -0
  41. package/dist/gates.js +92 -0
  42. package/dist/handoff.js +521 -0
  43. package/dist/hosts.js +331 -0
  44. package/dist/index.js +1910 -0
  45. package/dist/init.js +231 -0
  46. package/dist/leases.js +508 -0
  47. package/dist/ledger.js +138 -0
  48. package/dist/manifest.js +255 -0
  49. package/dist/master.js +453 -0
  50. package/dist/memoria.js +773 -0
  51. package/dist/modos.js +220 -0
  52. package/dist/orkmind.js +487 -0
  53. package/dist/orquestracao.js +559 -0
  54. package/dist/phase.js +572 -0
  55. package/dist/policies.js +176 -0
  56. package/dist/prompts.js +406 -0
  57. package/dist/ratelimit.js +179 -0
  58. package/dist/recall.js +252 -0
  59. package/dist/retry.js +917 -0
  60. package/dist/sandbox.js +94 -0
  61. package/dist/sessoes.js +104 -0
  62. package/dist/ship.js +551 -0
  63. package/dist/slug.js +100 -0
  64. package/dist/superficie.js +1347 -0
  65. package/dist/thread.js +333 -0
  66. package/dist/tokens.js +228 -0
  67. package/dist/types.js +20 -0
  68. package/dist/util.js +156 -0
  69. package/dist/verify.js +262 -0
  70. package/dist/worktree.js +576 -0
  71. package/dist/yaml.js +112 -0
  72. package/eval/README.md +85 -0
  73. package/eval/casos/check-quality.json +103 -0
  74. package/eval/casos/code-reviewer.json +102 -0
  75. package/eval/casos/decision-triage.json +103 -0
  76. package/eval/casos/go-implementation.json +103 -0
  77. package/eval/casos/goal-definition.json +103 -0
  78. package/eval/casos/master-metrics.json +103 -0
  79. package/eval/casos/narrative-guardian.json +163 -0
  80. package/eval/casos/orkastery-bootstrap.json +102 -0
  81. package/eval/casos/plan-specification.json +102 -0
  82. package/eval/casos/roadmap-keeper.json +103 -0
  83. package/eval/casos/scope-check-capability-map.json +83 -0
  84. package/eval/casos/security-auditor.json +102 -0
  85. package/eval/casos/ship-release.json +122 -0
  86. package/eval/casos/test-engineer.json +122 -0
  87. package/eval/casos/thread-state.json +82 -0
  88. package/eval/casos/thread-tracing.json +83 -0
  89. package/eval/casos/web-performance-auditor.json +102 -0
  90. package/eval/fixtures/b0-slug-e-modos/caso.json +56 -0
  91. package/eval/fixtures/b2-master-log/caso.json +91 -0
  92. package/eval/fixtures/fx-concurrency/caso.json +15 -0
  93. package/eval/fixtures/fx-hallucination/caso.json +12 -0
  94. package/eval/fixtures/fx-happy/caso.json +16 -0
  95. package/eval/fixtures/fx-schema-drift/caso.json +15 -0
  96. package/eval/fixtures/fx-stale-base/caso.json +12 -0
  97. package/eval/fixtures/fx-wiki-destroy/caso.json +16 -0
  98. package/eval/fixtures/superficie-de-rede/README.md +22 -0
  99. package/eval/fixtures/superficie-de-rede/api-express.js +30 -0
  100. package/eval/fixtures/superficie-de-rede/api_fastapi.py +17 -0
  101. package/eval/fixtures/superficie-de-rede/api_gin.go +18 -0
  102. package/package.json +56 -0
  103. package/references/README.md +25 -0
  104. package/references/code-review-axes.md +82 -0
  105. package/references/definition-of-done.md +74 -0
  106. package/references/performance-checklist.md +53 -0
  107. package/references/security-checklist.md +101 -0
  108. package/references/testing-patterns.md +51 -0
  109. package/skills/README.md +43 -0
  110. package/skills/core/orkastery-bootstrap/SKILL.md +89 -0
  111. package/skills/core/thread-state/SKILL.md +86 -0
  112. package/skills/governance/decision-triage/SKILL.md +65 -0
  113. package/skills/governance/narrative-guardian/SKILL.md +66 -0
  114. package/skills/governance/roadmap-keeper/SKILL.md +60 -0
  115. package/skills/governance/scope-check-capability-map/SKILL.md +61 -0
  116. package/skills/observability/thread-tracing/SKILL.md +63 -0
  117. package/skills/phases/check-quality/SKILL.md +87 -0
  118. package/skills/phases/go-implementation/SKILL.md +70 -0
  119. package/skills/phases/goal-definition/SKILL.md +69 -0
  120. package/skills/phases/master-metrics/SKILL.md +65 -0
  121. package/skills/phases/plan-specification/SKILL.md +67 -0
  122. package/skills/phases/ship-release/SKILL.md +68 -0
  123. package/skills/reviewers/code-reviewer/SKILL.md +61 -0
  124. package/skills/reviewers/security-auditor/SKILL.md +68 -0
  125. package/skills/reviewers/test-engineer/SKILL.md +66 -0
  126. package/skills/reviewers/web-performance-auditor/SKILL.md +63 -0
package/dist/thread.js ADDED
@@ -0,0 +1,333 @@
1
+ "use strict";
2
+ /**
3
+ * `ork thread new|list|status`: o estado da thread em disco.
4
+ *
5
+ * O `thread.json` e a fonte unica da verdade da thread: fases, modo de conducao, base
6
+ * carimbada pelo `ork` (nunca escolhida pelo executor), worktree e sessoes despachadas.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.dirThreads = dirThreads;
43
+ exports.dirThread = dirThread;
44
+ exports.caminhoThread = caminhoThread;
45
+ exports.listarIds = listarIds;
46
+ exports.lerThread = lerThread;
47
+ exports.gravarThread = gravarThread;
48
+ exports.idDisponivel = idDisponivel;
49
+ exports.criarWorktree = criarWorktree;
50
+ exports.novaThread = novaThread;
51
+ exports.blocoDaThread = blocoDaThread;
52
+ exports.pausaNaThread = pausaNaThread;
53
+ exports.pausasDaThread = pausasDaThread;
54
+ exports.resumoDaThread = resumoDaThread;
55
+ exports.tabelaDeThreads = tabelaDeThreads;
56
+ exports.exigirFase = exigirFase;
57
+ const fs = __importStar(require("node:fs"));
58
+ const path = __importStar(require("node:path"));
59
+ const ciclos_1 = require("./ciclos");
60
+ const modos_1 = require("./modos");
61
+ const manifest_1 = require("./manifest");
62
+ const slug_1 = require("./slug");
63
+ const util_1 = require("./util");
64
+ const ledger_1 = require("./ledger");
65
+ /** Diretorio de threads do projeto. */
66
+ function dirThreads(raiz) {
67
+ return path.join((0, manifest_1.dirEstado)(raiz), 'threads');
68
+ }
69
+ /** Diretorio de uma thread. */
70
+ function dirThread(raiz, id) {
71
+ return path.join(dirThreads(raiz), id);
72
+ }
73
+ /** Caminho do `thread.json`. */
74
+ function caminhoThread(raiz, id) {
75
+ return path.join(dirThread(raiz, id), 'thread.json');
76
+ }
77
+ /** Ids das threads existentes, em ordem alfabetica. */
78
+ function listarIds(raiz) {
79
+ const dir = dirThreads(raiz);
80
+ if (!fs.existsSync(dir))
81
+ return [];
82
+ return fs
83
+ .readdirSync(dir, { withFileTypes: true })
84
+ .filter((e) => e.isDirectory() && fs.existsSync(caminhoThread(raiz, e.name)))
85
+ .map((e) => e.name)
86
+ .sort();
87
+ }
88
+ /** Le uma thread do disco. */
89
+ function lerThread(raiz, id) {
90
+ const caminho = caminhoThread(raiz, id);
91
+ if (!fs.existsSync(caminho)) {
92
+ throw new Error(`thread "${id}" nao encontrada em ${dirThreads(raiz)}`);
93
+ }
94
+ return (0, util_1.lerJson)(caminho);
95
+ }
96
+ /** Grava a thread, sempre atualizando o carimbo de alteracao. */
97
+ function gravarThread(raiz, thread) {
98
+ thread.atualizadaEm = (0, util_1.agora)();
99
+ (0, util_1.gravarJson)(caminhoThread(raiz, thread.id), thread);
100
+ }
101
+ /** Carimba a base da thread: branch e commit reais no momento da criacao. */
102
+ function carimbarBase(raiz) {
103
+ const branch = (0, util_1.exec)('git', ['rev-parse', '--abbrev-ref', 'HEAD'], raiz);
104
+ const commit = (0, util_1.exec)('git', ['rev-parse', 'HEAD'], raiz);
105
+ return {
106
+ branch: branch.ok ? branch.stdout.trim() : 'desconhecida',
107
+ commit: commit.ok ? commit.stdout.trim() : 'desconhecido',
108
+ };
109
+ }
110
+ /** Resolve um id livre a partir do assunto normalizado (`<abbrev>-<assunto>`). */
111
+ function idDisponivel(raiz, abbrev, assunto) {
112
+ let candidato = assunto;
113
+ let n = 1;
114
+ while (fs.existsSync(dirThread(raiz, `${abbrev}-${candidato}`))) {
115
+ n += 1;
116
+ const sufixo = String(n);
117
+ candidato = assunto.slice(0, 12 - sufixo.length) + sufixo;
118
+ }
119
+ return { id: `${abbrev}-${candidato}`, assunto: candidato };
120
+ }
121
+ /**
122
+ * Cria a worktree isolada da thread (paralelismo sem colisao entre threads).
123
+ *
124
+ * A base vem de `worktree.base_branch` do manifesto e e resolvida em commit antes do
125
+ * `git worktree add`; depois de criar, o `ork` confere em `git worktree list --porcelain`
126
+ * que a worktree existe de fato, em vez de confiar no codigo de saida do git.
127
+ */
128
+ function criarWorktree(carregado, id, slug, opcoes = {}) {
129
+ const { raiz, manifesto } = carregado;
130
+ const dir = path.resolve(raiz, manifesto.worktree.dir, id);
131
+ const branch = opcoes.branchExistente ?? `ork/${slug}`;
132
+ if (fs.existsSync(dir)) {
133
+ throw new Error(`worktree ja existe em ${dir}`);
134
+ }
135
+ const base = opcoes.base ?? manifesto.worktree.base_branch;
136
+ const commitBase = (0, util_1.exec)('git', ['rev-parse', '--verify', base], raiz);
137
+ const ref = commitBase.ok ? commitBase.stdout.trim() : 'HEAD';
138
+ // Branch nova (`-b`) no caso comum; branch que ja existe no ciclo `merge-branch`.
139
+ const argumentos = opcoes.branchExistente
140
+ ? ['worktree', 'add', dir, opcoes.branchExistente]
141
+ : ['worktree', 'add', '-b', branch, dir, ref];
142
+ const r = (0, util_1.exec)('git', argumentos, raiz);
143
+ if (!r.ok) {
144
+ throw new Error(`git worktree add falhou: ${(r.stderr || r.stdout).trim()}`);
145
+ }
146
+ const lista = (0, util_1.exec)('git', ['worktree', 'list', '--porcelain'], raiz);
147
+ const criada = lista.ok && lista.stdout.split('\n').some((l) => l.trim() === `worktree ${dir}`);
148
+ if (!criada) {
149
+ throw new Error(`worktree nao apareceu em \`git worktree list\` apos a criacao: ${dir}`);
150
+ }
151
+ return { dir, branch };
152
+ }
153
+ /** Cria a thread: valida modo contra o manifesto, gera o slug e carimba a base. */
154
+ function novaThread(carregado, opcoes) {
155
+ const { raiz, manifesto } = carregado;
156
+ const permitidos = manifesto.conduction.allowed_modes;
157
+ if (!permitidos.includes(opcoes.modo)) {
158
+ throw new Error(`modo ${modos_1.MODOS[opcoes.modo].tag} fora de conduction.allowed_modes ` +
159
+ `(permitidos: ${permitidos.map((m) => modos_1.MODOS[m].tag).join(', ')})`);
160
+ }
161
+ const assuntoBruto = (0, slug_1.normalizarAssunto)(opcoes.assunto ?? opcoes.nome);
162
+ if (!assuntoBruto) {
163
+ throw new Error(`nome "${opcoes.nome}" nao produz assunto valido (esperado [a-z0-9])`);
164
+ }
165
+ const abbrev = manifesto.project.abbrev;
166
+ const def = (0, modos_1.definicaoDoModo)(opcoes.modo);
167
+ const variante = opcoes.variante ?? null;
168
+ // A variante muda o desenho dos blocos ANTES do slug: a parte 3 do slug vem do
169
+ // primeiro bloco, entao um ciclo `goal-plan` ja nasce com o slug `f12`.
170
+ const blocos = (0, ciclos_1.aplicarVariante)(def.blocos, variante);
171
+ if (variante) {
172
+ const dv = (0, ciclos_1.definicaoDaVariante)(variante);
173
+ if (dv.exigeBranch && !opcoes.branch) {
174
+ throw new Error(`o ciclo ${variante} parte de uma branch que ja existe: informe --branch <branch>`);
175
+ }
176
+ if (opcoes.branch) {
177
+ const existe = (0, util_1.exec)('git', ['rev-parse', '--verify', `refs/heads/${opcoes.branch}`], raiz);
178
+ if (!existe.ok) {
179
+ throw new Error(`branch "${opcoes.branch}" nao existe neste repositorio`);
180
+ }
181
+ }
182
+ }
183
+ else if (opcoes.branch) {
184
+ throw new Error('--branch so vale com --ciclo merge-branch');
185
+ }
186
+ const { id, assunto } = opcoes.dryRun
187
+ ? { id: `${abbrev}-${assuntoBruto}`, assunto: assuntoBruto }
188
+ : idDisponivel(raiz, abbrev, assuntoBruto);
189
+ const slug = opcoes.slug ?? (0, slug_1.montarSlug)(abbrev, assunto, blocos[0].slugFases);
190
+ // O override `--slug` do builder tambem passa pela regex canonica.
191
+ if (!(0, slug_1.slugValido)(slug)) {
192
+ throw new Error(`slug invalido: "${slug}" (regex canonica: ${slug_1.REGEX_SLUG.source})`);
193
+ }
194
+ const thread = {
195
+ id,
196
+ slug,
197
+ nome: opcoes.nome,
198
+ assunto,
199
+ modo: opcoes.modo,
200
+ fases: blocos.flatMap((b) => b.fases),
201
+ blocos,
202
+ faseAtual: blocos[0].fases[0],
203
+ status: 'aberta',
204
+ criadaEm: (0, util_1.agora)(),
205
+ atualizadaEm: (0, util_1.agora)(),
206
+ projeto: { name: manifesto.project.name, abbrev },
207
+ base: carimbarBase(raiz),
208
+ worktree: opcoes.worktree ?? null,
209
+ sessoes: [],
210
+ decisoes: [],
211
+ claims: [],
212
+ leases: [],
213
+ baseline: null,
214
+ variante,
215
+ score: null,
216
+ };
217
+ if (variante === 'feature-xl-faseada')
218
+ thread.fatias = opcoes.fatias ?? 3;
219
+ if (opcoes.dryRun)
220
+ return { thread, gravada: false };
221
+ let worktreeCriada = null;
222
+ // `greenfield`, `merge-branch` e `feature-xl-faseada` exigem worktree isolada: sao os
223
+ // ciclos que escrevem codigo em paralelo com outras threads.
224
+ const exigeWorktree = variante ? (0, ciclos_1.definicaoDaVariante)(variante).exigeWorktree : false;
225
+ if (opcoes.criarWorktree || exigeWorktree) {
226
+ worktreeCriada = criarWorktree(carregado, id, slug, {
227
+ branchExistente: opcoes.branch,
228
+ });
229
+ thread.worktree = worktreeCriada.dir;
230
+ const commitDaBranch = (0, util_1.exec)('git', ['rev-parse', worktreeCriada.branch], raiz);
231
+ thread.base = {
232
+ branch: worktreeCriada.branch,
233
+ commit: commitDaBranch.ok ? commitDaBranch.stdout.trim() : thread.base.commit,
234
+ };
235
+ }
236
+ gravarThread(raiz, thread);
237
+ (0, ledger_1.registrar)(dirThread(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.threadCriada, {
238
+ slug,
239
+ modo: opcoes.modo,
240
+ tag: def.tag,
241
+ blocos: def.blocos.map((b) => ({ fases: b.fases, pausa: b.pausa, slug: b.slugFases })),
242
+ pausas: blocos.filter((b) => b.pausa).length,
243
+ base: thread.base,
244
+ projeto: manifesto.project.name,
245
+ worktree: thread.worktree,
246
+ variante,
247
+ branchDeOrigem: opcoes.branch ?? null,
248
+ });
249
+ if (worktreeCriada) {
250
+ (0, ledger_1.registrar)(dirThread(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.worktreeCriada, {
251
+ dir: worktreeCriada.dir,
252
+ branch: worktreeCriada.branch,
253
+ base: manifesto.worktree.base_branch,
254
+ fonte: 'git worktree list --porcelain',
255
+ });
256
+ }
257
+ return { thread, gravada: true };
258
+ }
259
+ /**
260
+ * O bloco de loop que conduz a fase DESTA thread.
261
+ *
262
+ * Le `thread.blocos`, nao a matriz do modo: uma variante de ciclo (`--ciclo goal-plan`,
263
+ * `--ciclo gap`) redesenha os blocos da thread, e o prompt e o slug precisam seguir o
264
+ * desenho que a thread realmente tem.
265
+ */
266
+ function blocoDaThread(thread, fase) {
267
+ const achado = thread.blocos.find((b) => b.fases.includes(fase));
268
+ if (!achado) {
269
+ throw new Error(`fase ${fase} nao pertence a nenhum bloco da thread ${thread.id}`);
270
+ }
271
+ return achado;
272
+ }
273
+ /** A fase pausa para o humano ao fechar seu bloco NESTA thread? */
274
+ function pausaNaThread(thread, fase) {
275
+ const b = blocoDaThread(thread, fase);
276
+ return b.pausa && b.fases[b.fases.length - 1] === fase;
277
+ }
278
+ /** Pausas humanas do ciclo real da thread (modo + variante). */
279
+ function pausasDaThread(thread) {
280
+ return thread.blocos.filter((b) => b.pausa).length;
281
+ }
282
+ /** Texto do resumo de uma thread recem-criada ou consultada. */
283
+ function resumoDaThread(thread) {
284
+ const def = (0, modos_1.definicaoDoModo)(thread.modo);
285
+ const linhas = [];
286
+ linhas.push(` id ${thread.id}`);
287
+ linhas.push(` slug ${thread.slug}`);
288
+ linhas.push(` nome ${thread.nome}`);
289
+ linhas.push(` modo ${def.tag} (${pausasDaThread(thread)} pausas humanas)`);
290
+ linhas.push(` ciclo ${thread.variante ?? 'padrao do modo'}`);
291
+ linhas.push(` fase ${thread.faseAtual}`);
292
+ linhas.push(` status ${thread.status}`);
293
+ linhas.push(` base ${thread.base.branch} @ ${thread.base.commit.slice(0, 8)}`);
294
+ linhas.push(` worktree ${thread.worktree ?? '(raiz do projeto)'}`);
295
+ if (thread.score) {
296
+ linhas.push(` score ${thread.score.valor}/5 por ${thread.score.avaliadoPor} ` +
297
+ `(${thread.score.regime}) em ${thread.score.avaliadoEm}`);
298
+ }
299
+ linhas.push(' blocos');
300
+ for (const b of thread.blocos) {
301
+ const marca = b.pausa ? `pausa: ${b.pausaSobre}` : 'sem pausa (decisao autonoma no ledger)';
302
+ linhas.push(` ${b.slugFases.padEnd(6)} ${b.fases.join('-').padEnd(18)} ${marca}`);
303
+ }
304
+ return linhas.join('\n');
305
+ }
306
+ /** Tabela de `ork thread list`. */
307
+ function tabelaDeThreads(raiz) {
308
+ const ids = listarIds(raiz);
309
+ if (ids.length === 0) {
310
+ return 'Nenhuma thread em .orkastery/threads. Crie uma com: ork thread new "<nome>" --modo default';
311
+ }
312
+ const linhas = ids.map((id) => {
313
+ const t = lerThread(raiz, id);
314
+ return [
315
+ t.id,
316
+ t.slug,
317
+ (0, modos_1.tagDoModo)(t.modo),
318
+ t.faseAtual,
319
+ t.status,
320
+ String(pausasDaThread(t)),
321
+ String(t.sessoes.length),
322
+ ];
323
+ });
324
+ return (0, util_1.tabela)(['ID', 'SLUG', 'MODO', 'FASE', 'STATUS', 'PAUSAS', 'SESSOES'], linhas);
325
+ }
326
+ /** Fase valida e pertencente ao ciclo da thread. */
327
+ function exigirFase(thread, bruto) {
328
+ const fase = bruto.toUpperCase();
329
+ if (!thread.fases.includes(fase)) {
330
+ throw new Error(`fase invalida: "${bruto}" (esperado uma de ${thread.fases.join(', ')})`);
331
+ }
332
+ return fase;
333
+ }
package/dist/tokens.js ADDED
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ /**
3
+ * `ork gate next`: o gate de tokens (visao unificada, secao 3.6).
4
+ *
5
+ * Ao fim de cada fase, ANTES de despachar o proximo passo, o `ork` mede a lotacao da
6
+ * janela da sessao atual e decide entre continuar na MESMA sessao ou abrir uma NOVA.
7
+ *
8
+ * Honestidade de medicao, a regra que manda aqui:
9
+ * - `runtime_reported`: o runtime adapter sabe ler o uso de contexto. O `claude-bg`
10
+ * HOJE nao sabe (medido na versao 2.1.259), entao esta fonte nao aparece ainda.
11
+ * - `estimated`: existe transcript em disco; a estimativa e por tamanho, com o metodo
12
+ * e a janela nominal declarados na propria medida.
13
+ * - `informada`: o host adaptador (ou o operador) passou a ocupacao explicitamente.
14
+ * - `unavailable`: nada sabe medir. Nesse caso NAO se rotaciona, porque decidir
15
+ * rotacao por dado que nao existe e pior do que nao decidir. O veredito sai como
16
+ * `same-session` com `decididoPor: ausencia-de-medida`, e o ledger diz isso.
17
+ *
18
+ * Numero inventado nao entra aqui em nenhuma hipotese.
19
+ */
20
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ var desc = Object.getOwnPropertyDescriptor(m, k);
23
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
24
+ desc = { enumerable: true, get: function() { return m[k]; } };
25
+ }
26
+ Object.defineProperty(o, k2, desc);
27
+ }) : (function(o, m, k, k2) {
28
+ if (k2 === undefined) k2 = k;
29
+ o[k2] = m[k];
30
+ }));
31
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
32
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
33
+ }) : function(o, v) {
34
+ o["default"] = v;
35
+ });
36
+ var __importStar = (this && this.__importStar) || (function () {
37
+ var ownKeys = function(o) {
38
+ ownKeys = Object.getOwnPropertyNames || function (o) {
39
+ var ar = [];
40
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
41
+ return ar;
42
+ };
43
+ return ownKeys(o);
44
+ };
45
+ return function (mod) {
46
+ if (mod && mod.__esModule) return mod;
47
+ var result = {};
48
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
49
+ __setModuleDefault(result, mod);
50
+ return result;
51
+ };
52
+ })();
53
+ Object.defineProperty(exports, "__esModule", { value: true });
54
+ exports.BYTES_POR_TOKEN = exports.JANELA_NOMINAL_PADRAO = void 0;
55
+ exports.passoEhPesado = passoEhPesado;
56
+ exports.medirOcupacao = medirOcupacao;
57
+ exports.gateDeTokens = gateDeTokens;
58
+ exports.textoDoGateDeTokens = textoDoGateDeTokens;
59
+ const fs = __importStar(require("node:fs"));
60
+ const adapter = __importStar(require("./adapters/claude-bg"));
61
+ const ledger_1 = require("./ledger");
62
+ const modos_1 = require("./modos");
63
+ const phase_1 = require("./phase");
64
+ const thread_1 = require("./thread");
65
+ const util_1 = require("./util");
66
+ /**
67
+ * Janela nominal usada na estimativa por transcript.
68
+ * Declarada, nao escondida: quem le a medida sabe contra o que ela foi calculada.
69
+ */
70
+ exports.JANELA_NOMINAL_PADRAO = 200000;
71
+ /** Aproximacao classica de tokenizacao para texto tecnico: ~4 bytes por token. */
72
+ exports.BYTES_POR_TOKEN = 4;
73
+ /** Passos pesados: GO e CHECK, e refazer uma fase reprovada (visao, secao 3.6). */
74
+ function passoEhPesado(proximo, refazer) {
75
+ return refazer || proximo === 'GO' || proximo === 'CHECK';
76
+ }
77
+ /** Mede a ocupacao da janela, na ordem de confiabilidade das fontes. */
78
+ function medirOcupacao(thread, opcoes) {
79
+ if (typeof opcoes.ocupacao === 'number' && Number.isFinite(opcoes.ocupacao)) {
80
+ const valor = Math.max(0, Math.min(1, opcoes.ocupacao));
81
+ const fonte = opcoes.fonte === 'runtime_reported' || opcoes.fonte === 'estimated'
82
+ ? opcoes.fonte
83
+ : 'informada';
84
+ return {
85
+ ocupacao: valor,
86
+ fonte,
87
+ detalhe: `ocupacao ${(valor * 100).toFixed(1)}% declarada pelo chamador como fonte ${fonte}`,
88
+ };
89
+ }
90
+ if (opcoes.transcript) {
91
+ if (!fs.existsSync(opcoes.transcript)) {
92
+ return {
93
+ ocupacao: null,
94
+ fonte: 'unavailable',
95
+ detalhe: `transcript informado nao existe: ${opcoes.transcript}`,
96
+ };
97
+ }
98
+ const bytes = fs.statSync(opcoes.transcript).size;
99
+ const janela = opcoes.janela ?? exports.JANELA_NOMINAL_PADRAO;
100
+ const tokens = bytes / exports.BYTES_POR_TOKEN;
101
+ const valor = Math.max(0, Math.min(1, tokens / janela));
102
+ return {
103
+ ocupacao: valor,
104
+ fonte: 'estimated',
105
+ detalhe: `estimativa por tamanho: ${bytes} bytes / ${exports.BYTES_POR_TOKEN} bytes por token = ` +
106
+ `~${Math.round(tokens)} tokens de uma janela nominal de ${janela}`,
107
+ };
108
+ }
109
+ // O runtime adapter e a fonte preferida; hoje ele declara que nao sabe medir.
110
+ const ultima = thread.sessoes[thread.sessoes.length - 1];
111
+ const chave = opcoes.sessao ?? ultima?.sessionId ?? '';
112
+ const doRuntime = chave ? adapter.ocupacaoDeContexto(chave) : null;
113
+ if (doRuntime !== null) {
114
+ return {
115
+ ocupacao: Math.max(0, Math.min(1, doRuntime)),
116
+ fonte: 'runtime_reported',
117
+ detalhe: `uso de contexto lido do runtime para a sessao ${chave}`,
118
+ };
119
+ }
120
+ return {
121
+ ocupacao: null,
122
+ fonte: 'unavailable',
123
+ detalhe: 'o adapter claude-bg nao expoe uso de contexto nesta versao do runtime; ' +
124
+ 'nenhuma outra fonte foi informada (--ocupacao ou --transcript)',
125
+ };
126
+ }
127
+ /**
128
+ * Roda o gate de tokens: mede, decide e DOCUMENTA no ledger.
129
+ * Nao despacha nada: quem despacha e o `ork phase run`, com o slug que este gate sugere.
130
+ */
131
+ function gateDeTokens(carregado, threadId, opcoes = {}) {
132
+ const { raiz, manifesto } = carregado;
133
+ const thread = (0, thread_1.lerThread)(raiz, threadId);
134
+ const proximo = opcoes.proximo ?? thread.faseAtual;
135
+ const refazer = opcoes.refazer === true;
136
+ const pesado = passoEhPesado(proximo, refazer);
137
+ const medida = medirOcupacao(thread, opcoes);
138
+ const limiares = {
139
+ rotate_above: manifesto.handoff.rotate_above,
140
+ force_rotate_above: manifesto.handoff.force_rotate_above,
141
+ };
142
+ let veredito = 'same-session';
143
+ let decididoPor = 'medicao';
144
+ let razao = '';
145
+ if (medida.ocupacao === null) {
146
+ veredito = 'same-session';
147
+ decididoPor = 'ausencia-de-medida';
148
+ razao =
149
+ 'ocupacao da janela nao e medivel (fonte unavailable): a rotacao NAO e decidida por ' +
150
+ 'dado inexistente. Siga na mesma sessao e rotacione por decisao explicita se precisar.';
151
+ }
152
+ else if (medida.ocupacao >= limiares.force_rotate_above) {
153
+ veredito = 'new-session';
154
+ razao =
155
+ `ocupacao ${(medida.ocupacao * 100).toFixed(1)}% >= force_rotate_above ` +
156
+ `(${(limiares.force_rotate_above * 100).toFixed(0)}%): rotaciona sempre, ` +
157
+ 'independente do peso do proximo passo.';
158
+ }
159
+ else if (medida.ocupacao >= limiares.rotate_above && pesado) {
160
+ veredito = 'new-session';
161
+ razao =
162
+ `ocupacao ${(medida.ocupacao * 100).toFixed(1)}% >= rotate_above ` +
163
+ `(${(limiares.rotate_above * 100).toFixed(0)}%) e o proximo passo e pesado ` +
164
+ `(${refazer ? 'refazer fase reprovada' : proximo}): mais janela significa mais acuracia.`;
165
+ }
166
+ else if (medida.ocupacao >= limiares.rotate_above) {
167
+ razao =
168
+ `ocupacao ${(medida.ocupacao * 100).toFixed(1)}% >= rotate_above, mas o proximo passo ` +
169
+ `(${proximo}) nao e pesado: segue na mesma sessao.`;
170
+ }
171
+ else {
172
+ razao =
173
+ `ocupacao ${(medida.ocupacao * 100).toFixed(1)}% abaixo de rotate_above ` +
174
+ `(${(limiares.rotate_above * 100).toFixed(0)}%): segue na mesma sessao.`;
175
+ }
176
+ // O slug da proxima sessao: outra parte de fases quando o bloco muda, sufixo de
177
+ // rotacao quando a sessao nova conduz um bloco que a thread ja abriu.
178
+ const slugSugerido = veredito === 'new-session' ? (0, phase_1.slugDaSessao)(thread, proximo) : null;
179
+ const resultado = {
180
+ thread: threadId,
181
+ medida,
182
+ proximoPasso: proximo,
183
+ passoPesado: pesado,
184
+ veredito,
185
+ decididoPor,
186
+ razao,
187
+ slugSugerido,
188
+ limiares,
189
+ decididoEm: (0, util_1.agora)(),
190
+ };
191
+ (0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, threadId), threadId, ledger_1.TIPOS_DE_EVENTO.gateDeTokens, {
192
+ ocupacao: medida.ocupacao,
193
+ fonte: medida.fonte,
194
+ fonteDetalhe: medida.detalhe,
195
+ proximoPasso: proximo,
196
+ bloco: (0, modos_1.blocoDaFase)(thread.modo, proximo).fases.join('-'),
197
+ passoPesado: pesado,
198
+ veredito,
199
+ decididoPor,
200
+ razao,
201
+ slugSugerido,
202
+ limiares,
203
+ });
204
+ const atualizada = (0, thread_1.lerThread)(raiz, threadId);
205
+ atualizada.ultimoTokenGate = resultado;
206
+ (0, thread_1.gravarThread)(raiz, atualizada);
207
+ return resultado;
208
+ }
209
+ /** Texto de `ork gate next`. */
210
+ function textoDoGateDeTokens(v) {
211
+ const linhas = [];
212
+ linhas.push(`ork gate next: thread ${v.thread}`);
213
+ linhas.push(` ocupacao ${v.medida.ocupacao === null ? 'nao medivel' : `${(v.medida.ocupacao * 100).toFixed(1)}%`}`);
214
+ linhas.push(` fonte ${v.medida.fonte}`);
215
+ linhas.push(` ${v.medida.detalhe}`);
216
+ linhas.push(` limiares rotate_above ${v.limiares.rotate_above} | force_rotate_above ${v.limiares.force_rotate_above}`);
217
+ linhas.push(` proximo passo ${v.proximoPasso} (${v.passoPesado ? 'pesado' : 'leve'})`);
218
+ linhas.push('');
219
+ linhas.push(`VEREDITO: ${v.veredito} (decidido por ${v.decididoPor})`);
220
+ linhas.push(` razao: ${v.razao}`);
221
+ if (v.slugSugerido) {
222
+ linhas.push(` slug da nova sessao: ${v.slugSugerido}`);
223
+ linhas.push(` proximo passo: ork handoff export ${v.thread} --proxima-fase ${v.proximoPasso}`);
224
+ }
225
+ linhas.push('');
226
+ linhas.push(`Registrado no ledger como token_gate: ork phase list ${v.thread}`);
227
+ return linhas.join('\n');
228
+ }
package/dist/types.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /**
3
+ * Tipos centrais do nucleo `ork`.
4
+ *
5
+ * Regra de ouro herdada do Devmaster: o agente decide, o `ork` verifica.
6
+ * Nada aqui depende de LLM; tudo e deterministico e testavel.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.DIGITO_DA_FASE = exports.FASES = void 0;
10
+ /** Ordem canonica das fases. */
11
+ exports.FASES = ['GOAL', 'PLAN', 'GO', 'CHECK', 'SHIP', 'MASTER'];
12
+ /** Alias numerico herdado do Devmaster (F1..F6), usado na parte 3 do slug. */
13
+ exports.DIGITO_DA_FASE = {
14
+ GOAL: '1',
15
+ PLAN: '2',
16
+ GO: '3',
17
+ CHECK: '4',
18
+ SHIP: '5',
19
+ MASTER: '6',
20
+ };