@plumpslabs/kuma 2.1.8 → 2.2.1

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.
@@ -0,0 +1,161 @@
1
+ // src/utils/pathValidator.ts
2
+ import path from "path";
3
+ import fs from "fs";
4
+ function validateFilePath(filePath, projectRoot) {
5
+ try {
6
+ const root = projectRoot ?? getProjectRoot();
7
+ const resolvedRoot = path.resolve(root);
8
+ const normalizedRoot = path.normalize(resolvedRoot).toLowerCase();
9
+ let resolvedPath;
10
+ if (!path.isAbsolute(filePath)) {
11
+ const cwdPath = path.resolve(process.cwd(), filePath);
12
+ const normalizedCwdPath = path.normalize(cwdPath).toLowerCase();
13
+ if (fs.existsSync(cwdPath) && normalizedCwdPath.startsWith(normalizedRoot)) {
14
+ resolvedPath = cwdPath;
15
+ } else {
16
+ resolvedPath = path.resolve(resolvedRoot, filePath);
17
+ }
18
+ } else {
19
+ resolvedPath = path.resolve(resolvedRoot, filePath);
20
+ }
21
+ const normalizedPath = path.normalize(resolvedPath).toLowerCase();
22
+ if (!normalizedPath.startsWith(normalizedRoot)) {
23
+ return {
24
+ valid: false,
25
+ error: new Error(
26
+ `PATH_TRAVERSAL: Access denied. Path "${filePath}" resolves outside project root "${resolvedRoot}".`
27
+ )
28
+ };
29
+ }
30
+ if (normalizedPath.includes("..")) {
31
+ return {
32
+ valid: false,
33
+ error: new Error(
34
+ `PATH_TRAVERSAL: Path traversal detected in "${filePath}". Relative paths with ".." are not allowed.`
35
+ )
36
+ };
37
+ }
38
+ const blockedPatterns = [
39
+ "/etc/",
40
+ "/sys/",
41
+ "/proc/",
42
+ "/dev/",
43
+ "/boot/",
44
+ "/usr/",
45
+ "C:\\Windows",
46
+ "C:\\Program Files",
47
+ "C:\\Program Files (x86)",
48
+ "C:\\System32",
49
+ "~/.ssh",
50
+ "/root/"
51
+ ];
52
+ for (const pattern of blockedPatterns) {
53
+ if (normalizedPath.includes(pattern.toLowerCase())) {
54
+ return {
55
+ valid: false,
56
+ error: new Error(
57
+ `PATH_TRAVERSAL: Access to system directory "${pattern}" is blocked.`
58
+ )
59
+ };
60
+ }
61
+ }
62
+ try {
63
+ if (fs.existsSync(resolvedPath)) {
64
+ const realPath = fs.realpathSync(resolvedPath);
65
+ const normalizedRealPath = path.normalize(realPath).toLowerCase();
66
+ if (!normalizedRealPath.startsWith(normalizedRoot)) {
67
+ return {
68
+ valid: false,
69
+ error: new Error(
70
+ `PATH_TRAVERSAL: Symlink escape detected. Path "${filePath}" resolves to "${realPath}" which is outside project root "${resolvedRoot}".`
71
+ )
72
+ };
73
+ }
74
+ }
75
+ } catch {
76
+ }
77
+ if (normalizedPath.includes("node_modules")) {
78
+ return {
79
+ valid: true,
80
+ resolvedPath
81
+ // Warning will be handled by caller
82
+ };
83
+ }
84
+ return { valid: true, resolvedPath };
85
+ } catch (err) {
86
+ return {
87
+ valid: false,
88
+ error: err instanceof Error ? err : new Error(`Path validation error: ${String(err)}`)
89
+ };
90
+ }
91
+ }
92
+ function validateFileExtension(filePath) {
93
+ const allowedExtensions = [
94
+ ".ts",
95
+ ".tsx",
96
+ ".js",
97
+ ".jsx",
98
+ ".mjs",
99
+ ".cjs",
100
+ ".json",
101
+ ".md",
102
+ ".css",
103
+ ".html",
104
+ ".htm",
105
+ ".env",
106
+ ".env.example",
107
+ ".env.local",
108
+ ".yml",
109
+ ".yaml",
110
+ ".toml",
111
+ ".svg",
112
+ ".png",
113
+ ".jpg",
114
+ ".gif",
115
+ ".sh",
116
+ ".bat",
117
+ ".ps1",
118
+ ".sql",
119
+ ".graphql",
120
+ ".gql",
121
+ ".vue",
122
+ ".svelte",
123
+ ".astro",
124
+ ".prisma",
125
+ ".proto",
126
+ ".txt",
127
+ ".log"
128
+ ];
129
+ const ext = path.extname(filePath).toLowerCase();
130
+ return allowedExtensions.includes(ext);
131
+ }
132
+ function getProjectRoot() {
133
+ return process.env.AGENT_PROJECT_ROOT ?? process.cwd();
134
+ }
135
+ function getBackupPath(filePath, timestamp) {
136
+ const ts = timestamp ?? Date.now();
137
+ const root = getProjectRoot();
138
+ const relativePath = path.relative(root, filePath);
139
+ return path.join(root, ".kuma", "backups", String(ts), relativePath);
140
+ }
141
+ function ensureBackupDir() {
142
+ const root = getProjectRoot();
143
+ const backupDir = path.join(root, ".kuma", "backups");
144
+ if (!fs.existsSync(backupDir)) {
145
+ fs.mkdirSync(backupDir, { recursive: true });
146
+ }
147
+ return backupDir;
148
+ }
149
+ function getKumaDir() {
150
+ const root = getProjectRoot();
151
+ return path.join(root, ".kuma");
152
+ }
153
+
154
+ export {
155
+ validateFilePath,
156
+ validateFileExtension,
157
+ getProjectRoot,
158
+ getBackupPath,
159
+ ensureBackupDir,
160
+ getKumaDir
161
+ };
@@ -1,157 +1,10 @@
1
- // src/cli/init.ts
2
- import fs2 from "fs";
3
- import path2 from "path";
4
-
5
- // src/utils/pathValidator.ts
6
- import path from "path";
7
- import fs from "fs";
8
- function validateFilePath(filePath, projectRoot) {
9
- try {
10
- const root = projectRoot ?? getProjectRoot();
11
- const resolvedRoot = path.resolve(root);
12
- const normalizedRoot = path.normalize(resolvedRoot).toLowerCase();
13
- let resolvedPath;
14
- if (!path.isAbsolute(filePath)) {
15
- const cwdPath = path.resolve(process.cwd(), filePath);
16
- const normalizedCwdPath = path.normalize(cwdPath).toLowerCase();
17
- if (fs.existsSync(cwdPath) && normalizedCwdPath.startsWith(normalizedRoot)) {
18
- resolvedPath = cwdPath;
19
- } else {
20
- resolvedPath = path.resolve(resolvedRoot, filePath);
21
- }
22
- } else {
23
- resolvedPath = path.resolve(resolvedRoot, filePath);
24
- }
25
- const normalizedPath = path.normalize(resolvedPath).toLowerCase();
26
- if (!normalizedPath.startsWith(normalizedRoot)) {
27
- return {
28
- valid: false,
29
- error: new Error(
30
- `PATH_TRAVERSAL: Access denied. Path "${filePath}" resolves outside project root "${resolvedRoot}".`
31
- )
32
- };
33
- }
34
- if (normalizedPath.includes("..")) {
35
- return {
36
- valid: false,
37
- error: new Error(
38
- `PATH_TRAVERSAL: Path traversal detected in "${filePath}". Relative paths with ".." are not allowed.`
39
- )
40
- };
41
- }
42
- const blockedPatterns = [
43
- "/etc/",
44
- "/sys/",
45
- "/proc/",
46
- "/dev/",
47
- "/boot/",
48
- "/usr/",
49
- "C:\\Windows",
50
- "C:\\Program Files",
51
- "C:\\Program Files (x86)",
52
- "C:\\System32",
53
- "~/.ssh",
54
- "/root/"
55
- ];
56
- for (const pattern of blockedPatterns) {
57
- if (normalizedPath.includes(pattern.toLowerCase())) {
58
- return {
59
- valid: false,
60
- error: new Error(
61
- `PATH_TRAVERSAL: Access to system directory "${pattern}" is blocked.`
62
- )
63
- };
64
- }
65
- }
66
- try {
67
- if (fs.existsSync(resolvedPath)) {
68
- const realPath = fs.realpathSync(resolvedPath);
69
- const normalizedRealPath = path.normalize(realPath).toLowerCase();
70
- if (!normalizedRealPath.startsWith(normalizedRoot)) {
71
- return {
72
- valid: false,
73
- error: new Error(
74
- `PATH_TRAVERSAL: Symlink escape detected. Path "${filePath}" resolves to "${realPath}" which is outside project root "${resolvedRoot}".`
75
- )
76
- };
77
- }
78
- }
79
- } catch {
80
- }
81
- if (normalizedPath.includes("node_modules")) {
82
- return {
83
- valid: true,
84
- resolvedPath
85
- // Warning will be handled by caller
86
- };
87
- }
88
- return { valid: true, resolvedPath };
89
- } catch (err) {
90
- return {
91
- valid: false,
92
- error: err instanceof Error ? err : new Error(`Path validation error: ${String(err)}`)
93
- };
94
- }
95
- }
96
- function validateFileExtension(filePath) {
97
- const allowedExtensions = [
98
- ".ts",
99
- ".tsx",
100
- ".js",
101
- ".jsx",
102
- ".mjs",
103
- ".cjs",
104
- ".json",
105
- ".md",
106
- ".css",
107
- ".html",
108
- ".htm",
109
- ".env",
110
- ".env.example",
111
- ".env.local",
112
- ".yml",
113
- ".yaml",
114
- ".toml",
115
- ".svg",
116
- ".png",
117
- ".jpg",
118
- ".gif",
119
- ".sh",
120
- ".bat",
121
- ".ps1",
122
- ".sql",
123
- ".graphql",
124
- ".gql",
125
- ".vue",
126
- ".svelte",
127
- ".astro",
128
- ".prisma",
129
- ".proto",
130
- ".txt",
131
- ".log"
132
- ];
133
- const ext = path.extname(filePath).toLowerCase();
134
- return allowedExtensions.includes(ext);
135
- }
136
- function getProjectRoot() {
137
- return process.env.AGENT_PROJECT_ROOT ?? process.cwd();
138
- }
139
- function getBackupPath(filePath, timestamp) {
140
- const ts = timestamp ?? Date.now();
141
- const root = getProjectRoot();
142
- const relativePath = path.relative(root, filePath);
143
- return path.join(root, ".agent-backups", String(ts), relativePath);
144
- }
145
- function ensureBackupDir() {
146
- const root = getProjectRoot();
147
- const backupDir = path.join(root, ".agent-backups");
148
- if (!fs.existsSync(backupDir)) {
149
- fs.mkdirSync(backupDir, { recursive: true });
150
- }
151
- return backupDir;
152
- }
1
+ import {
2
+ getProjectRoot
3
+ } from "./chunk-7Q3YUJSM.js";
153
4
 
