Project Init and Docs Config
Build From Scratch: Mintlify-like AI-driven Documentation Generator CLI - Part 006
Building docforge init: project scaffolding, docs configuration, schema validation, template generation, idempotent writes, package script integration, and config diagnostics.
Part 006 — Project Init and Docs Config
Di Part 005 kita mendesain CLI sebagai public API.
Sekarang kita implementasikan command pertama yang benar-benar terasa sebagai produk: docforge init.
Command init terlihat sederhana:
docforge init
Tapi di developer tools, init adalah titik pembentukan trust.
Jika init menimpa file sembarangan, membuat struktur membingungkan, atau menghasilkan config yang sulit dievolusikan, user akan kehilangan confidence sebelum mencoba fitur lain.
Part ini membahas:
- struktur project docs,
- config file,
- schema validation,
- template generation,
- idempotent file writes,
- interactive dan non-interactive mode,
- package script integration,
- default MDX pages,
- diagnostics untuk config error.
Target akhir part ini:
docforge initmampu membuat project dokumentasi awal yang aman, followable, valid, dan siap dibangun oleh command berikutnya.
1. Mental Model: Init Bukan Copy Template Saja
Banyak CLI memperlakukan init sebagai copy folder template.
Untuk DocForge, itu terlalu dangkal.
init harus melakukan orchestration:
Perhatikan: sebelum menulis, kita membuat plan.
Plan ini penting karena:
- bisa ditampilkan pada
--dry-run, - bisa dicek conflict,
- bisa dites tanpa filesystem write,
- bisa dipakai nanti oleh PR automation,
- bisa dibuat reversible sebagian.
2. Struktur Project Awal
Kita akan membuat struktur default seperti ini:
.
├── docforge.config.json
├── docs/
│ ├── index.mdx
│ ├── quickstart.mdx
│ ├── concepts/
│ │ └── overview.mdx
│ └── api-reference/
│ └── introduction.mdx
└── package.json
Jika project sudah punya docs/, kita tidak langsung overwrite.
Jika project belum punya package.json, kita tetap bisa membuat docs project, tapi package script integration akan dilewati atau dibuat opsional.
2.1 Kenapa Config di Root?
Config di root memudahkan command:
docforge build
Tidak perlu user selalu menulis:
docforge build --config docs/docforge.config.json
Root config juga memudahkan CI dan repo automation.
Tapi docs content tetap di docs/ agar jelas terpisah dari code.
3. Nama Config: docforge.config.json
Kita pilih JSON untuk awal.
Alternatif:
docforge.config.ts,docforge.config.mjs,docs.json,mint.json-style config,package.jsonfield.
Kenapa JSON dulu?
- mudah divalidasi,
- mudah diedit oleh manusia,
- mudah diproses oleh tool lain,
- tidak perlu menjalankan kode user,
- aman untuk CI dan agent,
- cocok untuk schema-driven diagnostics.
Dynamic config seperti TypeScript bisa datang nanti, tapi default awal sebaiknya declarative.
Rule security:
Config awal tidak boleh mengharuskan DocForge mengeksekusi kode user.
Itu mengurangi risiko supply chain dan membuat behavior lebih deterministic.
4. Config Shape Awal
Config minimal:
{
"$schema": "https://docforge.dev/schemas/docforge.config.schema.json",
"version": 1,
"site": {
"name": "My Docs",
"description": "Developer documentation for this project"
},
"docs": {
"dir": "docs"
},
"navigation": [
{
"group": "Start Here",
"pages": [
"index",
"quickstart"
]
},
{
"group": "Concepts",
"pages": [
"concepts/overview"
]
},
{
"group": "API Reference",
"pages": [
"api-reference/introduction"
]
}
],
"search": {
"enabled": true
},
"ai": {
"enabled": false
}
}
Kita sengaja membuat ai.enabled default false.
Kenapa?
Karena init harus bisa berjalan tanpa API key, tanpa network, tanpa provider dependency.
AI adalah fitur opt-in. Documentation generator harus berguna bahkan sebelum AI aktif.
5. Config Domain Model
Kita tidak ingin semua package membaca raw JSON langsung.
Raw config harus divalidasi dan dinormalisasi menjadi domain config.
export type DocforgeConfig = {
version: 1;
site: SiteConfig;
docs: DocsConfig;
navigation: NavigationGroup[];
search: SearchConfig;
ai: AiConfig;
};
export type SiteConfig = {
name: string;
description: string;
logo?: string;
url?: string;
};
export type DocsConfig = {
dir: string;
};
export type NavigationGroup = {
group: string;
pages: string[];
};
export type SearchConfig = {
enabled: boolean;
};
export type AiConfig = {
enabled: boolean;
provider?: string;
};
Raw JSON:
unknown
Validated config:
DocforgeConfig
Resolved config:
ResolvedDocforgeConfig
Resolved config sudah mengandung absolute paths dan default values.
export type ResolvedDocforgeConfig = DocforgeConfig & {
paths: {
rootDir: string;
configPath: string;
docsDir: string;
outputDir: string;
};
};
Jangan campur raw config, validated config, dan resolved config.
6. Schema Validation dengan Zod
Kita bisa memakai Zod untuk runtime validation di TypeScript.
Schema awal:
import * as z from "zod";
export const NavigationGroupSchema = z.object({
group: z.string().min(1),
pages: z.array(z.string().min(1)),
});
export const DocforgeConfigSchema = z.object({
$schema: z.string().optional(),
version: z.literal(1),
site: z.object({
name: z.string().min(1),
description: z.string().min(1),
logo: z.string().optional(),
url: z.string().url().optional(),
}),
docs: z.object({
dir: z.string().min(1).default("docs"),
}),
navigation: z.array(NavigationGroupSchema).default([]),
search: z.object({
enabled: z.boolean().default(true),
}).default({ enabled: true }),
ai: z.object({
enabled: z.boolean().default(false),
provider: z.string().optional(),
}).default({ enabled: false }),
});
export type DocforgeConfig = z.infer<typeof DocforgeConfigSchema>;
Namun, Zod error default tidak boleh langsung dilempar ke user.
Kita convert ke diagnostics.
export function zodIssuesToDiagnostics(issues: z.ZodIssue[], file: string): Diagnostic[] {
return issues.map((issue) => ({
code: "CONFIG_INVALID_SCHEMA",
severity: "error",
message: issue.message,
file,
hint: `Check field: ${issue.path.join(".") || "<root>"}`,
details: {
path: issue.path,
},
}));
}
Nanti kita bisa meningkatkan ini dengan line/column mapping dari JSON parser.
7. JSON Schema untuk Editor Support
Zod bagus untuk runtime. Tapi user juga butuh editor autocomplete.
Maka config menyertakan $schema:
{
"$schema": "https://docforge.dev/schemas/docforge.config.schema.json"
}
JSON Schema berguna untuk:
- autocomplete di editor,
- validation sebelum CLI dijalankan,
- dokumentasi config,
- compatibility tooling,
- future migration checks.
Kita bisa generate JSON Schema dari Zod atau maintain manual schema.
Trade-off:
| Approach | Kelebihan | Kekurangan |
|---|---|---|
| Generate dari Zod | single source untuk runtime dan schema | hasil kadang kurang ekspresif |
| Manual JSON Schema | kontrol penuh untuk editor docs | rawan drift dari runtime schema |
| Hybrid | runtime Zod + checked generated schema | butuh test contract |
Untuk awal, kita bisa generate dari Zod, lalu menambahkan test agar generated schema tidak drift.
8. Init Templates
Template awal sebaiknya tidak terlalu banyak.
Kita definisikan tiga template:
minimal
api
full
8.1 minimal
Untuk project kecil.
Files:
docforge.config.json
docs/index.mdx
docs/quickstart.mdx
8.2 api
Untuk API product.
Files:
docforge.config.json
docs/index.mdx
docs/quickstart.mdx
docs/api-reference/introduction.mdx
openapi.yaml placeholder reference optional
8.3 full
Untuk project yang ingin struktur lengkap.
Files:
docs/index.mdx
docs/quickstart.mdx
docs/concepts/overview.mdx
docs/guides/installation.mdx
docs/guides/configuration.mdx
docs/api-reference/introduction.mdx
docs/troubleshooting/common-errors.mdx
Default kita pilih minimal atau api?
Untuk Mintlify-like developer docs, default yang baik adalah minimal agar tidak menghasilkan terlalu banyak halaman kosong.
Namun jika repo punya OpenAPI file, kita bisa suggest api.
9. Project Detection
Sebelum init, CLI bisa membaca repository untuk memberi default lebih baik.
Detection ringan:
package.json -> Node/TypeScript project
pom.xml -> Java Maven project
build.gradle -> Java/Kotlin Gradle project
go.mod -> Go project
pyproject.toml -> Python project
openapi.yaml/json -> API docs candidate
README.md -> source for landing page
Tapi hati-hati:
Project detection tidak boleh membuat init unpredictable.
Jika detection dipakai, tampilkan jelas:
Detected:
package manager: pnpm
README: README.md
OpenAPI spec: openapi.yaml
Suggested template: api
Dalam non-interactive mode, default harus stabil atau bisa dikontrol flag.
docforge init --template minimal --yes
10. Init Request
Dari CLI, kita bentuk request.
export type InitTemplate = "minimal" | "api" | "full";
export type InitRequest = CommonCommandOptions & {
template: InitTemplate;
yes: boolean;
dryRun: boolean;
force: boolean;
packageManager?: "npm" | "pnpm" | "yarn" | "bun";
siteName?: string;
};
Catatan: force perlu didefinisikan ketat.
Dalam DocForge:
--force hanya boleh menimpa file yang sebelumnya diketahui sebagai generated file DocForge, bukan file user arbitrary.
Lebih aman lagi pakai flag:
--overwrite-generated
Tapi karena user umum mengenal --force, kita boleh menyediakan --force dengan semantic aman dan dokumentasi jelas.
11. Init Plan
Service initProject tidak langsung menulis file.
Ia membuat plan.
export type FileChange =
| {
type: "create";
path: string;
content: string;
}
| {
type: "update";
path: string;
before: string;
after: string;
}
| {
type: "skip";
path: string;
reason: string;
};
export type InitPlan = {
rootDir: string;
docsDir: string;
template: InitTemplate;
changes: FileChange[];
diagnostics: Diagnostic[];
};
Execution:
export async function initProject(
request: InitRequest,
context: ExecutionContext,
): Promise<CommandResult<InitResultData>> {
const plan = await createInitPlan(request, context);
if (hasBlockingDiagnostics(plan.diagnostics)) {
return initFailure(plan);
}
if (request.dryRun) {
return initDryRunSuccess(plan);
}
await applyInitPlan(plan, context);
return initSuccess(plan);
}
Dengan pattern ini, dry-run bukan fitur tambahan. Ia natural dari architecture.
12. File Write Safety
Aturan menulis file:
- jika file belum ada: create,
- jika file ada dan sama persis: skip,
- jika file ada dan berbeda: conflict,
- jika file generated by DocForge dan
--force: update, - jangan hapus file user saat init.
Kita bisa menandai generated file lewat comment.
Untuk MDX:
{/* Generated by DocForge init. Safe to edit. */}
Tapi setelah user edit, file tidak lagi sama.
Lebih baik gunakan manifest:
.docforge/
manifest.json
Namun untuk init awal, kita belum butuh manifest kompleks. Kita bisa mulai dengan conservative behavior:
- tidak overwrite file berbeda,
- tampilkan conflict,
- user bisa rename atau pilih docs dir lain.
Contoh diagnostic:
error INIT_FILE_CONFLICT
docs/index.mdx already exists and differs from the template.
hint
Choose a different --dir, or move the existing file before running init.
Jangan jadikan --yes sebagai overwrite.
--yes hanya menjawab prompt default aman.
13. Default MDX Pages
Generated docs harus berguna, bukan filler.
13.1 docs/index.mdx
---
title: Introduction
description: Developer documentation for this project.
---
# Introduction
Welcome to the project documentation.
Use this space to explain what the project does, who it is for, and what problem it solves.
## Start here
- [Quickstart](/quickstart)
- [Concepts](/concepts/overview)
- [API Reference](/api-reference/introduction)
13.2 docs/quickstart.mdx
------
title: Quickstart
description: Install and run the project quickly.
---
# Quickstart
This guide helps developers get started quickly.
## Prerequisites
- Node.js or the runtime required by your project
- Package manager or build tool
- Access to required environment variables
## Install
```bash
# Replace this with your actual install command
npm install
Run
# Replace this with your actual run command
npm run dev
Important: generated content harus jujur. Jangan mengarang command project jika belum diketahui.
Lebih baik placeholder eksplisit daripada hallucinated instructions.
---
## 14. Frontmatter Standard
Setiap page perlu frontmatter minimal:
```yaml
title: Quickstart
description: Install and run the project quickly.
Kenapa description wajib?
Karena dipakai untuk:
- SEO,
- search result snippet,
- generated navigation preview,
- LLM/agent summaries,
- docs quality gates.
Frontmatter schema:
export const PageFrontmatterSchema = z.object({
title: z.string().min(1),
description: z.string().min(1),
icon: z.string().optional(),
hidden: z.boolean().optional(),
});
Nanti frontmatter bisa diperluas.
Tapi minimal awal harus ketat.
15. Package Script Integration
Jika package.json ada, init bisa menawarkan menambahkan scripts:
{
"scripts": {
"docs:dev": "docforge dev",
"docs:build": "docforge build",
"docs:check": "docforge check"
}
}
Tapi ini harus hati-hati.
Rules:
- jangan overwrite script existing,
- jika script sudah ada sama persis: skip,
- jika script name conflict: diagnostic warning,
- update package.json dengan formatting stable,
- dalam non-interactive mode, hanya tambah jika
--yesdan tidak conflict.
Contoh conflict:
{
"scripts": {
"docs:build": "vitepress build docs"
}
}
Diagnostic:
warning INIT_PACKAGE_SCRIPT_CONFLICT
package.json already has script "docs:build".
hint
Existing script was left unchanged. Add "docforge build" manually if needed.
16. Config Resolution
Setelah init, command lain perlu menemukan config.
Resolution algorithm:
1. if --config provided, use it
2. else search cwd/docforge.config.json
3. else search upward until repository root
4. else fail with CONFIG_NOT_FOUND
Tapi hati-hati dengan upward search.
Jika user menjalankan command dari subdirectory, upward search membantu.
cd docs/guides
docforge build
Tapi jangan search sampai / tanpa batas. Stop di:
- git root,
- filesystem root,
- home boundary optional.
Pseudo-code:
export async function findConfig(startDir: string): Promise<string | undefined> {
let dir = startDir;
while (true) {
const candidate = path.join(dir, "docforge.config.json");
if (await exists(candidate)) return candidate;
if (await exists(path.join(dir, ".git"))) return undefined;
const parent = path.dirname(dir);
if (parent === dir) return undefined;
dir = parent;
}
}
Ini akan disempurnakan nanti.
17. Reading Config Safely
Config read pipeline:
Error per tahap berbeda:
| Tahap | Diagnostic |
|---|---|
| file not found | CONFIG_NOT_FOUND |
| file unreadable | CONFIG_READ_FAILED |
| invalid JSON | CONFIG_INVALID_JSON |
| schema invalid | CONFIG_INVALID_SCHEMA |
| path invalid | CONFIG_INVALID_PATH |
Jangan semua menjadi CONFIG_ERROR.
Semakin spesifik diagnostic, semakin cepat user memperbaiki.
18. JSON Parse Error dengan Location
JSON.parse default tidak memberi line/column yang enak.
Untuk production, gunakan parser yang memberi location atau buat mapper dari position.
Untuk awal, kita bisa minimal:
export function parseJsonConfig(text: string, file: string): unknown {
try {
return JSON.parse(text);
} catch (error) {
throw new ExpectedFailure("Invalid JSON config", 3, [
{
code: "CONFIG_INVALID_JSON",
severity: "error",
message: error instanceof Error ? error.message : "Invalid JSON",
file,
hint: "Fix JSON syntax. Common issues: trailing comma, comments, or unquoted property names.",
},
]);
}
}
Catatan: JSON tidak mendukung comment dan trailing comma. Ini sering mengejutkan user yang terbiasa JSONC.
Kita bisa mendukung JSONC nanti, tapi jika iya, file extension dan semantics harus jelas.
19. Template Renderer
Template jangan langsung hardcode string di command handler.
Buat module template.
packages/core/src/init/
templates/
minimal.ts
api.ts
full.ts
create-init-plan.ts
apply-init-plan.ts
Template interface:
export type InitTemplateDefinition = {
name: InitTemplate;
files(input: InitTemplateInput): TemplateFile[];
config(input: InitTemplateInput): DocforgeConfig;
};
export type TemplateFile = {
path: string;
content: string;
};
export type InitTemplateInput = {
siteName: string;
docsDir: string;
detected: ProjectDetection;
};
Dengan ini, menambah template baru tidak mengubah CLI parser.
20. Project Detection Implementation
Detection jangan terlalu dalam di init awal. Kita hanya butuh hint.
export type ProjectDetection = {
hasPackageJson: boolean;
packageManager?: "npm" | "pnpm" | "yarn" | "bun";
hasReadme: boolean;
openApiFiles: string[];
languages: string[];
};
Package manager detection:
pnpm-lock.yaml -> pnpm
yarn.lock -> yarn
bun.lockb/bun.lock -> bun
package-lock.json -> npm
Language detection ringan:
pom.xml -> java
build.gradle -> java/kotlin
go.mod -> go
pyproject.toml -> python
package.json -> javascript/typescript
Jangan scan seluruh repository dulu. Full scanner baru dibahas di Part 008 dan seterusnya.
21. Init CLI Registration
Command:
export function registerInitCommand(program: Command): void {
program
.command("init")
.description("Initialize DocForge documentation in this repository")
.option("--template <template>", "Template: minimal, api, full", "minimal")
.option("--site-name <name>", "Site name")
.option("--package-manager <name>", "Package manager: npm, pnpm, yarn, bun")
.option("--yes", "Use safe defaults without prompting", false)
.option("--dry-run", "Show planned changes without writing files", false)
.option("--force", "Overwrite generated files when safe", false)
.action(async (options, command) => {
const globals = command.parent?.optsWithGlobals() ?? {};
const context = createExecutionContext(globals);
const request: InitRequest = {
cwd: globals.cwd,
configPath: globals.config,
docsDir: globals.dir ?? "docs",
format: globals.format,
verbose: globals.verbose,
quiet: globals.quiet,
color: globals.color,
template: parseTemplate(options.template),
yes: Boolean(options.yes),
dryRun: Boolean(options.dryRun),
force: Boolean(options.force),
packageManager: options.packageManager,
siteName: options.siteName,
};
const result = await initProject(request, context);
await renderResult(result, context);
process.exitCode = result.exitCode;
});
}
parseTemplate harus menghasilkan diagnostic jika invalid.
docforge init --template enterprise
Output:
error CLI_INVALID_OPTION_VALUE
Invalid value for --template: enterprise
hint
Allowed values: minimal, api, full
22. Init Result Output
Human success:
DocForge initialized successfully.
Created:
docforge.config.json
docs/index.mdx
docs/quickstart.mdx
Next steps:
1. Run: docforge dev
2. Edit: docs/index.mdx
3. Build: docforge build
Dry-run success:
DocForge init plan:
Would create:
docforge.config.json
docs/index.mdx
docs/quickstart.mdx
No files were written.
JSON output:
{
"tool": "docforge",
"version": "0.1.0",
"command": "init",
"ok": true,
"exitCode": 0,
"diagnostics": [],
"data": {
"template": "minimal",
"docsDir": "docs",
"written": true,
"changes": [
{ "type": "create", "path": "docforge.config.json" },
{ "type": "create", "path": "docs/index.mdx" },
{ "type": "create", "path": "docs/quickstart.mdx" }
]
}
}
23. Idempotency
Idempotency berarti command bisa dijalankan ulang tanpa merusak state.
docforge init
docforge init
Run kedua seharusnya tidak membuat konflik jika file sama dengan template.
Output:
DocForge is already initialized.
Unchanged:
docforge.config.json
docs/index.mdx
docs/quickstart.mdx
Tapi jika user sudah mengedit file, run kedua tidak boleh overwrite.
warning INIT_FILE_EXISTS
docs/index.mdx already exists and was left unchanged.
Idempotency adalah sinyal maturity.
24. Handling Existing Docs Projects
Banyak repo sudah punya docs/.
Scenario:
docs/
architecture.md
api.md
Saat user menjalankan:
docforge init
Kita tidak ingin menimpa. Kita bisa:
- create config,
- leave existing docs,
- suggest migration command later.
Diagnostic warning:
warning INIT_EXISTING_DOCS_DIR
docs directory already exists.
hint
Existing files were left unchanged. You can add them to navigation in docforge.config.json.
Kita bisa generate navigation kosong atau berdasarkan existing docs?
Untuk awal, jangan auto-classify terlalu banyak. Itu nanti bagian scanner dan AI planner.
Default aman:
- buat config jika belum ada,
- buat missing default pages jika path tidak conflict,
- skip conflict,
- laporkan jelas.
25. Avoiding Hallucinated Setup Instructions
Karena ini AI-driven docs generator, temptation-nya besar: dari package.json, langsung generate quickstart lengkap.
Tapi di init, jangan terlalu agresif.
Jika package.json punya scripts:
{
"scripts": {
"dev": "next dev",
"build": "next build"
}
}
Kita boleh membuat suggestion:
## Run locally
```bash
npm run dev
Tapi jika tidak yakin, jangan mengarang.
Aturan:
- fakta eksplisit dari file boleh dipakai,
- asumsi harus ditandai sebagai placeholder,
- command yang tidak ditemukan jangan ditulis sebagai pasti,
- AI generation nanti harus pakai provenance.
Untuk template init awal, lebih aman pakai placeholder.
---
## 26. Generated Config Example by Template
### 26.1 Minimal Template Config
```json
{
"$schema": "https://docforge.dev/schemas/docforge.config.schema.json",
"version": 1,
"site": {
"name": "My Project",
"description": "Developer documentation for My Project"
},
"docs": {
"dir": "docs"
},
"navigation": [
{
"group": "Start Here",
"pages": ["index", "quickstart"]
}
],
"search": {
"enabled": true
},
"ai": {
"enabled": false
}
}
26.2 API Template Config
{
"$schema": "https://docforge.dev/schemas/docforge.config.schema.json",
"version": 1,
"site": {
"name": "My API",
"description": "Developer documentation for My API"
},
"docs": {
"dir": "docs"
},
"navigation": [
{
"group": "Start Here",
"pages": ["index", "quickstart"]
},
{
"group": "API Reference",
"pages": ["api-reference/introduction"]
}
],
"openapi": {
"spec": "openapi.yaml"
},
"search": {
"enabled": true
},
"ai": {
"enabled": false
}
}
Notice: openapi belum ada di schema awal yang kita tulis. Jika template API menggunakannya, schema harus mendukungnya.
Maka update schema:
openapi: z.object({
spec: z.string().min(1),
}).optional()
Ini contoh penting: template dan schema harus diuji bersama.
27. Config Contract Tests
Kita butuh test agar config generated selalu valid.
it.each(["minimal", "api", "full"] as const)("generates valid config for %s", (template) => {
const definition = getInitTemplate(template);
const config = definition.config({
siteName: "Test Project",
docsDir: "docs",
detected: emptyDetection,
});
expect(() => DocforgeConfigSchema.parse(config)).not.toThrow();
});
Test lain:
it("rejects unknown config version", () => {
const result = DocforgeConfigSchema.safeParse({ version: 999 });
expect(result.success).toBe(false);
});
Dan snapshot untuk generated files.
it("minimal template file list is stable", () => {
const files = minimalTemplate.files(input).map((file) => file.path);
expect(files).toMatchInlineSnapshot();
});
28. Formatting JSON Output
Generated config harus readable.
export function formatJson(value: unknown): string {
return `${JSON.stringify(value, null, 2)}\n`;
}
Kenapa newline di akhir?
Karena convention POSIX text file dan diff lebih bersih.
Saat update package.json, pertahankan indentasi jika bisa. Untuk awal, dua spasi cukup, tapi nanti kita bisa detect formatting.
29. Path Normalization
Config harus portable lintas OS.
Dalam config, gunakan POSIX-like path:
{
"docs": {
"dir": "docs"
}
}
Bukan:
{
"docs": {
"dir": "docs\\guides"
}
}
Internal runtime boleh resolve dengan path.resolve, tapi config path sebaiknya normal.
Utility:
export function toConfigPath(filePath: string): string {
return filePath.split(path.sep).join("/");
}
Navigation pages juga pakai slash / dan tanpa extension:
"pages": ["concepts/overview"]
Bukan:
"pages": ["concepts\\overview.mdx"]
Ini memudahkan routing.
30. Route Mapping
Page file:
docs/concepts/overview.mdx
Navigation entry:
concepts/overview
Route:
/concepts/overview
Index page:
docs/index.mdx -> /
Nested index:
docs/concepts/index.mdx -> /concepts
Kita belum implement router penuh di part ini, tapi config init harus mengikuti rule yang akan dipakai renderer.
31. Config Diagnostics Examples
31.1 Missing Config
error CONFIG_NOT_FOUND
Could not find docforge.config.json.
hint
Run `docforge init` from your repository root, or pass --config <path>.
31.2 Invalid Version
error CONFIG_INVALID_SCHEMA
Expected version to be 1.
hint
This version of DocForge supports config version 1.
31.3 Missing Page in Navigation
This is not init validation; this is build/check validation.
error NAV_PAGE_NOT_FOUND
Navigation points to concepts/overview, but docs/concepts/overview.mdx does not exist.
Still, init should generate consistent config so this never happens by default.
32. Full initProject Pseudo-code
export async function initProject(
request: InitRequest,
context: ExecutionContext,
): Promise<CommandResult<InitResultData>> {
const startedAt = context.clock.monotonicMs();
const detected = await detectProject(request.cwd);
const template = getInitTemplate(request.template);
const input: InitTemplateInput = {
siteName: request.siteName ?? inferSiteName(request.cwd),
docsDir: request.docsDir ?? "docs",
detected,
};
const plan = await createInitPlan({ request, template, input, context });
const blocking = plan.diagnostics.some((d) => d.severity === "error");
if (blocking) {
return {
ok: false,
exitCode: 4,
diagnostics: plan.diagnostics,
data: {
template: request.template,
docsDir: input.docsDir,
written: false,
changes: summarizeChanges(plan.changes),
},
};
}
if (!request.dryRun) {
await applyInitPlan(plan, context);
}
return {
ok: true,
exitCode: 0,
diagnostics: plan.diagnostics,
data: {
template: request.template,
docsDir: input.docsDir,
written: !request.dryRun,
changes: summarizeChanges(plan.changes),
durationMs: context.clock.monotonicMs() - startedAt,
},
};
}
Exit code for conflict bisa 4 source error atau 3 config error tergantung sumber. Untuk init file conflict, 4 masuk akal karena masalah ada pada filesystem target.
33. Mermaid: Init State Machine
34. What Not To Build Yet
Jangan membangun semua sekarang.
Belum perlu:
- AI generation,
- full repo scanner,
- MDX compiler,
- dev server,
- theme system,
- OpenAPI reference generator,
- search index,
- MCP server,
- GitHub PR integration.
init harus kecil tapi benar.
Kita hanya butuh foundation agar command berikutnya punya project untuk dibaca.
35. Checklist Part 006
docforge init dianggap production-aware jika:
- membuat
docforge.config.json, - membuat
docs/default, - generated config valid terhadap schema,
- generated pages punya frontmatter minimal,
- tidak overwrite file user,
- support
--dry-run, - support
--template, - support
--yes, - memiliki diagnostics untuk conflict,
- bisa berjalan non-interactive,
- optional package script integration aman,
- path dalam config portable,
- JSON output machine-readable,
- rerun init tidak merusak project.
36. Ringkasan
Part ini membuat docforge init sebagai operasi yang aman dan predictable.
Poin paling penting:
initadalah orchestration, bukan copy template mentah.- Config awal sebaiknya declarative JSON, bukan executable config.
- Raw config, validated config, dan resolved config harus dipisah.
- Schema validation harus menghasilkan diagnostic yang actionable.
- Generated files harus minimal, jujur, dan tidak hallucinated.
--yesbukan izin overwrite.--dry-runmuncul natural jika kita memakai init plan.- Package script update harus non-destructive.
- Config path dan navigation harus portable.
- Init harus idempotent.
Di part berikutnya, kita akan melanjutkan ke configuration schema versioning.
Kita akan membahas bagaimana config berevolusi tanpa merusak project user: version field, migration, compatibility, deprecation diagnostics, feature flags, dan upgrade workflow.
You just completed lesson 06 in start here. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.
Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.