@orkastery/cli 0.4.2 → 0.5.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.
- package/adapters/README.md +11 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +3 -1
- package/adapters/claude-code/agents/ork-check.md +1 -1
- package/adapters/claude-code/agents/ork-go.md +2 -2
- package/adapters/claude-code/agents/ork-goal.md +1 -1
- package/adapters/claude-code/agents/ork-master.md +1 -1
- package/adapters/claude-code/agents/ork-plan.md +1 -1
- package/adapters/claude-code/agents/ork-ship.md +1 -1
- package/adapters/claude-code/commands/master.md +11 -8
- package/adapters/claude-code/commands/ork.md +8 -0
- package/adapters/claude-code/commands/ship.md +2 -2
- package/adapters/codex/skills/ork/SKILL.md +12 -1
- package/adapters/hermes/README.md +3 -0
- package/adapters/hermes/bin/ork-abrir-thread.sh +17 -2
- package/adapters/hermes/bin/ork-brain.sh +3 -0
- package/adapters/hermes/bin/ork-maestro.sh +4 -1
- package/adapters/hermes/bin/ork-network-roadmap.sh +11 -0
- package/adapters/hermes/bin/ork-roadmap-status.sh +9 -0
- package/adapters/hermes/hermes.plugin.json +2 -0
- package/adapters/hermes/hitl-ingress/__init__.py +36 -3
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +19 -20
- package/adapters/openclaw/README.md +34 -3
- package/adapters/openclaw/dist/hitl-ingress.js +37 -3
- package/adapters/openclaw/dist/index.js +92 -39
- package/adapters/openclaw/openclaw.plugin.json +10 -3
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +32 -3
- package/adapters/openclaw/src/index.ts +108 -41
- package/assets/docs/padroes/documentacao-de-produto.md +2 -2
- package/assets/docs/padroes/roadmap-de-produto.md +1 -1
- package/assets/docs/produto/_modelo-feature.md +2 -0
- package/assets/orkmind_bridge.py +214 -1
- package/dist/adapters/claude-bg.js +88 -9
- package/dist/adapters/codex-events.js +3 -1
- package/dist/board.js +74 -0
- package/dist/branch-de-estado.js +5 -4
- package/dist/busca-semantica.js +161 -0
- package/dist/ci.js +141 -10
- package/dist/company-brain-cli.js +27 -2
- package/dist/company-brain-context.js +132 -0
- package/dist/company-brain-dossie.js +376 -0
- package/dist/company-brain-mcp.js +4 -1
- package/dist/conducao.js +117 -31
- package/dist/decisao-autonoma.js +42 -0
- package/dist/docs.js +64 -22
- package/dist/doctor.js +33 -0
- package/dist/entrega-pr.js +109 -5
- package/dist/experiencia-instalacao.js +256 -0
- package/dist/experiencia.js +72 -0
- package/dist/fabrica-estado.js +43 -10
- package/dist/fechamento.js +105 -0
- package/dist/fix.js +1 -1
- package/dist/forja.js +435 -0
- package/dist/gates.js +10 -1
- package/dist/hitl-classificacao.js +11 -1
- package/dist/hitl-contract.js +50 -7
- package/dist/hitl-curto.js +158 -0
- package/dist/hitl-gates.js +67 -1
- package/dist/hitl-lote.js +30 -15
- package/dist/hitl-presentation.js +10 -7
- package/dist/hitl-resumo.js +33 -12
- package/dist/hitl-texto-livre.js +150 -0
- package/dist/hosts.js +86 -1
- package/dist/index.js +605 -41
- package/dist/indice-vetorial.js +317 -0
- package/dist/init.js +13 -0
- package/dist/integracoes-locais.js +1 -1
- package/dist/intelligence-benchmark-contract.js +570 -0
- package/dist/intelligence-graph-cli.js +430 -0
- package/dist/intelligence-graph-contract.js +577 -0
- package/dist/intelligence-graph-extract-md.js +469 -0
- package/dist/intelligence-graph-extract-ts.js +810 -0
- package/dist/intelligence-graph-extract.js +369 -0
- package/dist/intelligence-graph-index.js +512 -0
- package/dist/intelligence-graph-parsers.js +327 -0
- package/dist/intelligence-graph-query.js +344 -0
- package/dist/intelligence-graph-repo.js +187 -0
- package/dist/leases.js +84 -0
- package/dist/ledger.js +14 -0
- package/dist/maestro-cli.js +10 -1
- package/dist/maestro-contract.js +5 -1
- package/dist/maestro-discovery.js +16 -2
- package/dist/maestro-snapshot.js +6 -2
- package/dist/manifest.js +142 -5
- package/dist/master-batch.js +3 -2
- package/dist/master-digest.js +2 -2
- package/dist/master-nota.js +203 -0
- package/dist/master.js +4 -0
- package/dist/mcp-experiencia.js +164 -0
- package/dist/mcp-git.js +36 -10
- package/dist/mcp-install.js +3 -1
- package/dist/mcp-maestro.js +3 -2
- package/dist/mcp-server.js +84 -5
- package/dist/memoria.js +147 -1
- package/dist/network-roadmap.js +907 -0
- package/dist/ocupacao.js +38 -2
- package/dist/onboarding.js +113 -7
- package/dist/orkmind.js +165 -9
- package/dist/phase.js +166 -13
- package/dist/policies.js +2 -0
- package/dist/projeto-alvo.js +551 -0
- package/dist/prompts.js +1 -1
- package/dist/pulse-consentimento.js +4 -2
- package/dist/pulse-delivery.js +5 -0
- package/dist/pulse-resposta.js +674 -49
- package/dist/pulse.js +21 -0
- package/dist/ratelimit.js +3 -2
- package/dist/retry.js +230 -9
- package/dist/roadmap-reservas.js +262 -20
- package/dist/roadmap-status.js +213 -0
- package/dist/runtime-ambiente.js +12 -1
- package/dist/session-watcher-claude.js +26 -0
- package/dist/sessoes.js +49 -15
- package/dist/thread-close.js +3 -0
- package/dist/verify.js +5 -4
- package/dist/worktree.js +77 -0
- package/eval/casos/orchestration-experience-pt-br.json +48 -0
- package/eval/casos/orchestration-experience.json +48 -0
- package/package.json +1 -1
- package/schemas/code-artifact-graph.v1.schema.json +581 -0
- package/schemas/graph-benchmark.v1.schema.json +2240 -0
- package/schemas/maestro-snapshot.schema.json +23 -0
- package/skills/README.md +7 -5
- package/skills/core/onboarding/SKILL.md +7 -0
- package/skills/core/orchestration-experience/SKILL.md +113 -0
- package/skills/core/orchestration-experience-pt-br/SKILL.md +112 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/go-implementation/SKILL.md +2 -0
|
@@ -76,6 +76,9 @@ async function rodarOrk(args, signal, entrada) {
|
|
|
76
76
|
env.ORK_RECEIPT_VERIFIERS = verifiers;
|
|
77
77
|
// I-36 (D6): o adaptador declara o proprio canal de conducao. Descreve a porta, nao concede autoridade.
|
|
78
78
|
env.ORK_CANAL = 'openclaw';
|
|
79
|
+
// RM-052 (D3): o cwd do gateway nao e projeto de ninguem. Sem `projeto` pedido, o nucleo usa o unico
|
|
80
|
+
// projeto conhecido da maquina ou devolve a escolha; nunca o manifesto que estiver no cwd do gateway.
|
|
81
|
+
env.ORK_PROJETO_EXPLICITO = '1';
|
|
79
82
|
return new Promise((resolve) => {
|
|
80
83
|
const filho = execFile(bin, args, { maxBuffer: MAX_BUFFER, signal, env }, (erro, stdout, stderr) => {
|
|
81
84
|
const partes = [stdout, stderr].map((t) => (t ?? '').trim()).filter((t) => t !== '');
|
|
@@ -156,10 +159,36 @@ function argvDaResposta(p, alvo) {
|
|
|
156
159
|
return [alvo, 'answer', thread, pedido, '--resposta-stdin', '--por', `telegram:${usuario}`,
|
|
157
160
|
'--mensagem', `telegram:${chat}:${referencia}`, '--origem', 'telegram'];
|
|
158
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* RM-052 (D4): o projeto pedido chega como NOME de projeto registrado (`ork projetos`), nunca como
|
|
164
|
+
* caminho. O modelo escolhe entre os projetos da maquina; nao aponta o nucleo para um diretorio.
|
|
165
|
+
*/
|
|
166
|
+
const PADRAO_DO_PROJETO = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/;
|
|
167
|
+
const PARAMETRO_PROJETO = {
|
|
168
|
+
type: 'string',
|
|
169
|
+
pattern: PADRAO_DO_PROJETO.source,
|
|
170
|
+
description: 'Nome do projeto que o dono pediu (ex.: orkastery), como em `ork projetos`. Sem ele e com mais de um projeto na máquina, a resposta é a escolha; nunca use o diretório do gateway.',
|
|
171
|
+
};
|
|
172
|
+
const PROJETO_DA_RM052 = {
|
|
173
|
+
padrao: PADRAO_DO_PROJETO,
|
|
174
|
+
descricao: PARAMETRO_PROJETO.description,
|
|
175
|
+
recusa: 'informe o NOME de um projeto de `ork projetos` (ex.: orkastery), nunca um caminho',
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* RM-054 (fatia 2, D-G4): no roadmap da rede, `projeto` tambem aceita a forja (`github:dono/repo`,
|
|
179
|
+
* `gitlab:grupo/repo`): numa maquina sem clone, e o unico caminho ate o projeto enquanto a rede por
|
|
180
|
+
* pessoa (RM-053) nao chega. Caminho e URL continuam fora (a URL pode levar credencial), e o nucleo
|
|
181
|
+
* confere de novo, porque o adaptador declara ORK_PROJETO_EXPLICITO=1.
|
|
182
|
+
*/
|
|
183
|
+
const PROJETO_DA_REDE = {
|
|
184
|
+
padrao: /^(?:[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}|(?:github|gitlab):[A-Za-z0-9][A-Za-z0-9._-]{0,99}(?:\/[A-Za-z0-9][A-Za-z0-9._-]{0,99}){1,8})$/,
|
|
185
|
+
descricao: 'Projeto que o dono pediu: o nome, como em `ork projetos` (ex.: orkastery), ou a forja (github:dono/repo, gitlab:grupo/repo) quando não há clone nesta máquina. Sem ele, vêm todos os projetos conhecidos. Nunca caminho nem URL.',
|
|
186
|
+
recusa: 'informe o NOME de um projeto de `ork projetos` (ex.: orkastery) ou github:dono/repo, nunca caminho nem URL',
|
|
187
|
+
};
|
|
159
188
|
const FERRAMENTAS = [
|
|
160
189
|
{
|
|
161
190
|
name: 'ork_maestro',
|
|
162
|
-
description: 'Ao receber a frase exata orkastery maestro,
|
|
191
|
+
description: 'Ao receber a frase exata orkastery maestro: sem projeto nomeado, chame ork_network_roadmap sem projeto (o panorama da rede, com fontes, frescor e lacunas) e apresente-o como vem; com projeto nomeado, consulte este panorama somente leitura com projeto. Não cria thread. Apresente fontes, lacunas e HITL com recomendação e opções claras; horários para o dono vêm dos campos *Local (fuso do dono), nunca do ISO. O panorama NÃO lê o roadmap, as reservas nem as outras máquinas (veja notConsulted): zero threads nunca é roadmap vazio; para o roadmap use ork_network_roadmap. Ação posterior exige operação autorizada e readback.',
|
|
163
192
|
parameters: { type: 'object', additionalProperties: false, properties: {
|
|
164
193
|
thread: { type: 'string' }, section: { type: 'string', enum: ['portfolio', 'demands', 'threads', 'sessions', 'blockers', 'leases', 'retries', 'hitl', 'ship', 'master', 'nextActions'] },
|
|
165
194
|
offset: { type: 'integer', minimum: 0, maximum: 100000 },
|
|
@@ -254,25 +283,6 @@ const FERRAMENTAS = [
|
|
|
254
283
|
parameters: schema({ thread: { type: 'string' } }),
|
|
255
284
|
argv: (p) => ['phase', 'list', texto(p, 'thread')],
|
|
256
285
|
},
|
|
257
|
-
{
|
|
258
|
-
name: 'ork_objective_status',
|
|
259
|
-
description: 'Aposentado na I-43 junto com ork objective: devolve a recusa tipada objective.aposentado, com saida != 0. Estado de uma entrega: ork_thread_status. Ciclos ligados a um produto, projeto ou iniciativa: ork portfolio inspect <id> --json.',
|
|
260
|
-
parameters: schema({ objective: { type: 'string', description: 'ID obj-* do ticket' } }),
|
|
261
|
-
argv: (p) => ['objective', 'status', texto(p, 'objective'), '--json'],
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
name: 'ork_objective_message',
|
|
265
|
-
description: 'Aposentado na I-43 junto com ork objective: devolve a recusa tipada objective.aposentado, com saida != 0, e nao grava nada. Resposta do dono a uma pausa vai por ork_gate_answer ou ork_session_answer.',
|
|
266
|
-
parameters: schema({
|
|
267
|
-
objective: { type: 'string', description: 'ID obj-* do ticket' },
|
|
268
|
-
author: { type: 'string', description: 'Identidade fornecida pelo gateway autenticado' },
|
|
269
|
-
message: { type: 'string', description: 'Texto integral recebido do dono' },
|
|
270
|
-
}),
|
|
271
|
-
argv: (p) => [
|
|
272
|
-
'objective', 'message', texto(p, 'objective'),
|
|
273
|
-
'--text', texto(p, 'message'), '--por', texto(p, 'author'), '--role', 'human',
|
|
274
|
-
],
|
|
275
|
-
},
|
|
276
286
|
{
|
|
277
287
|
name: 'ork_portfolio_list',
|
|
278
288
|
description: 'Lista produtos, projetos e iniciativas do catálogo canônico usado pelo Kanban.',
|
|
@@ -300,6 +310,27 @@ const FERRAMENTAS = [
|
|
|
300
310
|
parameters: schema({ thread: { type: 'string' }, id: { type: 'string' } }),
|
|
301
311
|
argv: (p) => ['brain', 'get', texto(p, 'id'), '--thread', texto(p, 'thread')],
|
|
302
312
|
},
|
|
313
|
+
{
|
|
314
|
+
name: 'ork_brain_context',
|
|
315
|
+
description: 'Pacote de contexto citável do Company Brain: entidades pedidas e seus pais, cada uma com a citação da fonte, o frescor contra o portfólio e as lacunas. Só leitura.',
|
|
316
|
+
parameters: schema({
|
|
317
|
+
thread: { type: 'string' },
|
|
318
|
+
ids: { type: 'string', description: 'ids do portfólio separados por vírgula (prod-, proj-, init-)' },
|
|
319
|
+
}),
|
|
320
|
+
argv: (p) => ['brain', 'context', '--thread', texto(p, 'thread'), '--ids', texto(p, 'ids')],
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
name: 'ork_brain_dossie',
|
|
324
|
+
description: 'Dossiê de decisão da thread: vínculo com objetivo e projeto, contexto citável, alternativas, decisão, quem decidiu e evidência, com os ids do Company Brain e as lacunas. Resposta do dono só com recibo conferido. Só leitura.',
|
|
325
|
+
parameters: {
|
|
326
|
+
type: 'object', additionalProperties: false, required: ['thread'],
|
|
327
|
+
properties: {
|
|
328
|
+
thread: { type: 'string' },
|
|
329
|
+
decisao: { type: 'string', description: 'id do pedido ou fact- da decisão; omita para a thread inteira' },
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
argv: (p) => ['brain', 'dossie', '--thread', texto(p, 'thread'), ...(p.decisao === undefined ? [] : ['--decisao', texto(p, 'decisao')])],
|
|
333
|
+
},
|
|
303
334
|
{
|
|
304
335
|
name: 'ork_claims_add',
|
|
305
336
|
description: 'Registra uma alegacao verificavel com o comando que a comprova. Toda citacao de arquivo ou teste vira claim.',
|
|
@@ -377,30 +408,32 @@ const FERRAMENTAS = [
|
|
|
377
408
|
},
|
|
378
409
|
{
|
|
379
410
|
name: 'ork_master',
|
|
380
|
-
description: '
|
|
411
|
+
description: 'Pede ao dono a nota do MASTER (0 a 5, com o porque) de uma entrega: devolve a linha com o codigo curto para mostrar a ele. A nota volta pelo Telegram, com a prova do ingresso. Esta tool nunca grava nota e nao recebe nome de pessoa (RM-048).',
|
|
381
412
|
parameters: schema({
|
|
382
413
|
thread: { type: 'string' },
|
|
383
|
-
score: { type: 'string', description: 'inteiro de 0 a 5, dado pelo humano' },
|
|
384
|
-
justificativa: { type: 'string' },
|
|
385
|
-
quem: { type: 'string' },
|
|
386
414
|
}),
|
|
387
|
-
argv: (p) => [
|
|
388
|
-
'master',
|
|
389
|
-
texto(p, 'thread'),
|
|
390
|
-
'--score',
|
|
391
|
-
texto(p, 'score'),
|
|
392
|
-
'--justificativa',
|
|
393
|
-
texto(p, 'justificativa'),
|
|
394
|
-
'--por',
|
|
395
|
-
texto(p, 'quem'),
|
|
396
|
-
],
|
|
415
|
+
argv: (p) => ['master', 'pedir', texto(p, 'thread'), '--formato', 'telegram'],
|
|
397
416
|
},
|
|
398
417
|
{
|
|
399
418
|
name: 'ork_board',
|
|
400
|
-
description: '
|
|
419
|
+
description: 'As threads DESTE projeto nesta maquina e o escalonador dizendo quem avanca agora e quem espera. NAO le o roadmap: nunca conclua sobre o roadmap a partir do board (zero threads nao e roadmap vazio); para o roadmap use ork_network_roadmap. O cabecalho diz o projeto consultado e o que nao foi lido.',
|
|
401
420
|
parameters: schema({}),
|
|
402
421
|
argv: () => ['board', 'plan'],
|
|
403
422
|
},
|
|
423
|
+
{
|
|
424
|
+
name: 'ork_roadmap_status',
|
|
425
|
+
description: 'Status report do roadmap SO desta maquina (o checkout local), no formato aprovado pelo dono (grupos com icones, #HITL e o fecho): nao le as reservas nem as outras maquinas, e o cabecalho diz isso. Para o status do roadmap com as threads de todas as maquinas, as reservas, as fontes e as lacunas, use ork_network_roadmap. Somente leitura: transporte o texto como vem, sem reescrever. Passe projeto com o nome que o dono pediu (ex.: orkastery); sem ele e com mais de um projeto na maquina, a resposta e a escolha. Nunca deduza o roadmap de ork_board ou ork_maestro.',
|
|
426
|
+
parameters: schema({}),
|
|
427
|
+
argv: () => ['roadmap', 'status'],
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
name: 'ork_network_roadmap',
|
|
431
|
+
description: 'Roadmap da rede, somente leitura: para cada projeto, o status report do roadmap (RM-048) com as threads de TODAS as máquinas, as reservas, as threads por máquina com a idade da batida, a fonte e a hora de cada parte e as lacunas. É a fonte para qualquer pergunta sobre o roadmap ou o status report: transporte o texto como vem, sem reescrever nem resumir. Passe projeto com o nome que o dono pediu (ex.: orkastery) ou github:dono/repo; sem projeto, vêm todos os projetos conhecidos, e esse é o panorama da frase orkastery maestro sem projeto. Lacuna, "não lido" e "Não consultado" são fontes que ficaram sem leitura: nunca conclua "roadmap vazio" nem "nenhuma máquina publicou" a partir delas.',
|
|
432
|
+
parameters: schema({}),
|
|
433
|
+
projeto: PROJETO_DA_REDE,
|
|
434
|
+
// RM-054 (fatia 2): uma chamada de CLI; o `--projeto` vai no inicio e o nucleo o devolve ao `network`.
|
|
435
|
+
argv: () => ['network', 'roadmap'],
|
|
436
|
+
},
|
|
404
437
|
{
|
|
405
438
|
name: 'ork_master_batch',
|
|
406
439
|
description: 'Todas as entregas em JSON, com o indice derivado do ledger, as ja pontuadas e as aceitas por omissao. Roda ork master --todas; o nome e da antiga fila de score (ork master --batch), aposentada na I-43.',
|
|
@@ -408,21 +441,41 @@ const FERRAMENTAS = [
|
|
|
408
441
|
argv: () => ['master', '--todas', '--json'],
|
|
409
442
|
},
|
|
410
443
|
];
|
|
444
|
+
/** RM-052: toda tool aceita `projeto` opcional, sem mudar o que ela ja exigia. */
|
|
445
|
+
function comProjeto(f) {
|
|
446
|
+
const propriedades = (f.parameters.properties ?? {});
|
|
447
|
+
const p = f.projeto;
|
|
448
|
+
const projeto = p ? { type: 'string', pattern: p.padrao.source, description: p.descricao } : PARAMETRO_PROJETO;
|
|
449
|
+
return { ...f.parameters, properties: { ...propriedades, projeto } };
|
|
450
|
+
}
|
|
451
|
+
/** `--projeto <nome>` vai no inicio do argv; o resto dos parametros segue para a tool como antes. */
|
|
452
|
+
function argvComProjeto(f, params) {
|
|
453
|
+
const { projeto, ...resto } = params;
|
|
454
|
+
if (projeto !== undefined && (typeof projeto !== 'string' || !(f.projeto ?? PROJETO_DA_RM052).padrao.test(projeto))) {
|
|
455
|
+
throw new Error('projeto.invalido');
|
|
456
|
+
}
|
|
457
|
+
return { argv: [...(projeto === undefined ? [] : ['--projeto', projeto]), ...f.argv(resto)], resto };
|
|
458
|
+
}
|
|
411
459
|
const plugin = defineToolPlugin({
|
|
412
460
|
id: 'orkastery',
|
|
413
461
|
name: 'Orkastery',
|
|
414
|
-
description: 'Conducao de looping threads em 6 fases pelo nucleo `ork`, exposta ao OpenClaw como tools `ork_*`. Zero regra de negocio no host: cada tool e uma chamada de CLI.',
|
|
462
|
+
description: 'Conducao de looping threads em 6 fases pelo nucleo `ork`, exposta ao OpenClaw como tools `ork_*`. Zero regra de negocio no host: cada tool e uma chamada de CLI. Toda tool aceita projeto (nome do projeto pedido); o cwd do gateway nunca escolhe o projeto. O status do roadmap vem de ork_network_roadmap.',
|
|
415
463
|
tools: (tool) => FERRAMENTAS.map((f) => tool({
|
|
416
464
|
name: f.name,
|
|
417
465
|
description: f.description,
|
|
418
|
-
parameters: f
|
|
466
|
+
parameters: comProjeto(f),
|
|
419
467
|
execute: (params, _config, contexto) => {
|
|
468
|
+
let chamada;
|
|
420
469
|
try {
|
|
421
|
-
|
|
470
|
+
chamada = argvComProjeto(f, params);
|
|
422
471
|
}
|
|
423
|
-
catch {
|
|
472
|
+
catch (e) {
|
|
473
|
+
if (e.message === 'projeto.invalido') {
|
|
474
|
+
return `[ork recusou] projeto.invalido: ${(f.projeto ?? PROJETO_DA_RM052).recusa}`;
|
|
475
|
+
}
|
|
424
476
|
return '[ork recusou] resposta humana não confirmada; confira origem e correlação do pedido';
|
|
425
477
|
}
|
|
478
|
+
return rodarOrk(chamada.argv, contexto.signal, f.entrada?.(chamada.resto));
|
|
426
479
|
},
|
|
427
480
|
})),
|
|
428
481
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "orkastery",
|
|
3
3
|
"name": "Orkastery",
|
|
4
4
|
"description": "Conducao de looping threads em 6 fases pelo nucleo `ork`, exposta ao OpenClaw como tools `ork_*`. Zero regra de negocio no host: cada tool e uma chamada de CLI.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.5.0",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"properties": {},
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
"ork_thread_status",
|
|
22
22
|
"ork_phase_run",
|
|
23
23
|
"ork_phase_list",
|
|
24
|
-
"ork_objective_status",
|
|
25
|
-
"ork_objective_message",
|
|
26
24
|
"ork_portfolio_list",
|
|
27
25
|
"ork_brain_status",
|
|
28
26
|
"ork_brain_query",
|
|
29
27
|
"ork_brain_get",
|
|
28
|
+
"ork_brain_context",
|
|
29
|
+
"ork_brain_dossie",
|
|
30
30
|
"ork_claims_add",
|
|
31
31
|
"ork_verify",
|
|
32
32
|
"ork_verify_baseline",
|
|
@@ -37,7 +37,14 @@
|
|
|
37
37
|
"ork_ship",
|
|
38
38
|
"ork_master",
|
|
39
39
|
"ork_board",
|
|
40
|
+
"ork_roadmap_status",
|
|
41
|
+
"ork_network_roadmap",
|
|
40
42
|
"ork_master_batch"
|
|
41
43
|
]
|
|
44
|
+
},
|
|
45
|
+
"toolMetadata": {
|
|
46
|
+
"ork_network_roadmap": {
|
|
47
|
+
"profiles": ["coding", "messaging"]
|
|
48
|
+
}
|
|
42
49
|
}
|
|
43
50
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orkastery/openclaw-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Plugin Orkastery para OpenClaw:
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Plugin Orkastery para OpenClaw: 27 tools ork_* que traduzem intencao em chamada de CLI do nucleo `ork`. Zero regra de negocio no host.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": true,
|
|
7
7
|
"license": "MIT",
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
/** D12: this host is the `openclaw` channel, and the channel is signed, not labelled. */
|
|
3
3
|
import { createHash, createHmac } from 'node:crypto';
|
|
4
4
|
import { execFile } from 'node:child_process';
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { isAbsolute, join } from 'node:path';
|
|
5
7
|
|
|
6
8
|
interface NativeEvent {
|
|
7
9
|
content: string; channel: string; commandAuthorized?: boolean; accountId?: string;
|
|
@@ -18,14 +20,41 @@ interface NativeApi { on: (name: 'inbound_claim', handler: (event: NativeEvent,
|
|
|
18
20
|
* stay equal. The code starts with a letter and has a digit: ordinary chat never has this shape,
|
|
19
21
|
* and it never reads as an answer to the batch, which starts with the question number.
|
|
20
22
|
*/
|
|
21
|
-
const PULSE_CONSENT = new RegExp("^[ \\t]*(?=[A-HJKMNP-TV-Z][2-9A-HJKMNP-TV-Z]{0,2}[2-9])[A-HJKMNP-TV-Z][2-9A-HJKMNP-TV-Z]{3}[ \\t]+[^\\r\\n]{1,
|
|
23
|
+
const PULSE_CONSENT = new RegExp("^[ \\t]*(?=[A-HJKMNP-TV-Z][2-9A-HJKMNP-TV-Z]{0,2}[2-9])[A-HJKMNP-TV-Z][2-9A-HJKMNP-TV-Z]{3}[ \\t]+[^\\r\\n]{1,200}$", 'i');
|
|
22
24
|
const PULSE_LOTE = new RegExp("^[ \\t]*[0-9]{1,2}[ \\t]*[a-zA-Z](?:[ \\t,;]*[0-9]{1,2}[ \\t]*[a-zA-Z])*[ \\t]*[.!]?[ \\t]*$");
|
|
23
25
|
/** I-50 (RM-039): the pulse cadence tag, alone in the message. It starts with '#', so it never reads as the other two. */
|
|
24
26
|
const PULSE_CADENCIA = new RegExp("^[ \\t]*#OrkPulse(?:On(?:-(?:15|30|60)m)?|Off)[ \\t]*[.!]?[ \\t]*$", 'i');
|
|
25
27
|
/** The pulse address inside the signed body: neither a thread nor a request. The core translates. */
|
|
26
28
|
const PULSE_ALVO = 'pulse', PULSE_ENDERECO = 'resposta';
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
/**
|
|
30
|
+
* RM-048 (D2 and D3): the free-text shapes, generated in the core from the closed vocabulary. `lista`
|
|
31
|
+
* ("1. B, 2. aprovo") is always claimed; `livre` ("sim", "aprovo", "a") only while the core's
|
|
32
|
+
* listening window is open: outside it, the word goes to the assistant, as it always did. The
|
|
33
|
+
* window is a routing hint, not proof: the message still goes through the signature and the core rule.
|
|
34
|
+
*/
|
|
35
|
+
const PULSE_LISTA = new RegExp("^[ \\t]*[0-9]{1,2}(?:[ \\t]*[.):-])?[ \\t]*(?:[a-d]|pode seguir|pode ir|pode|sim|s|ok|aprovo|aprovado|aprovada|aprovar|aprova|segue|siga|seguir|manda|confirmo|confirmado|de acordo|agora n[aã]o|n[aã]o|n|recuso|recusado|recusar|reprovo|reprovado|revisar|revis[aã]o|refazer|volta|voltar|esperar|espera|aguardar|aguarda|aguarde|depois|mais tarde|detalhes?|evid[eê]ncias?)(?:[ \\t\\r\\n,;]*[0-9]{1,2}(?:[ \\t]*[.):-])?[ \\t]*(?:[a-d]|pode seguir|pode ir|pode|sim|s|ok|aprovo|aprovado|aprovada|aprovar|aprova|segue|siga|seguir|manda|confirmo|confirmado|de acordo|agora n[aã]o|n[aã]o|n|recuso|recusado|recusar|reprovo|reprovado|revisar|revis[aã]o|refazer|volta|voltar|esperar|espera|aguardar|aguarda|aguarde|depois|mais tarde|detalhes?|evid[eê]ncias?))*[ \\t]*(?:[.!][ \\t]*)?$", 'i');
|
|
36
|
+
const PULSE_LIVRE = new RegExp("^[ \\t]*(?:[1-4]|(?:[a-d]|pode seguir|pode ir|pode|sim|s|ok|aprovo|aprovado|aprovada|aprovar|aprova|segue|siga|seguir|manda|confirmo|confirmado|de acordo|agora n[aã]o|n[aã]o|n|recuso|recusado|recusar|reprovo|reprovado|revisar|revis[aã]o|refazer|volta|voltar|esperar|espera|aguardar|aguarda|aguarde|depois|mais tarde|detalhes?|evid[eê]ncias?))[ \\t]*(?:[.!]+[ \\t]*)?$", 'i');
|
|
37
|
+
/** RM-048 (item 8): the digest keyboard line goes to the core at the same signed address. */
|
|
38
|
+
const PULSE_RATIFICACAO = new RegExp("^[ \\t]*ratificar(?:-lote)?(?:[ \\t]+[A-Za-z0-9._-]{1,80}){2,3}[ \\t]*$", 'i');
|
|
39
|
+
/** Text above this is never tested against the shapes: they run before the allowlist. */
|
|
40
|
+
const PULSE_TETO = 300, ESCUTA_TETO = 4096;
|
|
41
|
+
|
|
42
|
+
/** The bare-word window (ork.pulse-escuta/v1), read with a byte cap; any error closes it. */
|
|
43
|
+
export function listeningOpen(root: string | undefined, now = Date.now()): boolean {
|
|
44
|
+
try {
|
|
45
|
+
if (!root || !isAbsolute(root)) return false;
|
|
46
|
+
const raw = readFileSync(join(root, '.orkastery', 'monitor', 'pulse-escuta.json'));
|
|
47
|
+
if (raw.length > ESCUTA_TETO) return false;
|
|
48
|
+
const value = JSON.parse(raw.toString('utf8'));
|
|
49
|
+
return value?.contrato === 'ork.pulse-escuta/v1' && typeof value.livreAte === 'string' && Date.parse(value.livreAte) > now;
|
|
50
|
+
} catch { return false; }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function isPulseAnswer(text: string, root: string | undefined = process.env.ORK_HITL_ROOT): boolean {
|
|
54
|
+
if (text.includes('\0') || text.length > PULSE_TETO) return false;
|
|
55
|
+
if (PULSE_CONSENT.test(text) || PULSE_LOTE.test(text) || PULSE_CADENCIA.test(text) || PULSE_LISTA.test(text) ||
|
|
56
|
+
PULSE_RATIFICACAO.test(text)) return true;
|
|
57
|
+
return PULSE_LIVRE.test(text) && listeningOpen(root);
|
|
29
58
|
}
|
|
30
59
|
|
|
31
60
|
export function registerHitlIngress(api: NativeApi, bin: string): void {
|
|
@@ -71,6 +71,9 @@ async function rodarOrk(args: string[], signal?: AbortSignal, entrada?: string):
|
|
|
71
71
|
if (verifiers) env.ORK_RECEIPT_VERIFIERS = verifiers;
|
|
72
72
|
// I-36 (D6): o adaptador declara o proprio canal de conducao. Descreve a porta, nao concede autoridade.
|
|
73
73
|
env.ORK_CANAL = 'openclaw';
|
|
74
|
+
// RM-052 (D3): o cwd do gateway nao e projeto de ninguem. Sem `projeto` pedido, o nucleo usa o unico
|
|
75
|
+
// projeto conhecido da maquina ou devolve a escolha; nunca o manifesto que estiver no cwd do gateway.
|
|
76
|
+
env.ORK_PROJETO_EXPLICITO = '1';
|
|
74
77
|
return new Promise((resolve) => {
|
|
75
78
|
const filho = execFile(bin, args, { maxBuffer: MAX_BUFFER, signal, env }, (erro, stdout, stderr) => {
|
|
76
79
|
const partes = [stdout, stderr].map((t) => (t ?? '').trim()).filter((t) => t !== '');
|
|
@@ -154,6 +157,38 @@ function argvDaResposta(p: Record<string, unknown>, alvo: 'gate' | 'sessions'):
|
|
|
154
157
|
'--mensagem', `telegram:${chat}:${referencia}`, '--origem', 'telegram'];
|
|
155
158
|
}
|
|
156
159
|
|
|
160
|
+
/**
|
|
161
|
+
* RM-052 (D4): o projeto pedido chega como NOME de projeto registrado (`ork projetos`), nunca como
|
|
162
|
+
* caminho. O modelo escolhe entre os projetos da maquina; nao aponta o nucleo para um diretorio.
|
|
163
|
+
*/
|
|
164
|
+
const PADRAO_DO_PROJETO = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/;
|
|
165
|
+
const PARAMETRO_PROJETO = {
|
|
166
|
+
type: 'string',
|
|
167
|
+
pattern: PADRAO_DO_PROJETO.source,
|
|
168
|
+
description: 'Nome do projeto que o dono pediu (ex.: orkastery), como em `ork projetos`. Sem ele e com mais de um projeto na máquina, a resposta é a escolha; nunca use o diretório do gateway.',
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/** O `projeto` de uma tool: o padrao que o host confere, a descricao para o modelo e a recusa. */
|
|
172
|
+
interface ProjetoDaTool { padrao: RegExp; descricao: string; recusa: string }
|
|
173
|
+
|
|
174
|
+
const PROJETO_DA_RM052: ProjetoDaTool = {
|
|
175
|
+
padrao: PADRAO_DO_PROJETO,
|
|
176
|
+
descricao: PARAMETRO_PROJETO.description,
|
|
177
|
+
recusa: 'informe o NOME de um projeto de `ork projetos` (ex.: orkastery), nunca um caminho',
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* RM-054 (fatia 2, D-G4): no roadmap da rede, `projeto` tambem aceita a forja (`github:dono/repo`,
|
|
182
|
+
* `gitlab:grupo/repo`): numa maquina sem clone, e o unico caminho ate o projeto enquanto a rede por
|
|
183
|
+
* pessoa (RM-053) nao chega. Caminho e URL continuam fora (a URL pode levar credencial), e o nucleo
|
|
184
|
+
* confere de novo, porque o adaptador declara ORK_PROJETO_EXPLICITO=1.
|
|
185
|
+
*/
|
|
186
|
+
const PROJETO_DA_REDE: ProjetoDaTool = {
|
|
187
|
+
padrao: /^(?:[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}|(?:github|gitlab):[A-Za-z0-9][A-Za-z0-9._-]{0,99}(?:\/[A-Za-z0-9][A-Za-z0-9._-]{0,99}){1,8})$/,
|
|
188
|
+
descricao: 'Projeto que o dono pediu: o nome, como em `ork projetos` (ex.: orkastery), ou a forja (github:dono/repo, gitlab:grupo/repo) quando não há clone nesta máquina. Sem ele, vêm todos os projetos conhecidos. Nunca caminho nem URL.',
|
|
189
|
+
recusa: 'informe o NOME de um projeto de `ork projetos` (ex.: orkastery) ou github:dono/repo, nunca caminho nem URL',
|
|
190
|
+
};
|
|
191
|
+
|
|
157
192
|
/** Catálogo em paridade nome a nome com o manifesto; aprovação genérica legada aposentada. */
|
|
158
193
|
interface FerramentaOrk {
|
|
159
194
|
name: string;
|
|
@@ -161,12 +196,14 @@ interface FerramentaOrk {
|
|
|
161
196
|
parameters: Record<string, unknown>;
|
|
162
197
|
argv: (params: Record<string, unknown>) => string[];
|
|
163
198
|
entrada?: (params: Record<string, unknown>) => string;
|
|
199
|
+
/** O `projeto` desta tool, quando nao e o da RM-052 (so nome). */
|
|
200
|
+
projeto?: ProjetoDaTool;
|
|
164
201
|
}
|
|
165
202
|
|
|
166
203
|
const FERRAMENTAS: FerramentaOrk[] = [
|
|
167
204
|
{
|
|
168
205
|
name: 'ork_maestro',
|
|
169
|
-
description: 'Ao receber a frase exata orkastery maestro,
|
|
206
|
+
description: 'Ao receber a frase exata orkastery maestro: sem projeto nomeado, chame ork_network_roadmap sem projeto (o panorama da rede, com fontes, frescor e lacunas) e apresente-o como vem; com projeto nomeado, consulte este panorama somente leitura com projeto. Não cria thread. Apresente fontes, lacunas e HITL com recomendação e opções claras; horários para o dono vêm dos campos *Local (fuso do dono), nunca do ISO. O panorama NÃO lê o roadmap, as reservas nem as outras máquinas (veja notConsulted): zero threads nunca é roadmap vazio; para o roadmap use ork_network_roadmap. Ação posterior exige operação autorizada e readback.',
|
|
170
207
|
parameters: { type: 'object', additionalProperties: false, properties: {
|
|
171
208
|
thread: { type: 'string' }, section: { type: 'string', enum: ['portfolio','demands','threads','sessions','blockers','leases','retries','hitl','ship','master','nextActions'] },
|
|
172
209
|
offset: { type: 'integer', minimum: 0, maximum: 100000 },
|
|
@@ -265,27 +302,6 @@ const FERRAMENTAS: FerramentaOrk[] = [
|
|
|
265
302
|
parameters: schema({ thread: { type: 'string' } }),
|
|
266
303
|
argv: (p) => ['phase', 'list', texto(p, 'thread')],
|
|
267
304
|
},
|
|
268
|
-
{
|
|
269
|
-
name: 'ork_objective_status',
|
|
270
|
-
description:
|
|
271
|
-
'Aposentado na I-43 junto com ork objective: devolve a recusa tipada objective.aposentado, com saida != 0. Estado de uma entrega: ork_thread_status. Ciclos ligados a um produto, projeto ou iniciativa: ork portfolio inspect <id> --json.',
|
|
272
|
-
parameters: schema({ objective: { type: 'string', description: 'ID obj-* do ticket' } }),
|
|
273
|
-
argv: (p) => ['objective', 'status', texto(p, 'objective'), '--json'],
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
name: 'ork_objective_message',
|
|
277
|
-
description:
|
|
278
|
-
'Aposentado na I-43 junto com ork objective: devolve a recusa tipada objective.aposentado, com saida != 0, e nao grava nada. Resposta do dono a uma pausa vai por ork_gate_answer ou ork_session_answer.',
|
|
279
|
-
parameters: schema({
|
|
280
|
-
objective: { type: 'string', description: 'ID obj-* do ticket' },
|
|
281
|
-
author: { type: 'string', description: 'Identidade fornecida pelo gateway autenticado' },
|
|
282
|
-
message: { type: 'string', description: 'Texto integral recebido do dono' },
|
|
283
|
-
}),
|
|
284
|
-
argv: (p) => [
|
|
285
|
-
'objective', 'message', texto(p, 'objective'),
|
|
286
|
-
'--text', texto(p, 'message'), '--por', texto(p, 'author'), '--role', 'human',
|
|
287
|
-
],
|
|
288
|
-
},
|
|
289
305
|
{
|
|
290
306
|
name: 'ork_portfolio_list',
|
|
291
307
|
description: 'Lista produtos, projetos e iniciativas do catálogo canônico usado pelo Kanban.',
|
|
@@ -313,6 +329,29 @@ const FERRAMENTAS: FerramentaOrk[] = [
|
|
|
313
329
|
parameters: schema({ thread: { type: 'string' }, id: { type: 'string' } }),
|
|
314
330
|
argv: (p) => ['brain', 'get', texto(p, 'id'), '--thread', texto(p, 'thread')],
|
|
315
331
|
},
|
|
332
|
+
{
|
|
333
|
+
name: 'ork_brain_context',
|
|
334
|
+
description:
|
|
335
|
+
'Pacote de contexto citável do Company Brain: entidades pedidas e seus pais, cada uma com a citação da fonte, o frescor contra o portfólio e as lacunas. Só leitura.',
|
|
336
|
+
parameters: schema({
|
|
337
|
+
thread: { type: 'string' },
|
|
338
|
+
ids: { type: 'string', description: 'ids do portfólio separados por vírgula (prod-, proj-, init-)' },
|
|
339
|
+
}),
|
|
340
|
+
argv: (p) => ['brain', 'context', '--thread', texto(p, 'thread'), '--ids', texto(p, 'ids')],
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: 'ork_brain_dossie',
|
|
344
|
+
description:
|
|
345
|
+
'Dossiê de decisão da thread: vínculo com objetivo e projeto, contexto citável, alternativas, decisão, quem decidiu e evidência, com os ids do Company Brain e as lacunas. Resposta do dono só com recibo conferido. Só leitura.',
|
|
346
|
+
parameters: {
|
|
347
|
+
type: 'object', additionalProperties: false, required: ['thread'],
|
|
348
|
+
properties: {
|
|
349
|
+
thread: { type: 'string' },
|
|
350
|
+
decisao: { type: 'string', description: 'id do pedido ou fact- da decisão; omita para a thread inteira' },
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
argv: (p) => ['brain', 'dossie', '--thread', texto(p, 'thread'), ...(p.decisao === undefined ? [] : ['--decisao', texto(p, 'decisao')])],
|
|
354
|
+
},
|
|
316
355
|
{
|
|
317
356
|
name: 'ork_claims_add',
|
|
318
357
|
description:
|
|
@@ -396,31 +435,35 @@ const FERRAMENTAS: FerramentaOrk[] = [
|
|
|
396
435
|
{
|
|
397
436
|
name: 'ork_master',
|
|
398
437
|
description:
|
|
399
|
-
'
|
|
438
|
+
'Pede ao dono a nota do MASTER (0 a 5, com o porque) de uma entrega: devolve a linha com o codigo curto para mostrar a ele. A nota volta pelo Telegram, com a prova do ingresso. Esta tool nunca grava nota e nao recebe nome de pessoa (RM-048).',
|
|
400
439
|
parameters: schema({
|
|
401
440
|
thread: { type: 'string' },
|
|
402
|
-
score: { type: 'string', description: 'inteiro de 0 a 5, dado pelo humano' },
|
|
403
|
-
justificativa: { type: 'string' },
|
|
404
|
-
quem: { type: 'string' },
|
|
405
441
|
}),
|
|
406
|
-
argv: (p) => [
|
|
407
|
-
'master',
|
|
408
|
-
texto(p, 'thread'),
|
|
409
|
-
'--score',
|
|
410
|
-
texto(p, 'score'),
|
|
411
|
-
'--justificativa',
|
|
412
|
-
texto(p, 'justificativa'),
|
|
413
|
-
'--por',
|
|
414
|
-
texto(p, 'quem'),
|
|
415
|
-
],
|
|
442
|
+
argv: (p) => ['master', 'pedir', texto(p, 'thread'), '--formato', 'telegram'],
|
|
416
443
|
},
|
|
417
444
|
{
|
|
418
445
|
name: 'ork_board',
|
|
419
446
|
description:
|
|
420
|
-
'
|
|
447
|
+
'As threads DESTE projeto nesta maquina e o escalonador dizendo quem avanca agora e quem espera. NAO le o roadmap: nunca conclua sobre o roadmap a partir do board (zero threads nao e roadmap vazio); para o roadmap use ork_network_roadmap. O cabecalho diz o projeto consultado e o que nao foi lido.',
|
|
421
448
|
parameters: schema({}),
|
|
422
449
|
argv: () => ['board', 'plan'],
|
|
423
450
|
},
|
|
451
|
+
{
|
|
452
|
+
name: 'ork_roadmap_status',
|
|
453
|
+
description:
|
|
454
|
+
'Status report do roadmap SO desta maquina (o checkout local), no formato aprovado pelo dono (grupos com icones, #HITL e o fecho): nao le as reservas nem as outras maquinas, e o cabecalho diz isso. Para o status do roadmap com as threads de todas as maquinas, as reservas, as fontes e as lacunas, use ork_network_roadmap. Somente leitura: transporte o texto como vem, sem reescrever. Passe projeto com o nome que o dono pediu (ex.: orkastery); sem ele e com mais de um projeto na maquina, a resposta e a escolha. Nunca deduza o roadmap de ork_board ou ork_maestro.',
|
|
455
|
+
parameters: schema({}),
|
|
456
|
+
argv: () => ['roadmap', 'status'],
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
name: 'ork_network_roadmap',
|
|
460
|
+
description:
|
|
461
|
+
'Roadmap da rede, somente leitura: para cada projeto, o status report do roadmap (RM-048) com as threads de TODAS as máquinas, as reservas, as threads por máquina com a idade da batida, a fonte e a hora de cada parte e as lacunas. É a fonte para qualquer pergunta sobre o roadmap ou o status report: transporte o texto como vem, sem reescrever nem resumir. Passe projeto com o nome que o dono pediu (ex.: orkastery) ou github:dono/repo; sem projeto, vêm todos os projetos conhecidos, e esse é o panorama da frase orkastery maestro sem projeto. Lacuna, "não lido" e "Não consultado" são fontes que ficaram sem leitura: nunca conclua "roadmap vazio" nem "nenhuma máquina publicou" a partir delas.',
|
|
462
|
+
parameters: schema({}),
|
|
463
|
+
projeto: PROJETO_DA_REDE,
|
|
464
|
+
// RM-054 (fatia 2): uma chamada de CLI; o `--projeto` vai no inicio e o nucleo o devolve ao `network`.
|
|
465
|
+
argv: () => ['network', 'roadmap'],
|
|
466
|
+
},
|
|
424
467
|
{
|
|
425
468
|
name: 'ork_master_batch',
|
|
426
469
|
description:
|
|
@@ -430,20 +473,44 @@ const FERRAMENTAS: FerramentaOrk[] = [
|
|
|
430
473
|
},
|
|
431
474
|
];
|
|
432
475
|
|
|
476
|
+
/** RM-052: toda tool aceita `projeto` opcional, sem mudar o que ela ja exigia. */
|
|
477
|
+
function comProjeto(f: FerramentaOrk): Record<string, unknown> {
|
|
478
|
+
const propriedades = (f.parameters.properties ?? {}) as Record<string, unknown>;
|
|
479
|
+
const p = f.projeto;
|
|
480
|
+
const projeto = p ? { type: 'string', pattern: p.padrao.source, description: p.descricao } : PARAMETRO_PROJETO;
|
|
481
|
+
return { ...f.parameters, properties: { ...propriedades, projeto } };
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** `--projeto <nome>` vai no inicio do argv; o resto dos parametros segue para a tool como antes. */
|
|
485
|
+
function argvComProjeto(f: FerramentaOrk, params: Record<string, unknown>): { argv: string[]; resto: Record<string, unknown> } {
|
|
486
|
+
const { projeto, ...resto } = params;
|
|
487
|
+
if (projeto !== undefined && (typeof projeto !== 'string' || !(f.projeto ?? PROJETO_DA_RM052).padrao.test(projeto))) {
|
|
488
|
+
throw new Error('projeto.invalido');
|
|
489
|
+
}
|
|
490
|
+
return { argv: [...(projeto === undefined ? [] : ['--projeto', projeto]), ...f.argv(resto)], resto };
|
|
491
|
+
}
|
|
492
|
+
|
|
433
493
|
const plugin = defineToolPlugin({
|
|
434
494
|
id: 'orkastery',
|
|
435
495
|
name: 'Orkastery',
|
|
436
496
|
description:
|
|
437
|
-
'Conducao de looping threads em 6 fases pelo nucleo `ork`, exposta ao OpenClaw como tools `ork_*`. Zero regra de negocio no host: cada tool e uma chamada de CLI.',
|
|
497
|
+
'Conducao de looping threads em 6 fases pelo nucleo `ork`, exposta ao OpenClaw como tools `ork_*`. Zero regra de negocio no host: cada tool e uma chamada de CLI. Toda tool aceita projeto (nome do projeto pedido); o cwd do gateway nunca escolhe o projeto. O status do roadmap vem de ork_network_roadmap.',
|
|
438
498
|
tools: (tool) =>
|
|
439
499
|
FERRAMENTAS.map((f) =>
|
|
440
500
|
tool({
|
|
441
501
|
name: f.name,
|
|
442
502
|
description: f.description,
|
|
443
|
-
parameters: f
|
|
503
|
+
parameters: comProjeto(f),
|
|
444
504
|
execute: (params, _config, contexto) => {
|
|
445
|
-
|
|
446
|
-
|
|
505
|
+
let chamada: ReturnType<typeof argvComProjeto>;
|
|
506
|
+
try { chamada = argvComProjeto(f, params); }
|
|
507
|
+
catch (e) {
|
|
508
|
+
if ((e as Error).message === 'projeto.invalido') {
|
|
509
|
+
return `[ork recusou] projeto.invalido: ${(f.projeto ?? PROJETO_DA_RM052).recusa}`;
|
|
510
|
+
}
|
|
511
|
+
return '[ork recusou] resposta humana não confirmada; confira origem e correlação do pedido';
|
|
512
|
+
}
|
|
513
|
+
return rodarOrk(chamada.argv, contexto.signal, f.entrada?.(chamada.resto));
|
|
447
514
|
},
|
|
448
515
|
})
|
|
449
516
|
),
|
|
@@ -16,7 +16,7 @@ O **roadmap** registra problemas, hipóteses, escolhas, prioridades e andamento
|
|
|
16
16
|
|
|
17
17
|
| Regra | Aplicação |
|
|
18
18
|
| --- | --- |
|
|
19
|
-
| Identidade | Atribua IDs estáveis, por exemplo `PLAT-01`, `SYS-01`, `MOD-01`, `FEAT-042`, `UC-042-01`, `BR-042-01`, `API-01`, `EVT-01`, `JOB-01` e `RM-042`. Renomear não muda o ID. |
|
|
19
|
+
| Identidade | Atribua IDs estáveis, por exemplo `PLAT-01`, `SYS-01`, `MOD-01`, `FEAT-042`, `UC-042-01`, `BR-042-01`, `API-01`, `EVT-01`, `JOB-01` e `RM-042`. Renomear não muda o ID. No Orkastery, o número da FEAT nova sai de `ork roadmap feat`, reservado entre máquinas; nunca do maior número da sua branch. |
|
|
20
20
|
| Escopo e estado | Informe se a página descreve `vigente`, `em desenvolvimento`, `proposto` ou `descontinuado`, com ambiente, versão e data de verificação. |
|
|
21
21
|
| Rastreabilidade | Ligue feature ↔ item de roadmap ↔ casos de uso/regras ↔ contratos/esquemas ↔ código/PR ↔ testes/evidências ↔ release. Use `Não aplicável — motivo` quando uma seção não fizer sentido. |
|
|
22
22
|
| Fonte | Diferencie `confirmado` (código, contrato, teste ou decisão aprovada), `planejado` (item de roadmap) e `a validar`. Nunca complete uma lacuna por inferência silenciosa. |
|
|
@@ -75,7 +75,7 @@ Registre risco, perfis e matriz de permissões (RBAC/ABAC), autenticação/sess
|
|
|
75
75
|
|
|
76
76
|
## 4. Modelo de página de feature
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
Reserve o número com `ork roadmap feat --thread <thread>` e copie [o modelo](../produto/_modelo-feature.md) para cada feature; substitua colchetes por valores ou por `Não aplicável — motivo`. O frontmatter é a camada que agentes e o verificador leem; o corpo é a camada que pessoas leem. Acrescente tabelas de campos e contratos quando houver vários elementos.
|
|
79
79
|
|
|
80
80
|
Chaves do frontmatter de uma feature:
|
|
81
81
|
|
|
@@ -80,7 +80,7 @@ Copie [o modelo](../roadmap/_modelo-item.md) para cada item. O frontmatter guard
|
|
|
80
80
|
|
|
81
81
|
Revisite itens ativos na cadência do time e sempre após decisão, PR relevante, teste, deploy, alteração de flag ou medição de resultado. A cada revisão, compare roadmap, documentação da feature, código, contratos e evidências de produção; anote divergências com owner e prazo. Agentes podem sugerir atualização de status a partir de eventos do repositório, mas um PR mesclado atualiza somente a dimensão **código** até que deploy e exposição sejam comprovados. Registre a data de atualização e mantenha histórico das decisões e mudanças de escopo.
|
|
82
82
|
|
|
83
|
-
No Orkastery, `ork docs sincronizar` é esse agente: ele atualiza `estado.codigo` e `evidencias.codigo.commit` a partir do ledger (`ship_done`) e do git, e a seção `sdlc` a partir da thread. **Ele nunca mexe em ciclo, documentação, deploy, exposição ou habilitação** — essas dimensões são decisão de pessoa, e o verificador só cobra que estejam coerentes com o código.
|
|
83
|
+
No Orkastery, `ork docs sincronizar` é esse agente: ele atualiza `estado.codigo` e `evidencias.codigo.commit` a partir do ledger (`ship_done`) e do git, e a seção `sdlc` a partir da thread. **Ele nunca mexe em ciclo, documentação, deploy, exposição ou habilitação** — essas dimensões são decisão de pessoa, e o verificador só cobra que estejam coerentes com o código. Na worktree de uma thread, ele só toca o item dela e os índices, para que o PR de uma thread não mude o item de outra; `--so RM-NNN` escolhe os itens, e `--todos` (o padrão na raiz do projeto, onde o condutor sincroniza depois do merge) volta a todos.
|
|
84
84
|
|
|
85
85
|
## 6. Três leitores e fatos de SDLC (adaptação Orkastery)
|
|
86
86
|
|
|
@@ -17,6 +17,8 @@ fontes:
|
|
|
17
17
|
comandos: []
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
+
<!-- O número da feature sai de `ork roadmap feat --thread <thread>`, reservado entre máquinas; nunca do maior número da branch. -->
|
|
21
|
+
|
|
20
22
|
# FEAT-000 — Nome da feature
|
|
21
23
|
|
|
22
24
|
> **Em uma frase:** [o que esta feature faz, para quem, em até 240 caracteres]
|