154
5
  // src/cli/init.ts
6
+ import fs from "fs";
7
+ import path from "path";
155
8
  var ALL_CONFIG_TYPES = [
156
9
  "claude",
157
10
  "cursor",
@@ -467,21 +320,21 @@ var APPEND_SEPARATOR = "\n\n---\n_Generated by Kuma MCP - https://github.com/plu
467
320
  function handleOpencodeSecondary(_root, _results) {
468
321
  }
469
322
  function handleCodexSecondary(root, results) {
470
- const tomlPath = path2.resolve(root, ".codex/config.toml");
323
+ const tomlPath = path.resolve(root, ".codex/config.toml");
471
324
  if (results.some((r) => r.filePath === ".codex/config.toml")) return;
472
325
  try {
473
- const dir = path2.dirname(tomlPath);
474
- if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
475
- if (fs2.existsSync(tomlPath)) {
476
- const existingContent = fs2.readFileSync(tomlPath, "utf-8");
326
+ const dir = path.dirname(tomlPath);
327
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
328
+ if (fs.existsSync(tomlPath)) {
329
+ const existingContent = fs.readFileSync(tomlPath, "utf-8");
477
330
  if (existingContent.includes("kuma")) {
478
331
  results.push({ type: "codex", filePath: ".codex/config.toml", action: "skipped" });
479
332
  return;
480
333
  }
481
- fs2.writeFileSync(tomlPath, existingContent.trimEnd() + "\n\n" + codexConfigTomlTemplate(), "utf-8");
334
+ fs.writeFileSync(tomlPath, existingContent.trimEnd() + "\n\n" + codexConfigTomlTemplate(), "utf-8");
482
335
  results.push({ type: "codex", filePath: ".codex/config.toml", action: "appended" });
483
336
  } else {
484
- fs2.writeFileSync(tomlPath, codexConfigTomlTemplate(), "utf-8");
337
+ fs.writeFileSync(tomlPath, codexConfigTomlTemplate(), "utf-8");
485
338
  results.push({ type: "codex", filePath: ".codex/config.toml", action: "created" });
486
339
  }
487
340
  } catch (err) {
@@ -494,11 +347,11 @@ function handleCodexSecondary(root, results) {
494
347
  }
495
348
  }
496
349
  function handleQwenSecondary(root, results) {
497
- const settingsPath = path2.resolve(root, "settings.json");
350
+ const settingsPath = path.resolve(root, "settings.json");
498
351
  if (results.some((r) => r.filePath === "settings.json")) return;
499
352
  try {
500
- if (fs2.existsSync(settingsPath)) {
501
- const existingContent = fs2.readFileSync(settingsPath, "utf-8");
353
+ if (fs.existsSync(settingsPath)) {
354
+ const existingContent = fs.readFileSync(settingsPath, "utf-8");
502
355
  if (existingContent.includes("kuma")) {
503
356
  if (!existingContent.includes("_Generated by Kuma MCP_")) {
504
357
  try {
@@ -506,7 +359,7 @@ function handleQwenSecondary(root, results) {
506
359
  parsed.mcpServers = parsed.mcpServers || {};
507
360
  if (!parsed.mcpServers.kuma) {
508
361
  parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
509
- fs2.writeFileSync(settingsPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
362
+ fs.writeFileSync(settingsPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
510
363
  results.push({ type: "qwen", filePath: "settings.json", action: "appended" });
511
364
  return;
512
365
  }
@@ -521,13 +374,13 @@ function handleQwenSecondary(root, results) {
521
374
  parsed.mcpServers = parsed.mcpServers || {};
522
375
  if (!parsed.mcpServers.kuma) {
523
376
  parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
524
- fs2.writeFileSync(settingsPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
377
+ fs.writeFileSync(settingsPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
525
378
  results.push({ type: "qwen", filePath: "settings.json", action: "appended" });
526
379
  }
527
380
  } catch {
528
381
  }
529
382
  } else {
530
- fs2.writeFileSync(settingsPath, qwenSettingsTemplate(), "utf-8");
383
+ fs.writeFileSync(settingsPath, qwenSettingsTemplate(), "utf-8");
531
384
  results.push({ type: "qwen", filePath: "settings.json", action: "created" });
532
385
  }
533
386
  } catch (err) {
@@ -563,18 +416,18 @@ function getCombinedAgentsMd(selectedTypes) {
563
416
  return sections.join("\n\n---\n\n");
564
417
  }
565
418
  function handleAntigravityMcpConfig(root, results) {
566
- const mcpPath = path2.resolve(root, ".agents/mcp_config.json");
419
+ const mcpPath = path.resolve(root, ".agents/mcp_config.json");
567
420
  if (results.some((r) => r.filePath === ".agents/mcp_config.json")) return;
568
421
  try {
569
- const mcpDir = path2.dirname(mcpPath);
570
- if (fs2.existsSync(mcpPath)) {
571
- const existingContent = fs2.readFileSync(mcpPath, "utf-8");
422
+ const mcpDir = path.dirname(mcpPath);
423
+ if (fs.existsSync(mcpPath)) {
424
+ const existingContent = fs.readFileSync(mcpPath, "utf-8");
572
425
  if (existingContent.includes("kuma")) {
573
426
  if (!existingContent.includes("_Generated by Kuma MCP_")) {
574
427
  const trimmed = existingContent.trimEnd();
575
428
  if (trimmed.endsWith("}")) {
576
429
  const updated = trimmed.slice(0, -1).trimEnd() + ',\n "_kuma_note": "Kuma MCP - Generated by kuma init"\n}\n';
577
- fs2.writeFileSync(mcpPath, updated, "utf-8");
430
+ fs.writeFileSync(mcpPath, updated, "utf-8");
578
431
  results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "appended" });
579
432
  }
580
433
  }
@@ -583,11 +436,11 @@ function handleAntigravityMcpConfig(root, results) {
583
436
  const parsed = JSON.parse(existingContent);
584
437
  parsed.mcpServers = parsed.mcpServers || {};
585
438
  parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
586
- fs2.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
439
+ fs.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
587
440
  results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "appended" });
588
441
  } else {
589
- if (!fs2.existsSync(mcpDir)) fs2.mkdirSync(mcpDir, { recursive: true });
590
- fs2.writeFileSync(mcpPath, antigravityMcpConfigTemplate(), "utf-8");
442
+ if (!fs.existsSync(mcpDir)) fs.mkdirSync(mcpDir, { recursive: true });
443
+ fs.writeFileSync(mcpPath, antigravityMcpConfigTemplate(), "utf-8");
591
444
  results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "created" });
592
445
  }
593
446
  } catch (err) {
@@ -600,18 +453,18 @@ function handleAntigravityMcpConfig(root, results) {
600
453
  }
601
454
  }
602
455
  function handleAiderSecondary(root, results) {
603
- const ymlPath = path2.resolve(root, ".aider.conf.yml");
456
+ const ymlPath = path.resolve(root, ".aider.conf.yml");
604
457
  if (results.some((r) => r.filePath === ".aider.conf.yml")) return;
605
458
  try {
606
459
  const conventionsRef = "read: CONVENTIONS.md";
607
- if (fs2.existsSync(ymlPath)) {
608
- const existingContent = fs2.readFileSync(ymlPath, "utf-8");
460
+ if (fs.existsSync(ymlPath)) {
461
+ const existingContent = fs.readFileSync(ymlPath, "utf-8");
609
462
  if (existingContent.includes("CONVENTIONS.md") || existingContent.includes("kuma")) {
610
463
  results.push({ type: "aider", filePath: ".aider.conf.yml", action: "skipped" });
611
464
  return;
612
465
  }
613
466
  const newContent = existingContent.trimEnd() + "\n\n# Kuma MCP conventions\n" + conventionsRef + "\n";
614
- fs2.writeFileSync(ymlPath, newContent, "utf-8");
467
+ fs.writeFileSync(ymlPath, newContent, "utf-8");
615
468
  results.push({ type: "aider", filePath: ".aider.conf.yml", action: "appended" });
616
469
  } else {
617
470
  const content = [
@@ -621,7 +474,7 @@ function handleAiderSecondary(root, results) {
621
474
  conventionsRef,
622
475
  ""
623
476
  ].join("\n");
624
- fs2.writeFileSync(ymlPath, content, "utf-8");
477
+ fs.writeFileSync(ymlPath, content, "utf-8");
625
478
  results.push({ type: "aider", filePath: ".aider.conf.yml", action: "created" });
626
479
  }
627
480
  } catch (err) {
@@ -634,10 +487,10 @@ function handleAiderSecondary(root, results) {
634
487
  }
635
488
  }
636
489
  function handleCopilotSecondary(root, results) {
637
- const skillPath = path2.resolve(root, ".github/skills/kuma/SKILL.md");
490
+ const skillPath = path.resolve(root, ".github/skills/kuma/SKILL.md");
638
491
  if (results.some((r) => r.filePath === ".github/skills/kuma/SKILL.md")) return;
639
492
  try {
640
- const dir = path2.dirname(skillPath);
493
+ const dir = path.dirname(skillPath);
641
494
  const content = [
642
495
  "---",
643
496
  "name: kuma-mcp",
@@ -648,17 +501,17 @@ function handleCopilotSecondary(root, results) {
648
501
  "",
649
502
  "\u{1F4D6} Read `.kuma/init.md` for detailed rules."
650
503
  ].join("\n");
651
- if (fs2.existsSync(skillPath)) {
652
- const existingContent = fs2.readFileSync(skillPath, "utf-8");
504
+ if (fs.existsSync(skillPath)) {
505
+ const existingContent = fs.readFileSync(skillPath, "utf-8");
653
506
  if (existingContent.includes("kuma")) {
654
507
  results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "skipped" });
655
508
  return;
656
509
  }
657
- fs2.writeFileSync(skillPath, existingContent.trimEnd() + "\n\n" + content, "utf-8");
510
+ fs.writeFileSync(skillPath, existingContent.trimEnd() + "\n\n" + content, "utf-8");
658
511
  results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "appended" });
659
512
  } else {
660
- if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
661
- fs2.writeFileSync(skillPath, content, "utf-8");
513
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
514
+ fs.writeFileSync(skillPath, content, "utf-8");
662
515
  results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "created" });
663
516
  }
664
517
  } catch (err) {
@@ -671,21 +524,21 @@ function handleCopilotSecondary(root, results) {
671
524
  }
672
525
  }
673
526
  function handleOpenclawSecondary(root, results) {
674
- const mcpPath = path2.resolve(root, ".agents/mcp_config.json");
527
+ const mcpPath = path.resolve(root, ".agents/mcp_config.json");
675
528
  if (results.some((r) => r.filePath === ".agents/mcp_config.json")) return;
676
529
  try {
677
- const dir = path2.dirname(mcpPath);
678
- if (fs2.existsSync(mcpPath)) {
679
- const existingContent = fs2.readFileSync(mcpPath, "utf-8");
530
+ const dir = path.dirname(mcpPath);
531
+ if (fs.existsSync(mcpPath)) {
532
+ const existingContent = fs.readFileSync(mcpPath, "utf-8");
680
533
  if (existingContent.includes("kuma")) return;
681
534
  const parsed = JSON.parse(existingContent);
682
535
  parsed.mcpServers = parsed.mcpServers || {};
683
536
  parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
684
- fs2.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
537
+ fs.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
685
538
  results.push({ type: "openclaw", filePath: ".agents/mcp_config.json", action: "appended" });
686
539
  } else {
687
- if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
688
- fs2.writeFileSync(mcpPath, antigravityMcpConfigTemplate(), "utf-8");
540
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
541
+ fs.writeFileSync(mcpPath, antigravityMcpConfigTemplate(), "utf-8");
689
542
  results.push({ type: "openclaw", filePath: ".agents/mcp_config.json", action: "created" });
690
543
  }
691
544
  } catch (err) {
@@ -698,20 +551,20 @@ function handleOpenclawSecondary(root, results) {
698
551
  }
699
552
  }
700
553
  function handleInitMdGeneration(root, results) {
701
- const initMdPath = path2.resolve(root, ".kuma/init.md");
554
+ const initMdPath = path.resolve(root, ".kuma/init.md");
702
555
  try {
703
- const kumaDir = path2.dirname(initMdPath);
704
- if (!fs2.existsSync(kumaDir)) fs2.mkdirSync(kumaDir, { recursive: true });
705
- if (fs2.existsSync(initMdPath)) {
706
- const existing = fs2.readFileSync(initMdPath, "utf-8");
556
+ const kumaDir = path.dirname(initMdPath);
557
+ if (!fs.existsSync(kumaDir)) fs.mkdirSync(kumaDir, { recursive: true });
558
+ if (fs.existsSync(initMdPath)) {
559
+ const existing = fs.readFileSync(initMdPath, "utf-8");
707
560
  if (existing.includes("_Generated by Kuma MCP_")) {
708
561
  results.push({ type: "claude", filePath: ".kuma/init.md", action: "skipped" });
709
562
  return;
710
563
  }
711
- fs2.writeFileSync(initMdPath, existing.trimEnd() + "\n\n" + generateInitMdContent(), "utf-8");
564
+ fs.writeFileSync(initMdPath, existing.trimEnd() + "\n\n" + generateInitMdContent(), "utf-8");
712
565
  results.push({ type: "claude", filePath: ".kuma/init.md", action: "appended" });
713
566
  } else {
714
- fs2.writeFileSync(initMdPath, generateInitMdContent(), "utf-8");
567
+ fs.writeFileSync(initMdPath, generateInitMdContent(), "utf-8");
715
568
  results.push({ type: "claude", filePath: ".kuma/init.md", action: "created" });
716
569
  }
717
570
  } catch (err) {
@@ -724,20 +577,20 @@ function handleInitMdGeneration(root, results) {
724
577
  }
725
578
  }
726
579
  function handleCodewhaleSecondary(root, results) {
727
- const mcpPath = path2.resolve(root, ".codewhale/mcp.json");
580
+ const mcpPath = path.resolve(root, ".codewhale/mcp.json");
728
581
  if (results.some((r) => r.filePath === ".codewhale/mcp.json")) return;
729
582
  try {
730
- const dir = path2.dirname(mcpPath);
731
- if (fs2.existsSync(mcpPath)) {
732
- const existingContent = fs2.readFileSync(mcpPath, "utf-8");
583
+ const dir = path.dirname(mcpPath);
584
+ if (fs.existsSync(mcpPath)) {
585
+ const existingContent = fs.readFileSync(mcpPath, "utf-8");
733
586
  if (existingContent.includes("kuma")) return;
734
587
  const parsed = JSON.parse(existingContent);
735
588
  parsed.mcpServers = parsed.mcpServers || {};
736
589
  parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
737
- fs2.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
590
+ fs.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
738
591
  results.push({ type: "codewhale", filePath: ".codewhale/mcp.json", action: "appended" });
739
592
  } else {
740
- if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
593
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
741
594
  const config = {
742
595
  mcpServers: {
743
596
  kuma: {
@@ -747,7 +600,7 @@ function handleCodewhaleSecondary(root, results) {
747
600
  }
748
601
  }
749
602
  };
750
- fs2.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
603
+ fs.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
751
604
  results.push({ type: "codewhale", filePath: ".codewhale/mcp.json", action: "created" });
752
605
  }
753
606
  } catch (err) {
@@ -769,28 +622,28 @@ function runInit(options) {
769
622
  let agentsMdHandled = false;
770
623
  for (const type of selected) {
771
624
  const relativePath = configFilePath(type);
772
- const fullPath = path2.resolve(root, relativePath);
625
+ const fullPath = path.resolve(root, relativePath);
773
626
  const getTemplate = TEMPLATES[type];
774
627
  try {
775
628
  if (AGENTS_MD_TYPES.includes(type) && !agentsMdHandled) {
776
629
  agentsMdHandled = true;
777
630
  const combinedContent = getCombinedAgentsMd(new Set(agentsMdSelected));
778
- if (fs2.existsSync(fullPath)) {
631
+ if (fs.existsSync(fullPath)) {
779
632
  if (options.skipExisting) {
780
633
  results.push({ type, filePath: relativePath, action: "skipped" });
781
634
  } else {
782
- const existingContent = fs2.readFileSync(fullPath, "utf-8");
635
+ const existingContent = fs.readFileSync(fullPath, "utf-8");
783
636
  if (existingContent.includes("_Generated by Kuma MCP_")) {
784
637
  results.push({ type, filePath: relativePath, action: "skipped" });
785
638
  } else {
786
- fs2.writeFileSync(fullPath, existingContent.trimEnd() + "\n\n" + combinedContent, "utf-8");
639
+ fs.writeFileSync(fullPath, existingContent.trimEnd() + "\n\n" + combinedContent, "utf-8");
787
640
  results.push({ type, filePath: relativePath, action: "appended" });
788
641
  }
789
642
  }
790
643
  } else {
791
- const dir = path2.dirname(fullPath);
792
- if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
793
- fs2.writeFileSync(fullPath, combinedContent, "utf-8");
644
+ const dir = path.dirname(fullPath);
645
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
646
+ fs.writeFileSync(fullPath, combinedContent, "utf-8");
794
647
  results.push({ type, filePath: relativePath, action: "created" });
795
648
  }
796
649
  if (selectedSet.has("codex")) handleCodexSecondary(root, results);
@@ -801,12 +654,12 @@ function runInit(options) {
801
654
  continue;
802
655
  } else {
803
656
  const template = getTemplate();
804
- if (fs2.existsSync(fullPath)) {
657
+ if (fs.existsSync(fullPath)) {
805
658
  if (options.skipExisting) {
806
659
  results.push({ type, filePath: relativePath, action: "skipped" });
807
660
  continue;
808
661
  }
809
- const existingContent = fs2.readFileSync(fullPath, "utf-8");
662
+ const existingContent = fs.readFileSync(fullPath, "utf-8");
810
663
  if (existingContent.includes("_Generated by Kuma MCP_")) {
811
664
  if (type === "antigravity") {
812
665
  handleAntigravityMcpConfig(root, results);
@@ -819,14 +672,14 @@ function runInit(options) {
819
672
  continue;
820
673
  }
821
674
  const newContent = existingContent.trimEnd() + APPEND_SEPARATOR + template;
822
- fs2.writeFileSync(fullPath, newContent, "utf-8");
675
+ fs.writeFileSync(fullPath, newContent, "utf-8");
823
676
  results.push({ type, filePath: relativePath, action: "appended" });
824
677
  } else {
825
- const dir = path2.dirname(fullPath);
826
- if (!fs2.existsSync(dir)) {
827
- fs2.mkdirSync(dir, { recursive: true });
678
+ const dir = path.dirname(fullPath);
679
+ if (!fs.existsSync(dir)) {
680
+ fs.mkdirSync(dir, { recursive: true });
828
681
  }
829
- fs2.writeFileSync(fullPath, template, "utf-8");
682
+ fs.writeFileSync(fullPath, template, "utf-8");
830
683
  results.push({ type, filePath: relativePath, action: "created" });
831
684
  }
832
685
  if (type === "antigravity") {
@@ -894,11 +747,6 @@ function formatInitResults(results) {
894
747
  }
895
748
 
896
749
  export {
897
- validateFilePath,
898
- validateFileExtension,
899
- getProjectRoot,
900
- getBackupPath,
901
- ensureBackupDir,
902
750
  ALL_CONFIG_TYPES,
903
751
  CONFIG_LABELS,
904
752
  generateInitMdContent,