laporan-generator 2.6.2 → 2.7.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/.agents/skills/laporan-generator/SKILL.md +99 -109
- package/.agents/skills/laporan-generator/resources/laporan +37 -4
- package/.agents/skills/laporan-generator/resources/laporan.ps1 +74 -28
- package/.agents/skills/laporan-generator/resources/scripts/bundle.py +11 -2
- package/.agents/skills/laporan-generator/resources/scripts/report-doctor.py +121 -48
- package/.agents/skills/laporan-generator/resources/scripts/report-stats.py +53 -2
- package/.agents/skills/laporan-generator/resources/scripts/test_scripts.py +63 -2
- package/.agents/skills/laporan-generator/scripts/bundle.py +11 -2
- package/.agents/skills/laporan-generator/scripts/finalize-docx.py +140 -1
- package/.agents/skills/laporan-generator/scripts/make-reference-docx.py +42 -2
- package/.agents/skills/laporan-generator/scripts/report-doctor.py +121 -48
- package/.agents/skills/laporan-generator/scripts/report-stats.py +53 -2
- package/.agents/skills/laporan-generator/scripts/test_scripts.py +88 -2
- package/README.md +44 -0
- package/bin/laporan-generator.js +15 -3
- package/bin/mcp-server.js +4 -0
- package/laporan +37 -4
- package/laporan.ps1 +50 -11
- package/lib/init.js +115 -44
- package/lib/mcp.js +569 -0
- package/lib/sync-hosts.js +196 -68
- package/lib/uninstall.js +21 -0
- package/package.json +9 -3
- package/scripts/bundle.py +11 -2
- package/scripts/report-doctor.py +121 -48
- package/scripts/report-stats.py +46 -2
- package/scripts/test_scripts.py +50 -3
package/laporan.ps1
CHANGED
|
@@ -9,7 +9,9 @@ param (
|
|
|
9
9
|
[Parameter(Position=2)]
|
|
10
10
|
[string]$Arg1 = "",
|
|
11
11
|
[Parameter(Position=3)]
|
|
12
|
-
[string]$Arg2 = ""
|
|
12
|
+
[string]$Arg2 = "",
|
|
13
|
+
[Parameter(ValueFromRemainingArguments=$true)]
|
|
14
|
+
[string[]]$RemainingArgs
|
|
13
15
|
)
|
|
14
16
|
|
|
15
17
|
$ErrorActionPreference = "Stop"
|
|
@@ -38,7 +40,7 @@ function Get-PythonCommand {
|
|
|
38
40
|
|
|
39
41
|
function Show-Banner {
|
|
40
42
|
Write-Host " ========================================================" -ForegroundColor Cyan
|
|
41
|
-
Write-Host " LAPORAN GENERATOR CLI v2.
|
|
43
|
+
Write-Host " LAPORAN GENERATOR CLI v2.7.0 " -ForegroundColor Cyan
|
|
42
44
|
Write-Host " Otomatisasi Dokumen Akademik (Typst + DOCX Engine) " -ForegroundColor Cyan
|
|
43
45
|
Write-Host " ========================================================" -ForegroundColor Cyan
|
|
44
46
|
Write-Host ""
|
|
@@ -453,27 +455,30 @@ function Cmd-Clean {
|
|
|
453
455
|
}
|
|
454
456
|
|
|
455
457
|
function Cmd-Stats {
|
|
458
|
+
param([string[]]$ArgsList)
|
|
456
459
|
$pyCmd = Get-PythonCommand
|
|
457
460
|
if ($pyCmd) {
|
|
458
|
-
& $pyCmd scripts/report-stats.py
|
|
461
|
+
& $pyCmd scripts/report-stats.py @ArgsList
|
|
459
462
|
} else {
|
|
460
463
|
Write-Host "Error: Python 3 tidak ditemukan." -ForegroundColor Red
|
|
461
464
|
}
|
|
462
465
|
}
|
|
463
466
|
|
|
464
467
|
function Cmd-Doctor {
|
|
468
|
+
param([string[]]$ArgsList)
|
|
465
469
|
$pyCmd = Get-PythonCommand
|
|
466
470
|
if ($pyCmd) {
|
|
467
|
-
& $pyCmd scripts/report-doctor.py
|
|
471
|
+
& $pyCmd scripts/report-doctor.py @ArgsList
|
|
468
472
|
} else {
|
|
469
473
|
Write-Host "Error: Python 3 tidak ditemukan." -ForegroundColor Red
|
|
470
474
|
}
|
|
471
475
|
}
|
|
472
476
|
|
|
473
477
|
function Cmd-Bundle {
|
|
478
|
+
param([string[]]$ArgsList)
|
|
474
479
|
$pyCmd = Get-PythonCommand
|
|
475
480
|
if ($pyCmd) {
|
|
476
|
-
& $pyCmd scripts/bundle.py
|
|
481
|
+
& $pyCmd scripts/bundle.py @ArgsList
|
|
477
482
|
} else {
|
|
478
483
|
Write-Host "Error: Python 3 tidak ditemukan." -ForegroundColor Red
|
|
479
484
|
}
|
|
@@ -525,25 +530,59 @@ function Cmd-Watch {
|
|
|
525
530
|
}
|
|
526
531
|
}
|
|
527
532
|
|
|
533
|
+
$extraArgs = @()
|
|
534
|
+
if ($SubCommand) { $extraArgs += $SubCommand }
|
|
535
|
+
if ($Arg1) { $extraArgs += $Arg1 }
|
|
536
|
+
if ($Arg2) { $extraArgs += $Arg2 }
|
|
537
|
+
if ($RemainingArgs) { $extraArgs += $RemainingArgs }
|
|
538
|
+
|
|
528
539
|
switch ($Command.ToLower()) {
|
|
529
540
|
"build" { Cmd-Build }
|
|
530
541
|
"pdf" { Cmd-Build-PDF }
|
|
531
542
|
"docx" { Cmd-Build-DOCX }
|
|
532
543
|
"init" { Cmd-Init }
|
|
533
|
-
"stats" { Cmd-Stats }
|
|
534
|
-
"doctor" { Cmd-Doctor }
|
|
535
|
-
"bundle"
|
|
536
|
-
"setup"
|
|
544
|
+
"stats" { Cmd-Stats $extraArgs }
|
|
545
|
+
"doctor" { Cmd-Doctor $extraArgs }
|
|
546
|
+
"bundle" { Cmd-Bundle $extraArgs }
|
|
547
|
+
"setup" { & powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "scripts\setup-deps.ps1") }
|
|
548
|
+
"mcp" {
|
|
549
|
+
if (Get-Command "node" -ErrorAction SilentlyContinue) {
|
|
550
|
+
$mcpPath = Join-Path $PSScriptRoot "bin\mcp-server.js"
|
|
551
|
+
if (Test-Path $mcpPath) {
|
|
552
|
+
node $mcpPath @extraArgs
|
|
553
|
+
} elseif (Get-Command "npx" -ErrorAction SilentlyContinue) {
|
|
554
|
+
npx -y laporan-generator mcp @extraArgs
|
|
555
|
+
} else {
|
|
556
|
+
Write-Host "bin\mcp-server.js tidak ditemukan dan npx tidak tersedia." -ForegroundColor Red
|
|
557
|
+
}
|
|
558
|
+
} else {
|
|
559
|
+
Write-Host "Node.js diperlukan untuk menjalankan MCP Server." -ForegroundColor Yellow
|
|
560
|
+
}
|
|
561
|
+
}
|
|
537
562
|
"sync-skills" {
|
|
538
563
|
if (Get-Command "node" -ErrorAction SilentlyContinue) {
|
|
539
|
-
|
|
564
|
+
$genPath = Join-Path $PSScriptRoot "bin\laporan-generator.js"
|
|
565
|
+
if (Test-Path $genPath) {
|
|
566
|
+
node $genPath sync-hosts @extraArgs
|
|
567
|
+
} elseif (Get-Command "npx" -ErrorAction SilentlyContinue) {
|
|
568
|
+
npx -y laporan-generator sync-hosts @extraArgs
|
|
569
|
+
} else {
|
|
570
|
+
Write-Host "bin\laporan-generator.js tidak ditemukan dan npx tidak tersedia." -ForegroundColor Red
|
|
571
|
+
}
|
|
540
572
|
} else {
|
|
541
573
|
Write-Host "Node.js diperlukan untuk sinkronisasi multi-host AI agent." -ForegroundColor Yellow
|
|
542
574
|
}
|
|
543
575
|
}
|
|
544
576
|
"sync-hosts" {
|
|
545
577
|
if (Get-Command "node" -ErrorAction SilentlyContinue) {
|
|
546
|
-
|
|
578
|
+
$genPath = Join-Path $PSScriptRoot "bin\laporan-generator.js"
|
|
579
|
+
if (Test-Path $genPath) {
|
|
580
|
+
node $genPath sync-hosts @extraArgs
|
|
581
|
+
} elseif (Get-Command "npx" -ErrorAction SilentlyContinue) {
|
|
582
|
+
npx -y laporan-generator sync-hosts @extraArgs
|
|
583
|
+
} else {
|
|
584
|
+
Write-Host "bin\laporan-generator.js tidak ditemukan dan npx tidak tersedia." -ForegroundColor Red
|
|
585
|
+
}
|
|
547
586
|
} else {
|
|
548
587
|
Write-Host "Node.js diperlukan untuk sinkronisasi multi-host AI agent." -ForegroundColor Yellow
|
|
549
588
|
}
|
package/lib/init.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
|
+
const os = require('os');
|
|
7
|
+
|
|
6
8
|
function copyRecursiveSync(src, dest) {
|
|
7
9
|
if (!fs.existsSync(src)) return;
|
|
8
10
|
const stats = fs.statSync(src);
|
|
@@ -18,25 +20,47 @@ function copyRecursiveSync(src, dest) {
|
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
function
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
function initProject(options = {}) {
|
|
24
|
+
const targetDir = options.targetDir ? path.resolve(options.targetDir) : process.cwd();
|
|
25
|
+
const silent = !!options.silent;
|
|
26
|
+
|
|
27
|
+
if (!fs.existsSync(targetDir)) {
|
|
28
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
29
|
+
}
|
|
26
30
|
|
|
27
|
-
const metaPath = path.join(
|
|
28
|
-
if (fs.existsSync(metaPath)) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
const metaPath = path.join(targetDir, 'metadata.yml');
|
|
32
|
+
if (fs.existsSync(metaPath) && !options.force) {
|
|
33
|
+
if (!silent) {
|
|
34
|
+
console.log('[WARN] Dokumen laporan sudah ada di direktori ini (metadata.yml ditemukan).');
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
alreadyExists: true,
|
|
39
|
+
targetDir,
|
|
40
|
+
message: 'Dokumen laporan sudah ada di direktori ini (metadata.yml ditemukan).'
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Cari direktori resources (prioritas: skill resources, package root, global home)
|
|
45
|
+
const candidates = [
|
|
46
|
+
path.join(__dirname, '../.agents/skills/laporan-generator/resources'),
|
|
47
|
+
path.join(__dirname, '..'),
|
|
48
|
+
path.join(os.homedir(), '.agents/skills/laporan-generator/resources'),
|
|
49
|
+
path.join(os.homedir(), '.gemini/config/skills/laporan-generator/resources')
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
let sourceDir = null;
|
|
53
|
+
for (const c of candidates) {
|
|
54
|
+
if (fs.existsSync(path.join(c, 'template.typ')) && fs.existsSync(path.join(c, 'metadata.yml'))) {
|
|
55
|
+
sourceDir = c;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
31
58
|
}
|
|
32
59
|
|
|
33
|
-
|
|
34
|
-
const pkgResources = path.join(__dirname, '../.agents/skills/laporan-generator/resources');
|
|
35
|
-
const globalResources = path.join(require('os').homedir(), '.agents/skills/laporan-generator/resources');
|
|
36
|
-
const sourceDir = fs.existsSync(pkgResources) ? pkgResources : (fs.existsSync(globalResources) ? globalResources : null);
|
|
60
|
+
const copiedFiles = [];
|
|
37
61
|
|
|
38
62
|
if (sourceDir) {
|
|
39
|
-
console.log(`Menyalin template lengkap dari: ${sourceDir} ...\n`);
|
|
63
|
+
if (!silent) console.log(`Menyalin template lengkap dari: ${sourceDir} ...\n`);
|
|
40
64
|
const filesToCopy = [
|
|
41
65
|
'metadata.yml',
|
|
42
66
|
'cover.md',
|
|
@@ -58,61 +82,108 @@ function run(argv = []) {
|
|
|
58
82
|
|
|
59
83
|
for (const f of filesToCopy) {
|
|
60
84
|
const srcFile = path.join(sourceDir, f);
|
|
61
|
-
const destFile = path.join(
|
|
62
|
-
if (fs.existsSync(srcFile) && !fs.existsSync(destFile)) {
|
|
85
|
+
const destFile = path.join(targetDir, f);
|
|
86
|
+
if (fs.existsSync(srcFile) && (!fs.existsSync(destFile) || options.force)) {
|
|
63
87
|
copyRecursiveSync(srcFile, destFile);
|
|
64
|
-
|
|
88
|
+
copiedFiles.push(f);
|
|
89
|
+
if (!silent) console.log(` ✔ Disalin: ${f}`);
|
|
65
90
|
}
|
|
66
91
|
}
|
|
67
92
|
} else {
|
|
68
93
|
// Fallback manual jika source resources tidak ditemukan
|
|
69
|
-
const defaultMeta =
|
|
94
|
+
const defaultMeta = `---
|
|
95
|
+
title: "${options.title || 'Laporan Akademik & Proyek'}"
|
|
96
|
+
subtitle: "Laporan Penelitian / Tugas Akhir"
|
|
70
97
|
author:
|
|
71
|
-
- name: "Nama Penulis"
|
|
72
|
-
|
|
73
|
-
date: "2026"
|
|
74
|
-
institution: "Universitas / Institusi Akademik"
|
|
75
|
-
faculty: "Fakultas Teknik
|
|
76
|
-
department: "Program Studi Informatika"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
font_size: "12pt"
|
|
81
|
-
line_spacing: "0.75em"
|
|
82
|
-
first_line_indent: "1.25cm"
|
|
83
|
-
heading_chapter_prefix: "BAB "
|
|
84
|
-
heading_chapter_num_format: "roman"
|
|
98
|
+
- name: "${options.authorName || 'Nama Penulis'}"
|
|
99
|
+
nim: "${options.authorId || '1234567890'}"
|
|
100
|
+
date: "${options.year || '2026'}"
|
|
101
|
+
institution: "${options.institution || 'Universitas / Institusi Akademik'}"
|
|
102
|
+
faculty: "${options.faculty || 'Fakultas Teknik'}"
|
|
103
|
+
department: "${options.department || 'Program Studi Informatika'}"
|
|
104
|
+
year: "${options.year || '2026'}"
|
|
105
|
+
preset: "${options.preset || 'skripsi-4433'}"
|
|
106
|
+
...
|
|
85
107
|
`;
|
|
86
108
|
fs.writeFileSync(metaPath, defaultMeta, 'utf8');
|
|
87
|
-
|
|
109
|
+
copiedFiles.push('metadata.yml');
|
|
88
110
|
|
|
89
|
-
const coverPath = path.join(
|
|
111
|
+
const coverPath = path.join(targetDir, 'cover.md');
|
|
90
112
|
fs.writeFileSync(coverPath, '# Laporan Akademik\n\nDokumen ini disusun sebagai dokumentasi resmi laporan proyek/penelitian.\n', 'utf8');
|
|
91
|
-
|
|
113
|
+
copiedFiles.push('cover.md');
|
|
92
114
|
|
|
93
|
-
const chaptersDir = path.join(
|
|
115
|
+
const chaptersDir = path.join(targetDir, 'chapters');
|
|
94
116
|
if (!fs.existsSync(chaptersDir)) fs.mkdirSync(chaptersDir, { recursive: true });
|
|
95
117
|
const bab1 = `# Pendahuluan\n\n## Latar Belakang\nTuliskan latar belakang masalah di sini.\n\n## Rumusan Masalah\n1. Masalah pertama.\n2. Masalah kedua.\n\n## Tujuan Penelitian\nTujuan dari pelaksanaan proyek ini adalah menghasilkan analisis yang komprehensif.\n`;
|
|
96
118
|
fs.writeFileSync(path.join(chaptersDir, 'bab1-pendahuluan.md'), bab1, 'utf8');
|
|
97
|
-
|
|
119
|
+
copiedFiles.push('chapters/bab1-pendahuluan.md');
|
|
98
120
|
|
|
99
|
-
const refBibPath = path.join(
|
|
121
|
+
const refBibPath = path.join(targetDir, 'references.bib');
|
|
100
122
|
fs.writeFileSync(refBibPath, `@book{contoh2026,\n title = {Panduan Penulisan Karya Ilmiah},\n author = {Penulis, Contoh},\n year = {2026},\n publisher = {Penerbit Akademik}\n}\n`, 'utf8');
|
|
101
|
-
|
|
123
|
+
copiedFiles.push('references.bib');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Update metadata.yml if custom options were provided
|
|
127
|
+
if (fs.existsSync(metaPath) && (options.title || options.preset || options.authorName || options.institution)) {
|
|
128
|
+
try {
|
|
129
|
+
let metaContent = fs.readFileSync(metaPath, 'utf8');
|
|
130
|
+
if (options.title) {
|
|
131
|
+
metaContent = metaContent.replace(/^title:\s*".*?"/m, `title: "${options.title.replace(/"/g, '\\"')}"`);
|
|
132
|
+
}
|
|
133
|
+
if (options.preset) {
|
|
134
|
+
metaContent = metaContent.replace(/^preset:\s*".*?"/m, `preset: "${options.preset}"`);
|
|
135
|
+
}
|
|
136
|
+
if (options.institution) {
|
|
137
|
+
metaContent = metaContent.replace(/^institution:\s*".*?"/m, `institution: "${options.institution.replace(/"/g, '\\"')}"`);
|
|
138
|
+
}
|
|
139
|
+
if (options.faculty) {
|
|
140
|
+
metaContent = metaContent.replace(/^faculty:\s*".*?"/m, `faculty: "${options.faculty.replace(/"/g, '\\"')}"`);
|
|
141
|
+
}
|
|
142
|
+
if (options.department) {
|
|
143
|
+
metaContent = metaContent.replace(/^department:\s*".*?"/m, `department: "${options.department.replace(/"/g, '\\"')}"`);
|
|
144
|
+
}
|
|
145
|
+
if (options.year) {
|
|
146
|
+
metaContent = metaContent.replace(/^year:\s*".*?"/m, `year: "${options.year}"`);
|
|
147
|
+
}
|
|
148
|
+
if (options.authorName) {
|
|
149
|
+
metaContent = metaContent.replace(/name:\s*".*?"/, `name: "${options.authorName.replace(/"/g, '\\"')}"`);
|
|
150
|
+
if (options.authorId) {
|
|
151
|
+
metaContent = metaContent.replace(/nim:\s*".*?"/, `nim: "${options.authorId}"`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
fs.writeFileSync(metaPath, metaContent, 'utf8');
|
|
155
|
+
} catch (_) {}
|
|
102
156
|
}
|
|
103
157
|
|
|
104
158
|
// Ensure scripts are executable on Unix
|
|
105
159
|
try {
|
|
106
160
|
if (process.platform !== 'win32') {
|
|
107
|
-
const laporanBin = path.join(
|
|
108
|
-
const buildSh = path.join(
|
|
161
|
+
const laporanBin = path.join(targetDir, 'laporan');
|
|
162
|
+
const buildSh = path.join(targetDir, 'build.sh');
|
|
109
163
|
if (fs.existsSync(laporanBin)) fs.chmodSync(laporanBin, '755');
|
|
110
164
|
if (fs.existsSync(buildSh)) fs.chmodSync(buildSh, '755');
|
|
111
165
|
}
|
|
112
166
|
} catch (_) {}
|
|
113
167
|
|
|
114
|
-
|
|
115
|
-
|
|
168
|
+
return {
|
|
169
|
+
success: true,
|
|
170
|
+
targetDir,
|
|
171
|
+
copiedFiles,
|
|
172
|
+
message: 'Inisialisasi dokumen selesai dengan sukses.'
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function run(argv = []) {
|
|
177
|
+
const cwd = process.cwd();
|
|
178
|
+
console.log('\n==========================================================');
|
|
179
|
+
console.log(' LAPORAN GENERATOR - INISIALISASI PROYEK DOKUMEN ');
|
|
180
|
+
console.log('==========================================================\n');
|
|
181
|
+
|
|
182
|
+
const res = initProject({ targetDir: cwd });
|
|
183
|
+
if (res.success) {
|
|
184
|
+
console.log('\n✨ Inisialisasi dokumen selesai!');
|
|
185
|
+
console.log('Jalankan "./laporan build" atau "npx laporan-generator build" untuk mengompilasi dokumen ke PDF & Word!\n');
|
|
186
|
+
}
|
|
116
187
|
}
|
|
117
188
|
|
|
118
|
-
module.exports = { run };
|
|
189
|
+
module.exports = { run, initProject };
|