@softize/opus 8.8.1 → 8.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +1 -1
  3. package/bin/cli.mjs +45 -15
  4. package/bin/lib/create.mjs +5 -5
  5. package/bin/lib/init.mjs +20 -228
  6. package/bin/lib/materialize.mjs +273 -0
  7. package/bin/lib/mcp.mjs +3 -3
  8. package/bin/lib/postinstall.mjs +7 -4
  9. package/bin/lib/validate-skill.mjs +40 -0
  10. package/docs/code-style.md +4 -4
  11. package/docs/releasing.md +12 -14
  12. package/package.json +16 -14
  13. package/registry/git/pre-push +8 -0
  14. package/registry/git/pre-push.d/opus +9 -0
  15. package/registry/github/opus.yml +22 -0
  16. package/registry/instructions/opus.md +15 -0
  17. package/registry/skills/build-opus-ui/SKILL.md +39 -0
  18. package/registry/skills/build-opus-ui/agents/openai.yaml +4 -0
  19. package/registry/skills/build-opus-ui/references/evaluations.md +5 -0
  20. package/registry/skills/build-opus-ui/references/ui-patterns.md +8 -0
  21. package/registry/skills/create-opus-action/SKILL.md +43 -0
  22. package/registry/skills/create-opus-action/agents/openai.yaml +4 -0
  23. package/registry/skills/create-opus-action/references/contract-and-binding.md +12 -0
  24. package/registry/skills/create-opus-action/references/evaluations.md +5 -0
  25. package/registry/skills/create-opus-action/scripts/scaffold.mjs +100 -0
  26. package/registry/skills/implement-opus-change/SKILL.md +43 -0
  27. package/registry/skills/implement-opus-change/agents/openai.yaml +4 -0
  28. package/registry/skills/implement-opus-change/references/evaluations.md +5 -0
  29. package/registry/skills/implement-opus-change/references/protocol-boundaries.md +11 -0
  30. package/registry/skills/test-opus-action/SKILL.md +38 -0
  31. package/registry/skills/test-opus-action/agents/openai.yaml +4 -0
  32. package/registry/skills/test-opus-action/references/evaluations.md +5 -0
  33. package/registry/skills/test-opus-action/references/harness.md +10 -0
  34. package/registry/skills/upgrade-opus/SKILL.md +37 -0
  35. package/registry/skills/upgrade-opus/agents/openai.yaml +4 -0
  36. package/registry/skills/upgrade-opus/references/evaluations.md +5 -0
  37. package/registry/skills/upgrade-opus/references/upgrade-checklist.md +8 -0
  38. package/registry/templates/app/src/domains/tasks/index.ts +1 -1
  39. package/src/ui/components/primitives/select.tsx +18 -6
  40. package/src/ui/docs/content/actions.md +1 -1
  41. package/src/ui/docs/content/cli.md +2 -2
  42. package/src/ui/docs/content/customization.md +1 -1
  43. package/src/ui/docs/content/data.md +1 -1
  44. package/src/ui/docs/content/microcopy.md +1 -1
  45. package/src/ui/docs/content/scroll-area.md +1 -1
  46. package/src/ui/docs/content/tokens.md +1 -1
  47. package/src/ui/docs/content/ui.md +1 -1
  48. package/src/ui/docs/doc-client.tsx +2 -2
  49. package/src/ui/theme.css +1 -1
  50. package/registry/hooks/hooks.json +0 -26
  51. package/registry/hooks/link-memory-on-start.mjs +0 -46
  52. package/registry/skills/create-action/SKILL.md +0 -49
  53. package/registry/skills/create-action/scaffold.mjs +0 -122
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: create-opus-action
3
+ description: Cria uma action Opus no split canônico de defineContract e bindAction, incluindo registro e validação. Use ao adicionar endpoint, comando, consulta ou operação de domínio em projeto baseado no Opus.
4
+ ---
5
+
6
+ # Criar action Opus
7
+
8
+ ## Resultado
9
+
10
+ Criar contrato compartilhável e binding server-only que passam no `opus check` e ficam
11
+ registrados no runtime do projeto.
12
+
13
+ ## Entradas
14
+
15
+ - nome `<resource>.<verb>` e `kind` (`simple`, `form`, `list` ou `view`);
16
+ - comportamento, autorização, input, output, erros e efeitos esperados;
17
+ - caminhos reais de `shared` e `api` adotados pelo repo.
18
+
19
+ ## Procedimento
20
+
21
+ 1. Rodar `node scripts/scaffold.mjs <resource> <verb> <kind>` a partir desta skill.
22
+ 2. Colocar o contrato em módulo importável pelos consumidores e o binding no lado servidor.
23
+ 3. Substituir os exemplos do scaffold por schemas, descrições e comportamento do domínio.
24
+ 4. Declarar `requires` somente com `authorize` efetivo; `requires` sozinho não protege a action.
25
+ 5. Exportar e registrar o binding no domínio/runtime existente.
26
+ 6. Acionar `test-opus-action` para cobrir contrato, sucesso, erros e efeitos relevantes.
27
+
28
+ ## Verificação
29
+
30
+ Rodar `opus check <escopo>`, typecheck e testes do domínio. Se o projeto gera manifest,
31
+ OpenAPI ou docs, regenerar e conferir o diff.
32
+
33
+ ## Limites
34
+
35
+ - Não colocar banco, segredo ou driver no arquivo de contrato compartilhado.
36
+ - Não duplicar input/output em tipos manuais, cliente ou documentação paralela.
37
+ - Não criar autorização fictícia apenas para satisfazer o check.
38
+
39
+ ## Recursos
40
+
41
+ - Leia [contrato e binding](references/contract-and-binding.md) quando houver dúvida de fronteira.
42
+ - Use [avaliações](references/evaluations.md) ao evoluir esta skill.
43
+ - Execute [scaffold.mjs](scripts/scaffold.mjs) para gerar os dois arquivos iniciais.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Criar action Opus"
3
+ short_description: "Cria contrato e binding canônicos de uma action"
4
+ default_prompt: "Use $create-opus-action para criar esta action Opus."
@@ -0,0 +1,12 @@
1
+ # Contrato e binding
2
+
3
+ O contrato contém `name`, `kind`, documentação, schemas, metadata de UI, autorização
4
+ declarativa compartilhável e `mockHandler` de design quando aplicável.
5
+
6
+ O binding contém `handler`, `loads`, autorização dependente de dados carregados, execução
7
+ em background, eventos e idempotência. `bindAction(contract, binding)` produz o `ActionDef`
8
+ registrado no runtime.
9
+
10
+ Ordem cobrada pelo check: identidade → documentação → input/output → autorização →
11
+ handler/mock → comportamento. Campos específicos do kind permanecem junto do grupo ao qual
12
+ servem e o scaffold acompanha a forma atual do SDK.
@@ -0,0 +1,5 @@
1
+ # Avaliações
2
+
3
+ - Dispara: “Crie a action `invoice.approve` no Opus.”
4
+ - Não dispara: “Crie uma função TypeScript que soma duas parcelas.”
5
+ - Execução: gerar contrato e binding para cada kind e obter zero findings no `opus check`.
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+
3
+ const KINDS = new Set(['simple', 'form', 'list', 'view'])
4
+ const cap = (value) => value.charAt(0).toUpperCase() + value.slice(1)
5
+ const constant = (resource, verb) => resource + verb.split('-').map(cap).join('')
6
+
7
+ function contractBody({ resource, verb, kind }) {
8
+ const name = `${resource}.${verb}`
9
+ const identifier = constant(resource, verb)
10
+ const common = ` name: '${name}',\n kind: '${kind}',`
11
+
12
+ if (kind === 'list') {
13
+ return `export const ${identifier} = defineContract({
14
+ ${common}
15
+ summary: 'Descreva o que esta consulta lista.',
16
+ description: 'Descreva o comportamento de negócio e seus limites.',
17
+ input: z.object({}),
18
+ output: z.object({ id: z.string() }),
19
+ paginate: 'cursor',
20
+ mockHandler: async () => ({ items: [], cursor: { next: null }, total: 0 }),
21
+ })`
22
+ }
23
+ if (kind === 'form') {
24
+ return `export const ${identifier} = defineContract({
25
+ ${common}
26
+ label: 'Descreva a ação.',
27
+ description: 'Descreva o comportamento de negócio e seus limites.',
28
+ messages: { success: 'Operação concluída.', error: 'Não foi possível concluir.' },
29
+ input: z.object({ id: z.string() }),
30
+ output: z.object({ id: z.string() }),
31
+ fields: { id: { label: 'Identificador' } },
32
+ mockHandler: async (_ctx, input) => ({ id: input.id }),
33
+ })`
34
+ }
35
+ if (kind === 'view') {
36
+ return `export const ${identifier} = defineContract({
37
+ ${common}
38
+ summary: 'Descreva o que esta consulta retorna.',
39
+ description: 'Descreva o comportamento de negócio e seus limites.',
40
+ input: z.object({ id: z.string() }),
41
+ output: z.object({ id: z.string() }),
42
+ mockHandler: async (_ctx, input) => ({ id: input.id }),
43
+ })`
44
+ }
45
+ return `export const ${identifier} = defineContract({
46
+ ${common}
47
+ label: 'Descreva a ação.',
48
+ description: 'Descreva o comportamento de negócio e seus limites.',
49
+ messages: { success: 'Operação concluída.', error: 'Não foi possível concluir.' },
50
+ input: z.object({ id: z.string() }),
51
+ output: z.object({ ok: z.boolean() }),
52
+ mockHandler: async () => ({ ok: true }),
53
+ })`
54
+ }
55
+
56
+ export function actionFiles({ resource, verb, kind = 'simple' }) {
57
+ if (!/^[a-z][a-z0-9-]*$/.test(resource) || !/^[a-z][a-z0-9-]*$/.test(verb)) {
58
+ throw new Error('resource e verb devem usar lowercase kebab-case')
59
+ }
60
+ if (!KINDS.has(kind)) throw new Error(`kind inválido: ${kind}`)
61
+ const identifier = constant(resource, verb)
62
+ return {
63
+ contract: `import { z } from 'zod'
64
+ import { defineContract } from '@softize/opus/core'
65
+
66
+ ${contractBody({ resource, verb, kind })}
67
+ `,
68
+ binding: `import { bindAction } from '@softize/opus/core'
69
+
70
+ import { ${identifier} } from './${verb}.contract.js'
71
+
72
+ export const ${identifier}Impl = bindAction(${identifier}, {
73
+ handler: async (_ctx, _input) => {
74
+ throw new Error('Implemente o comportamento de ${resource}.${verb}.')
75
+ },
76
+ })
77
+ `,
78
+ }
79
+ }
80
+
81
+ export function actionFileNames(verb) {
82
+ return { contract: `${verb}.contract.ts`, binding: `${verb}.ts` }
83
+ }
84
+
85
+ const isMain = process.argv[1] !== undefined && import.meta.url === new URL(`file://${process.argv[1]}`).href
86
+ if (isMain) {
87
+ const [resource, verb, kind = 'simple'] = process.argv.slice(2)
88
+ if (resource === undefined || verb === undefined) {
89
+ console.error('uso: node scaffold.mjs <resource> <verb> [simple|form|list|view]')
90
+ process.exit(1)
91
+ }
92
+ try {
93
+ const generated = actionFiles({ resource, verb, kind })
94
+ const names = actionFileNames(verb)
95
+ process.stdout.write(`// ${names.contract}\n${generated.contract}\n// ${names.binding}\n${generated.binding}`)
96
+ } catch (error) {
97
+ console.error(error.message)
98
+ process.exit(1)
99
+ }
100
+ }
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: implement-opus-change
3
+ description: Implementa uma mudança em projeto baseado no Opus preservando contrato, binding, registro e projeções. Use ao alterar domínio, action, runtime ou integração que utilize @softize/opus.
4
+ ---
5
+
6
+ # Implementar mudança Opus
7
+
8
+ ## Resultado
9
+
10
+ Entregar a mudança coerente com o protocolo do projeto, com contratos consumíveis sem
11
+ dependências server-only, bindings registrados, testes e gates verdes.
12
+
13
+ ## Entradas
14
+
15
+ - critérios de aceite e instruções locais;
16
+ - `opus.json`, `opus.config.ts` e estrutura real do domínio afetado;
17
+ - versão instalada de `@softize/opus` e APIs que o repo já usa.
18
+
19
+ ## Procedimento
20
+
21
+ 1. Localizar contrato, binding, registro e consumidores da capacidade afetada.
22
+ 2. Modelar primeiro o contrato observável: nome, descrição, input, output, erros e metadata.
23
+ 3. Manter código compartilhável fora de banco, segredo, filesystem e drivers server-only.
24
+ 4. Implementar o binding e registrar o `ActionDef` no domínio/runtime conforme a topologia local.
25
+ 5. Usar `create-opus-action`, `test-opus-action` ou `build-opus-ui` quando a mudança entrar
26
+ nesses workflows especializados.
27
+ 6. Atualizar manifest, docs geradas e exemplos somente pelos comandos do repo.
28
+
29
+ ## Verificação
30
+
31
+ Rodar `opus check` no escopo correto, testes afetados, typecheck e os geradores que o
32
+ projeto declara. Conferir o diff gerado antes do handoff.
33
+
34
+ ## Limites
35
+
36
+ - Opus é SDK/protocolo; não atribuir a ele DDD, arquitetura ou regra de negócio universal.
37
+ - Não criar fetch, tipo de transporte ou validação paralela quando o contrato já os fornece.
38
+ - Não supor caminhos fixos quando o repo adotou outra topologia válida.
39
+
40
+ ## Recursos
41
+
42
+ - Leia [fronteiras do protocolo](references/protocol-boundaries.md) ao decidir onde o código mora.
43
+ - Use [avaliações](references/evaluations.md) ao evoluir esta skill.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Implementar mudança Opus"
3
+ short_description: "Implementa mudanças usando contratos e gates do Opus"
4
+ default_prompt: "Use $implement-opus-change para implementar esta mudança no projeto Opus."
@@ -0,0 +1,5 @@
1
+ # Avaliações
2
+
3
+ - Dispara: “Implemente a mudança de cancelamento no domínio de pedidos que usa Opus.”
4
+ - Não dispara: “Atualize uma função utilitária sem relação com Opus.”
5
+ - Execução: alterar uma action existente, provar `opus check`, teste e projeção gerada sem diff inesperado.
@@ -0,0 +1,11 @@
1
+ # Fronteiras do protocolo
2
+
3
+ - `defineContract`: identidade e superfície declarativa compartilhável.
4
+ - `bindAction`: execução server-only, loaders, autorização por linha e efeitos.
5
+ - domínio/runtime: composição e registro; não é fonte duplicada da documentação.
6
+ - `description`: explicação de negócio próxima da declaração e fonte das projeções.
7
+ - manifest/OpenAPI/docs: saídas geradas, nunca fonte editada em paralelo.
8
+
9
+ Use `defineAction` junto apenas quando o código não cruzar fronteira cliente/servidor e o
10
+ projeto já adotar explicitamente esse formato. Para uma action consumida pela UI, prefira
11
+ contrato separado do binding.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: test-opus-action
3
+ description: Testa actions Opus pela fronteira do contrato, cobrindo validação, autorização, handler e efeitos observáveis. Use ao criar ou alterar defineContract, bindAction, defineAction ou comportamento executado pelo runtime Opus.
4
+ ---
5
+
6
+ # Testar action Opus
7
+
8
+ ## Resultado
9
+
10
+ Provar o comportamento público da action e suas regressões sem depender de transporte ou
11
+ infraestrutura real quando a unidade é o contrato.
12
+
13
+ ## Procedimento
14
+
15
+ 1. Importar o `ActionDef` produzido pelo binding; para contrato puro, testar `mockHandler`
16
+ apenas no modo design.
17
+ 2. Executar com `runAction` e fornecer `user`, `can`, `loaded`, storage ou outros adapters
18
+ pelas opções do harness.
19
+ 3. Cobrir sucesso, input inválido, autorização negada e erros de negócio materiais.
20
+ 4. Afirmar output e efeitos observáveis como `emitted`, `logged` e estado do adapter fake.
21
+ 5. Adicionar integração somente para a fronteira não provada pela unidade: banco, transporte,
22
+ registro real ou wiring entre actions.
23
+
24
+ ## Verificação
25
+
26
+ Rodar o teste focado, a suíte do pacote e `opus check` no domínio. Um teste não substitui
27
+ typecheck nem freshness de manifest.
28
+
29
+ ## Limites
30
+
31
+ - Não chamar handler diretamente quando isso pula validação/autorização do runtime.
32
+ - Não mockar o próprio contrato que deveria estar sob teste.
33
+ - Não usar snapshot amplo no lugar de invariantes observáveis.
34
+
35
+ ## Recursos
36
+
37
+ - Leia [harness](references/harness.md) para escolher os fakes.
38
+ - Use [avaliações](references/evaluations.md) ao evoluir esta skill.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Testar action Opus"
3
+ short_description: "Testa contratos, bindings e comportamento de actions"
4
+ default_prompt: "Use $test-opus-action para testar esta action Opus."
@@ -0,0 +1,5 @@
1
+ # Avaliações
2
+
3
+ - Dispara: “Cubra com testes a action Opus de arquivar cliente.”
4
+ - Não dispara: “Teste este parser CSV que não usa action.”
5
+ - Execução: provar input inválido, autorização e efeitos com `runAction`, sem rede real.
@@ -0,0 +1,10 @@
1
+ # Harness Opus
2
+
3
+ - `runAction(action, input, options)`: aplica validação, autenticação, autorização, execução
4
+ e validação de output como o runtime.
5
+ - `testContext(options)`: cria `ActionContext` com `emit` e `log` observáveis.
6
+ - `memStorage()`: adapter de storage em memória com inspeção de chaves.
7
+ - `fake` e `fakeMany`: dados determinísticos derivados de schema.
8
+
9
+ Prefira o binding completo em testes de execução. Um contrato sem handler só executa via
10
+ `mockHandler` quando `OPUS_MODE=design`.
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: upgrade-opus
3
+ description: Atualiza @softize/opus com leitura de changelog, materialização, migrações e gates reproduzíveis. Use ao alterar a versão do Opus ou corrigir drift após um upgrade do pacote.
4
+ ---
5
+
6
+ # Atualizar Opus
7
+
8
+ ## Resultado
9
+
10
+ Resolver a nova versão no lockfile, aplicar suas migrações e deixar código, artefatos
11
+ materializados e projeções geradas consistentes no mesmo diff.
12
+
13
+ ## Procedimento
14
+
15
+ 1. Registrar versão atual e alvo e ler todas as entradas intermediárias do `CHANGELOG.md`,
16
+ priorizando seções Breaking e instruções de migração.
17
+ 2. Atualizar a dependência com o package manager do repo.
18
+ 3. Executar `opus setup` para materializar exatamente a versão resolvida.
19
+ 4. Aplicar migrações no código por domínio, sem compatibilidade temporária silenciosa.
20
+ 5. Rodar `opus check`, typecheck, testes, build e geradores declarados pelo projeto.
21
+ 6. Conferir `base.json`, lockfile, skills, hooks, instruções e outputs gerados no diff.
22
+
23
+ ## Verificação
24
+
25
+ Executar `opus check` novamente em checkout limpo com instalação frozen. Nenhum arquivo
26
+ gerenciado ou manifest pode mudar depois dos gates.
27
+
28
+ ## Limites
29
+
30
+ - Não pular versões intermediárias do changelog.
31
+ - Não editar projeção gerenciada para contornar o setup; corrigir fonte ou colisão.
32
+ - Não publicar nem fazer deploy como consequência implícita do upgrade.
33
+
34
+ ## Recursos
35
+
36
+ - Leia [checklist de upgrade](references/upgrade-checklist.md) antes do handoff.
37
+ - Use [avaliações](references/evaluations.md) ao evoluir esta skill.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Atualizar Opus"
3
+ short_description: "Atualiza o SDK com migração e gates reproduzíveis"
4
+ default_prompt: "Use $upgrade-opus para atualizar o Opus neste projeto."
@@ -0,0 +1,5 @@
1
+ # Avaliações
2
+
3
+ - Dispara: “Atualize o Opus 8.8 para 9.0 e aplique as migrações.”
4
+ - Não dispara: “Atualize apenas o Vitest.”
5
+ - Execução: upgrade entre versões com breaking e checkout final sem drift após `opus setup`.
@@ -0,0 +1,8 @@
1
+ # Checklist de upgrade
2
+
3
+ - versão alvo presente em `package.json`, lockfile, `base.json` e `opus.json` aplicáveis;
4
+ - `opus setup` e `opus check` verdes;
5
+ - breakings intermediários tratados;
6
+ - typecheck, testes e build verdes;
7
+ - manifest, OpenAPI e docs regenerados quando configurados;
8
+ - diff sem edição manual de arquivo gerenciado e sem compatibilidade morta.
@@ -2,7 +2,7 @@ import { defineDomain } from '@softize/opus/core'
2
2
  import { taskListImpl } from './actions/list.ts'
