create-sdd-ai-stack 0.1.17
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/AGENTS.md +164 -0
- package/APP-STACK.md +31 -0
- package/APP.md +46 -0
- package/ARCHITECTURE.md +115 -0
- package/DESIGN.md +219 -0
- package/LICENSE +21 -0
- package/NEXT.md +345 -0
- package/NODE.md +107 -0
- package/REACT.md +92 -0
- package/README.md +260 -0
- package/SKILLS/check-docs/SKILL.md +24 -0
- package/SKILLS/check-docs/check-docs.mjs +56 -0
- package/SKILLS/create-feature/SKILL.md +40 -0
- package/SKILLS/create-feature/create-feature.mjs +128 -0
- package/SKILLS/create-feature.sh +112 -0
- package/SKILLS/install-submodule/SKILL.md +42 -0
- package/SKILLS/install-submodule/install-submodule.mjs +119 -0
- package/bin/create-sdd-ai-stack.mjs +93 -0
- package/docs/CHANGELOG.md +209 -0
- package/docs/PLANNING.md +68 -0
- package/docs/PRODUCT.md +76 -0
- package/docs/RELEASE.md +332 -0
- package/lib/check-links.mjs +42 -0
- package/lib/constants.mjs +60 -0
- package/lib/scaffold.mjs +253 -0
- package/package.json +80 -0
- package/specs/BACKLOG.md +24 -0
- package/specs/PLAN.md +57 -0
- package/specs/ROADMAP.md +35 -0
- package/specs/history/phases/phase-0-bootstrap.md +67 -0
- package/specs/tasks/TASK_TEMPLATE.md +46 -0
- package/src/cli.mjs +119 -0
- package/stacks/README.md +33 -0
- package/stacks/ai.md +52 -0
- package/stacks/ci.md +59 -0
- package/stacks/database.md +56 -0
- package/stacks/git.md +51 -0
- package/stacks/shadcn.md +52 -0
- package/stacks/tailwind.md +65 -0
- package/stacks/testing.md +74 -0
- package/stacks/typescript.md +58 -0
- package/template/next/.env.example +4 -0
- package/template/next/README.md +47 -0
- package/template/next/biome.json +36 -0
- package/template/next/gitignore +32 -0
- package/template/next/next.config.ts +12 -0
- package/template/next/package.json +47 -0
- package/template/next/playwright.config.ts +21 -0
- package/template/next/postcss.config.mjs +7 -0
- package/template/next/src/app/(app)/app/page.tsx +18 -0
- package/template/next/src/app/(app)/error.tsx +30 -0
- package/template/next/src/app/(app)/layout.tsx +25 -0
- package/template/next/src/app/(marketing)/page.tsx +73 -0
- package/template/next/src/app/globals.css +437 -0
- package/template/next/src/app/layout.tsx +41 -0
- package/template/next/src/app/not-found.tsx +11 -0
- package/template/next/src/features/example/actions.ts +43 -0
- package/template/next/src/features/example/application/create-example.usecase.ts +26 -0
- package/template/next/src/features/example/container.ts +16 -0
- package/template/next/src/features/example/domain/IExampleRepository.ts +11 -0
- package/template/next/src/features/example/domain/example.schema.ts +18 -0
- package/template/next/src/features/example/infrastructure/example.repository.ts +26 -0
- package/template/next/src/features/example/ui/create-example-form.tsx +56 -0
- package/template/next/src/proxy.ts +23 -0
- package/template/next/src/shared/lib/cn.ts +6 -0
- package/template/next/src/shared/server/auth.ts +32 -0
- package/template/next/src/shared/server/env.ts +14 -0
- package/template/next/src/shared/ui/index.ts +3 -0
- package/template/next/tests/e2e/routes.spec.ts +34 -0
- package/template/next/tests/unit/create-example.test.ts +55 -0
- package/template/next/tsconfig.json +37 -0
- package/template/next/vitest.config.ts +19 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SDD SKILL: instala o core de regras (SDD/) em um projeto EXISTENTE
|
|
4
|
+
* como git submodule, e cria os atalhos na raiz apontando para ./SDD/AGENTS.md
|
|
5
|
+
*
|
|
6
|
+
* Uso:
|
|
7
|
+
* node SDD/SKILLS/install-submodule/install-submodule.mjs [caminho-do-projeto]
|
|
8
|
+
* node SDD/SKILLS/install-submodule/install-submodule.mjs # usa cwd
|
|
9
|
+
* node SDD/SKILLS/install-submodule/install-submodule.mjs . --copy # copia em vez de submodule
|
|
10
|
+
*/
|
|
11
|
+
import fs from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { execFileSync } from "node:child_process";
|
|
14
|
+
|
|
15
|
+
const REPO_URL = "https://github.com/marcelinosandroni/sdd-ai-stack.git";
|
|
16
|
+
const SDD_DIR = "SDD";
|
|
17
|
+
|
|
18
|
+
const RULE_FILES = [
|
|
19
|
+
"AGENTS.md", "APP.md", "APP-STACK.md", "ARCHITECTURE.md",
|
|
20
|
+
"DESIGN.md", "NEXT.md", "NODE.md", "REACT.md",
|
|
21
|
+
];
|
|
22
|
+
const RULE_DIRS = ["stacks", "specs", "docs", "SKILLS"];
|
|
23
|
+
const SHORTCUTS = [
|
|
24
|
+
"AGENTS.md", "CLAUDE.md", "GEMINI.md", ".cursorrules",
|
|
25
|
+
".windsurfrules", ".github/copilot-instructions.md", ".clinerules",
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const STUB = `# 🤖 AGENTS.md (ponteiro)
|
|
29
|
+
|
|
30
|
+
> A fonte da verdade vive em **[SDD/AGENTS.md](./SDD/AGENTS.md)**.
|
|
31
|
+
|
|
32
|
+
\`\`\`bash
|
|
33
|
+
cat SDD/AGENTS.md # leis + fluxo (LEIA PRIMEIRO)
|
|
34
|
+
cat SDD/specs/PLAN.md # a task AGORA
|
|
35
|
+
cat SDD/NEXT.md # regras da stack
|
|
36
|
+
\`\`\`
|
|
37
|
+
|
|
38
|
+
> **Não edite \`SDD/\`** sem pedir ao humano.
|
|
39
|
+
> Atualize com: \`git submodule update --remote --merge SDD\`
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
const log = (m) => console.log(m);
|
|
43
|
+
const sh = (cmd, args, cwd) => execFileSync(cmd, args, { cwd, stdio: "inherit" });
|
|
44
|
+
const has = (cmd) => { try { execFileSync(cmd, ["--version"], { stdio: "ignore" }); return true; } catch { return false; } };
|
|
45
|
+
|
|
46
|
+
function copyDir(from, to) {
|
|
47
|
+
fs.mkdirSync(to, { recursive: true });
|
|
48
|
+
for (const e of fs.readdirSync(from, { withFileTypes: true })) {
|
|
49
|
+
if (e.name === ".git" || e.name === "node_modules") continue;
|
|
50
|
+
const s = path.join(from, e.name), d = path.join(to, e.name);
|
|
51
|
+
if (e.isDirectory()) copyDir(s, d);
|
|
52
|
+
else if (e.isFile()) fs.copyFileSync(s, d);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function installShortcuts(root) {
|
|
57
|
+
for (const rel of SHORTCUTS) {
|
|
58
|
+
const p = path.join(root, rel);
|
|
59
|
+
if (fs.existsSync(p)) { log(` = ${rel} (já existe, preservado)`); continue; }
|
|
60
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
61
|
+
try {
|
|
62
|
+
fs.symlinkSync("SDD/AGENTS.md", p, "file");
|
|
63
|
+
log(` ✓ ${rel} (symlink)`);
|
|
64
|
+
} catch {
|
|
65
|
+
fs.writeFileSync(p, STUB, "utf8");
|
|
66
|
+
log(` ✓ ${rel} (stub)`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function installRulesLocally(root) {
|
|
72
|
+
const here = path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, "$1"));
|
|
73
|
+
const source = path.resolve(here, "..", "..");
|
|
74
|
+
const dest = path.join(root, SDD_DIR);
|
|
75
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
76
|
+
for (const f of RULE_FILES) {
|
|
77
|
+
const s = path.join(source, f);
|
|
78
|
+
if (fs.existsSync(s)) { fs.copyFileSync(s, path.join(dest, f)); log(` ✓ SDD/${f}`); }
|
|
79
|
+
}
|
|
80
|
+
for (const d of RULE_DIRS) {
|
|
81
|
+
const s = path.join(source, d);
|
|
82
|
+
if (fs.existsSync(s)) { copyDir(s, path.join(dest, d)); log(` ✓ SDD/${d}/`); }
|
|
83
|
+
}
|
|
84
|
+
return dest;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const args = process.argv.slice(2);
|
|
88
|
+
const useCopy = args.includes("--copy");
|
|
89
|
+
const target = path.resolve(process.cwd(), args.find((a) => !a.startsWith("-")) ?? ".");
|
|
90
|
+
|
|
91
|
+
log(`\n🤖 Instalando o core SDD em: ${target}\n`);
|
|
92
|
+
|
|
93
|
+
if (!fs.existsSync(target)) { console.error("✖ Pasta não existe."); process.exit(1); }
|
|
94
|
+
|
|
95
|
+
if (useCopy) {
|
|
96
|
+
log("📋 Modo: cópia local");
|
|
97
|
+
installRulesLocally(target);
|
|
98
|
+
installShortcuts(target);
|
|
99
|
+
} else {
|
|
100
|
+
if (!has("git")) { console.error("✖ git não encontrado. Use --copy."); process.exit(1); }
|
|
101
|
+
if (fs.existsSync(path.join(target, ".git")) === false) {
|
|
102
|
+
log("! Projeto não é um repositório git. Rode 'git init' antes ou use --copy.");
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
log(`🔗 Modo: git submodule → ${REPO_URL}`);
|
|
106
|
+
sh("git", ["submodule", "add", REPO_URL, SDD_DIR], target);
|
|
107
|
+
installShortcuts(target);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
log(`
|
|
111
|
+
✅ Pronto.
|
|
112
|
+
|
|
113
|
+
Leia agora:
|
|
114
|
+
cat ${SDD_DIR}/AGENTS.md
|
|
115
|
+
cat ${SDD_DIR}/specs/PLAN.md
|
|
116
|
+
|
|
117
|
+
Atualizar regras depois:
|
|
118
|
+
git submodule update --remote --merge ${SDD_DIR}
|
|
119
|
+
`);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createInterface } from "node:readline/promises";
|
|
5
|
+
import { parseArgs, HELP } from "../src/cli.mjs";
|
|
6
|
+
import { scaffold } from "../lib/scaffold.mjs";
|
|
7
|
+
|
|
8
|
+
const pkg = JSON.parse(
|
|
9
|
+
fs.readFileSync(new URL("../package.json", import.meta.url), "utf8"),
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
function ask(question, fallback) {
|
|
13
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
14
|
+
return rl
|
|
15
|
+
.question(`${question} `)
|
|
16
|
+
.then((a) => a.trim() || fallback)
|
|
17
|
+
.finally(() => rl.close());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function main() {
|
|
21
|
+
let opts;
|
|
22
|
+
try {
|
|
23
|
+
opts = parseArgs(process.argv.slice(2));
|
|
24
|
+
} catch (err) {
|
|
25
|
+
console.error(`\n✖ ${err.message}\n`);
|
|
26
|
+
console.error(HELP);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (opts.help) {
|
|
31
|
+
console.log(HELP);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (opts.version) {
|
|
35
|
+
console.log(pkg.version);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log("\n🤖 create-sdd-ai-stack — Spec-Driven Development para agentes de IA\n");
|
|
40
|
+
|
|
41
|
+
const name = opts.name ?? (await ask("Nome do projeto:", "meu-app"));
|
|
42
|
+
const target = path.resolve(process.cwd(), name);
|
|
43
|
+
const usingSubmodule = Boolean(opts.submodule);
|
|
44
|
+
|
|
45
|
+
console.log(`
|
|
46
|
+
📁 projeto: ${name}
|
|
47
|
+
📦 template: ${opts.template === "none" ? "(nenhum — só regras)" : opts.template}
|
|
48
|
+
🧠 regras: ${usingSubmodule ? `submodule ${opts.submodule}` : "copiadas para ./SDD"}
|
|
49
|
+
`);
|
|
50
|
+
|
|
51
|
+
if (process.stdin.isTTY) {
|
|
52
|
+
const answer = await ask("Criar agora? [Y/n]", "Y");
|
|
53
|
+
if (!/^y(es)?$/i.test(answer)) {
|
|
54
|
+
console.log("\nCancelado. Nada foi criado.\n");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const summary = scaffold({
|
|
61
|
+
target,
|
|
62
|
+
template: opts.template,
|
|
63
|
+
install: opts.install,
|
|
64
|
+
git: opts.git,
|
|
65
|
+
submodule: opts.submodule,
|
|
66
|
+
shortcutMode: opts.shortcutMode,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
console.log(`
|
|
70
|
+
✅ Projeto criado em: ${summary.target}
|
|
71
|
+
|
|
72
|
+
Próximos passos:
|
|
73
|
+
cd ${name}
|
|
74
|
+
${opts.install ? "" : " npm install\n"}${opts.git ? "" : " git init && git add -A && git commit -m \"chore: scaffold inicial\"\n"}
|
|
75
|
+
npm run dev
|
|
76
|
+
|
|
77
|
+
⚠️ LEIA ANTES DE CODAR:
|
|
78
|
+
cat SDD/AGENTS.md # leis do agente
|
|
79
|
+
cat SDD/specs/PLAN.md # a task AGORA
|
|
80
|
+
cat SDD/NEXT.md # regras da stack (Next.js 16)
|
|
81
|
+
|
|
82
|
+
Atualizar as regras depois (se submodule):
|
|
83
|
+
git submodule update --remote --merge SDD
|
|
84
|
+
|
|
85
|
+
Docs: ${pkg.homepage}
|
|
86
|
+
`);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
console.error(`\n✖ Falhou: ${err.message}\n`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
main();
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# CHANGELOG
|
|
2
|
+
|
|
3
|
+
Todas as mudanças relevantes deste template. Formato baseado em
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/); versionamento por
|
|
5
|
+
[SemVer](https://semver.org/lang/pt-BR/).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## [0.1.17] — 2026-09-29
|
|
10
|
+
|
|
11
|
+
> **Primeira versão publicada.** O projeto segue em `0.x` de propósito: a API de
|
|
12
|
+
> regras e o template ainda vão mudar com base no uso real. `0.y.z` comunica
|
|
13
|
+
> isso sem versionar breaking changes a cada duas semanas.
|
|
14
|
+
|
|
15
|
+
### 🔒 Publicação no npm
|
|
16
|
+
|
|
17
|
+
- `.github/workflows/release.yml` — publica no push de tag `v*` (automático), com
|
|
18
|
+
`workflow_dispatch` para disparo manual e `dry_run` para validar sem queimar versão
|
|
19
|
+
- Secret do repositório: **`NPM_TOKEN`** (mapeado para `NODE_AUTH_TOKEN` pelo npm)
|
|
20
|
+
- `.npmrc` commitado apenas com `${NODE_AUTH_TOKEN}` — **nenhum token em arquivo**
|
|
21
|
+
- `publishConfig`: `access: public` + `provenance` (pacote assinado via GitHub OIDC)
|
|
22
|
+
- Guards antes do publish: `npm test` · links da doc · tag `vX.Y.Z` == `version` ·
|
|
23
|
+
10 arquivos essenciais no tarball · `concurrency: release-npm`
|
|
24
|
+
- `exports` no `package.json` (consumo programático: `import { scaffold } from "create-sdd-ai-stack"`)
|
|
25
|
+
- `docs/RELEASE.md` — passo a passo, troubleshooting e a alternativa sem token (Trusted Publishing/OIDC)
|
|
26
|
+
- Scripts: `version:patch|minor|major`, `check:docs`, `check:pack`
|
|
27
|
+
(o `npm publish` manual saiu de propósito: **um caminho só**, o do CI)
|
|
28
|
+
|
|
29
|
+
### 🐛 `.gitignore` do template não chegava no pacote
|
|
30
|
+
|
|
31
|
+
O npm **nunca** empacota arquivo chamado `.gitignore` (é um dos default-ignore dele).
|
|
32
|
+
O app gerado saía **sem `.gitignore`** — ou seja, `.env.local`, `.next/` e
|
|
33
|
+
`node_modules/` podiam ser commitados por acidente.
|
|
34
|
+
|
|
35
|
+
A negação `!template/**/.gitignore` no campo `files` **não resolve** (npm exclui por
|
|
36
|
+
padrão, antes de aplicar o allowlist). Solução: o template guarda como `gitignore`
|
|
37
|
+
(sem ponto, que o npm empacota normalmente) e `installTemplate` renomeia para
|
|
38
|
+
`.gitignore` ao copiar — fonte única, sem duplicar conteúdo.
|
|
39
|
+
|
|
40
|
+
`restoreGitignore()` lança erro se o arquivo faltar, então o app **nunca** sai sem
|
|
41
|
+
proteção de `.env`.
|
|
42
|
+
|
|
43
|
+
### 📈 Cobertura de teste
|
|
44
|
+
|
|
45
|
+
- 20 → **27 testes** (symlink resolvendo o conteúdo certo · `.gitignore` no app gerado ·
|
|
46
|
+
provenance fora do `publishConfig` · `--provenance` explícito no CI ·
|
|
47
|
+
`NODE_AUTH_TOKEN` ausente do passo Publica · `_authToken` fora do `.npmrc` do projeto ·
|
|
48
|
+
token injetado via `GITHUB_ENV`)
|
|
49
|
+
|
|
50
|
+
### 🐛 `.npmrc` do projeto zerava a autenticação (404 na primeira publicação)
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
npm error code E404
|
|
54
|
+
npm error 404 Not Found - PUT https://registry.npmjs.org/create-sdd-ai-stack - Not found
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Não era o pacote ausente** — o `npm publish` cria o pacote sozinho na primeira vez.
|
|
58
|
+
O 404 no PUT significa que o registry não reconheceu o usuário como autorizado.
|
|
59
|
+
|
|
60
|
+
Causa: o `.npmrc` do projeto declarava
|
|
61
|
+
`//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}`. O npm lê os arquivos nesta
|
|
62
|
+
ordem — **projeto > usuário > global** — então esse placeholder **sombreava** o token
|
|
63
|
+
real do `~/.npmrc`. Com a variável vazia, o token efetivo ficava vazio:
|
|
64
|
+
|
|
65
|
+
| Comando | Antes | Depois |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| `npm whoami` | `401 Unauthorized` | `marcelinosandroni` |
|
|
68
|
+
| `npm publish` (1ª vez) | `404 Not Found` (PUT) | cria o pacote |
|
|
69
|
+
|
|
70
|
+
O mesmo defeito atingia o caminho por token **no CI**, porque o `.npmrc` do repositório
|
|
71
|
+
também é copiado para o runner e tem prioridade sobre o `~/.npmrc` gerado pelo
|
|
72
|
+
`setup-node`.
|
|
73
|
+
|
|
74
|
+
Correções:
|
|
75
|
+
- `.npmrc` do projeto ficou só com `registry=` (o comentário no arquivo explica por quê)
|
|
76
|
+
- CI passou a injetar `NODE_AUTH_TOKEN` via `$GITHUB_ENV` em vez de escrever token em
|
|
77
|
+
arquivo — assim o OIDC continua engatando quando não há secret
|
|
78
|
+
- três testes de regressão, todos verificados reintroduzindo o bug de propósito
|
|
79
|
+
|
|
80
|
+
### 🐛 `publishConfig.provenance` quebrava o publish local
|
|
81
|
+
|
|
82
|
+
Com 2FA ligado, o bootstrap local do pacote falhou:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
npm error code EUSAGE
|
|
86
|
+
npm error Automatic provenance generation not supported for provider: null
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
A causa era o **nosso** `package.json`: `publishConfig.provenance: true`.
|
|
90
|
+
|
|
91
|
+
O npm lê `publishConfig` **com prioridade sobre flag de CLI e sobre variável de
|
|
92
|
+
ambiente** — então `--provenance=false` e `NPM_CONFIG_PROVENANCE=false` **não resolvem**.
|
|
93
|
+
O `publishConfig` vence os dois, e o provenance passou a ser exigido também no publish
|
|
94
|
+
local, onde não existe provedor OIDC.
|
|
95
|
+
|
|
96
|
+
Correção: `provenance` saiu do `publishConfig` (ficou só `access: public`) e passou a ser
|
|
97
|
+
controlado **por invocação** — o workflow de release passa `--provenance` explicitamente,
|
|
98
|
+
o publish local não passa nada.
|
|
99
|
+
|
|
100
|
+
Três testes de regressão agora travam esse comportamento (o terceiro foi verificado
|
|
101
|
+
reintroduzindo o bug de propósito):
|
|
102
|
+
- `provenance` não pode estar no `publishConfig`
|
|
103
|
+
- o passo `Publica` passa `--provenance`
|
|
104
|
+
- o passo `Publica` não define `NODE_AUTH_TOKEN` (senão o OIDC não engata)
|
|
105
|
+
|
|
106
|
+
### 🔐 Autenticação: 2FA no npm quebrou o publish por token
|
|
107
|
+
|
|
108
|
+
Com 2FA ligado na conta, `npm publish` por token passa a exigir **OTP do autenticador**,
|
|
109
|
+
que o CI não tem como digitar:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
npm error code EOTP
|
|
113
|
+
npm error This operation requires a one-time password from your authenticator.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Três saídas, e o workflow agora suporta as duas principais:
|
|
117
|
+
|
|
118
|
+
- **Bootstrap local + Trusted Publishing (OIDC)** — publica a primeira versão da máquina
|
|
119
|
+
com `--otp`, configura o publisher no npmjs.com, **apaga o token**. Da frente em diante
|
|
120
|
+
o CI publica sem credencial nenhuma. É o caminho recomendado.
|
|
121
|
+
- **Token com "Bypass 2FA"** — funciona hoje, mas o npm avisa que publicação direta com
|
|
122
|
+
token granular **será removida em janeiro de 2027**, e há bug aberto onde o bypass é
|
|
123
|
+
ignorado pelo npm 11.x ([npm/cli#9268](https://github.com/npm/cli/issues/9268)).
|
|
124
|
+
- **Token stage-only** — o CI sobe a versão, um maintainer aprova com 2FA.
|
|
125
|
+
|
|
126
|
+
Mudanças no workflow:
|
|
127
|
+
|
|
128
|
+
- `npm install -g npm@latest` no job de publish — **OIDC exige npm ≥ 11.5.1** e o Node 22
|
|
129
|
+
do runner do GitHub vem com npm 10.x. Sem isso o OIDC nunca engata.
|
|
130
|
+
- `NODE_AUTH_TOKEN` **removido** do passo `Publica`. O npm só usa OIDC quando o auth está
|
|
131
|
+
**ausente**; com a variável no ambiente, ele ignora o OIDC e volta a falhar por token.
|
|
132
|
+
- Step `Autentica` virou condicional: com `NPM_TOKEN` escreve o token; sem ele, **não
|
|
133
|
+
escreve nada** no `.npmrc` e deixa o npm escolher o OIDC sozinho.
|
|
134
|
+
|
|
135
|
+
### 🎯 Objetivo
|
|
136
|
+
Reestruturar o core de regras para **Next.js 16 como stack padrão** (antes: React/Vite + Node),
|
|
137
|
+
tornar o repositório instalável como **git submodule em `SDD/`**, e transformar em **pacote npm
|
|
138
|
+
CLI** (`npx create-sdd-ai-stack`) com um template Next.js funcional embutido.
|
|
139
|
+
|
|
140
|
+
### ✨ Adicionado
|
|
141
|
+
|
|
142
|
+
**Regras**
|
|
143
|
+
- `NEXT.md` — 11 seções de regras do Next.js 16 (Server-First, Data Layer, Server Actions,
|
|
144
|
+
Cache Components, Segurança, Route Handlers, Forms, Metadata, Performance, armadilhas)
|
|
145
|
+
- `APP-STACK.md` — ponteiro de stack do app (qual doc de regra ler)
|
|
146
|
+
- `stacks/` — pasta nova com regras por linguagem/ferramenta:
|
|
147
|
+
`typescript.md`, `tailwind.md`, `shadcn.md`, `testing.md`, `database.md`, `ai.md`, `git.md`, `ci.md`
|
|
148
|
+
- `specs/history/phases/phase-0-bootstrap.md` — histórico da fase
|
|
149
|
+
|
|
150
|
+
**Template**
|
|
151
|
+
- `template/next/` — projeto Next.js 16 completo e validado:
|
|
152
|
+
App Router com route groups, `cacheComponents`, React Compiler, Turbopack,
|
|
153
|
+
Tailwind v4 com tokens, Biome, Vitest, Playwright, vertical slice de exemplo,
|
|
154
|
+
`proxy.ts` com hardening de headers, `shared/server` com `server-only`
|
|
155
|
+
- `src/app/globals.css` — design system completo como `@theme` do Tailwind v4
|
|
156
|
+
- Testes: 1 unit (3 casos) + 4 E2E prontos
|
|
157
|
+
|
|
158
|
+
**CLI / npm**
|
|
159
|
+
- `create-sdd-ai-stack` — pacote npm com bin (`bin/create-sdd-ai-stack.mjs`)
|
|
160
|
+
- Opções: `--template`, `--rules-only`, `--submodule [url]`, `--install/--no-install`,
|
|
161
|
+
`--git/--no-git`, `--shortcuts auto|stub|symlink`, `-y`, `--help`, `--version`
|
|
162
|
+
- Atalhos criados na raiz: `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.cursorrules`,
|
|
163
|
+
`.windsurfrules`, `.github/copilot-instructions.md`, `.clinerules`
|
|
164
|
+
- `npm run release` / `release:minor` / `release:major` (testa → versiona com tag → publica)
|
|
165
|
+
|
|
166
|
+
**SKILLS**
|
|
167
|
+
- `SKILLS/create-feature/` — cria vertical slice com a estrutura padrão
|
|
168
|
+
- `SKILLS/install-submodule/` — instala as regras em projeto existente
|
|
169
|
+
|
|
170
|
+
**Testes da própria lib**
|
|
171
|
+
- `tests/scaffold.test.mjs` — 17 testes cobrindo `parseArgs`, `installRules`,
|
|
172
|
+
`installShortcuts` e `scaffold`
|
|
173
|
+
|
|
174
|
+
### 🔄 Alterado
|
|
175
|
+
|
|
176
|
+
- `AGENTS.md` — Next-first; documenta que vive em `SDD/` quando instalado
|
|
177
|
+
- `DESIGN.md` — substituído pelo design system **Executive Engineering**
|
|
178
|
+
(tokens, tipografia tripla, grid 12 col/1320px, elevação em tiers, componentes)
|
|
179
|
+
- `ARCHITECTURE.md` — de "monorepo client/server" para Next-first com vertical slices
|
|
180
|
+
- `NODE.md` — de "backend padrão" para **complemento** (worker/cron/fila) com critério de uso
|
|
181
|
+
- `REACT.md` — de "frontend padrão" para **complemento** (Server-First)
|
|
182
|
+
- `APP.md` — ponteiro para `NEXT.md` e `stacks/`
|
|
183
|
+
- `specs/PLAN.md` — regra de "exatamente uma task `[-]`" + checklist de entrega
|
|
184
|
+
- `specs/tasks/TASK_TEMPLATE.md` — DoD com os comandos de gate reais
|
|
185
|
+
- `README.md` — reescrito como documentação do pacote npm
|
|
186
|
+
|
|
187
|
+
### 🗑️ Removido
|
|
188
|
+
|
|
189
|
+
- `specs/ROADMAP.md` vazio → reescrito
|
|
190
|
+
- `docs/PRODUCT.md` vazio → mantido como stub de template
|
|
191
|
+
|
|
192
|
+
### 🐛 Correções encontradas pela própria validação
|
|
193
|
+
|
|
194
|
+
1. `error.tsx` sem `"use client"` — quebrava o build
|
|
195
|
+
2. `reactCompiler: true` sem `babel-plugin-react-compiler` — quebrava o build
|
|
196
|
+
3. `biome.json` no schema 1.x — quebrava o lint no Biome 2
|
|
197
|
+
4. `prepare: "husky || true"` — quebrava `npm install` no Windows
|
|
198
|
+
5. `proxy.ts` redirecionando `/` — escondia a landing (achado pelo E2E)
|
|
199
|
+
6. Rota `/` duplicada entre `app/page.tsx` e `(marketing)/page.tsx`
|
|
200
|
+
7. `installShortcuts` retornava `undefined` no modo stub
|
|
201
|
+
8. symlink dos atalhos com caminho relativo quebrado (`path.relative` com caminho não-absoluto) — **achado pelo CI no Linux**, mascarado no Windows pelo fallback pra stub
|
|
202
|
+
9. `cache: npm` no workflow sem lockfile commitado
|
|
203
|
+
10. rota `/` duplicada entre `app/page.tsx` e `(marketing)/page.tsx` (achado pelo E2E)
|
|
204
|
+
11. `.gitignore` do template não empacotado pelo npm (achado conferindo o `npm pack`)
|
|
205
|
+
|
|
206
|
+
> O nº 8 é o melhor argumento pra manter o CI que **revalida o template a cada push**:
|
|
207
|
+
> o teste passava 100% na minha máquina e só quebrou no Linux.
|
|
208
|
+
|
|
209
|
+
[0.1.17]: https://github.com/marcelinosandroni/sdd-ai-stack/releases/tag/v0.1.17
|
package/docs/PLANNING.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# PLANNING
|
|
2
|
+
|
|
3
|
+
> Espaço para **planejamento e refinamento** antes de virar task em [`../specs/PLAN.md`](../specs/PLAN.md).
|
|
4
|
+
> Aqui a gente pensa. No PLAN a gente faz.
|
|
5
|
+
|
|
6
|
+
## 🎯 Como este fluxo funciona
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
IDEIA
|
|
10
|
+
↓
|
|
11
|
+
BACKLOG.md captura cru, sem compromisso
|
|
12
|
+
↓
|
|
13
|
+
REFINAMENTO aqui em PLANNING.md: problema, escopo, decisões, riscos
|
|
14
|
+
↓
|
|
15
|
+
PLAN.md vira task, com micro-passos
|
|
16
|
+
↓
|
|
17
|
+
tasks/TASK-N.M.md vira checklist executável
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 📄 Template de refinamento
|
|
21
|
+
|
|
22
|
+
Crie `docs/planning/AAAA-MM-DD-<slug>.md` com:
|
|
23
|
+
|
|
24
|
+
```markdown
|
|
25
|
+
# Refinamento: [Nome]
|
|
26
|
+
|
|
27
|
+
## 🧠 Problema
|
|
28
|
+
Quem sofre com o quê, hoje. Sem solução — só o problema.
|
|
29
|
+
|
|
30
|
+
## 🎯 Objetivo
|
|
31
|
+
Uma frase. Como saberemos que deu certo (métrica).
|
|
32
|
+
|
|
33
|
+
## 🗺️ Escopo
|
|
34
|
+
- [ ] Inclui:
|
|
35
|
+
- [ ] Não inclui: (o mais importante de preencher)
|
|
36
|
+
|
|
37
|
+
## 🧩 Entidades e invariantes
|
|
38
|
+
[domínio: o que existe e o que nunca pode quebrar]
|
|
39
|
+
|
|
40
|
+
## 🏗️ Decisões de arquitetura
|
|
41
|
+
| Decisão | Alternativa | Por quê |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| [ex] Server Action | Route Handler | sem URL pública, sem boilerplate |
|
|
44
|
+
|
|
45
|
+
## ⚠️ Riscos
|
|
46
|
+
| Risco | Mitigação |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
|
|
49
|
+
## 🪓 Task breakdown
|
|
50
|
+
1. TASK-1.1 — …
|
|
51
|
+
2. TASK-1.2 — …
|
|
52
|
+
|
|
53
|
+
## ✅ Definition of Done
|
|
54
|
+
- [ ] critérios mensuráveis
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 🎨 Artefatos visuais
|
|
58
|
+
|
|
59
|
+
Qualquer coisa que vira imagem (wireframe, fluxo, diagrama de arquitetura) vai em
|
|
60
|
+
`docs/planning/assets/` e é referenciada pelo markdown. Sem commit de binário no
|
|
61
|
+
`specs/` — os specs são texto, para o git diff fazer sentido.
|
|
62
|
+
|
|
63
|
+
## 📌 Regras
|
|
64
|
+
|
|
65
|
+
1. **Refinamento não vira código.** Se tem `diff` no arquivo, virou task.
|
|
66
|
+
2. **Escopo negativo é obrigatório.** "O que NÃO vai entrar" evita 50% das retrabalhadas.
|
|
67
|
+
3. **Toda decisão de arquitetura vira linha no `ARCHITECTURE.md`** quando estabilizar.
|
|
68
|
+
4. **Se a task virar > 1h, quebramos antes de começar** (regra do `AGENTS.md`).
|
package/docs/PRODUCT.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# 📦 PRODUCT
|
|
2
|
+
|
|
3
|
+
> **O que este template entrega, para quem, e o que ele NÃO entrega.**
|
|
4
|
+
> Preencha a seção "Contexto do projeto consumidor" ao usar num projeto real.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 🎯 O problema
|
|
9
|
+
|
|
10
|
+
Agentes de IA (Claude Code, Cursor, Copilot…) falham em projetos sem regra explícita por
|
|
11
|
+
três motivos previsíveis:
|
|
12
|
+
|
|
13
|
+
1. **Contexto demais** — leem 40 arquivos e não concluem nada.
|
|
14
|
+
2. **Sem critério de parada** — voltam a inventar arquitetura e não param.
|
|
15
|
+
3. **Sem memória de processo** — não sabem que estão no meio de uma task.
|
|
16
|
+
|
|
17
|
+
Isto é **Spec-Driven Development aplicado a agentes**: a especificação vira o sistema
|
|
18
|
+
nervoso, e o agente só precisa saber *onde olhar agora*.
|
|
19
|
+
|
|
20
|
+
## 💡 A solução
|
|
21
|
+
|
|
22
|
+
Um pacote com três partes:
|
|
23
|
+
|
|
24
|
+
| Parte | Problema que resolve |
|
|
25
|
+
| --- | --- |
|
|
26
|
+
| **Roteador de regras** | O agente lê `AGENTS.md` → `PLAN.md` → o doc da stack. Contexto mínimo. |
|
|
27
|
+
| **Template de projeto** | Não precisa inventar estrutura. Já vem com Next.js 16 + design system. |
|
|
28
|
+
| **CLI + submodule** | Instalação em 1 comando, atualizável por git. |
|
|
29
|
+
|
|
30
|
+
## 👤 Quem é
|
|
31
|
+
|
|
32
|
+
-_times pequenos/médios com IA como pair programmer
|
|
33
|
+
- Devs que perdem contexto no meio de refatorações longas
|
|
34
|
+
- Times que querem padronizar a entrega entre humanos e agentes
|
|
35
|
+
|
|
36
|
+
## 🧭 Princípios do design
|
|
37
|
+
|
|
38
|
+
| Princípio | Consequência prática |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| **Roteador em tudo** | Todo doc tem "se você está fazendo X, leia §Y" no topo |
|
|
41
|
+
| **Uma task por vez** | `PLAN.md` permite exatamente uma task `[-]` |
|
|
42
|
+
| **Prova de vida** | Não marca `[x]` sem output verde do terminal colado |
|
|
43
|
+
| **Vertical slices** | Um requisito = uma pasta |
|
|
44
|
+
| **Tokens em um lugar só** | Design muda no `@theme`, nunca no componente |
|
|
45
|
+
| **Doc perto do que edita** | `error.tsx` sem `"use client"` = build quebrado. proximity paga. |
|
|
46
|
+
|
|
47
|
+
## 🚫 O que NÃO entregamos
|
|
48
|
+
|
|
49
|
+
- Conta de IA, prompts de modelo, gateway de LLM
|
|
50
|
+
- Autenticação pronta (o ponto de extensão é `src/shared/server/auth.ts`)
|
|
51
|
+
- Banco de dados configurado (o slice de exemplo usa repositório em memória)
|
|
52
|
+
- Monorepo com vários pacotes
|
|
53
|
+
- CI/CD pronto (as regras estão em `stacks/ci.md`)
|
|
54
|
+
|
|
55
|
+
> O que não entregamos é **deliberado**: o template que resolve um problema por vez
|
|
56
|
+
> é mais útil que o que resolve tudo mal.
|
|
57
|
+
|
|
58
|
+
## 🧪 Como sabemos que funciona
|
|
59
|
+
|
|
60
|
+
O template em `template/next/` passa em `typecheck`, `lint`, `test`, `test:e2e` e `build`.
|
|
61
|
+
A própria CLI tem 17 testes. Bugs reais já foram encontrados por essa validação
|
|
62
|
+
(veja [`CHANGELOG.md`](./CHANGELOG.md) § 🐛).
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 📝 Contexto do projeto consumidor
|
|
67
|
+
|
|
68
|
+
> Preencha ao instalar este template num projeto real.
|
|
69
|
+
|
|
70
|
+
**Nome:** [SEU APP]
|
|
71
|
+
**O que faz:** [1 linha]
|
|
72
|
+
**Usuário final:** [quem usa]
|
|
73
|
+
**Stack ativa:** Next.js 16 · [outras]
|
|
74
|
+
**Integrações:** [Stripe, OpenAI, …]
|
|
75
|
+
**Regras críticas de negócio:**
|
|
76
|
+
- [ex: usuário free gera no máximo 5 vídeos/dia]
|