drm-core 1.0.0 → 1.3.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/cli/index.js +396 -201
- package/package.json +1 -1
- package/scaffold/.claude/CLAUDE.md +65 -0
- package/scaffold/.claude/settings.json +30 -0
- package/scaffold/.claude/settings.local.json +8 -0
- package/scaffold/.drm-core/agents/afrodite.md +10 -0
- package/scaffold/.drm-core/agents/apolo.md +9 -0
- package/scaffold/.drm-core/agents/atena.md +9 -0
- package/scaffold/.drm-core/agents/cronos.md +9 -0
- package/scaffold/.drm-core/agents/hefesto.md +10 -0
- package/scaffold/.drm-core/agents/hermes.md +9 -0
- package/scaffold/.drm-core/agents/poseidon.md +9 -0
- package/scaffold/.drm-core/agents/temis.md +9 -0
- package/scaffold/.drm-core/agents/zeus.md +10 -0
- package/scaffold/.drm-core/checklists/campanha-pre-launch.md +49 -0
- package/scaffold/.drm-core/checklists/compliance-google.md +46 -0
- package/scaffold/.drm-core/checklists/compliance-meta.md +49 -0
- package/scaffold/.drm-core/checklists/compliance-tiktok.md +46 -0
- package/scaffold/.drm-core/checklists/compliance-vsl.md +48 -0
- package/scaffold/.drm-core/checklists/copy-quality-gate.md +55 -0
- package/scaffold/.drm-core/checklists/funil-pre-launch.md +66 -0
- package/scaffold/.drm-core/checklists/whatsapp-compliance.md +49 -0
- package/scaffold/.drm-core/data/agent-config.yaml +220 -0
- package/scaffold/.drm-core/data/workflow-chains.yaml +374 -0
- package/scaffold/.drm-core/rules/interaction-patterns.md +138 -0
- package/scaffold/.drm-core/tasks/analise/diagnostico-funil.md +314 -0
- package/scaffold/.drm-core/tasks/compliance/revisar-ad-meta.md +242 -0
- package/scaffold/.drm-core/tasks/copy/escrever-ad-meta.md +191 -0
- package/scaffold/.drm-core/tasks/copy/escrever-email-sequence.md +237 -0
- package/scaffold/.drm-core/tasks/copy/escrever-msgs-whatsapp.md +220 -0
- package/scaffold/.drm-core/tasks/copy/escrever-pagina-vendas.md +276 -0
- package/scaffold/.drm-core/tasks/copy/escrever-vsl-full.md +235 -0
- package/scaffold/.drm-core/tasks/direcao-criativa/criar-conceito.md +249 -0
- package/scaffold/.drm-core/tasks/direcao-criativa/validar-criativo.md +202 -0
- package/scaffold/.drm-core/tasks/estrategia/criar-briefing.md +221 -0
- package/scaffold/.drm-core/tasks/estrategia/pesquisa-mercado.md +200 -0
- package/scaffold/.drm-core/tasks/funil/arquitetar-funil-vsl.md +228 -0
- package/scaffold/.drm-core/tasks/funil/fluxo-whatsapp-backend.md +291 -0
- package/scaffold/.drm-core/tasks/trafego/escalar-campanha.md +256 -0
- package/scaffold/.drm-core/tasks/trafego/montar-campanha-meta.md +303 -0
- package/scaffold/.drm-core/templates/ad-meta-template.md +391 -0
- package/scaffold/.drm-core/templates/avatar-template.md +468 -0
- package/scaffold/.drm-core/templates/briefing-estrategico-template.md +462 -0
- package/scaffold/.drm-core/templates/compliance-report-template.md +86 -0
- package/scaffold/.drm-core/templates/estrutura-campanha-template.md +492 -0
- package/scaffold/.drm-core/templates/funil-vsl-template.md +470 -0
- package/scaffold/.drm-core/templates/msgs-whatsapp-template.md +516 -0
- package/scaffold/.drm-core/templates/pagina-vendas-template.md +517 -0
- package/scaffold/.drm-core/templates/relatorio-template.md +313 -0
- package/scaffold/.drm-core/templates/vsl-curta-template.md +341 -0
- package/scaffold/.drm-core/templates/vsl-full-template.md +780 -0
- package/scaffold/.drm-core/templates/vsl-mini-template.md +360 -0
- package/scaffold/.drm-core/workflows/backend-whatsapp.yaml +395 -0
- package/scaffold/.drm-core/workflows/campanha-trafego.yaml +465 -0
- package/scaffold/.drm-core/workflows/copy-review-loop.yaml +333 -0
- package/scaffold/.drm-core/workflows/funil-vsl-completo.yaml +544 -0
- package/scaffold/.drm-core/workflows/lancamento-produto.yaml +402 -0
- package/scaffold/.drm-core/workflows/microlead-pipeline.yaml +408 -0
- package/scaffold/.drm-core/workflows/otimizacao-cycle.yaml +394 -0
- package/scaffold/.drm-core/workflows/reativacao-cycle.yaml +468 -0
package/cli/index.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DRM CLI — Direct Response Marketing Framework
|
|
3
|
-
*
|
|
4
|
-
* Commands:
|
|
5
|
-
* init <name> Create new DRM project
|
|
6
|
-
* install Install DRM in existing project
|
|
7
|
-
* doctor Diagnose DRM installation
|
|
3
|
+
* Orquestracao de agentes de IA para marketing de resposta direta
|
|
8
4
|
*/
|
|
9
5
|
|
|
10
6
|
const { Command } = require('commander');
|
|
@@ -15,6 +11,185 @@ const chalk = require('chalk');
|
|
|
15
11
|
const SCAFFOLD_DIR = path.join(__dirname, '..', 'scaffold');
|
|
16
12
|
const VERSION = require('../package.json').version;
|
|
17
13
|
|
|
14
|
+
// ══════════════════════════════════════════════
|
|
15
|
+
// LOGO & BRANDING
|
|
16
|
+
// ══════════════════════════════════════════════
|
|
17
|
+
|
|
18
|
+
const LOGO = `
|
|
19
|
+
${chalk.bold.cyan(' ╔══════════════════════════════════════════════════════╗')}
|
|
20
|
+
${chalk.bold.cyan(' ║')} ${chalk.bold.cyan('║')}
|
|
21
|
+
${chalk.bold.cyan(' ║')} ${chalk.bold.white('██████╗ ██████╗ ███╗ ███╗')} ${chalk.bold.cyan('║')}
|
|
22
|
+
${chalk.bold.cyan(' ║')} ${chalk.bold.white('██╔══██╗ ██╔══██╗ ████╗ ████║')} ${chalk.bold.cyan('║')}
|
|
23
|
+
${chalk.bold.cyan(' ║')} ${chalk.bold.white('██║ ██║ ██████╔╝ ██╔████╔██║')} ${chalk.bold.cyan('║')}
|
|
24
|
+
${chalk.bold.cyan(' ║')} ${chalk.bold.white('██║ ██║ ██╔══██╗ ██║╚██╔╝██║')} ${chalk.bold.cyan('║')}
|
|
25
|
+
${chalk.bold.cyan(' ║')} ${chalk.bold.white('██████╔╝ ██║ ██║ ██║ ╚═╝ ██║')} ${chalk.bold.cyan('║')}
|
|
26
|
+
${chalk.bold.cyan(' ║')} ${chalk.bold.white('╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝')} ${chalk.bold.cyan('║')}
|
|
27
|
+
${chalk.bold.cyan(' ║')} ${chalk.bold.cyan('║')}
|
|
28
|
+
${chalk.bold.cyan(' ║')} ${chalk.gray('Direct Response Marketing Framework')} ${chalk.bold.cyan('║')}
|
|
29
|
+
${chalk.bold.cyan(' ║')} ${chalk.gray(`v${VERSION} — Orquestracao de Agentes de IA`)} ${chalk.bold.cyan('║')}
|
|
30
|
+
${chalk.bold.cyan(' ║')} ${chalk.bold.cyan('║')}
|
|
31
|
+
${chalk.bold.cyan(' ╚══════════════════════════════════════════════════════╝')}
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
const LOGO_MINI = `
|
|
35
|
+
${chalk.bold.cyan(' ┌─────────────────────────────────────────┐')}
|
|
36
|
+
${chalk.bold.cyan(' │')} ${chalk.bold.white('DRM')} ${chalk.gray('— Direct Response Marketing')} ${chalk.dim(`v${VERSION}`)} ${chalk.bold.cyan('│')}
|
|
37
|
+
${chalk.bold.cyan(' └─────────────────────────────────────────┘')}
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
const DIVIDER = chalk.gray(' ─────────────────────────────────────────');
|
|
41
|
+
|
|
42
|
+
// ══════════════════════════════════════════════
|
|
43
|
+
// AGENTS TABLE
|
|
44
|
+
// ══════════════════════════════════════════════
|
|
45
|
+
|
|
46
|
+
const AGENTS = [
|
|
47
|
+
{ id: 'atena', icon: '⚔️', name: 'Atena', role: 'Estrategista / Master', refs: 'Kennedy, Abraham, Deiss' },
|
|
48
|
+
{ id: 'apolo', icon: '✍️', name: 'Apolo', role: 'Copywriter', refs: 'Georgi, Schwartz, Halbert' },
|
|
49
|
+
{ id: 'hermes', icon: '🚀', name: 'Hermes', role: 'Media Buyer', refs: 'Mandalia, Barbas, Shackelford' },
|
|
50
|
+
{ id: 'zeus', icon: '👑', name: 'Zeus', role: 'Creative Director', refs: 'Ogilvy, Sullivan, Georgi' },
|
|
51
|
+
{ id: 'cronos', icon: '📊', name: 'Cronos', role: 'Analista de Dados', refs: 'Hormozi, Sanocki, Chen' },
|
|
52
|
+
{ id: 'hefesto', icon: '🔨', name: 'Hefesto', role: 'Editor de Video', refs: 'Agora, MindValley, Icaro' },
|
|
53
|
+
{ id: 'afrodite', icon: '🎨', name: 'Afrodite', role: 'Designer', refs: 'Brunson, Wiebe, Laja' },
|
|
54
|
+
{ id: 'temis', icon: '⚖️', name: 'Temis', role: 'Compliance', refs: 'Meta, Google, LGPD' },
|
|
55
|
+
{ id: 'poseidon', icon: '🌊', name: 'Poseidon', role: 'Funnel Architect', refs: 'Brunson, Brown, Ladeira' },
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
// ══════════════════════════════════════════════
|
|
59
|
+
// PROGRESS HELPERS
|
|
60
|
+
// ══════════════════════════════════════════════
|
|
61
|
+
|
|
62
|
+
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
63
|
+
|
|
64
|
+
async function step(label, fn) {
|
|
65
|
+
const isTTY = process.stdout.isTTY;
|
|
66
|
+
if (isTTY) process.stdout.write(chalk.gray(` ◌ ${label}...`));
|
|
67
|
+
try {
|
|
68
|
+
await fn();
|
|
69
|
+
if (isTTY) {
|
|
70
|
+
process.stdout.clearLine(0);
|
|
71
|
+
process.stdout.cursorTo(0);
|
|
72
|
+
}
|
|
73
|
+
console.log(chalk.green(` ✓ ${label}`));
|
|
74
|
+
} catch (err) {
|
|
75
|
+
if (isTTY) {
|
|
76
|
+
process.stdout.clearLine(0);
|
|
77
|
+
process.stdout.cursorTo(0);
|
|
78
|
+
}
|
|
79
|
+
console.log(chalk.red(` ✗ ${label} — ${err.message}`));
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function printStats(targetDir) {
|
|
85
|
+
const counts = {
|
|
86
|
+
agents: 0, workflows: 0, templates: 0, checklists: 0, tasks: 0, rules: 0
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const dirs = {
|
|
90
|
+
agents: '.drm-core/agents',
|
|
91
|
+
workflows: '.drm-core/workflows',
|
|
92
|
+
templates: '.drm-core/templates',
|
|
93
|
+
checklists: '.drm-core/checklists',
|
|
94
|
+
rules: '.drm-core/rules',
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
for (const [key, dir] of Object.entries(dirs)) {
|
|
98
|
+
const full = path.join(targetDir, dir);
|
|
99
|
+
if (fs.existsSync(full)) {
|
|
100
|
+
counts[key] = fs.readdirSync(full).filter(f => f.endsWith('.md') || f.endsWith('.yaml')).length;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Count tasks recursively
|
|
105
|
+
const tasksDir = path.join(targetDir, '.drm-core/tasks');
|
|
106
|
+
if (fs.existsSync(tasksDir)) {
|
|
107
|
+
const countFiles = (dir) => {
|
|
108
|
+
let count = 0;
|
|
109
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
110
|
+
if (entry.isDirectory()) count += countFiles(path.join(dir, entry.name));
|
|
111
|
+
else if (entry.name.endsWith('.md')) count++;
|
|
112
|
+
}
|
|
113
|
+
return count;
|
|
114
|
+
};
|
|
115
|
+
counts.tasks = countFiles(tasksDir);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.log('');
|
|
119
|
+
console.log(chalk.bold(' Framework Stats'));
|
|
120
|
+
console.log(DIVIDER);
|
|
121
|
+
console.log(` ${chalk.cyan('Agentes')} ${chalk.white(counts.agents)} ${chalk.gray('personas com referencias reais de DR')}`);
|
|
122
|
+
console.log(` ${chalk.cyan('Workflows')} ${chalk.white(counts.workflows)} ${chalk.gray('fluxos multi-agente declarativos')}`);
|
|
123
|
+
console.log(` ${chalk.cyan('Templates')} ${chalk.white(String(counts.templates).padStart(2))} ${chalk.gray('estruturas de entregaveis')}`);
|
|
124
|
+
console.log(` ${chalk.cyan('Tasks')} ${chalk.white(String(counts.tasks).padStart(2))} ${chalk.gray('definicoes step-by-step')}`);
|
|
125
|
+
console.log(` ${chalk.cyan('Checklists')} ${chalk.white(counts.checklists)} ${chalk.gray('quality gates e validacoes')}`);
|
|
126
|
+
console.log(` ${chalk.cyan('Rules')} ${chalk.white(counts.rules)} ${chalk.gray('regras de dominio')}`);
|
|
127
|
+
console.log(DIVIDER);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function printAgentsTable() {
|
|
131
|
+
console.log('');
|
|
132
|
+
console.log(chalk.bold(' Panteao de Agentes'));
|
|
133
|
+
console.log(DIVIDER);
|
|
134
|
+
console.log(chalk.gray(` ${'Agente'.padEnd(14)} ${'Papel'.padEnd(24)} Referencias`));
|
|
135
|
+
console.log(DIVIDER);
|
|
136
|
+
AGENTS.forEach(a => {
|
|
137
|
+
console.log(` ${chalk.cyan(`@${a.id}`.padEnd(14))} ${chalk.white(a.role.padEnd(24))} ${chalk.gray(a.refs)}`);
|
|
138
|
+
});
|
|
139
|
+
console.log(DIVIDER);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function printConstitution() {
|
|
143
|
+
console.log('');
|
|
144
|
+
console.log(chalk.bold(' Constitution'));
|
|
145
|
+
console.log(DIVIDER);
|
|
146
|
+
const articles = [
|
|
147
|
+
['I', 'Conversao First', 'NON-NEGOTIABLE', chalk.red],
|
|
148
|
+
['II', 'Agent Authority', 'NON-NEGOTIABLE', chalk.red],
|
|
149
|
+
['III', 'Data-Driven', 'MUST', chalk.yellow],
|
|
150
|
+
['IV', 'No Invention', 'MUST', chalk.yellow],
|
|
151
|
+
['V', 'Compliance First', 'MUST', chalk.yellow],
|
|
152
|
+
['VI', 'Copy is King', 'SHOULD', chalk.blue],
|
|
153
|
+
];
|
|
154
|
+
articles.forEach(([num, name, sev, color]) => {
|
|
155
|
+
console.log(` ${chalk.gray(`Art. ${num}`.padEnd(10))} ${chalk.white(name.padEnd(20))} ${color(sev)}`);
|
|
156
|
+
});
|
|
157
|
+
console.log(DIVIDER);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function printFunnelDiagram() {
|
|
161
|
+
console.log('');
|
|
162
|
+
console.log(chalk.bold(' Modelo de Negocio'));
|
|
163
|
+
console.log(DIVIDER);
|
|
164
|
+
console.log(chalk.cyan(' Frontend (VSL)'));
|
|
165
|
+
console.log(chalk.white(' Ad → Microlead/VSL → LP → Checkout → Bump → Upsell → Downsell'));
|
|
166
|
+
console.log(chalk.gray(' │'));
|
|
167
|
+
console.log(chalk.gray(' ▼'));
|
|
168
|
+
console.log(chalk.cyan(' Backend (WhatsApp)'));
|
|
169
|
+
console.log(chalk.white(' API WhatsApp → Nutricao → Qualificacao → Oferta High-Ticket'));
|
|
170
|
+
console.log(DIVIDER);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function printNextSteps(name) {
|
|
174
|
+
console.log('');
|
|
175
|
+
console.log(chalk.bold(' Proximos Passos'));
|
|
176
|
+
console.log(DIVIDER);
|
|
177
|
+
if (name) {
|
|
178
|
+
console.log(chalk.white(` ${chalk.cyan('$')} cd ${name}`));
|
|
179
|
+
}
|
|
180
|
+
console.log(chalk.white(` ${chalk.cyan('$')} claude${' '.repeat(20)}${chalk.gray('# Abrir Claude Code')}`));
|
|
181
|
+
console.log(chalk.white(` ${chalk.cyan('$')} @atena${' '.repeat(20)}${chalk.gray('# Estrategista Master')}`));
|
|
182
|
+
console.log(chalk.white(` ${chalk.cyan('$')} /DRM:agents:apolo${' '.repeat(9)}${chalk.gray('# Copywriter')}`));
|
|
183
|
+
console.log(chalk.white(` ${chalk.cyan('$')} /DRM:agents:hermes${' '.repeat(8)}${chalk.gray('# Media Buyer')}`));
|
|
184
|
+
console.log('');
|
|
185
|
+
console.log(chalk.gray(' Tambem compativel com Gemini CLI e Antigravity.'));
|
|
186
|
+
console.log('');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ══════════════════════════════════════════════
|
|
190
|
+
// COMMANDS
|
|
191
|
+
// ══════════════════════════════════════════════
|
|
192
|
+
|
|
18
193
|
function createProgram() {
|
|
19
194
|
const program = new Command();
|
|
20
195
|
|
|
@@ -27,50 +202,89 @@ function createProgram() {
|
|
|
27
202
|
program
|
|
28
203
|
.command('init <name>')
|
|
29
204
|
.description('Criar novo projeto DRM')
|
|
30
|
-
.
|
|
205
|
+
.option('--skip-git', 'Nao inicializar git')
|
|
206
|
+
.option('--minimal', 'Instalar apenas agentes e constitution')
|
|
207
|
+
.action(async (name, opts) => {
|
|
31
208
|
const targetDir = path.resolve(process.cwd(), name);
|
|
32
209
|
|
|
33
210
|
if (fs.existsSync(targetDir)) {
|
|
34
|
-
console.error(chalk.red(
|
|
211
|
+
console.error(chalk.red(`\n Erro: Diretorio "${name}" ja existe.\n`));
|
|
35
212
|
process.exit(1);
|
|
36
213
|
}
|
|
37
214
|
|
|
38
|
-
console.log(
|
|
39
|
-
|
|
215
|
+
console.log(LOGO);
|
|
216
|
+
|
|
217
|
+
console.log(chalk.bold(' Instalando Framework'));
|
|
218
|
+
console.log(DIVIDER);
|
|
40
219
|
|
|
41
|
-
// Create directory
|
|
42
220
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
43
221
|
|
|
44
|
-
|
|
45
|
-
|
|
222
|
+
await step('Constitution + Agentes (9 deuses)', async () => {
|
|
223
|
+
fs.copySync(path.join(SCAFFOLD_DIR, '.drm-core'), path.join(targetDir, '.drm-core'));
|
|
224
|
+
await sleep(100);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
await step('Runtime config', async () => {
|
|
228
|
+
fs.copySync(path.join(SCAFFOLD_DIR, '.drm'), path.join(targetDir, '.drm'));
|
|
229
|
+
await sleep(50);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
await step('Claude Code (commands + rules + settings)', async () => {
|
|
233
|
+
const srcClaude = path.join(SCAFFOLD_DIR, '.claude');
|
|
234
|
+
if (fs.existsSync(srcClaude)) {
|
|
235
|
+
fs.copySync(srcClaude, path.join(targetDir, '.claude'));
|
|
236
|
+
}
|
|
237
|
+
createClaudeMd(targetDir, name, true);
|
|
238
|
+
createSettingsJson(targetDir, true);
|
|
239
|
+
await sleep(50);
|
|
240
|
+
});
|
|
46
241
|
|
|
47
|
-
|
|
242
|
+
await step('Gemini CLI + Antigravity', async () => {
|
|
243
|
+
const srcGemini = path.join(SCAFFOLD_DIR, '.gemini');
|
|
244
|
+
const srcAnti = path.join(SCAFFOLD_DIR, '.antigravity');
|
|
245
|
+
if (fs.existsSync(srcGemini)) fs.copySync(srcGemini, path.join(targetDir, '.gemini'));
|
|
246
|
+
if (fs.existsSync(srcAnti)) fs.copySync(srcAnti, path.join(targetDir, '.antigravity'));
|
|
247
|
+
await sleep(50);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
await step('Estrutura de entregaveis (docs/)', async () => {
|
|
251
|
+
fs.copySync(path.join(SCAFFOLD_DIR, 'docs'), path.join(targetDir, 'docs'));
|
|
252
|
+
await sleep(50);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// package.json
|
|
48
256
|
const projectPkg = {
|
|
49
257
|
name: name,
|
|
50
258
|
version: '1.0.0',
|
|
51
|
-
description: '',
|
|
259
|
+
description: 'Projeto DRM — Direct Response Marketing',
|
|
52
260
|
private: true,
|
|
53
|
-
scripts: {},
|
|
54
|
-
keywords: [],
|
|
261
|
+
scripts: { doctor: 'npx drm-core doctor' },
|
|
262
|
+
keywords: ['drm', 'direct-response-marketing'],
|
|
55
263
|
license: 'UNLICENSED',
|
|
56
264
|
type: 'commonjs'
|
|
57
265
|
};
|
|
58
266
|
fs.writeJsonSync(path.join(targetDir, 'package.json'), projectPkg, { spaces: 2 });
|
|
59
267
|
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
268
|
+
// Git
|
|
269
|
+
if (!opts.skipGit) {
|
|
270
|
+
await step('Git init', async () => {
|
|
271
|
+
const { execSync } = require('child_process');
|
|
272
|
+
execSync('git init', { cwd: targetDir, stdio: 'ignore' });
|
|
273
|
+
|
|
274
|
+
// Create .gitignore
|
|
275
|
+
const gitignore = `node_modules/\n.env\n.drm/handoffs/\n.drm/cache/\n.DS_Store\n*.log\n`;
|
|
276
|
+
fs.writeFileSync(path.join(targetDir, '.gitignore'), gitignore);
|
|
277
|
+
await sleep(50);
|
|
278
|
+
});
|
|
67
279
|
}
|
|
68
280
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
createSettingsJson(targetDir);
|
|
281
|
+
console.log(DIVIDER);
|
|
282
|
+
console.log(chalk.bold.green(`\n ✓ Projeto "${name}" criado com sucesso!`));
|
|
72
283
|
|
|
73
|
-
|
|
284
|
+
printStats(targetDir);
|
|
285
|
+
printConstitution();
|
|
286
|
+
printAgentsTable();
|
|
287
|
+
printFunnelDiagram();
|
|
74
288
|
printNextSteps(name);
|
|
75
289
|
});
|
|
76
290
|
|
|
@@ -80,39 +294,78 @@ function createProgram() {
|
|
|
80
294
|
.description('Instalar DRM em projeto existente')
|
|
81
295
|
.action(async () => {
|
|
82
296
|
const targetDir = process.cwd();
|
|
297
|
+
const projectName = path.basename(targetDir);
|
|
298
|
+
|
|
299
|
+
console.log(LOGO_MINI);
|
|
300
|
+
|
|
301
|
+
console.log(chalk.bold(' Instalando em projeto existente'));
|
|
302
|
+
console.log(DIVIDER);
|
|
83
303
|
|
|
84
|
-
|
|
304
|
+
await step('Framework core (.drm-core/)', async () => {
|
|
305
|
+
fs.copySync(path.join(SCAFFOLD_DIR, '.drm-core'), path.join(targetDir, '.drm-core'), { overwrite: false });
|
|
306
|
+
await sleep(100);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
await step('Runtime config (.drm/)', async () => {
|
|
310
|
+
const srcDrm = path.join(SCAFFOLD_DIR, '.drm');
|
|
311
|
+
const destDrm = path.join(targetDir, '.drm');
|
|
312
|
+
if (!fs.existsSync(destDrm)) fs.copySync(srcDrm, destDrm);
|
|
313
|
+
await sleep(50);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
await step('Claude Code commands', async () => {
|
|
317
|
+
fs.copySync(
|
|
318
|
+
path.join(SCAFFOLD_DIR, '.claude/commands/DRM'),
|
|
319
|
+
path.join(targetDir, '.claude/commands/DRM'),
|
|
320
|
+
{ overwrite: false }
|
|
321
|
+
);
|
|
322
|
+
await sleep(50);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
await step('Claude Code rules', async () => {
|
|
326
|
+
const srcRules = path.join(SCAFFOLD_DIR, '.claude/rules');
|
|
327
|
+
if (fs.existsSync(srcRules)) {
|
|
328
|
+
fs.copySync(srcRules, path.join(targetDir, '.claude/rules'), { overwrite: false });
|
|
329
|
+
}
|
|
330
|
+
await sleep(50);
|
|
331
|
+
});
|
|
85
332
|
|
|
86
|
-
|
|
87
|
-
|
|
333
|
+
await step('Estrutura docs/', async () => {
|
|
334
|
+
fs.copySync(path.join(SCAFFOLD_DIR, 'docs'), path.join(targetDir, 'docs'), { overwrite: false });
|
|
335
|
+
await sleep(50);
|
|
336
|
+
});
|
|
88
337
|
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
338
|
+
// CLAUDE.md merge
|
|
339
|
+
await step('CLAUDE.md', async () => {
|
|
340
|
+
const claudeMdPath = path.join(targetDir, '.claude', 'CLAUDE.md');
|
|
341
|
+
if (fs.existsSync(claudeMdPath)) {
|
|
342
|
+
const existing = fs.readFileSync(claudeMdPath, 'utf8');
|
|
343
|
+
if (!existing.includes('DRM — Direct Response Marketing')) {
|
|
344
|
+
fs.appendFileSync(claudeMdPath, '\n\n' + getDrmClaudeMdSection());
|
|
345
|
+
}
|
|
97
346
|
} else {
|
|
98
|
-
|
|
347
|
+
createClaudeMd(targetDir, projectName, true);
|
|
99
348
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
createClaudeMd(targetDir, path.basename(targetDir));
|
|
103
|
-
}
|
|
349
|
+
await sleep(50);
|
|
350
|
+
});
|
|
104
351
|
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
352
|
+
// settings.json merge
|
|
353
|
+
await step('settings.json', async () => {
|
|
354
|
+
const settingsPath = path.join(targetDir, '.claude', 'settings.json');
|
|
355
|
+
if (fs.existsSync(settingsPath)) {
|
|
356
|
+
mergeSettingsJson(settingsPath);
|
|
357
|
+
} else {
|
|
358
|
+
createSettingsJson(targetDir, true);
|
|
359
|
+
}
|
|
360
|
+
await sleep(50);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
console.log(DIVIDER);
|
|
364
|
+
console.log(chalk.bold.green(`\n ✓ DRM instalado com sucesso!`));
|
|
113
365
|
|
|
114
|
-
|
|
115
|
-
|
|
366
|
+
printStats(targetDir);
|
|
367
|
+
printAgentsTable();
|
|
368
|
+
printNextSteps(null);
|
|
116
369
|
});
|
|
117
370
|
|
|
118
371
|
// === doctor ===
|
|
@@ -122,88 +375,96 @@ function createProgram() {
|
|
|
122
375
|
.action(() => {
|
|
123
376
|
const targetDir = process.cwd();
|
|
124
377
|
|
|
125
|
-
console.log(
|
|
378
|
+
console.log(LOGO_MINI);
|
|
379
|
+
console.log(chalk.bold(' Diagnostico'));
|
|
380
|
+
console.log(DIVIDER);
|
|
126
381
|
|
|
127
382
|
const checks = [
|
|
128
|
-
{ name: '
|
|
129
|
-
{ name: '
|
|
130
|
-
{ name: '
|
|
131
|
-
{ name: '
|
|
132
|
-
{ name: '
|
|
133
|
-
{ name: '
|
|
134
|
-
{ name: '
|
|
135
|
-
{ name: '
|
|
136
|
-
{ name: '
|
|
137
|
-
{ name: '.
|
|
383
|
+
{ name: 'Constitution', path: '.drm-core/constitution.md' },
|
|
384
|
+
{ name: 'Agentes (9)', path: '.drm-core/agents/atena.md' },
|
|
385
|
+
{ name: 'Workflows (8)', path: '.drm-core/workflows/funil-vsl-completo.yaml' },
|
|
386
|
+
{ name: 'Templates (12)', path: '.drm-core/templates/vsl-full-template.md' },
|
|
387
|
+
{ name: 'Checklists (8)', path: '.drm-core/checklists/copy-quality-gate.md' },
|
|
388
|
+
{ name: 'Tasks (15)', path: '.drm-core/tasks/copy/escrever-vsl-full.md' },
|
|
389
|
+
{ name: 'Rules (4)', path: '.drm-core/rules/copy-standards.md' },
|
|
390
|
+
{ name: 'Data files', path: '.drm-core/data/workflow-chains.yaml' },
|
|
391
|
+
{ name: 'Runtime config', path: '.drm/config.yaml' },
|
|
392
|
+
{ name: 'CLAUDE.md', path: '.claude/CLAUDE.md' },
|
|
393
|
+
{ name: 'settings.json', path: '.claude/settings.json' },
|
|
394
|
+
{ name: 'Slash commands (DRM)', path: '.claude/commands/DRM/agents/atena.md' },
|
|
395
|
+
{ name: 'Governance rules', path: '.claude/rules/drm-agent-authority.md' },
|
|
396
|
+
{ name: 'Gemini CLI', path: '.gemini/commands/drm-atena.toml', optional: true },
|
|
397
|
+
{ name: 'Antigravity', path: '.antigravity/rules/agents/atena.md', optional: true },
|
|
138
398
|
];
|
|
139
399
|
|
|
140
|
-
let passed = 0;
|
|
141
|
-
let failed = 0;
|
|
142
|
-
let optional = 0;
|
|
400
|
+
let passed = 0, failed = 0, skipped = 0;
|
|
143
401
|
|
|
144
|
-
checks.forEach(({ name, path: checkPath, optional
|
|
402
|
+
checks.forEach(({ name, path: checkPath, optional }) => {
|
|
145
403
|
const fullPath = path.join(targetDir, checkPath);
|
|
146
404
|
if (fs.existsSync(fullPath)) {
|
|
147
405
|
console.log(chalk.green(` ✓ ${name}`));
|
|
148
406
|
passed++;
|
|
149
|
-
} else if (
|
|
150
|
-
console.log(chalk.
|
|
151
|
-
|
|
407
|
+
} else if (optional) {
|
|
408
|
+
console.log(chalk.gray(` ○ ${name} ${chalk.dim('(opcional)')}`));
|
|
409
|
+
skipped++;
|
|
152
410
|
} else {
|
|
153
411
|
console.log(chalk.red(` ✗ ${name}`));
|
|
154
412
|
failed++;
|
|
155
413
|
}
|
|
156
414
|
});
|
|
157
415
|
|
|
158
|
-
|
|
416
|
+
console.log(DIVIDER);
|
|
417
|
+
|
|
418
|
+
// Agent listing
|
|
159
419
|
const agentsDir = path.join(targetDir, '.drm-core', 'agents');
|
|
160
420
|
if (fs.existsSync(agentsDir)) {
|
|
161
|
-
const
|
|
162
|
-
console.log(
|
|
421
|
+
const agentFiles = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md'));
|
|
422
|
+
console.log('');
|
|
423
|
+
console.log(chalk.bold(' Agentes detectados'));
|
|
424
|
+
console.log(DIVIDER);
|
|
425
|
+
agentFiles.forEach(f => {
|
|
426
|
+
const id = f.replace('.md', '');
|
|
427
|
+
const agent = AGENTS.find(a => a.id === id);
|
|
428
|
+
if (agent) {
|
|
429
|
+
console.log(` ${agent.icon} ${chalk.cyan(`@${agent.id}`.padEnd(14))} ${chalk.white(agent.role)}`);
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
console.log(DIVIDER);
|
|
163
433
|
}
|
|
164
434
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (failed
|
|
168
|
-
console.log(chalk.
|
|
169
|
-
|
|
435
|
+
// Verdict
|
|
436
|
+
console.log('');
|
|
437
|
+
if (failed === 0) {
|
|
438
|
+
console.log(chalk.bold.green(` ✓ Framework DRM operacional (${passed}/${passed + failed} checks)`));
|
|
439
|
+
console.log(chalk.gray(' Use @atena para comecar.\n'));
|
|
170
440
|
} else {
|
|
171
|
-
console.log(chalk.
|
|
441
|
+
console.log(chalk.bold.red(` ✗ ${failed} problema(s) encontrado(s)`));
|
|
442
|
+
console.log(chalk.yellow(' Execute "npx drm-core install" para corrigir.\n'));
|
|
443
|
+
process.exit(1);
|
|
172
444
|
}
|
|
173
445
|
});
|
|
174
446
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const items = [
|
|
182
|
-
{ src: '.drm-core', label: '.drm-core/ (framework)' },
|
|
183
|
-
{ src: '.drm', label: '.drm/ (runtime)' },
|
|
184
|
-
{ src: '.claude/commands/DRM', label: '.claude/commands/DRM/ (slash commands)' },
|
|
185
|
-
{ src: '.claude/rules', label: '.claude/rules/ (governance rules)', merge: true },
|
|
186
|
-
{ src: '.gemini', label: '.gemini/ (Gemini CLI)' },
|
|
187
|
-
{ src: '.antigravity', label: '.antigravity/ (Antigravity)' },
|
|
188
|
-
{ src: 'docs', label: 'docs/ (entregaveis)', merge: true },
|
|
189
|
-
];
|
|
190
|
-
|
|
191
|
-
for (const item of items) {
|
|
192
|
-
const srcPath = path.join(SCAFFOLD_DIR, item.src);
|
|
193
|
-
const destPath = path.join(targetDir, item.src);
|
|
194
|
-
|
|
195
|
-
if (!fs.existsSync(srcPath)) continue;
|
|
447
|
+
// === info ===
|
|
448
|
+
program
|
|
449
|
+
.command('info')
|
|
450
|
+
.description('Mostrar informacoes do framework')
|
|
451
|
+
.action(() => {
|
|
452
|
+
const targetDir = process.cwd();
|
|
196
453
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
454
|
+
console.log(LOGO);
|
|
455
|
+
printStats(targetDir);
|
|
456
|
+
printConstitution();
|
|
457
|
+
printAgentsTable();
|
|
458
|
+
printFunnelDiagram();
|
|
459
|
+
});
|
|
202
460
|
|
|
203
|
-
|
|
204
|
-
}
|
|
461
|
+
return program;
|
|
205
462
|
}
|
|
206
463
|
|
|
464
|
+
// ══════════════════════════════════════════════
|
|
465
|
+
// HELPERS
|
|
466
|
+
// ══════════════════════════════════════════════
|
|
467
|
+
|
|
207
468
|
function getDrmClaudeMdSection() {
|
|
208
469
|
return `---
|
|
209
470
|
|
|
@@ -260,23 +521,17 @@ Frontend (VSL) -> Checkout -> Order Bump -> Upsell -> Downsell
|
|
|
260
521
|
Backend (WhatsApp API) -> Nutricao -> Oferta High-Ticket -> Onboarding
|
|
261
522
|
|
|
262
523
|
---
|
|
263
|
-
*DRM Framework
|
|
524
|
+
*DRM Framework v${VERSION}*`;
|
|
264
525
|
}
|
|
265
526
|
|
|
266
|
-
function createClaudeMd(targetDir, projectName) {
|
|
527
|
+
function createClaudeMd(targetDir, projectName, silent) {
|
|
267
528
|
const claudeDir = path.join(targetDir, '.claude');
|
|
268
529
|
fs.ensureDirSync(claudeDir);
|
|
269
|
-
|
|
270
|
-
const content = `# ${projectName}
|
|
271
|
-
|
|
272
|
-
${getDrmClaudeMdSection()}
|
|
273
|
-
`;
|
|
274
|
-
|
|
530
|
+
const content = `# ${projectName}\n\n${getDrmClaudeMdSection()}\n`;
|
|
275
531
|
fs.writeFileSync(path.join(claudeDir, 'CLAUDE.md'), content);
|
|
276
|
-
console.log(chalk.green(' ✓ .claude/CLAUDE.md'));
|
|
277
532
|
}
|
|
278
533
|
|
|
279
|
-
function createSettingsJson(targetDir) {
|
|
534
|
+
function createSettingsJson(targetDir, silent) {
|
|
280
535
|
const claudeDir = path.join(targetDir, '.claude');
|
|
281
536
|
fs.ensureDirSync(claudeDir);
|
|
282
537
|
|
|
@@ -284,112 +539,52 @@ function createSettingsJson(targetDir) {
|
|
|
284
539
|
language: 'portuguese',
|
|
285
540
|
permissions: {
|
|
286
541
|
deny: [
|
|
287
|
-
'Edit(.drm-core/constitution.md)',
|
|
288
|
-
'Write(.drm-core/
|
|
289
|
-
'Edit(.drm-core/
|
|
290
|
-
'Write(.drm-core/
|
|
291
|
-
'Edit(.drm-core/
|
|
292
|
-
'Write(.drm-core/
|
|
293
|
-
'Edit(.drm-core/
|
|
294
|
-
'Write(.drm-core/templates/**)',
|
|
295
|
-
'Edit(.drm-core/workflows/**)',
|
|
296
|
-
'Write(.drm-core/workflows/**)',
|
|
297
|
-
'Edit(.drm-core/checklists/**)',
|
|
298
|
-
'Write(.drm-core/checklists/**)',
|
|
299
|
-
'Edit(.drm-core/rules/**)',
|
|
300
|
-
'Write(.drm-core/rules/**)'
|
|
542
|
+
'Edit(.drm-core/constitution.md)', 'Write(.drm-core/constitution.md)',
|
|
543
|
+
'Edit(.drm-core/agents/**)', 'Write(.drm-core/agents/**)',
|
|
544
|
+
'Edit(.drm-core/tasks/**)', 'Write(.drm-core/tasks/**)',
|
|
545
|
+
'Edit(.drm-core/templates/**)', 'Write(.drm-core/templates/**)',
|
|
546
|
+
'Edit(.drm-core/workflows/**)', 'Write(.drm-core/workflows/**)',
|
|
547
|
+
'Edit(.drm-core/checklists/**)', 'Write(.drm-core/checklists/**)',
|
|
548
|
+
'Edit(.drm-core/rules/**)', 'Write(.drm-core/rules/**)'
|
|
301
549
|
],
|
|
302
550
|
allow: [
|
|
303
551
|
'Read(.drm-core/**)',
|
|
304
|
-
'Edit(.drm-core/data/**)',
|
|
305
|
-
'Write(.drm
|
|
306
|
-
'Edit(
|
|
307
|
-
'Write(.drm/**)',
|
|
308
|
-
'Edit(docs/**)',
|
|
309
|
-
'Write(docs/**)'
|
|
552
|
+
'Edit(.drm-core/data/**)', 'Write(.drm-core/data/**)',
|
|
553
|
+
'Edit(.drm/**)', 'Write(.drm/**)',
|
|
554
|
+
'Edit(docs/**)', 'Write(docs/**)'
|
|
310
555
|
]
|
|
311
556
|
}
|
|
312
557
|
};
|
|
313
558
|
|
|
314
559
|
fs.writeJsonSync(path.join(claudeDir, 'settings.json'), settings, { spaces: 2 });
|
|
315
|
-
console.log(chalk.green(' ✓ .claude/settings.json'));
|
|
316
560
|
}
|
|
317
561
|
|
|
318
562
|
function mergeSettingsJson(settingsPath) {
|
|
319
563
|
const settings = fs.readJsonSync(settingsPath);
|
|
320
|
-
|
|
321
564
|
if (!settings.permissions) settings.permissions = {};
|
|
322
565
|
if (!settings.permissions.deny) settings.permissions.deny = [];
|
|
323
566
|
if (!settings.permissions.allow) settings.permissions.allow = [];
|
|
324
567
|
|
|
325
|
-
const
|
|
326
|
-
'Edit(.drm-core/constitution.md)',
|
|
327
|
-
'Write(.drm-core/
|
|
328
|
-
'Edit(.drm-core/
|
|
329
|
-
'Write(.drm-core/
|
|
330
|
-
'Edit(.drm-core/
|
|
331
|
-
'Write(.drm-core/
|
|
332
|
-
'Edit(.drm-core/
|
|
333
|
-
'Write(.drm-core/templates/**)',
|
|
334
|
-
'Edit(.drm-core/workflows/**)',
|
|
335
|
-
'Write(.drm-core/workflows/**)',
|
|
336
|
-
'Edit(.drm-core/checklists/**)',
|
|
337
|
-
'Write(.drm-core/checklists/**)',
|
|
338
|
-
'Edit(.drm-core/rules/**)',
|
|
339
|
-
'Write(.drm-core/rules/**)'
|
|
568
|
+
const drmDeny = [
|
|
569
|
+
'Edit(.drm-core/constitution.md)', 'Write(.drm-core/constitution.md)',
|
|
570
|
+
'Edit(.drm-core/agents/**)', 'Write(.drm-core/agents/**)',
|
|
571
|
+
'Edit(.drm-core/tasks/**)', 'Write(.drm-core/tasks/**)',
|
|
572
|
+
'Edit(.drm-core/templates/**)', 'Write(.drm-core/templates/**)',
|
|
573
|
+
'Edit(.drm-core/workflows/**)', 'Write(.drm-core/workflows/**)',
|
|
574
|
+
'Edit(.drm-core/checklists/**)', 'Write(.drm-core/checklists/**)',
|
|
575
|
+
'Edit(.drm-core/rules/**)', 'Write(.drm-core/rules/**)'
|
|
340
576
|
];
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
'
|
|
344
|
-
'Edit(.drm-core/data/**)',
|
|
345
|
-
'Write(.drm-core/data/**)',
|
|
346
|
-
'Edit(.drm/**)',
|
|
347
|
-
'Write(.drm/**)'
|
|
577
|
+
const drmAllow = [
|
|
578
|
+
'Read(.drm-core/**)', 'Edit(.drm-core/data/**)', 'Write(.drm-core/data/**)',
|
|
579
|
+
'Edit(.drm/**)', 'Write(.drm/**)'
|
|
348
580
|
];
|
|
349
581
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
settings.permissions.deny.push(rule);
|
|
353
|
-
}
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
drmAllowRules.forEach(rule => {
|
|
357
|
-
if (!settings.permissions.allow.includes(rule)) {
|
|
358
|
-
settings.permissions.allow.push(rule);
|
|
359
|
-
}
|
|
360
|
-
});
|
|
582
|
+
drmDeny.forEach(r => { if (!settings.permissions.deny.includes(r)) settings.permissions.deny.push(r); });
|
|
583
|
+
drmAllow.forEach(r => { if (!settings.permissions.allow.includes(r)) settings.permissions.allow.push(r); });
|
|
361
584
|
|
|
362
585
|
fs.writeJsonSync(settingsPath, settings, { spaces: 2 });
|
|
363
586
|
}
|
|
364
587
|
|
|
365
|
-
function printNextSteps(name) {
|
|
366
|
-
console.log(chalk.bold('Proximos passos:\n'));
|
|
367
|
-
console.log(chalk.white(` cd ${name}`));
|
|
368
|
-
console.log(chalk.white(' claude # Abrir Claude Code'));
|
|
369
|
-
console.log(chalk.white(' @atena # Ativar estrategista'));
|
|
370
|
-
console.log(chalk.white(' /DRM:agents:apolo # Ativar copywriter'));
|
|
371
|
-
console.log(chalk.gray('\n Ou use Gemini CLI / Antigravity com os mesmos agentes.\n'));
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
function printAgents() {
|
|
375
|
-
console.log(chalk.bold('Agentes disponiveis:\n'));
|
|
376
|
-
const agents = [
|
|
377
|
-
['@atena', 'Estrategista / Master'],
|
|
378
|
-
['@apolo', 'Copywriter'],
|
|
379
|
-
['@hermes', 'Media Buyer'],
|
|
380
|
-
['@zeus', 'Creative Director'],
|
|
381
|
-
['@cronos', 'Analista de Dados'],
|
|
382
|
-
['@hefesto', 'Editor de Video'],
|
|
383
|
-
['@afrodite', 'Designer'],
|
|
384
|
-
['@temis', 'Compliance'],
|
|
385
|
-
['@poseidon', 'Funnel Architect'],
|
|
386
|
-
];
|
|
387
|
-
agents.forEach(([name, role]) => {
|
|
388
|
-
console.log(chalk.white(` ${chalk.cyan(name.padEnd(12))} ${role}`));
|
|
389
|
-
});
|
|
390
|
-
console.log('');
|
|
391
|
-
}
|
|
392
|
-
|
|
393
588
|
async function run(args = process.argv) {
|
|
394
589
|
const program = createProgram();
|
|
395
590
|
await program.parseAsync(args);
|