laporan-generator 2.6.2 → 2.8.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 +109 -109
- package/.agents/skills/laporan-generator/resources/cover.md +18 -14
- package/.agents/skills/laporan-generator/resources/docx.lua +65 -0
- 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/metadata.yml +13 -16
- 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 +160 -2
- package/.agents/skills/laporan-generator/resources/template.typ +3 -0
- 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 +185 -2
- package/README.md +49 -0
- package/bin/laporan-generator.js +15 -3
- package/bin/mcp-server.js +4 -0
- package/cover.md +18 -14
- package/docs/metadata-schema.md +2 -0
- package/docx.lua +65 -0
- package/laporan +37 -4
- package/laporan.ps1 +50 -11
- package/lib/init.js +139 -45
- package/lib/mcp.js +579 -0
- package/lib/sync-hosts.js +196 -68
- package/lib/uninstall.js +21 -0
- package/metadata.yml +13 -16
- 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 +147 -3
- package/template.typ +3 -0
package/lib/sync-hosts.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
|
-
const fs = require(
|
|
4
|
-
const path = require(
|
|
5
|
-
const os = require(
|
|
6
|
-
const { removeRecursiveSync, parseJsonc } = require(
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
const { removeRecursiveSync, parseJsonc } = require("./uninstall");
|
|
7
7
|
|
|
8
8
|
function copyRecursiveSync(src, dest) {
|
|
9
9
|
if (!fs.existsSync(src)) return;
|
|
@@ -20,28 +20,154 @@ function copyRecursiveSync(src, dest) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function syncInternalResources(packageRoot) {
|
|
24
|
+
const skillRoot = path.join(packageRoot, ".agents/skills/laporan-generator");
|
|
25
|
+
const resDir = path.join(skillRoot, "resources");
|
|
26
|
+
const skillScriptsDir = path.join(skillRoot, "scripts");
|
|
27
|
+
const resScriptsDir = path.join(resDir, "scripts");
|
|
28
|
+
const resPresetsDir = path.join(resDir, "presets");
|
|
29
|
+
|
|
30
|
+
fs.mkdirSync(resDir, { recursive: true });
|
|
31
|
+
fs.mkdirSync(skillScriptsDir, { recursive: true });
|
|
32
|
+
fs.mkdirSync(resScriptsDir, { recursive: true });
|
|
33
|
+
fs.mkdirSync(resPresetsDir, { recursive: true });
|
|
34
|
+
|
|
35
|
+
// 1. Sync scripts from root scripts/
|
|
36
|
+
const srcScripts = path.join(packageRoot, "scripts");
|
|
37
|
+
if (fs.existsSync(srcScripts)) {
|
|
38
|
+
for (const f of fs.readdirSync(srcScripts)) {
|
|
39
|
+
if (f.startsWith("__pycache__") || f.endsWith(".pyc")) continue;
|
|
40
|
+
const srcFile = path.join(srcScripts, f);
|
|
41
|
+
copyRecursiveSync(srcFile, path.join(skillScriptsDir, f));
|
|
42
|
+
copyRecursiveSync(srcFile, path.join(resScriptsDir, f));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 2. Sync presets from root presets/
|
|
47
|
+
const srcPresets = path.join(packageRoot, "presets");
|
|
48
|
+
if (fs.existsSync(srcPresets)) {
|
|
49
|
+
copyRecursiveSync(srcPresets, resPresetsDir);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 3. Sync template files
|
|
53
|
+
const templateFiles = [
|
|
54
|
+
"template.typ",
|
|
55
|
+
"reference.docx",
|
|
56
|
+
"docx.lua",
|
|
57
|
+
"references.bib",
|
|
58
|
+
"apa.csl",
|
|
59
|
+
"metadata.yml",
|
|
60
|
+
"cover.md",
|
|
61
|
+
"logo.jpg",
|
|
62
|
+
"Makefile",
|
|
63
|
+
"build.sh",
|
|
64
|
+
"build-docx.sh",
|
|
65
|
+
"laporan",
|
|
66
|
+
"laporan.ps1"
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
for (const tf of templateFiles) {
|
|
70
|
+
const src = path.join(packageRoot, tf);
|
|
71
|
+
if (fs.existsSync(src)) {
|
|
72
|
+
fs.copyFileSync(src, path.join(resDir, tf));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function registerMcpServers(packageRoot, home) {
|
|
78
|
+
const mcpBin = path.join(packageRoot, "bin/mcp-server.js");
|
|
79
|
+
|
|
80
|
+
// 1. Antigravity MCP (~/.gemini/config/mcp_config.json)
|
|
81
|
+
const agyMcpPath = path.join(home, ".gemini/config/mcp_config.json");
|
|
82
|
+
try {
|
|
83
|
+
let agyConfig = { mcpServers: {} };
|
|
84
|
+
if (fs.existsSync(agyMcpPath)) {
|
|
85
|
+
try {
|
|
86
|
+
agyConfig = JSON.parse(fs.readFileSync(agyMcpPath, "utf8"));
|
|
87
|
+
} catch (_) {
|
|
88
|
+
agyConfig = { mcpServers: {} };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!agyConfig.mcpServers) agyConfig.mcpServers = {};
|
|
92
|
+
agyConfig.mcpServers["laporan"] = {
|
|
93
|
+
command: "node",
|
|
94
|
+
args: [mcpBin],
|
|
95
|
+
disabled: false
|
|
96
|
+
};
|
|
97
|
+
fs.mkdirSync(path.dirname(agyMcpPath), { recursive: true });
|
|
98
|
+
fs.writeFileSync(agyMcpPath, JSON.stringify(agyConfig, null, 2), "utf8");
|
|
99
|
+
console.log(" ✔ [ANTIGRAVITY MCP] Registered server \"laporan\" -> ~/.gemini/config/mcp_config.json");
|
|
100
|
+
} catch (err) {
|
|
101
|
+
console.log(" ✖ [ANTIGRAVITY MCP SKIP] " + err.message);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 2. Claude Desktop & Claude Code (~/.config/Claude/claude_desktop_config.json, ~/.claude.json)
|
|
105
|
+
const claudePaths = [
|
|
106
|
+
path.join(home, ".config/Claude/claude_desktop_config.json"),
|
|
107
|
+
path.join(home, ".claude.json")
|
|
108
|
+
];
|
|
109
|
+
for (const cPath of claudePaths) {
|
|
110
|
+
try {
|
|
111
|
+
let conf = { mcpServers: {} };
|
|
112
|
+
if (fs.existsSync(cPath)) {
|
|
113
|
+
try {
|
|
114
|
+
conf = JSON.parse(fs.readFileSync(cPath, "utf8"));
|
|
115
|
+
} catch (_) {}
|
|
116
|
+
}
|
|
117
|
+
if (!conf.mcpServers) conf.mcpServers = {};
|
|
118
|
+
conf.mcpServers["laporan"] = {
|
|
119
|
+
command: "node",
|
|
120
|
+
args: [mcpBin]
|
|
121
|
+
};
|
|
122
|
+
fs.mkdirSync(path.dirname(cPath), { recursive: true });
|
|
123
|
+
fs.writeFileSync(cPath, JSON.stringify(conf, null, 2), "utf8");
|
|
124
|
+
console.log(" ✔ [CLAUDE MCP] Registered server \"laporan\" -> " + cPath);
|
|
125
|
+
} catch (_) {}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 3. OpenCode (~/.config/opencode/opencode.jsonc)
|
|
129
|
+
const opencodeConfigPath = path.join(home, ".config/opencode/opencode.jsonc");
|
|
130
|
+
if (fs.existsSync(opencodeConfigPath)) {
|
|
131
|
+
try {
|
|
132
|
+
const raw = fs.readFileSync(opencodeConfigPath, "utf8");
|
|
133
|
+
const opencodeConfig = parseJsonc(raw);
|
|
134
|
+
if (!opencodeConfig.mcpServers) opencodeConfig.mcpServers = {};
|
|
135
|
+
opencodeConfig.mcpServers["laporan"] = {
|
|
136
|
+
command: "node",
|
|
137
|
+
args: [mcpBin]
|
|
138
|
+
};
|
|
139
|
+
fs.writeFileSync(opencodeConfigPath, JSON.stringify(opencodeConfig, null, 2), "utf8");
|
|
140
|
+
console.log(" ✔ [OPENCODE MCP] Registered MCP server \"laporan\" -> ~/.config/opencode/opencode.jsonc");
|
|
141
|
+
} catch (_) {}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
23
145
|
function run(argv = []) {
|
|
24
146
|
const home = os.homedir();
|
|
25
|
-
const packageRoot = path.join(__dirname,
|
|
26
|
-
const skillGeneratorSource = path.join(packageRoot,
|
|
27
|
-
const workflowSource = path.join(packageRoot,
|
|
147
|
+
const packageRoot = path.join(__dirname, "..");
|
|
148
|
+
const skillGeneratorSource = path.join(packageRoot, ".agents/skills/laporan-generator");
|
|
149
|
+
const workflowSource = path.join(packageRoot, ".agents/workflows/laporan-generator.md");
|
|
28
150
|
|
|
29
|
-
console.log(
|
|
30
|
-
console.log(
|
|
31
|
-
console.log(
|
|
151
|
+
console.log("\n==========================================================");
|
|
152
|
+
console.log(" LAPORAN GENERATOR - AI AGENT SKILLS & MCP SYNC ");
|
|
153
|
+
console.log("==========================================================\n");
|
|
32
154
|
|
|
33
|
-
//
|
|
155
|
+
// Step 0: Sinkronisasi berkas internal sebelum distribusi
|
|
156
|
+
syncInternalResources(packageRoot);
|
|
157
|
+
console.log(" ✔ [INTERNAL SYNC] Root scripts & templates synced to .agents/skills/laporan-generator");
|
|
158
|
+
|
|
159
|
+
// Bersihkan alias lama/duplikat laporan agar tidak dobel dengan laporan-generator
|
|
34
160
|
const legacyDuplicates = [
|
|
35
|
-
path.join(home,
|
|
36
|
-
path.join(home,
|
|
37
|
-
path.join(home,
|
|
38
|
-
path.join(home,
|
|
39
|
-
path.join(home,
|
|
40
|
-
path.join(process.cwd(),
|
|
41
|
-
path.join(home,
|
|
42
|
-
path.join(home,
|
|
43
|
-
path.join(process.cwd(),
|
|
44
|
-
path.join(home,
|
|
161
|
+
path.join(home, ".agents/skills/laporan"),
|
|
162
|
+
path.join(home, ".gemini/config/skills/laporan"),
|
|
163
|
+
path.join(home, ".claude/skills/laporan"),
|
|
164
|
+
path.join(home, ".grok/skills/laporan"),
|
|
165
|
+
path.join(home, ".config/opencode/skills/laporan"),
|
|
166
|
+
path.join(process.cwd(), ".agents/skills/laporan"),
|
|
167
|
+
path.join(home, ".kiro/agents/laporan.json"),
|
|
168
|
+
path.join(home, ".agents/workflows/laporan.md"),
|
|
169
|
+
path.join(process.cwd(), ".agents/workflows/laporan.md"),
|
|
170
|
+
path.join(home, ".claude/commands/laporan.md")
|
|
45
171
|
];
|
|
46
172
|
for (const legacy of legacyDuplicates) {
|
|
47
173
|
if (fs.existsSync(legacy)) {
|
|
@@ -51,28 +177,28 @@ function run(argv = []) {
|
|
|
51
177
|
|
|
52
178
|
const targets = [
|
|
53
179
|
{
|
|
54
|
-
name:
|
|
55
|
-
skillsDir: path.join(home,
|
|
180
|
+
name: "Global Agents (~/.agents/skills/)",
|
|
181
|
+
skillsDir: path.join(home, ".agents/skills")
|
|
56
182
|
},
|
|
57
183
|
{
|
|
58
|
-
name:
|
|
59
|
-
skillsDir: path.join(home,
|
|
184
|
+
name: "Antigravity CLI (~/.gemini/config/skills/)",
|
|
185
|
+
skillsDir: path.join(home, ".gemini/config/skills")
|
|
60
186
|
},
|
|
61
187
|
{
|
|
62
|
-
name:
|
|
63
|
-
skillsDir: path.join(home,
|
|
188
|
+
name: "Claude Code (~/.claude/skills/)",
|
|
189
|
+
skillsDir: path.join(home, ".claude/skills")
|
|
64
190
|
},
|
|
65
191
|
{
|
|
66
|
-
name:
|
|
67
|
-
skillsDir: path.join(home,
|
|
192
|
+
name: "Grok CLI (~/.grok/skills/)",
|
|
193
|
+
skillsDir: path.join(home, ".grok/skills")
|
|
68
194
|
},
|
|
69
195
|
{
|
|
70
|
-
name:
|
|
71
|
-
skillsDir: path.join(home,
|
|
196
|
+
name: "OpenCode (~/.config/opencode/skills/)",
|
|
197
|
+
skillsDir: path.join(home, ".config/opencode/skills")
|
|
72
198
|
},
|
|
73
199
|
{
|
|
74
|
-
name:
|
|
75
|
-
skillsDir: path.join(process.cwd(),
|
|
200
|
+
name: "Local Project Workspace (.agents/skills/)",
|
|
201
|
+
skillsDir: path.join(process.cwd(), ".agents/skills")
|
|
76
202
|
}
|
|
77
203
|
];
|
|
78
204
|
|
|
@@ -80,7 +206,7 @@ function run(argv = []) {
|
|
|
80
206
|
|
|
81
207
|
for (const target of targets) {
|
|
82
208
|
try {
|
|
83
|
-
const destGen = path.join(target.skillsDir,
|
|
209
|
+
const destGen = path.join(target.skillsDir, "laporan-generator");
|
|
84
210
|
copyRecursiveSync(skillGeneratorSource, destGen);
|
|
85
211
|
console.log(` ✔ [INSTALLED] ${target.name} (with full templates & resources)`);
|
|
86
212
|
installedCount++;
|
|
@@ -90,25 +216,24 @@ function run(argv = []) {
|
|
|
90
216
|
}
|
|
91
217
|
|
|
92
218
|
// Daftarkan Slash Command ke OpenCode (~/.config/opencode/opencode.jsonc)
|
|
93
|
-
const opencodeConfigPath = path.join(home,
|
|
219
|
+
const opencodeConfigPath = path.join(home, ".config/opencode/opencode.jsonc");
|
|
94
220
|
try {
|
|
95
221
|
let opencodeConfig = {};
|
|
96
222
|
if (fs.existsSync(opencodeConfigPath)) {
|
|
97
|
-
const raw = fs.readFileSync(opencodeConfigPath,
|
|
223
|
+
const raw = fs.readFileSync(opencodeConfigPath, "utf8");
|
|
98
224
|
try {
|
|
99
225
|
opencodeConfig = parseJsonc(raw);
|
|
100
226
|
} catch (_) {
|
|
101
227
|
opencodeConfig = {};
|
|
102
228
|
}
|
|
103
229
|
}
|
|
104
|
-
opencodeConfig[
|
|
230
|
+
opencodeConfig["$schema"] = "https://opencode.ai/config.json";
|
|
105
231
|
if (!opencodeConfig.command) {
|
|
106
232
|
opencodeConfig.command = {};
|
|
107
233
|
}
|
|
108
|
-
|
|
109
|
-
delete opencodeConfig.command['laporan'];
|
|
234
|
+
delete opencodeConfig.command["laporan"];
|
|
110
235
|
|
|
111
|
-
opencodeConfig.command[
|
|
236
|
+
opencodeConfig.command["laporan-generator"] = {
|
|
112
237
|
description: "Universal Academic Report & Thesis Generator",
|
|
113
238
|
template: "Gunakan skill laporan-generator untuk membuat atau mengompilasi dokumen laporan/skripsi akademik: $ARGUMENTS"
|
|
114
239
|
};
|
|
@@ -117,62 +242,62 @@ function run(argv = []) {
|
|
|
117
242
|
opencodeConfig.skills = {};
|
|
118
243
|
}
|
|
119
244
|
const skillPaths = new Set(opencodeConfig.skills.paths || []);
|
|
120
|
-
skillPaths.add(path.join(home,
|
|
121
|
-
skillPaths.add(path.join(home,
|
|
245
|
+
skillPaths.add(path.join(home, ".agents/skills"));
|
|
246
|
+
skillPaths.add(path.join(home, ".config/opencode/skills"));
|
|
122
247
|
opencodeConfig.skills.paths = Array.from(skillPaths);
|
|
123
248
|
|
|
124
249
|
const opencodeDir = path.dirname(opencodeConfigPath);
|
|
125
250
|
if (!fs.existsSync(opencodeDir)) {
|
|
126
251
|
fs.mkdirSync(opencodeDir, { recursive: true });
|
|
127
252
|
}
|
|
128
|
-
fs.writeFileSync(opencodeConfigPath, JSON.stringify(opencodeConfig, null, 2),
|
|
129
|
-
console.log(
|
|
253
|
+
fs.writeFileSync(opencodeConfigPath, JSON.stringify(opencodeConfig, null, 2), "utf8");
|
|
254
|
+
console.log(" ✔ [OPENCODE] Registered /laporan-generator command -> ~/.config/opencode/opencode.jsonc");
|
|
130
255
|
} catch (err) {
|
|
131
|
-
console.log(
|
|
256
|
+
console.log(" ✖ [OPENCODE SKIP] " + err.message);
|
|
132
257
|
}
|
|
133
258
|
|
|
134
259
|
// Daftarkan Custom Agent ke Kiro CLI (~/.kiro/agents/laporan-generator.json)
|
|
135
|
-
const kiroAgentsDir = path.join(home,
|
|
260
|
+
const kiroAgentsDir = path.join(home, ".kiro/agents");
|
|
136
261
|
try {
|
|
137
262
|
if (!fs.existsSync(kiroAgentsDir)) {
|
|
138
263
|
fs.mkdirSync(kiroAgentsDir, { recursive: true });
|
|
139
264
|
}
|
|
140
265
|
const kiroPrompt = [
|
|
141
|
-
"You are an expert academic writing assistant powered by Laporan Generator.",
|
|
266
|
+
"You are an expert academic writing assistant powered by Laporan Generator and Native MCP Tools.",
|
|
142
267
|
"",
|
|
143
268
|
"CRITICAL INSTRUCTION: DO NOT write raw Typst code from scratch or create standalone .typ files (such as laporan.typ). DO NOT import @preview/ieee or external packages.",
|
|
144
269
|
"",
|
|
145
270
|
"Always follow the Modular Markdown Architecture:",
|
|
146
|
-
"1. Scaffolding:
|
|
147
|
-
"2. Metadata: Edit metadata.yml to set title, author, institution, faculty, department, year, and campus preset (standard, skripsi-4433, itb-ta, ui-skripsi, ugm-skripsi, its-skripsi).",
|
|
271
|
+
"1. Scaffolding: Use MCP tool laporan_init or run npx laporan-generator init.",
|
|
272
|
+
"2. Metadata: Edit metadata.yml to set title, author, institution, faculty, department, year, and campus preset (standard, skripsi-4433, itb-ta, ui-skripsi, ugm-skripsi, its-skripsi, unpad-skripsi).",
|
|
148
273
|
"3. Content: Write chapters in pure standard Markdown inside chapters/ (bab1-pendahuluan.md, bab2-tinjauan-pustaka.md, bab3-metodologi.md, bab4-hasil-dan-pembahasan.md, bab5-penutup.md).",
|
|
149
|
-
"4. Citations:
|
|
150
|
-
"5.
|
|
151
|
-
"6.
|
|
274
|
+
"4. Citations: Use MCP tool laporan_citations to search DOIs on Crossref, add BibTeX entries to references.bib, and cite in Markdown using [@citekey].",
|
|
275
|
+
"5. Audit: Use MCP tool laporan_doctor to audit document health.",
|
|
276
|
+
"6. Compilation: Use MCP tool laporan_build or run ./laporan build to compile both PDF and Word DOCX simultaneously."
|
|
152
277
|
].join("\n");
|
|
153
278
|
|
|
154
279
|
const kiroConfig = {
|
|
155
280
|
name: "laporan-generator",
|
|
156
|
-
description: "Universal Academic Report & Thesis Generator
|
|
281
|
+
description: "Universal Academic Report & Thesis Generator with Native MCP Tools",
|
|
157
282
|
prompt: kiroPrompt,
|
|
158
283
|
tools: ["read", "write", "shell", "grep", "glob"],
|
|
159
284
|
allowedTools: ["read", "write", "shell", "grep", "glob"]
|
|
160
285
|
};
|
|
161
286
|
fs.writeFileSync(
|
|
162
|
-
path.join(kiroAgentsDir,
|
|
287
|
+
path.join(kiroAgentsDir, "laporan-generator.json"),
|
|
163
288
|
JSON.stringify(kiroConfig, null, 2),
|
|
164
|
-
|
|
289
|
+
"utf8"
|
|
165
290
|
);
|
|
166
|
-
console.log(
|
|
291
|
+
console.log(" ✔ [KIRO AGENT] Registered agent: \"laporan-generator\" -> ~/.kiro/agents/");
|
|
167
292
|
} catch (err) {
|
|
168
|
-
console.log(
|
|
293
|
+
console.log(" ✖ [KIRO SKIP] " + err.message);
|
|
169
294
|
}
|
|
170
295
|
|
|
171
|
-
// Daftarkan Slash Commands / Workflows
|
|
296
|
+
// Daftarkan Slash Commands / Workflows
|
|
172
297
|
const workflowDestinations = [
|
|
173
|
-
path.join(home,
|
|
174
|
-
path.join(process.cwd(),
|
|
175
|
-
path.join(home,
|
|
298
|
+
path.join(home, ".agents/workflows"),
|
|
299
|
+
path.join(process.cwd(), ".agents/workflows"),
|
|
300
|
+
path.join(home, ".claude/commands")
|
|
176
301
|
];
|
|
177
302
|
|
|
178
303
|
for (const wDir of workflowDestinations) {
|
|
@@ -180,15 +305,18 @@ function run(argv = []) {
|
|
|
180
305
|
if (!fs.existsSync(wDir)) {
|
|
181
306
|
fs.mkdirSync(wDir, { recursive: true });
|
|
182
307
|
}
|
|
183
|
-
fs.copyFileSync(workflowSource, path.join(wDir,
|
|
308
|
+
fs.copyFileSync(workflowSource, path.join(wDir, "laporan-generator.md"));
|
|
184
309
|
console.log(` ✔ [SLASH COMMAND] /laporan-generator -> ${wDir}`);
|
|
185
310
|
} catch (_) {}
|
|
186
311
|
}
|
|
187
312
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
console.log(
|
|
313
|
+
// Registrasi Native MCP Server
|
|
314
|
+
registerMcpServers(packageRoot, home);
|
|
315
|
+
|
|
316
|
+
console.log("\n==========================================================");
|
|
317
|
+
console.log("✨ Sukses! Skill dan MCP server /laporan-generator berhasil disinkronkan.");
|
|
318
|
+
console.log("Seluruh AI Agent (Antigravity, Claude, OpenCode, Kiro) kini siap digunakan!");
|
|
319
|
+
console.log("==========================================================\n");
|
|
192
320
|
}
|
|
193
321
|
|
|
194
|
-
module.exports = { run };
|
|
322
|
+
module.exports = { run, syncInternalResources };
|
package/lib/uninstall.js
CHANGED
|
@@ -115,6 +115,27 @@ function run(argv = []) {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
// 5. Bersihkan registrasi MCP Server
|
|
119
|
+
const mcpConfigs = [
|
|
120
|
+
path.join(home, '.gemini/config/mcp_config.json'),
|
|
121
|
+
path.join(home, '.claude.json'),
|
|
122
|
+
path.join(home, '.config/Claude/claude_desktop_config.json')
|
|
123
|
+
];
|
|
124
|
+
for (const mcpFile of mcpConfigs) {
|
|
125
|
+
if (fs.existsSync(mcpFile)) {
|
|
126
|
+
try {
|
|
127
|
+
const raw = fs.readFileSync(mcpFile, 'utf8');
|
|
128
|
+
const data = JSON.parse(raw);
|
|
129
|
+
if (data.mcpServers && (data.mcpServers['laporan'] || data.mcpServers['laporan-generator'])) {
|
|
130
|
+
delete data.mcpServers['laporan'];
|
|
131
|
+
delete data.mcpServers['laporan-generator'];
|
|
132
|
+
fs.writeFileSync(mcpFile, JSON.stringify(data, null, 2), 'utf8');
|
|
133
|
+
console.log(` ✔ [CLEANED MCP] Removed laporan server from: ${mcpFile}`);
|
|
134
|
+
}
|
|
135
|
+
} catch (_) {}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
118
139
|
console.log('\n==========================================================');
|
|
119
140
|
console.log('✨ Uninstall selesai! Seluruh integrasi AI agent telah dibersihkan.');
|
|
120
141
|
console.log('==========================================================\n');
|
package/metadata.yml
CHANGED
|
@@ -15,21 +15,18 @@ institution: "Universitas Example"
|
|
|
15
15
|
faculty: "Program Studi Informatika"
|
|
16
16
|
year: "2026/2027"
|
|
17
17
|
date: "Agustus 2026"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
abstract_en: |
|
|
27
|
-
|
|
28
|
-
keywords_en:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- Typst
|
|
32
|
-
- OpenXML
|
|
33
|
-
- Software Engineering
|
|
18
|
+
daftar_gambar: false
|
|
19
|
+
daftar_tabel: false
|
|
20
|
+
# Komponen halaman depan opsional (aktifkan jika diperlukan):
|
|
21
|
+
# abstract_id: |
|
|
22
|
+
# Ringkasan atau abstrak laporan dalam Bahasa Indonesia...
|
|
23
|
+
# keywords_id:
|
|
24
|
+
# - Kata Kunci 1
|
|
25
|
+
# - Kata Kunci 2
|
|
26
|
+
# abstract_en: |
|
|
27
|
+
# Summary or abstract of the report in English...
|
|
28
|
+
# keywords_en:
|
|
29
|
+
# - Keyword 1
|
|
30
|
+
# - Keyword 2
|
|
34
31
|
preset: "standard"
|
|
35
32
|
...
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "laporan-generator",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Universal Academic Report & Thesis Generator with Multi-Host AI Agent Skills
|
|
3
|
+
"version": "2.8.0",
|
|
4
|
+
"description": "Universal Academic Report & Thesis Generator with Multi-Host AI Agent Skills & Native MCP Server",
|
|
5
5
|
"main": "bin/laporan-generator.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"laporan-generator": "bin/laporan-generator.js",
|
|
8
|
-
"laporan": "bin/laporan-generator.js"
|
|
8
|
+
"laporan": "bin/laporan-generator.js",
|
|
9
|
+
"laporan-mcp": "bin/mcp-server.js"
|
|
9
10
|
},
|
|
10
11
|
"files": [
|
|
11
12
|
"bin/",
|
|
@@ -35,7 +36,10 @@
|
|
|
35
36
|
],
|
|
36
37
|
"scripts": {
|
|
37
38
|
"test": "./test.sh",
|
|
39
|
+
"test:mcp": "node tests/test_mcp.js",
|
|
40
|
+
"mcp": "node bin/mcp-server.js",
|
|
38
41
|
"doctor": "node bin/laporan-generator.js doctor",
|
|
42
|
+
"stats": "node bin/laporan-generator.js stats",
|
|
39
43
|
"sync-hosts": "node bin/laporan-generator.js sync-hosts",
|
|
40
44
|
"setup": "node bin/laporan-generator.js setup"
|
|
41
45
|
},
|
|
@@ -46,6 +50,8 @@
|
|
|
46
50
|
"thesis-generator",
|
|
47
51
|
"skripsi",
|
|
48
52
|
"ai-agents",
|
|
53
|
+
"mcp",
|
|
54
|
+
"model-context-protocol",
|
|
49
55
|
"antigravity",
|
|
50
56
|
"antigravity-cli",
|
|
51
57
|
"claude-code",
|
package/scripts/bundle.py
CHANGED
|
@@ -15,8 +15,17 @@ NC = "\033[0m"
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def main():
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
pos_args = [a for a in sys.argv[1:] if not a.startswith("-")]
|
|
19
|
+
|
|
20
|
+
target_dir = os.getcwd()
|
|
21
|
+
if pos_args:
|
|
22
|
+
target_dir = os.path.abspath(pos_args[0])
|
|
23
|
+
elif not (os.path.isfile(os.path.join(target_dir, "metadata.yml")) or os.path.isdir(os.path.join(target_dir, "chapters"))):
|
|
24
|
+
pkg_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
25
|
+
if os.path.isfile(os.path.join(pkg_root, "metadata.yml")):
|
|
26
|
+
target_dir = pkg_root
|
|
27
|
+
|
|
28
|
+
os.chdir(target_dir)
|
|
20
29
|
|
|
21
30
|
print(f"{CYAN}{BOLD}")
|
|
22
31
|
print(" ========================================================")
|