@softize/opus 8.6.6 → 8.6.8
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/bin/lib/init.mjs +45 -3
- package/package.json +14 -13
- package/src/ui/components/primitives/select.tsx +17 -0
package/bin/lib/init.mjs
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { promises as fs } from "node:fs";
|
|
14
14
|
import path from "node:path";
|
|
15
|
+
import { execFileSync } from "node:child_process";
|
|
15
16
|
|
|
16
17
|
const MARKER = "opus.json";
|
|
17
18
|
|
|
@@ -398,10 +399,51 @@ export async function setupUiFoundation(projectDir) {
|
|
|
398
399
|
* Inicializa/atualiza um projeto pra usar a base.
|
|
399
400
|
* @returns {Promise<{version:string, created:string[], synced:string[], warnings:string[], wasInitialized:boolean}>}
|
|
400
401
|
*/
|
|
401
|
-
/**
|
|
402
|
+
/**
|
|
403
|
+
* Onde o `info/exclude` deste repo mora de verdade. `null` = não há onde escrever.
|
|
404
|
+
*
|
|
405
|
+
* Montar `<raiz>/.git/info/exclude` na mão parece óbvio e está errado em **worktree**,
|
|
406
|
+
* que é como toda sessão do Maestro nasce: ali `.git` não é pasta, é um ARQUIVO com um
|
|
407
|
+
* ponteiro (`gitdir: …`). Checar só existência passava, e o `mkdir` seguinte estourava
|
|
408
|
+
* `ENOTDIR` — derrubando o postinstall, e com ele o `pnpm install` inteiro. O sintoma
|
|
409
|
+
* chegava longe da causa: ambiente "subindo" pra sempre, sem erro na tela.
|
|
410
|
+
*
|
|
411
|
+
* Perguntar ao git responde certo nos dois layouts. O caminho montado à mão FICA como
|
|
412
|
+
* rede: `opus setup` roda em postinstall, e num ambiente sem `git` no PATH trocar a
|
|
413
|
+
* resposta por `null` faria a exclusão parar em silêncio — a base voltaria a sujar o
|
|
414
|
+
* `git status` de todo mundo. Só se aplica quando `.git` é pasta; sendo arquivo, sem o
|
|
415
|
+
* git não há como saber pra onde o ponteiro aponta, e chutar é o defeito original.
|
|
416
|
+
*/
|
|
417
|
+
function excludePathOf(repoRoot) {
|
|
418
|
+
try {
|
|
419
|
+
const p = execFileSync("git", ["rev-parse", "--git-path", "info/exclude"], {
|
|
420
|
+
cwd: repoRoot,
|
|
421
|
+
encoding: "utf-8",
|
|
422
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
423
|
+
}).trim();
|
|
424
|
+
// `--git-path` devolve relativo ao cwd quando o repo é o próprio cwd.
|
|
425
|
+
if (p !== "") return path.isAbsolute(p) ? p : path.join(repoRoot, p);
|
|
426
|
+
} catch {
|
|
427
|
+
/* sem git no PATH, ou fora de repo — cai na rede abaixo. */
|
|
428
|
+
}
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** A rede: só vale com `.git` PASTA, que é o layout onde montar o caminho acerta. */
|
|
433
|
+
async function excludePathFallback(repoRoot) {
|
|
434
|
+
const dotGit = path.join(repoRoot, ".git");
|
|
435
|
+
try {
|
|
436
|
+
if (!(await fs.stat(dotGit)).isDirectory()) return null;
|
|
437
|
+
} catch {
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
return path.join(dotGit, "info", "exclude");
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** Anexa padrões ao info/exclude do repo (exclusão local, fora do .gitignore). */
|
|
402
444
|
async function gitExclude(repoRoot, patterns) {
|
|
403
|
-
const excludePath =
|
|
404
|
-
if (
|
|
445
|
+
const excludePath = excludePathOf(repoRoot) ?? (await excludePathFallback(repoRoot));
|
|
446
|
+
if (excludePath === null) return;
|
|
405
447
|
let cur = "";
|
|
406
448
|
try {
|
|
407
449
|
cur = await fs.readFile(excludePath, "utf-8");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softize/opus",
|
|
3
|
-
"version": "8.6.
|
|
3
|
+
"version": "8.6.8",
|
|
4
4
|
"description": "End-to-end action protocol for TypeScript. Single package with subpath exports (core + adapters).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -185,6 +185,16 @@
|
|
|
185
185
|
"bin": {
|
|
186
186
|
"opus": "./bin/cli.mjs"
|
|
187
187
|
},
|
|
188
|
+
"scripts": {
|
|
189
|
+
"postinstall": "node ./bin/lib/postinstall.mjs",
|
|
190
|
+
"typecheck": "tsc --noEmit",
|
|
191
|
+
"test": "vitest run",
|
|
192
|
+
"test:watch": "vitest",
|
|
193
|
+
"test:cov": "vitest run --coverage",
|
|
194
|
+
"registry:up": "npx -y verdaccio --config ~/.config/verdaccio/config.yaml",
|
|
195
|
+
"release": "bash ./scripts/release.sh",
|
|
196
|
+
"release:local": "bash ./scripts/release.sh --local"
|
|
197
|
+
},
|
|
188
198
|
"dependencies": {
|
|
189
199
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
190
200
|
"@radix-ui/react-checkbox": "^1.1.3",
|
|
@@ -319,20 +329,11 @@
|
|
|
319
329
|
"vitest": "^2.1.0",
|
|
320
330
|
"zod": "^3.24.0"
|
|
321
331
|
},
|
|
332
|
+
"packageManager": "pnpm@9.0.0",
|
|
322
333
|
"repository": {
|
|
323
334
|
"type": "git",
|
|
324
335
|
"url": "git+https://github.com/softize-dev/opus.git",
|
|
325
336
|
"directory": "packages/opus"
|
|
326
337
|
},
|
|
327
|
-
"homepage": "https://opus.softize.com.br"
|
|
328
|
-
|
|
329
|
-
"postinstall": "node ./bin/lib/postinstall.mjs",
|
|
330
|
-
"typecheck": "tsc --noEmit",
|
|
331
|
-
"test": "vitest run",
|
|
332
|
-
"test:watch": "vitest",
|
|
333
|
-
"test:cov": "vitest run --coverage",
|
|
334
|
-
"registry:up": "npx -y verdaccio --config ~/.config/verdaccio/config.yaml",
|
|
335
|
-
"release": "bash ./scripts/release.sh",
|
|
336
|
-
"release:local": "bash ./scripts/release.sh --local"
|
|
337
|
-
}
|
|
338
|
-
}
|
|
338
|
+
"homepage": "https://opus.softize.com.br"
|
|
339
|
+
}
|
|
@@ -223,6 +223,23 @@ function CustomSelect(props: Exclude<SelectProps, SelectNativeProps>): React.Rea
|
|
|
223
223
|
setQuery(selectedOption?.label ?? '')
|
|
224
224
|
}, [singleValue, selectedOption, open])
|
|
225
225
|
|
|
226
|
+
/**
|
|
227
|
+
* ABRIR limpa a busca.
|
|
228
|
+
*
|
|
229
|
+
* O rótulo do item escolhido vive no mesmo estado que o termo de busca (é o mesmo
|
|
230
|
+
* input). Sem isto, abrir o seletor herda esse rótulo como CONSULTA: a lista nasce
|
|
231
|
+
* filtrada pelo nome do que já está selecionado — e some justamente quando a pessoa
|
|
232
|
+
* abriu pra trocar. Com `onSearch`, o efeito abaixo ainda dispara uma busca remota por
|
|
233
|
+
* essa frase, que quase nunca casa com outra coisa.
|
|
234
|
+
*
|
|
235
|
+
* Visto em produção duas vezes no mesmo dia: o seletor de task da cabine buscando
|
|
236
|
+
* "Sem task", e o de modelo de IA buscando o nome do modelo corrente. Nos dois, a
|
|
237
|
+
* pessoa via lista vazia com dados no servidor.
|
|
238
|
+
*/
|
|
239
|
+
React.useEffect(() => {
|
|
240
|
+
if (open) setQuery('')
|
|
241
|
+
}, [open])
|
|
242
|
+
|
|
226
243
|
// O cmdk carimba o id DELE no input (a11y interna) e descarta o nosso — reaplicamos por
|
|
227
244
|
// ref, senão o `htmlFor` do Label não acha campo nenhum (clicar no rótulo não focaria).
|
|
228
245
|
React.useEffect(() => {
|