impact-analyzer
1
总安装量
1
周安装量
#43675
全站排名
安装命令
npx skills add https://github.com/monicajeon28/gmcruise --skill impact-analyzer
Agent 安装分布
amp
1
openclaw
1
opencode
1
kimi-cli
1
codex
1
github-copilot
1
Skill 文档
Impact Analyzer Skill v1.0
ë³ê²½ ìí¥ë ë¶ì기 – ì½ë ìì ì ìí¥ ë²ì를 ì íí íì íì¬ ìì í ë³ê²½ ë³´ì¥
íµì¬ 컨ì
Problem:
Without_Analysis: "í¨ì ìì â ììì¹ ëª»í ê³³ìì ìë¬ â 롤백"
Reality: "í ì¤ ë³ê²½ì´ 100ê° íì¼ì ìí¥ì ì¤ ì ìì"
Solution:
Impact_Analysis:
- "ë³ê²½ ëìì ììì¡´ì± í¸ë¦¬ ì¶ì "
- "ì§ì /ê°ì ìí¥ ë²ì ê³ì°"
- "ìíë ì ì ì°ì¶"
- "í
ì¤í¸ ë²ì ìë ì ì"
Key_Metrics:
Direct_Impact: "ì§ì í¸ì¶íë í¨ìë¤"
Indirect_Impact: "ì°ìì ì¼ë¡ ìí¥ë°ë ë²ì"
Risk_Score: "ë³ê²½ ìíë (0-100)"
Test_Coverage: "ìí¥ ë²ì ëë¹ í
ì¤í¸ 커ë²ë¦¬ì§"
ìë ë°ë ì¡°ê±´
Auto_Trigger_Conditions:
Before_Edit:
- "Edit ë구 ì¬ì© ì ìë ë¶ì ì ì"
- "ê³µì ì½ë(shared, utils) ìì ì"
- "íµì¬ 모ë(auth, api) ìì ì"
Keywords_KO:
- "ìí¥ë ë¶ì, ìí¥ ë²ì"
- "ì´ê±° ë°ê¾¸ë©´ ì´ë ìí¥?"
- "ë³ê²½ ìí¥, ìì ìí¥"
- "리í©í ë§ ìí¥, 리í©í ë§ ë²ì"
- "ìíë, ì¼ë§ë ìí"
- "í
ì¤í¸ ë²ì, í
ì¤í¸í´ì¼ í ê³³"
Keywords_EN:
- "impact analysis, impact scope"
- "what will this affect"
- "change impact, modification impact"
- "refactoring scope, refactoring impact"
- "risk assessment, risk score"
- "test scope, what to test"
High_Risk_Patterns:
- "export ë³ê²½/ìì "
- "í¨ì ìê·¸ëì² ë³ê²½"
- "íì
ì ì ë³ê²½"
- "API ìëí¬ì¸í¸ ë³ê²½"
- "shared 모ë ë³ê²½"
ìí¥ë ë¶ì ìê³ ë¦¬ì¦
interface ImpactAnalysisRequest {
target: string; // ë³ê²½ ëì ë
¸ë ID
changeType: ChangeType; // ë³ê²½ ì í
depth?: number; // ë¶ì ê¹ì´ (기본 3)
}
type ChangeType =
| 'signature' // ìê·¸ëì² ë³ê²½ (íë¼ë¯¸í°, ë°ííì
)
| 'implementation' // 구í ë³ê²½ (ë¡ì§)
| 'rename' // ì´ë¦ ë³ê²½
| 'delete' // ìì
| 'move' // ì´ë
| 'type' // íì
ë³ê²½
;
interface ImpactResult {
target: GraphNode; // ë³ê²½ ëì
changeType: ChangeType;
directImpact: ImpactNode[]; // ì§ì ìí¥
indirectImpact: ImpactNode[]; // ê°ì ìí¥
totalAffected: number; // ì´ ìí¥ë°ë ë
¸ë ì
riskScore: number; // ìíë (0-100)
riskLevel: 'low' | 'medium' | 'high' | 'critical';
testSuggestions: TestSuggestion[]; // í
ì¤í¸ ì ì
warnings: string[]; // ê²½ê³ ë©ìì§
}
interface ImpactNode {
node: GraphNode;
depth: number; // ìí¥ ê¹ì´ (1=ì§ì , 2+=ê°ì )
impactType: 'caller' | 'type-user' | 'importer' | 'extender';
confidence: number; // ìí¥ íì¤ì± (0-1)
reason: string; // ìí¥ ì´ì
}
ìí¥ ë²ì ê³ì°
class ImpactAnalyzer {
private graph: CodebaseGraph;
constructor(graph: CodebaseGraph) {
this.graph = graph;
}
/**
* ìí¥ë ë¶ì ì¤í
*/
analyze(request: ImpactAnalysisRequest): ImpactResult {
const target = this.graph.findNode(request.target);
if (!target) {
throw new Error(`Node not found: ${request.target}`);
}
// 1. ì§ì ìí¥ ìì§
const directImpact = this.collectDirectImpact(target, request.changeType);
// 2. ê°ì ìí¥ ìì§ (ì¬ê·)
const indirectImpact = this.collectIndirectImpact(
directImpact,
request.depth || 3
);
// 3. ìíë ê³ì°
const riskScore = this.calculateRiskScore(target, directImpact, indirectImpact, request.changeType);
// 4. í
ì¤í¸ ì ì ìì±
const testSuggestions = this.generateTestSuggestions(target, directImpact, indirectImpact);
// 5. ê²½ê³ ìì±
const warnings = this.generateWarnings(target, directImpact, request.changeType);
return {
target,
changeType: request.changeType,
directImpact,
indirectImpact,
totalAffected: directImpact.length + indirectImpact.length,
riskScore,
riskLevel: this.getRiskLevel(riskScore),
testSuggestions,
warnings,
};
}
/**
* ì§ì ìí¥ ìì§
*/
private collectDirectImpact(target: GraphNode, changeType: ChangeType): ImpactNode[] {
const impacts: ImpactNode[] = [];
// 1. ì´ ë
¸ë를 í¸ì¶íë í¨ìë¤
const callers = this.graph.getIncomingEdges(target.id, 'calls');
for (const edge of callers) {
const caller = this.graph.findNode(edge.source);
if (caller) {
impacts.push({
node: caller,
depth: 1,
impactType: 'caller',
confidence: changeType === 'signature' ? 1.0 : 0.7,
reason: `Calls ${target.name}`,
});
}
}
// 2. ì´ íì
ì ì¬ì©íë ê³³
if (target.type === 'type' || target.type === 'class') {
const typeUsers = this.graph.getIncomingEdges(target.id, 'uses');
for (const edge of typeUsers) {
const user = this.graph.findNode(edge.source);
if (user) {
impacts.push({
node: user,
depth: 1,
impactType: 'type-user',
confidence: changeType === 'type' ? 1.0 : 0.5,
reason: `Uses type ${target.name}`,
});
}
}
}
// 3. ì´ ëª¨ëì importíë íì¼ë¤
const importers = this.graph.getIncomingEdges(target.id, 'imports');
for (const edge of importers) {
const importer = this.graph.findNode(edge.source);
if (importer && !impacts.some(i => i.node.id === importer.id)) {
impacts.push({
node: importer,
depth: 1,
impactType: 'importer',
confidence: changeType === 'rename' || changeType === 'delete' ? 1.0 : 0.3,
reason: `Imports from ${target.path}`,
});
}
}
// 4. ì´ í´ëì¤ë¥¼ ììíë í´ëì¤ë¤
if (target.type === 'class') {
const extenders = this.graph.getIncomingEdges(target.id, 'extends');
for (const edge of extenders) {
const extender = this.graph.findNode(edge.source);
if (extender) {
impacts.push({
node: extender,
depth: 1,
impactType: 'extender',
confidence: 1.0,
reason: `Extends ${target.name}`,
});
}
}
}
return impacts;
}
/**
* ê°ì ìí¥ ìì§ (BFS)
*/
private collectIndirectImpact(directImpact: ImpactNode[], maxDepth: number): ImpactNode[] {
const indirect: ImpactNode[] = [];
const visited = new Set<string>(directImpact.map(i => i.node.id));
const queue: ImpactNode[] = [...directImpact];
while (queue.length > 0) {
const current = queue.shift()!;
if (current.depth >= maxDepth) continue;
// íì¬ ë
¸ëì í¸ì¶ìë¤ ìì§
const callers = this.graph.getIncomingEdges(current.node.id, 'calls');
for (const edge of callers) {
if (visited.has(edge.source)) continue;
const caller = this.graph.findNode(edge.source);
if (!caller) continue;
visited.add(caller.id);
const impactNode: ImpactNode = {
node: caller,
depth: current.depth + 1,
impactType: 'caller',
confidence: current.confidence * 0.7, // ê¹ì´ì ë°ë¼ ê°ì
reason: `Indirect via ${current.node.name}`,
};
indirect.push(impactNode);
queue.push(impactNode);
}
}
return indirect;
}
/**
* ìíë ì ì ê³ì°
*/
private calculateRiskScore(
target: GraphNode,
direct: ImpactNode[],
indirect: ImpactNode[],
changeType: ChangeType
): number {
let score = 0;
// 1. ë³ê²½ ì í 기본 ì ì
const changeTypeScores: Record<ChangeType, number> = {
implementation: 20,
signature: 50,
type: 60,
rename: 40,
move: 45,
delete: 80,
};
score += changeTypeScores[changeType];
// 2. ì§ì ìí¥ ì (ê° +3ì , ìµë 30ì )
score += Math.min(direct.length * 3, 30);
// 3. ê°ì ìí¥ ì (ê° +1ì , ìµë 20ì )
score += Math.min(indirect.length, 20);
// 4. ëì í¹ì± ë³´ì
if (target.isExported) score += 10; // exportë ê²
if (target.path.includes('shared')) score += 15; // shared 모ë
if (target.path.includes('api')) score += 10; // API ê´ë ¨
if (target.path.includes('auth')) score += 15; // ì¸ì¦ ê´ë ¨
if (target.type === 'type') score += 10; // íì
ì ì
// 5. í
ì¤í¸ 커ë²ë¦¬ì§ ë³´ì
const hasTests = this.hasTestCoverage(target);
if (!hasTests) score += 10; // í
ì¤í¸ ìì¼ë©´ ìí
return Math.min(score, 100);
}
/**
* ìí ìì¤ ê²°ì
*/
private getRiskLevel(score: number): 'low' | 'medium' | 'high' | 'critical' {
if (score < 30) return 'low';
if (score < 50) return 'medium';
if (score < 75) return 'high';
return 'critical';
}
/**
* í
ì¤í¸ ì ì ìì±
*/
private generateTestSuggestions(
target: GraphNode,
direct: ImpactNode[],
indirect: ImpactNode[]
): TestSuggestion[] {
const suggestions: TestSuggestion[] = [];
// 1. ëì í
ì¤í¸
suggestions.push({
target: target.id,
priority: 'critical',
reason: 'Direct change target',
testType: 'unit',
});
// 2. ì§ì ìí¥ í
ì¤í¸
for (const impact of direct.slice(0, 10)) { // ìì 10ê°
suggestions.push({
target: impact.node.id,
priority: impact.confidence > 0.8 ? 'high' : 'medium',
reason: impact.reason,
testType: impact.impactType === 'caller' ? 'integration' : 'unit',
});
}
// 3. ê³ íì ê°ì ìí¥ í
ì¤í¸
const highConfidenceIndirect = indirect.filter(i => i.confidence > 0.5);
for (const impact of highConfidenceIndirect.slice(0, 5)) {
suggestions.push({
target: impact.node.id,
priority: 'low',
reason: `${impact.reason} (indirect)`,
testType: 'integration',
});
}
return suggestions;
}
/**
* ê²½ê³ ìì±
*/
private generateWarnings(
target: GraphNode,
direct: ImpactNode[],
changeType: ChangeType
): string[] {
const warnings: string[] = [];
// 1. ëì ìí¥ ë²ì
if (direct.length > 20) {
warnings.push(`HIGH IMPACT: This change affects ${direct.length} direct callers`);
}
// 2. export ìì /ë³ê²½
if (target.isExported && (changeType === 'delete' || changeType === 'rename')) {
warnings.push(`BREAKING: Modifying exported symbol may break external consumers`);
}
// 3. íµì¬ 모ë ë³ê²½
if (target.path.includes('shared') || target.path.includes('core')) {
warnings.push(`CORE MODULE: Changes to shared/core modules require extra caution`);
}
// 4. í
ì¤í¸ ë¶ì¡±
if (!this.hasTestCoverage(target)) {
warnings.push(`NO TESTS: Target has no test coverage - add tests before changing`);
}
// 5. ìí 참조 ê´ë ¨
const cycles = this.graph.detectCircularDependencies();
if (cycles.some(c => c.includes(target.id))) {
warnings.push(`CIRCULAR: Target is part of circular dependency - changes may cascade unpredictably`);
}
return warnings;
}
private hasTestCoverage(node: GraphNode): boolean {
// í
ì¤í¸ íì¼ ì¡´ì¬ íì¸
const testPatterns = [
node.path.replace('.ts', '.test.ts'),
node.path.replace('.ts', '.spec.ts'),
node.path.replace('/src/', '/test/'),
];
return testPatterns.some(pattern =>
this.graph.findNode(pattern) !== null
);
}
}
ìíë ì ì 기ì¤
Risk_Scoring:
Base_Scores_by_Change_Type:
implementation: 20 # ë¡ì§ë§ ë³ê²½
signature: 50 # ìê·¸ëì² ë³ê²½
type: 60 # íì
ì ì ë³ê²½
rename: 40 # ì´ë¦ ë³ê²½
move: 45 # ìì¹ ì´ë
delete: 80 # ìì
Modifiers:
direct_impact: "+3 per caller (max 30)"
indirect_impact: "+1 per node (max 20)"
is_exported: "+10"
in_shared: "+15"
in_api: "+10"
in_auth: "+15"
is_type: "+10"
no_tests: "+10"
Risk_Levels:
low: "0-29 (ìì í ë³ê²½)"
medium: "30-49 (주ì íì)"
high: "50-74 (ì ì¤í ê²í íì)"
critical: "75-100 (ìí, ì¶©ë¶í í
ì¤í¸ íì)"
ì¶ë ¥ í í릿
## Impact Analysis Report
### Target: {{targetName}}
- **Path**: {{targetPath}}:{{targetLine}}
- **Type**: {{targetType}}
- **Change Type**: {{changeType}}
---
### Risk Assessment
| Metric | Value |
|--------|-------|
| Risk Score | {{riskScore}}/100 |
| Risk Level | {{riskLevel}} |
| Direct Impact | {{directCount}} nodes |
| Indirect Impact | {{indirectCount}} nodes |
| Total Affected | {{totalAffected}} nodes |
---
### Direct Impact ({{directCount}} nodes)
| Node | Type | Reason | Confidence |
|------|------|--------|------------|
{{#each directImpact}}
| {{node.name}} | {{impactType}} | {{reason}} | {{confidence}}% |
{{/each}}
---
### Indirect Impact (Top 10 of {{indirectCount}})
| Node | Depth | Reason | Confidence |
|------|-------|--------|------------|
{{#each indirectImpact}}
| {{node.name}} | {{depth}} | {{reason}} | {{confidence}}% |
{{/each}}
---
### Warnings
{{#each warnings}}
- {{this}}
{{/each}}
---
### Recommended Tests
| Target | Priority | Type | Reason |
|--------|----------|------|--------|
{{#each testSuggestions}}
| {{target}} | {{priority}} | {{testType}} | {{reason}} |
{{/each}}
---
### Verdict
{{#if riskLevel === 'critical'}}
**CRITICAL**: Do NOT proceed without comprehensive test coverage and review.
{{else if riskLevel === 'high'}}
**HIGH RISK**: Proceed with caution. Ensure all suggested tests pass.
{{else if riskLevel === 'medium'}}
**MEDIUM RISK**: Safe to proceed with standard review process.
{{else}}
**LOW RISK**: Safe to proceed. Minor change with limited impact.
{{/if}}
Quick Commands
| Command | Action |
|---|---|
impact <target> |
기본 ìí¥ë ë¶ì |
impact signature <target> |
ìê·¸ëì² ë³ê²½ ìí¥ë |
impact delete <target> |
ìì ìí¥ë |
impact rename <target> |
ì´ë¦ ë³ê²½ ìí¥ë |
impact depth <n> <target> |
ê¹ì´ ì§ì ë¶ì |
impact test <target> |
í ì¤í¸ ì ìë§ |
impact warnings <target> |
ê²½ê³ ë§ |
ë¤ë¥¸ ì¤í¬ê³¼ì íµí©
Integration:
codebase-graph:
type: "ë°ì´í° ìì¤"
usage: "ê·¸ëí 쿼리, ììì¡´ì± ì¶ì "
smart-context:
type: "íë ¥"
usage: "ìí¥ ë²ì 컨í
ì¤í¸ ìì±"
code-reviewer:
type: "ìë¹ì"
usage: "리뷰 ì ìí¥ë íì"
tdd-guardian:
type: "ìë¹ì"
usage: "í
ì¤í¸ ë²ì ì ì ìì "
vibe-coding-orchestrator:
type: "ì ê³µì"
usage: "ëê·ëª¨ 리í©í ë§ ì ìë ë¶ì"
문ì 구조
impact-analyzer/
âââ SKILL.md # ì´ íì¼ (ë©ì¸)
âââ core/
â âââ impact-calculation.md # ìí¥ë ê³ì° ìì¸
â âââ risk-scoring.md # ìíë ì ì ì²´ê³
â âââ test-suggestion.md # í
ì¤í¸ ì ì ìê³ ë¦¬ì¦
âââ algorithms/
â âââ reverse-dependency.md # ììì¡´ì± ì¶ì
â âââ propagation.md # ìí¥ ì í ìê³ ë¦¬ì¦
â âââ confidence.md # íì ë ê³ì°
âââ templates/
â âââ report-template.md # 리í¬í¸ í
í릿
â âââ warning-templates.md # ê²½ê³ ë©ìì§ í
í릿
âââ quick-reference/
âââ commands.md # ëª
ë ¹ì´ ê°ì´ë
âââ risk-guide.md # ìíë í´ì ê°ì´ë
Version: 1.0.0 Quality Target: 95% Required Skill: codebase-graph Related Skills: smart-context, code-reviewer, tdd-guardian