@plumpslabs/kuma 2.1.3 → 2.1.5
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/dist/chunk-2BFTJMBP.js +907 -0
- package/dist/index.js +865 -1255
- package/dist/init-AMRMKI4X.js +14 -0
- package/package.json +1 -1
|
@@ -0,0 +1,907 @@
|
|
|
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
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/cli/init.ts
|
|
155
|
+
var ALL_CONFIG_TYPES = [
|
|
156
|
+
"claude",
|
|
157
|
+
"cursor",
|
|
158
|
+
"windsurf",
|
|
159
|
+
"copilot",
|
|
160
|
+
"cline",
|
|
161
|
+
"aider",
|
|
162
|
+
"antigravity",
|
|
163
|
+
"opencode",
|
|
164
|
+
"codex",
|
|
165
|
+
"qwen",
|
|
166
|
+
"kiro",
|
|
167
|
+
"openclaw",
|
|
168
|
+
"codewhale"
|
|
169
|
+
];
|
|
170
|
+
var CONFIG_LABELS = {
|
|
171
|
+
claude: "Claude Code (CLAUDE.md / plugin)",
|
|
172
|
+
cursor: "Cursor (.cursor/rules/*.mdc)",
|
|
173
|
+
windsurf: "Windsurf (.windsurfrules)",
|
|
174
|
+
copilot: "GitHub Copilot Editor (AGENTS.md + Skill)",
|
|
175
|
+
cline: "Cline (.clinerules/*.md)",
|
|
176
|
+
aider: "Aider (CONVENTIONS.md via .aider.conf.yml)",
|
|
177
|
+
antigravity: "Antigravity CLI (.agents/skills/)",
|
|
178
|
+
opencode: "OpenCode (opencode.json)",
|
|
179
|
+
codex: "Codex CLI (AGENTS.md + .codex/config.toml)",
|
|
180
|
+
qwen: "Qwen Code (AGENTS.md + settings.json)",
|
|
181
|
+
kiro: "Kiro (.kiro/steering/*.md)",
|
|
182
|
+
openclaw: "OpenClaw (skills/)",
|
|
183
|
+
codewhale: "CodeWhale (skills/ + .codewhale/mcp.json)"
|
|
184
|
+
};
|
|
185
|
+
function configFilePath(type) {
|
|
186
|
+
switch (type) {
|
|
187
|
+
case "claude":
|
|
188
|
+
return "CLAUDE.md";
|
|
189
|
+
case "cursor":
|
|
190
|
+
return ".cursor/rules/kuma.mdc";
|
|
191
|
+
case "windsurf":
|
|
192
|
+
return ".windsurfrules";
|
|
193
|
+
case "copilot":
|
|
194
|
+
return "AGENTS.md";
|
|
195
|
+
case "cline":
|
|
196
|
+
return ".clinerules/kuma.md";
|
|
197
|
+
case "aider":
|
|
198
|
+
return "CONVENTIONS.md";
|
|
199
|
+
case "antigravity":
|
|
200
|
+
return ".agents/skills/kuma/SKILL.md";
|
|
201
|
+
case "opencode":
|
|
202
|
+
return "opencode.json";
|
|
203
|
+
case "codex":
|
|
204
|
+
return "AGENTS.md";
|
|
205
|
+
case "qwen":
|
|
206
|
+
return "AGENTS.md";
|
|
207
|
+
case "kiro":
|
|
208
|
+
return ".kiro/steering/kuma.md";
|
|
209
|
+
case "openclaw":
|
|
210
|
+
return "skills/kuma/SKILL.md";
|
|
211
|
+
case "codewhale":
|
|
212
|
+
return "skills/kuma/SKILL.md";
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
var BOOTSTRAP_LINES = [
|
|
216
|
+
"Kuma MCP tools are installed. All behavioral rules are in `.kuma/init.md`.",
|
|
217
|
+
"**Before coding, call `kuma_init()`** to load project context and session memory.",
|
|
218
|
+
"Project knowledge persists in `.kuma/memories/*.md` across sessions."
|
|
219
|
+
].join("\n");
|
|
220
|
+
var KUMA_CORE_INSTRUCTIONS = BOOTSTRAP_LINES;
|
|
221
|
+
function claudeTemplate() {
|
|
222
|
+
return [
|
|
223
|
+
"# Kuma MCP",
|
|
224
|
+
"",
|
|
225
|
+
KUMA_CORE_INSTRUCTIONS,
|
|
226
|
+
"",
|
|
227
|
+
"\u{1F4D6} Rules: `.kuma/init.md`",
|
|
228
|
+
"\u{1F9E0} Memories: `.kuma/memories/*.md`"
|
|
229
|
+
].join("\n");
|
|
230
|
+
}
|
|
231
|
+
function cursorRulesTemplate() {
|
|
232
|
+
return [
|
|
233
|
+
"---",
|
|
234
|
+
"description: Kuma MCP \u2014 .kuma/ is the single source of truth",
|
|
235
|
+
"alwaysApply: true",
|
|
236
|
+
"---",
|
|
237
|
+
"",
|
|
238
|
+
KUMA_CORE_INSTRUCTIONS,
|
|
239
|
+
"",
|
|
240
|
+
"\u{1F4D6} Read `.kuma/init.md` for detailed rules."
|
|
241
|
+
].join("\n");
|
|
242
|
+
}
|
|
243
|
+
function windsurfRulesTemplate() {
|
|
244
|
+
return [
|
|
245
|
+
"# Kuma MCP \u2014 Windsurf",
|
|
246
|
+
"",
|
|
247
|
+
KUMA_CORE_INSTRUCTIONS
|
|
248
|
+
].join("\n");
|
|
249
|
+
}
|
|
250
|
+
function copilotTemplate() {
|
|
251
|
+
return [
|
|
252
|
+
"## Kuma MCP",
|
|
253
|
+
"",
|
|
254
|
+
KUMA_CORE_INSTRUCTIONS
|
|
255
|
+
].join("\n");
|
|
256
|
+
}
|
|
257
|
+
function clineRulesTemplate() {
|
|
258
|
+
return [
|
|
259
|
+
"---",
|
|
260
|
+
"description: Kuma MCP \u2014 .kuma/ is the single source of truth",
|
|
261
|
+
"paths:",
|
|
262
|
+
' - "*"',
|
|
263
|
+
"---",
|
|
264
|
+
"",
|
|
265
|
+
KUMA_CORE_INSTRUCTIONS,
|
|
266
|
+
"",
|
|
267
|
+
"\u{1F4D6} Read `.kuma/init.md` for detailed rules."
|
|
268
|
+
].join("\n");
|
|
269
|
+
}
|
|
270
|
+
function aiderTemplate() {
|
|
271
|
+
return [
|
|
272
|
+
"# Kuma MCP",
|
|
273
|
+
"",
|
|
274
|
+
KUMA_CORE_INSTRUCTIONS
|
|
275
|
+
].join("\n");
|
|
276
|
+
}
|
|
277
|
+
function opencodeTemplate() {
|
|
278
|
+
const config = {
|
|
279
|
+
$schema: "https://opencode-ai.github.io/schema.json",
|
|
280
|
+
mcp: {
|
|
281
|
+
kuma: {
|
|
282
|
+
type: "local",
|
|
283
|
+
command: ["npx", "-y", "@plumpslabs/kuma"],
|
|
284
|
+
enabled: true
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
instructions: [".kuma/init.md"]
|
|
288
|
+
};
|
|
289
|
+
return JSON.stringify(config, null, 2) + "\n";
|
|
290
|
+
}
|
|
291
|
+
function codexTemplate() {
|
|
292
|
+
return [
|
|
293
|
+
"## Kuma MCP",
|
|
294
|
+
"",
|
|
295
|
+
KUMA_CORE_INSTRUCTIONS,
|
|
296
|
+
"",
|
|
297
|
+
"\u{1F4D6} Rules: `.kuma/init.md`"
|
|
298
|
+
].join("\n");
|
|
299
|
+
}
|
|
300
|
+
function codexConfigTomlTemplate() {
|
|
301
|
+
return [
|
|
302
|
+
"# Generated by Kuma MCP - https://github.com/plumpslabs/kuma",
|
|
303
|
+
"# Kuma MCP server config for Codex CLI",
|
|
304
|
+
"",
|
|
305
|
+
"[mcp_servers.kuma]",
|
|
306
|
+
'command = "npx"',
|
|
307
|
+
'args = ["-y", "@plumpslabs/kuma"]',
|
|
308
|
+
""
|
|
309
|
+
].join("\n");
|
|
310
|
+
}
|
|
311
|
+
function qwenTemplate() {
|
|
312
|
+
return [
|
|
313
|
+
"## Kuma MCP",
|
|
314
|
+
"",
|
|
315
|
+
KUMA_CORE_INSTRUCTIONS,
|
|
316
|
+
"",
|
|
317
|
+
"\u{1F4D6} Rules: `.kuma/init.md`"
|
|
318
|
+
].join("\n");
|
|
319
|
+
}
|
|
320
|
+
function qwenSettingsTemplate() {
|
|
321
|
+
const config = {
|
|
322
|
+
mcpServers: {
|
|
323
|
+
kuma: {
|
|
324
|
+
command: "npx",
|
|
325
|
+
args: ["-y", "@plumpslabs/kuma"],
|
|
326
|
+
env: {}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
return JSON.stringify(config, null, 2) + "\n";
|
|
331
|
+
}
|
|
332
|
+
function kiroRulesTemplate() {
|
|
333
|
+
return [
|
|
334
|
+
"---",
|
|
335
|
+
"name: kuma-mcp",
|
|
336
|
+
"description: Kuma MCP \u2014 .kuma/ is the single source of truth",
|
|
337
|
+
"inclusion: always",
|
|
338
|
+
"---",
|
|
339
|
+
"",
|
|
340
|
+
KUMA_CORE_INSTRUCTIONS,
|
|
341
|
+
"",
|
|
342
|
+
"\u{1F4D6} Read `.kuma/init.md` for detailed rules."
|
|
343
|
+
].join("\n");
|
|
344
|
+
}
|
|
345
|
+
function openclawSkillTemplate() {
|
|
346
|
+
return [
|
|
347
|
+
"---",
|
|
348
|
+
"name: kuma-mcp",
|
|
349
|
+
"description: Kuma MCP \u2014 .kuma/ is the single source of truth",
|
|
350
|
+
"---",
|
|
351
|
+
"",
|
|
352
|
+
KUMA_CORE_INSTRUCTIONS,
|
|
353
|
+
"",
|
|
354
|
+
"\u{1F4D6} Read `.kuma/init.md` for detailed rules.",
|
|
355
|
+
"\u{1F9E0} Memories: `.kuma/memories/*.md`"
|
|
356
|
+
].join("\n");
|
|
357
|
+
}
|
|
358
|
+
function codewhaleTemplate() {
|
|
359
|
+
return [
|
|
360
|
+
"---",
|
|
361
|
+
"name: kuma-mcp",
|
|
362
|
+
"description: Kuma MCP \u2014 .kuma/ is the single source of truth",
|
|
363
|
+
"---",
|
|
364
|
+
"",
|
|
365
|
+
KUMA_CORE_INSTRUCTIONS,
|
|
366
|
+
"",
|
|
367
|
+
"\u{1F4D6} Read `.kuma/init.md` for detailed rules.",
|
|
368
|
+
"\u{1F9E0} Memories: `.kuma/memories/*.md`"
|
|
369
|
+
].join("\n");
|
|
370
|
+
}
|
|
371
|
+
function antigravitySkillTemplate() {
|
|
372
|
+
return [
|
|
373
|
+
"---",
|
|
374
|
+
"name: kuma-mcp",
|
|
375
|
+
"description: Kuma MCP \u2014 .kuma/ is the single source of truth",
|
|
376
|
+
"---",
|
|
377
|
+
"",
|
|
378
|
+
KUMA_CORE_INSTRUCTIONS,
|
|
379
|
+
"",
|
|
380
|
+
"\u{1F4D6} Read `.kuma/init.md` for detailed rules.",
|
|
381
|
+
"\u{1F9E0} Memories: `.kuma/memories/*.md`"
|
|
382
|
+
].join("\n");
|
|
383
|
+
}
|
|
384
|
+
function antigravityMcpConfigTemplate() {
|
|
385
|
+
const config = {
|
|
386
|
+
mcpServers: {
|
|
387
|
+
kuma: {
|
|
388
|
+
command: "npx",
|
|
389
|
+
args: ["-y", "@plumpslabs/kuma"],
|
|
390
|
+
env: {}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
return JSON.stringify(config, null, 2) + "\n";
|
|
395
|
+
}
|
|
396
|
+
function generateInitMdContent() {
|
|
397
|
+
return [
|
|
398
|
+
"# Kuma Init \u2014 Behavioral Rules",
|
|
399
|
+
"",
|
|
400
|
+
"_(Auto-generated by `kuma init` \u2014 edit this file directly to customize rules)_",
|
|
401
|
+
"",
|
|
402
|
+
"## Code Search",
|
|
403
|
+
"",
|
|
404
|
+
"- Use the **smart_grep** tool to search code \u2014 NOT bash grep/ripgrep manually",
|
|
405
|
+
"- smart_grep returns line numbers + context, caches results, respects .gitignore",
|
|
406
|
+
"",
|
|
407
|
+
"## Reading Code",
|
|
408
|
+
"",
|
|
409
|
+
"- Use the **smart_file_picker** tool to read files with smart chunking",
|
|
410
|
+
"- For large files, use startLine/endLine to read specific ranges",
|
|
411
|
+
"",
|
|
412
|
+
"## Editing Code",
|
|
413
|
+
"",
|
|
414
|
+
"- Use the **precise_diff_editor** tool to edit files (fuzzy matching + auto-backup)",
|
|
415
|
+
"- DO NOT create Python/Node scripts to patch files; use precise_diff_editor directly",
|
|
416
|
+
"- DO NOT use bash sed/cat/awk to modify source files",
|
|
417
|
+
"",
|
|
418
|
+
"## Creating Files",
|
|
419
|
+
"",
|
|
420
|
+
"- Use the **batch_file_writer** tool to create new files (up to 15 at once)",
|
|
421
|
+
"- Before creating a file, ask: does this need to exist, or can it merge into an existing module?",
|
|
422
|
+
"",
|
|
423
|
+
"## Running Tasks",
|
|
424
|
+
"",
|
|
425
|
+
"- Use the **execute_safe_test** tool for test/build/lint/typecheck",
|
|
426
|
+
"- Always run typecheck after editing TypeScript files",
|
|
427
|
+
"",
|
|
428
|
+
"## Code Review",
|
|
429
|
+
"",
|
|
430
|
+
"- Use the **code_reviewer** tool after making changes",
|
|
431
|
+
"- Supports focus: correctness, security, performance, over-engineering",
|
|
432
|
+
"",
|
|
433
|
+
"## Session Awareness",
|
|
434
|
+
"",
|
|
435
|
+
"- Start each session by calling **kuma_init()** to load context",
|
|
436
|
+
"- Use **get_session_memory()** to recall what happened before",
|
|
437
|
+
"- Use **kuma_reflect** / **kuma_guard** to stay on track",
|
|
438
|
+
"- Use **write_memory()** to persist decisions and glossary terms",
|
|
439
|
+
"",
|
|
440
|
+
"## General Rules",
|
|
441
|
+
"",
|
|
442
|
+
"- When a tool errors, READ the error carefully before acting",
|
|
443
|
+
"- After 3+ edits without running tests, stop and verify",
|
|
444
|
+
"- If a tool fails, check the message \u2014 don't retry blindly",
|
|
445
|
+
"- Detect conventions first with **project_conventions()**",
|
|
446
|
+
"",
|
|
447
|
+
"---",
|
|
448
|
+
"_Generated by Kuma MCP - https://github.com/plumpslabs/kuma_"
|
|
449
|
+
].join("\n");
|
|
450
|
+
}
|
|
451
|
+
var TEMPLATES = {
|
|
452
|
+
claude: claudeTemplate,
|
|
453
|
+
cursor: cursorRulesTemplate,
|
|
454
|
+
windsurf: windsurfRulesTemplate,
|
|
455
|
+
copilot: copilotTemplate,
|
|
456
|
+
cline: clineRulesTemplate,
|
|
457
|
+
aider: aiderTemplate,
|
|
458
|
+
antigravity: antigravitySkillTemplate,
|
|
459
|
+
opencode: opencodeTemplate,
|
|
460
|
+
codex: codexTemplate,
|
|
461
|
+
qwen: qwenTemplate,
|
|
462
|
+
kiro: kiroRulesTemplate,
|
|
463
|
+
openclaw: openclawSkillTemplate,
|
|
464
|
+
codewhale: codewhaleTemplate
|
|
465
|
+
};
|
|
466
|
+
var APPEND_SEPARATOR = "\n\n---\n_Generated by Kuma MCP - https://github.com/plumpslabs/kuma_\n\n";
|
|
467
|
+
function handleOpencodeSecondary(_root, _results) {
|
|
468
|
+
}
|
|
469
|
+
function handleCodexSecondary(root, results) {
|
|
470
|
+
const tomlPath = path2.resolve(root, ".codex/config.toml");
|
|
471
|
+
if (results.some((r) => r.filePath === ".codex/config.toml")) return;
|
|
472
|
+
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");
|
|
477
|
+
if (existingContent.includes("kuma")) {
|
|
478
|
+
results.push({ type: "codex", filePath: ".codex/config.toml", action: "skipped" });
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
fs2.writeFileSync(tomlPath, existingContent.trimEnd() + "\n\n" + codexConfigTomlTemplate(), "utf-8");
|
|
482
|
+
results.push({ type: "codex", filePath: ".codex/config.toml", action: "appended" });
|
|
483
|
+
} else {
|
|
484
|
+
fs2.writeFileSync(tomlPath, codexConfigTomlTemplate(), "utf-8");
|
|
485
|
+
results.push({ type: "codex", filePath: ".codex/config.toml", action: "created" });
|
|
486
|
+
}
|
|
487
|
+
} catch (err) {
|
|
488
|
+
results.push({
|
|
489
|
+
type: "codex",
|
|
490
|
+
filePath: ".codex/config.toml",
|
|
491
|
+
action: "error",
|
|
492
|
+
error: err instanceof Error ? err.message : String(err)
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function handleQwenSecondary(root, results) {
|
|
497
|
+
const settingsPath = path2.resolve(root, "settings.json");
|
|
498
|
+
if (results.some((r) => r.filePath === "settings.json")) return;
|
|
499
|
+
try {
|
|
500
|
+
if (fs2.existsSync(settingsPath)) {
|
|
501
|
+
const existingContent = fs2.readFileSync(settingsPath, "utf-8");
|
|
502
|
+
if (existingContent.includes("kuma")) {
|
|
503
|
+
if (!existingContent.includes("_Generated by Kuma MCP_")) {
|
|
504
|
+
try {
|
|
505
|
+
const parsed = JSON.parse(existingContent);
|
|
506
|
+
parsed.mcpServers = parsed.mcpServers || {};
|
|
507
|
+
if (!parsed.mcpServers.kuma) {
|
|
508
|
+
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
509
|
+
fs2.writeFileSync(settingsPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
510
|
+
results.push({ type: "qwen", filePath: "settings.json", action: "appended" });
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
} catch {
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
results.push({ type: "qwen", filePath: "settings.json", action: "skipped" });
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
try {
|
|
520
|
+
const parsed = JSON.parse(existingContent);
|
|
521
|
+
parsed.mcpServers = parsed.mcpServers || {};
|
|
522
|
+
if (!parsed.mcpServers.kuma) {
|
|
523
|
+
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
524
|
+
fs2.writeFileSync(settingsPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
525
|
+
results.push({ type: "qwen", filePath: "settings.json", action: "appended" });
|
|
526
|
+
}
|
|
527
|
+
} catch {
|
|
528
|
+
}
|
|
529
|
+
} else {
|
|
530
|
+
fs2.writeFileSync(settingsPath, qwenSettingsTemplate(), "utf-8");
|
|
531
|
+
results.push({ type: "qwen", filePath: "settings.json", action: "created" });
|
|
532
|
+
}
|
|
533
|
+
} catch (err) {
|
|
534
|
+
results.push({
|
|
535
|
+
type: "qwen",
|
|
536
|
+
filePath: "settings.json",
|
|
537
|
+
action: "error",
|
|
538
|
+
error: err instanceof Error ? err.message : String(err)
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
var AGENTS_MD_TYPES = ["codex", "qwen", "copilot"];
|
|
543
|
+
function getAgentsMdHeader() {
|
|
544
|
+
return [
|
|
545
|
+
"# Kuma MCP - Combined Agent Instructions",
|
|
546
|
+
"",
|
|
547
|
+
"This file contains instructions for AI coding agents that read AGENTS.md.",
|
|
548
|
+
"Each section applies to a specific agent. Unused sections can be safely removed.",
|
|
549
|
+
"",
|
|
550
|
+
"---",
|
|
551
|
+
"_Generated by Kuma MCP - https://github.com/plumpslabs/kuma_",
|
|
552
|
+
""
|
|
553
|
+
].join("\n");
|
|
554
|
+
}
|
|
555
|
+
function getCombinedAgentsMd(selectedTypes) {
|
|
556
|
+
const sections = [getAgentsMdHeader()];
|
|
557
|
+
const agentOrder = ["codex", "qwen", "copilot"];
|
|
558
|
+
for (const t of agentOrder) {
|
|
559
|
+
if (selectedTypes.has(t)) {
|
|
560
|
+
sections.push(TEMPLATES[t]());
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return sections.join("\n\n---\n\n");
|
|
564
|
+
}
|
|
565
|
+
function handleAntigravityMcpConfig(root, results) {
|
|
566
|
+
const mcpPath = path2.resolve(root, ".agents/mcp_config.json");
|
|
567
|
+
if (results.some((r) => r.filePath === ".agents/mcp_config.json")) return;
|
|
568
|
+
try {
|
|
569
|
+
const mcpDir = path2.dirname(mcpPath);
|
|
570
|
+
if (fs2.existsSync(mcpPath)) {
|
|
571
|
+
const existingContent = fs2.readFileSync(mcpPath, "utf-8");
|
|
572
|
+
if (existingContent.includes("kuma")) {
|
|
573
|
+
if (!existingContent.includes("_Generated by Kuma MCP_")) {
|
|
574
|
+
const trimmed = existingContent.trimEnd();
|
|
575
|
+
if (trimmed.endsWith("}")) {
|
|
576
|
+
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");
|
|
578
|
+
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "appended" });
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
const parsed = JSON.parse(existingContent);
|
|
584
|
+
parsed.mcpServers = parsed.mcpServers || {};
|
|
585
|
+
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
586
|
+
fs2.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
587
|
+
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "appended" });
|
|
588
|
+
} else {
|
|
589
|
+
if (!fs2.existsSync(mcpDir)) fs2.mkdirSync(mcpDir, { recursive: true });
|
|
590
|
+
fs2.writeFileSync(mcpPath, antigravityMcpConfigTemplate(), "utf-8");
|
|
591
|
+
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "created" });
|
|
592
|
+
}
|
|
593
|
+
} catch (err) {
|
|
594
|
+
results.push({
|
|
595
|
+
type: "antigravity",
|
|
596
|
+
filePath: ".agents/mcp_config.json",
|
|
597
|
+
action: "error",
|
|
598
|
+
error: err instanceof Error ? err.message : String(err)
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
function handleAiderSecondary(root, results) {
|
|
603
|
+
const ymlPath = path2.resolve(root, ".aider.conf.yml");
|
|
604
|
+
if (results.some((r) => r.filePath === ".aider.conf.yml")) return;
|
|
605
|
+
try {
|
|
606
|
+
const conventionsRef = "read: CONVENTIONS.md";
|
|
607
|
+
if (fs2.existsSync(ymlPath)) {
|
|
608
|
+
const existingContent = fs2.readFileSync(ymlPath, "utf-8");
|
|
609
|
+
if (existingContent.includes("CONVENTIONS.md") || existingContent.includes("kuma")) {
|
|
610
|
+
results.push({ type: "aider", filePath: ".aider.conf.yml", action: "skipped" });
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
const newContent = existingContent.trimEnd() + "\n\n# Kuma MCP conventions\n" + conventionsRef + "\n";
|
|
614
|
+
fs2.writeFileSync(ymlPath, newContent, "utf-8");
|
|
615
|
+
results.push({ type: "aider", filePath: ".aider.conf.yml", action: "appended" });
|
|
616
|
+
} else {
|
|
617
|
+
const content = [
|
|
618
|
+
"# Generated by Kuma MCP - https://github.com/plumpslabs/kuma",
|
|
619
|
+
"# Aider will read CONVENTIONS.md for coding conventions",
|
|
620
|
+
"",
|
|
621
|
+
conventionsRef,
|
|
622
|
+
""
|
|
623
|
+
].join("\n");
|
|
624
|
+
fs2.writeFileSync(ymlPath, content, "utf-8");
|
|
625
|
+
results.push({ type: "aider", filePath: ".aider.conf.yml", action: "created" });
|
|
626
|
+
}
|
|
627
|
+
} catch (err) {
|
|
628
|
+
results.push({
|
|
629
|
+
type: "aider",
|
|
630
|
+
filePath: ".aider.conf.yml",
|
|
631
|
+
action: "error",
|
|
632
|
+
error: err instanceof Error ? err.message : String(err)
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
function handleCopilotSecondary(root, results) {
|
|
637
|
+
const skillPath = path2.resolve(root, ".github/skills/kuma/SKILL.md");
|
|
638
|
+
if (results.some((r) => r.filePath === ".github/skills/kuma/SKILL.md")) return;
|
|
639
|
+
try {
|
|
640
|
+
const dir = path2.dirname(skillPath);
|
|
641
|
+
const content = [
|
|
642
|
+
"---",
|
|
643
|
+
"name: kuma-mcp",
|
|
644
|
+
"description: Kuma MCP \u2014 .kuma/ is the single source of truth",
|
|
645
|
+
"---",
|
|
646
|
+
"",
|
|
647
|
+
KUMA_CORE_INSTRUCTIONS,
|
|
648
|
+
"",
|
|
649
|
+
"\u{1F4D6} Read `.kuma/init.md` for detailed rules."
|
|
650
|
+
].join("\n");
|
|
651
|
+
if (fs2.existsSync(skillPath)) {
|
|
652
|
+
const existingContent = fs2.readFileSync(skillPath, "utf-8");
|
|
653
|
+
if (existingContent.includes("kuma")) {
|
|
654
|
+
results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "skipped" });
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
fs2.writeFileSync(skillPath, existingContent.trimEnd() + "\n\n" + content, "utf-8");
|
|
658
|
+
results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "appended" });
|
|
659
|
+
} else {
|
|
660
|
+
if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
|
|
661
|
+
fs2.writeFileSync(skillPath, content, "utf-8");
|
|
662
|
+
results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "created" });
|
|
663
|
+
}
|
|
664
|
+
} catch (err) {
|
|
665
|
+
results.push({
|
|
666
|
+
type: "copilot",
|
|
667
|
+
filePath: ".github/skills/kuma/SKILL.md",
|
|
668
|
+
action: "error",
|
|
669
|
+
error: err instanceof Error ? err.message : String(err)
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
function handleOpenclawSecondary(root, results) {
|
|
674
|
+
const mcpPath = path2.resolve(root, ".agents/mcp_config.json");
|
|
675
|
+
if (results.some((r) => r.filePath === ".agents/mcp_config.json")) return;
|
|
676
|
+
try {
|
|
677
|
+
const dir = path2.dirname(mcpPath);
|
|
678
|
+
if (fs2.existsSync(mcpPath)) {
|
|
679
|
+
const existingContent = fs2.readFileSync(mcpPath, "utf-8");
|
|
680
|
+
if (existingContent.includes("kuma")) return;
|
|
681
|
+
const parsed = JSON.parse(existingContent);
|
|
682
|
+
parsed.mcpServers = parsed.mcpServers || {};
|
|
683
|
+
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
684
|
+
fs2.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
685
|
+
results.push({ type: "openclaw", filePath: ".agents/mcp_config.json", action: "appended" });
|
|
686
|
+
} else {
|
|
687
|
+
if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
|
|
688
|
+
fs2.writeFileSync(mcpPath, antigravityMcpConfigTemplate(), "utf-8");
|
|
689
|
+
results.push({ type: "openclaw", filePath: ".agents/mcp_config.json", action: "created" });
|
|
690
|
+
}
|
|
691
|
+
} catch (err) {
|
|
692
|
+
results.push({
|
|
693
|
+
type: "openclaw",
|
|
694
|
+
filePath: ".agents/mcp_config.json",
|
|
695
|
+
action: "error",
|
|
696
|
+
error: err instanceof Error ? err.message : String(err)
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
function handleInitMdGeneration(root, results) {
|
|
701
|
+
const initMdPath = path2.resolve(root, ".kuma/init.md");
|
|
702
|
+
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");
|
|
707
|
+
if (existing.includes("_Generated by Kuma MCP_")) {
|
|
708
|
+
results.push({ type: "claude", filePath: ".kuma/init.md", action: "skipped" });
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
fs2.writeFileSync(initMdPath, existing.trimEnd() + "\n\n" + generateInitMdContent(), "utf-8");
|
|
712
|
+
results.push({ type: "claude", filePath: ".kuma/init.md", action: "appended" });
|
|
713
|
+
} else {
|
|
714
|
+
fs2.writeFileSync(initMdPath, generateInitMdContent(), "utf-8");
|
|
715
|
+
results.push({ type: "claude", filePath: ".kuma/init.md", action: "created" });
|
|
716
|
+
}
|
|
717
|
+
} catch (err) {
|
|
718
|
+
results.push({
|
|
719
|
+
type: "claude",
|
|
720
|
+
filePath: ".kuma/init.md",
|
|
721
|
+
action: "error",
|
|
722
|
+
error: err instanceof Error ? err.message : String(err)
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
function handleCodewhaleSecondary(root, results) {
|
|
727
|
+
const mcpPath = path2.resolve(root, ".codewhale/mcp.json");
|
|
728
|
+
if (results.some((r) => r.filePath === ".codewhale/mcp.json")) return;
|
|
729
|
+
try {
|
|
730
|
+
const dir = path2.dirname(mcpPath);
|
|
731
|
+
if (fs2.existsSync(mcpPath)) {
|
|
732
|
+
const existingContent = fs2.readFileSync(mcpPath, "utf-8");
|
|
733
|
+
if (existingContent.includes("kuma")) return;
|
|
734
|
+
const parsed = JSON.parse(existingContent);
|
|
735
|
+
parsed.mcpServers = parsed.mcpServers || {};
|
|
736
|
+
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
737
|
+
fs2.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
738
|
+
results.push({ type: "codewhale", filePath: ".codewhale/mcp.json", action: "appended" });
|
|
739
|
+
} else {
|
|
740
|
+
if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
|
|
741
|
+
const config = {
|
|
742
|
+
mcpServers: {
|
|
743
|
+
kuma: {
|
|
744
|
+
command: "npx",
|
|
745
|
+
args: ["-y", "@plumpslabs/kuma"],
|
|
746
|
+
env: {}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
fs2.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
751
|
+
results.push({ type: "codewhale", filePath: ".codewhale/mcp.json", action: "created" });
|
|
752
|
+
}
|
|
753
|
+
} catch (err) {
|
|
754
|
+
results.push({
|
|
755
|
+
type: "codewhale",
|
|
756
|
+
filePath: ".codewhale/mcp.json",
|
|
757
|
+
action: "error",
|
|
758
|
+
error: err instanceof Error ? err.message : String(err)
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
function runInit(options) {
|
|
763
|
+
const root = options.projectRoot ?? getProjectRoot();
|
|
764
|
+
const selected = options.types.length > 0 ? options.types : ALL_CONFIG_TYPES;
|
|
765
|
+
const results = [];
|
|
766
|
+
handleInitMdGeneration(root, results);
|
|
767
|
+
const selectedSet = new Set(selected);
|
|
768
|
+
const agentsMdSelected = AGENTS_MD_TYPES.filter((t) => selectedSet.has(t));
|
|
769
|
+
let agentsMdHandled = false;
|
|
770
|
+
for (const type of selected) {
|
|
771
|
+
const relativePath = configFilePath(type);
|
|
772
|
+
const fullPath = path2.resolve(root, relativePath);
|
|
773
|
+
const getTemplate = TEMPLATES[type];
|
|
774
|
+
try {
|
|
775
|
+
if (AGENTS_MD_TYPES.includes(type) && !agentsMdHandled) {
|
|
776
|
+
agentsMdHandled = true;
|
|
777
|
+
const combinedContent = getCombinedAgentsMd(new Set(agentsMdSelected));
|
|
778
|
+
if (fs2.existsSync(fullPath)) {
|
|
779
|
+
if (options.skipExisting) {
|
|
780
|
+
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
781
|
+
} else {
|
|
782
|
+
const existingContent = fs2.readFileSync(fullPath, "utf-8");
|
|
783
|
+
if (existingContent.includes("_Generated by Kuma MCP_")) {
|
|
784
|
+
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
785
|
+
} else {
|
|
786
|
+
fs2.writeFileSync(fullPath, existingContent.trimEnd() + "\n\n" + combinedContent, "utf-8");
|
|
787
|
+
results.push({ type, filePath: relativePath, action: "appended" });
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
} else {
|
|
791
|
+
const dir = path2.dirname(fullPath);
|
|
792
|
+
if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
|
|
793
|
+
fs2.writeFileSync(fullPath, combinedContent, "utf-8");
|
|
794
|
+
results.push({ type, filePath: relativePath, action: "created" });
|
|
795
|
+
}
|
|
796
|
+
if (selectedSet.has("codex")) handleCodexSecondary(root, results);
|
|
797
|
+
if (selectedSet.has("qwen")) handleQwenSecondary(root, results);
|
|
798
|
+
if (selectedSet.has("copilot")) handleCopilotSecondary(root, results);
|
|
799
|
+
} else if (AGENTS_MD_TYPES.includes(type) && agentsMdHandled) {
|
|
800
|
+
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
801
|
+
continue;
|
|
802
|
+
} else {
|
|
803
|
+
const template = getTemplate();
|
|
804
|
+
if (fs2.existsSync(fullPath)) {
|
|
805
|
+
if (options.skipExisting) {
|
|
806
|
+
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
const existingContent = fs2.readFileSync(fullPath, "utf-8");
|
|
810
|
+
if (existingContent.includes("_Generated by Kuma MCP_")) {
|
|
811
|
+
if (type === "antigravity") {
|
|
812
|
+
handleAntigravityMcpConfig(root, results);
|
|
813
|
+
} else if (type === "openclaw") {
|
|
814
|
+
handleOpenclawSecondary(root, results);
|
|
815
|
+
} else if (type === "codewhale") {
|
|
816
|
+
handleCodewhaleSecondary(root, results);
|
|
817
|
+
}
|
|
818
|
+
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
const newContent = existingContent.trimEnd() + APPEND_SEPARATOR + template;
|
|
822
|
+
fs2.writeFileSync(fullPath, newContent, "utf-8");
|
|
823
|
+
results.push({ type, filePath: relativePath, action: "appended" });
|
|
824
|
+
} else {
|
|
825
|
+
const dir = path2.dirname(fullPath);
|
|
826
|
+
if (!fs2.existsSync(dir)) {
|
|
827
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
828
|
+
}
|
|
829
|
+
fs2.writeFileSync(fullPath, template, "utf-8");
|
|
830
|
+
results.push({ type, filePath: relativePath, action: "created" });
|
|
831
|
+
}
|
|
832
|
+
if (type === "antigravity") {
|
|
833
|
+
handleAntigravityMcpConfig(root, results);
|
|
834
|
+
} else if (type === "openclaw") {
|
|
835
|
+
handleOpenclawSecondary(root, results);
|
|
836
|
+
} else if (type === "codewhale") {
|
|
837
|
+
handleCodewhaleSecondary(root, results);
|
|
838
|
+
} else if (type === "aider") {
|
|
839
|
+
handleAiderSecondary(root, results);
|
|
840
|
+
} else if (type === "opencode") {
|
|
841
|
+
handleOpencodeSecondary(root, results);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
} catch (err) {
|
|
845
|
+
results.push({
|
|
846
|
+
type,
|
|
847
|
+
filePath: relativePath,
|
|
848
|
+
action: "error",
|
|
849
|
+
error: err instanceof Error ? err.message : String(err)
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
return results;
|
|
854
|
+
}
|
|
855
|
+
function formatInitResults(results) {
|
|
856
|
+
const lines = [
|
|
857
|
+
"\u{1F43B} **Kuma Init - AI Agent Config Generator**",
|
|
858
|
+
""
|
|
859
|
+
];
|
|
860
|
+
for (const r of results) {
|
|
861
|
+
const label = CONFIG_LABELS[r.type];
|
|
862
|
+
switch (r.action) {
|
|
863
|
+
case "created":
|
|
864
|
+
lines.push(" \u2705 " + label);
|
|
865
|
+
lines.push(" \u2192 Created: " + r.filePath);
|
|
866
|
+
break;
|
|
867
|
+
case "appended":
|
|
868
|
+
lines.push(" \u2795 " + label);
|
|
869
|
+
lines.push(" \u2192 Appended to: " + r.filePath);
|
|
870
|
+
break;
|
|
871
|
+
case "skipped":
|
|
872
|
+
lines.push(" \u23ED " + label);
|
|
873
|
+
lines.push(" \u2192 Skipped (already has Kuma): " + r.filePath);
|
|
874
|
+
break;
|
|
875
|
+
case "error":
|
|
876
|
+
lines.push(" \u274C " + label);
|
|
877
|
+
lines.push(" \u2192 Error: " + (r.error ?? "unknown"));
|
|
878
|
+
break;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
const created = results.filter((r) => r.action === "created").length;
|
|
882
|
+
const appended = results.filter((r) => r.action === "appended").length;
|
|
883
|
+
const skipped = results.filter((r) => r.action === "skipped").length;
|
|
884
|
+
const errors = results.filter((r) => r.action === "error").length;
|
|
885
|
+
lines.push(
|
|
886
|
+
"",
|
|
887
|
+
"\u{1F4CA} Summary: " + created + " created, " + appended + " appended, " + skipped + " skipped, " + errors + " errors",
|
|
888
|
+
"",
|
|
889
|
+
"\u{1F4A1} Rules are in `.kuma/init.md` \u2014 single source of truth for all tools.",
|
|
890
|
+
"\u{1F4A1} Call `kuma_init()` at session start to load project context.",
|
|
891
|
+
"\u{1F4A1} Run again to generate additional config files anytime."
|
|
892
|
+
);
|
|
893
|
+
return lines.join("\n");
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
export {
|
|
897
|
+
validateFilePath,
|
|
898
|
+
validateFileExtension,
|
|
899
|
+
getProjectRoot,
|
|
900
|
+
getBackupPath,
|
|
901
|
+
ensureBackupDir,
|
|
902
|
+
ALL_CONFIG_TYPES,
|
|
903
|
+
CONFIG_LABELS,
|
|
904
|
+
generateInitMdContent,
|
|
905
|
+
runInit,
|
|
906
|
+
formatInitResults
|
|
907
|
+
};
|