@nathanramorim/forge-sdd 1.1.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 (3) hide show
  1. package/README.md +94 -0
  2. package/bin/run.js +172 -0
  3. package/package.json +27 -0
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # forge-sdd
2
+
3
+ > CLI que scaffolda a estrutura **Forge-SDD** em qualquer projeto — pronta para uso com GitHub Copilot.
4
+
5
+ ## Uso rápido
6
+
7
+ ```bash
8
+ npx forge-sdd@latest init
9
+ ```
10
+
11
+ Apresenta um formulário interativo e cria **32 arquivos** com memória de projeto, spec, chatmodes, prompts e configuração de MCPs.
12
+
13
+ ---
14
+
15
+ ## Requisitos
16
+
17
+ - Node.js ≥ 18
18
+ - Acesso à internet (primeiro uso baixa o binário ~5 MB)
19
+
20
+ O binário Go é baixado automaticamente do GitHub Releases, validado por SHA256 e cacheado em `~/.cache/forge-sdd/`. Execuções seguintes são instantâneas.
21
+
22
+ ---
23
+
24
+ ## Opções
25
+
26
+ ```bash
27
+ # Modo interativo (padrão)
28
+ npx forge-sdd@latest init
29
+
30
+ # Pular formulário, usar valores padrão
31
+ npx forge-sdd@latest init --yes
32
+
33
+ # Especificar diretório destino
34
+ npx forge-sdd@latest init /caminho/do/projeto
35
+
36
+ # Preview sem criar arquivos
37
+ npx forge-sdd@latest init --yes --dry-run
38
+
39
+ # Ver versão
40
+ npx forge-sdd@latest version
41
+ ```
42
+
43
+ ---
44
+
45
+ ## O que é gerado
46
+
47
+ ```
48
+ sdd/ → memória e especificação do projeto
49
+ memory/
50
+ progress.md → estado ativo (leia primeiro a cada sessão)
51
+ constitution.md → regras imutáveis do projeto
52
+ mcps.md → MCPs configurados
53
+ progress-log.md → histórico de sessões
54
+ spec/
55
+ overview.md, stack.md, modules.md, flows.md, decisions.md
56
+ features/
57
+ feat-00-foundation.md
58
+ index.md
59
+ skills/index.md
60
+ plan.md, README.md
61
+
62
+ .github/
63
+ copilot-instructions.md → instruções globais para o Copilot
64
+ chatmodes/ → 6 modos de agente (orquestrador, builder, revisor…)
65
+ prompts/ → 7 prompts reutilizáveis
66
+
67
+ .vscode/
68
+ mcp.json → configuração dos MCPs (context7, git)
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Instalação permanente (opcional)
74
+
75
+ Se preferir ter o comando disponível globalmente sem `npx`:
76
+
77
+ ```bash
78
+ npm install -g forge-sdd
79
+ forge-sdd init
80
+ ```
81
+
82
+ Ou via Homebrew (macOS/Linux):
83
+
84
+ ```bash
85
+ brew install nathanramorim/forge-sdd/forge-sdd
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Links
91
+
92
+ - [Repositório do projeto](https://github.com/nathanramorim/homebrew-forge-sdd)
93
+ - [Releases / Changelog](https://github.com/nathanramorim/homebrew-forge-sdd/releases)
94
+ - [Metodologia Forge-SDD](https://github.com/nathanramorim/homebrew-forge-sdd#readme)
package/bin/run.js ADDED
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const https = require('https');
5
+ const http = require('http');
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const os = require('os');
9
+ const { execFileSync, spawnSync } = require('child_process');
10
+ const crypto = require('crypto');
11
+ const zlib = require('zlib');
12
+
13
+ // ─── helpers ──────────────────────────────────────────────────────────────────
14
+
15
+ const VERSION = require('../package.json').version;
16
+ const BASE_URL = `https://github.com/nathanramorim/homebrew-forge-sdd/releases/download/v${VERSION}`;
17
+
18
+ function platformAsset() {
19
+ const plat = process.platform;
20
+ const arch = process.arch;
21
+
22
+ const map = {
23
+ 'linux-x64': { file: `homebrew-forge-sdd_linux_amd64.tar.gz`, ext: 'tar.gz' },
24
+ 'linux-arm64': { file: `homebrew-forge-sdd_linux_arm64.tar.gz`, ext: 'tar.gz' },
25
+ 'darwin-x64': { file: `homebrew-forge-sdd_darwin_amd64.tar.gz`, ext: 'tar.gz' },
26
+ 'darwin-arm64': { file: `homebrew-forge-sdd_darwin_arm64.tar.gz`, ext: 'tar.gz' },
27
+ 'win32-x64': { file: `homebrew-forge-sdd_windows_amd64.zip`, ext: 'zip' },
28
+ };
29
+
30
+ const key = `${plat}-${arch}`;
31
+ const asset = map[key];
32
+ if (!asset) {
33
+ console.error(`forge-sdd: unsupported platform ${key}`);
34
+ process.exit(1);
35
+ }
36
+ return asset;
37
+ }
38
+
39
+ function cacheDir() {
40
+ const base = process.env.XDG_CACHE_HOME
41
+ || (process.platform === 'win32'
42
+ ? path.join(os.homedir(), 'AppData', 'Local')
43
+ : path.join(os.homedir(), '.cache'));
44
+ return path.join(base, 'forge-sdd', VERSION);
45
+ }
46
+
47
+ function binaryPath(dir) {
48
+ const name = process.platform === 'win32' ? 'forge-sdd.exe' : 'forge-sdd';
49
+ return path.join(dir, name);
50
+ }
51
+
52
+ // ─── download ─────────────────────────────────────────────────────────────────
53
+
54
+ function download(url, dest) {
55
+ return new Promise((resolve, reject) => {
56
+ const file = fs.createWriteStream(dest);
57
+ const protocol = url.startsWith('https') ? https : http;
58
+ const req = protocol.get(url, (res) => {
59
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
60
+ file.close();
61
+ fs.unlinkSync(dest);
62
+ return download(res.headers.location, dest).then(resolve).catch(reject);
63
+ }
64
+ if (res.statusCode !== 200) {
65
+ file.close();
66
+ fs.unlinkSync(dest);
67
+ return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
68
+ }
69
+ res.pipe(file);
70
+ file.on('finish', () => { file.close(); resolve(); });
71
+ });
72
+ req.on('error', (err) => { fs.unlinkSync(dest); reject(err); });
73
+ });
74
+ }
75
+
76
+ function sha256(filePath) {
77
+ const data = fs.readFileSync(filePath);
78
+ return crypto.createHash('sha256').update(data).digest('hex');
79
+ }
80
+
81
+ async function verifyChecksum(assetFile, binDir) {
82
+ const checksumsUrl = `${BASE_URL}/checksums.txt`;
83
+ const checksumsPath = path.join(binDir, 'checksums.txt');
84
+ await download(checksumsUrl, checksumsPath);
85
+
86
+ const lines = fs.readFileSync(checksumsPath, 'utf8').split('\n');
87
+ const entry = lines.find(l => l.includes(assetFile));
88
+ if (!entry) {
89
+ throw new Error(`checksum not found for ${assetFile}`);
90
+ }
91
+ return entry.split(/\s+/)[0].toLowerCase();
92
+ }
93
+
94
+ // ─── extract ──────────────────────────────────────────────────────────────────
95
+
96
+ function extractTarGz(archive, destDir) {
97
+ // Use system tar — available on all platforms we target
98
+ const result = spawnSync('tar', ['-xzf', archive, '-C', destDir, '--strip-components=0'], {
99
+ stdio: 'inherit',
100
+ });
101
+ if (result.status !== 0) {
102
+ throw new Error('tar extraction failed');
103
+ }
104
+ }
105
+
106
+ function extractZip(archive, destDir) {
107
+ // Use system unzip or PowerShell on Windows
108
+ if (process.platform === 'win32') {
109
+ const ps = spawnSync('powershell', [
110
+ '-NoProfile', '-Command',
111
+ `Expand-Archive -Path "${archive}" -DestinationPath "${destDir}" -Force`,
112
+ ], { stdio: 'inherit' });
113
+ if (ps.status !== 0) throw new Error('zip extraction failed');
114
+ } else {
115
+ const result = spawnSync('unzip', ['-o', archive, '-d', destDir], { stdio: 'inherit' });
116
+ if (result.status !== 0) throw new Error('unzip extraction failed');
117
+ }
118
+ }
119
+
120
+ // ─── main ─────────────────────────────────────────────────────────────────────
121
+
122
+ async function ensureBinary() {
123
+ const dir = cacheDir();
124
+ const bin = binaryPath(dir);
125
+
126
+ if (fs.existsSync(bin)) {
127
+ return bin;
128
+ }
129
+
130
+ fs.mkdirSync(dir, { recursive: true });
131
+
132
+ const asset = platformAsset();
133
+ const archivePath = path.join(dir, asset.file);
134
+
135
+ process.stderr.write(`forge-sdd: downloading v${VERSION} for ${process.platform}/${process.arch}...\n`);
136
+
137
+ const assetUrl = `${BASE_URL}/${asset.file}`;
138
+ await download(assetUrl, archivePath);
139
+
140
+ // Verify SHA256
141
+ const expectedHash = await verifyChecksum(asset.file, dir);
142
+ const actualHash = sha256(archivePath);
143
+ if (actualHash !== expectedHash) {
144
+ fs.unlinkSync(archivePath);
145
+ throw new Error(`SHA256 mismatch: expected ${expectedHash}, got ${actualHash}`);
146
+ }
147
+
148
+ // Extract
149
+ if (asset.ext === 'tar.gz') {
150
+ extractTarGz(archivePath, dir);
151
+ } else {
152
+ extractZip(archivePath, dir);
153
+ }
154
+
155
+ // Ensure executable bit on Unix
156
+ if (process.platform !== 'win32') {
157
+ fs.chmodSync(bin, 0o755);
158
+ }
159
+
160
+ return bin;
161
+ }
162
+
163
+ ensureBinary()
164
+ .then((bin) => {
165
+ const args = process.argv.slice(2);
166
+ const result = spawnSync(bin, args, { stdio: 'inherit' });
167
+ process.exit(result.status ?? 1);
168
+ })
169
+ .catch((err) => {
170
+ console.error(`forge-sdd error: ${err.message}`);
171
+ process.exit(1);
172
+ });
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@nathanramorim/forge-sdd",
3
+ "version": "1.1.0",
4
+ "description": "CLI que scaffolda estruturas Forge-SDD em segundos",
5
+ "homepage": "https://github.com/nathanramorim/homebrew-forge-sdd",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/nathanramorim/forge-sdd.git"
9
+ },
10
+ "license": "MIT",
11
+ "bin": {
12
+ "forge-sdd": "bin/run.js"
13
+ },
14
+ "files": [
15
+ "bin/run.js"
16
+ ],
17
+ "engines": {
18
+ "node": ">=18"
19
+ },
20
+ "keywords": [
21
+ "scaffold",
22
+ "cli",
23
+ "sdd",
24
+ "forge",
25
+ "copilot"
26
+ ]
27
+ }