@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.
Files changed (48) hide show
  1. package/dist/index.js +117 -67
  2. package/package.json +1 -1
  3. package/templates/.claude/agents/build-fixer.md +371 -0
  4. package/templates/.claude/agents/critic.md +223 -0
  5. package/templates/.claude/agents/deep-executor.md +320 -0
  6. package/templates/.claude/agents/git-operator.md +15 -0
  7. package/templates/.claude/agents/planner.md +11 -7
  8. package/templates/.claude/agents/qa-tester.md +488 -0
  9. package/templates/.claude/agents/researcher.md +189 -0
  10. package/templates/.claude/agents/scientist.md +544 -0
  11. package/templates/.claude/agents/security-reviewer.md +549 -0
  12. package/templates/.claude/agents/tdd-guide.md +413 -0
  13. package/templates/.claude/agents/vision.md +165 -0
  14. package/templates/.claude/commands/pre-deploy.md +79 -2
  15. package/templates/.claude/instructions/agent-patterns/model-routing.md +2 -2
  16. package/templates/.claude/skills/brainstorm/SKILL.md +952 -0
  17. package/templates/.claude/skills/bug-fix/SKILL.md +69 -0
  18. package/templates/.claude/skills/crawler/SKILL.md +156 -0
  19. package/templates/.claude/skills/crawler/references/anti-bot-checklist.md +162 -0
  20. package/templates/.claude/skills/crawler/references/code-templates.md +119 -0
  21. package/templates/.claude/skills/crawler/references/crawling-patterns.md +167 -0
  22. package/templates/.claude/skills/crawler/references/document-templates.md +147 -0
  23. package/templates/.claude/skills/crawler/references/network-crawling.md +141 -0
  24. package/templates/.claude/skills/crawler/references/playwriter-commands.md +172 -0
  25. package/templates/.claude/skills/crawler/references/pre-crawl-checklist.md +221 -0
  26. package/templates/.claude/skills/crawler/references/selector-strategies.md +140 -0
  27. package/templates/.claude/skills/docs-fetch/CLAUDE.md +3 -0
  28. package/templates/.claude/skills/docs-fetch/SKILL.md +626 -0
  29. package/templates/.claude/skills/elon-musk/CLAUDE.md +3 -0
  30. package/templates/.claude/skills/elon-musk/SKILL.md +620 -0
  31. package/templates/.claude/skills/execute/SKILL.md +5 -0
  32. package/templates/.claude/skills/feedback/SKILL.md +570 -0
  33. package/templates/.claude/skills/figma-to-code/SKILL.md +1 -0
  34. package/templates/.claude/skills/global-uiux-design/SKILL.md +1 -0
  35. package/templates/.claude/skills/korea-uiux-design/SKILL.md +1 -0
  36. package/templates/.claude/skills/nextjs-react-best-practices/SKILL.md +1 -0
  37. package/templates/.claude/skills/plan/SKILL.md +44 -0
  38. package/templates/.claude/skills/prd/SKILL.md +216 -589
  39. package/templates/.claude/skills/prd/references/ai-native-prd.md +116 -0
  40. package/templates/.claude/skills/prd/references/anti-patterns.md +82 -0
  41. package/templates/.claude/skills/prd/references/frameworks.md +216 -0
  42. package/templates/.claude/skills/prd/references/pm-leaders.md +106 -0
  43. package/templates/.claude/skills/prd/references/trends-2026.md +157 -0
  44. package/templates/.claude/skills/ralph/SKILL.md +16 -18
  45. package/templates/.claude/skills/refactor/SKILL.md +19 -0
  46. package/templates/.claude/skills/research/SKILL.md +260 -0
  47. package/templates/.claude/skills/research/report-template.md +88 -0
  48. 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 fs7 from "fs-extra";
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 fs4 from "fs-extra";
236
- import path6 from "path";
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
- var FRAMEWORK_SPECIFIC_SKILLS = {
246
- nextjs: ["nextjs-react-best-practices"],
247
- "tanstack-start": ["tanstack-start-react-best-practices"]
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 = path6.join(targetDir, ".claude", extrasType);
255
- const extrasSrc = path6.join(getTemplatesDir(), ".claude", extrasType);
256
- if (await fs4.pathExists(extrasSrc)) {
257
- await fs4.ensureDir(targetExtrasDir);
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 getAllSkills = async (skillsSrc) => {
267
- const entries = await fs4.readdir(skillsSrc, { withFileTypes: true });
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
- return allSkills.filter((skill) => {
273
- if (isNonUITemplate && UI_SKILLS.includes(skill)) {
274
- return false;
325
+ const skillsToInstall = [];
326
+ for (const [skillName, metadata] of metadataMap) {
327
+ if (isNonUITemplate && metadata.uiOnly) {
328
+ continue;
275
329
  }
276
- for (const [framework, skills] of Object.entries(
277
- FRAMEWORK_SPECIFIC_SKILLS
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
- return true;
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 = path6.join(targetDir, ".claude", "skills");
289
- const skillsSrc = path6.join(getTemplatesDir(), ".claude", "skills");
290
- if (!await fs4.pathExists(skillsSrc)) {
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 fs4.ensureDir(targetSkillsDir);
294
- const allSkills = await getAllSkills(skillsSrc);
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 = path6.join(skillsSrc, skill);
298
- const skillDest = path6.join(targetSkillsDir, skill);
299
- if (await fs4.pathExists(skillDest)) {
300
- await fs4.remove(skillDest);
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 fs5 from "fs-extra";
309
- import path7 from "path";
358
+ import fs6 from "fs-extra";
359
+ import path8 from "path";
310
360
  var checkExistingClaudeFiles = async (targetDir) => {
311
361
  const existingFiles = [];
312
- const skillsDir = path7.join(targetDir, ".claude", "skills");
313
- const commandsDir = path7.join(targetDir, ".claude", "commands");
314
- const agentsDir = path7.join(targetDir, ".claude", "agents");
315
- const instructionsDir = path7.join(targetDir, ".claude", "instructions");
316
- if (await fs5.pathExists(skillsDir)) {
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 fs5.pathExists(commandsDir)) {
369
+ if (await fs6.pathExists(commandsDir)) {
320
370
  existingFiles.push(".claude/commands/");
321
371
  }
322
- if (await fs5.pathExists(agentsDir)) {
372
+ if (await fs6.pathExists(agentsDir)) {
323
373
  existingFiles.push(".claude/agents/");
324
374
  }
325
- if (await fs5.pathExists(instructionsDir)) {
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 = path7.join(getTemplatesDir(), ".claude");
332
- const skillsSrc = path7.join(claudeDir, "skills");
333
- const commandsSrc = path7.join(claudeDir, "commands");
334
- const agentsSrc = path7.join(claudeDir, "agents");
335
- const instructionsSrc = path7.join(claudeDir, "instructions");
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
- let installAgents = agents ?? hasAgents;
426
- let installInstructions = instructions ?? hasInstructions;
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 fs6 from "fs-extra";
573
- import path8 from "path";
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 = path8.join(targetDir, ".gitignore");
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 fs6.readFile(gitignorePath, "utf-8");
636
+ content = await fs7.readFile(gitignorePath, "utf-8");
587
637
  hasGitignore = true;
588
- } catch (error) {
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 fs6.writeFile(gitignorePath, newContent, "utf-8");
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 fs7.stat(targetDir);
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 fs7.access(targetDir, fs7.constants.W_OK);
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.5.10");
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kood/claude-code",
3
- "version": "0.5.10",
3
+ "version": "0.6.1",
4
4
  "description": "Claude Code documentation installer for projects",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.js",
@@ -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>