onboarding-kit 0.0.0-stage → 0.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.
- package/README.md +89 -2
- package/dist/adapters/nest.d.ts +15 -0
- package/dist/adapters/nest.js +53 -0
- package/dist/adapters/next.d.ts +23 -0
- package/dist/adapters/next.js +41 -0
- package/dist/adapters/node.d.ts +10 -0
- package/dist/adapters/node.js +33 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +272 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +57 -0
- package/dist/load.d.ts +19 -0
- package/dist/load.js +59 -0
- package/dist/render/assets.d.ts +4 -0
- package/dist/render/assets.js +381 -0
- package/dist/render/copy.d.ts +132 -0
- package/dist/render/copy.js +290 -0
- package/dist/render/html.d.ts +12 -0
- package/dist/render/html.js +74 -0
- package/dist/render/index.d.ts +5 -0
- package/dist/render/index.js +167 -0
- package/dist/render/slides.d.ts +19 -0
- package/dist/render/slides.js +232 -0
- package/dist/template.d.ts +9 -0
- package/dist/template.js +88 -0
- package/dist/types.d.ts +150 -0
- package/dist/types.js +6 -0
- package/dist/validate.d.ts +17 -0
- package/dist/validate.js +224 -0
- package/package.json +69 -4
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.escapeHtml = void 0;
|
|
4
|
+
exports.renderMissingPage = renderMissingPage;
|
|
5
|
+
exports.renderErrorPage = renderErrorPage;
|
|
6
|
+
exports.renderOnboarding = renderOnboarding;
|
|
7
|
+
const load_1 = require("../load");
|
|
8
|
+
const template_1 = require("../template");
|
|
9
|
+
const assets_1 = require("./assets");
|
|
10
|
+
const copy_1 = require("./copy");
|
|
11
|
+
const html_1 = require("./html");
|
|
12
|
+
const slides_1 = require("./slides");
|
|
13
|
+
var html_2 = require("./html");
|
|
14
|
+
Object.defineProperty(exports, "escapeHtml", { enumerable: true, get: function () { return html_2.escapeHtml; } });
|
|
15
|
+
const ICON_GRID = '<svg viewBox="0 0 20 20" aria-hidden="true"><path fill="currentColor" d="M3 3h5.5v5.5H3zM11.5 3H17v5.5h-5.5zM3 11.5h5.5V17H3zM11.5 11.5H17V17h-5.5z" opacity=".9"/></svg>';
|
|
16
|
+
const ICON_THEME = '<svg viewBox="0 0 20 20" aria-hidden="true"><circle cx="10" cy="10" r="7" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M10 3a7 7 0 0 1 0 14z" fill="currentColor"/></svg>';
|
|
17
|
+
function shell(lang, title, body, opts = {}) {
|
|
18
|
+
return `<!doctype html>
|
|
19
|
+
<html lang="${(0, html_1.escapeHtml)(lang)}">
|
|
20
|
+
<head>
|
|
21
|
+
<meta charset="utf-8">
|
|
22
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
23
|
+
<meta name="robots" content="noindex">
|
|
24
|
+
<title>${(0, html_1.escapeHtml)(title)}</title>
|
|
25
|
+
${assets_1.FONTS}
|
|
26
|
+
<style>${assets_1.CSS}</style>
|
|
27
|
+
</head>
|
|
28
|
+
<body${opts.solo ? ' class="solo"' : ""}>
|
|
29
|
+
${body}
|
|
30
|
+
<script>${assets_1.JS}</script>
|
|
31
|
+
${opts.mermaid ? `<script src="${assets_1.MERMAID_URL}" onload="window.__okMermaid&&window.__okMermaid()"></script>` : ""}
|
|
32
|
+
</body>
|
|
33
|
+
</html>`;
|
|
34
|
+
}
|
|
35
|
+
function soloPage(lang, t, heading, body, code, isError = false) {
|
|
36
|
+
const block = isError
|
|
37
|
+
? `<pre>${(0, html_1.escapeHtml)(code)}</pre>`
|
|
38
|
+
: `<div class="prompt"><pre>${(0, html_1.escapeHtml)(code)}</pre><button type="button" class="copy" data-copy="${(0, html_1.escapeHtml)(code)}" data-done="${(0, html_1.escapeHtml)(t.setup.copied)}">${t.setup.copy}</button></div>`;
|
|
39
|
+
return shell(lang, "Onboarding", `<div class="solo-wrap"><div class="solo-card"><p class="kicker"><span class="num">👋</span>onboarding-kit</p><h1>${(0, html_1.escapeHtml)(heading)}</h1>${body ? `<p>${(0, html_1.escapeHtml)(body)}</p>` : ""}${block}</div></div>`, { solo: true });
|
|
40
|
+
}
|
|
41
|
+
function renderMissingPage() {
|
|
42
|
+
const t = (0, copy_1.copyFor)("pt-BR");
|
|
43
|
+
return soloPage("pt-BR", t, t.missing.title, t.missing.body, "npx onboarding-kit init");
|
|
44
|
+
}
|
|
45
|
+
function renderErrorPage(message) {
|
|
46
|
+
const t = (0, copy_1.copyFor)("pt-BR");
|
|
47
|
+
return soloPage("pt-BR", t, t.invalid.title, "", message, true);
|
|
48
|
+
}
|
|
49
|
+
function welcomeSlide(data, t, chapters, minutes) {
|
|
50
|
+
const p = data.project;
|
|
51
|
+
const name = p?.name?.trim() || "Onboarding";
|
|
52
|
+
const generated = data.meta?.generatedAt ? new Date(data.meta.generatedAt) : undefined;
|
|
53
|
+
const base = (0, html_1.repoBase)(data);
|
|
54
|
+
const meta = [
|
|
55
|
+
p?.status ? `<span class="badge">${(0, html_1.escapeHtml)(p.status)}</span>` : "",
|
|
56
|
+
`<span>⏱ ${minutes} ${t.ui.minutes}</span>`,
|
|
57
|
+
generated && !Number.isNaN(generated.getTime())
|
|
58
|
+
? `<span>${t.ui.generated} ${(0, html_1.escapeHtml)(generated.toLocaleDateString(data.meta.language || "pt-BR"))}${data.meta.generatedBy ? ` ${t.ui.by} ${(0, html_1.escapeHtml)(data.meta.generatedBy)}` : ""}</span>`
|
|
59
|
+
: "",
|
|
60
|
+
base ? `<a href="${(0, html_1.escapeHtml)(base)}" target="_blank" rel="noopener">${(0, html_1.escapeHtml)(base.replace(/^https?:\/\//, ""))}</a>` : "",
|
|
61
|
+
].filter(Boolean);
|
|
62
|
+
const journey = chapters
|
|
63
|
+
.filter((c) => c.id !== "welcome" && c.id !== "finale")
|
|
64
|
+
.map((c) => `<li><button type="button" data-go="${c.start}" style="--ch:${copy_1.CHAPTER_HUES[c.id]}"><span class="n">${c.num}</span>${(0, html_1.escapeHtml)(c.name)}</button></li>`)
|
|
65
|
+
.join("");
|
|
66
|
+
return {
|
|
67
|
+
chapter: "welcome",
|
|
68
|
+
kind: "welcome",
|
|
69
|
+
title: t.chapters.welcome,
|
|
70
|
+
html: `<div class="welcome">
|
|
71
|
+
<p class="hi"><span class="wave" aria-hidden="true">👋</span>${(0, html_1.escapeHtml)(t.welcome.hi)}</p>
|
|
72
|
+
<h1 class="title">${(0, html_1.escapeHtml)(t.welcome.title("")).trim()} <span class="grad">${(0, html_1.escapeHtml)(name)}</span></h1>
|
|
73
|
+
${p?.tagline ? `<p class="tagline">${(0, html_1.text)(p.tagline)}</p>` : ""}
|
|
74
|
+
<p class="lede">${(0, html_1.escapeHtml)(t.welcome.intro(minutes))}</p>
|
|
75
|
+
<button type="button" class="cta" data-next>${(0, html_1.escapeHtml)(t.welcome.start)} <span class="arrow" aria-hidden="true">→</span></button>
|
|
76
|
+
<div class="meta">${meta.join("")}</div>
|
|
77
|
+
${journey ? `<div class="journey-wrap"><p class="mini-label">${(0, html_1.escapeHtml)(t.welcome.journey)}</p><ul class="journey">${journey}</ul></div>` : ""}
|
|
78
|
+
</div>`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function finaleSlide(t, chapters, hasTasks) {
|
|
82
|
+
const recap = chapters
|
|
83
|
+
.filter((c) => c.id !== "welcome" && c.id !== "finale")
|
|
84
|
+
.map((c) => `<li style="--ch:${copy_1.CHAPTER_HUES[c.id]}">${(0, html_1.escapeHtml)(c.name)}</li>`)
|
|
85
|
+
.join("");
|
|
86
|
+
return {
|
|
87
|
+
chapter: "finale",
|
|
88
|
+
kind: "finale",
|
|
89
|
+
title: t.finale.title,
|
|
90
|
+
html: `<div class="finale">
|
|
91
|
+
<span class="party" aria-hidden="true">🎉</span>
|
|
92
|
+
<h1 class="title">${(0, html_1.escapeHtml)(t.finale.title)}</h1>
|
|
93
|
+
<p class="lede">${(0, html_1.escapeHtml)(t.finale.lede)}</p>
|
|
94
|
+
${recap ? `<p class="mini-label">${(0, html_1.escapeHtml)(t.finale.recap)}</p><ul class="recap">${recap}</ul>` : ""}
|
|
95
|
+
${hasTasks ? `<p class="task-count" data-task-count data-template="${(0, html_1.escapeHtml)(t.finale.tasks)}"></p>` : ""}
|
|
96
|
+
<div class="actions"><button type="button" class="cta" data-go="0">↺ ${(0, html_1.escapeHtml)(t.finale.restart)}</button><button type="button" class="ghost" data-overview>${(0, html_1.escapeHtml)(t.finale.index)}</button></div>
|
|
97
|
+
</div>`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function renderOnboarding(data) {
|
|
101
|
+
const lang = data.meta?.language || "pt-BR";
|
|
102
|
+
const t = (0, copy_1.copyFor)(lang);
|
|
103
|
+
const name = data.project?.name?.trim() || "Onboarding";
|
|
104
|
+
if (data.meta?.status !== "filled") {
|
|
105
|
+
return soloPage(lang, t, t.pending.title, t.pending.body, (0, template_1.aiPrompt)());
|
|
106
|
+
}
|
|
107
|
+
// 1. capítulos do meio, numerados só quando têm conteúdo
|
|
108
|
+
const middle = [];
|
|
109
|
+
let num = 1;
|
|
110
|
+
for (const [id, build] of slides_1.CHAPTERS) {
|
|
111
|
+
const slides = build({ data, t, num });
|
|
112
|
+
if (slides.length) {
|
|
113
|
+
middle.push({ id, slides });
|
|
114
|
+
num++;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// 2. índice de capítulos com a posição de cada um no deck
|
|
118
|
+
const chapters = [];
|
|
119
|
+
let cursor = 1; // slide 0 é a boas-vindas
|
|
120
|
+
chapters.push({ id: "welcome", num: 0, name: t.chapters.welcome, start: 0, count: 1 });
|
|
121
|
+
middle.forEach((c, i) => {
|
|
122
|
+
chapters.push({ id: c.id, num: i + 1, name: t.chapters[c.id], start: cursor, count: c.slides.length });
|
|
123
|
+
cursor += c.slides.length;
|
|
124
|
+
});
|
|
125
|
+
chapters.push({ id: "finale", num: middle.length + 1, name: t.chapters.finale, start: cursor, count: 1 });
|
|
126
|
+
const total = cursor + 1;
|
|
127
|
+
const minutes = Math.max(3, Math.round(total * 0.4));
|
|
128
|
+
const hasTasks = middle.some((c) => c.id === "mission");
|
|
129
|
+
const slides = [welcomeSlide(data, t, chapters, minutes), ...middle.flatMap((c) => c.slides), finaleSlide(t, chapters, hasTasks)];
|
|
130
|
+
const slideHtml = slides
|
|
131
|
+
.map((s, i) => `<section class="slide" data-kind="${(0, html_1.escapeHtml)(s.kind ?? s.chapter)}" data-chapter="${s.chapter}" data-chapter-name="${(0, html_1.escapeHtml)(t.chapters[s.chapter])}" style="--h:${copy_1.CHAPTER_HUES[s.chapter]}" aria-roledescription="slide" aria-label="${i + 1} / ${total}" aria-hidden="true"><div class="slide-inner">${s.html}</div></section>`)
|
|
132
|
+
.join("\n");
|
|
133
|
+
const segments = chapters
|
|
134
|
+
.map((c) => `<button type="button" class="seg" data-go="${c.start}" data-start="${c.start}" data-count="${c.count}" style="--h:${copy_1.CHAPTER_HUES[c.id]};--w:${c.count}" title="${(0, html_1.escapeHtml)(c.name)}" aria-label="${(0, html_1.escapeHtml)(c.name)}"><span></span></button>`)
|
|
135
|
+
.join("");
|
|
136
|
+
let index = 0;
|
|
137
|
+
const overview = chapters
|
|
138
|
+
.map((c) => {
|
|
139
|
+
const items = slides
|
|
140
|
+
.slice(c.start, c.start + c.count)
|
|
141
|
+
.map((s) => `<button type="button" data-go="${index++}">${(0, html_1.escapeHtml)(s.title)}</button>`)
|
|
142
|
+
.join("");
|
|
143
|
+
return `<div class="ov-chapter" style="--ch:${copy_1.CHAPTER_HUES[c.id]}"><h3><span class="n">${c.id === "welcome" ? "👋" : c.id === "finale" ? "🎉" : c.num}</span>${(0, html_1.escapeHtml)(c.name)}</h3>${items}</div>`;
|
|
144
|
+
})
|
|
145
|
+
.join("");
|
|
146
|
+
const body = `<div class="deck">
|
|
147
|
+
<header class="topbar">
|
|
148
|
+
<span class="brand">${(0, html_1.escapeHtml)(name)}</span>
|
|
149
|
+
<nav class="segments" aria-label="${(0, html_1.escapeHtml)(t.ui.chapters)}">${segments}</nav>
|
|
150
|
+
<button type="button" class="icon-btn" data-overview title="${(0, html_1.escapeHtml)(t.ui.overview)} (O)" aria-label="${(0, html_1.escapeHtml)(t.ui.overview)}">${ICON_GRID}</button>
|
|
151
|
+
<button type="button" class="icon-btn" data-theme-toggle title="${(0, html_1.escapeHtml)(t.ui.theme)}" aria-label="${(0, html_1.escapeHtml)(t.ui.theme)}">${ICON_THEME}</button>
|
|
152
|
+
</header>
|
|
153
|
+
<main class="stage" id="ok-stage">
|
|
154
|
+
${slideHtml}
|
|
155
|
+
</main>
|
|
156
|
+
<footer class="controls">
|
|
157
|
+
<button type="button" class="nav-btn" data-prev aria-label="${(0, html_1.escapeHtml)(t.ui.prev)}">← <span class="txt">${(0, html_1.escapeHtml)(t.ui.prev)}</span></button>
|
|
158
|
+
<div class="where" aria-live="polite"><strong id="ok-chapter"></strong><span><span id="ok-count"></span><span class="hint" id="ok-hint"> · ${(0, html_1.escapeHtml)(t.ui.keys)}</span></span></div>
|
|
159
|
+
<button type="button" class="nav-btn primary" data-next aria-label="${(0, html_1.escapeHtml)(t.ui.next)}"><span class="txt">${(0, html_1.escapeHtml)(t.ui.next)}</span> →</button>
|
|
160
|
+
</footer>
|
|
161
|
+
</div>
|
|
162
|
+
<div class="overview" id="ok-overview" aria-hidden="true" role="dialog" aria-label="${(0, html_1.escapeHtml)(t.ui.overview)}">
|
|
163
|
+
<div class="overview-inner"><div class="overview-head"><h2>${(0, html_1.escapeHtml)(t.ui.overview)}</h2><button type="button" class="icon-btn" data-close aria-label="${(0, html_1.escapeHtml)(t.ui.close)}">✕</button></div><div class="ov-grid">${overview}</div></div>
|
|
164
|
+
</div>
|
|
165
|
+
<div class="toast" id="ok-toast" data-template="${(0, html_1.escapeHtml)(t.ui.resume)}" role="status"><span></span><button type="button" class="yes" data-resume="0">${(0, html_1.escapeHtml)(t.ui.resumeYes)}</button><button type="button" data-dismiss>${(0, html_1.escapeHtml)(t.ui.resumeNo)}</button></div>`;
|
|
166
|
+
return shell(lang, `${name} · Onboarding`, body, { mermaid: slides.some((s) => s.kind === "diagram") && !(0, load_1.isBlank)(data.architecture?.diagram) });
|
|
167
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { OnboardingData } from "../types";
|
|
2
|
+
import type { ChapterId, Copy } from "./copy";
|
|
3
|
+
export interface Slide {
|
|
4
|
+
chapter: ChapterId;
|
|
5
|
+
/** Título curto, usado no índice. */
|
|
6
|
+
title: string;
|
|
7
|
+
html: string;
|
|
8
|
+
kind?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface Ctx {
|
|
11
|
+
data: OnboardingData;
|
|
12
|
+
t: Copy;
|
|
13
|
+
/** Número do capítulo (só capítulos com conteúdo contam). */
|
|
14
|
+
num: number;
|
|
15
|
+
}
|
|
16
|
+
type Builder = (ctx: Ctx) => Slide[];
|
|
17
|
+
/** Capítulos do meio, em ordem narrativa: do porquê ao código. */
|
|
18
|
+
export declare const CHAPTERS: [ChapterId, Builder][];
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CHAPTERS = void 0;
|
|
4
|
+
const load_1 = require("../load");
|
|
5
|
+
const html_1 = require("./html");
|
|
6
|
+
function head(ctx, chapter, title, lede, page) {
|
|
7
|
+
const cont = page && page[1] > 1 ? ` <span class="cont">${page[0]}/${page[1]}</span>` : "";
|
|
8
|
+
return `<p class="kicker"><span class="num">${ctx.num}</span>${(0, html_1.escapeHtml)(ctx.t.chapters[chapter])}</p>
|
|
9
|
+
<h1 class="title">${(0, html_1.escapeHtml)(title)}${cont}</h1>${lede && (!page || page[0] === 1) ? `<p class="lede">${(0, html_1.text)(lede)}</p>` : ""}`;
|
|
10
|
+
}
|
|
11
|
+
/** Monta uma sequência de slides paginando uma lista. */
|
|
12
|
+
function paged(ctx, chapter, items, size, title, lede, body, kind) {
|
|
13
|
+
const pages = (0, html_1.chunk)(items, size);
|
|
14
|
+
return pages.map((page, i) => ({
|
|
15
|
+
chapter,
|
|
16
|
+
kind,
|
|
17
|
+
title: pages.length > 1 ? `${title} (${i + 1}/${pages.length})` : title,
|
|
18
|
+
html: `${head(ctx, chapter, title, lede, [i + 1, pages.length])}${body(page, i * size)}`,
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
/* ───────────────────────── capítulos ───────────────────────── */
|
|
22
|
+
const why = (ctx) => {
|
|
23
|
+
const p = ctx.data.project;
|
|
24
|
+
if (!p || (0, load_1.isBlank)(p.purpose))
|
|
25
|
+
return [];
|
|
26
|
+
const { t } = ctx;
|
|
27
|
+
const cards = [
|
|
28
|
+
!(0, load_1.isBlank)(p.audience) ? `<div class="card"><span class="label">${t.why.audience}</span><p class="strong">${(0, html_1.text)(p.audience)}</p></div>` : "",
|
|
29
|
+
!(0, load_1.isBlank)(p.status) ? `<div class="card"><span class="label">${t.why.status}</span><p class="strong">${(0, html_1.text)(p.status)}</p></div>` : "",
|
|
30
|
+
].join("");
|
|
31
|
+
return [
|
|
32
|
+
{
|
|
33
|
+
chapter: "why",
|
|
34
|
+
title: t.why.title,
|
|
35
|
+
html: `${head(ctx, "why", t.why.title, t.why.lede)}<p class="statement">${(0, html_1.text)(p.purpose)}</p>${cards ? `<div class="grid-2 reveal">${cards}</div>` : ""}`,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
};
|
|
39
|
+
const business = (ctx) => {
|
|
40
|
+
const b = ctx.data.business;
|
|
41
|
+
if (!b)
|
|
42
|
+
return [];
|
|
43
|
+
const { t, data } = ctx;
|
|
44
|
+
const slides = [];
|
|
45
|
+
if (!(0, load_1.isBlank)(b.model) || !(0, load_1.isBlank)(b.valueProposition)) {
|
|
46
|
+
const cards = [
|
|
47
|
+
!(0, load_1.isBlank)(b.valueProposition)
|
|
48
|
+
? `<div class="card big-card"><span class="emoji" aria-hidden="true">💎</span><span class="label">${t.business.value}</span><p>${(0, html_1.text)(b.valueProposition)}</p></div>`
|
|
49
|
+
: "",
|
|
50
|
+
!(0, load_1.isBlank)(b.model)
|
|
51
|
+
? `<div class="card big-card"><span class="emoji" aria-hidden="true">🧭</span><span class="label">${t.business.model}</span><p>${(0, html_1.text)(b.model)}</p></div>`
|
|
52
|
+
: "",
|
|
53
|
+
].join("");
|
|
54
|
+
slides.push({ chapter: "business", title: t.business.title, html: `${head(ctx, "business", t.business.title, t.business.lede)}<div class="grid-2 reveal">${cards}</div>` });
|
|
55
|
+
}
|
|
56
|
+
const actors = (0, load_1.filled)(b.actors);
|
|
57
|
+
slides.push(...paged(ctx, "business", actors, 6, t.business.actorsTitle, t.business.actorsLede, (page) => `<div class="grid reveal">${page
|
|
58
|
+
.map((a) => `<div class="card actor"><span class="avatar" style="--ah:${(0, html_1.hueFrom)(a.name)}">${(0, html_1.escapeHtml)((0, html_1.initials)(a.name))}</span><div><h3>${(0, html_1.escapeHtml)(a.name)}</h3><p>${(0, html_1.text)(a.description)}</p>${(0, html_1.evidenceList)(a.evidence, data)}</div></div>`)
|
|
59
|
+
.join("")}</div>`));
|
|
60
|
+
const flows = (0, load_1.filled)(b.flows);
|
|
61
|
+
slides.push(...paged(ctx, "business", flows, 2, t.business.flowsTitle, t.business.flowsLede, (page) => page
|
|
62
|
+
.map((f) => `<div class="flow"><h3>${(0, html_1.escapeHtml)(f.name)}</h3><ol class="chain reveal">${(0, load_1.filled)(f.steps)
|
|
63
|
+
.map((s, i) => `<li><span class="step"><b>${i + 1}</b>${(0, html_1.text)(s)}</span></li>`)
|
|
64
|
+
.join("")}</ol>${(0, html_1.evidenceList)(f.evidence, data)}</div>`)
|
|
65
|
+
.join("")));
|
|
66
|
+
return slides;
|
|
67
|
+
};
|
|
68
|
+
const product = (ctx) => {
|
|
69
|
+
const { t, data } = ctx;
|
|
70
|
+
return paged(ctx, "product", (0, load_1.filled)(data.features), 6, t.product.title, t.product.lede, (page) => `<div class="grid reveal">${page
|
|
71
|
+
.map((f) => `<div class="card feature"><div class="card-top"><h3>${(0, html_1.escapeHtml)(f.name)}</h3>${f.status ? `<span class="badge">${(0, html_1.escapeHtml)(f.status)}</span>` : ""}</div><p>${(0, html_1.text)(f.description)}</p>${(0, html_1.evidenceList)(f.entryPoints, data)}</div>`)
|
|
72
|
+
.join("")}</div>`);
|
|
73
|
+
};
|
|
74
|
+
const language = (ctx) => {
|
|
75
|
+
const { t, data } = ctx;
|
|
76
|
+
const terms = (0, load_1.filled)(data.domain?.glossary);
|
|
77
|
+
const slides = paged(ctx, "language", terms, 8, t.language.title, t.language.lede, (page) => `<div class="flips reveal">${page
|
|
78
|
+
.map((g) => `<button class="flip" type="button" aria-pressed="false"><span class="flip-inner"><span class="face front"><strong>${(0, html_1.escapeHtml)(g.term)}</strong><small>${t.language.tap} ↻</small></span><span class="face back">${(0, html_1.text)(g.definition)}</span></span></button>`)
|
|
79
|
+
.join("")}</div>`);
|
|
80
|
+
if (terms.length >= 3) {
|
|
81
|
+
const picks = [...new Set([0, Math.floor(terms.length / 2), terms.length - 1])];
|
|
82
|
+
const questions = picks
|
|
83
|
+
.map((idx, qi) => {
|
|
84
|
+
const right = terms[idx];
|
|
85
|
+
const wrong = [terms[(idx + 1) % terms.length], terms[(idx + 2) % terms.length]];
|
|
86
|
+
const options = [
|
|
87
|
+
{ label: right.definition, correct: true },
|
|
88
|
+
...wrong.map((w) => ({ label: w.definition, correct: false })),
|
|
89
|
+
];
|
|
90
|
+
// embaralha de forma determinística, para a resposta certa não ficar sempre em primeiro
|
|
91
|
+
const shift = ((0, html_1.hueFrom)(right.term) + qi) % 3;
|
|
92
|
+
const rotated = options.slice(shift).concat(options.slice(0, shift));
|
|
93
|
+
return `<div class="quiz-q" data-quiz data-right="${(0, html_1.escapeHtml)(t.language.right)}" data-wrong="${(0, html_1.escapeHtml)(t.language.wrong)}"><p class="q">${(0, html_1.escapeHtml)(t.language.question(right.term))}</p><div class="opts">${rotated
|
|
94
|
+
.map((o) => `<button type="button" class="opt" data-correct="${o.correct ? 1 : 0}">${(0, html_1.text)(o.label)}</button>`)
|
|
95
|
+
.join("")}</div><p class="feedback" aria-live="polite"></p></div>`;
|
|
96
|
+
})
|
|
97
|
+
.join("");
|
|
98
|
+
slides.push({
|
|
99
|
+
chapter: "language",
|
|
100
|
+
kind: "quiz",
|
|
101
|
+
title: t.language.quizTitle,
|
|
102
|
+
html: `${head(ctx, "language", t.language.quizTitle, t.language.quizLede)}<div class="quiz reveal">${questions}</div><p class="score" data-quiz-score data-template="${(0, html_1.escapeHtml)(t.language.score)}" aria-live="polite"></p>`,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return slides;
|
|
106
|
+
};
|
|
107
|
+
const rules = (ctx) => {
|
|
108
|
+
const { t, data } = ctx;
|
|
109
|
+
return paged(ctx, "rules", (0, load_1.filled)(data.domain?.businessRules), 3, t.rules.title, t.rules.lede, (page, offset) => `<div class="rules reveal">${page
|
|
110
|
+
.map((r, i) => `<div class="card rule"><span class="rule-num" aria-label="${t.rules.rule} ${offset + i + 1}">${(0, html_1.pad)(offset + i + 1)}</span><div><h3>${(0, html_1.escapeHtml)(r.title)}</h3><p>${(0, html_1.text)(r.description)}</p>${(0, html_1.evidenceList)(r.evidence, data)}</div></div>`)
|
|
111
|
+
.join("")}</div>`);
|
|
112
|
+
};
|
|
113
|
+
const stack = (ctx) => {
|
|
114
|
+
const { t, data } = ctx;
|
|
115
|
+
const items = (0, load_1.filled)(data.stack).sort((a, b) => (a.category || "").localeCompare(b.category || ""));
|
|
116
|
+
return paged(ctx, "stack", items, 6, t.stack.title, t.stack.lede, (page) => `<div class="grid reveal">${page
|
|
117
|
+
.map((s) => `<div class="card tech"><div class="tech-head"><span class="logo" style="--th:${(0, html_1.hueFrom)(s.name)}">${(0, html_1.escapeHtml)((0, html_1.initials)(s.name))}</span><div><strong>${(0, html_1.escapeHtml)(s.name)}</strong><small>${(0, html_1.escapeHtml)([s.category, s.version].filter(Boolean).join(" · "))}</small></div></div><p>${(0, html_1.text)(s.why)}</p>${(0, html_1.evidenceList)(s.evidence, data)}</div>`)
|
|
118
|
+
.join("")}</div>`);
|
|
119
|
+
};
|
|
120
|
+
const architecture = (ctx) => {
|
|
121
|
+
const { t, data } = ctx;
|
|
122
|
+
const a = data.architecture;
|
|
123
|
+
if (!a)
|
|
124
|
+
return [];
|
|
125
|
+
const slides = [];
|
|
126
|
+
if (!(0, load_1.isBlank)(a.summary) || !(0, load_1.isBlank)(a.diagram)) {
|
|
127
|
+
const hasDiagram = !(0, load_1.isBlank)(a.diagram);
|
|
128
|
+
slides.push({
|
|
129
|
+
chapter: "architecture",
|
|
130
|
+
kind: hasDiagram ? "diagram" : undefined,
|
|
131
|
+
title: t.architecture.title,
|
|
132
|
+
html: `${head(ctx, "architecture", t.architecture.title, hasDiagram ? a.summary : undefined)}${hasDiagram ? `<div class="diagram reveal"><pre class="mermaid">${(0, html_1.escapeHtml)(a.diagram)}</pre></div>` : `<p class="statement">${(0, html_1.text)(a.summary)}</p>`}`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
slides.push(...paged(ctx, "architecture", (0, load_1.filled)(a.modules), 6, t.architecture.modulesTitle, t.architecture.modulesLede, (page) => `<div class="modules reveal">${page
|
|
136
|
+
.map((m) => `<div class="module"><div><strong>${(0, html_1.escapeHtml)(m.name)}</strong><code>${(0, html_1.escapeHtml)(m.path)}</code></div><p>${(0, html_1.text)(m.responsibility)}</p></div>`)
|
|
137
|
+
.join("")}</div>`));
|
|
138
|
+
return slides;
|
|
139
|
+
};
|
|
140
|
+
const setup = (ctx) => {
|
|
141
|
+
const { t, data } = ctx;
|
|
142
|
+
const gs = data.gettingStarted;
|
|
143
|
+
if (!gs)
|
|
144
|
+
return [];
|
|
145
|
+
const slides = [];
|
|
146
|
+
const prereq = (0, load_1.filled)(gs.prerequisites);
|
|
147
|
+
const steps = (0, load_1.filled)(gs.setup);
|
|
148
|
+
if (prereq.length || steps.length) {
|
|
149
|
+
slides.push({
|
|
150
|
+
chapter: "setup",
|
|
151
|
+
title: t.setup.title,
|
|
152
|
+
html: `${head(ctx, "setup", t.setup.title, t.setup.lede)}${prereq.length ? `<p class="mini-label">${t.setup.prereq}</p><ul class="pills reveal">${prereq.map((p) => `<li>${(0, html_1.text)(p)}</li>`).join("")}</ul>` : ""}${steps.length ? `<ol class="steps reveal">${steps.map((s) => `<li><span>${(0, html_1.text)(s)}</span></li>`).join("")}</ol>` : ""}`,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
slides.push(...paged(ctx, "setup", (0, load_1.filled)(gs.commands), 6, t.setup.commandsTitle, t.setup.commandsLede, (page) => `<div class="terminal reveal"><div class="term-bar" aria-hidden="true"><i></i><i></i><i></i></div>${page
|
|
156
|
+
.map((c) => `<div class="cmd"><div><code>${(0, html_1.escapeHtml)(c.command)}</code>${c.description ? `<p>${(0, html_1.text)(c.description)}</p>` : ""}</div><button type="button" class="copy" data-copy="${(0, html_1.escapeHtml)(c.command)}" data-done="${(0, html_1.escapeHtml)(t.setup.copied)}">${t.setup.copy}</button></div>`)
|
|
157
|
+
.join("")}</div>`));
|
|
158
|
+
slides.push(...paged(ctx, "setup", (0, load_1.filled)(gs.envVars), 8, t.setup.envTitle, t.setup.envLede, (page) => `<div class="grid reveal">${page
|
|
159
|
+
.map((v) => `<div class="card env"><div class="card-top"><code>${(0, html_1.escapeHtml)(v.name)}</code><span class="badge ${v.required ? "badge-strong" : ""}">${v.required ? t.setup.required : t.setup.optional}</span></div><p>${(0, html_1.text)(v.description)}</p></div>`)
|
|
160
|
+
.join("")}</div>`));
|
|
161
|
+
return slides;
|
|
162
|
+
};
|
|
163
|
+
const conventions = (ctx) => {
|
|
164
|
+
const { t, data } = ctx;
|
|
165
|
+
return paged(ctx, "conventions", (0, load_1.filled)(data.conventions), 2, t.conventions.title, t.conventions.lede, (page) => `<div class="grid-2 reveal">${page
|
|
166
|
+
.map((c) => `<div class="card convention"><h3>${(0, html_1.escapeHtml)(c.title)}</h3><p>${(0, html_1.text)(c.description)}</p>${c.example?.trim() ? `<pre><code>${(0, html_1.escapeHtml)(c.example)}</code></pre>` : ""}${(0, html_1.evidenceList)(c.evidence, data)}</div>`)
|
|
167
|
+
.join("")}</div>`);
|
|
168
|
+
};
|
|
169
|
+
const tours = (ctx) => {
|
|
170
|
+
const { t, data } = ctx;
|
|
171
|
+
const slides = [];
|
|
172
|
+
for (const tour of (0, load_1.filled)(data.tours)) {
|
|
173
|
+
const steps = (0, load_1.filled)(tour.steps);
|
|
174
|
+
if (!steps.length)
|
|
175
|
+
continue;
|
|
176
|
+
slides.push({
|
|
177
|
+
chapter: "tours",
|
|
178
|
+
title: tour.title,
|
|
179
|
+
html: `<p class="kicker"><span class="num">${ctx.num}</span>${(0, html_1.escapeHtml)(t.tours.kicker)}</p><h1 class="title">${(0, html_1.escapeHtml)(tour.title)}</h1><p class="lede">${(0, html_1.text)(tour.description || t.tours.intro)}</p><p class="mini-label">${t.tours.stops}</p><ol class="route reveal">${steps
|
|
180
|
+
.map((s) => `<li><code>${(0, html_1.escapeHtml)(s.file)}</code></li>`)
|
|
181
|
+
.join("")}</ol>`,
|
|
182
|
+
});
|
|
183
|
+
steps.forEach((s, i) => {
|
|
184
|
+
slides.push({
|
|
185
|
+
chapter: "tours",
|
|
186
|
+
kind: "tour-step",
|
|
187
|
+
title: `${tour.title} · ${i + 1}`,
|
|
188
|
+
html: `<p class="kicker"><span class="num">${ctx.num}</span>${(0, html_1.escapeHtml)(tour.title)}</p><div class="tour-step"><span class="bignum" aria-hidden="true">${(0, html_1.pad)(i + 1)}</span><div><p class="mini-label">${(0, html_1.escapeHtml)(t.tours.stop(i + 1, steps.length))}</p>${(0, html_1.evidenceChip)(s.file, data, "ev-lg")}<p class="statement small">${(0, html_1.text)(s.explanation)}</p><div class="stops" aria-hidden="true">${steps
|
|
189
|
+
.map((_, j) => `<i class="${j <= i ? "on" : ""}"></i>`)
|
|
190
|
+
.join("")}</div></div></div>`,
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return slides;
|
|
195
|
+
};
|
|
196
|
+
const gotchas = (ctx) => {
|
|
197
|
+
const { t, data } = ctx;
|
|
198
|
+
return paged(ctx, "gotchas", (0, load_1.filled)(data.gotchas), 3, t.gotchas.title, t.gotchas.lede, (page) => `<div class="stack-list reveal">${page
|
|
199
|
+
.map((g) => `<div class="card gotcha"><span class="emoji" aria-hidden="true">🍌</span><div><h3>${(0, html_1.escapeHtml)(g.title)}</h3><p>${(0, html_1.text)(g.description)}</p>${(0, html_1.evidenceList)(g.evidence, data)}</div></div>`)
|
|
200
|
+
.join("")}</div>`);
|
|
201
|
+
};
|
|
202
|
+
const decisions = (ctx) => {
|
|
203
|
+
const { t, data } = ctx;
|
|
204
|
+
return paged(ctx, "decisions", (0, load_1.filled)(data.decisions), 2, t.decisions.title, t.decisions.lede, (page) => `<div class="stack-list reveal">${page
|
|
205
|
+
.map((d) => `<div class="card decision"><h3>${(0, html_1.escapeHtml)(d.title)}</h3><div class="split">${d.context ? `<div><span class="label">${t.decisions.context}</span><p>${(0, html_1.text)(d.context)}</p></div>` : ""}${d.decision ? `<div><span class="label">${t.decisions.decision}</span><p>${(0, html_1.text)(d.decision)}</p></div>` : ""}</div>${(0, html_1.evidenceList)(d.evidence, data)}</div>`)
|
|
206
|
+
.join("")}</div>`);
|
|
207
|
+
};
|
|
208
|
+
const mission = (ctx) => {
|
|
209
|
+
const { t, data } = ctx;
|
|
210
|
+
return paged(ctx, "mission", (0, load_1.filled)(data.firstTasks), 5, t.mission.title, t.mission.lede, (page) => `<div class="tasks reveal">${page
|
|
211
|
+
.map((task) => {
|
|
212
|
+
const id = `task-${(0, html_1.hueFrom)(task.title)}-${task.title.length}`;
|
|
213
|
+
return `<label class="task"><input type="checkbox" data-task="${id}"><span><strong>${(0, html_1.escapeHtml)(task.title)}</strong><span class="task-desc">${(0, html_1.text)(task.description)}</span>${(0, html_1.evidenceList)(task.files, data)}</span></label>`;
|
|
214
|
+
})
|
|
215
|
+
.join("")}</div>`);
|
|
216
|
+
};
|
|
217
|
+
/** Capítulos do meio, em ordem narrativa: do porquê ao código. */
|
|
218
|
+
exports.CHAPTERS = [
|
|
219
|
+
["why", why],
|
|
220
|
+
["business", business],
|
|
221
|
+
["product", product],
|
|
222
|
+
["language", language],
|
|
223
|
+
["rules", rules],
|
|
224
|
+
["stack", stack],
|
|
225
|
+
["architecture", architecture],
|
|
226
|
+
["setup", setup],
|
|
227
|
+
["conventions", conventions],
|
|
228
|
+
["tours", tours],
|
|
229
|
+
["gotchas", gotchas],
|
|
230
|
+
["decisions", decisions],
|
|
231
|
+
["mission", mission],
|
|
232
|
+
];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Framework, OnboardingData } from "./types";
|
|
2
|
+
export declare const SCHEMA_VERSION = "1";
|
|
3
|
+
export interface TemplateOptions {
|
|
4
|
+
framework: Framework;
|
|
5
|
+
language?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function createTemplate({ framework, language }: TemplateOptions): OnboardingData;
|
|
8
|
+
/** Frase para colar na IA local depois do `init`. */
|
|
9
|
+
export declare function aiPrompt(file?: string): string;
|
package/dist/template.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SCHEMA_VERSION = void 0;
|
|
4
|
+
exports.createTemplate = createTemplate;
|
|
5
|
+
exports.aiPrompt = aiPrompt;
|
|
6
|
+
exports.SCHEMA_VERSION = "1";
|
|
7
|
+
const FRAMEWORK_HINTS = {
|
|
8
|
+
next: "Projeto Next.js. Identifique se usa App Router (app/) ou Pages Router (pages/), as rotas e layouts, Server Components e Client Components ('use client'), Server Actions, route handlers (app/**/route.ts), middleware.ts, estratégia de fetch e cache, autenticação, estilização (Tailwind, CSS Modules etc.) e onde fica o acesso a dados. Use as rotas principais como base para features e tours. Ignore a rota app/onboarding criada pelo onboarding-kit.",
|
|
9
|
+
nest: "Projeto NestJS. Mapeie os módulos (*.module.ts) e as dependências entre eles para architecture.modules e o diagrama. Levante controllers e rotas, providers e services, guards, interceptors, pipes, DTOs e validação (class-validator), entidades e ORM (TypeORM, Prisma, Mongoose), filas, jobs agendados e configuração (ConfigModule). As regras de negócio costumam estar nos services. Ignore o OnboardingModule do onboarding-kit.",
|
|
10
|
+
node: "Projeto Node.js. Identifique o ponto de entrada (main e scripts do package.json), o framework HTTP (Express, Fastify, Koa ou http nativo), a organização de rotas e middlewares, o acesso a dados, jobs/workers e scripts utilitários.",
|
|
11
|
+
};
|
|
12
|
+
const SECTION_INSTRUCTIONS = {
|
|
13
|
+
project: "name: nome do projeto. tagline: uma frase que resume o projeto. purpose: 2 a 4 frases sobre o problema que ele resolve e para quem. audience: quem usa o produto (usuários finais, não devs). status: fase do projeto (ex.: em produção, MVP, em migração). repoUrl: URL do repositório (git remote get-url origin, convertida para https), vazio se não houver.",
|
|
14
|
+
business: "Explique o negócio para alguém que nunca ouviu falar do produto. model: como o produto gera valor ou receita (assinatura, comissão, venda, uso interno...). Baseie-se em planos, pagamentos, roles e docs do repositório; se não der para inferir a receita, descreva o valor gerado sem inventar. valueProposition: o que o produto resolve para quem usa, em 1 ou 2 frases. actors: os perfis que participam (tipos de usuário, roles, sistemas parceiros), com evidence de onde aparecem no código. flows: 1 a 3 jornadas principais (ex.: 'Do cadastro à primeira compra'), cada uma com 3 a 6 passos curtos e evidence.",
|
|
15
|
+
gettingStarted: "prerequisites: o que precisa estar instalado (versão do Node, banco, Docker...). setup: passos em ordem para rodar localmente pela primeira vez. commands: scripts úteis do package.json com o comando exato e o que fazem. envVars: variáveis de ambiente (de .env.example, código ou docs) com nome, para que servem e se são obrigatórias. NUNCA inclua valores.",
|
|
16
|
+
stack: "Uma entrada por tecnologia relevante (linguagem, framework, banco, ORM, auth, testes, lint, deploy, filas, libs centrais). category: ex. framework, database, orm, testing, styling, infra. version: do package.json/lockfile. why: para que é usada NESTE projeto. evidence: onde aparece (package.json, arquivo de config ou de uso).",
|
|
17
|
+
architecture: "summary: como o código está organizado e como uma requisição/ação flui pelo sistema. diagram: diagrama Mermaid (graph TD ou flowchart) com os principais módulos e integrações externas, sem cercas ```. modules: pastas ou módulos principais com path e responsibility.",
|
|
18
|
+
conventions: "Padrões de código que alguém novo precisa seguir: nomenclatura, estrutura de pastas, tratamento de erro, validação, padrões de componentes, testes, commits, lint/format. example: trecho curto real ou ilustrativo. evidence obrigatória.",
|
|
19
|
+
domain: "glossary: termos do negócio que aparecem no código e nas telas, com definição em linguagem simples. businessRules: regras de negócio macro (ex.: quem pode fazer o quê, limites, cálculos, estados de um pedido), com evidence apontando onde a regra está implementada.",
|
|
20
|
+
features: "Funcionalidades do produto do ponto de vista do usuário. entryPoints: arquivos onde a feature começa (página, controller, rota). status: ex. estável, beta, em desenvolvimento, descontinuada.",
|
|
21
|
+
tours: "1 a 3 tours guiados pelo código, ex.: 'Como uma requisição é atendida', 'Ciclo de vida de um pedido'. Cada step aponta um arquivo (com linha quando ajudar) e explica o que olhar ali. Ordene os steps na sequência de leitura.",
|
|
22
|
+
gotchas: "Armadilhas e cuidados: comportamentos não óbvios, partes frágeis, workarounds, TODOs/FIXMEs importantes, coisas que costumam quebrar. Só inclua o que tiver evidence.",
|
|
23
|
+
decisions: "Decisões de arquitetura perceptíveis no código ou na documentação (ex.: por que tal ORM, monorepo, cache, autenticação). context: o problema. decision: o que foi escolhido. Não invente motivos: se o motivo não está documentado, descreva só a decisão.",
|
|
24
|
+
firstTasks: "3 a 5 tarefas pequenas e seguras para alguém novo começar (ex.: um teste faltando, um TODO simples, uma melhoria de mensagem de erro), com os arquivos envolvidos.",
|
|
25
|
+
};
|
|
26
|
+
function createTemplate({ framework, language = "pt-BR" }) {
|
|
27
|
+
return {
|
|
28
|
+
_instructions: {
|
|
29
|
+
readme: "Você é a IA local deste projeto. Sua tarefa é preencher este arquivo analisando o código do repositório. O resultado será exibido em /onboarding para quem está chegando no projeto.",
|
|
30
|
+
steps: [
|
|
31
|
+
"1. Leia este arquivo inteiro antes de começar, incluindo _instructions.sections.",
|
|
32
|
+
"2. Explore o repositório: package.json e lockfile, README e docs, arquivos de config (tsconfig, eslint, prettier, next.config, nest-cli.json, docker, CI), código-fonte, testes e .env.example.",
|
|
33
|
+
"3. Preencha cada seção seguindo _instructions.sections.<seção> e _instructions.framework. Os arrays vêm com um item modelo em branco: substitua-o por itens reais, quantos forem necessários. Se não houver nada real para a seção, deixe o array vazio ([]).",
|
|
34
|
+
"4. Preencha meta: status = \"filled\", generatedAt = data e hora atuais em ISO 8601, generatedBy = nome da ferramenta e do modelo, commit = saída de `git rev-parse HEAD` (vazio se não houver git).",
|
|
35
|
+
"5. Rode `npx onboarding-kit validate` e corrija tudo o que ele apontar até passar sem erros.",
|
|
36
|
+
],
|
|
37
|
+
rules: [
|
|
38
|
+
"Escreva todo o conteúdo no idioma de meta.language.",
|
|
39
|
+
"Nunca invente. Toda afirmação sobre stack, convenções, regras, decisões e armadilhas precisa de evidence com arquivos reais, no formato caminho/relativo ou caminho/relativo:linha. Se não encontrar evidência, não inclua o item.",
|
|
40
|
+
"Caminhos são relativos à raiz do repositório e usam '/'.",
|
|
41
|
+
"Nunca copie segredos: valores de .env, tokens, chaves de API, senhas ou URLs com credenciais. Em envVars descreva apenas o nome e a finalidade.",
|
|
42
|
+
"Mantenha o JSON válido. Não renomeie nem remova chaves e não apague _instructions: ele é usado nas próximas atualizações.",
|
|
43
|
+
"Escreva de forma curta, direta e humana, como um colega explicando o projeto: quem lê é uma pessoa no primeiro dia. O conteúdo vira slides, então prefira frases curtas a parágrafos longos.",
|
|
44
|
+
"Se meta.status já for \"filled\", você está ATUALIZANDO: use `git diff <meta.commit>..HEAD` para revisar apenas o que mudou, mantenha o restante e atualize meta.commit e meta.generatedAt.",
|
|
45
|
+
],
|
|
46
|
+
framework: FRAMEWORK_HINTS[framework],
|
|
47
|
+
sections: SECTION_INSTRUCTIONS,
|
|
48
|
+
},
|
|
49
|
+
meta: {
|
|
50
|
+
schemaVersion: exports.SCHEMA_VERSION,
|
|
51
|
+
status: "pending",
|
|
52
|
+
language,
|
|
53
|
+
framework,
|
|
54
|
+
generatedAt: "",
|
|
55
|
+
generatedBy: "",
|
|
56
|
+
commit: "",
|
|
57
|
+
},
|
|
58
|
+
project: { name: "", tagline: "", purpose: "", audience: "", status: "", repoUrl: "" },
|
|
59
|
+
business: {
|
|
60
|
+
model: "",
|
|
61
|
+
valueProposition: "",
|
|
62
|
+
actors: [{ name: "", description: "", evidence: [""] }],
|
|
63
|
+
flows: [{ name: "", steps: [""], evidence: [""] }],
|
|
64
|
+
},
|
|
65
|
+
gettingStarted: {
|
|
66
|
+
prerequisites: [""],
|
|
67
|
+
setup: [""],
|
|
68
|
+
commands: [{ name: "", command: "", description: "" }],
|
|
69
|
+
envVars: [{ name: "", description: "", required: false }],
|
|
70
|
+
},
|
|
71
|
+
stack: [{ name: "", category: "", version: "", why: "", evidence: [""] }],
|
|
72
|
+
architecture: { summary: "", diagram: "", modules: [{ name: "", path: "", responsibility: "" }] },
|
|
73
|
+
conventions: [{ title: "", description: "", example: "", evidence: [""] }],
|
|
74
|
+
domain: {
|
|
75
|
+
glossary: [{ term: "", definition: "" }],
|
|
76
|
+
businessRules: [{ title: "", description: "", evidence: [""] }],
|
|
77
|
+
},
|
|
78
|
+
features: [{ name: "", description: "", entryPoints: [""], status: "" }],
|
|
79
|
+
tours: [{ title: "", description: "", steps: [{ file: "", explanation: "" }] }],
|
|
80
|
+
gotchas: [{ title: "", description: "", evidence: [""] }],
|
|
81
|
+
decisions: [{ title: "", context: "", decision: "", evidence: [""] }],
|
|
82
|
+
firstTasks: [{ title: "", description: "", files: [""] }],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** Frase para colar na IA local depois do `init`. */
|
|
86
|
+
function aiPrompt(file = "onboarding.json") {
|
|
87
|
+
return `Leia o arquivo ${file} e siga as instruções em "_instructions" para preenchê-lo com base neste repositório. No final, rode "npx onboarding-kit validate" e corrija o que ele apontar.`;
|
|
88
|
+
}
|