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/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.6.2 " -ForegroundColor Cyan
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" { Cmd-Bundle }
536
- "setup" { & powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "scripts\setup-deps.ps1") }
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
- node (Join-Path $PSScriptRoot "bin\laporan-generator.js") sync-hosts
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
- node (Join-Path $PSScriptRoot "bin\laporan-generator.js") sync-hosts
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 run(argv = []) {
22
- const cwd = process.cwd();
23
- console.log('\n==========================================================');
24
- console.log(' LAPORAN GENERATOR - INISIALISASI PROYEK DOKUMEN ');
25
- console.log('==========================================================\n');
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(cwd, 'metadata.yml');
28
- if (fs.existsSync(metaPath)) {
29
- console.log('[WARN] Dokumen laporan sudah ada di direktori ini (metadata.yml ditemukan).');
30
- return;
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
- // Cari direktori resources (bisa dari package root atau global skills)
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(cwd, f);
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
- console.log(` ✔ Disalin: ${f}`);
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 = `title: "Laporan Akademik & Proyek"
94
+ const defaultMeta = `---
95
+ title: "${options.title || 'Laporan Akademik & Proyek'}"
96
+ subtitle: "Laporan Penelitian / Tugas Akhir"
70
97
  author:
71
- - name: "Nama Penulis"
72
- id: "1234567890"
73
- date: "2026"
74
- institution: "Universitas / Institusi Akademik"
75
- faculty: "Fakultas Teknik & Ilmu Komputer"
76
- department: "Program Studi Informatika"
77
- location: "Indonesia"
78
- margin_preset: "skripsi-4433"
79
- font_family: "Times New Roman"
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
- console.log(' ✔ Dibuat: metadata.yml (Preset: skripsi-4433)');
109
+ copiedFiles.push('metadata.yml');
88
110
 
89
- const coverPath = path.join(cwd, 'cover.md');
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
- console.log(' ✔ Dibuat: cover.md');
113
+ copiedFiles.push('cover.md');
92
114
 
93
- const chaptersDir = path.join(cwd, 'chapters');
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
- console.log(' ✔ Dibuat: chapters/bab1-pendahuluan.md');
119
+ copiedFiles.push('chapters/bab1-pendahuluan.md');
98
120
 
99
- const refBibPath = path.join(cwd, 'references.bib');
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
- console.log(' ✔ Dibuat: references.bib');
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(cwd, 'laporan');
108
- const buildSh = path.join(cwd, 'build.sh');
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
- console.log('\n✨ Inisialisasi dokumen selesai!');
115
- console.log('Jalankan "./laporan build" atau "npx laporan-generator build" untuk mengompilasi dokumen ke PDF & Word!\n');
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 };