Learn Ai Docs Km Cli Part 005 Repository Scanning Core
title: Build From Scratch: Mintlify-like AI-driven Documentation Generator CLI - Part 005 description: Membangun repository scanner sebagai fondasi sistem AI documentation generator: traversal, ignore rules, hashing, incremental scan, metadata model, dan failure modelling. series: learn-ai-docs-km-cli seriesTitle: Build From Scratch: Mintlify-like AI-driven Documentation Generator CLI with Code2Prompt and Open-source Knowledge Management order: 5 partTitle: Repository Scanning Core tags:
- ai-docs
- documentation
- cli
- repository-scanning
- code2prompt
- knowledge-management
- mdx date: 2026-07-04
Part 005 — Repository Scanning Core
Di part sebelumnya kita sudah menetapkan bahwa sistem ini bukan sekadar generator Markdown. Sistem ini adalah pipeline yang mengubah repository menjadi artifact dokumentasi, context AI, dan knowledge graph yang bisa diverifikasi.
Sekarang kita masuk ke bagian pertama yang benar-benar menentukan kualitas seluruh pipeline: repository scanner.
Repository scanner adalah stage yang membaca codebase dan mengubahnya menjadi inventory terstruktur. Inventory ini akan dipakai oleh stage berikutnya: file classification, source tree model, symbol extraction, context compiler, documentation planner, verifier, dan knowledge sync.
Kalau scanner buruk, semua stage setelahnya ikut buruk.
Scanner yang buruk menghasilkan:
- file penting tidak terbaca,
- file sampah masuk ke prompt,
- generated file dianggap source of truth,
- secrets ikut dikirim ke model,
- incremental build tidak stabil,
- doc drift tidak bisa dideteksi,
- monorepo menjadi terlalu mahal untuk diproses,
- hasil generasi berubah-ubah walaupun source tidak berubah.
Scanner yang baik menghasilkan satu hal: peta repository yang deterministic, aman, dan bisa diulang.
1. Mental Model: Scanner Bukan find .
Cara berpikir paling lemah adalah menganggap scanner hanya melakukan ini:
find . -type f
Itu hanya traversal file system. Bukan repository understanding.
Scanner production-grade harus menjawab beberapa pertanyaan:
- File apa saja yang ada?
- File mana yang relevan untuk dokumentasi?
- File mana yang harus diabaikan?
- File mana yang berbahaya untuk dikirim ke AI provider?
- File mana yang berubah sejak scan terakhir?
- File mana yang kemungkinan generated?
- File mana yang terlalu besar untuk diproses?
- File mana yang binary?
- File mana yang merupakan source of truth?
- Bagaimana membangun scan result yang reproducible?
Repository scanner adalah controlled observation layer.
Ia tidak boleh langsung mengambil keputusan dokumentasi yang terlalu tinggi, tetapi ia harus menyediakan metadata yang cukup agar stage berikutnya bisa mengambil keputusan dengan baik.
2. Posisi Scanner dalam Pipeline
Scanner adalah stage pertama setelah konfigurasi dibaca.
Scanner tidak menulis docs. Scanner tidak memanggil LLM. Scanner tidak membuat ringkasan. Scanner hanya mengamati repository dan menghasilkan artifact mentah yang dapat diverifikasi.
Output scanner harus bisa disimpan sebagai artifact:
.aidocs/
scans/
latest.json
scan-2026-07-04T10-30-00Z.json
Kenapa scan result perlu disimpan?
Karena kita butuh:
- debugging,
- incremental build,
- drift detection,
- audit trail,
- reproducibility,
- comparison antar commit,
- CI report.
Kalau scan hanya hidup di memory, sistem akan sulit dijelaskan ketika hasil AI berbeda dari ekspektasi.
3. Prinsip Desain Scanner
Scanner kita harus mengikuti beberapa invariant.
3.1 Deterministic
Repository yang sama dengan konfigurasi yang sama harus menghasilkan scan result yang sama.
Artinya:
- traversal order harus stabil,
- path separator harus distandardisasi,
- timestamp file tidak boleh menjadi primary identity,
- hash content harus dipakai untuk mendeteksi perubahan,
- hasil output di-sort sebelum disimpan.
Jangan mengandalkan order bawaan file system.
3.2 Safe by Default
Scanner tidak boleh memasukkan semua file ke pipeline.
Ia harus punya default exclusion untuk:
.git/,node_modules/,target/,build/,dist/,.next/,.turbo/,.gradle/,.mvn/wrapper/binary jar,vendor/pada beberapa ecosystem,- lock/cache/output generated tertentu,
- binary files,
- file terlalu besar,
- file yang terlihat seperti secret dump.
Safe by default tidak berarti scanner menyembunyikan file. Ia tetap bisa melaporkan file yang diabaikan, tetapi tidak memprosesnya sebagai input AI.
3.3 Explainable
Setiap file yang di-include atau exclude harus punya alasan.
Contoh:
{
"path": "node_modules/lodash/index.js",
"included": false,
"reason": "excluded_by_default_vendor_directory"
}
Atau:
{
"path": "src/api/users.ts",
"included": true,
"reason": "source_file_candidate"
}
Ini penting untuk debugging. Ketika user bertanya, “kenapa endpoint ini tidak masuk dokumentasi?”, kita bisa menjawab dari artifact, bukan menebak.
3.4 Incremental-friendly
Scanner harus mengumpulkan metadata yang memungkinkan build berikutnya lebih murah.
Minimal:
- path,
- size,
- mtime,
- content hash,
- file type guess,
- include/exclude reason.
Tapi jangan bergantung hanya pada mtime. Banyak situasi membuat mtime tidak cukup:
- checkout ulang repo,
- container build,
- generated workspace,
- file restore,
- CI cache,
- clock skew.
mtime boleh dipakai sebagai shortcut, tetapi contentHash tetap menjadi kebenaran untuk perubahan isi.
3.5 Provider-safe
Scanner harus memisahkan dua konsep:
- file yang ada di repo,
- file yang boleh masuk context AI.
Tidak semua file yang bisa dibaca boleh dikirim ke model.
Contoh:
.envmungkin penting untuk memahami config names, tapi tidak boleh dikirim mentah.- sample
.env.exampleboleh dipertimbangkan. - private key harus ditolak.
- database dump harus ditolak.
- customer data fixture harus dicurigai.
Scanner bukan secret scanner penuh, tetapi ia harus memicu safety stage awal.
4. Output Model Scanner
Kita mulai dari data model.
Scanner harus menghasilkan ScanResult.
export type ScanResult = {
schemaVersion: "scan.v1";
repositoryRoot: string;
scannedAt: string;
configHash: string;
git?: GitMetadata;
summary: ScanSummary;
files: ScannedFile[];
directories: ScannedDirectory[];
warnings: ScanWarning[];
};
Field penting:
schemaVersion: agar artifact bisa berevolusi.repositoryRoot: root path normalized.scannedAt: audit metadata, bukan untuk determinism.configHash: scanner result dipengaruhi config.git: commit/branch kalau tersedia.summary: ringkasan jumlah file, size, ignored, included.files: daftar file hasil scan.directories: metadata directory.warnings: masalah yang perlu ditindaklanjuti.
4.1 File Model
export type ScannedFile = {
path: string;
absolutePath?: string;
name: string;
extension?: string;
sizeBytes: number;
mtimeMs?: number;
contentHash?: string;
binary: boolean;
readable: boolean;
included: boolean;
includeReason?: string;
excludeReason?: string;
ignoreMatches: IgnoreMatch[];
detectedKind?: DetectedFileKind;
safetyFlags: SafetyFlag[];
lineCount?: number;
};
Untuk artifact yang committed atau dibagikan, hindari menyimpan absolutePath karena bisa membocorkan struktur mesin developer.
path harus relatif terhadap repository root dan menggunakan / sebagai separator, bahkan di Windows.
Contoh path stabil:
src/main/java/com/example/api/UserResource.java
Bukan:
C:\Users\alice\workspace\repo\src\main\java\com\example\api\UserResource.java
4.2 Directory Model
export type ScannedDirectory = {
path: string;
fileCount: number;
includedFileCount: number;
totalSizeBytes: number;
detectedRole?: DirectoryRole;
excludeReason?: string;
};
Directory role berguna untuk repository map:
export type DirectoryRole =
| "source"
| "test"
| "documentation"
| "configuration"
| "build_output"
| "dependency_vendor"
| "ci"
| "examples"
| "schema"
| "unknown";
Di stage scanning, role ini masih boleh bersifat sementara. Stage classification akan memperkaya.
5. Traversal Strategy
Traversal repository terlihat sederhana, tetapi banyak bug muncul di sini.
Hal yang harus diputuskan:
- Apakah scanner mengikuti symlink?
- Apakah scanner membaca hidden files?
- Apakah scanner berhenti ketika directory excluded?
- Apakah scanner tetap melaporkan excluded files?
- Bagaimana menangani permission error?
- Bagaimana menangani file berubah saat scan berlangsung?
5.1 Default Traversal Rule
Default yang aman:
- baca hidden files penting seperti
.github/,.gitignore,.env.example,.npmrc.example,.mvn/,.editorconfig,.prettierrc,.eslintrc,.tool-versions,.java-version,.nvmrc; - jangan traverse
.git/; - jangan follow symlink secara default;
- jangan masuk directory dependency/build output;
- laporkan directory yang di-skip sebagai metadata;
- tangani permission error sebagai warning, bukan crash total.
5.2 Pseudocode Traversal
function scanRepository(root: string, config: ScannerConfig): ScanResult {
const state = createScanState(root, config);
const queue = [root];
while (queue.length > 0) {
const currentDir = queue.shift()!;
const dirEntries = safeReadDirectory(currentDir);
for (const entry of sortByName(dirEntries)) {
const relativePath = normalizeRelativePath(root, entry.path);
const decision = evaluatePath(relativePath, entry, config, state.ignoreEngine);
if (entry.isDirectory) {
state.directories.push(buildDirectoryRecord(entry, decision));
if (decision.traverse) {
queue.push(entry.path);
}
continue;
}
if (entry.isFile) {
const fileRecord = scanFile(entry, relativePath, decision, config);
state.files.push(fileRecord);
}
}
}
return finalizeScanResult(state);
}
Perhatikan ada dua keputusan berbeda:
traverse: apakah masuk ke directory.included: apakah file dipakai sebagai candidate input.
Directory bisa tidak di-traverse karena terlalu jelas tidak relevan, seperti node_modules/.
File bisa discan metadata-nya tetapi tidak di-include, seperti .env.
6. Ignore Rules
Scanner butuh ignore engine. Jangan hanya hardcode beberapa folder.
Sumber ignore rules:
- default ignore rules dari tool,
.gitignore,.aidocsignoreatau.docignore,- config eksplisit di
aidocs.config.*, - command override seperti
--includedan--exclude.
Urutan prioritas harus jelas.
6.1 Suggested Precedence
1. Force include from CLI/config
2. Force exclude from CLI/config
3. .aidocsignore / .docignore
4. .gitignore
5. Built-in safety exclusions
6. Built-in default exclusions
7. Include by default if readable text-like file
Kenapa force include di atas force exclude? Ini pilihan desain yang harus hati-hati.
Untuk safe-by-default system, saya lebih suka:
1. Hard safety blocklist
2. Force include from CLI/config
3. Force exclude from CLI/config
4. .aidocsignore / .docignore
5. .gitignore
6. Built-in default exclusions
7. Include by default if readable text-like file
Hard safety blocklist tetap tidak boleh dikalahkan oleh user kecuali memakai flag eksplisit seperti:
aidocs scan --unsafe-allow-secret-like-files
Flag seperti ini sebaiknya hanya ada untuk debugging lokal, tidak aktif di CI.
6.2 .aidocsignore
Kenapa perlu .aidocsignore kalau sudah ada .gitignore?
Karena file yang tracked Git belum tentu cocok untuk prompt AI.
Contoh file tracked tapi tidak cocok untuk AI context:
fixtures/customer-production-like.json
load-test/large-payloads.json
benchmarks/generated-baseline.txt
public/vendor.bundle.js
snapshots/huge-response.snap
.gitignore menjawab: “apakah file ini masuk version control?”
.aidocsignore menjawab: “apakah file ini layak masuk documentation intelligence pipeline?”
Contoh .aidocsignore:
# Large generated snapshots
**/__snapshots__/**
*.snap
# Sensitive fixtures
fixtures/customer-data/**
fixtures/**/prod-like-*.json
# Generated bundles
public/**/*.bundle.js
coverage/**
# Local notes not intended for generated docs
private-notes/**
7. Binary Detection
Scanner harus bisa membedakan text file dan binary file.
Jangan hanya bergantung pada extension.
File .txt bisa berisi binary dump. File tanpa extension bisa berisi script. File .pem text tetapi sensitif.
Strategi sederhana:
- baca beberapa KB pertama,
- cari null byte,
- hitung rasio karakter non-printable,
- cek known binary extensions,
- cek size.
Pseudocode:
function isProbablyBinary(sample: Buffer): boolean {
if (sample.includes(0)) return true;
let suspicious = 0;
for (const byte of sample) {
const printable =
byte === 9 || byte === 10 || byte === 13 ||
(byte >= 32 && byte <= 126) ||
byte >= 128; // allow UTF-8-ish bytes, validate separately if needed
if (!printable) suspicious++;
}
return suspicious / Math.max(sample.length, 1) > 0.30;
}
Known binary extensions:
.png .jpg .jpeg .gif .webp .ico .pdf
.zip .tar .gz .7z .rar
.jar .war .class
.exe .dll .so .dylib
.ttf .otf
.mp4 .mov .mp3 .wav
.sqlite .db
Tapi jangan menjadikan extension sebagai satu-satunya kebenaran.
8. File Size and Line Limits
File terlalu besar dapat merusak pipeline.
Masalahnya bukan hanya token cost. File besar sering kali:
- generated,
- minified,
- snapshot,
- vendored,
- dump,
- bundle,
- lock file,
- machine output.
Default limit yang masuk akal:
export type ScannerLimits = {
maxFileSizeBytesForContentRead: number; // e.g. 1 MB
maxFileSizeBytesForPromptCandidate: number; // e.g. 300 KB
maxLineCountForPromptCandidate: number; // e.g. 5000 lines
};
File yang terlalu besar tetap bisa dicatat metadata-nya:
{
"path": "test/fixtures/huge-response.json",
"sizeBytes": 12890122,
"included": false,
"excludeReason": "file_too_large_for_prompt_candidate"
}
Kalau file besar itu penting, stage berikutnya bisa membuat summary extraction khusus, bukan memasukkan file mentah ke prompt.
9. Content Hashing
Content hash adalah dasar incremental scan dan drift detection.
Gunakan hash dari isi file, bukan metadata file.
Contoh:
function hashContent(buffer: Buffer): string {
return createHash("sha256").update(buffer).digest("hex");
}
Hash digunakan untuk:
- mendeteksi file berubah,
- cache extraction result,
- membandingkan scan antar commit,
- menghindari regenerasi docs yang tidak perlu,
- membangun provenance.
9.1 Hash Path vs Hash Content
Perlu dibedakan:
contentHash: isi file.identityHash: path + content hash.
Kalau file dipindahkan tanpa perubahan isi, contentHash sama, tapi path berubah. Ini tetap penting karena docs link dan ownership bisa berubah.
const contentHash = sha256(content);
const identityHash = sha256(`${relativePath}\n${contentHash}`);
10. Git Metadata
Kalau repository adalah Git repo, scanner sebaiknya mengambil metadata Git.
export type GitMetadata = {
available: boolean;
branch?: string;
commit?: string;
dirty?: boolean;
remoteUrlHash?: string;
};
Jangan menyimpan remote URL mentah jika artifact bisa dibagikan. Remote URL bisa mengandung nama organisasi atau path internal.
Gunakan hash atau redacted value.
Git metadata berguna untuk:
- audit,
- CI report,
- doc provenance,
- detecting dirty local generation,
- comparing docs generated from commit A vs B.
10.1 Dirty Working Tree
Jika working tree dirty, docs yang dihasilkan tidak merepresentasikan commit bersih.
Scanner harus mencatat:
{
"git": {
"available": true,
"branch": "feature/docs-cli",
"commit": "abc1234",
"dirty": true
}
}
CLI bisa memperingatkan:
Warning: repository has uncommitted changes. Generated docs will not map cleanly to a committed revision.
Tapi jangan otomatis gagal. Local workflow sering memang berjalan pada dirty tree.
CI mode bisa lebih ketat.
11. Symlink Handling
Symlink bisa menyebabkan:
- infinite traversal,
- keluar dari repository root,
- membaca file sensitif di luar repo,
- duplicate scan,
- platform-specific behavior.
Default paling aman:
- jangan follow symlink,
- record symlink sebagai entry,
- berikan opsi eksplisit untuk follow symlink yang tetap berada di dalam root.
export type SymlinkPolicy = "ignore" | "record_only" | "follow_inside_root" | "follow_all_unsafe";
Default:
symlinkPolicy: "record_only"
Jika mengikuti symlink, scanner harus resolve real path dan mendeteksi cycle.
const visitedRealPaths = new Set<string>();
Kalau real path keluar dari root, tolak kecuali unsafe mode.
12. Permission and Race Conditions
Repository bisa berubah saat sedang discan.
File bisa:
- dihapus setelah directory listing,
- berubah saat dibaca,
- tidak punya permission,
- locked oleh process lain.
Scanner tidak boleh crash total karena satu file gagal.
Record warning:
{
"code": "file_read_failed",
"path": "tmp/generated.json",
"message": "File disappeared during scan"
}
Strategi:
- best effort local scan,
- fail hard only untuk root tidak valid atau config corrupt,
- CI mode dapat mengubah warning tertentu menjadi failure.
13. Scan Summary
Ringkasan scan membantu user memahami repository tanpa membuka JSON besar.
export type ScanSummary = {
totalFiles: number;
includedFiles: number;
excludedFiles: number;
binaryFiles: number;
unreadableFiles: number;
totalSizeBytes: number;
includedSizeBytes: number;
safetyFlaggedFiles: number;
largestFiles: { path: string; sizeBytes: number }[];
topExtensions: { extension: string; count: number }[];
};
CLI output:
Scan complete
Repository: .
Files seen: 1,284
Included candidates: 214
Excluded: 1,070
Binary: 42
Safety flagged: 3
Total size: 84.2 MB
Included text size: 1.8 MB
Warnings:
- 3 files look secret-like and were excluded
- 1 symlink points outside repository root and was not followed
User harus langsung tahu apakah scan masuk akal.
14. Safety Flags
Scanner bukan replacement untuk secret scanner khusus, tetapi harus punya early warning.
export type SafetyFlag =
| "secret_like_filename"
| "secret_like_content"
| "env_file"
| "private_key_like_content"
| "large_data_dump"
| "customer_data_like_fixture"
| "binary_content"
| "outside_repository_symlink";
14.1 Filename-based Signals
File yang harus dicurigai:
.env
.env.local
.env.production
id_rsa
id_dsa
*.pem
*.key
*.p12
*.keystore
credentials.json
service-account.json
14.2 Content-based Signals
Pattern yang patut dicurigai:
-----BEGIN PRIVATE KEY-----
-----BEGIN RSA PRIVATE KEY-----
AWS_SECRET_ACCESS_KEY=
Authorization: Bearer
password=
api_key=
secret=
token=
Jangan tampilkan value mentah dalam report. Tampilkan hanya path, flag, dan alasan.
{
"path": ".env.local",
"safetyFlags": ["env_file", "secret_like_content"],
"included": false,
"excludeReason": "safety_sensitive_file"
}
15. Scanner Config
Scanner harus configurable, tetapi default harus kuat.
export type ScannerConfig = {
root: string;
include?: string[];
exclude?: string[];
respectGitignore: boolean;
respectAidocsIgnore: boolean;
symlinkPolicy: SymlinkPolicy;
limits: ScannerLimits;
hashAlgorithm: "sha256";
safety: {
enabled: boolean;
excludeSecretLikeFiles: boolean;
failOnSecretLikeFilesInCi: boolean;
};
};
Default:
const defaultScannerConfig: ScannerConfig = {
root: ".",
respectGitignore: true,
respectAidocsIgnore: true,
symlinkPolicy: "record_only",
limits: {
maxFileSizeBytesForContentRead: 1_000_000,
maxFileSizeBytesForPromptCandidate: 300_000,
maxLineCountForPromptCandidate: 5_000,
},
hashAlgorithm: "sha256",
safety: {
enabled: true,
excludeSecretLikeFiles: true,
failOnSecretLikeFilesInCi: true,
},
};
16. Artifact Format: scan.v1
Contoh hasil scan ringkas:
{
"schemaVersion": "scan.v1",
"repositoryRoot": ".",
"scannedAt": "2026-07-04T10:30:00.000Z",
"configHash": "7c2f...",
"git": {
"available": true,
"branch": "main",
"commit": "abc1234",
"dirty": false
},
"summary": {
"totalFiles": 1284,
"includedFiles": 214,
"excludedFiles": 1070,
"binaryFiles": 42,
"unreadableFiles": 0,
"totalSizeBytes": 84200000,
"includedSizeBytes": 1800000,
"safetyFlaggedFiles": 3,
"largestFiles": [
{ "path": "fixtures/huge.json", "sizeBytes": 12890122 }
],
"topExtensions": [
{ "extension": ".ts", "count": 128 },
{ "extension": ".md", "count": 37 }
]
},
"files": [
{
"path": "src/api/users.ts",
"name": "users.ts",
"extension": ".ts",
"sizeBytes": 8420,
"contentHash": "19ab...",
"binary": false,
"readable": true,
"included": true,
"includeReason": "source_file_candidate",
"ignoreMatches": [],
"detectedKind": "source_code",
"safetyFlags": [],
"lineCount": 241
}
],
"directories": [],
"warnings": []
}
Kita belum melakukan classification final di sini. detectedKind boleh preliminary.
17. Implementation Skeleton
Struktur modul:
src/
scanner/
scanRepository.ts
ignoreEngine.ts
binaryDetection.ts
safetyDetection.ts
hashing.ts
gitMetadata.ts
pathUtils.ts
scanTypes.ts
17.1 Path Normalization
export function normalizeRepoPath(path: string): string {
return path.replace(/\\/g, "/").replace(/^\.\//, "");
}
Rule:
- no leading
./, - no backslash,
- no absolute path,
- directory path should not need trailing slash in file model,
- sorting lexicographic by normalized path.
17.2 Safe Directory Read
export function safeReadDirectory(dir: string): DirectoryEntryResult {
try {
return {
ok: true,
entries: fs.readdirSync(dir, { withFileTypes: true }),
};
} catch (error) {
return {
ok: false,
entries: [],
warning: toScanWarning("directory_read_failed", dir, error),
};
}
}
Jangan biarkan exception raw tersebar ke semua command.
17.3 File Read Policy
Tidak semua file perlu dibaca penuh.
18. Incremental Scan
Ada dua level incremental:
- incremental metadata scan,
- incremental downstream processing.
Scanner tetap perlu melihat file system, tetapi bisa menghindari membaca ulang content jika metadata tidak berubah.
Cache previous scan:
export type PreviousFileState = {
path: string;
sizeBytes: number;
mtimeMs?: number;
contentHash?: string;
};
Optimization:
function canReuseHash(previous: PreviousFileState, currentStat: fs.Stats): boolean {
return previous.sizeBytes === currentStat.size && previous.mtimeMs === currentStat.mtimeMs;
}
Tetapi ada trade-off:
- cepat tetapi bisa salah kalau mtime tidak reliable,
- lambat tetapi akurat kalau selalu hash ulang.
Mode yang disarankan:
local fast mode: reuse hash if size + mtime same
ci strict mode: hash all included candidate files
19. Monorepo Considerations
Monorepo membuat scanner lebih sulit karena:
- banyak package,
- banyak language,
- multiple docs roots,
- multiple OpenAPI specs,
- shared packages,
- generated output besar,
- nested Git repos atau submodules,
- workspace tooling.
Scanner harus mendeteksi boundary:
packages/api/package.json
packages/web/package.json
services/billing/pom.xml
services/order/build.gradle
libs/contracts/openapi.yaml
Stage scanning belum harus memahami semua dependency, tapi harus menyediakan data untuk part berikutnya.
Tambahkan workspaceHints:
export type WorkspaceHint = {
path: string;
kind: "npm_package" | "maven_project" | "gradle_project" | "go_module" | "python_project" | "rust_crate" | "unknown";
manifestPath: string;
};
Contoh:
{
"path": "services/order",
"kind": "maven_project",
"manifestPath": "services/order/pom.xml"
}
Ini akan membantu documentation planner membuat docs per service/module.
20. Failure Model Scanner
Scanner harus punya failure model yang eksplisit.
| Failure | Penyebab | Behavior Local | Behavior CI |
|---|---|---|---|
| Root tidak ada | path salah | fail | fail |
| Config invalid | syntax error | fail | fail |
| Directory unreadable | permission | warning | configurable fail |
| File disappeared | race condition | warning | warning |
| Secret-like file found | .env, key | exclude + warning | fail by default |
| Too many files | monorepo besar | warning + limits | fail or require config |
| File terlalu besar | dump/generated | exclude | exclude |
| Binary file | asset/bundle | exclude | exclude |
| Symlink outside root | unsafe symlink | exclude + warning | fail by default |
Kenapa penting?
Karena CLI documentation generator akan dipakai dalam dua konteks berbeda:
- local developer workflow yang harus toleran,
- CI governance workflow yang harus ketat.
Satu behavior untuk semua konteks akan membuat tool tidak nyaman atau tidak aman.
21. Testing Scanner
Scanner harus punya test serius. Ini fondasi sistem.
21.1 Golden Repository Fixtures
Buat fixture repository kecil:
fixtures/
repos/
simple-node/
java-service/
monorepo-mixed/
repo-with-secrets/
repo-with-symlinks/
repo-with-large-files/
Test output scan disimpan sebagai snapshot.
21.2 Test Cases Minimum
- exclude
.git/, - respect
.gitignore, - respect
.aidocsignore, - include hidden config file penting,
- detect binary file,
- detect secret-like file,
- detect large file,
- stable sorted output,
- consistent hash,
- symlink outside root blocked,
- unreadable file warning,
- monorepo workspace hints.
21.3 Determinism Test
Scan dua kali repository yang sama.
Bandingkan result setelah field volatile dihapus:
function normalizeForDeterminism(scan: ScanResult): unknown {
return {
...scan,
scannedAt: "<removed>",
};
}
Output harus sama.
22. CLI UX untuk scan
Command awal:
aidocs scan
Dengan opsi:
aidocs scan --json
aidocs scan --root ./services/order
aidocs scan --strict
aidocs scan --no-gitignore
aidocs scan --include "src/**"
aidocs scan --exclude "fixtures/**"
aidocs scan --write .aidocs/scans/latest.json
Output human-readable:
Repository scan complete
Root: .
Files scanned: 1,284
Prompt candidates: 214
Excluded by ignore rules: 947
Excluded by safety rules: 3
Excluded as binary: 42
Warnings: 4
Artifacts:
- .aidocs/scans/latest.json
Output JSON harus cocok untuk automation:
aidocs scan --json > scan.json
23. Relationship dengan Stage Berikutnya
Scanner menghasilkan inventory. File classifier akan memperkaya inventory.
Scanner tidak perlu menjawab:
- “file ini menjelaskan public API apa?”
- “file ini harus masuk halaman docs mana?”
- “fungsi ini punya behavior apa?”
Itu tugas part berikutnya.
Scanner hanya menjawab:
- “file ini ada,”
- “file ini aman/tidak aman,”
- “file ini berubah/tidak berubah,”
- “file ini kandidat/tidak kandidat untuk diproses.”
24. Common Mistakes
Mistake 1 — Mengirim Semua File ke LLM
Ini mahal, lambat, dan tidak aman.
Context window besar bukan alasan untuk memasukkan semua file.
Mistake 2 — Mengandalkan .gitignore Saja
File tracked bisa tetap sensitif atau irrelevant.
Mistake 3 — Tidak Menyimpan Scan Artifact
Tanpa artifact, bug documentation generation sulit direproduksi.
Mistake 4 — Tidak Membedakan Local dan CI Mode
Local mode perlu toleran. CI mode perlu deterministic dan strict.
Mistake 5 — Tidak Punya Include/Exclude Reason
User tidak akan percaya tool yang tidak bisa menjelaskan kenapa file tertentu dipakai atau diabaikan.
Mistake 6 — Tidak Memikirkan Monorepo dari Awal
Monorepo bukan edge case untuk developer tools modern. Ia harus dipertimbangkan sejak scanner.
25. Minimal Acceptance Criteria
Scanner dianggap cukup untuk lanjut ke part berikutnya jika mampu:
- membaca repository root,
- menghasilkan list file normalized,
- respect default ignores,
- respect
.gitignore, - respect
.aidocsignore, - detect binary file,
- detect file terlalu besar,
- detect secret-like file sederhana,
- menghasilkan content hash,
- menghasilkan summary,
- menyimpan
scan.v1artifact, - output deterministic,
- punya warning model,
- punya local dan CI strict mode.
Kalau belum memenuhi ini, jangan masuk ke AI generation. Hasilnya akan rapuh.
26. Mini Implementation Checklist
[ ] Define scan.v1 schema
[ ] Implement path normalization
[ ] Implement sorted traversal
[ ] Implement default ignore rules
[ ] Implement .gitignore loading
[ ] Implement .aidocsignore loading
[ ] Implement binary detection
[ ] Implement size limits
[ ] Implement safety flag detection
[ ] Implement content hashing
[ ] Implement git metadata reader
[ ] Implement scan summary
[ ] Implement JSON artifact writer
[ ] Implement deterministic test
[ ] Implement fixture-based tests
[ ] Implement CLI command: aidocs scan
27. What We Built Conceptually
Kita belum menulis generator dokumentasi. Kita membangun mata sistem.
Repository scanner adalah stage yang menentukan apakah sistem ini akan menjadi:
- AI wrapper rapuh yang membaca file sembarangan, atau
- documentation intelligence pipeline yang bisa dipercaya.
Di part berikutnya, kita akan mengambil ScanResult ini dan melakukan file classification: membedakan source code, config, contract, docs, tests, generated files, examples, schema, dan artifact lain.
Scanner menjawab: apa yang ada di repository?
Classifier menjawab: apa arti file itu bagi dokumentasi?
You just completed lesson 05 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.