@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.
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/adapters/README.md +22 -0
- package/adapters/claude-code/.claude-plugin/marketplace.json +15 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +31 -0
- package/adapters/claude-code/README.md +102 -0
- package/adapters/claude-code/agents/ork-check.md +32 -0
- package/adapters/claude-code/agents/ork-go.md +32 -0
- package/adapters/claude-code/agents/ork-goal.md +33 -0
- package/adapters/claude-code/agents/ork-master.md +32 -0
- package/adapters/claude-code/agents/ork-plan.md +32 -0
- package/adapters/claude-code/agents/ork-ship.md +32 -0
- package/adapters/claude-code/commands/check.md +45 -0
- package/adapters/claude-code/commands/go.md +46 -0
- package/adapters/claude-code/commands/goal.md +54 -0
- package/adapters/claude-code/commands/master.md +42 -0
- package/adapters/claude-code/commands/ork.md +37 -0
- package/adapters/claude-code/commands/plan.md +47 -0
- package/adapters/claude-code/commands/ship.md +45 -0
- package/adapters/claude-code/hooks/hooks.json +17 -0
- package/adapters/claude-code/hooks/ork-guard.js +130 -0
- package/adapters/hermes/README.md +45 -0
- package/adapters/hermes/bin/ork-abrir-thread.sh +25 -0
- package/adapters/hermes/hermes.plugin.json +21 -0
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +116 -0
- package/adapters/openclaw/README.md +63 -0
- package/adapters/openclaw/bin/ork-abrir-thread.sh +24 -0
- package/adapters/openclaw/openclaw.plugin.json +138 -0
- package/dist/adapters/claude-bg.js +308 -0
- package/dist/auditoria.js +848 -0
- package/dist/auditrun.js +976 -0
- package/dist/board.js +314 -0
- package/dist/canarios.js +271 -0
- package/dist/catalogo.js +124 -0
- package/dist/ciclos.js +156 -0
- package/dist/claims.js +226 -0
- package/dist/divida.js +374 -0
- package/dist/doctor.js +246 -0
- package/dist/evalrunner.js +458 -0
- package/dist/fix.js +450 -0
- package/dist/gates.js +92 -0
- package/dist/handoff.js +521 -0
- package/dist/hosts.js +331 -0
- package/dist/index.js +1910 -0
- package/dist/init.js +231 -0
- package/dist/leases.js +508 -0
- package/dist/ledger.js +138 -0
- package/dist/manifest.js +255 -0
- package/dist/master.js +453 -0
- package/dist/memoria.js +773 -0
- package/dist/modos.js +220 -0
- package/dist/orkmind.js +487 -0
- package/dist/orquestracao.js +559 -0
- package/dist/phase.js +572 -0
- package/dist/policies.js +176 -0
- package/dist/prompts.js +406 -0
- package/dist/ratelimit.js +179 -0
- package/dist/recall.js +252 -0
- package/dist/retry.js +917 -0
- package/dist/sandbox.js +94 -0
- package/dist/sessoes.js +104 -0
- package/dist/ship.js +551 -0
- package/dist/slug.js +100 -0
- package/dist/superficie.js +1347 -0
- package/dist/thread.js +333 -0
- package/dist/tokens.js +228 -0
- package/dist/types.js +20 -0
- package/dist/util.js +156 -0
- package/dist/verify.js +262 -0
- package/dist/worktree.js +576 -0
- package/dist/yaml.js +112 -0
- package/eval/README.md +85 -0
- package/eval/casos/check-quality.json +103 -0
- package/eval/casos/code-reviewer.json +102 -0
- package/eval/casos/decision-triage.json +103 -0
- package/eval/casos/go-implementation.json +103 -0
- package/eval/casos/goal-definition.json +103 -0
- package/eval/casos/master-metrics.json +103 -0
- package/eval/casos/narrative-guardian.json +163 -0
- package/eval/casos/orkastery-bootstrap.json +102 -0
- package/eval/casos/plan-specification.json +102 -0
- package/eval/casos/roadmap-keeper.json +103 -0
- package/eval/casos/scope-check-capability-map.json +83 -0
- package/eval/casos/security-auditor.json +102 -0
- package/eval/casos/ship-release.json +122 -0
- package/eval/casos/test-engineer.json +122 -0
- package/eval/casos/thread-state.json +82 -0
- package/eval/casos/thread-tracing.json +83 -0
- package/eval/casos/web-performance-auditor.json +102 -0
- package/eval/fixtures/b0-slug-e-modos/caso.json +56 -0
- package/eval/fixtures/b2-master-log/caso.json +91 -0
- package/eval/fixtures/fx-concurrency/caso.json +15 -0
- package/eval/fixtures/fx-hallucination/caso.json +12 -0
- package/eval/fixtures/fx-happy/caso.json +16 -0
- package/eval/fixtures/fx-schema-drift/caso.json +15 -0
- package/eval/fixtures/fx-stale-base/caso.json +12 -0
- package/eval/fixtures/fx-wiki-destroy/caso.json +16 -0
- package/eval/fixtures/superficie-de-rede/README.md +22 -0
- package/eval/fixtures/superficie-de-rede/api-express.js +30 -0
- package/eval/fixtures/superficie-de-rede/api_fastapi.py +17 -0
- package/eval/fixtures/superficie-de-rede/api_gin.go +18 -0
- package/package.json +56 -0
- package/references/README.md +25 -0
- package/references/code-review-axes.md +82 -0
- package/references/definition-of-done.md +74 -0
- package/references/performance-checklist.md +53 -0
- package/references/security-checklist.md +101 -0
- package/references/testing-patterns.md +51 -0
- package/skills/README.md +43 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +89 -0
- package/skills/core/thread-state/SKILL.md +86 -0
- package/skills/governance/decision-triage/SKILL.md +65 -0
- package/skills/governance/narrative-guardian/SKILL.md +66 -0
- package/skills/governance/roadmap-keeper/SKILL.md +60 -0
- package/skills/governance/scope-check-capability-map/SKILL.md +61 -0
- package/skills/observability/thread-tracing/SKILL.md +63 -0
- package/skills/phases/check-quality/SKILL.md +87 -0
- package/skills/phases/go-implementation/SKILL.md +70 -0
- package/skills/phases/goal-definition/SKILL.md +69 -0
- package/skills/phases/master-metrics/SKILL.md +65 -0
- package/skills/phases/plan-specification/SKILL.md +67 -0
- package/skills/phases/ship-release/SKILL.md +68 -0
- package/skills/reviewers/code-reviewer/SKILL.md +61 -0
- package/skills/reviewers/security-auditor/SKILL.md +68 -0
- package/skills/reviewers/test-engineer/SKILL.md +66 -0
- package/skills/reviewers/web-performance-auditor/SKILL.md +63 -0
package/dist/worktree.js
ADDED
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `ork worktree ensure|sync|audit|release`: a worktree como RECURSO GERENCIADO (bloco B2).
|
|
4
|
+
*
|
|
5
|
+
* No Devmaster a worktree era criada na mao e conferida de memoria. Aqui ela tem ciclo de
|
|
6
|
+
* vida: o `ork` garante que existe, sincroniza quando a base andou, audita a consistencia
|
|
7
|
+
* no proprio git (nunca por self-report) e devolve o recurso ao fim.
|
|
8
|
+
*
|
|
9
|
+
* Duas regras nao negociaveis:
|
|
10
|
+
* 1. a BASE e resolvida pelo `ork` (manifesto), nunca escolhida pelo executor;
|
|
11
|
+
* 2. os artefatos da thread (prompts, ledger, claims, handoffs) ficam em
|
|
12
|
+
* `.orkastery/threads/<id>/`, FORA da arvore, entao remover a worktree nunca
|
|
13
|
+
* leva junto a evidencia da conducao.
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.dirDaWorktree = dirDaWorktree;
|
|
50
|
+
exports.branchDaWorktree = branchDaWorktree;
|
|
51
|
+
exports.worktreesDoGit = worktreesDoGit;
|
|
52
|
+
exports.registradaNoGit = registradaNoGit;
|
|
53
|
+
exports.arvoreLimpa = arvoreLimpa;
|
|
54
|
+
exports.garantirWorktree = garantirWorktree;
|
|
55
|
+
exports.sincronizarWorktree = sincronizarWorktree;
|
|
56
|
+
exports.auditarWorktree = auditarWorktree;
|
|
57
|
+
exports.textoDoAudit = textoDoAudit;
|
|
58
|
+
exports.liberarWorktree = liberarWorktree;
|
|
59
|
+
const fs = __importStar(require("node:fs"));
|
|
60
|
+
const path = __importStar(require("node:path"));
|
|
61
|
+
const ledger_1 = require("./ledger");
|
|
62
|
+
const leases_1 = require("./leases");
|
|
63
|
+
const thread_1 = require("./thread");
|
|
64
|
+
const util_1 = require("./util");
|
|
65
|
+
/** Diretorio canonico da worktree de uma thread, resolvido pelo `ork`. */
|
|
66
|
+
function dirDaWorktree(carregado, id) {
|
|
67
|
+
return path.resolve(carregado.raiz, carregado.manifesto.worktree.dir, id);
|
|
68
|
+
}
|
|
69
|
+
/** Branch canonica da worktree de uma thread. */
|
|
70
|
+
function branchDaWorktree(thread) {
|
|
71
|
+
return thread.base.branch.startsWith('ork/') ? thread.base.branch : `ork/${thread.slug}`;
|
|
72
|
+
}
|
|
73
|
+
/** As worktrees que o git realmente conhece (`git worktree list --porcelain`). */
|
|
74
|
+
function worktreesDoGit(raiz) {
|
|
75
|
+
const r = (0, util_1.exec)('git', ['worktree', 'list', '--porcelain'], raiz);
|
|
76
|
+
if (!r.ok)
|
|
77
|
+
return [];
|
|
78
|
+
const saida = [];
|
|
79
|
+
let atual = null;
|
|
80
|
+
for (const linha of r.stdout.split('\n')) {
|
|
81
|
+
if (linha.startsWith('worktree ')) {
|
|
82
|
+
if (atual)
|
|
83
|
+
saida.push(atual);
|
|
84
|
+
atual = { dir: linha.slice('worktree '.length).trim(), branch: '', head: '' };
|
|
85
|
+
}
|
|
86
|
+
else if (atual && linha.startsWith('HEAD ')) {
|
|
87
|
+
atual.head = linha.slice('HEAD '.length).trim();
|
|
88
|
+
}
|
|
89
|
+
else if (atual && linha.startsWith('branch ')) {
|
|
90
|
+
atual.branch = linha.slice('branch '.length).trim().replace(/^refs\/heads\//, '');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (atual)
|
|
94
|
+
saida.push(atual);
|
|
95
|
+
return saida;
|
|
96
|
+
}
|
|
97
|
+
/** A worktree deste diretorio esta registrada no git? */
|
|
98
|
+
function registradaNoGit(raiz, dir) {
|
|
99
|
+
return worktreesDoGit(raiz).some((w) => w.dir === dir);
|
|
100
|
+
}
|
|
101
|
+
/** A arvore da worktree esta limpa? `null` quando o diretorio nem e uma arvore git. */
|
|
102
|
+
function arvoreLimpa(dir) {
|
|
103
|
+
const r = (0, util_1.exec)('git', ['status', '--porcelain'], dir);
|
|
104
|
+
if (!r.ok)
|
|
105
|
+
return null;
|
|
106
|
+
return r.stdout.trim() === '';
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Garante a worktree da thread: cria se faltar, confere se ja existe.
|
|
110
|
+
*
|
|
111
|
+
* Nunca conserta sozinho uma worktree divergente: divergencia e assunto do `audit` e do
|
|
112
|
+
* `sync`, que dizem o que houve em vez de esconder.
|
|
113
|
+
*/
|
|
114
|
+
function garantirWorktree(carregado, id) {
|
|
115
|
+
const { raiz, manifesto } = carregado;
|
|
116
|
+
const thread = (0, thread_1.lerThread)(raiz, id);
|
|
117
|
+
const dir = thread.worktree ?? dirDaWorktree(carregado, id);
|
|
118
|
+
const branch = branchDaWorktree(thread);
|
|
119
|
+
const base = manifesto.worktree.base_branch;
|
|
120
|
+
if (fs.existsSync(dir)) {
|
|
121
|
+
if (!registradaNoGit(raiz, dir)) {
|
|
122
|
+
return {
|
|
123
|
+
ok: false,
|
|
124
|
+
criada: false,
|
|
125
|
+
dir,
|
|
126
|
+
branch,
|
|
127
|
+
base,
|
|
128
|
+
motivo: 'tree.blocked',
|
|
129
|
+
detalhe: `${dir} existe mas nao aparece em \`git worktree list\``,
|
|
130
|
+
correcao: `remova o diretorio ou registre a worktree; depois rode: ork worktree ensure ${id}`,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (thread.worktree !== dir) {
|
|
134
|
+
thread.worktree = dir;
|
|
135
|
+
(0, thread_1.gravarThread)(raiz, thread);
|
|
136
|
+
}
|
|
137
|
+
(0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.worktreeGarantida, {
|
|
138
|
+
dir,
|
|
139
|
+
branch,
|
|
140
|
+
base,
|
|
141
|
+
criada: false,
|
|
142
|
+
fonte: 'git worktree list --porcelain',
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
ok: true,
|
|
146
|
+
criada: false,
|
|
147
|
+
dir,
|
|
148
|
+
branch,
|
|
149
|
+
base,
|
|
150
|
+
motivo: null,
|
|
151
|
+
detalhe: 'a worktree da thread ja existe e o git a reconhece',
|
|
152
|
+
correcao: '',
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// Branch da thread ja existe (worktree removida antes)? Entao reusa a branch.
|
|
156
|
+
const jaTemBranch = (0, util_1.exec)('git', ['rev-parse', '--verify', `refs/heads/${branch}`], raiz).ok;
|
|
157
|
+
const criada = (0, thread_1.criarWorktree)(carregado, id, thread.slug, {
|
|
158
|
+
branchExistente: jaTemBranch ? branch : undefined,
|
|
159
|
+
});
|
|
160
|
+
thread.worktree = criada.dir;
|
|
161
|
+
thread.base = { branch: criada.branch, commit: thread.base.commit };
|
|
162
|
+
(0, thread_1.gravarThread)(raiz, thread);
|
|
163
|
+
(0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.worktreeGarantida, {
|
|
164
|
+
dir: criada.dir,
|
|
165
|
+
branch: criada.branch,
|
|
166
|
+
base,
|
|
167
|
+
criada: true,
|
|
168
|
+
branchReusada: jaTemBranch,
|
|
169
|
+
fonte: 'git worktree list --porcelain',
|
|
170
|
+
});
|
|
171
|
+
return {
|
|
172
|
+
ok: true,
|
|
173
|
+
criada: true,
|
|
174
|
+
dir: criada.dir,
|
|
175
|
+
branch: criada.branch,
|
|
176
|
+
base,
|
|
177
|
+
motivo: null,
|
|
178
|
+
detalhe: jaTemBranch
|
|
179
|
+
? `worktree recriada sobre a branch ${criada.branch}, que ja existia`
|
|
180
|
+
: `worktree criada a partir de ${base}`,
|
|
181
|
+
correcao: '',
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Sincroniza a branch da thread com a base ATUAL (rebase), quando a base avancou.
|
|
186
|
+
*
|
|
187
|
+
* A pergunta "a base avancou?" e respondida pelo git (`merge-base --is-ancestor`), nao
|
|
188
|
+
* pela data de criacao da thread: e o mesmo criterio que o `audit` usa.
|
|
189
|
+
*/
|
|
190
|
+
function sincronizarWorktree(carregado, id, opcoes = {}) {
|
|
191
|
+
const { raiz, manifesto } = carregado;
|
|
192
|
+
const thread = (0, thread_1.lerThread)(raiz, id);
|
|
193
|
+
const base = manifesto.worktree.base_branch;
|
|
194
|
+
const dir = thread.worktree ?? dirDaWorktree(carregado, id);
|
|
195
|
+
const branch = branchDaWorktree(thread);
|
|
196
|
+
const passos = [];
|
|
197
|
+
const vazio = {
|
|
198
|
+
dir,
|
|
199
|
+
branch,
|
|
200
|
+
base,
|
|
201
|
+
shaBase: '',
|
|
202
|
+
shaAntes: '',
|
|
203
|
+
shaDepois: '',
|
|
204
|
+
passos,
|
|
205
|
+
dryRun: opcoes.dryRun === true,
|
|
206
|
+
};
|
|
207
|
+
if (!fs.existsSync(dir) || !registradaNoGit(raiz, dir)) {
|
|
208
|
+
return {
|
|
209
|
+
...vazio,
|
|
210
|
+
ok: false,
|
|
211
|
+
jaAtualizada: false,
|
|
212
|
+
rebaseFeito: false,
|
|
213
|
+
motivo: 'tree.blocked',
|
|
214
|
+
detalhe: `a thread ${id} nao tem worktree registrada no git`,
|
|
215
|
+
correcao: `crie a worktree com: ork worktree ensure ${id}`,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
const limpa = arvoreLimpa(dir);
|
|
219
|
+
if (limpa !== true) {
|
|
220
|
+
return {
|
|
221
|
+
...vazio,
|
|
222
|
+
ok: false,
|
|
223
|
+
jaAtualizada: false,
|
|
224
|
+
rebaseFeito: false,
|
|
225
|
+
motivo: 'tree.blocked',
|
|
226
|
+
detalhe: limpa === null
|
|
227
|
+
? `${dir} nao respondeu a \`git status\``
|
|
228
|
+
: `${dir} tem alteracao nao commitada; rebase em arvore suja perde trabalho`,
|
|
229
|
+
correcao: `commite ou guarde as alteracoes em ${dir} e rode de novo: ork worktree sync ${id}`,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
const refBase = (0, util_1.exec)('git', ['rev-parse', '--verify', base], raiz);
|
|
233
|
+
if (!refBase.ok) {
|
|
234
|
+
return {
|
|
235
|
+
...vazio,
|
|
236
|
+
ok: false,
|
|
237
|
+
jaAtualizada: false,
|
|
238
|
+
rebaseFeito: false,
|
|
239
|
+
motivo: 'tree.blocked',
|
|
240
|
+
detalhe: `a base ${base} do manifesto nao existe neste repositorio`,
|
|
241
|
+
correcao: `corrija worktree.base_branch em ${carregado.caminho}`,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const shaBase = refBase.stdout.trim();
|
|
245
|
+
const shaAntes = (0, util_1.exec)('git', ['rev-parse', 'HEAD'], dir).stdout.trim();
|
|
246
|
+
passos.push(`git rev-parse --verify ${base} (${shaBase.slice(0, 8)})`);
|
|
247
|
+
const jaTem = (0, util_1.exec)('git', ['merge-base', '--is-ancestor', shaBase, 'HEAD'], dir).ok;
|
|
248
|
+
passos.push(`git merge-base --is-ancestor ${shaBase.slice(0, 8)} HEAD`);
|
|
249
|
+
if (jaTem) {
|
|
250
|
+
if (!opcoes.dryRun) {
|
|
251
|
+
(0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.worktreeSincronizada, {
|
|
252
|
+
dir,
|
|
253
|
+
branch,
|
|
254
|
+
base,
|
|
255
|
+
shaBase,
|
|
256
|
+
shaAntes,
|
|
257
|
+
shaDepois: shaAntes,
|
|
258
|
+
rebaseFeito: false,
|
|
259
|
+
detalhe: 'a base ja era ancestral do HEAD da thread',
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
...vazio,
|
|
264
|
+
shaBase,
|
|
265
|
+
shaAntes,
|
|
266
|
+
shaDepois: shaAntes,
|
|
267
|
+
ok: true,
|
|
268
|
+
jaAtualizada: true,
|
|
269
|
+
rebaseFeito: false,
|
|
270
|
+
motivo: null,
|
|
271
|
+
detalhe: `a base ${base} ja esta incorporada: nada a sincronizar`,
|
|
272
|
+
correcao: '',
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
if (opcoes.dryRun) {
|
|
276
|
+
passos.push(`git rebase ${shaBase}`);
|
|
277
|
+
return {
|
|
278
|
+
...vazio,
|
|
279
|
+
shaBase,
|
|
280
|
+
shaAntes,
|
|
281
|
+
shaDepois: shaAntes,
|
|
282
|
+
ok: true,
|
|
283
|
+
jaAtualizada: false,
|
|
284
|
+
rebaseFeito: false,
|
|
285
|
+
motivo: null,
|
|
286
|
+
detalhe: `a base ${base} avancou; o sync real faria o rebase acima`,
|
|
287
|
+
correcao: '',
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
const rebase = (0, util_1.exec)('git', ['rebase', shaBase], dir);
|
|
291
|
+
passos.push(`git rebase ${shaBase}`);
|
|
292
|
+
if (!rebase.ok) {
|
|
293
|
+
(0, util_1.exec)('git', ['rebase', '--abort'], dir);
|
|
294
|
+
passos.push('git rebase --abort');
|
|
295
|
+
const detalhe = (rebase.stderr || rebase.stdout).trim().split('\n').slice(-4).join(' | ');
|
|
296
|
+
(0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.worktreeSincronizada, {
|
|
297
|
+
dir,
|
|
298
|
+
branch,
|
|
299
|
+
base,
|
|
300
|
+
shaBase,
|
|
301
|
+
shaAntes,
|
|
302
|
+
rebaseFeito: false,
|
|
303
|
+
motivo: 'tree.blocked',
|
|
304
|
+
detalhe,
|
|
305
|
+
});
|
|
306
|
+
return {
|
|
307
|
+
...vazio,
|
|
308
|
+
shaBase,
|
|
309
|
+
shaAntes,
|
|
310
|
+
shaDepois: shaAntes,
|
|
311
|
+
ok: false,
|
|
312
|
+
jaAtualizada: false,
|
|
313
|
+
rebaseFeito: false,
|
|
314
|
+
motivo: 'tree.blocked',
|
|
315
|
+
detalhe: `o rebase sobre ${base} conflitou e foi abortado: ${detalhe}`,
|
|
316
|
+
correcao: `resolva o conflito na worktree ${dir} (git rebase ${shaBase} e resolucao manual), ` +
|
|
317
|
+
`ou reduza a regiao da thread com um lease path:<glob> menor`,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
const shaDepois = (0, util_1.exec)('git', ['rev-parse', 'HEAD'], dir).stdout.trim();
|
|
321
|
+
(0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.worktreeSincronizada, {
|
|
322
|
+
dir,
|
|
323
|
+
branch,
|
|
324
|
+
base,
|
|
325
|
+
shaBase,
|
|
326
|
+
shaAntes,
|
|
327
|
+
shaDepois,
|
|
328
|
+
rebaseFeito: true,
|
|
329
|
+
detalhe: `branch da thread rebasada sobre ${base}`,
|
|
330
|
+
});
|
|
331
|
+
return {
|
|
332
|
+
...vazio,
|
|
333
|
+
shaBase,
|
|
334
|
+
shaAntes,
|
|
335
|
+
shaDepois,
|
|
336
|
+
ok: true,
|
|
337
|
+
jaAtualizada: false,
|
|
338
|
+
rebaseFeito: true,
|
|
339
|
+
motivo: null,
|
|
340
|
+
detalhe: `a base ${base} avancou e a branch ${branch} foi rebasada sobre ela`,
|
|
341
|
+
correcao: '',
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Confere a consistencia da worktree NO PROPRIO GIT.
|
|
346
|
+
*
|
|
347
|
+
* Cada check responde uma pergunta que ja causou incidente no Devmaster: a worktree
|
|
348
|
+
* existe mesmo? o git a conhece? a branch em check-out e a da thread? a arvore esta
|
|
349
|
+
* limpa? a base avancou? os artefatos da thread estao fora da arvore?
|
|
350
|
+
*/
|
|
351
|
+
function auditarWorktree(carregado, id) {
|
|
352
|
+
const { raiz, manifesto } = carregado;
|
|
353
|
+
const thread = (0, thread_1.lerThread)(raiz, id);
|
|
354
|
+
const dir = thread.worktree ?? dirDaWorktree(carregado, id);
|
|
355
|
+
const branchEsperada = branchDaWorktree(thread);
|
|
356
|
+
const checks = [];
|
|
357
|
+
checks.push(thread.worktree
|
|
358
|
+
? { nome: 'registro', nivel: 'ok', detalhe: `thread.json aponta para ${thread.worktree}` }
|
|
359
|
+
: {
|
|
360
|
+
nome: 'registro',
|
|
361
|
+
nivel: 'fail',
|
|
362
|
+
detalhe: 'a thread nao tem worktree registrada no thread.json',
|
|
363
|
+
correcao: `ork worktree ensure ${id}`,
|
|
364
|
+
});
|
|
365
|
+
const existe = fs.existsSync(dir);
|
|
366
|
+
checks.push(existe
|
|
367
|
+
? { nome: 'diretorio', nivel: 'ok', detalhe: dir }
|
|
368
|
+
: {
|
|
369
|
+
nome: 'diretorio',
|
|
370
|
+
nivel: 'fail',
|
|
371
|
+
detalhe: `${dir} nao existe no disco`,
|
|
372
|
+
correcao: `ork worktree ensure ${id}`,
|
|
373
|
+
});
|
|
374
|
+
const registro = worktreesDoGit(raiz).find((w) => w.dir === dir);
|
|
375
|
+
checks.push(registro
|
|
376
|
+
? { nome: 'git', nivel: 'ok', detalhe: 'aparece em `git worktree list --porcelain`' }
|
|
377
|
+
: {
|
|
378
|
+
nome: 'git',
|
|
379
|
+
nivel: 'fail',
|
|
380
|
+
detalhe: `${dir} nao aparece em \`git worktree list\``,
|
|
381
|
+
correcao: `git worktree prune && ork worktree ensure ${id}`,
|
|
382
|
+
});
|
|
383
|
+
if (registro) {
|
|
384
|
+
checks.push(registro.branch === branchEsperada
|
|
385
|
+
? { nome: 'branch', nivel: 'ok', detalhe: `${registro.branch} em check-out` }
|
|
386
|
+
: {
|
|
387
|
+
nome: 'branch',
|
|
388
|
+
nivel: 'fail',
|
|
389
|
+
detalhe: `esperado ${branchEsperada}, encontrado "${registro.branch || '(HEAD solto)'}"`,
|
|
390
|
+
correcao: `git -C ${dir} switch ${branchEsperada}`,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
if (existe) {
|
|
394
|
+
const limpa = arvoreLimpa(dir);
|
|
395
|
+
checks.push(limpa === true
|
|
396
|
+
? { nome: 'arvore', nivel: 'ok', detalhe: 'sem alteracao nao commitada' }
|
|
397
|
+
: {
|
|
398
|
+
nome: 'arvore',
|
|
399
|
+
nivel: limpa === null ? 'fail' : 'warn',
|
|
400
|
+
detalhe: limpa === null
|
|
401
|
+
? '`git status` nao respondeu neste diretorio'
|
|
402
|
+
: 'ha alteracao nao commitada na worktree',
|
|
403
|
+
correcao: `git -C ${dir} status`,
|
|
404
|
+
});
|
|
405
|
+
const base = manifesto.worktree.base_branch;
|
|
406
|
+
const refBase = (0, util_1.exec)('git', ['rev-parse', '--verify', base], raiz);
|
|
407
|
+
if (refBase.ok) {
|
|
408
|
+
const shaBase = refBase.stdout.trim();
|
|
409
|
+
const incorporada = (0, util_1.exec)('git', ['merge-base', '--is-ancestor', shaBase, 'HEAD'], dir).ok;
|
|
410
|
+
checks.push(incorporada
|
|
411
|
+
? { nome: 'base', nivel: 'ok', detalhe: `${base} @ ${shaBase.slice(0, 8)} ja incorporada` }
|
|
412
|
+
: {
|
|
413
|
+
nome: 'base',
|
|
414
|
+
nivel: 'fail',
|
|
415
|
+
detalhe: `a base ${base} avancou para ${shaBase.slice(0, 8)} e nao esta na branch da thread`,
|
|
416
|
+
correcao: `ork worktree sync ${id}`,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const dirArtefatos = (0, thread_1.dirThread)(raiz, id);
|
|
421
|
+
const dentroDaArvore = path.resolve(dirArtefatos).startsWith(path.resolve(dir) + path.sep);
|
|
422
|
+
checks.push(dentroDaArvore
|
|
423
|
+
? {
|
|
424
|
+
nome: 'artefatos',
|
|
425
|
+
nivel: 'fail',
|
|
426
|
+
detalhe: `os artefatos da thread estao DENTRO da worktree (${dirArtefatos})`,
|
|
427
|
+
correcao: 'os artefatos da thread vivem em .orkastery/threads/<id>, fora da arvore',
|
|
428
|
+
}
|
|
429
|
+
: {
|
|
430
|
+
nome: 'artefatos',
|
|
431
|
+
nivel: 'ok',
|
|
432
|
+
detalhe: `${path.relative(raiz, dirArtefatos)} fica fora da worktree`,
|
|
433
|
+
});
|
|
434
|
+
const divergencias = checks.filter((c) => c.nivel === 'fail').length;
|
|
435
|
+
(0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.worktreeAuditada, {
|
|
436
|
+
dir,
|
|
437
|
+
branch: branchEsperada,
|
|
438
|
+
divergencias,
|
|
439
|
+
checks: checks.map((c) => ({ nome: c.nome, nivel: c.nivel, detalhe: c.detalhe })),
|
|
440
|
+
fonte: 'git worktree list --porcelain, git status, git merge-base',
|
|
441
|
+
});
|
|
442
|
+
return { ok: divergencias === 0, divergencias, dir, branch: branchEsperada, checks };
|
|
443
|
+
}
|
|
444
|
+
/** Texto de `ork worktree audit`. */
|
|
445
|
+
function textoDoAudit(id, r) {
|
|
446
|
+
const linhas = [
|
|
447
|
+
`Auditoria da worktree da thread ${id}`,
|
|
448
|
+
` dir ${r.dir}`,
|
|
449
|
+
` branch ${r.branch}`,
|
|
450
|
+
'',
|
|
451
|
+
];
|
|
452
|
+
for (const c of r.checks) {
|
|
453
|
+
linhas.push(` ${(0, util_1.simbolo)(c.nivel)} ${c.nome.padEnd(11)} ${c.detalhe}`);
|
|
454
|
+
if (c.nivel !== 'ok' && c.correcao)
|
|
455
|
+
linhas.push(` correcao: ${c.correcao}`);
|
|
456
|
+
}
|
|
457
|
+
linhas.push('');
|
|
458
|
+
linhas.push(r.ok
|
|
459
|
+
? ' worktree consistente: o git confirma cada item acima.'
|
|
460
|
+
: ` ${r.divergencias} divergencia(s) encontrada(s) no proprio git.`);
|
|
461
|
+
return linhas.join('\n');
|
|
462
|
+
}
|
|
463
|
+
/** Solta os leases de escrita que a thread ainda segurava. */
|
|
464
|
+
function soltarLeasesDaThread(raiz, id) {
|
|
465
|
+
const soltos = [];
|
|
466
|
+
const escrita = (0, leases_1.nomeDeLease)('worktree-write', id);
|
|
467
|
+
for (const lease of (0, leases_1.leasesDaThread)(raiz, id)) {
|
|
468
|
+
if (lease.nome !== escrita)
|
|
469
|
+
continue;
|
|
470
|
+
const r = (0, leases_1.liberar)(raiz, lease.nome, id);
|
|
471
|
+
if (r.ok)
|
|
472
|
+
soltos.push(lease.nome);
|
|
473
|
+
}
|
|
474
|
+
return soltos;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Devolve o recurso: remove a worktree da thread e limpa o registro.
|
|
478
|
+
*
|
|
479
|
+
* Recusa arvore suja sem `--forcar`: apagar trabalho nao commitado de outra sessao e
|
|
480
|
+
* irreversivel, e o `ork` nao faz passo irreversivel por conta propria.
|
|
481
|
+
*/
|
|
482
|
+
function liberarWorktree(carregado, id, opcoes = {}) {
|
|
483
|
+
const { raiz } = carregado;
|
|
484
|
+
const thread = (0, thread_1.lerThread)(raiz, id);
|
|
485
|
+
const dir = thread.worktree ?? dirDaWorktree(carregado, id);
|
|
486
|
+
const branch = branchDaWorktree(thread);
|
|
487
|
+
if (!fs.existsSync(dir)) {
|
|
488
|
+
(0, util_1.exec)('git', ['worktree', 'prune'], raiz);
|
|
489
|
+
const soltos = soltarLeasesDaThread(raiz, id);
|
|
490
|
+
if (thread.worktree) {
|
|
491
|
+
thread.worktree = null;
|
|
492
|
+
(0, thread_1.gravarThread)(raiz, thread);
|
|
493
|
+
}
|
|
494
|
+
(0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.worktreeLiberada, {
|
|
495
|
+
dir,
|
|
496
|
+
branch,
|
|
497
|
+
removida: false,
|
|
498
|
+
leasesLiberados: soltos,
|
|
499
|
+
detalhe: 'o diretorio ja nao existia; o registro foi limpo',
|
|
500
|
+
});
|
|
501
|
+
return {
|
|
502
|
+
ok: true,
|
|
503
|
+
removida: false,
|
|
504
|
+
dir,
|
|
505
|
+
branch,
|
|
506
|
+
leasesLiberados: soltos,
|
|
507
|
+
motivo: null,
|
|
508
|
+
detalhe: 'o diretorio ja nao existia; registro limpo e `git worktree prune` rodado',
|
|
509
|
+
correcao: '',
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
const limpa = arvoreLimpa(dir);
|
|
513
|
+
if (limpa === false && !opcoes.forcar) {
|
|
514
|
+
return {
|
|
515
|
+
ok: false,
|
|
516
|
+
removida: false,
|
|
517
|
+
dir,
|
|
518
|
+
branch,
|
|
519
|
+
leasesLiberados: [],
|
|
520
|
+
motivo: 'tree.blocked',
|
|
521
|
+
detalhe: `${dir} tem alteracao nao commitada`,
|
|
522
|
+
correcao: `commite o que interessa em ${dir}, ou repita com --forcar para descartar`,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
const argumentos = ['worktree', 'remove', dir];
|
|
526
|
+
if (opcoes.forcar)
|
|
527
|
+
argumentos.push('--force');
|
|
528
|
+
const r = (0, util_1.exec)('git', argumentos, raiz);
|
|
529
|
+
if (!r.ok) {
|
|
530
|
+
return {
|
|
531
|
+
ok: false,
|
|
532
|
+
removida: false,
|
|
533
|
+
dir,
|
|
534
|
+
branch,
|
|
535
|
+
leasesLiberados: [],
|
|
536
|
+
motivo: 'tree.blocked',
|
|
537
|
+
detalhe: `git worktree remove falhou: ${(r.stderr || r.stdout).trim()}`,
|
|
538
|
+
correcao: `rode \`git worktree remove ${dir}\` na mao e confira \`git worktree list\``,
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
(0, util_1.exec)('git', ['worktree', 'prune'], raiz);
|
|
542
|
+
// Prova no git, nao no codigo de saida: a worktree sumiu mesmo da lista?
|
|
543
|
+
if (registradaNoGit(raiz, dir)) {
|
|
544
|
+
return {
|
|
545
|
+
ok: false,
|
|
546
|
+
removida: false,
|
|
547
|
+
dir,
|
|
548
|
+
branch,
|
|
549
|
+
leasesLiberados: [],
|
|
550
|
+
motivo: 'tree.blocked',
|
|
551
|
+
detalhe: `${dir} continua em \`git worktree list\` depois do remove`,
|
|
552
|
+
correcao: 'git worktree prune && git worktree list',
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
const soltos = soltarLeasesDaThread(raiz, id);
|
|
556
|
+
thread.worktree = null;
|
|
557
|
+
(0, thread_1.gravarThread)(raiz, thread);
|
|
558
|
+
(0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.worktreeLiberada, {
|
|
559
|
+
dir,
|
|
560
|
+
branch,
|
|
561
|
+
removida: true,
|
|
562
|
+
forcado: opcoes.forcar === true,
|
|
563
|
+
leasesLiberados: soltos,
|
|
564
|
+
fonte: 'git worktree list --porcelain',
|
|
565
|
+
});
|
|
566
|
+
return {
|
|
567
|
+
ok: true,
|
|
568
|
+
removida: true,
|
|
569
|
+
dir,
|
|
570
|
+
branch,
|
|
571
|
+
leasesLiberados: soltos,
|
|
572
|
+
motivo: null,
|
|
573
|
+
detalhe: `worktree removida; a branch ${branch} continua no repositorio`,
|
|
574
|
+
correcao: '',
|
|
575
|
+
};
|
|
576
|
+
}
|
package/dist/yaml.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Leitor de YAML do subconjunto que o `orkastery.yaml` usa.
|
|
4
|
+
*
|
|
5
|
+
* Deliberadamente pequeno e sem dependencia externa: mapas aninhados por indentacao
|
|
6
|
+
* de 2 espacos, listas em bloco (`- item`) e listas inline (`[a, b]`), escalares
|
|
7
|
+
* string/numero/booleano/nulo, comentarios com `#`. Qualquer construcao fora disso
|
|
8
|
+
* levanta erro com numero de linha, em vez de adivinhar.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.escalar = escalar;
|
|
12
|
+
exports.lerYaml = lerYaml;
|
|
13
|
+
function tirarComentario(linha) {
|
|
14
|
+
let dentroDeAspas = null;
|
|
15
|
+
for (let i = 0; i < linha.length; i++) {
|
|
16
|
+
const c = linha[i];
|
|
17
|
+
if (dentroDeAspas) {
|
|
18
|
+
if (c === dentroDeAspas)
|
|
19
|
+
dentroDeAspas = null;
|
|
20
|
+
}
|
|
21
|
+
else if (c === '"' || c === "'") {
|
|
22
|
+
dentroDeAspas = c;
|
|
23
|
+
}
|
|
24
|
+
else if (c === '#' && (i === 0 || /\s/.test(linha[i - 1]))) {
|
|
25
|
+
return linha.slice(0, i);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return linha;
|
|
29
|
+
}
|
|
30
|
+
/** Converte um escalar YAML para o tipo JavaScript correspondente. */
|
|
31
|
+
function escalar(bruto) {
|
|
32
|
+
const t = bruto.trim();
|
|
33
|
+
if (t === '' || t === '~' || t === 'null')
|
|
34
|
+
return null;
|
|
35
|
+
if (t === 'true' || t === 'yes')
|
|
36
|
+
return true;
|
|
37
|
+
if (t === 'false' || t === 'no')
|
|
38
|
+
return false;
|
|
39
|
+
if (/^-?\d+$/.test(t))
|
|
40
|
+
return Number(t);
|
|
41
|
+
if (/^-?\d*\.\d+$/.test(t))
|
|
42
|
+
return Number(t);
|
|
43
|
+
if ((t.startsWith('"') && t.endsWith('"')) || (t.startsWith("'") && t.endsWith("'"))) {
|
|
44
|
+
return t.slice(1, -1);
|
|
45
|
+
}
|
|
46
|
+
if (t.startsWith('[') && t.endsWith(']')) {
|
|
47
|
+
const dentro = t.slice(1, -1).trim();
|
|
48
|
+
if (dentro === '')
|
|
49
|
+
return [];
|
|
50
|
+
return dentro.split(',').map((item) => escalar(item));
|
|
51
|
+
}
|
|
52
|
+
return t;
|
|
53
|
+
}
|
|
54
|
+
function preparar(texto) {
|
|
55
|
+
const linhas = [];
|
|
56
|
+
texto.split('\n').forEach((original, i) => {
|
|
57
|
+
const semComentario = tirarComentario(original).replace(/\s+$/, '');
|
|
58
|
+
if (semComentario.trim() === '')
|
|
59
|
+
return;
|
|
60
|
+
if (semComentario.trim() === '---')
|
|
61
|
+
return;
|
|
62
|
+
const indent = semComentario.length - semComentario.trimStart().length;
|
|
63
|
+
linhas.push({ numero: i + 1, indent, conteudo: semComentario.trim() });
|
|
64
|
+
});
|
|
65
|
+
return linhas;
|
|
66
|
+
}
|
|
67
|
+
function parseBloco(linhas, inicio, indent) {
|
|
68
|
+
let i = inicio;
|
|
69
|
+
if (i >= linhas.length)
|
|
70
|
+
return [null, i];
|
|
71
|
+
if (linhas[i].conteudo.startsWith('- ') || linhas[i].conteudo === '-') {
|
|
72
|
+
const lista = [];
|
|
73
|
+
while (i < linhas.length && linhas[i].indent === indent && linhas[i].conteudo.startsWith('-')) {
|
|
74
|
+
const resto = linhas[i].conteudo.slice(1).trim();
|
|
75
|
+
lista.push(escalar(resto));
|
|
76
|
+
i++;
|
|
77
|
+
}
|
|
78
|
+
return [lista, i];
|
|
79
|
+
}
|
|
80
|
+
const mapa = {};
|
|
81
|
+
while (i < linhas.length && linhas[i].indent === indent) {
|
|
82
|
+
const linha = linhas[i];
|
|
83
|
+
const sep = linha.conteudo.indexOf(':');
|
|
84
|
+
if (sep < 0) {
|
|
85
|
+
throw new Error(`YAML invalido na linha ${linha.numero}: esperado "chave: valor"`);
|
|
86
|
+
}
|
|
87
|
+
const chave = linha.conteudo.slice(0, sep).trim().replace(/^["']|["']$/g, '');
|
|
88
|
+
const resto = linha.conteudo.slice(sep + 1).trim();
|
|
89
|
+
i++;
|
|
90
|
+
if (resto !== '') {
|
|
91
|
+
mapa[chave] = escalar(resto);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (i < linhas.length && linhas[i].indent > indent) {
|
|
95
|
+
const [filho, proximo] = parseBloco(linhas, i, linhas[i].indent);
|
|
96
|
+
mapa[chave] = filho;
|
|
97
|
+
i = proximo;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
mapa[chave] = null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return [mapa, i];
|
|
104
|
+
}
|
|
105
|
+
/** Le um documento YAML do subconjunto suportado. */
|
|
106
|
+
function lerYaml(texto) {
|
|
107
|
+
const linhas = preparar(texto);
|
|
108
|
+
if (linhas.length === 0)
|
|
109
|
+
return {};
|
|
110
|
+
const [valor] = parseBloco(linhas, 0, linhas[0].indent);
|
|
111
|
+
return valor;
|
|
112
|
+
}
|