3
3
 
4
4
  /** Domínio-exemplo do esqueleto. Troque pelo seu domínio real — a skill
5
- * `create-action` gera actions novas no formato canônico. O domínio registra
5
+ * `create-opus-action` gera actions novas no formato canônico. O domínio registra
6
6
  * o BOUND (contrato + handler); o contrato puro é o que a web consome. */
7
7
  export const tasksDomain = defineDomain({
8
8
  name: 'task',
@@ -310,10 +310,19 @@ function CustomSelect(props: Exclude<SelectProps, SelectNativeProps>): React.Rea
310
310
  <Popover
311
311
  open={open}
312
312
  onOpenChange={(v) => {
313
- // Fechar devolve o foco pro gatilho, e o gatilho abre AO RECEBER FOCO. Sem armar a
314
- // guarda em TODO fechamento (não só no Esc), o seletor reabria sozinho — o
315
- // "abre, fecha e abre de novo" que aparecia no primeiro clique.
316
- if (!v) closingRef.current = true
313
+ // Fechar devolve o foco pro gatilho, e o gatilho abre AO RECEBER FOCO sem
314
+ // guarda, o seletor reabre sozinho ("abre, fecha e abre de novo").
315
+ //
316
+ // A guarda vale UM FRAME, não até ser consumida. Fechar clicando noutro lugar da
317
+ // página não devolve o foco pra cá, então uma trava que espera esse retorno ficaria
318
+ // armada pra sempre e engoliria a PRÓXIMA abertura por teclado — trocando um
319
+ // defeito visível por um que só aparece pra quem navega por Tab.
320
+ if (!v) {
321
+ closingRef.current = true
322
+ requestAnimationFrame(() => {
323
+ closingRef.current = false
324
+ })
325
+ }
317
326
  setOpen(v)
318
327
  }}
319
328
  >
@@ -370,9 +379,12 @@ function CustomSelect(props: Exclude<SelectProps, SelectNativeProps>): React.Rea
370
379
  o texto rolava pro fim ao ganhar foco (o browser leva o cursor pro final)
371
380
  e o começo do título sumia — num campo estreito, a pessoa via o meio de
372
381
  uma frase. `<span>` trunca com reticências, que é o certo. */}
373
- {!props.multiple && (
382
+ {/* No MÚLTIPLO os chips ocupam o lugar do rótulo — mas quando não há nenhum,
383
+ o gatilho ficaria em branco (o input que carregava o placeholder virou
384
+ invisível). Campo mudo é pior que campo vazio: não diz nem o que é. */}
385
+ {(!props.multiple || selectedValues.length === 0) && (
374
386
  <span data-slot="select-trigger-label" className={cn('min-w-0 flex-1 truncate text-left text-sm', selectedOption === undefined && 'text-muted-foreground')}>
375
- {selectedOption?.triggerLabel ?? (selectedOption?.label ?? placeholder)}
387
+ {props.multiple ? placeholder : (selectedOption?.triggerLabel ?? selectedOption?.label ?? placeholder)}
376
388
  </span>
377
389
  )}
378
390
  <input
@@ -52,7 +52,7 @@ export const workspaceCreate = bindAction(workspaceCreateContract, {
52
52
  ## Ordem canônica dos campos
53
53
 
54
54
  > O `opus check` reprova fora desta ordem — é a régua, 0 violação antes de entregar. A skill
55
- > `create-action` gera o esqueleto certo por construção.
55
+ > `create-opus-action` gera contrato e binding no formato certo por construção.
56
56
 
57
57
  ```text
58
58
  name → kind → (label / summary / messages / tags…)
@@ -50,9 +50,9 @@ só os arquivos do app; o que a raiz precisa ter vira aviso, sem clobber).
50
50
 
51
51
  ## MCP — estado vivo pros agentes
52
52
 
53
- > O server MCP expõe `introspect`/`check`/`create-action`. É como um agente lê a estrutura e cria
53
+ > O server MCP expõe introspecção, check e scaffold de action. É como um agente lê a estrutura e cria
54
54
  > action no formato canônico sem decorar convenção.
55
55
 
56
56
  `opus mcp` sobe o server (configurado em `.mcp.json`): o agente consulta a estrutura, roda o
57
- check e gera o esqueleto de uma action nova pela skill `create-action` — a mesma régua do
57
+ check e gera o esqueleto de uma action nova pela skill `create-opus-action` — a mesma régua do
58
58
  `opus check`, por construção.
@@ -107,4 +107,4 @@ const [open, setOpen] = useState(false)
107
107
 
108
108
  Se uma necessidade real não cabe nas alavancas (tokens · className · slots/asChild · props ·
109
109
  contrato), o movimento não é dialeto local: é evoluir o componente **no Opus**, com a divergência
110
- declarada (skill `opus`) — assim a mudança vale pra casa toda, e esta doc passa a mostrá-la.
110
+ declarada (skill `build-opus-ui`) — assim a mudança vale pra casa toda, e esta doc passa a mostrá-la.
@@ -32,7 +32,7 @@ export const SkillEntity = defineEntity({
32
32
 
33
33
  ## Código camelCase, banco snake
34
34
 
35
- > Regra dura (skill `code-style`): uma camada em snake e outra em camel é defeito — não tem meio-termo.
35
+ > Regra dura do protocolo: uma camada em snake e outra em camel é defeito — não tem meio-termo.
36
36
 
37
37
  O schema tipado do Kysely e **toda** query (select/insert/update/where) usam camelCase
38
38
  (`workspaceId`, `ghRepo`). O banco é snake (a DDL no schema idempotente — `db/schema.sql`).
@@ -6,7 +6,7 @@ title: Microcopy
6
6
 
7
7
  Código em inglês; o que humano lê (UI, comentário, doc) em pt-BR. Frases começam com
8
8
  maiúscula e terminam com ponto; labels curtos em sentence case, sem ponto. **Esta página
9
- é a fonte** — a skill `code-style` aponta pra (regra duplicada em vez de apontada é
9
+ é a fonte** — `build-opus-ui` aplica esta convenção (regra duplicada em vez de apontada é
10
10
  defeito).
11
11
 
12
12
  ## Língua por camada
@@ -33,7 +33,7 @@ render(
33
33
  Pra rolar na horizontal, acrescente <ScrollBar orientation="horizontal" /> como filho e deixe o conteúdo numa linha que não quebra (flex + w-max).
34
34
 
35
35
  ```tsx preview col
36
- const skills = ['clean-code', 'code-style', 'ddd', 'test', 'create-action', 'opus', 'clean-architecture']
36
+ const skills = ['implement-opus-change', 'create-opus-action', 'test-opus-action', 'build-opus-ui', 'upgrade-opus']
37
37
 
38
38
  render(
39
39
  <ScrollArea className="w-full rounded-md border border-border whitespace-nowrap">
@@ -142,7 +142,7 @@ render(
142
142
  ## Divergências declaradas vs shadcn
143
143
 
144
144
  > A regra anti-drift: o que não está declarado aqui (e no `theme.css`) é drift e deve ser
145
- > sincronizado — skill `opus`.
145
+ > sincronizado — skill `build-opus-ui`.
146
146
 
147
147
  1. Formato HSL nos tokens (o upstream migrou pra oklch) — legibilidade e ferramentas nossas; os
148
148
  valores acompanham o upstream, só o formato difere.
@@ -35,6 +35,6 @@ import { Button, Dialog, useAction } from '@softize/opus/ui/react'
35
35
 
36
36
  - **Origem shadcn** — port curado do shadcn (Radix + cmdk). Toda divergência do upstream é
37
37
  DECLARADA no componente ou no tema — o que diverge sem declaração é drift e deve ser
38
- sincronizado (skill `opus`).
38
+ sincronizado (skill `build-opus-ui`).
39
39
  - **Nativo do Opus** — nasceu aqui (os patterns de action). Não tem upstream pra acompanhar; a
40
40
  referência é esta doc.
@@ -124,6 +124,8 @@ export const docSessionDelete = defineContract({
124
124
  kind: 'simple',
125
125
  label: 'Excluir sessão',
126
126
  messages: { success: 'Sessão excluída', error: 'Falha ao excluir' },
127
+ input: z.object({ id: z.string() }),
128
+ output: z.object({ ok: z.boolean() }),
127
129
  // ConfirmSpec DECLARADO no contrato: o ActionTrigger monta o dialog sozinho.
128
130
  confirm: {
129
131
  title: 'Excluir a sessão?',
@@ -131,8 +133,6 @@ export const docSessionDelete = defineContract({
131
133
  confirmLabel: 'Excluir',
132
134
  destructive: true,
133
135
  },
134
- input: z.object({ id: z.string() }),
135
- output: z.object({ ok: z.boolean() }),
136
136
  })
137
137
 
138
138
  const sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms))
package/src/ui/theme.css CHANGED
@@ -5,7 +5,7 @@
5
5
  * + um `@source` pros componentes do Opus (monorepo), e sobrescreve vars pra identidade.
6
6
  *
7
7
  * ── DIVERGÊNCIAS DECLARADAS vs shadcn upstream (regra: o que não está aqui é DRIFT
8
- * e deve ser sincronizado; ver skill `shadcn-ui`) ─────────────────────────────────
8
+ * e deve ser sincronizado; ver skill `build-opus-ui`) ─────────────────────────────
9
9
  * 1. Formato HSL nos tokens (upstream migrou pra oklch) — legibilidade/ferramentas
10
10
  * nossas; os VALORES acompanham o upstream, só o formato difere.
11
11
  * 2. Elevação dark: --card/--popover ACIMA de --background (10% vs 3.9%) — identidade
@@ -1,26 +0,0 @@
1
- {
2
- "hooks": {
3
- "SessionStart": [
4
- {
5
- "hooks": [
6
- {
7
- "type": "command",
8
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/link-memory-on-start.mjs\"",
9
- "timeout": 10
10
- }
11
- ]
12
- }
13
- ],
14
- "Stop": [
15
- {
16
- "hooks": [
17
- {
18
- "type": "command",
19
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/opus-check-on-stop.mjs\"",
20
- "timeout": 120
21
- }
22
- ]
23
- }
24
- ]
25
- }
26
- }
@@ -1,46 +0,0 @@
1
- /**
2
- * Hook SessionStart da base Opus: liga a memória versionada do repo
3
- * (`.claude/memory`, política da skill `memory`) ao diretório que o Claude Code
4
- * lê (`~/.claude/projects/<hash>/memory`). Sem isso, sessão em worktree/máquina
5
- * nova roda AMNÉSICA — recall vazio e memória gravada num diretório órfão que
6
- * morre com o worktree. É o `link-memory.sh` automatizado: idempotente, com
7
- * backup de memória local pré-existente, e fail-open (repo sem `.claude/memory`
8
- * versionado → no-op; qualquer erro → segue sem travar a sessão).
9
- */
10
- import { existsSync, lstatSync, mkdirSync, readdirSync, readlinkSync, renameSync, rmdirSync, symlinkSync, unlinkSync } from 'node:fs'
11
- import { homedir } from 'node:os'
12
- import { dirname, join } from 'node:path'
13
-
14
- try {
15
- const root = process.env.CLAUDE_PROJECT_DIR ?? process.cwd()
16
- const memRepo = join(root, '.claude', 'memory')
17
- // Sem memória versionada → o repo não adota a política; nada a ligar.
18
- if (!existsSync(memRepo)) process.exit(0)
19
-
20
- // O hash do projeto é o caminho absoluto com separadores (e pontos) virando '-',
21
- // como o Claude Code nomeia ~/.claude/projects/. Espelha o link-memory.sh.
22
- const hash = root.replace(/[/.]/g, '-')
23
- const dest = join(homedir(), '.claude', 'projects', hash, 'memory')
24
-
25
- let st = null
26
- try {
27
- st = lstatSync(dest)
28
- } catch {
29
- /* Não existe → segue pro link. */
30
- }
31
- if (st?.isSymbolicLink()) {
32
- if (readlinkSync(dest) === memRepo) process.exit(0) // Já ligado.
33
- unlinkSync(dest) // Aponta pra outro lugar → religa.
34
- } else if (st?.isDirectory()) {
35
- // Memória local pré-existente: com conteúdo → backup (nunca descarta); vazia → sai.
36
- if (readdirSync(dest).length > 0) renameSync(dest, `${dest}.local-${Date.now()}`)
37
- else rmdirSync(dest)
38
- } else if (st !== null) {
39
- unlinkSync(dest) // Arquivo solto no lugar do diretório — afasta.
40
- }
41
- mkdirSync(dirname(dest), { recursive: true })
42
- symlinkSync(memRepo, dest)
43
- } catch {
44
- /* Fail-open: memória desligada é degradação, não motivo pra travar a sessão. */
45
- }
46
- process.exit(0)