@semacode/cli 1.5.55 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -7
- package/dist/agentContext.d.ts +1 -1
- package/dist/agentContext.js +3 -3
- package/dist/agentContextPack.js +33 -92
- package/dist/agentContextTipos.d.ts +6 -9
- package/dist/agentContextTipos.js +5 -4
- package/dist/agentEntryPoints.d.ts +22 -2
- package/dist/agentEntryPoints.js +229 -126
- package/dist/docs.part01.js +1 -1
- package/dist/docs.part02.js +21 -10
- package/dist/doctorCommand.d.ts +4 -4
- package/dist/doctorCommand.js +21 -10
- package/dist/exemplosOficiais.d.ts +10 -0
- package/dist/exemplosOficiais.js +32 -22
- package/dist/fsGovernado.js +8 -6
- package/dist/iaPrompts.d.ts +2 -2
- package/dist/iaPrompts.js +3 -3
- package/dist/index.part01.d.ts +1 -1
- package/dist/index.part01.js +6 -6
- package/dist/index.part02.js +1 -1
- package/dist/index.part03.d.ts +1 -0
- package/dist/index.part03.js +26 -12
- package/dist/index.part04.d.ts +1 -0
- package/dist/index.part04.js +49 -21
- package/dist/index.part05.js +1 -1
- package/dist/index.part06.d.ts +1 -1
- package/dist/index.part06.js +27 -18
- package/dist/index.part07.js +20 -20
- package/dist/index.part08.js +4 -8
- package/dist/initCommand.d.ts +3 -1
- package/dist/initCommand.js +41 -12
- package/dist/initTemplatesBase.js +8 -10
- package/dist/workspaceWrite.d.ts +22 -0
- package/dist/workspaceWrite.js +332 -0
- package/docs/README.md +10 -4
- package/docs/ai-integration.md +31 -12
- package/docs/ai-onboarding.md +43 -14
- package/docs/ai-workflow.md +22 -39
- package/docs/auth.md +13 -3
- package/docs/cli.md +21 -8
- package/docs/commands.md +9 -10
- package/docs/deploy.md +27 -4
- package/docs/documentation.md +2 -2
- package/docs/env.md +3 -1
- package/docs/getting-started.md +27 -3
- package/docs/repositories.md +18 -2
- package/docs/rollback.md +23 -5
- package/docs/security.md +8 -0
- package/docs/syntax.md +8 -9
- package/docs/testing.md +15 -4
- package/docs/vocabulary.md +2 -1
- package/package.json +20 -12
- package/dist/billing/index.d.ts +0 -17
- package/dist/billing/index.js +0 -74
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
// SEMA-GOVERNED: sema.produto.escrita_segura_workspace
|
|
2
|
+
// Descrição: valida e executa escritas locais sem atravessar symlink, junction ou a base escolhida.
|
|
3
|
+
import { constants } from "node:fs";
|
|
4
|
+
import { lstat, mkdir, open, realpath, rename, stat, unlink, } from "node:fs/promises";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
const IDENTIDADE_DESTINO = Symbol("sema.identidade-destino-workspace");
|
|
8
|
+
const IDENTIDADE_BASE_DESTINO = Symbol("sema.identidade-base-workspace");
|
|
9
|
+
class ErroIntegridadeWorkspace extends Error {
|
|
10
|
+
code = "SEMA_WORKSPACE_TOCTOU";
|
|
11
|
+
constructor(mensagem) {
|
|
12
|
+
super(`Integridade do workspace mudou durante a escrita (TOCTOU): ${mensagem}`);
|
|
13
|
+
this.name = "ErroIntegridadeWorkspace";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function caminhoEstaContido(base, alvo) {
|
|
17
|
+
const relativo = path.relative(base, alvo);
|
|
18
|
+
return relativo === "" || (relativo !== ".." &&
|
|
19
|
+
!relativo.startsWith(`..${path.sep}`) &&
|
|
20
|
+
!path.isAbsolute(relativo));
|
|
21
|
+
}
|
|
22
|
+
function identidadeDe(info) {
|
|
23
|
+
return { dev: info.dev, ino: info.ino };
|
|
24
|
+
}
|
|
25
|
+
function mesmaIdentidade(esquerda, direita) {
|
|
26
|
+
return esquerda.dev === direita.dev && esquerda.ino === direita.ino;
|
|
27
|
+
}
|
|
28
|
+
async function lstatSeguro(caminho) {
|
|
29
|
+
try {
|
|
30
|
+
return await lstat(caminho, { bigint: true });
|
|
31
|
+
}
|
|
32
|
+
catch (erro) {
|
|
33
|
+
if (["ENOENT", "ENOTDIR"].includes(erro.code ?? "")) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
throw erro;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function validarRelativo(caminhoRelativo) {
|
|
40
|
+
if (!caminhoRelativo.trim() || path.isAbsolute(caminhoRelativo)) {
|
|
41
|
+
throw new Error(`Destino de workspace deve ser relativo: ${caminhoRelativo}`);
|
|
42
|
+
}
|
|
43
|
+
if (caminhoRelativo.includes("\0")) {
|
|
44
|
+
throw new Error("Destino de workspace não pode conter byte nulo.");
|
|
45
|
+
}
|
|
46
|
+
if (caminhoRelativo.split(/[\\/]+/u).includes("..")) {
|
|
47
|
+
throw new Error(`Destino de workspace não pode conter traversal: ${caminhoRelativo}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function resolverBaseReal(base) {
|
|
51
|
+
const baseAbsoluta = path.resolve(base);
|
|
52
|
+
const baseInfo = await stat(baseAbsoluta).catch(() => null);
|
|
53
|
+
if (!baseInfo?.isDirectory()) {
|
|
54
|
+
throw new Error(`Base de escrita não existe ou não é diretório: ${baseAbsoluta}`);
|
|
55
|
+
}
|
|
56
|
+
const baseReal = await realpath(baseAbsoluta);
|
|
57
|
+
const baseRealInfo = await lstatSeguro(baseReal);
|
|
58
|
+
if (!baseRealInfo?.isDirectory() || baseRealInfo.isSymbolicLink()) {
|
|
59
|
+
throw new Error(`Base real de escrita não é diretório confiável: ${baseReal}`);
|
|
60
|
+
}
|
|
61
|
+
return baseReal;
|
|
62
|
+
}
|
|
63
|
+
function resolverDestino(baseReal, caminhoRelativo) {
|
|
64
|
+
validarRelativo(caminhoRelativo);
|
|
65
|
+
const caminhoAbsoluto = path.resolve(baseReal, caminhoRelativo);
|
|
66
|
+
if (!caminhoEstaContido(baseReal, caminhoAbsoluto)) {
|
|
67
|
+
throw new Error(`Destino escapa da base de escrita: ${caminhoRelativo}`);
|
|
68
|
+
}
|
|
69
|
+
return caminhoAbsoluto;
|
|
70
|
+
}
|
|
71
|
+
async function revalidarGuardasDiretorio(guardas) {
|
|
72
|
+
for (const guarda of guardas) {
|
|
73
|
+
const [porCaminho, porHandle] = await Promise.all([
|
|
74
|
+
lstatSeguro(guarda.caminho),
|
|
75
|
+
guarda.handle.stat({ bigint: true }),
|
|
76
|
+
]);
|
|
77
|
+
if (!porCaminho?.isDirectory() ||
|
|
78
|
+
porCaminho.isSymbolicLink() ||
|
|
79
|
+
!porHandle.isDirectory() ||
|
|
80
|
+
!mesmaIdentidade(guarda.identidade, identidadeDe(porCaminho)) ||
|
|
81
|
+
!mesmaIdentidade(guarda.identidade, identidadeDe(porHandle))) {
|
|
82
|
+
throw new ErroIntegridadeWorkspace(`diretório substituído: ${guarda.caminho}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async function abrirEGuardarDiretorio(caminho, guardasAnteriores) {
|
|
87
|
+
await revalidarGuardasDiretorio(guardasAnteriores);
|
|
88
|
+
const antes = await lstatSeguro(caminho);
|
|
89
|
+
if (!antes?.isDirectory() || antes.isSymbolicLink()) {
|
|
90
|
+
throw new Error(`Destino atravessa symlink, junction ou não-diretório: ${caminho}`);
|
|
91
|
+
}
|
|
92
|
+
const handle = await open(caminho, constants.O_RDONLY);
|
|
93
|
+
try {
|
|
94
|
+
const [depois, porHandle] = await Promise.all([
|
|
95
|
+
lstatSeguro(caminho),
|
|
96
|
+
handle.stat({ bigint: true }),
|
|
97
|
+
]);
|
|
98
|
+
const identidadeAntes = identidadeDe(antes);
|
|
99
|
+
if (!depois?.isDirectory() ||
|
|
100
|
+
depois.isSymbolicLink() ||
|
|
101
|
+
!porHandle.isDirectory() ||
|
|
102
|
+
!mesmaIdentidade(identidadeAntes, identidadeDe(depois)) ||
|
|
103
|
+
!mesmaIdentidade(identidadeAntes, identidadeDe(porHandle))) {
|
|
104
|
+
throw new ErroIntegridadeWorkspace(`diretório mudou ao ser aberto: ${caminho}`);
|
|
105
|
+
}
|
|
106
|
+
await revalidarGuardasDiretorio(guardasAnteriores);
|
|
107
|
+
return { caminho, handle, identidade: identidadeAntes };
|
|
108
|
+
}
|
|
109
|
+
catch (erro) {
|
|
110
|
+
await handle.close().catch(() => undefined);
|
|
111
|
+
throw erro;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function fecharGuardasDiretorio(guardas) {
|
|
115
|
+
await Promise.all(guardas.map((guarda) => guarda.handle.close().catch(() => undefined)));
|
|
116
|
+
}
|
|
117
|
+
function segmentosEntre(baseReal, alvo) {
|
|
118
|
+
if (!caminhoEstaContido(baseReal, alvo)) {
|
|
119
|
+
throw new Error(`Destino resolve fora da base de escrita: ${alvo}`);
|
|
120
|
+
}
|
|
121
|
+
return path.relative(baseReal, alvo).split(path.sep).filter(Boolean);
|
|
122
|
+
}
|
|
123
|
+
async function garantirDiretoriosDestino(baseReal, diretorioDestino, identidadeBaseEsperada) {
|
|
124
|
+
const guardas = [];
|
|
125
|
+
try {
|
|
126
|
+
guardas.push(await abrirEGuardarDiretorio(baseReal, guardas));
|
|
127
|
+
if (!mesmaIdentidade(guardas[0].identidade, identidadeBaseEsperada)) {
|
|
128
|
+
throw new ErroIntegridadeWorkspace(`base substituída: ${baseReal}`);
|
|
129
|
+
}
|
|
130
|
+
let atual = baseReal;
|
|
131
|
+
for (const segmento of segmentosEntre(baseReal, diretorioDestino)) {
|
|
132
|
+
atual = path.join(atual, segmento);
|
|
133
|
+
await revalidarGuardasDiretorio(guardas);
|
|
134
|
+
let info = await lstatSeguro(atual);
|
|
135
|
+
if (!info) {
|
|
136
|
+
try {
|
|
137
|
+
await mkdir(atual);
|
|
138
|
+
}
|
|
139
|
+
catch (erro) {
|
|
140
|
+
if (erro.code !== "EEXIST") {
|
|
141
|
+
throw erro;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
await revalidarGuardasDiretorio(guardas);
|
|
145
|
+
info = await lstatSeguro(atual);
|
|
146
|
+
}
|
|
147
|
+
if (!info?.isDirectory() || info.isSymbolicLink()) {
|
|
148
|
+
throw new Error(`Destino atravessa symlink, junction ou não-diretório: ${atual}`);
|
|
149
|
+
}
|
|
150
|
+
guardas.push(await abrirEGuardarDiretorio(atual, guardas));
|
|
151
|
+
}
|
|
152
|
+
await revalidarGuardasDiretorio(guardas);
|
|
153
|
+
return guardas;
|
|
154
|
+
}
|
|
155
|
+
catch (erro) {
|
|
156
|
+
await fecharGuardasDiretorio(guardas);
|
|
157
|
+
throw erro;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function confirmarArquivoPorHandle(caminho, handle, guardas) {
|
|
161
|
+
await revalidarGuardasDiretorio(guardas);
|
|
162
|
+
const [porCaminho, porHandle] = await Promise.all([
|
|
163
|
+
lstatSeguro(caminho),
|
|
164
|
+
handle.stat({ bigint: true }),
|
|
165
|
+
]);
|
|
166
|
+
if (!porCaminho?.isFile() ||
|
|
167
|
+
porCaminho.isSymbolicLink() ||
|
|
168
|
+
!porHandle.isFile() ||
|
|
169
|
+
!mesmaIdentidade(identidadeDe(porCaminho), identidadeDe(porHandle)) ||
|
|
170
|
+
porHandle.nlink !== 1n) {
|
|
171
|
+
throw new ErroIntegridadeWorkspace(`arquivo substituído, ocultado ou ligado externamente: ${caminho}`);
|
|
172
|
+
}
|
|
173
|
+
await revalidarGuardasDiretorio(guardas);
|
|
174
|
+
}
|
|
175
|
+
async function confirmarEstadoInicialDestino(destino, guardas) {
|
|
176
|
+
await revalidarGuardasDiretorio(guardas);
|
|
177
|
+
const atual = await lstatSeguro(destino.caminhoAbsoluto);
|
|
178
|
+
const esperada = destino[IDENTIDADE_DESTINO] ?? null;
|
|
179
|
+
if (atual?.isSymbolicLink()) {
|
|
180
|
+
throw new ErroIntegridadeWorkspace(`destino virou symlink ou junction: ${destino.caminhoAbsoluto}`);
|
|
181
|
+
}
|
|
182
|
+
if ((esperada === null && atual !== null) ||
|
|
183
|
+
(esperada !== null && (atual === null || !mesmaIdentidade(esperada, identidadeDe(atual))))) {
|
|
184
|
+
throw new ErroIntegridadeWorkspace(`destino mudou desde a validação: ${destino.caminhoAbsoluto}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async function escreverConteudoConfirmado(caminho, handle, guardas, conteudo) {
|
|
188
|
+
await confirmarArquivoPorHandle(caminho, handle, guardas);
|
|
189
|
+
await handle.writeFile(conteudo, { encoding: "utf8" });
|
|
190
|
+
await handle.sync();
|
|
191
|
+
await confirmarArquivoPorHandle(caminho, handle, guardas);
|
|
192
|
+
}
|
|
193
|
+
async function removerArquivoSeAindaForMesmo(caminho, handle, guardas) {
|
|
194
|
+
try {
|
|
195
|
+
await confirmarArquivoPorHandle(caminho, handle, guardas);
|
|
196
|
+
await unlink(caminho);
|
|
197
|
+
await revalidarGuardasDiretorio(guardas);
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// Falha fechada: não removemos um caminho cuja identidade já não pode ser provada.
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
export async function validarDestinosEscritaWorkspace(base, caminhosRelativos) {
|
|
204
|
+
const baseReal = await resolverBaseReal(base);
|
|
205
|
+
const destinos = [];
|
|
206
|
+
const guardaBase = await abrirEGuardarDiretorio(baseReal, []);
|
|
207
|
+
try {
|
|
208
|
+
for (const caminhoRelativo of [...new Set(caminhosRelativos)]) {
|
|
209
|
+
const caminhoAbsoluto = resolverDestino(baseReal, caminhoRelativo);
|
|
210
|
+
const segmentos = segmentosEntre(baseReal, caminhoAbsoluto);
|
|
211
|
+
const guardas = [guardaBase];
|
|
212
|
+
let atual = baseReal;
|
|
213
|
+
let infoFinal = null;
|
|
214
|
+
try {
|
|
215
|
+
for (let indice = 0; indice < segmentos.length; indice += 1) {
|
|
216
|
+
atual = path.join(atual, segmentos[indice]);
|
|
217
|
+
await revalidarGuardasDiretorio(guardas);
|
|
218
|
+
const info = await lstatSeguro(atual);
|
|
219
|
+
if (!info) {
|
|
220
|
+
infoFinal = null;
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
if (info.isSymbolicLink()) {
|
|
224
|
+
throw new Error(`Destino atravessa symlink ou junction: ${caminhoRelativo}`);
|
|
225
|
+
}
|
|
226
|
+
const ultimo = indice === segmentos.length - 1;
|
|
227
|
+
if (ultimo) {
|
|
228
|
+
infoFinal = info;
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
if (!info.isDirectory()) {
|
|
232
|
+
// O validador também é usado por migrações que sondam caminhos
|
|
233
|
+
// legados alternativos (arquivo único versus diretório). Um
|
|
234
|
+
// componente regular torna o descendente inexistente; a escrita,
|
|
235
|
+
// se tentada, continua bloqueada em garantirDiretoriosDestino.
|
|
236
|
+
infoFinal = null;
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
guardas.push(await abrirEGuardarDiretorio(atual, guardas));
|
|
240
|
+
}
|
|
241
|
+
await revalidarGuardasDiretorio(guardas);
|
|
242
|
+
}
|
|
243
|
+
finally {
|
|
244
|
+
await fecharGuardasDiretorio(guardas.slice(1));
|
|
245
|
+
}
|
|
246
|
+
const destino = {
|
|
247
|
+
caminhoRelativo,
|
|
248
|
+
caminhoAbsoluto,
|
|
249
|
+
existe: infoFinal !== null,
|
|
250
|
+
};
|
|
251
|
+
destino[IDENTIDADE_DESTINO] = infoFinal ? identidadeDe(infoFinal) : null;
|
|
252
|
+
destino[IDENTIDADE_BASE_DESTINO] = guardaBase.identidade;
|
|
253
|
+
destinos.push(destino);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
finally {
|
|
257
|
+
await fecharGuardasDiretorio([guardaBase]);
|
|
258
|
+
}
|
|
259
|
+
return destinos;
|
|
260
|
+
}
|
|
261
|
+
export async function escreverArquivoWorkspaceSeguro(base, caminhoRelativo, conteudo, opcoes = {}) {
|
|
262
|
+
const [destino] = await validarDestinosEscritaWorkspace(base, [caminhoRelativo]);
|
|
263
|
+
if (destino.existe && !opcoes.sobrescrever) {
|
|
264
|
+
return { status: "preservado", caminho: destino.caminhoAbsoluto };
|
|
265
|
+
}
|
|
266
|
+
const baseReal = await resolverBaseReal(base);
|
|
267
|
+
const identidadeBaseEsperada = destino[IDENTIDADE_BASE_DESTINO];
|
|
268
|
+
if (!identidadeBaseEsperada) {
|
|
269
|
+
throw new ErroIntegridadeWorkspace("a validação inicial não registrou a identidade da base");
|
|
270
|
+
}
|
|
271
|
+
if (!caminhoEstaContido(baseReal, destino.caminhoAbsoluto)) {
|
|
272
|
+
throw new ErroIntegridadeWorkspace("a base real mudou desde a validação inicial");
|
|
273
|
+
}
|
|
274
|
+
const guardas = await garantirDiretoriosDestino(baseReal, path.dirname(destino.caminhoAbsoluto), identidadeBaseEsperada);
|
|
275
|
+
try {
|
|
276
|
+
await confirmarEstadoInicialDestino(destino, guardas);
|
|
277
|
+
if (!opcoes.sobrescrever) {
|
|
278
|
+
let handle;
|
|
279
|
+
try {
|
|
280
|
+
handle = await open(destino.caminhoAbsoluto, "wx", 0o600);
|
|
281
|
+
}
|
|
282
|
+
catch (erro) {
|
|
283
|
+
if (erro.code === "EEXIST") {
|
|
284
|
+
return { status: "preservado", caminho: destino.caminhoAbsoluto };
|
|
285
|
+
}
|
|
286
|
+
throw erro;
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
await escreverConteudoConfirmado(destino.caminhoAbsoluto, handle, guardas, conteudo);
|
|
290
|
+
}
|
|
291
|
+
catch (erro) {
|
|
292
|
+
if (!(erro instanceof ErroIntegridadeWorkspace)) {
|
|
293
|
+
await removerArquivoSeAindaForMesmo(destino.caminhoAbsoluto, handle, guardas);
|
|
294
|
+
}
|
|
295
|
+
throw erro;
|
|
296
|
+
}
|
|
297
|
+
finally {
|
|
298
|
+
await handle.close().catch(() => undefined);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
const temporario = path.join(path.dirname(destino.caminhoAbsoluto), `.${path.basename(destino.caminhoAbsoluto)}.sema-${randomUUID()}.tmp`);
|
|
303
|
+
const handle = await open(temporario, "wx", 0o600);
|
|
304
|
+
let renomeado = false;
|
|
305
|
+
try {
|
|
306
|
+
await escreverConteudoConfirmado(temporario, handle, guardas, conteudo);
|
|
307
|
+
await confirmarEstadoInicialDestino(destino, guardas);
|
|
308
|
+
await confirmarArquivoPorHandle(temporario, handle, guardas);
|
|
309
|
+
await rename(temporario, destino.caminhoAbsoluto);
|
|
310
|
+
renomeado = true;
|
|
311
|
+
await confirmarArquivoPorHandle(destino.caminhoAbsoluto, handle, guardas);
|
|
312
|
+
}
|
|
313
|
+
catch (erro) {
|
|
314
|
+
if (!renomeado && !(erro instanceof ErroIntegridadeWorkspace)) {
|
|
315
|
+
await removerArquivoSeAindaForMesmo(temporario, handle, guardas);
|
|
316
|
+
}
|
|
317
|
+
throw erro;
|
|
318
|
+
}
|
|
319
|
+
finally {
|
|
320
|
+
await handle.close().catch(() => undefined);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
finally {
|
|
325
|
+
await fecharGuardasDiretorio(guardas);
|
|
326
|
+
}
|
|
327
|
+
return {
|
|
328
|
+
status: destino.existe ? "atualizado" : "criado",
|
|
329
|
+
caminho: destino.caminhoAbsoluto,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
//# sourceMappingURL=workspaceWrite.js.map
|
package/docs/README.md
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# Sema Documentation
|
|
2
2
|
|
|
3
|
-
These docs describe the public local CLI distribution of Sema.
|
|
3
|
+
These docs describe the public Codex-native local CLI distribution of Sema.
|
|
4
4
|
|
|
5
|
-
Sema is local-first:
|
|
6
|
-
contracts,
|
|
7
|
-
evidence. Public docs are written
|
|
5
|
+
Sema is local-first: Codex runs the CLI inside a project folder, reads `.sema`
|
|
6
|
+
contracts, checks drift, maps impact, and closes changes with documentation
|
|
7
|
+
evidence. `AGENTS.md` is the official Codex entrypoint. Public docs are written
|
|
8
|
+
in English.
|
|
9
|
+
|
|
10
|
+
Sema is an independent product and is not affiliated with or endorsed by
|
|
11
|
+
OpenAI. Codex-native describes Sema's product direction, not an official OpenAI
|
|
12
|
+
program or submission.
|
|
8
13
|
|
|
9
14
|
Start here:
|
|
10
15
|
|
|
@@ -15,5 +20,6 @@ Start here:
|
|
|
15
20
|
- [Documentation Governance](./documentation.md)
|
|
16
21
|
- [Repository Boundary](./repositories.md)
|
|
17
22
|
- [Security](./security.md)
|
|
23
|
+
- [Codex Integration](./ai-integration.md)
|
|
18
24
|
|
|
19
25
|
Official support: [suporte@otimitare.online](mailto:suporte@otimitare.online)
|
package/docs/ai-integration.md
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Codex Integration
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
The official Sema agent integration is Codex through the repository-level
|
|
4
|
+
`AGENTS.md`. Codex loads that file automatically and runs the local Sema CLI
|
|
5
|
+
inside the project to read contracts, check drift, map impact, and close changes
|
|
6
|
+
with evidence.
|
|
7
|
+
|
|
8
|
+
Sema is an independent project and is not affiliated with or endorsed by OpenAI.
|
|
9
|
+
Codex surfaces and `AGENTS.md` behavior are documented at
|
|
10
|
+
https://learn.chatgpt.com/docs/agent-configuration/agents-md.
|
|
11
|
+
|
|
12
|
+
## Before AGENTS.md Exists
|
|
13
|
+
|
|
14
|
+
The official Sema skill is the required first-contact bootstrap for Codex in a
|
|
15
|
+
project that does not have Sema yet. Install it explicitly:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
codex plugin marketplace add gerlanss/Sema
|
|
19
|
+
codex plugin add sema@sema
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The skill locates the local CLI, runs `sema iniciar`, generates `AGENTS.md`, and
|
|
23
|
+
then delegates all ongoing workspace governance to that file. It contains no
|
|
24
|
+
MCP server, remote workspace bridge, login, license, billing, token, credit,
|
|
25
|
+
panel, or authorization gate.
|
|
7
26
|
|
|
8
27
|
Supported capacity labels: fraca, pequena, media, forte, grande.
|
|
9
28
|
|
|
@@ -13,12 +32,12 @@ For a local workspace, use the local CLI:
|
|
|
13
32
|
|
|
14
33
|
```bash
|
|
15
34
|
sema --version
|
|
16
|
-
sema preflight resumo --json
|
|
17
35
|
sema resumo
|
|
18
36
|
```
|
|
19
37
|
|
|
20
|
-
|
|
21
|
-
|
|
38
|
+
A successful `sema --version` is enough to use the local CLI directly. Local
|
|
39
|
+
commands require no login, activation key, license check, token, credits, billing
|
|
40
|
+
service, control panel, or external authorization request.
|
|
22
41
|
|
|
23
42
|
## Before Editing
|
|
24
43
|
|
|
@@ -32,19 +51,19 @@ sema impacto contratos/example.sema --alvo app.example --mudanca "describe the c
|
|
|
32
51
|
Read every document listed by `docs-impacto` before changing code, contracts,
|
|
33
52
|
operational docs, generated artifacts, workflows, profiles, or release material.
|
|
34
53
|
|
|
35
|
-
##
|
|
54
|
+
## Codex Context Tiers
|
|
36
55
|
|
|
37
|
-
|
|
56
|
+
Codex with a small context budget should start with `SEMA_BOOT.md`,
|
|
38
57
|
`SEMA_SMALL_MODEL.md`, `SEMA_BRIEF.micro.txt`,
|
|
39
58
|
`AGENT_CONTEXT_PACK.json`, and `SEMA_INDEX.json`. They should stop early
|
|
40
59
|
when a gate is unclear.
|
|
41
60
|
|
|
42
|
-
|
|
61
|
+
Codex with a medium context budget should start with `SEMA_BOOT.md`,
|
|
43
62
|
`AGENT_CONTEXT_PACK.json`, `SEMA_BRIEF.curto.txt`,
|
|
44
63
|
`SEMA_INDEX.json`, and `AGENTS.md`. They must run docs-impact, drift, and
|
|
45
64
|
impact before edits.
|
|
46
65
|
|
|
47
|
-
|
|
66
|
+
Codex with a large context budget may consume `AGENT_CONTEXT_PACK.json`, `SEMA_BRIEF.md`,
|
|
48
67
|
`SEMA_INDEX.json`, AST, IR, drift, and impact outputs, but larger context does
|
|
49
68
|
not remove the contract-first rule.
|
|
50
69
|
|
package/docs/ai-onboarding.md
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Codex Onboarding
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
The official Sema skill is required for Codex to recognize and initialize Sema
|
|
4
|
+
before a project has its own protocol:
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
codex plugin marketplace add gerlanss/Sema
|
|
8
|
+
codex plugin add sema@sema
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Open a new Codex task in the target repository after installation. Codex loads
|
|
12
|
+
the plugin/skill catalog at task start, so `$sema` is not added retroactively to
|
|
13
|
+
an already-open task. Invoke `$sema` in the new task; it bootstraps the project,
|
|
14
|
+
reads the generated `AGENTS.md`, and later tasks load that protocol
|
|
15
|
+
automatically.
|
|
16
|
+
|
|
17
|
+
`AGENTS.md` is the official Sema entrypoint for Codex. It gives Codex the durable
|
|
18
|
+
repository rule that contracts come first and the local CLI is the source of
|
|
19
|
+
truth. Once the file exists, the skill delegates to it instead of carrying a
|
|
20
|
+
second copy of the governed workflow.
|
|
21
|
+
|
|
22
|
+
Create or refresh the entrypoint with:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
sema sync-codex --json
|
|
26
|
+
```
|
|
5
27
|
|
|
6
28
|
## Minimal Prompt
|
|
7
29
|
|
|
@@ -10,19 +32,19 @@ This workspace is governed by Sema. Before editing code, contracts, docs,
|
|
|
10
32
|
workflows, profiles, or release scripts, run:
|
|
11
33
|
|
|
12
34
|
1. sema --version
|
|
13
|
-
2. sema
|
|
14
|
-
3. sema
|
|
15
|
-
4. sema
|
|
16
|
-
5. sema
|
|
17
|
-
6. sema
|
|
18
|
-
7. sema
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
35
|
+
2. sema resumo
|
|
36
|
+
3. sema docs-impacto --intencao "<change>" --json
|
|
37
|
+
4. sema inspecionar on the applicable .sema contract
|
|
38
|
+
5. sema drift before editing existing code
|
|
39
|
+
6. sema impacto before changing behavior
|
|
40
|
+
7. sema finalizar-mudanca before closing
|
|
41
|
+
|
|
42
|
+
If the CLI is unavailable, stop and install or repair @semacode/cli. There is
|
|
43
|
+
no separate Sema login, license activation, or authorization gate. Do not
|
|
44
|
+
replace Sema with manual search or guessed contracts.
|
|
23
45
|
```
|
|
24
46
|
|
|
25
|
-
## What
|
|
47
|
+
## What Codex Should Not Do
|
|
26
48
|
|
|
27
49
|
- Do not invent `.sema` contracts.
|
|
28
50
|
- Do not implement code without an applicable contract.
|
|
@@ -30,4 +52,11 @@ Do not replace Sema with manual search or guessed contracts.
|
|
|
30
52
|
- Do not publish private or sensitive operational material.
|
|
31
53
|
- Do not replace the local CLI when the local workspace is available.
|
|
32
54
|
|
|
55
|
+
Other AI clients can invoke the public CLI manually, but Sema does not ship or
|
|
56
|
+
manage an official repository entrypoint for them. The supported product path
|
|
57
|
+
is Codex plus `AGENTS.md`.
|
|
58
|
+
|
|
59
|
+
Sema is an independent product and is not affiliated with or endorsed by
|
|
60
|
+
OpenAI.
|
|
61
|
+
|
|
33
62
|
Support: suporte@otimitare.online
|
package/docs/ai-workflow.md
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
<!-- sema:agent-entrypoint:start -->
|
|
2
|
-
# Practical
|
|
3
|
-
|
|
4
|
-
This is the minimum workflow for
|
|
5
|
-
|
|
6
|
-
1.
|
|
7
|
-
2.
|
|
8
|
-
3.
|
|
9
|
-
4.
|
|
10
|
-
5.
|
|
11
|
-
6.
|
|
12
|
-
7.
|
|
13
|
-
8.
|
|
14
|
-
9.
|
|
15
|
-
10.
|
|
16
|
-
11. Antes de concluir, rode `sema finalizar-mudanca` com as docs lidas.
|
|
2
|
+
# Practical Codex + Sema Workflow
|
|
3
|
+
|
|
4
|
+
This is the minimum workflow for Codex in a local workspace.
|
|
5
|
+
|
|
6
|
+
1. Read `SEMA_BOOT.md`.
|
|
7
|
+
2. Run `sema --version`. Success enables direct local execution; there is no login, license, token, billing, panel, or authorization gate.
|
|
8
|
+
3. Run `sema resumo`.
|
|
9
|
+
4. Run `sema docs-impacto --intencao "<change>" --json`.
|
|
10
|
+
5. Read every required document returned by the command.
|
|
11
|
+
6. Read `docs/commands.md` before selecting a command or interpreting `--saida`.
|
|
12
|
+
7. Use `exemplos/` and `docs/syntax.md` before creating or editing a contract.
|
|
13
|
+
8. Run `sema drift` and `sema impacto` before editing existing code.
|
|
14
|
+
9. Run `sema formatar` and `sema validar` after changing a `.sema` contract.
|
|
15
|
+
10. Run `sema finalizar-mudanca` with the documents read before closure.
|
|
17
16
|
|
|
18
17
|
Contract edit rule: `.sema` has its own size budget. Above 300 lines, plan a split by domain/capability; above 500, do not create or edit before splitting. Do not use parte_1/parte_2 and do not force a 1:1 contract-to-file relationship; several contracts can govern the same file through `vinculos`.
|
|
19
18
|
|
|
@@ -21,11 +20,11 @@ Closing rule: `sema drift --json` must return `sucesso:true`. If it reports `suc
|
|
|
21
20
|
|
|
22
21
|
UI rule: if the task involves an interface, minimum evidence includes desktop and mobile. On a narrow viewport such as 390px, `document.documentElement.scrollWidth <= document.documentElement.clientWidth` must pass; horizontal scroll blocks closure.
|
|
23
22
|
|
|
24
|
-
##
|
|
23
|
+
## Codex Context Capacity
|
|
25
24
|
|
|
26
|
-
-
|
|
27
|
-
- Medium
|
|
28
|
-
-
|
|
25
|
+
- Small context: `SEMA_SMALL_MODEL.md`, `SEMA_BRIEF.micro.txt`, `AGENT_CONTEXT_PACK.json`, `SEMA_INDEX.json`.
|
|
26
|
+
- Medium context: `SEMA_BOOT.md`, `AGENT_CONTEXT_PACK.json`, `SEMA_BRIEF.curto.txt`, `SEMA_INDEX.json`, `AGENTS.md`.
|
|
27
|
+
- Large context: `SEMA_BOOT.md`, `AGENT_CONTEXT_PACK.json`, `SEMA_BRIEF.md`, `SEMA_INDEX.json`, AST, IR, drift, and impact.
|
|
29
28
|
|
|
30
29
|
## When to Generate Code
|
|
31
30
|
|
|
@@ -39,24 +38,8 @@ Replace `javascript` with `typescript`, `python`, `php`, `dart`, `lua`, `html`,
|
|
|
39
38
|
|
|
40
39
|
## Fail Closed
|
|
41
40
|
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
- Se arquivos_codigo.conteudo ou conteudo inline passar de 262144 caracteres, não aumente timeout para forçar: divida por responsabilidade ou use anexo/caminho de servidor autorizado.
|
|
47
|
-
- Se for criar ou corrigir .sema, use sema_exemplos antes de escrever sintaxe.
|
|
48
|
-
- Se a resposta humana estiver em PT-BR, use vocabulário Sema canônico e preserve acentos mesmo que a DSL use ASCII.
|
|
49
|
-
- Se um arquivo de código tiver SEMA-GOVERNED, consulte Sema e o contrato aplicável antes de editar.
|
|
50
|
-
- Se codigo governado passar de 1000 linhas, planeje divisao; se passar de 2000, pare e divida antes de concluir. Documentacao Markdown nao entra nesse limite de codigo.
|
|
51
|
-
- Se contrato .sema passar de 300 linhas, planeje split por dominio/capacidade; acima de 500, criacao e edicao ficam bloqueadas. Nao use parte_1/parte_2 e nao remova guarantees, tests ou vinculos so para caber.
|
|
52
|
-
- Um mesmo arquivo de codigo pode ser governado por varios contratos .sema via vinculos; Sema Codigo deve preservar essa rastreabilidade.
|
|
53
|
-
- Se score, achados ou decisaoAgente parecerem bons, trate como sinal de triagem e confira evidência concreta no contrato e no código.
|
|
54
|
-
- Se validar artefato inline com 100/100, ainda preserve cabeçalho SEMA-GOVERNED no arquivo físico sincronizado.
|
|
55
|
-
- Se `sema drift` retornar sucesso:false, `vinculos_quebrados`, `rotas_divergentes` ou impls quebradas, a mudanca nao pode ser declarada concluida; corrija contrato/codigo e rode drift de novo.
|
|
56
|
-
- Se a tarefa tiver site, sistema, app, UI, painel, jogo, CLI/TUI ou terminal, nao conclua sem acabamento moderno, contextual e evidenciado; em UI mobile estreita (ex. 390px), `document.documentElement.scrollWidth <= document.documentElement.clientWidth` precisa ser verdadeiro.
|
|
57
|
-
- Se texto visivel PT-BR perder acento ou cedilha em termos como descricao, lancamentos, saude ou alimentacao, trate como defeito bloqueante quando houver i18n/idioma declarado.
|
|
58
|
-
- Se aparecer caminho que não pertence ao workspace local aberto pelo usuário, pare e confirme a fonte antes de agir.
|
|
59
|
-
- Se uma chamada Sema estourar por timeout local, aumente o timeout e tente de novo; timeout do agente não libera ação sem Sema.
|
|
60
|
-
- Se a plataforma bloquear ou alertar política, trate como bloqueio externo ou falso positivo possível; explique sem tentar contornar filtro.
|
|
61
|
-
- Se a tarefa tiver experiência de uso e você não conseguir garantir padrão moderno, contextual e não genérico, pare e peça revisão em vez de entregar coisa engessada.
|
|
41
|
+
- If the local CLI is unavailable, stop before editing governed code or contracts and ask for `@semacode/cli` installation.
|
|
42
|
+
- If the applicable contract or semantic link is missing, create or repair it before code.
|
|
43
|
+
- If validation or drift reports failure, broken links, divergent routes, or broken implementations, fix the evidence and run the gate again.
|
|
44
|
+
- A local timeout is not authorization to skip Sema; retry with a larger timeout or a narrower scope.
|
|
62
45
|
<!-- sema:agent-entrypoint:end -->
|
package/docs/auth.md
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Local Runtime Boundary
|
|
2
2
|
|
|
3
|
-
The public Sema CLI
|
|
3
|
+
The public Sema CLI executes directly against the local workspace. It does not
|
|
4
|
+
require a Sema login, user authentication, product authorization, license
|
|
5
|
+
activation, install key, token, credits, billing service, control panel, or an
|
|
6
|
+
external request.
|
|
4
7
|
|
|
5
8
|
Use local commands:
|
|
6
9
|
|
|
7
10
|
```bash
|
|
8
11
|
sema --version
|
|
9
|
-
sema
|
|
12
|
+
sema resumo
|
|
10
13
|
sema validar contratos/example.sema --json
|
|
11
14
|
```
|
|
12
15
|
|
|
16
|
+
The repository license governs use and redistribution. It is not checked as a
|
|
17
|
+
runtime activation mechanism.
|
|
18
|
+
|
|
19
|
+
An `authz` block inside a `.sema` contract describes authorization in the
|
|
20
|
+
application being governed. It does not authenticate a person before the Sema
|
|
21
|
+
CLI can run.
|
|
22
|
+
|
|
13
23
|
Do not put private or sensitive operational material in this repository.
|
|
14
24
|
|
|
15
25
|
Application-specific authentication belongs in the application repository that
|