feat(kontext): add cross-file dependency graph tool#1458
feat(kontext): add cross-file dependency graph tool#1458junghyeonsu wants to merge 6 commits intodevfrom
Conversation
Add the Kontext ecosystem for tracking and visualizing cross-file dependencies defined in kontext.yaml files. Includes: - @kontext/core: graph builder, query API, linter, schema validation - @kontext/cli: CLI with build, check, deps, lint, serve commands - @kontext/dashboard: React + Cytoscape graph visualization - Claude hook for showing affected files during edits - Skill definition for AI-assisted usage Security: path traversal protection in dashboard serve command. Performance: O(1) node lookups via Map in all query functions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughKontext 의존성 관리 시스템이 새로 도입되었습니다. 여기에는 YAML 설정으로부터 의존성 그래프를 구축하고 분석하는 TypeScript 핵심 라이브러리, 검증 및 시각화를 위한 Node.js CLI, 대화형 그래프 탐색용 React 대시보드, 그리고 자동 영향 분석을 위한 Claude 통합 훅이 포함됩니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User as 사용자
participant CLI as Kontext CLI
participant GraphBuilder as Graph Builder
participant FileSystem as 파일 시스템
participant Cache as 캐시<br/>(.kontext/)
User->>CLI: kontext build --root .
CLI->>FileSystem: kontext.yaml 파일 스캔
FileSystem-->>CLI: 설정 목록
CLI->>GraphBuilder: buildGraph({ rootDir })
GraphBuilder->>FileSystem: 글로브 패턴 매칭 & 파일 읽기
FileSystem-->>GraphBuilder: 파일 경로 & 내용
GraphBuilder->>GraphBuilder: 엣지 생성, 노드 존재 여부 체크
GraphBuilder-->>CLI: KontextGraph
CLI->>Cache: graph.json 쓰기
Cache-->>CLI: 저장 완료
CLI-->>User: 빌드 통계 (노드/엣지/패키지)
sequenceDiagram
participant User as 사용자
participant CLI as CLI:<br/>check/deps
participant Cache as 캐시<br/>(.kontext/)
participant GraphBuilder as Graph Builder
participant Query as Query 엔진
User->>CLI: kontext check / kontext deps <file>
CLI->>Cache: graph.json 읽기
alt 캐시 미스 또는 오류
CLI->>GraphBuilder: buildGraph() 빌드
GraphBuilder-->>CLI: KontextGraph
else 캐시 히트
Cache-->>CLI: KontextGraph
end
CLI->>Query: checkCompleteness(graph)<br/>또는 findDeps(graph, file)
Query->>Query: 그래프 쿼리 & 분석
Query-->>CLI: CheckResult[] 또는<br/>DepResult[]
CLI-->>User: 포맷된 결과<br/>(트리/테이블/JSON)
sequenceDiagram
participant Browser as 브라우저
participant Dashboard as React<br/>Dashboard
participant Server as HTTP<br/>Server
participant FileSystem as 파일 시스템
participant GraphBuilder as Graph<br/>Builder
Browser->>Dashboard: 페이지 로드
Dashboard->>Dashboard: useGraph() 초기화
Dashboard->>Server: GET /api/graph
Server->>FileSystem: graph.json 확인
alt 캐시 존재
FileSystem-->>Server: graph.json
else 캐시 미스
Server->>GraphBuilder: buildGraph() 구축
GraphBuilder-->>Server: KontextGraph
end
Server-->>Dashboard: JSON 응답
Dashboard->>Dashboard: 그래프 상태 업데이트
Dashboard-->>Browser: GraphExplorer 또는<br/>CompletenessMatrix 렌더링
Browser->>Browser: Cytoscape / 표 시각화
sequenceDiagram
participant Claude as Claude<br/>Editor
participant Hook as Kontext<br/>Hook Script
participant Cache as 캐시<br/>(.kontext/)
participant GraphBuilder as Graph<br/>Builder
participant Query as Query<br/>엔진
Claude->>Hook: 파일 쓰기/편집 감지
Hook->>Cache: graph.json 읽기 시도
alt 캐시 미스 또는 오류
Hook->>GraphBuilder: buildGraph() (10초 타임아웃)
GraphBuilder-->>Hook: KontextGraph
else 캐시 히트
Cache-->>Hook: KontextGraph
end
Hook->>Query: 수정된 파일에 대해<br/>findDeps() 실행
Query-->>Hook: DepResult[]
Hook->>Hook: 수동/자동 생성 의존성<br/>분류
Hook-->>Claude: 추가 컨텍스트 JSON<br/>(영향받는 파일 & 생성 명령)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Alpha Preview (Docs)
|
There was a problem hiding this comment.
Actionable comments posted: 16
🧹 Nitpick comments (8)
ecosystem/kontext/cli/src/commands/build.ts (1)
11-11: 공통 CLI 옵션 타입을 공유 타입으로 분리하는 편이 좋습니다.Line 11의
{ root: string }는 다른 커맨드에도 반복되므로, 공통 타입(예:RootOption)으로 추출해 재사용하면 변경 전파가 쉬워집니다.As per coding guidelines,
ecosystem/**/*.{ts,tsx}: Maintain TypeScript ESM pattern for CLI and core modules with explicit type sharing for common types.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ecosystem/kontext/cli/src/commands/build.ts` at line 11, The inline anonymous options type in the build command (.action((options: { root: string }) => { ... }) in build.ts) should be extracted to a shared named type (e.g., RootOption) so other CLI commands can reuse it; create/export RootOption from a common CLI types module (or an existing shared module), replace the inline `{ root: string }` with `RootOption` in the build command's .action signature, and update other commands to import and use RootOption to maintain the TypeScript ESM pattern and avoid duplicated option types.ecosystem/kontext/cli/src/commands/check.ts (1)
36-43: 그래프 로더를 공용 유틸로 추출하는 편이 안전합니다.동일한
loadOrBuildGraph로직이 다른 명령 파일에도 복제되어 있어, 추후 로딩/에러 처리 정책이 쉽게 어긋납니다. 공용 유틸로 묶어 동작 일관성을 유지해 주세요.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ecosystem/kontext/cli/src/commands/check.ts` around lines 36 - 43, Extract the duplicated loadOrBuildGraph logic into a single exported utility (e.g., export function loadOrBuildGraph(rootDir: string): KontextGraph) in a shared module, keep its current behavior (resolve .kontext/graph.json, readFileSync + JSON.parse, fallback to buildGraph({ rootDir })), and update command files to import this utility instead of duplicating the code; ensure you export/import the KontextGraph type and buildGraph reference or import buildGraph inside the util so error/typing behavior remains identical.ecosystem/kontext/dashboard/src/types.ts (1)
7-20: 대시보드GraphEdge타입이 서버 payload와 이미 드리프트했습니다.
serve는 core graph를 그대로 JSON으로 내보내고, core의GraphEdge에는optional과definedBy도 포함됩니다. 여기서 빠뜨리면 클라이언트가 실제 응답보다 더 좁은 타입을 갖게 되어 이후 UI에서 해당 필드를 쓰려 할 때 타입 안전성이 깨집니다.@kontext/core의 공유 타입을import type으로 재사용하거나, 최소한 동일 필드를 모두 반영해 두는 편이 좋습니다.As per coding guidelines, "Maintain TypeScript ESM pattern for CLI and core modules with explicit type sharing for common types".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ecosystem/kontext/dashboard/src/types.ts` around lines 7 - 20, The client GraphEdge type has drifted from the server/core shape; update the GraphEdge definition to match core by importing the canonical type (import type { GraphEdge } from "@kontext/core") and reuse it in KontextGraph (or, if you cannot import, add the missing fields optional?: boolean and definedBy?: string to the local GraphEdge) so the client signature matches the server payload; ensure KontextGraph still uses GraphNode[] and GraphEdge[] types consistently and follow the ESM explicit type import pattern when referencing shared types.ecosystem/kontext/cli/bin/kontext.mjs (1)
151-161:loadOrBuildGraph함수가 중복 정의되어 있습니다.
loadOrBuildGraph$1(lines 151-161)과loadOrBuildGraph(lines 184-195)가 동일한 로직을 포함합니다. 빌드 번들링 과정에서 발생한 것으로 보이지만, 원본 소스에서 단일 함수로 통합하는 것이 유지보수에 좋습니다.Also applies to: 184-195
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ecosystem/kontext/cli/bin/kontext.mjs` around lines 151 - 161, There are two identical implementations of the same logic: loadOrBuildGraph$1 and loadOrBuildGraph; remove the duplicate by keeping a single canonical function (e.g., loadOrBuildGraph) and delete the redundant loadOrBuildGraph$1 definition, then update any internal references/usages to call the kept function name so the bundle uses one implementation (ensure buildGraph and resolve/readFileSync/JSON.parse usage remain unchanged).ecosystem/kontext/cli/src/utils/render.ts (1)
71-75:LAYER_LABELS매핑이 불필요합니다.키와 값이 동일하므로 이 매핑 객체는 실질적인 변환을 수행하지 않습니다. 직접
layer값을 사용하거나, 향후 다국어 지원을 위해 유지한다면 주석으로 의도를 명시하는 것이 좋습니다.♻️ 불필요한 매핑 제거
-const LAYER_LABELS: Record<string, string> = { - naming: "naming", - import: "import", - "co-change": "co-change", -}; - export function renderLintResults(result: LintResult): string { // ... - lines.push(pc.cyan(` [${LAYER_LABELS[layer]}] ${items.length} suggestions`)); + lines.push(pc.cyan(` [${layer}] ${items.length} suggestions`));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ecosystem/kontext/cli/src/utils/render.ts` around lines 71 - 75, The LAYER_LABELS constant is redundant because keys equal values; remove the LAYER_LABELS object and replace references to LAYER_LABELS[...]/LAYER_LABELS[layer] with the raw layer value (e.g., use layer directly) in functions that render labels (search for usages of LAYER_LABELS in this file), or if you intend to preserve it for future i18n, keep it but add a comment above LAYER_LABELS explaining it's a placeholder for localization and why keys mirror values.ecosystem/kontext/core/src/query.ts (1)
6-47:findDeps와findAffectedBy의 중복 로직을 추출할 수 있습니다.두 함수가 거의 동일한 패턴을 따릅니다.
nodeMap생성과 결과 매핑 로직이 중복되어 있으며, 차이점은edge.sourcevsedge.target비교와 결과의path필드뿐입니다.현재 구현이 명확하고 읽기 쉬우므로 선택적 개선 사항입니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ecosystem/kontext/core/src/query.ts` around lines 6 - 47, Extract the shared logic from findDeps and findAffectedBy into a single helper (e.g., collectDeps) that builds nodeMap from graph.nodes and iterates graph.edges, taking a selector or boolean flag to choose whether to compare edge.source or edge.target and which path to emit; update findDeps and findAffectedBy to call this helper (preserving returned DepResult shape with path, reason, generated, command, exists using node?.exists ?? false) so the duplicated nodeMap creation and result mapping are removed while behavior and symbols (findDeps, findAffectedBy, nodeMap, DepResult, graph.edges) remain unchanged.ecosystem/kontext/core/src/graph-builder.ts (1)
208-218:extractIdFromWatched가resolver.ts의toKebabCase로직을 중복합니다.이 함수는
resolver.ts의toKebabCase(lines 74-80)와 동일한 정규식 패턴을 사용합니다. 또한resolver.ts에서 export된extractId함수가 더 정교한 패턴 기반 ID 추출을 지원하지만 사용되지 않습니다.중복 제거를 위해
resolver.ts의 함수를 import하여 사용하거나, 사용하지 않는 export를 정리하는 것을 고려해 주세요.♻️ resolver.ts의 toKebabCase를 재사용
resolver.ts에서toKebabCase를 export하고:// resolver.ts export function toKebabCase(name: string): string { return name .replace(/([a-z0-9])([A-Z])/g, "$1-$2") .replace(/([A-Z])([A-Z][a-z])/g, "$1-$2") .toLowerCase(); }
graph-builder.ts에서 import하여 사용:-import { expandTemplate, hasTemplate } from "./resolver.js"; +import { expandTemplate, hasTemplate, toKebabCase } from "./resolver.js"; function extractIdFromWatched(filePath: string): string | null { const name = filePath.split("/").pop() ?? ""; const dotIdx = name.lastIndexOf("."); const base = dotIdx >= 0 ? name.slice(0, dotIdx) : name; if (!base || base === "index") return null; - return base - .replace(/([a-z0-9])([A-Z])/g, "$1-$2") - .replace(/([A-Z])([A-Z][a-z])/g, "$1-$2") - .toLowerCase(); + return toKebabCase(base); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ecosystem/kontext/core/src/graph-builder.ts` around lines 208 - 218, The extractIdFromWatched function duplicates the kebab-casing logic in resolver.ts (toKebabCase) and overlaps with resolver.ts's exported extractId; remove the duplicated logic by importing and using resolver.ts's toKebabCase (or directly use the exported extractId if it fits the behavior) in graph-builder.ts, replace calls to extractIdFromWatched with the imported function (update any callers inside graph-builder.ts), and delete the local extractIdFromWatched declaration to avoid duplication.ecosystem/kontext/core/src/schema.ts (1)
25-37:validateStringArray가 유효하지 않은 항목을 포함한 배열을 반환합니다.검증 루프에서 문자열이 아닌 항목을 발견하면 issues에 추가하지만, 반환 시에는 원본 배열을 그대로
string[]으로 타입 단언하여 반환합니다. 유효하지 않은 항목이 포함된 상태로 사용될 수 있습니다.현재
validateConfig에서 issues가 있으면 예외를 던지므로 실제 런타임 문제는 발생하지 않지만, 함수 자체의 계약을 명확히 하려면 필터링하거나 빈 배열을 반환하는 것이 좋습니다.♻️ 유효한 항목만 반환하도록 개선
function validateStringArray(value: unknown, fieldName: string, issues: string[]): string[] { if (value === undefined) return []; if (!Array.isArray(value)) { issues.push(`${fieldName} must be an array of strings`); return []; } + const valid: string[] = []; for (let i = 0; i < value.length; i++) { if (typeof value[i] !== "string") { issues.push(`${fieldName}[${i}] must be a string`); + } else { + valid.push(value[i]); } } - return value as string[]; + return valid; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ecosystem/kontext/core/src/schema.ts` around lines 25 - 37, validateStringArray currently records non-string items to issues but returns the original array cast to string[], allowing invalid entries to slip through; update validateStringArray to iterate value, collect only items of type "string" into a new result array, push issues for non-string entries (using fieldName and index like `${fieldName}[${i}] must be a string`), and return the filtered result (or [] if undefined/not an array) so callers (e.g., validateConfig) receive only valid strings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.claude/hooks/kontext-deps-hook.ts:
- Around line 20-32: The current hook only builds when .kontext/graph.json is
missing (graphPath) so edits to kontext.yaml or source files can leave a stale
graph; change the logic that currently calls execSync("bun
ecosystem/kontext/cli/bin/kontext.mjs build", { cwd: rootDir, ... }) and the
subsequent existsSync(graphPath) check to first detect whether the graph is
outdated by comparing graphPath mtime (or a stored input hash inside graph.json)
against kontext.yaml and relevant source files (e.g., project src tree or any
files that affect graph membership); if any input is newer or hashes differ,
invoke the existing execSync rebuild and then re-check graphPath (keeping the
same stdio/timeout options), otherwise keep using the existing graph.json.
In @.claude/settings.json:
- Around line 73-79: The matcher value "Write|Edit" in the settings is missing
the MultiEdit token so the kontext-deps-hook.sh command will not run for
multi-file edits; update the matcher to include "MultiEdit" (e.g.,
"Write|Edit|MultiEdit") and then verify the kontext-deps-hook.sh hook logic
(which currently expects a single filePath) to ensure it either iterates
per-file when invoked for MultiEdit or is adjusted to accept/process multiple
file paths; update any parsing/looping in the hook to handle multiple files if
necessary.
In `@ecosystem/kontext/cli/bin/kontext.mjs`:
- Around line 266-288: The code builds file paths from user-controlled req.url
into filePath and indexPath without sanitization, allowing path traversal; fix
by resolving the requested path with path.resolve(join(dashboardDir, url)) (use
path.resolve on dashboardDir + url) and verify the resolved path is inside the
dashboardDir using a safeBase = path.resolve(dashboardDir) and
safePath.startsWith(safeBase + path.sep) before reading; if the check fails
return 404 or fallback to index.html; apply the same resolve+startsWith guard
for indexPath handling so reads never escape dashboardDir.
In `@ecosystem/kontext/cli/src/commands/deps.ts`:
- Around line 18-20: The current check using resolve(file).startsWith(rootDir)
mis-identifies paths and fails on Windows separators; replace that logic in
deps.ts (where relFile is computed) with path.relative(rootDir, resolve(file))
to get a true relative path and normalize separators to forward slashes,
handling absolute inputs (use path.isAbsolute if needed) and falling back to the
original file when appropriate; apply the identical fix to graph-builder.ts's
relativeFromRoot function (add imports for path.relative and path.isAbsolute) so
both modules compute and normalize relative paths consistently and avoid false
positives like "/repo-other/...".
In `@ecosystem/kontext/cli/src/commands/lint.ts`:
- Line 18: The CLI currently exposes option("--fix", "Auto-apply suggestions to
kontext.yaml files") but doesn't implement writing (the handler only prints a
warning and returns); locate the option("--fix", ...) declaration in lint.ts and
either remove it or mark it hidden/experimental (e.g., use a hidden or
experimental flag label or rename to --experimental-fix) and update the help
text accordingly; also update the handler block that prints the warning and
returns (the current early-return path for the fix warning) so that when --fix
is passed with --json it does not silently exit—ensure it emits an explicit
warning/error and exits non-zero or implements the actual write behavior before
advertising the flag.
In `@ecosystem/kontext/cli/src/commands/serve.ts`:
- Around line 49-53: The handler currently checks only existsSync(filePath)
before calling readFileSync, which will throw EISDIR for directory requests
(e.g., requesting /assets); update the logic in the serve handler to stat the
path (use fs.statSync or fs.lstatSync on filePath) and verify stats.isFile()
before reading; if it's not a file, respond with 404 or delegate to the next
handler instead of calling readFileSync; also wrap the file read in a try/catch
to log and respond with 500 on unexpected I/O errors.
- Line 24: The resolved dashboardDir currently points to cli/dashboard/dist
(using "../../dashboard/dist") but the built dashboard lives at
kontext/dashboard/dist; update the path used when creating dashboardDir (the
resolve call that assigns dashboardDir in serve.ts) to go up one more directory
(use "../../../dashboard/dist") so it resolves to the actual
ecosystem/kontext/dashboard/dist output.
- Around line 40-47: The containment check using
filePath.startsWith(dashboardDir) can be bypassed by sibling prefixes; replace
it with a robust containment test: compute the resolved absolute filePath (from
url and dashboardDir) and use path.relative(dashboardDir, filePath) to ensure
the result does not start with '..' and is not equal to '..' (and also ensure
filePath is absolute), rejecting the request (403) if the relative path begins
with '..' or if path.isAbsolute check fails; update the check around the
variables filePath, dashboardDir, and url accordingly.
In `@ecosystem/kontext/core/kontext.schema.json`:
- Around line 27-30: The JSON schema allows empty strings but runtime validation
rejects empty values for the string fields, so update the schema entries for the
string properties referenced (the "when" property and the other string
occurrences at the later blocks referenced by "match" and "path") to enforce
non-empty values by adding "minLength": 1 to each of those string schemas
(update the "when" schema and the two other string schemas at the other
occurrences) so the schema matches the runtime validation behavior.
In `@ecosystem/kontext/core/src/lint.ts`:
- Around line 194-202: The git command construction in lint.ts inserts the
CLI-derived commitCount directly into a shell string causing potential shell
injection; update the code to validate and sanitize commitCount (parseInt,
ensure it's a finite positive integer and clamp to a safe max) and then call a
non-shell API that accepts argv arrays (e.g., child_process.spawnSync or
execFileSync) instead of execSync so you pass
['log','--name-only','--pretty=format:COMMIT:%H','-n', String(commitCount)] with
the same options (cwd, encoding, maxBuffer); reference the gitOutput variable
and the commitCount usage in the try/catch around execSync to locate where to
apply these changes.
In `@ecosystem/kontext/dashboard/src/App.tsx`:
- Around line 72-87: The button currently only uses color to indicate active
state which is inaccessible; update the JSX for the interactive element (the
button with props onClick, children and the active boolean) to include an ARIA
state by adding aria-pressed={active} (ensure active is a boolean) so screen
readers receive the pressed/active state; keep existing visual styles intact and
do not change the button's semantics.
In `@ecosystem/kontext/dashboard/src/hooks/useGraph.ts`:
- Line 15: The catch handler in useGraph currently assumes the thrown value has
a message property; update the .catch callback to accept err as unknown and
perform a type guard before calling setError — e.g., in the promise chain inside
useGraph, change the anonymous catch to check if err is an instance of Error and
use err.message, otherwise coerce the value (e.g., String(err)) and pass that to
setError so TypeScript type-safety is preserved for the setError call.
In `@ecosystem/kontext/dashboard/src/views/CompletenessMatrix.tsx`:
- Around line 61-66: The cell currently conveys status only via a colored dot
(in CompletenessMatrix.tsx inside the td rendering using row.layers[l]) which is
inaccessible; update the rendering to include an accessible status label for
assistive tech: when rendering the span for the dot (the element that shows ● or
○), add an aria-label (or role="img" + aria-label) with a clear text like
"complete" or "incomplete" based on row.layers[l], and/or include a
visually-hidden text node (e.g., an sr-only span) next to the symbol so screen
readers announce the state; keep the existing visual dot and tdStyle but ensure
the status text is derived from row.layers[l].
In `@ecosystem/kontext/dashboard/src/views/GraphExplorer.tsx`:
- Around line 29-37: The label computation for graph nodes uses
node.id.split("/").pop() which returns an empty string for IDs with a trailing
slash (e.g., "pkg-b/docs/"); update the label logic in GraphExplorer.tsx where
elements are built (the node loop that sets data.label) to strip any trailing
slashes from node.id before extracting the basename (or otherwise normalize the
ID) so labels are never empty; keep the rest of the data fields (packageDir,
color via getColor, exists) unchanged.
In `@skills/kontext/references/yaml-schema.md`:
- Line 22: 문서의 용어 불일치 문제: 스키마 키 이름은 `when`인데 설명 문장에 `watch`가 사용되어 혼동을 줍니다. 해당
문장(현재 "watch 패턴에서 매칭된 파일명에서 자동 추출됨.")에서 `watch`를 `when`으로 교체하여 "when 패턴에서 매칭된
파일명에서 자동 추출됨."처럼 스키마 키(`when`)와 용어를 일치시켜 주세요.
In `@skills/kontext/SKILL.md`:
- Around line 1-4: Add a YAML frontmatter block at the very top of SKILL.md
containing at minimum the required `name` and `description` fields (e.g., ---
name: "Kontext" description: "모노레포 의존성 그래프를 탐색하고, 변경 영향 범위를 파악한다." ---),
ensuring the frontmatter is the first content in the file so the skills metadata
collector can parse `name` and `description` correctly.
---
Nitpick comments:
In `@ecosystem/kontext/cli/bin/kontext.mjs`:
- Around line 151-161: There are two identical implementations of the same
logic: loadOrBuildGraph$1 and loadOrBuildGraph; remove the duplicate by keeping
a single canonical function (e.g., loadOrBuildGraph) and delete the redundant
loadOrBuildGraph$1 definition, then update any internal references/usages to
call the kept function name so the bundle uses one implementation (ensure
buildGraph and resolve/readFileSync/JSON.parse usage remain unchanged).
In `@ecosystem/kontext/cli/src/commands/build.ts`:
- Line 11: The inline anonymous options type in the build command
(.action((options: { root: string }) => { ... }) in build.ts) should be
extracted to a shared named type (e.g., RootOption) so other CLI commands can
reuse it; create/export RootOption from a common CLI types module (or an
existing shared module), replace the inline `{ root: string }` with `RootOption`
in the build command's .action signature, and update other commands to import
and use RootOption to maintain the TypeScript ESM pattern and avoid duplicated
option types.
In `@ecosystem/kontext/cli/src/commands/check.ts`:
- Around line 36-43: Extract the duplicated loadOrBuildGraph logic into a single
exported utility (e.g., export function loadOrBuildGraph(rootDir: string):
KontextGraph) in a shared module, keep its current behavior (resolve
.kontext/graph.json, readFileSync + JSON.parse, fallback to buildGraph({ rootDir
})), and update command files to import this utility instead of duplicating the
code; ensure you export/import the KontextGraph type and buildGraph reference or
import buildGraph inside the util so error/typing behavior remains identical.
In `@ecosystem/kontext/cli/src/utils/render.ts`:
- Around line 71-75: The LAYER_LABELS constant is redundant because keys equal
values; remove the LAYER_LABELS object and replace references to
LAYER_LABELS[...]/LAYER_LABELS[layer] with the raw layer value (e.g., use layer
directly) in functions that render labels (search for usages of LAYER_LABELS in
this file), or if you intend to preserve it for future i18n, keep it but add a
comment above LAYER_LABELS explaining it's a placeholder for localization and
why keys mirror values.
In `@ecosystem/kontext/core/src/graph-builder.ts`:
- Around line 208-218: The extractIdFromWatched function duplicates the
kebab-casing logic in resolver.ts (toKebabCase) and overlaps with resolver.ts's
exported extractId; remove the duplicated logic by importing and using
resolver.ts's toKebabCase (or directly use the exported extractId if it fits the
behavior) in graph-builder.ts, replace calls to extractIdFromWatched with the
imported function (update any callers inside graph-builder.ts), and delete the
local extractIdFromWatched declaration to avoid duplication.
In `@ecosystem/kontext/core/src/query.ts`:
- Around line 6-47: Extract the shared logic from findDeps and findAffectedBy
into a single helper (e.g., collectDeps) that builds nodeMap from graph.nodes
and iterates graph.edges, taking a selector or boolean flag to choose whether to
compare edge.source or edge.target and which path to emit; update findDeps and
findAffectedBy to call this helper (preserving returned DepResult shape with
path, reason, generated, command, exists using node?.exists ?? false) so the
duplicated nodeMap creation and result mapping are removed while behavior and
symbols (findDeps, findAffectedBy, nodeMap, DepResult, graph.edges) remain
unchanged.
In `@ecosystem/kontext/core/src/schema.ts`:
- Around line 25-37: validateStringArray currently records non-string items to
issues but returns the original array cast to string[], allowing invalid entries
to slip through; update validateStringArray to iterate value, collect only items
of type "string" into a new result array, push issues for non-string entries
(using fieldName and index like `${fieldName}[${i}] must be a string`), and
return the filtered result (or [] if undefined/not an array) so callers (e.g.,
validateConfig) receive only valid strings.
In `@ecosystem/kontext/dashboard/src/types.ts`:
- Around line 7-20: The client GraphEdge type has drifted from the server/core
shape; update the GraphEdge definition to match core by importing the canonical
type (import type { GraphEdge } from "@kontext/core") and reuse it in
KontextGraph (or, if you cannot import, add the missing fields optional?:
boolean and definedBy?: string to the local GraphEdge) so the client signature
matches the server payload; ensure KontextGraph still uses GraphNode[] and
GraphEdge[] types consistently and follow the ESM explicit type import pattern
when referencing shared types.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 90440f8e-ab49-4c32-986b-35ced69ee6c0
⛔ Files ignored due to path filters (5)
bun.lockis excluded by!**/*.lockecosystem/kontext/cli/package.jsonis excluded by!**/package.jsonecosystem/kontext/core/package.jsonis excluded by!**/package.jsonecosystem/kontext/dashboard/package.jsonis excluded by!**/package.jsonpackage.jsonis excluded by!**/package.json
📒 Files selected for processing (40)
.claude/hooks/kontext-deps-hook.sh.claude/hooks/kontext-deps-hook.ts.claude/settings.json.gitignoreecosystem/kontext/cli/bin/kontext.mjsecosystem/kontext/cli/src/bin/kontext.tsecosystem/kontext/cli/src/commands/build.tsecosystem/kontext/cli/src/commands/check.tsecosystem/kontext/cli/src/commands/deps.tsecosystem/kontext/cli/src/commands/lint.tsecosystem/kontext/cli/src/commands/serve.tsecosystem/kontext/cli/src/utils/render.tsecosystem/kontext/cli/tsconfig.jsonecosystem/kontext/core/kontext.schema.jsonecosystem/kontext/core/src/graph-builder.tsecosystem/kontext/core/src/index.tsecosystem/kontext/core/src/lint.tsecosystem/kontext/core/src/parser.tsecosystem/kontext/core/src/query.tsecosystem/kontext/core/src/resolver.tsecosystem/kontext/core/src/schema.tsecosystem/kontext/core/src/types.tsecosystem/kontext/core/test/graph-builder.test.tsecosystem/kontext/core/test/query.test.tsecosystem/kontext/core/test/resolver.test.tsecosystem/kontext/core/test/schema.test.tsecosystem/kontext/core/tsconfig.jsonecosystem/kontext/dashboard/.gitignoreecosystem/kontext/dashboard/index.htmlecosystem/kontext/dashboard/src/App.tsxecosystem/kontext/dashboard/src/hooks/useGraph.tsecosystem/kontext/dashboard/src/main.tsxecosystem/kontext/dashboard/src/types.tsecosystem/kontext/dashboard/src/views/CompletenessMatrix.tsxecosystem/kontext/dashboard/src/views/GraphExplorer.tsxecosystem/kontext/dashboard/tsconfig.jsonecosystem/kontext/dashboard/vite.config.tspackages/cli/kontext.yamlskills/kontext/SKILL.mdskills/kontext/references/yaml-schema.md
- fix path traversal: sibling prefix guard, rootDir-based dashboard path, EISDIR check - fix shell injection: use execFileSync with array args instead of string interpolation - fix cache invalidation: compare kontext.yaml mtime vs graph.json for auto-rebuild - fix MultiEdit matcher missing from PostToolUse hook - fix deps path resolution for subdirectory execution - fix JSON Schema minLength mismatch with runtime validation - add accessibility: aria-pressed on tabs, aria-label on matrix cells - fix trailing slash empty label in graph explorer - fix --fix option marked as experimental (not yet implemented) - fix SKILL.md missing frontmatter, yaml-schema.md terminology (watch→when) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- extract shared RootOption type and loadOrBuildGraph utility - sync dashboard GraphEdge type with core (add optional, definedBy) - remove redundant LAYER_LABELS mapping in render.ts - reuse toKebabCase from resolver.ts in graph-builder.ts - add overrides.affects entry validation in schema.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@coderabbitai resolve |
✅ Actions performedComments resolved. Approval is disabled; enable |
…ighlighting Replace Explorer/Editor dual-tab layout with a single unified KontextView: - Left panel: KONTEXTS/FILES tabs with dependency-aware file tree - Center panel: relation editor with exists/missing/optional status badges - Right panel: YAML/File preview tabs with shiki syntax highlighting Key features: - Status-based tree highlights (when/exists/missing/optional/folder) - Hover tooltips showing all related when patterns and reasons - Active kontext.yaml glow animation in Files tab - File content preview with line numbers via /api/file-content endpoint - Auto-expand folders containing highlighted files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Alpha Preview (Stackflow SPA)
|
Alpha Preview (Storybook)
|
- Delete 6 unused files (Editor, Explorer, ConnectionLines, DependencyTree, RelationCard, PackageSidebar) — 1,832 lines removed - Remove dead types (ViewMode, RelationGroup, AffectedFile, PackageSummary) and dead functions (buildPackageSummaries, buildRelationGroups) from graph-utils - serve.ts: hoist BINARY_EXTS to module scope, fix double statSync, remove unnecessary existsSync before mkdirSync - useFileTree: fix loadDir identity churn via ref (was re-creating on every state update, causing cascading re-renders) - CodePreview: useEffect → useMemo (synchronous highlight, no extra render) - useConfig: remove unused currentPackage state - handleDrop: fix stale deps (was [relations, ignorePatterns], now []) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
question: https://www.npmjs.com/package/madge 같은 기존 도구들이랑 어떤 부분에서 다른거에요? |

Summary
SEED Design 모노레포에서 파일 간 의존 관계를 선언적으로 정의하고, 빌드/검증/시각화할 수 있는 Kontext 도구를 추가합니다.
배경
SEED Design은
rootage YAML → qvism-preset → css → react생성 파이프라인을 따르는데, 이 파일 간 관계가 코드에 암묵적으로만 존재했습니다. Kontext는 이 관계를kontext.yaml로 명시화하여:구성
@kontext/core@kontext/clikontext build,check,deps,lint,serveCLI@kontext/dashboard주요 기능
kontext build—kontext.yaml파일들을 파싱하여 전체 의존 그래프 생성kontext check— 선언된 의존 파일이 실제로 존재하는지 완전성 검증kontext deps <file>— 특정 파일의 상/하위 의존 관계 조회kontext lint— 네이밍 규칙, import 패턴, git co-change 분석kontext serve— 대시보드에서 그래프 탐색 및 완전성 매트릭스 확인보안/성능
resolve+startsWith검증)Map기반 O(1) 노드 조회 (O(n·m) → O(n))Test plan
@kontext/core단위 테스트: schema, resolver, graph-builder, querykontext build로packages/cli/kontext.yaml빌드 확인kontext serve로 대시보드 접근 확인🤖 Generated with Claude Code
Summary by CodeRabbit
릴리스 노트
새로운 기능
kontext.yaml구성을 통한 관계 선언 기능문서화