@promovaweb/brandfy 1.1.0 → 1.3.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 +24 -39
- package/package.json +1 -1
- package/src/cli.mjs +69 -136
- package/src/project.mjs +49 -11
package/README.md
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# CLI do Brandfy
|
|
2
2
|
|
|
3
|
-
O pacote `@promovaweb/brandfy` instala
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
continuam compatíveis com `skills-lock.json`.
|
|
3
|
+
O pacote `@promovaweb/brandfy` instala e mantém o sistema de skills no projeto
|
|
4
|
+
consumidor. A conversa sobre a marca acontece exclusivamente na skill
|
|
5
|
+
`$brandfy`; o CLI não expõe atalhos para especialistas, geradores ou auditorias.
|
|
7
6
|
|
|
8
7
|
## Instalação
|
|
9
8
|
|
|
@@ -11,57 +10,43 @@ Instale o executável globalmente:
|
|
|
11
10
|
|
|
12
11
|
```bash
|
|
13
12
|
npm install --global @promovaweb/brandfy
|
|
14
|
-
brandfy
|
|
13
|
+
brandfy install .
|
|
15
14
|
```
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
O comando instala o catálogo completo pelo gerenciador `skills`, prepara
|
|
17
|
+
`.brandfy/`, cria ou confere `BRAND.md`, mantém `brand/README.md` como índice e
|
|
18
|
+
atualiza o bloco gerenciado do `AGENTS.md`.
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
npx @promovaweb/brandfy init .
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
O comando `init` instala todas as skills no projeto, prepara `.brandfy/`,
|
|
24
|
-
cria a estrutura inicial em `brand/` e inclui o bloco gerenciado no
|
|
25
|
-
`AGENTS.md`.
|
|
26
|
-
|
|
27
|
-
## Comandos
|
|
20
|
+
## Comandos públicos
|
|
28
21
|
|
|
29
22
|
| Comando | Resultado |
|
|
30
23
|
| --- | --- |
|
|
31
|
-
| `brandfy
|
|
32
|
-
| `brandfy
|
|
33
|
-
| `brandfy
|
|
34
|
-
| `brandfy skills install [diretório]` | Instala todas as skills pelo `skills add`. |
|
|
35
|
-
| `brandfy skills update [diretório]` | Reinstala somente as skills do Brandfy a partir da origem. |
|
|
36
|
-
| `brandfy pdf [diretório]` | Compila `brand/README.md` em `brand/brand-guide.pdf`. |
|
|
37
|
-
| `brandfy audit [diretório]` | Executa a auditoria e atualiza `.brandfy/audit.md`. |
|
|
38
|
-
| `brandfy update [diretório]` | Atualiza as skills e executa o setup em modo de conferência. |
|
|
39
|
-
|
|
40
|
-
Use `--source <origem>` para testar um checkout local ou outra origem aceita
|
|
41
|
-
pelo gerenciador. O destino padrão é `promovaweb/brandfy`, e o agente padrão é
|
|
42
|
-
o Codex.
|
|
43
|
-
|
|
44
|
-
Os argumentos escritos depois de `--` seguem para o script da skill. Este
|
|
45
|
-
exemplo escolhe outro nome para o PDF:
|
|
24
|
+
| `brandfy install .` | Instala todas as skills e prepara o projeto consumidor. |
|
|
25
|
+
| `brandfy update` | Atualiza as skills, reconcilia a estrutura e confere o setup. |
|
|
26
|
+
| `brandfy doctor` | Diagnostica Node.js, as 19 skills, lock, configuração e arquivos da marca. |
|
|
46
27
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
28
|
+
O diretório usado por `update` e `doctor` é o projeto atual. Para instalar em
|
|
29
|
+
outro local, informe esse caminho somente no comando `install`, como em
|
|
30
|
+
`brandfy install ../meu-projeto`.
|
|
31
|
+
|
|
32
|
+
Depois de `brandfy install .`, abra o agente e use apenas `$brandfy`. A skill
|
|
33
|
+
orquestradora chama `brandfy-setup`, `brandfy-mvp` e as demais especialistas na
|
|
34
|
+
ordem adequada.
|
|
50
35
|
|
|
51
36
|
## Atualizações
|
|
52
37
|
|
|
53
38
|
O framework e o CLI compartilham a mesma SemVer. Atualize o executável pelo npm
|
|
54
|
-
e, dentro do projeto,
|
|
39
|
+
e, dentro do projeto, execute:
|
|
55
40
|
|
|
56
41
|
```bash
|
|
57
42
|
npm install --global @promovaweb/brandfy@latest
|
|
58
|
-
brandfy update
|
|
59
|
-
brandfy doctor
|
|
43
|
+
brandfy update
|
|
44
|
+
brandfy doctor
|
|
60
45
|
```
|
|
61
46
|
|
|
62
|
-
O
|
|
63
|
-
|
|
64
|
-
arquivo
|
|
47
|
+
O `brandfy update` preserva arquivos autorais e reaplica somente a estrutura
|
|
48
|
+
gerenciada. O diagnóstico retorna código diferente de zero quando faltar uma
|
|
49
|
+
skill, configuração, arquivo da marca ou bloco de instruções.
|
|
65
50
|
|
|
66
51
|
## Desenvolvimento
|
|
67
52
|
|
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Implementa a interface
|
|
2
|
+
* Implementa a interface pública mínima do CLI do Brandfy.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* A conversa de marca acontece na skill `$brandfy`. O CLI somente instala,
|
|
5
|
+
* atualiza e diagnostica o sistema no projeto consumidor.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { readFile } from "node:fs/promises";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { createProcessRunner } from "./process-runner.mjs";
|
|
12
|
-
import {
|
|
13
|
-
inspectProject,
|
|
14
|
-
locateSkillScript,
|
|
15
|
-
} from "./project.mjs";
|
|
12
|
+
import { inspectProject, locateSkillScript } from "./project.mjs";
|
|
16
13
|
|
|
17
14
|
const packageRoot = path.resolve(
|
|
18
15
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
@@ -27,50 +24,27 @@ export const DEFAULT_AGENT = "codex";
|
|
|
27
24
|
|
|
28
25
|
const HELP = `Brandfy ${packageManifest.version}
|
|
29
26
|
|
|
30
|
-
Instala
|
|
27
|
+
Instala e mantém o sistema de skills do Brandfy no projeto atual.
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
brandfy
|
|
34
|
-
brandfy
|
|
35
|
-
brandfy skills
|
|
36
|
-
brandfy
|
|
37
|
-
brandfy skills update [diretório] Atualiza somente as skills do Brandfy
|
|
38
|
-
brandfy pdf [diretório] [-- ...] Compila brand/README.md em PDF
|
|
39
|
-
brandfy audit [diretório] [-- ...] Audita a estrutura da marca
|
|
40
|
-
brandfy update [diretório] Atualiza as skills e confere o setup
|
|
41
|
-
brandfy --version Mostra a versão do CLI
|
|
29
|
+
Comandos:
|
|
30
|
+
brandfy install . Instala as skills e prepara o projeto
|
|
31
|
+
brandfy update Atualiza as skills e reconcilia o projeto
|
|
32
|
+
brandfy doctor Diagnostica skills e arquivos
|
|
33
|
+
brandfy --version Mostra a versão do CLI
|
|
42
34
|
|
|
43
35
|
Opções:
|
|
44
|
-
--
|
|
45
|
-
--
|
|
46
|
-
--json Emite o diagnóstico como JSON
|
|
47
|
-
--help Mostra esta ajuda
|
|
36
|
+
--json Emite o diagnóstico em JSON
|
|
37
|
+
--help Mostra esta ajuda
|
|
48
38
|
|
|
49
|
-
|
|
39
|
+
Depois da instalação, converse somente com a skill $brandfy.
|
|
50
40
|
`;
|
|
51
41
|
|
|
52
42
|
function parseArguments(argv) {
|
|
53
43
|
const positional = [];
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
json: false,
|
|
59
|
-
};
|
|
60
|
-
let forwarding = false;
|
|
61
|
-
|
|
62
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
63
|
-
const argument = argv[index];
|
|
64
|
-
if (forwarding) {
|
|
65
|
-
passthrough.push(argument);
|
|
66
|
-
} else if (argument === "--") {
|
|
67
|
-
forwarding = true;
|
|
68
|
-
} else if (argument === "--source" || argument === "--agent") {
|
|
69
|
-
const value = argv[index + 1];
|
|
70
|
-
if (!value) throw new Error(`${argument} exige um valor.`);
|
|
71
|
-
options[argument.slice(2)] = value;
|
|
72
|
-
index += 1;
|
|
73
|
-
} else if (argument === "--json") {
|
|
44
|
+
const options = { json: false };
|
|
45
|
+
|
|
46
|
+
for (const argument of argv) {
|
|
47
|
+
if (argument === "--json") {
|
|
74
48
|
options.json = true;
|
|
75
49
|
} else if (argument.startsWith("-")) {
|
|
76
50
|
throw new Error(`Opção desconhecida: ${argument}`);
|
|
@@ -79,7 +53,7 @@ function parseArguments(argv) {
|
|
|
79
53
|
}
|
|
80
54
|
}
|
|
81
55
|
|
|
82
|
-
return { positional,
|
|
56
|
+
return { positional, options };
|
|
83
57
|
}
|
|
84
58
|
|
|
85
59
|
function projectFrom(value, cwd) {
|
|
@@ -93,13 +67,13 @@ async function ensureSuccess(result, action) {
|
|
|
93
67
|
}
|
|
94
68
|
}
|
|
95
69
|
|
|
96
|
-
async function installSkills(runner, project,
|
|
70
|
+
async function installSkills(runner, project, label) {
|
|
97
71
|
console.log(`${label} as skills do Brandfy em ${project}...`);
|
|
98
72
|
const result = await runner.runSkills([
|
|
99
73
|
"add",
|
|
100
|
-
|
|
74
|
+
DEFAULT_SOURCE,
|
|
101
75
|
"--agent",
|
|
102
|
-
|
|
76
|
+
DEFAULT_AGENT,
|
|
103
77
|
"--skill",
|
|
104
78
|
"*",
|
|
105
79
|
"-y",
|
|
@@ -108,25 +82,31 @@ async function installSkills(runner, project, options, label) {
|
|
|
108
82
|
await ensureSuccess(result, "O gerenciador skills não concluiu a instalação");
|
|
109
83
|
}
|
|
110
84
|
|
|
111
|
-
async function
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
) {
|
|
118
|
-
const script = await locateSkillScript(project, skill, relativeScript);
|
|
85
|
+
async function runSetup(runner, project, check = false) {
|
|
86
|
+
const script = await locateSkillScript(
|
|
87
|
+
project,
|
|
88
|
+
"brandfy-setup",
|
|
89
|
+
"scripts/setup.mjs",
|
|
90
|
+
);
|
|
119
91
|
if (!script) {
|
|
120
92
|
throw new Error(
|
|
121
|
-
|
|
93
|
+
"A skill brandfy-setup não está instalada. Execute brandfy install . primeiro.",
|
|
122
94
|
);
|
|
123
95
|
}
|
|
96
|
+
const args = ["--project", project];
|
|
97
|
+
if (check) args.push("--check");
|
|
124
98
|
const result = await runner.runScript(script, args, project);
|
|
125
|
-
await ensureSuccess(result,
|
|
99
|
+
await ensureSuccess(result, "O brandfy-setup não concluiu a operação");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function assertCommandShape(positional) {
|
|
103
|
+
if (positional.length > 2) {
|
|
104
|
+
throw new Error("Use somente um comando e, opcionalmente, o diretório do projeto.");
|
|
105
|
+
}
|
|
126
106
|
}
|
|
127
107
|
|
|
128
108
|
/**
|
|
129
|
-
* Executa
|
|
109
|
+
* Executa a interface pública com dependências injetáveis para testes.
|
|
130
110
|
*
|
|
131
111
|
* @param {string[]} argv Argumentos sem `node` e sem o nome do binário.
|
|
132
112
|
* @param {{cwd?: string, runner?: object, stdout?: Function}} dependencies
|
|
@@ -142,100 +122,53 @@ export async function execute(argv, dependencies = {}) {
|
|
|
142
122
|
stdout(HELP.trimEnd());
|
|
143
123
|
return 0;
|
|
144
124
|
}
|
|
145
|
-
if (argv[0] === "--version" || argv[0] === "-v"
|
|
125
|
+
if (argv[0] === "--version" || argv[0] === "-v") {
|
|
146
126
|
stdout(packageManifest.version);
|
|
147
127
|
return 0;
|
|
148
128
|
}
|
|
149
129
|
|
|
150
|
-
const { positional,
|
|
151
|
-
const [command,
|
|
152
|
-
|
|
153
|
-
if (command === "skills" && subcommand === "list") {
|
|
154
|
-
const result = await runner.runSkills(
|
|
155
|
-
["add", options.source, "--list"],
|
|
156
|
-
cwd,
|
|
157
|
-
);
|
|
158
|
-
await ensureSuccess(result, "Não foi possível listar as skills");
|
|
159
|
-
return 0;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (
|
|
163
|
-
command === "skills"
|
|
164
|
-
&& (subcommand === "install" || subcommand === "update")
|
|
165
|
-
) {
|
|
166
|
-
const project = projectFrom(possibleProject, cwd);
|
|
167
|
-
await installSkills(
|
|
168
|
-
runner,
|
|
169
|
-
project,
|
|
170
|
-
options,
|
|
171
|
-
subcommand === "install" ? "Instalando" : "Atualizando",
|
|
172
|
-
);
|
|
173
|
-
return 0;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const project = projectFrom(subcommand, cwd);
|
|
177
|
-
if (command === "init") {
|
|
178
|
-
await installSkills(runner, project, options, "Instalando");
|
|
179
|
-
await runInstalledScript(
|
|
180
|
-
runner,
|
|
181
|
-
project,
|
|
182
|
-
"brandfy-setup",
|
|
183
|
-
"scripts/setup.mjs",
|
|
184
|
-
["--project", project],
|
|
185
|
-
);
|
|
186
|
-
stdout(`Brandfy preparado em ${project}.`);
|
|
187
|
-
return 0;
|
|
188
|
-
}
|
|
130
|
+
const { positional, options } = parseArguments(argv);
|
|
131
|
+
const [command, possibleProject] = positional;
|
|
132
|
+
assertCommandShape(positional);
|
|
189
133
|
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
for (const check of checks) {
|
|
197
|
-
stdout(`${check.ok ? "✓" : "✗"} ${check.name}: ${check.detail}`);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return checks.every((check) => check.ok) ? 0 : 1;
|
|
134
|
+
if (![
|
|
135
|
+
"install",
|
|
136
|
+
"update",
|
|
137
|
+
"doctor",
|
|
138
|
+
].includes(command)) {
|
|
139
|
+
throw new Error(`Comando desconhecido: ${command || "(vazio)"}`);
|
|
201
140
|
}
|
|
202
141
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
project,
|
|
207
|
-
"brandfy-guia-pdf",
|
|
208
|
-
"scripts/build-brand-guide.mjs",
|
|
209
|
-
["--project", project, ...passthrough],
|
|
210
|
-
);
|
|
211
|
-
return 0;
|
|
142
|
+
const project = projectFrom(possibleProject, cwd);
|
|
143
|
+
if (options.json && command !== "doctor") {
|
|
144
|
+
throw new Error("A opção --json só pode ser usada com brandfy doctor.");
|
|
212
145
|
}
|
|
213
146
|
|
|
214
|
-
if (command === "
|
|
215
|
-
await
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
"brandfy-auditoria",
|
|
219
|
-
"scripts/audit-brand.mjs",
|
|
220
|
-
["--project", project, ...passthrough],
|
|
221
|
-
);
|
|
147
|
+
if (command === "install") {
|
|
148
|
+
await installSkills(runner, project, "Instalando");
|
|
149
|
+
await runSetup(runner, project);
|
|
150
|
+
stdout(`Brandfy instalado e preparado em ${project}.`);
|
|
222
151
|
return 0;
|
|
223
152
|
}
|
|
224
153
|
|
|
225
154
|
if (command === "update") {
|
|
226
|
-
await installSkills(runner, project,
|
|
227
|
-
await
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
"brandfy-setup",
|
|
231
|
-
"scripts/setup.mjs",
|
|
232
|
-
["--project", project, "--check"],
|
|
233
|
-
);
|
|
234
|
-
stdout(`Skills atualizadas e setup conferido em ${project}.`);
|
|
155
|
+
await installSkills(runner, project, "Atualizando");
|
|
156
|
+
await runSetup(runner, project);
|
|
157
|
+
await runSetup(runner, project, true);
|
|
158
|
+
stdout(`Brandfy atualizado e conferido em ${project}.`);
|
|
235
159
|
return 0;
|
|
236
160
|
}
|
|
237
161
|
|
|
238
|
-
|
|
162
|
+
const checks = await inspectProject(project);
|
|
163
|
+
if (options.json) {
|
|
164
|
+
stdout(JSON.stringify({ project, checks }, null, 2));
|
|
165
|
+
} else {
|
|
166
|
+
stdout(`Diagnóstico do Brandfy em ${project}`);
|
|
167
|
+
for (const check of checks) {
|
|
168
|
+
stdout(`${check.ok ? "✓" : "✗"} ${check.name}: ${check.detail}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return checks.every((check) => check.ok) ? 0 : 1;
|
|
239
172
|
}
|
|
240
173
|
|
|
241
174
|
/**
|
package/src/project.mjs
CHANGED
|
@@ -14,6 +14,28 @@ export const SKILL_DIRECTORIES = [
|
|
|
14
14
|
".claude/skills",
|
|
15
15
|
];
|
|
16
16
|
|
|
17
|
+
export const REQUIRED_SKILLS = [
|
|
18
|
+
"brandfy",
|
|
19
|
+
"brandfy-setup",
|
|
20
|
+
"brandfy-mvp",
|
|
21
|
+
"brandfy-entrevista",
|
|
22
|
+
"brandfy-diagnostico",
|
|
23
|
+
"brandfy-estrategia",
|
|
24
|
+
"brandfy-naming",
|
|
25
|
+
"brandfy-slogan",
|
|
26
|
+
"brandfy-voz",
|
|
27
|
+
"brandfy-identidade-visual",
|
|
28
|
+
"brandfy-tipografia-web",
|
|
29
|
+
"brandfy-logo",
|
|
30
|
+
"brandfy-ativos-logo",
|
|
31
|
+
"brandfy-design-tokens",
|
|
32
|
+
"brandfy-aplicacoes",
|
|
33
|
+
"brandfy-templates-canais",
|
|
34
|
+
"brandfy-manual",
|
|
35
|
+
"brandfy-guia-pdf",
|
|
36
|
+
"brandfy-auditoria",
|
|
37
|
+
];
|
|
38
|
+
|
|
17
39
|
export async function exists(target) {
|
|
18
40
|
try {
|
|
19
41
|
await access(target);
|
|
@@ -51,15 +73,19 @@ function supportsRequiredNode(version) {
|
|
|
51
73
|
* @returns {Promise<Array<{name: string, ok: boolean, detail: string}>>}
|
|
52
74
|
*/
|
|
53
75
|
export async function inspectProject(project) {
|
|
54
|
-
const setup = await locateSkillScript(
|
|
55
|
-
project,
|
|
56
|
-
"brandfy-setup",
|
|
57
|
-
"scripts/setup.mjs",
|
|
58
|
-
);
|
|
59
76
|
const agentsPath = path.join(project, "AGENTS.md");
|
|
60
77
|
const agents = await exists(agentsPath)
|
|
61
78
|
? await readFile(agentsPath, "utf8")
|
|
62
79
|
: "";
|
|
80
|
+
const installedSkills = await Promise.all(
|
|
81
|
+
REQUIRED_SKILLS.map(async (skill) => ({
|
|
82
|
+
skill,
|
|
83
|
+
path: await locateSkillScript(project, skill, "SKILL.md"),
|
|
84
|
+
})),
|
|
85
|
+
);
|
|
86
|
+
const missingSkills = installedSkills
|
|
87
|
+
.filter(({ path: skillPath }) => !skillPath)
|
|
88
|
+
.map(({ skill }) => skill);
|
|
63
89
|
|
|
64
90
|
return [
|
|
65
91
|
{
|
|
@@ -68,11 +94,11 @@ export async function inspectProject(project) {
|
|
|
68
94
|
detail: `versão ${process.versions.node}`,
|
|
69
95
|
},
|
|
70
96
|
{
|
|
71
|
-
name: "
|
|
72
|
-
ok:
|
|
73
|
-
detail:
|
|
74
|
-
?
|
|
75
|
-
: "
|
|
97
|
+
name: "Biblioteca do Brandfy",
|
|
98
|
+
ok: missingSkills.length === 0,
|
|
99
|
+
detail: missingSkills.length === 0
|
|
100
|
+
? `${REQUIRED_SKILLS.length} skills instaladas`
|
|
101
|
+
: `ausentes: ${missingSkills.join(", ")}`,
|
|
76
102
|
},
|
|
77
103
|
{
|
|
78
104
|
name: "Lock das skills",
|
|
@@ -85,7 +111,12 @@ export async function inspectProject(project) {
|
|
|
85
111
|
detail: ".brandfy/config.yaml",
|
|
86
112
|
},
|
|
87
113
|
{
|
|
88
|
-
name: "
|
|
114
|
+
name: "Guia da marca",
|
|
115
|
+
ok: await exists(path.join(project, "BRAND.md")),
|
|
116
|
+
detail: "BRAND.md",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: "Índice da marca",
|
|
89
120
|
ok: await exists(path.join(project, "brand", "README.md")),
|
|
90
121
|
detail: "brand/README.md",
|
|
91
122
|
},
|
|
@@ -95,5 +126,12 @@ export async function inspectProject(project) {
|
|
|
95
126
|
&& agents.includes("<!-- brandfy:consumer:end -->"),
|
|
96
127
|
detail: "bloco gerenciado no AGENTS.md",
|
|
97
128
|
},
|
|
129
|
+
{
|
|
130
|
+
name: "MVP.md",
|
|
131
|
+
ok: true,
|
|
132
|
+
detail: await exists(path.join(project, "MVP.md"))
|
|
133
|
+
? "detectado; será lido pela skill brandfy-mvp"
|
|
134
|
+
: "não presente; entrada opcional",
|
|
135
|
+
},
|
|
98
136
|
];
|
|
99
137
|
}
|