This commit is contained in:
jb
2026-04-14 16:06:19 -03:00
commit c5621bcb49
10 changed files with 551 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
node_modules/
+153
View File
@@ -0,0 +1,153 @@
# vitruvio-cli
Ferramentas de linha de comando para criação e manutenção de repositórios Vitruvio.
---
## Pré-requisitos
- **Node.js** 18 ou superior
- **Git** instalado e acessível no terminal
- **Chave SSH** configurada no servidor Gitea da davinTI (necessária para clonar via SSH — há fallback automático para HTTPS)
---
## Instalação
### Linux
```bash
# 1. Clone o repositório
git clone ssh://git@git.davinti.com.br:2222/davinTI/vitruvio-cli.git
cd vitruvio-cli
# 2. Instale as dependências
npm install
# 3. Registre o comando globalmente
npm link
```
Se você usa **mise** para gerenciar versões do Node, rode também:
```bash
mise reshim
```
Verifique a instalação:
```bash
vitruvio --help
```
---
### Windows
Abra o **PowerShell** ou **Prompt de Comando** como administrador.
```powershell
# 1. Clone o repositório
git clone ssh://git@git.davinti.com.br:2222/davinTI/vitruvio-cli.git
cd vitruvio-cli
# 2. Instale as dependências
npm install
# 3. Registre o comando globalmente
npm link
```
Verifique a instalação:
```powershell
vitruvio --help
```
> **Nota:** Caso o terminal não reconheça o comando `vitruvio` após o `npm link`, verifique se o diretório de binários globais do npm está no PATH do sistema. Para descobrir o caminho:
> ```powershell
> npm config get prefix
> ```
> Adicione o caminho retornado (ex: `C:\Users\SeuUsuario\AppData\Roaming\npm`) nas variáveis de ambiente do sistema em **Painel de Controle → Sistema → Variáveis de Ambiente → Path**.
---
## Comandos
### `vitruvio init <nome>`
Cria um novo repositório Vitruvio a partir do template base.
```bash
vitruvio init meu-modulo
```
- Clona o template `projeto-base` para um diretório com o nome informado
- Remove o histórico git do template e inicializa um repositório limpo sem remotes
- Pronto para você adicionar seu próprio remote e fazer o primeiro commit
**Após o init:**
```bash
cd meu-modulo
# Edite o vitruvio.json com a chave e nome do seu módulo
# Depois:
git remote add origin <url-do-seu-repositório>
git add .
git commit -m "feat: scaffold inicial"
```
---
### `vitruvio update-repo`
Atualiza os arquivos `CLAUDE.md` gerenciados no repositório atual com a versão mais recente do template.
```bash
# Execute dentro de um repositório Vitruvio (onde há vitruvio.json)
cd meu-modulo
vitruvio update-repo
```
Arquivos atualizados por este comando:
```
CLAUDE.md
endpoints/CLAUDE.md
panels/CLAUDE.md
patches/CLAUDE.md
processes/CLAUDE.md
queries/CLAUDE.md
reports/CLAUDE.md
scripts/CLAUDE.md
```
Nenhum outro arquivo do repositório é tocado.
---
### `vitruvio update-base`
Sincroniza os diretórios gerenciados do diretório de plataforma local com o repositório `base-claude`.
```bash
vitruvio update-base
```
Diretório de plataforma atualizado:
| Sistema | Caminho |
|---------|---------|
| Linux | `~/.local/share/vitruvio-platform/` |
| Windows | `%LOCALAPPDATA%\vitruvio-platform\` |
Diretórios gerenciados (substituídos completamente):
```
libs/
docs/
examples/
```
Qualquer outro diretório ou arquivo fora dessa lista não é alterado.
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env node
'use strict';
var { Command } = require('commander');
var pkg = require('../package.json');
var initCmd = require('../src/commands/init');
var updateRepoCmd = require('../src/commands/update-repo');
var updateBaseCmd = require('../src/commands/update-base');
var program = new Command();
program
.name('vitruvio')
.description('Ferramentas de linha de comando para repositórios Vitruvio')
.version(pkg.version);
initCmd.register(program);
updateRepoCmd.register(program);
updateBaseCmd.register(program);
program.parse(process.argv);
+69
View File
@@ -0,0 +1,69 @@
{
"name": "vitruvio-cli",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vitruvio-cli",
"version": "1.0.0",
"dependencies": {
"commander": "^12.0.0",
"fs-extra": "^11.2.0"
},
"bin": {
"vitruvio": "bin/vitruvio.js"
}
},
"node_modules/commander": {
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/fs-extra": {
"version": "11.3.4",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz",
"integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=14.14"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC"
},
"node_modules/jsonfile": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
}
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"name": "vitruvio-cli",
"version": "1.0.0",
"description": "CLI tooling for Vitruvio content repositories",
"bin": {
"vitruvio": "bin/vitruvio.js"
},
"scripts": {
"start": "node bin/vitruvio.js"
},
"dependencies": {
"commander": "^12.0.0",
"fs-extra": "^11.2.0"
}
}
+63
View File
@@ -0,0 +1,63 @@
'use strict';
var path = require('path');
var fs = require('fs');
var { execSync } = require('child_process');
var fse = require('fs-extra');
var { cloneWithFallback, cleanupTemp } = require('../utils/git');
var SSH_URL = 'ssh://git@git.davinti.com.br:2222/davinTI/projeto-base.git';
var HTTPS_URL = 'https://git.davinti.com.br/davinTI/projeto-base.git';
function register(program) {
program
.command('init <nome>')
.description('Cria um novo repositório Vitruvio a partir do template base')
.action(function (name) {
var targetDir = path.resolve(process.cwd(), name);
if (fs.existsSync(targetDir)) {
console.error('Erro: o diretório já existe: ' + targetDir);
process.exit(1);
}
console.log('Clonando template para ' + targetDir + '...');
var tmpDir = null;
try {
tmpDir = cloneWithFallback(SSH_URL, HTTPS_URL);
} catch (e) {
console.error('Erro: não foi possível clonar o repositório template.');
console.error(e.message);
process.exit(1);
}
try {
fse.copySync(tmpDir, targetDir, {
filter: function (src) {
var rel = path.relative(tmpDir, src);
return rel !== '.git' && !rel.startsWith('.git' + path.sep);
}
});
} finally {
cleanupTemp(tmpDir);
}
execSync('git init', { cwd: targetDir, stdio: 'pipe' });
console.log('');
console.log('Pronto! Seu novo repositório Vitruvio está em:');
console.log(' ' + targetDir);
console.log('');
console.log('Próximos passos:');
console.log(' 1. Edite o vitruvio.json — atualize metadata.key e metadata.name do seu módulo');
console.log(' 2. Adicione o seu remote:');
console.log(' cd ' + name);
console.log(' git remote add origin <url-do-seu-repositório>');
console.log(' 3. Faça o commit inicial:');
console.log(' git add .');
console.log(' git commit -m "feat: scaffold inicial"');
});
}
module.exports = { register: register };
+76
View File
@@ -0,0 +1,76 @@
'use strict';
var path = require('path');
var fs = require('fs');
var fse = require('fs-extra');
var { cloneWithFallback, cleanupTemp } = require('../utils/git');
var { getPlatformDir } = require('../utils/platform');
var SSH_URL = 'ssh://git@git.davinti.com.br:2222/davinTI/base-claude.git';
var HTTPS_URL = 'https://git.davinti.com.br/davinTI/base-claude.git';
// Directories inside the platform dir that are fully managed by this command.
// Each of these is replaced in its entirety — files removed from the source
// are also removed locally. Directories not in this list are never touched.
var MANAGED_DIRS = ['libs', 'docs', 'examples'];
function register(program) {
program
.command('update-base')
.description('Sincroniza os diretórios gerenciados do diretório de plataforma Vitruvio com o repositório base-claude')
.action(function () {
var platformDir;
try {
platformDir = getPlatformDir();
} catch (e) {
console.error('Erro: ' + e.message);
process.exit(1);
}
console.log('Diretório de plataforma: ' + platformDir);
console.log('Buscando repositório base-claude...');
var tmpDir = null;
try {
tmpDir = cloneWithFallback(SSH_URL, HTTPS_URL);
} catch (e) {
console.error('Erro: não foi possível clonar o repositório base-claude.');
console.error(e.message);
process.exit(1);
}
try {
fse.ensureDirSync(platformDir);
var results = [];
MANAGED_DIRS.forEach(function (dirName) {
var src = path.join(tmpDir, dirName);
var dest = path.join(platformDir, dirName);
if (!fs.existsSync(src)) {
results.push({ dir: dirName, status: 'não encontrado no repositório' });
return;
}
// Full replacement: remove existing dir, copy fresh from source
if (fs.existsSync(dest)) {
fse.removeSync(dest);
}
fse.copySync(src, dest);
results.push({ dir: dirName, status: 'atualizado' });
});
console.log('');
results.forEach(function (r) {
console.log(' ' + r.dir + '/ — ' + r.status);
});
console.log('');
console.log('Atualização concluída.');
} finally {
cleanupTemp(tmpDir);
}
});
}
module.exports = { register: register };
+82
View File
@@ -0,0 +1,82 @@
'use strict';
var path = require('path');
var fs = require('fs');
var fse = require('fs-extra');
var { cloneWithFallback, cleanupTemp } = require('../utils/git');
var SSH_URL = 'ssh://git@git.davinti.com.br:2222/davinTI/projeto-base.git';
var HTTPS_URL = 'https://git.davinti.com.br/davinTI/projeto-base.git';
// CLAUDE.md files managed by the template — relative paths from repo root
var MANAGED_FILES = [
'CLAUDE.md',
'endpoints/CLAUDE.md',
'panels/CLAUDE.md',
'patches/CLAUDE.md',
'processes/CLAUDE.md',
'queries/CLAUDE.md',
'reports/CLAUDE.md',
'scripts/CLAUDE.md'
];
function register(program) {
program
.command('update-repo')
.description('Atualiza os arquivos CLAUDE.md gerenciados neste repositório com a versão mais recente do template')
.action(function () {
var repoDir = process.cwd();
if (!fs.existsSync(path.join(repoDir, 'vitruvio.json'))) {
console.error('Erro: nenhum vitruvio.json encontrado no diretório atual.');
console.error('Execute este comando dentro de um repositório Vitruvio.');
process.exit(1);
}
console.log('Buscando template mais recente...');
var tmpDir = null;
try {
tmpDir = cloneWithFallback(SSH_URL, HTTPS_URL);
} catch (e) {
console.error('Erro: não foi possível clonar o repositório template.');
console.error(e.message);
process.exit(1);
}
try {
var updated = [];
var skipped = [];
MANAGED_FILES.forEach(function (relPath) {
var src = path.join(tmpDir, relPath);
var dest = path.join(repoDir, relPath);
if (!fs.existsSync(src)) {
skipped.push(relPath);
return;
}
fse.ensureDirSync(path.dirname(dest));
fse.copySync(src, dest, { overwrite: true });
updated.push(relPath);
});
console.log('');
if (updated.length > 0) {
console.log('Arquivos atualizados (' + updated.length + '):');
updated.forEach(function (f) { console.log(' ' + f); });
}
if (skipped.length > 0) {
console.log('Não encontrados no template (' + skipped.length + '):');
skipped.forEach(function (f) { console.log(' ' + f); });
}
console.log('');
console.log('Atualização concluída.');
} finally {
cleanupTemp(tmpDir);
}
});
}
module.exports = { register: register };
+48
View File
@@ -0,0 +1,48 @@
'use strict';
var path = require('path');
var os = require('os');
var fs = require('fs');
var { execSync } = require('child_process');
/**
* Shallow-clones a git URL into a unique temp directory.
* Returns the path to the cloned directory.
* Throws on failure.
*/
function cloneToTemp(url) {
var tmpBase = os.tmpdir();
var tmpDir = path.join(tmpBase, 'vitruvio-' + Date.now() + '-' + Math.random().toString(36).slice(2));
fs.mkdirSync(tmpDir, { recursive: true });
execSync('git clone --depth 1 ' + url + ' ' + tmpDir, { stdio: 'pipe' });
return tmpDir;
}
/**
* Tries SSH clone first; if it fails, tries HTTPS clone.
* Returns the path to the cloned directory.
*/
function cloneWithFallback(sshUrl, httpsUrl) {
try {
return cloneToTemp(sshUrl);
} catch (e) {
console.log('Clone SSH falhou, tentando HTTPS...');
return cloneToTemp(httpsUrl);
}
}
/**
* Removes a temporary directory created by cloneToTemp.
*/
function cleanupTemp(tmpDir) {
if (!tmpDir || !tmpDir.includes('vitruvio-')) {
throw new Error('Refusing to delete unexpected path: ' + tmpDir);
}
fs.rmSync(tmpDir, { recursive: true, force: true });
}
module.exports = {
cloneToTemp: cloneToTemp,
cloneWithFallback: cloneWithFallback,
cleanupTemp: cleanupTemp
};
+22
View File
@@ -0,0 +1,22 @@
'use strict';
var path = require('path');
var os = require('os');
/**
* Returns the path to the Vitruvio platform directory for the current OS.
* Linux/macOS: ~/.local/share/vitruvio-platform/
* Windows: %LOCALAPPDATA%\vitruvio-platform\
*/
function getPlatformDir() {
if (process.platform === 'win32') {
var localAppData = process.env.LOCALAPPDATA;
if (!localAppData) {
throw new Error('LOCALAPPDATA environment variable is not set.');
}
return path.join(localAppData, 'vitruvio-platform');
}
return path.join(os.homedir(), '.local', 'share', 'vitruvio-platform');
}
module.exports = { getPlatformDir: getPlatformDir };