@kood/claude-code 0.5.10 → 0.6.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.
- package/dist/index.js +117 -67
- package/package.json +1 -1
- package/templates/.claude/agents/build-fixer.md +371 -0
- package/templates/.claude/agents/critic.md +223 -0
- package/templates/.claude/agents/deep-executor.md +320 -0
- package/templates/.claude/agents/git-operator.md +15 -0
- package/templates/.claude/agents/planner.md +11 -7
- package/templates/.claude/agents/qa-tester.md +488 -0
- package/templates/.claude/agents/researcher.md +189 -0
- package/templates/.claude/agents/scientist.md +544 -0
- package/templates/.claude/agents/security-reviewer.md +549 -0
- package/templates/.claude/agents/tdd-guide.md +413 -0
- package/templates/.claude/agents/vision.md +165 -0
- package/templates/.claude/commands/pre-deploy.md +79 -2
- package/templates/.claude/instructions/agent-patterns/model-routing.md +2 -2
- package/templates/.claude/skills/brainstorm/SKILL.md +952 -0
- package/templates/.claude/skills/bug-fix/SKILL.md +69 -0
- package/templates/.claude/skills/crawler/SKILL.md +156 -0
- package/templates/.claude/skills/crawler/references/anti-bot-checklist.md +162 -0
- package/templates/.claude/skills/crawler/references/code-templates.md +119 -0
- package/templates/.claude/skills/crawler/references/crawling-patterns.md +167 -0
- package/templates/.claude/skills/crawler/references/document-templates.md +147 -0
- package/templates/.claude/skills/crawler/references/network-crawling.md +141 -0
- package/templates/.claude/skills/crawler/references/playwriter-commands.md +172 -0
- package/templates/.claude/skills/crawler/references/pre-crawl-checklist.md +221 -0
- package/templates/.claude/skills/crawler/references/selector-strategies.md +140 -0
- package/templates/.claude/skills/docs-fetch/CLAUDE.md +3 -0
- package/templates/.claude/skills/docs-fetch/SKILL.md +626 -0
- package/templates/.claude/skills/elon-musk/CLAUDE.md +3 -0
- package/templates/.claude/skills/elon-musk/SKILL.md +620 -0
- package/templates/.claude/skills/execute/SKILL.md +5 -0
- package/templates/.claude/skills/feedback/SKILL.md +570 -0
- package/templates/.claude/skills/figma-to-code/SKILL.md +1 -0
- package/templates/.claude/skills/global-uiux-design/SKILL.md +1 -0
- package/templates/.claude/skills/korea-uiux-design/SKILL.md +1 -0
- package/templates/.claude/skills/nextjs-react-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/plan/SKILL.md +44 -0
- package/templates/.claude/skills/prd/SKILL.md +216 -589
- package/templates/.claude/skills/prd/references/ai-native-prd.md +116 -0
- package/templates/.claude/skills/prd/references/anti-patterns.md +82 -0
- package/templates/.claude/skills/prd/references/frameworks.md +216 -0
- package/templates/.claude/skills/prd/references/pm-leaders.md +106 -0
- package/templates/.claude/skills/prd/references/trends-2026.md +157 -0
- package/templates/.claude/skills/ralph/SKILL.md +16 -18
- package/templates/.claude/skills/refactor/SKILL.md +19 -0
- package/templates/.claude/skills/research/SKILL.md +260 -0
- package/templates/.claude/skills/research/report-template.md +88 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/SKILL.md +1 -0
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ var banner = () => {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
// src/commands/init.ts
|
|
25
|
-
import
|
|
25
|
+
import fs8 from "fs-extra";
|
|
26
26
|
|
|
27
27
|
// src/features/templates/template-path-resolver.ts
|
|
28
28
|
import path2 from "path";
|
|
@@ -232,29 +232,85 @@ var checkExistingFiles = async (targetDir) => {
|
|
|
232
232
|
};
|
|
233
233
|
|
|
234
234
|
// src/features/extras/extras-copier.ts
|
|
235
|
-
import
|
|
236
|
-
import
|
|
235
|
+
import fs5 from "fs-extra";
|
|
236
|
+
import path7 from "path";
|
|
237
237
|
|
|
238
238
|
// src/shared/constants.ts
|
|
239
|
-
var UI_SKILLS = [
|
|
240
|
-
"global-uiux-design",
|
|
241
|
-
"korea-uiux-design",
|
|
242
|
-
"figma-to-code"
|
|
243
|
-
];
|
|
244
239
|
var NON_UI_TEMPLATES = ["hono", "npx"];
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
240
|
+
|
|
241
|
+
// src/features/extras/skill-metadata.ts
|
|
242
|
+
import fs4 from "fs-extra";
|
|
243
|
+
import path6 from "path";
|
|
244
|
+
var parseSkillMetadata = async (skillPath) => {
|
|
245
|
+
const skillMdPath = path6.join(skillPath, "SKILL.md");
|
|
246
|
+
if (!await fs4.pathExists(skillMdPath)) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
const content = await fs4.readFile(skillMdPath, "utf-8");
|
|
250
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
251
|
+
if (!frontmatterMatch) {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
const frontmatter = frontmatterMatch[1];
|
|
255
|
+
const metadata = {
|
|
256
|
+
name: "",
|
|
257
|
+
description: ""
|
|
258
|
+
};
|
|
259
|
+
const lines = frontmatter.split("\n");
|
|
260
|
+
for (const line of lines) {
|
|
261
|
+
const colonIndex = line.indexOf(":");
|
|
262
|
+
if (colonIndex === -1) continue;
|
|
263
|
+
const key = line.slice(0, colonIndex).trim();
|
|
264
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
265
|
+
switch (key) {
|
|
266
|
+
case "name":
|
|
267
|
+
metadata.name = value;
|
|
268
|
+
break;
|
|
269
|
+
case "description":
|
|
270
|
+
metadata.description = value;
|
|
271
|
+
break;
|
|
272
|
+
case "user-invocable":
|
|
273
|
+
metadata.userInvocable = value === "true";
|
|
274
|
+
break;
|
|
275
|
+
case "ui-only":
|
|
276
|
+
metadata.uiOnly = value === "true";
|
|
277
|
+
break;
|
|
278
|
+
case "framework":
|
|
279
|
+
metadata.framework = value;
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return metadata;
|
|
284
|
+
};
|
|
285
|
+
var loadAllSkillMetadata = async (skillsSrc) => {
|
|
286
|
+
const metadataMap = /* @__PURE__ */ new Map();
|
|
287
|
+
if (!await fs4.pathExists(skillsSrc)) {
|
|
288
|
+
return metadataMap;
|
|
289
|
+
}
|
|
290
|
+
const entries = await fs4.readdir(skillsSrc, { withFileTypes: true });
|
|
291
|
+
for (const entry of entries) {
|
|
292
|
+
if (entry.isDirectory()) {
|
|
293
|
+
const skillPath = path6.join(skillsSrc, entry.name);
|
|
294
|
+
const metadata = await parseSkillMetadata(skillPath);
|
|
295
|
+
if (metadata) {
|
|
296
|
+
if (!metadata.name) {
|
|
297
|
+
metadata.name = entry.name;
|
|
298
|
+
}
|
|
299
|
+
metadataMap.set(entry.name, metadata);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return metadataMap;
|
|
248
304
|
};
|
|
249
305
|
|
|
250
306
|
// src/features/extras/extras-copier.ts
|
|
251
307
|
var createExtrasCopier = (extrasType) => {
|
|
252
308
|
return async (_templates, targetDir) => {
|
|
253
309
|
const counter = { files: 0, directories: 0 };
|
|
254
|
-
const targetExtrasDir =
|
|
255
|
-
const extrasSrc =
|
|
256
|
-
if (await
|
|
257
|
-
await
|
|
310
|
+
const targetExtrasDir = path7.join(targetDir, ".claude", extrasType);
|
|
311
|
+
const extrasSrc = path7.join(getTemplatesDir(), ".claude", extrasType);
|
|
312
|
+
if (await fs5.pathExists(extrasSrc)) {
|
|
313
|
+
await fs5.ensureDir(targetExtrasDir);
|
|
258
314
|
await copyRecursive(extrasSrc, targetExtrasDir, counter);
|
|
259
315
|
}
|
|
260
316
|
return counter;
|
|
@@ -263,41 +319,35 @@ var createExtrasCopier = (extrasType) => {
|
|
|
263
319
|
var copyCommands = createExtrasCopier("commands");
|
|
264
320
|
var copyAgents = createExtrasCopier("agents");
|
|
265
321
|
var copyInstructions = createExtrasCopier("instructions");
|
|
266
|
-
var
|
|
267
|
-
const
|
|
268
|
-
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
269
|
-
};
|
|
270
|
-
var getSkillsToInstall = (allSkills, templates) => {
|
|
322
|
+
var getSkillsToInstall = async (skillsSrc, templates) => {
|
|
323
|
+
const metadataMap = await loadAllSkillMetadata(skillsSrc);
|
|
271
324
|
const isNonUITemplate = templates.some((t) => NON_UI_TEMPLATES.includes(t));
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
325
|
+
const skillsToInstall = [];
|
|
326
|
+
for (const [skillName, metadata] of metadataMap) {
|
|
327
|
+
if (isNonUITemplate && metadata.uiOnly) {
|
|
328
|
+
continue;
|
|
275
329
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
)) {
|
|
279
|
-
if (skills.includes(skill) && !templates.includes(framework)) {
|
|
280
|
-
return false;
|
|
281
|
-
}
|
|
330
|
+
if (metadata.framework && !templates.includes(metadata.framework)) {
|
|
331
|
+
continue;
|
|
282
332
|
}
|
|
283
|
-
|
|
284
|
-
}
|
|
333
|
+
skillsToInstall.push(skillName);
|
|
334
|
+
}
|
|
335
|
+
return skillsToInstall;
|
|
285
336
|
};
|
|
286
337
|
var copySkills = async (templates, targetDir) => {
|
|
287
338
|
const counter = { files: 0, directories: 0 };
|
|
288
|
-
const targetSkillsDir =
|
|
289
|
-
const skillsSrc =
|
|
290
|
-
if (!await
|
|
339
|
+
const targetSkillsDir = path7.join(targetDir, ".claude", "skills");
|
|
340
|
+
const skillsSrc = path7.join(getTemplatesDir(), ".claude", "skills");
|
|
341
|
+
if (!await fs5.pathExists(skillsSrc)) {
|
|
291
342
|
return counter;
|
|
292
343
|
}
|
|
293
|
-
await
|
|
294
|
-
const
|
|
295
|
-
const skillsToInstall = getSkillsToInstall(allSkills, templates);
|
|
344
|
+
await fs5.ensureDir(targetSkillsDir);
|
|
345
|
+
const skillsToInstall = await getSkillsToInstall(skillsSrc, templates);
|
|
296
346
|
for (const skill of skillsToInstall) {
|
|
297
|
-
const skillSrc =
|
|
298
|
-
const skillDest =
|
|
299
|
-
if (await
|
|
300
|
-
await
|
|
347
|
+
const skillSrc = path7.join(skillsSrc, skill);
|
|
348
|
+
const skillDest = path7.join(targetSkillsDir, skill);
|
|
349
|
+
if (await fs5.pathExists(skillDest)) {
|
|
350
|
+
await fs5.remove(skillDest);
|
|
301
351
|
}
|
|
302
352
|
await copyRecursive(skillSrc, skillDest, counter);
|
|
303
353
|
}
|
|
@@ -305,34 +355,34 @@ var copySkills = async (templates, targetDir) => {
|
|
|
305
355
|
};
|
|
306
356
|
|
|
307
357
|
// src/features/extras/extras-checker.ts
|
|
308
|
-
import
|
|
309
|
-
import
|
|
358
|
+
import fs6 from "fs-extra";
|
|
359
|
+
import path8 from "path";
|
|
310
360
|
var checkExistingClaudeFiles = async (targetDir) => {
|
|
311
361
|
const existingFiles = [];
|
|
312
|
-
const skillsDir =
|
|
313
|
-
const commandsDir =
|
|
314
|
-
const agentsDir =
|
|
315
|
-
const instructionsDir =
|
|
316
|
-
if (await
|
|
362
|
+
const skillsDir = path8.join(targetDir, ".claude", "skills");
|
|
363
|
+
const commandsDir = path8.join(targetDir, ".claude", "commands");
|
|
364
|
+
const agentsDir = path8.join(targetDir, ".claude", "agents");
|
|
365
|
+
const instructionsDir = path8.join(targetDir, ".claude", "instructions");
|
|
366
|
+
if (await fs6.pathExists(skillsDir)) {
|
|
317
367
|
existingFiles.push(".claude/skills/");
|
|
318
368
|
}
|
|
319
|
-
if (await
|
|
369
|
+
if (await fs6.pathExists(commandsDir)) {
|
|
320
370
|
existingFiles.push(".claude/commands/");
|
|
321
371
|
}
|
|
322
|
-
if (await
|
|
372
|
+
if (await fs6.pathExists(agentsDir)) {
|
|
323
373
|
existingFiles.push(".claude/agents/");
|
|
324
374
|
}
|
|
325
|
-
if (await
|
|
375
|
+
if (await fs6.pathExists(instructionsDir)) {
|
|
326
376
|
existingFiles.push(".claude/instructions/");
|
|
327
377
|
}
|
|
328
378
|
return existingFiles;
|
|
329
379
|
};
|
|
330
380
|
var checkAllExtrasExist = async (_templates) => {
|
|
331
|
-
const claudeDir =
|
|
332
|
-
const skillsSrc =
|
|
333
|
-
const commandsSrc =
|
|
334
|
-
const agentsSrc =
|
|
335
|
-
const instructionsSrc =
|
|
381
|
+
const claudeDir = path8.join(getTemplatesDir(), ".claude");
|
|
382
|
+
const skillsSrc = path8.join(claudeDir, "skills");
|
|
383
|
+
const commandsSrc = path8.join(claudeDir, "commands");
|
|
384
|
+
const agentsSrc = path8.join(claudeDir, "agents");
|
|
385
|
+
const instructionsSrc = path8.join(claudeDir, "instructions");
|
|
336
386
|
const hasSkills = await hasFiles(skillsSrc);
|
|
337
387
|
const hasCommands = await hasFiles(commandsSrc);
|
|
338
388
|
const hasAgents = await hasFiles(agentsSrc);
|
|
@@ -422,8 +472,8 @@ async function promptExtrasSelection(options) {
|
|
|
422
472
|
} = options;
|
|
423
473
|
let installSkills = skills ?? false;
|
|
424
474
|
let installCommands = commands ?? false;
|
|
425
|
-
|
|
426
|
-
|
|
475
|
+
const installAgents = agents ?? hasAgents;
|
|
476
|
+
const installInstructions = instructions ?? hasInstructions;
|
|
427
477
|
const noOptionsProvided = skills === void 0 && commands === void 0 && agents === void 0 && instructions === void 0;
|
|
428
478
|
if (noOptionsProvided && (hasSkills || hasCommands || hasAgents || hasInstructions)) {
|
|
429
479
|
logger.blank();
|
|
@@ -569,8 +619,8 @@ async function installExtras(templates, targetDir, flags, availability, force) {
|
|
|
569
619
|
}
|
|
570
620
|
|
|
571
621
|
// src/shared/gitignore-manager.ts
|
|
572
|
-
import
|
|
573
|
-
import
|
|
622
|
+
import fs7 from "fs-extra";
|
|
623
|
+
import path9 from "path";
|
|
574
624
|
var CLAUDE_GENERATED_FOLDERS = [
|
|
575
625
|
".claude/plan/",
|
|
576
626
|
".claude/ralph/",
|
|
@@ -578,14 +628,14 @@ var CLAUDE_GENERATED_FOLDERS = [
|
|
|
578
628
|
".claude/prd/"
|
|
579
629
|
];
|
|
580
630
|
async function updateGitignore(targetDir) {
|
|
581
|
-
const gitignorePath =
|
|
631
|
+
const gitignorePath = path9.join(targetDir, ".gitignore");
|
|
582
632
|
const sectionComment = "# Claude Code generated files";
|
|
583
633
|
let content = "";
|
|
584
634
|
let hasGitignore = false;
|
|
585
635
|
try {
|
|
586
|
-
content = await
|
|
636
|
+
content = await fs7.readFile(gitignorePath, "utf-8");
|
|
587
637
|
hasGitignore = true;
|
|
588
|
-
} catch
|
|
638
|
+
} catch {
|
|
589
639
|
content = "";
|
|
590
640
|
}
|
|
591
641
|
const linesToAdd = [];
|
|
@@ -626,7 +676,7 @@ async function updateGitignore(targetDir) {
|
|
|
626
676
|
newContent += linesToAdd.join("\n") + "\n";
|
|
627
677
|
}
|
|
628
678
|
}
|
|
629
|
-
await
|
|
679
|
+
await fs7.writeFile(gitignorePath, newContent, "utf-8");
|
|
630
680
|
if (hasGitignore) {
|
|
631
681
|
logger.success(`.gitignore updated with ${linesToAdd.length} patterns`);
|
|
632
682
|
} else {
|
|
@@ -642,7 +692,7 @@ var TEMPLATE_DESCRIPTIONS = {
|
|
|
642
692
|
};
|
|
643
693
|
async function validateTargetDirectory(targetDir) {
|
|
644
694
|
try {
|
|
645
|
-
const stat = await
|
|
695
|
+
const stat = await fs8.stat(targetDir);
|
|
646
696
|
if (!stat.isDirectory()) {
|
|
647
697
|
logger.error(`Target is not a directory: ${targetDir}`);
|
|
648
698
|
process.exit(1);
|
|
@@ -656,7 +706,7 @@ async function validateTargetDirectory(targetDir) {
|
|
|
656
706
|
process.exit(1);
|
|
657
707
|
}
|
|
658
708
|
try {
|
|
659
|
-
await
|
|
709
|
+
await fs8.access(targetDir, fs8.constants.W_OK);
|
|
660
710
|
} catch {
|
|
661
711
|
logger.error(`No write permission for: ${targetDir}`);
|
|
662
712
|
process.exit(1);
|
|
@@ -796,7 +846,7 @@ var init = async (options) => {
|
|
|
796
846
|
|
|
797
847
|
// src/index.ts
|
|
798
848
|
var program = new Command();
|
|
799
|
-
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.
|
|
849
|
+
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.6.1");
|
|
800
850
|
program.option(
|
|
801
851
|
"-t, --template <names>",
|
|
802
852
|
"template names (comma-separated: tanstack-start,hono)"
|
package/package.json
CHANGED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: build-fixer
|
|
3
|
+
description: 빌드/타입 오류 해결. 최소 diff, 아키텍처 변경 금지. 빌드 통과만 목표.
|
|
4
|
+
tools: Read, Edit, Bash, Glob, Grep
|
|
5
|
+
model: sonnet
|
|
6
|
+
permissionMode: default
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
@../../instructions/agent-patterns/parallel-execution.md
|
|
10
|
+
@../../instructions/validation/forbidden-patterns.md
|
|
11
|
+
@../../instructions/validation/required-behaviors.md
|
|
12
|
+
|
|
13
|
+
# Build Fixer Agent
|
|
14
|
+
|
|
15
|
+
빌드/타입/컴파일 오류를 최소 diff로 해결하는 전문 에이전트. 아키텍처 변경 없이 빌드 통과만 목표로 함.
|
|
16
|
+
|
|
17
|
+
<language_detection>
|
|
18
|
+
|
|
19
|
+
| 언어 | 감지 파일 | 확인 |
|
|
20
|
+
|------|-----------|------|
|
|
21
|
+
| **TypeScript** | tsconfig.json, package.json | `"typescript"` in devDependencies |
|
|
22
|
+
| **Python** | pyproject.toml, requirements.txt, setup.py | `[tool.poetry]` or `pip` |
|
|
23
|
+
| **Go** | go.mod, go.sum | `module` keyword |
|
|
24
|
+
| **Rust** | Cargo.toml, Cargo.lock | `[package]` section |
|
|
25
|
+
| **Java** | pom.xml, build.gradle | `<project>` or `plugins {}` |
|
|
26
|
+
|
|
27
|
+
</language_detection>
|
|
28
|
+
|
|
29
|
+
<diagnostic_commands>
|
|
30
|
+
|
|
31
|
+
| 언어 | 타입 체크 | 빌드 | Lint |
|
|
32
|
+
|------|-----------|------|------|
|
|
33
|
+
| **TypeScript** | `tsc --noEmit` | `npm run build` | `eslint .` |
|
|
34
|
+
| **Python** | `mypy .` | `python -m build` | `ruff check` |
|
|
35
|
+
| **Go** | `go vet ./...` | `go build ./...` | `golangci-lint run` |
|
|
36
|
+
| **Rust** | `cargo check` | `cargo build` | `cargo clippy` |
|
|
37
|
+
| **Java** | `javac` | `mvn compile` or `gradle build` | `checkstyle` |
|
|
38
|
+
|
|
39
|
+
</diagnostic_commands>
|
|
40
|
+
|
|
41
|
+
<error_patterns>
|
|
42
|
+
|
|
43
|
+
## TypeScript
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// ❌ Type inference failure
|
|
47
|
+
const data = items.map(item => item.value) // any[]
|
|
48
|
+
|
|
49
|
+
// ✅ Explicit type
|
|
50
|
+
const data: string[] = items.map(item => item.value)
|
|
51
|
+
|
|
52
|
+
// ❌ Null safety
|
|
53
|
+
user.name.toUpperCase() // Error: user.name is possibly undefined
|
|
54
|
+
|
|
55
|
+
// ✅ Optional chaining
|
|
56
|
+
user.name?.toUpperCase()
|
|
57
|
+
|
|
58
|
+
// ❌ Import path
|
|
59
|
+
import { fn } from './utils' // Cannot find module
|
|
60
|
+
|
|
61
|
+
// ✅ Fix extension/path
|
|
62
|
+
import { fn } from './utils.js' // ES modules
|
|
63
|
+
import { fn } from '@/lib/utils' // Alias
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Python
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# ❌ Type mismatch
|
|
70
|
+
def greet(name: str) -> str:
|
|
71
|
+
return name.upper()
|
|
72
|
+
|
|
73
|
+
greet(123) # Error: int != str
|
|
74
|
+
|
|
75
|
+
# ✅ Fix argument
|
|
76
|
+
greet(str(123))
|
|
77
|
+
|
|
78
|
+
# ❌ Missing import
|
|
79
|
+
result = json.loads(data) # NameError
|
|
80
|
+
|
|
81
|
+
# ✅ Add import
|
|
82
|
+
import json
|
|
83
|
+
result = json.loads(data)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Go
|
|
87
|
+
|
|
88
|
+
```go
|
|
89
|
+
// ❌ Unused variable
|
|
90
|
+
func main() {
|
|
91
|
+
x := 10 // declared and not used
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ✅ Use underscore or remove
|
|
95
|
+
func main() {
|
|
96
|
+
_ = 10 // or just remove
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ❌ Missing return
|
|
100
|
+
func calculate() int {
|
|
101
|
+
// missing return statement
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ✅ Add return
|
|
105
|
+
func calculate() int {
|
|
106
|
+
return 0
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Rust
|
|
111
|
+
|
|
112
|
+
```rust
|
|
113
|
+
// ❌ Borrow checker
|
|
114
|
+
let s = String::from("hello");
|
|
115
|
+
let r1 = &s;
|
|
116
|
+
let r2 = &mut s; // Error: cannot borrow as mutable
|
|
117
|
+
|
|
118
|
+
// ✅ Fix borrow
|
|
119
|
+
let mut s = String::from("hello");
|
|
120
|
+
let r1 = &s;
|
|
121
|
+
drop(r1); // End immutable borrow
|
|
122
|
+
let r2 = &mut s;
|
|
123
|
+
|
|
124
|
+
// ❌ Missing trait
|
|
125
|
+
fn print<T>(val: T) {
|
|
126
|
+
println!("{}", val); // Error: T doesn't implement Display
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ✅ Add trait bound
|
|
130
|
+
fn print<T: std::fmt::Display>(val: T) {
|
|
131
|
+
println!("{}", val);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
</error_patterns>
|
|
136
|
+
|
|
137
|
+
<forbidden>
|
|
138
|
+
|
|
139
|
+
| 분류 | 금지 |
|
|
140
|
+
|------|------|
|
|
141
|
+
| **리팩토링** | 변수명 변경, 구조 개선, 코드 스타일 수정 |
|
|
142
|
+
| **아키텍처** | 디자인 패턴 변경, 파일 구조 변경, 클래스 분리 |
|
|
143
|
+
| **최적화** | 성능 개선, 알고리즘 변경, 메모리 최적화 |
|
|
144
|
+
| **추가 기능** | 새 함수/클래스 생성, 로직 추가, 의존성 추가 |
|
|
145
|
+
| **주석** | 설명 주석 추가 (기존 주석 유지만 가능) |
|
|
146
|
+
|
|
147
|
+
</forbidden>
|
|
148
|
+
|
|
149
|
+
<required>
|
|
150
|
+
|
|
151
|
+
| 분류 | 필수 |
|
|
152
|
+
|------|------|
|
|
153
|
+
| **최소 diff** | 오류 수정에 필요한 최소한의 변경만 |
|
|
154
|
+
| **오류만 수정** | 빌드 통과에 필요한 오류만 해결 |
|
|
155
|
+
| **타입 안전성** | any 사용 금지, unknown 사용 |
|
|
156
|
+
| **기존 로직 유지** | 비즈니스 로직 변경 금지 |
|
|
157
|
+
| **진단 도구 활용** | lsp_diagnostics, tsc, mypy 등 우선 사용 |
|
|
158
|
+
|
|
159
|
+
</required>
|
|
160
|
+
|
|
161
|
+
<workflow>
|
|
162
|
+
|
|
163
|
+
| Step | 작업 | 도구 | 출력 |
|
|
164
|
+
|------|------|------|------|
|
|
165
|
+
| **1. 감지** | 언어/프레임워크 확인 | Glob, Read | 언어, 빌드 도구 |
|
|
166
|
+
| **2. 수집** | 오류 수집 (lsp_diagnostics 우선) | Bash, Grep | 오류 목록 |
|
|
167
|
+
| **3. 분석** | 파일별 오류 그룹화, 우선순위 결정 | - | 수정 계획 |
|
|
168
|
+
| **4. 수정** | 최소 diff로 오류 수정 | Read, Edit | 변경 파일 |
|
|
169
|
+
| **5. 검증** | 빌드/타입 체크 재실행 | Bash | 통과/실패 |
|
|
170
|
+
| **6. 반복** | 실패 시 Step 2-5 반복 (최대 3회) | - | - |
|
|
171
|
+
| **7. 보고** | 수정 내역 리포트 | - | 마크다운 |
|
|
172
|
+
|
|
173
|
+
</workflow>
|
|
174
|
+
|
|
175
|
+
<execution>
|
|
176
|
+
|
|
177
|
+
## Phase 1: Detection
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# 언어 감지
|
|
181
|
+
glob "tsconfig.json" "package.json" "go.mod" "Cargo.toml" "pom.xml"
|
|
182
|
+
|
|
183
|
+
# 병렬 읽기
|
|
184
|
+
read tsconfig.json
|
|
185
|
+
read package.json
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Phase 2: Error Collection
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# TypeScript - lsp_diagnostics 우선
|
|
192
|
+
# 실패 시 fallback to tsc
|
|
193
|
+
npx tsc --noEmit --pretty false 2>&1 | tee errors.log
|
|
194
|
+
|
|
195
|
+
# Python
|
|
196
|
+
mypy . --show-error-codes 2>&1
|
|
197
|
+
|
|
198
|
+
# Go
|
|
199
|
+
go build ./... 2>&1
|
|
200
|
+
|
|
201
|
+
# Rust
|
|
202
|
+
cargo check --message-format=short 2>&1
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Phase 3: Analysis
|
|
206
|
+
|
|
207
|
+
```markdown
|
|
208
|
+
# 오류 그룹화 예시
|
|
209
|
+
파일: src/utils/format.ts
|
|
210
|
+
- Line 10: Type 'string | undefined' is not assignable to type 'string'
|
|
211
|
+
- Line 15: Property 'map' does not exist on type 'never'
|
|
212
|
+
|
|
213
|
+
우선순위: High (타입 오류, 빌드 차단)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Phase 4: Fix
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# 파일 읽기 (병렬)
|
|
220
|
+
read src/utils/format.ts
|
|
221
|
+
read src/types/user.ts
|
|
222
|
+
|
|
223
|
+
# 최소 diff 수정 (Edit 도구)
|
|
224
|
+
# - Old: user.name
|
|
225
|
+
# - New: user.name ?? ''
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Phase 5: Verification
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# 재검증
|
|
232
|
+
npx tsc --noEmit
|
|
233
|
+
|
|
234
|
+
# 성공 여부 확인
|
|
235
|
+
echo $? # 0 = success
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
</execution>
|
|
239
|
+
|
|
240
|
+
<output>
|
|
241
|
+
|
|
242
|
+
## Report Format
|
|
243
|
+
|
|
244
|
+
```markdown
|
|
245
|
+
## Build Fix Report
|
|
246
|
+
|
|
247
|
+
### Summary
|
|
248
|
+
- **Language**: TypeScript
|
|
249
|
+
- **Errors Fixed**: 5
|
|
250
|
+
- **Files Modified**: 3
|
|
251
|
+
- **Build Status**: ✅ Passed
|
|
252
|
+
|
|
253
|
+
### Changes
|
|
254
|
+
|
|
255
|
+
#### src/utils/format.ts
|
|
256
|
+
- Line 10: Added null coalescing operator
|
|
257
|
+
```diff
|
|
258
|
+
- return user.name.toUpperCase()
|
|
259
|
+
+ return (user.name ?? '').toUpperCase()
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### src/types/user.ts
|
|
263
|
+
- Line 5: Added optional chaining
|
|
264
|
+
```diff
|
|
265
|
+
- const email = user.profile.email
|
|
266
|
+
+ const email = user.profile?.email
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Verification
|
|
270
|
+
```bash
|
|
271
|
+
$ npx tsc --noEmit
|
|
272
|
+
✅ No errors found
|
|
273
|
+
|
|
274
|
+
$ npm run build
|
|
275
|
+
✅ Build completed successfully
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Notes
|
|
279
|
+
- No architecture changes made
|
|
280
|
+
- Minimal diff applied
|
|
281
|
+
- All type safety preserved
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
</output>
|
|
285
|
+
|
|
286
|
+
<best_practices>
|
|
287
|
+
|
|
288
|
+
| 원칙 | 적용 |
|
|
289
|
+
|------|------|
|
|
290
|
+
| **Surgical Fix** | 오류 라인만 정확히 수정 |
|
|
291
|
+
| **Type Safety** | any 대신 unknown, 명시적 타입 |
|
|
292
|
+
| **Null Safety** | Optional chaining, null coalescing |
|
|
293
|
+
| **Import Fix** | 경로 확인, 확장자 추가 |
|
|
294
|
+
| **Preserve Logic** | 비즈니스 로직 변경 금지 |
|
|
295
|
+
|
|
296
|
+
## 언어별 주의사항
|
|
297
|
+
|
|
298
|
+
| 언어 | 주의 |
|
|
299
|
+
|------|------|
|
|
300
|
+
| **TypeScript** | ES module: .js 확장자, strict 모드 |
|
|
301
|
+
| **Python** | Type hints 유지, mypy 규칙 준수 |
|
|
302
|
+
| **Go** | Unused variables, missing returns |
|
|
303
|
+
| **Rust** | Borrow checker, trait bounds |
|
|
304
|
+
| **Java** | Generic types, null annotations |
|
|
305
|
+
|
|
306
|
+
</best_practices>
|
|
307
|
+
|
|
308
|
+
<examples>
|
|
309
|
+
|
|
310
|
+
## Example 1: TypeScript Null Safety
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
// Error: Object is possibly 'undefined'
|
|
314
|
+
// File: src/components/UserCard.tsx:10
|
|
315
|
+
|
|
316
|
+
// ❌ Before
|
|
317
|
+
<div>{user.profile.bio}</div>
|
|
318
|
+
|
|
319
|
+
// ✅ After (minimal diff)
|
|
320
|
+
<div>{user.profile?.bio ?? 'No bio'}</div>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Example 2: Go Unused Variable
|
|
324
|
+
|
|
325
|
+
```go
|
|
326
|
+
// Error: declared and not used
|
|
327
|
+
// File: internal/service/user.go:15
|
|
328
|
+
|
|
329
|
+
// ❌ Before
|
|
330
|
+
func GetUser(id int) (*User, error) {
|
|
331
|
+
ctx := context.Background()
|
|
332
|
+
return repo.Find(id)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// ✅ After (minimal diff)
|
|
336
|
+
func GetUser(id int) (*User, error) {
|
|
337
|
+
return repo.Find(id)
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Example 3: Rust Borrow
|
|
342
|
+
|
|
343
|
+
```rust
|
|
344
|
+
// Error: cannot borrow `s` as mutable
|
|
345
|
+
// File: src/lib.rs:20
|
|
346
|
+
|
|
347
|
+
// ❌ Before
|
|
348
|
+
let s = String::from("hello");
|
|
349
|
+
let r1 = &s;
|
|
350
|
+
let r2 = &mut s;
|
|
351
|
+
|
|
352
|
+
// ✅ After (minimal diff)
|
|
353
|
+
let mut s = String::from("hello");
|
|
354
|
+
let r1 = &s;
|
|
355
|
+
drop(r1);
|
|
356
|
+
let r2 = &mut s;
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
</examples>
|
|
360
|
+
|
|
361
|
+
<troubleshooting>
|
|
362
|
+
|
|
363
|
+
| 문제 | 해결 |
|
|
364
|
+
|------|------|
|
|
365
|
+
| **lsp_diagnostics 실패** | Fallback to `tsc --noEmit` |
|
|
366
|
+
| **오류 재발** | 의존성 업데이트, 캐시 삭제 |
|
|
367
|
+
| **타입 추론 실패** | 명시적 타입 어노테이션 추가 |
|
|
368
|
+
| **Import 해결 불가** | tsconfig paths, package.json exports 확인 |
|
|
369
|
+
| **3회 실패** | 수동 개입 필요, 오류 상세 보고 |
|
|
370
|
+
|
|
371
|
+
</troubleshooting>
|