zark-seo 1.0.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/bin/cli.js +186 -0
- package/package.json +35 -0
- package/templates/README.md +537 -0
- package/templates/citations-builder-br-SKILL.md +594 -0
- package/templates/competitor-gap-analyzer-SKILL.md +505 -0
- package/templates/core-web-vitals-fixer-SKILL.md +582 -0
- package/templates/gbp-optimizer-SKILL.md +560 -0
- package/templates/geo-llm-optimizer-SKILL.md +631 -0
- package/templates/meta-tags-optimizer-SKILL.md +563 -0
- package/templates/review-acquisition-engine-SKILL.md +663 -0
- package/templates/schema-jsonld-builder-SKILL.md +663 -0
- package/templates/seo-content-writer-br-SKILL.md +639 -0
- package/templates/seo-local-auditor-SKILL.md +420 -0
- package/templates/seo-orchestrator-SKILL.md +574 -0
- package/templates/service-area-page-builder-SKILL.md +517 -0
- package/templates/topical-authority-architect-SKILL.md +587 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
import { promises as fs } from 'node:fs';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const TEMPLATES_DIR = path.resolve(__dirname, '../templates');
|
|
10
|
+
const PKG_PATH = path.resolve(__dirname, '../package.json');
|
|
11
|
+
|
|
12
|
+
const c = {
|
|
13
|
+
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
14
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
15
|
+
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
16
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
17
|
+
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
18
|
+
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function banner(title) {
|
|
22
|
+
const line = '═'.repeat(Math.max(title.length + 4, 50));
|
|
23
|
+
console.log(c.cyan(`\n╔${line}╗`));
|
|
24
|
+
console.log(c.cyan(`║ ${c.bold(title)}${' '.repeat(line.length - title.length - 2)}║`));
|
|
25
|
+
console.log(c.cyan(`╚${line}╝\n`));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function getVersion() {
|
|
29
|
+
try {
|
|
30
|
+
const pkg = JSON.parse(await fs.readFile(PKG_PATH, 'utf-8'));
|
|
31
|
+
return pkg.version;
|
|
32
|
+
} catch {
|
|
33
|
+
return 'unknown';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function copyDir(src, dest) {
|
|
38
|
+
await fs.mkdir(dest, { recursive: true });
|
|
39
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
40
|
+
for (const entry of entries) {
|
|
41
|
+
const sp = path.join(src, entry.name);
|
|
42
|
+
const dp = path.join(dest, entry.name);
|
|
43
|
+
if (entry.isDirectory()) {
|
|
44
|
+
await copyDir(sp, dp);
|
|
45
|
+
} else if (entry.isFile()) {
|
|
46
|
+
await fs.copyFile(sp, dp);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function appendNoteToContextFiles(targetRoot, skillsDirName) {
|
|
52
|
+
const note = `\n\n## SEO & GEO Skills (ZARK)\nEste projeto tem o **ZARK SEO & GEO Skills Stack** instalado em \`${skillsDirName}/\` — 13 skills especialistas (1 orquestrador + 12 especialistas) em SEO Local, SEO de conteúdo e GEO (otimização para LLMs).\n\n**Como usar:**\n- Em pedidos amplos ("monta plano de SEO completo", "cliente novo chegou", "como começo SEO") → consulte \`${skillsDirName}/seo-orchestrator-SKILL.md\` que roteia pra sequência correta.\n- Em pedidos específicos ("auditoria do site", "criar schema", "otimizar GBP", "página de cidade", "GEO/LLM SEO") → consulte a skill correspondente em \`${skillsDirName}/\`.\n- Lista completa de skills + pipelines documentados em \`${skillsDirName}/README.md\`.\n\nCada skill tem frontmatter \`name\` + \`description\` com triggers naturais — Claude Code roteia automaticamente; em outros clientes (Cursor/Codex/Aider) a IA usa o README como índice.\n\nFoco BR (CNPJ, CEP, +55, diretórios BR, idioma PT-BR). Stack derivado de auditoria real (não teórico).\n`;
|
|
53
|
+
|
|
54
|
+
const candidates = [
|
|
55
|
+
path.join(targetRoot, '.ai-context', 'CONTEXT.md'),
|
|
56
|
+
path.join(targetRoot, 'CLAUDE.md'),
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
for (const file of candidates) {
|
|
60
|
+
if (!existsSync(file)) continue;
|
|
61
|
+
try {
|
|
62
|
+
const stat = await fs.lstat(file);
|
|
63
|
+
const realFile = stat.isSymbolicLink()
|
|
64
|
+
? path.resolve(path.dirname(file), await fs.readlink(file))
|
|
65
|
+
: file;
|
|
66
|
+
const current = await fs.readFile(realFile, 'utf-8');
|
|
67
|
+
if (current.includes('ZARK SEO & GEO Skills')) {
|
|
68
|
+
return realFile + ' (já tem nota — pulando)';
|
|
69
|
+
}
|
|
70
|
+
await fs.writeFile(realFile, current + note, 'utf-8');
|
|
71
|
+
return realFile + ' (nota adicionada)';
|
|
72
|
+
} catch {
|
|
73
|
+
// continue
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function printHelp(version) {
|
|
80
|
+
banner(`zark-seo v${version}`);
|
|
81
|
+
console.log(` ${c.bold('Comandos:')}\n`);
|
|
82
|
+
console.log(` ${c.green('init')} ${c.dim('[pasta]')} instala 13 skills SEO/GEO (default: ./seo-skills)`);
|
|
83
|
+
console.log(` ${c.green('help')} mostra esta ajuda`);
|
|
84
|
+
console.log(` ${c.green('version')} mostra a versão\n`);
|
|
85
|
+
console.log(` ${c.bold('Uso:')}`);
|
|
86
|
+
console.log(` npx zark-seo init ${c.dim('# instala em ./seo-skills')}`);
|
|
87
|
+
console.log(` npx zark-seo init ./skills ${c.dim('# instala em ./skills')}`);
|
|
88
|
+
console.log(` npx zark-seo init --force ${c.dim('# sobrescreve se já existe')}\n`);
|
|
89
|
+
console.log(` ${c.bold('Filosofia:')}`);
|
|
90
|
+
console.log(` 13 skills (1 orquestrador + 12 especialistas) em SEO Local, conteúdo e GEO.`);
|
|
91
|
+
console.log(` Cada skill tem frontmatter com triggers naturais — Claude Code roteia auto;`);
|
|
92
|
+
console.log(` em outros clientes a IA usa o README como índice. Foco mercado brasileiro.\n`);
|
|
93
|
+
console.log(` ${c.bold('Skills incluídas:')}`);
|
|
94
|
+
console.log(` ${c.dim('Orquestrador:')} seo-orchestrator`);
|
|
95
|
+
console.log(` ${c.dim('Diagnóstico:')} seo-local-auditor · competitor-gap-analyzer`);
|
|
96
|
+
console.log(` ${c.dim('Local SEO:')} gbp-optimizer · service-area-page-builder · citations-builder-br`);
|
|
97
|
+
console.log(` ${c.dim('Técnico:')} schema-jsonld-builder · meta-tags-optimizer · core-web-vitals-fixer`);
|
|
98
|
+
console.log(` ${c.dim('Conteúdo:')} topical-authority-architect · seo-content-writer-br`);
|
|
99
|
+
console.log(` ${c.dim('Off-site:')} review-acquisition-engine`);
|
|
100
|
+
console.log(` ${c.dim('LLM/GEO:')} geo-llm-optimizer ★\n`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function runInit(args) {
|
|
104
|
+
const force = args.includes('--force');
|
|
105
|
+
const positional = args.filter((a) => !a.startsWith('--'));
|
|
106
|
+
const target = path.resolve(positional[0] ?? './seo-skills');
|
|
107
|
+
const skillsDirName = path.basename(target);
|
|
108
|
+
const projectRoot = path.dirname(target);
|
|
109
|
+
|
|
110
|
+
banner('🚀 zark-seo — install');
|
|
111
|
+
|
|
112
|
+
if (existsSync(target)) {
|
|
113
|
+
if (!force) {
|
|
114
|
+
console.error(c.red(`✗ Pasta '${target}' já existe.`));
|
|
115
|
+
console.error(c.dim(` Use --force pra sobrescrever, ou escolha outro nome.`));
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
console.log(c.yellow(`⚠ Sobrescrevendo ${target} (--force)`));
|
|
119
|
+
await fs.rm(target, { recursive: true, force: true });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log(c.dim(` origem: ${TEMPLATES_DIR}`));
|
|
123
|
+
console.log(c.dim(` destino: ${target}\n`));
|
|
124
|
+
|
|
125
|
+
await copyDir(TEMPLATES_DIR, target);
|
|
126
|
+
|
|
127
|
+
const fileList = [];
|
|
128
|
+
async function walk(dir, base = '') {
|
|
129
|
+
for (const e of await fs.readdir(dir, { withFileTypes: true })) {
|
|
130
|
+
const sub = path.join(base, e.name);
|
|
131
|
+
if (e.isDirectory()) await walk(path.join(dir, e.name), sub);
|
|
132
|
+
else fileList.push(sub);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
await walk(target);
|
|
136
|
+
|
|
137
|
+
for (const f of fileList) {
|
|
138
|
+
console.log(` ${c.green('✓')} ${f}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log('');
|
|
142
|
+
const noteResult = await appendNoteToContextFiles(projectRoot, skillsDirName);
|
|
143
|
+
if (noteResult) {
|
|
144
|
+
console.log(` ${c.green('✓')} ${noteResult}`);
|
|
145
|
+
} else {
|
|
146
|
+
console.log(c.dim(` (sem .ai-context/CONTEXT.md ou CLAUDE.md no projeto — sem auto-nota)`));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.log('');
|
|
150
|
+
console.log(c.green(`✓ 13 skills instaladas em ${skillsDirName}/`));
|
|
151
|
+
console.log('');
|
|
152
|
+
console.log(c.bold('Próximo passo:'));
|
|
153
|
+
console.log(` ${c.dim('•')} Diga pra IA: "leia ${skillsDirName}/README.md e use as skills SEO/GEO conforme contexto"`);
|
|
154
|
+
console.log(` ${c.dim('•')} Pra pedidos amplos, mencione: "use o seo-orchestrator pra montar o plano"`);
|
|
155
|
+
console.log(` ${c.dim('•')} Pra pedidos específicos, a IA detecta a skill via descriptions automaticamente.`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function main() {
|
|
159
|
+
const argv = process.argv.slice(2);
|
|
160
|
+
const cmd = argv[0] ?? 'help';
|
|
161
|
+
const version = await getVersion();
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
switch (cmd) {
|
|
165
|
+
case 'init':
|
|
166
|
+
await runInit(argv.slice(1));
|
|
167
|
+
return;
|
|
168
|
+
case 'version':
|
|
169
|
+
case '--version':
|
|
170
|
+
case '-v':
|
|
171
|
+
console.log(version);
|
|
172
|
+
return;
|
|
173
|
+
case 'help':
|
|
174
|
+
case '--help':
|
|
175
|
+
case '-h':
|
|
176
|
+
default:
|
|
177
|
+
printHelp(version);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
} catch (e) {
|
|
181
|
+
console.error(c.red(`✗ ${e.message ?? e}`));
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zark-seo",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "ZARK SEO & GEO Skills Stack — install via `npx zark-seo init` to drop 13 SEO/GEO specialist skills (Local SEO, content, GEO/LLM optimization) as REFERENCE for AI in any project. Brazilian market focus, derived from real ZARK audits.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"zark-seo": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"templates/"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=20.0.0"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"seo",
|
|
18
|
+
"local-seo",
|
|
19
|
+
"geo",
|
|
20
|
+
"llm-seo",
|
|
21
|
+
"ai-context",
|
|
22
|
+
"claude-code",
|
|
23
|
+
"skills",
|
|
24
|
+
"zark",
|
|
25
|
+
"brasil",
|
|
26
|
+
"google-business-profile",
|
|
27
|
+
"schema-jsonld"
|
|
28
|
+
],
|
|
29
|
+
"author": "Maycon Jordan",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/mayconjordanr/zark-seo.git"
|
|
34
|
+
}
|
|
35
|
+
}
|