mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-27 23:06:54 +05:00
feat(ipuaro): add circular dependencies to initial context
- Add formatCircularDeps() to display cycle chains in context
- Add includeCircularDeps config option (default: true)
- Add circularDeps parameter to BuildContextOptions
- Format: ## ⚠️ Circular Dependencies with → arrows
- 23 new tests (1798 total), 97.48% coverage
This commit is contained in:
@@ -5,6 +5,44 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.28.0] - 2025-12-05 - Circular Dependencies in Context
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Circular Dependencies in Initial Context (v0.28.0)**
|
||||||
|
- New `## ⚠️ Circular Dependencies` section in initial context
|
||||||
|
- Shows cycle chains immediately without requiring tool calls
|
||||||
|
- Format: `- services/user → services/auth → services/user`
|
||||||
|
- Uses same path shortening as dependency graph (removes `src/`, extensions, `/index`)
|
||||||
|
|
||||||
|
- **Configuration Option**
|
||||||
|
- `includeCircularDeps: boolean` in ContextConfigSchema (default: `true`)
|
||||||
|
- `includeCircularDeps` option in `BuildContextOptions`
|
||||||
|
- `circularDeps: string[][]` parameter to pass pre-computed cycles
|
||||||
|
- Users can disable to save tokens: `context.includeCircularDeps: false`
|
||||||
|
|
||||||
|
- **New Helper Function in prompts.ts**
|
||||||
|
- `formatCircularDeps()` - formats circular dependency cycles for display
|
||||||
|
|
||||||
|
### New Context Format
|
||||||
|
|
||||||
|
```
|
||||||
|
## ⚠️ Circular Dependencies
|
||||||
|
|
||||||
|
- services/user → services/auth → services/user
|
||||||
|
- utils/a → utils/b → utils/c → utils/a
|
||||||
|
```
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
|
||||||
|
- Total tests: 1798 passed (was 1775, +23 new tests)
|
||||||
|
- 12 new tests for formatCircularDeps()
|
||||||
|
- 6 new tests for buildInitialContext with includeCircularDeps
|
||||||
|
- 5 new tests for includeCircularDeps config option
|
||||||
|
- Coverage: 97.48% lines, 91.13% branches, 98.63% functions
|
||||||
|
- 0 ESLint errors, 3 warnings (pre-existing complexity in ASTParser and prompts)
|
||||||
|
- Build successful
|
||||||
|
|
||||||
## [0.27.0] - 2025-12-05 - Inline Dependency Graph
|
## [0.27.0] - 2025-12-05 - Inline Dependency Graph
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -1917,10 +1917,10 @@ Enhance initial context for LLM: add function signatures, interface field types,
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Version 0.28.0 - Circular Dependencies in Context 🔄
|
## Version 0.28.0 - Circular Dependencies in Context 🔄 ✅
|
||||||
|
|
||||||
**Priority:** MEDIUM
|
**Priority:** MEDIUM
|
||||||
**Status:** Planned
|
**Status:** Complete (v0.28.0 released)
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
|
|
||||||
@@ -1936,9 +1936,15 @@ Enhance initial context for LLM: add function signatures, interface field types,
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Changes:**
|
**Changes:**
|
||||||
- [ ] Add `formatCircularDeps()` to prompts.ts
|
- [x] Add `formatCircularDeps()` to prompts.ts
|
||||||
- [ ] Get circular deps from IndexBuilder
|
- [x] Add `includeCircularDeps: boolean` config option (default: true)
|
||||||
- [ ] Store in Redis as separate key or in meta
|
- [x] Add `circularDeps: string[][]` parameter to `BuildContextOptions`
|
||||||
|
- [x] Integrate into `buildInitialContext()`
|
||||||
|
|
||||||
|
**Tests:**
|
||||||
|
- [x] Unit tests for formatCircularDeps() (12 tests)
|
||||||
|
- [x] Unit tests for buildInitialContext with includeCircularDeps (6 tests)
|
||||||
|
- [x] Unit tests for includeCircularDeps config option (5 tests)
|
||||||
|
|
||||||
**Why:** LLM immediately sees architecture problems.
|
**Why:** LLM immediately sees architecture problems.
|
||||||
|
|
||||||
@@ -2016,12 +2022,12 @@ interface FileMeta {
|
|||||||
- [x] Error handling complete ✅ (v0.16.0)
|
- [x] Error handling complete ✅ (v0.16.0)
|
||||||
- [ ] Performance optimized
|
- [ ] Performance optimized
|
||||||
- [x] Documentation complete ✅ (v0.17.0)
|
- [x] Documentation complete ✅ (v0.17.0)
|
||||||
- [x] Test coverage ≥91% branches, ≥95% lines/functions/statements ✅ (91.21% branches, 97.5% lines, 98.58% functions, 97.5% statements - 1687 tests)
|
- [x] Test coverage ≥91% branches, ≥95% lines/functions/statements ✅ (91.13% branches, 97.48% lines, 98.63% functions, 97.48% statements - 1798 tests)
|
||||||
- [x] 0 ESLint errors ✅
|
- [x] 0 ESLint errors ✅
|
||||||
- [x] Examples working ✅ (v0.18.0)
|
- [x] Examples working ✅ (v0.18.0)
|
||||||
- [x] CHANGELOG.md up to date ✅
|
- [x] CHANGELOG.md up to date ✅
|
||||||
- [x] Rich initial context (v0.24.0-v0.26.0) — function signatures, interface fields, enum values, decorators ✅
|
- [x] Rich initial context (v0.24.0-v0.26.0) — function signatures, interface fields, enum values, decorators ✅
|
||||||
- [ ] Graph metrics in context (v0.27.0-v0.30.0) — dependency graph ✅, circular deps, impact score, transitive deps
|
- [ ] Graph metrics in context (v0.27.0-v0.30.0) — dependency graph ✅, circular deps ✅, impact score, transitive deps
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -2100,7 +2106,7 @@ sessions:list # List<session_id>
|
|||||||
|
|
||||||
**Last Updated:** 2025-12-05
|
**Last Updated:** 2025-12-05
|
||||||
**Target Version:** 1.0.0
|
**Target Version:** 1.0.0
|
||||||
**Current Version:** 0.27.0
|
**Current Version:** 0.28.0
|
||||||
**Next Milestones:** v0.28.0 (Circular Deps), v0.29.0 (Impact Score), v0.30.0 (Transitive Deps)
|
**Next Milestones:** v0.29.0 (Impact Score), v0.30.0 (Transitive Deps)
|
||||||
|
|
||||||
> **Note:** Rich Initial Context complete ✅ (v0.24.0-v0.26.0). Graph Metrics in progress (v0.27.0 ✅, v0.28.0-v0.30.0 pending) for 1.0.0 release.
|
> **Note:** Rich Initial Context complete ✅ (v0.24.0-v0.26.0). Graph Metrics in progress (v0.27.0 ✅, v0.28.0 ✅, v0.29.0-v0.30.0 pending) for 1.0.0 release.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@samiyev/ipuaro",
|
"name": "@samiyev/ipuaro",
|
||||||
"version": "0.27.0",
|
"version": "0.28.0",
|
||||||
"description": "Local AI agent for codebase operations with infinite context feeling",
|
"description": "Local AI agent for codebase operations with infinite context feeling",
|
||||||
"author": "Fozilbek Samiyev <fozilbek.samiyev@gmail.com>",
|
"author": "Fozilbek Samiyev <fozilbek.samiyev@gmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ export interface ProjectStructure {
|
|||||||
export interface BuildContextOptions {
|
export interface BuildContextOptions {
|
||||||
includeSignatures?: boolean
|
includeSignatures?: boolean
|
||||||
includeDepsGraph?: boolean
|
includeDepsGraph?: boolean
|
||||||
|
includeCircularDeps?: boolean
|
||||||
|
circularDeps?: string[][]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,6 +131,7 @@ export function buildInitialContext(
|
|||||||
const sections: string[] = []
|
const sections: string[] = []
|
||||||
const includeSignatures = options?.includeSignatures ?? true
|
const includeSignatures = options?.includeSignatures ?? true
|
||||||
const includeDepsGraph = options?.includeDepsGraph ?? true
|
const includeDepsGraph = options?.includeDepsGraph ?? true
|
||||||
|
const includeCircularDeps = options?.includeCircularDeps ?? true
|
||||||
|
|
||||||
sections.push(formatProjectHeader(structure))
|
sections.push(formatProjectHeader(structure))
|
||||||
sections.push(formatDirectoryTree(structure))
|
sections.push(formatDirectoryTree(structure))
|
||||||
@@ -141,6 +144,13 @@ export function buildInitialContext(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (includeCircularDeps && options?.circularDeps && options.circularDeps.length > 0) {
|
||||||
|
const circularDepsSection = formatCircularDeps(options.circularDeps)
|
||||||
|
if (circularDepsSection) {
|
||||||
|
sections.push(circularDepsSection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return sections.join("\n\n")
|
return sections.join("\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,6 +536,38 @@ export function formatDependencyGraph(metas: Map<string, FileMeta>): string | nu
|
|||||||
return lines.join("\n")
|
return lines.join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format circular dependencies for display in context.
|
||||||
|
* Shows warning section with cycle chains.
|
||||||
|
*
|
||||||
|
* Format:
|
||||||
|
* ## ⚠️ Circular Dependencies
|
||||||
|
* - services/user → services/auth → services/user
|
||||||
|
* - utils/a → utils/b → utils/c → utils/a
|
||||||
|
*/
|
||||||
|
export function formatCircularDeps(cycles: string[][]): string | null {
|
||||||
|
if (!cycles || cycles.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines: string[] = ["## ⚠️ Circular Dependencies", ""]
|
||||||
|
|
||||||
|
for (const cycle of cycles) {
|
||||||
|
if (cycle.length === 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const formattedCycle = cycle.map(shortenPath).join(" → ")
|
||||||
|
lines.push(`- ${formattedCycle}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return null if only header (no actual cycles)
|
||||||
|
if (lines.length <= 2) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines.join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format line range for display.
|
* Format line range for display.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ export const ContextConfigSchema = z.object({
|
|||||||
compressionMethod: z.enum(["llm-summary", "truncate"]).default("llm-summary"),
|
compressionMethod: z.enum(["llm-summary", "truncate"]).default("llm-summary"),
|
||||||
includeSignatures: z.boolean().default(true),
|
includeSignatures: z.boolean().default(true),
|
||||||
includeDepsGraph: z.boolean().default(true),
|
includeDepsGraph: z.boolean().default(true),
|
||||||
|
includeCircularDeps: z.boolean().default(true),
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
buildFileContext,
|
buildFileContext,
|
||||||
truncateContext,
|
truncateContext,
|
||||||
formatDependencyGraph,
|
formatDependencyGraph,
|
||||||
|
formatCircularDeps,
|
||||||
type ProjectStructure,
|
type ProjectStructure,
|
||||||
} from "../../../../src/infrastructure/llm/prompts.js"
|
} from "../../../../src/infrastructure/llm/prompts.js"
|
||||||
import type { FileAST } from "../../../../src/domain/value-objects/FileAST.js"
|
import type { FileAST } from "../../../../src/domain/value-objects/FileAST.js"
|
||||||
@@ -2092,7 +2093,12 @@ describe("prompts", () => {
|
|||||||
[
|
[
|
||||||
"src/services/user.ts",
|
"src/services/user.ts",
|
||||||
{
|
{
|
||||||
complexity: { loc: 80, nesting: 3, cyclomaticComplexity: 10, score: 50 },
|
complexity: {
|
||||||
|
loc: 80,
|
||||||
|
nesting: 3,
|
||||||
|
cyclomaticComplexity: 10,
|
||||||
|
score: 50,
|
||||||
|
},
|
||||||
dependencies: ["src/types/user.ts", "src/utils/validation.ts"],
|
dependencies: ["src/types/user.ts", "src/utils/validation.ts"],
|
||||||
dependents: ["src/controllers/user.ts", "src/api/routes.ts"],
|
dependents: ["src/controllers/user.ts", "src/api/routes.ts"],
|
||||||
isHub: false,
|
isHub: false,
|
||||||
@@ -2117,15 +2123,7 @@ describe("prompts", () => {
|
|||||||
{
|
{
|
||||||
complexity: { loc: 30, nesting: 1, cyclomaticComplexity: 3, score: 20 },
|
complexity: { loc: 30, nesting: 1, cyclomaticComplexity: 3, score: 20 },
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
dependents: [
|
dependents: ["a.ts", "b.ts", "c.ts", "d.ts", "e.ts", "f.ts", "g.ts"],
|
||||||
"a.ts",
|
|
||||||
"b.ts",
|
|
||||||
"c.ts",
|
|
||||||
"d.ts",
|
|
||||||
"e.ts",
|
|
||||||
"f.ts",
|
|
||||||
"g.ts",
|
|
||||||
],
|
|
||||||
isHub: true,
|
isHub: true,
|
||||||
isEntryPoint: false,
|
isEntryPoint: false,
|
||||||
fileType: "source",
|
fileType: "source",
|
||||||
@@ -2396,4 +2394,230 @@ describe("prompts", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("circular dependencies (0.28.0)", () => {
|
||||||
|
describe("formatCircularDeps", () => {
|
||||||
|
it("should return null for empty array", () => {
|
||||||
|
const result = formatCircularDeps([])
|
||||||
|
|
||||||
|
expect(result).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return null for undefined", () => {
|
||||||
|
const result = formatCircularDeps(undefined as unknown as string[][])
|
||||||
|
|
||||||
|
expect(result).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should format a simple two-node cycle", () => {
|
||||||
|
const cycles = [["src/a.ts", "src/b.ts", "src/a.ts"]]
|
||||||
|
|
||||||
|
const result = formatCircularDeps(cycles)
|
||||||
|
|
||||||
|
expect(result).toContain("## ⚠️ Circular Dependencies")
|
||||||
|
expect(result).toContain("- a → b → a")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should format a three-node cycle", () => {
|
||||||
|
const cycles = [
|
||||||
|
["src/services/user.ts", "src/services/auth.ts", "src/services/user.ts"],
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = formatCircularDeps(cycles)
|
||||||
|
|
||||||
|
expect(result).toContain("## ⚠️ Circular Dependencies")
|
||||||
|
expect(result).toContain("- services/user → services/auth → services/user")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should format multiple cycles", () => {
|
||||||
|
const cycles = [
|
||||||
|
["src/a.ts", "src/b.ts", "src/a.ts"],
|
||||||
|
["src/utils/x.ts", "src/utils/y.ts", "src/utils/z.ts", "src/utils/x.ts"],
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = formatCircularDeps(cycles)
|
||||||
|
|
||||||
|
expect(result).toContain("## ⚠️ Circular Dependencies")
|
||||||
|
expect(result).toContain("- a → b → a")
|
||||||
|
expect(result).toContain("- utils/x → utils/y → utils/z → utils/x")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should shorten paths (remove src/ prefix)", () => {
|
||||||
|
const cycles = [
|
||||||
|
["src/services/user.ts", "src/types/user.ts", "src/services/user.ts"],
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = formatCircularDeps(cycles)
|
||||||
|
|
||||||
|
expect(result).not.toContain("src/")
|
||||||
|
expect(result).toContain("services/user → types/user → services/user")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should remove file extensions", () => {
|
||||||
|
const cycles = [["lib/a.ts", "lib/b.tsx", "lib/c.js", "lib/a.ts"]]
|
||||||
|
|
||||||
|
const result = formatCircularDeps(cycles)
|
||||||
|
|
||||||
|
expect(result).not.toContain(".ts")
|
||||||
|
expect(result).not.toContain(".tsx")
|
||||||
|
expect(result).not.toContain(".js")
|
||||||
|
expect(result).toContain("lib/a → lib/b → lib/c → lib/a")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should remove /index suffix", () => {
|
||||||
|
const cycles = [
|
||||||
|
["src/components/index.ts", "src/utils/index.ts", "src/components/index.ts"],
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = formatCircularDeps(cycles)
|
||||||
|
|
||||||
|
expect(result).not.toContain("/index")
|
||||||
|
expect(result).toContain("components → utils → components")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should skip empty cycles", () => {
|
||||||
|
const cycles = [[], ["src/a.ts", "src/b.ts", "src/a.ts"], []]
|
||||||
|
|
||||||
|
const result = formatCircularDeps(cycles)
|
||||||
|
|
||||||
|
expect(result).toContain("- a → b → a")
|
||||||
|
const lines = result!.split("\n").filter((l) => l.startsWith("- "))
|
||||||
|
expect(lines).toHaveLength(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return null if all cycles are empty", () => {
|
||||||
|
const cycles = [[], [], []]
|
||||||
|
|
||||||
|
const result = formatCircularDeps(cycles)
|
||||||
|
|
||||||
|
expect(result).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should format self-referencing cycle", () => {
|
||||||
|
const cycles = [["src/self.ts", "src/self.ts"]]
|
||||||
|
|
||||||
|
const result = formatCircularDeps(cycles)
|
||||||
|
|
||||||
|
expect(result).toContain("- self → self")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle long cycles", () => {
|
||||||
|
const cycles = [
|
||||||
|
[
|
||||||
|
"src/a.ts",
|
||||||
|
"src/b.ts",
|
||||||
|
"src/c.ts",
|
||||||
|
"src/d.ts",
|
||||||
|
"src/e.ts",
|
||||||
|
"src/f.ts",
|
||||||
|
"src/a.ts",
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = formatCircularDeps(cycles)
|
||||||
|
|
||||||
|
expect(result).toContain("- a → b → c → d → e → f → a")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("buildInitialContext with includeCircularDeps", () => {
|
||||||
|
const structure: ProjectStructure = {
|
||||||
|
name: "test-project",
|
||||||
|
rootPath: "/test",
|
||||||
|
files: ["src/index.ts"],
|
||||||
|
directories: ["src"],
|
||||||
|
}
|
||||||
|
|
||||||
|
const asts = new Map<string, FileAST>([
|
||||||
|
[
|
||||||
|
"src/index.ts",
|
||||||
|
{
|
||||||
|
imports: [],
|
||||||
|
exports: [],
|
||||||
|
functions: [],
|
||||||
|
classes: [],
|
||||||
|
interfaces: [],
|
||||||
|
typeAliases: [],
|
||||||
|
parseError: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
|
||||||
|
it("should include circular deps when circularDeps provided", () => {
|
||||||
|
const circularDeps = [["src/a.ts", "src/b.ts", "src/a.ts"]]
|
||||||
|
|
||||||
|
const context = buildInitialContext(structure, asts, undefined, {
|
||||||
|
circularDeps,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(context).toContain("## ⚠️ Circular Dependencies")
|
||||||
|
expect(context).toContain("- a → b → a")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not include circular deps when includeCircularDeps is false", () => {
|
||||||
|
const circularDeps = [["src/a.ts", "src/b.ts", "src/a.ts"]]
|
||||||
|
|
||||||
|
const context = buildInitialContext(structure, asts, undefined, {
|
||||||
|
circularDeps,
|
||||||
|
includeCircularDeps: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(context).not.toContain("## ⚠️ Circular Dependencies")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not include circular deps when circularDeps is empty", () => {
|
||||||
|
const context = buildInitialContext(structure, asts, undefined, {
|
||||||
|
circularDeps: [],
|
||||||
|
includeCircularDeps: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(context).not.toContain("## ⚠️ Circular Dependencies")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not include circular deps when circularDeps is undefined", () => {
|
||||||
|
const context = buildInitialContext(structure, asts, undefined, {
|
||||||
|
includeCircularDeps: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(context).not.toContain("## ⚠️ Circular Dependencies")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should include circular deps by default when circularDeps provided", () => {
|
||||||
|
const circularDeps = [["src/x.ts", "src/y.ts", "src/x.ts"]]
|
||||||
|
|
||||||
|
const context = buildInitialContext(structure, asts, undefined, {
|
||||||
|
circularDeps,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(context).toContain("## ⚠️ Circular Dependencies")
|
||||||
|
expect(context).toContain("- x → y → x")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should include both dependency graph and circular deps", () => {
|
||||||
|
const metas = new Map<string, FileMeta>([
|
||||||
|
[
|
||||||
|
"src/index.ts",
|
||||||
|
{
|
||||||
|
complexity: { loc: 10, nesting: 1, cyclomaticComplexity: 1, score: 10 },
|
||||||
|
dependencies: ["src/utils.ts"],
|
||||||
|
dependents: [],
|
||||||
|
isHub: false,
|
||||||
|
isEntryPoint: true,
|
||||||
|
fileType: "source",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
const circularDeps = [["src/a.ts", "src/b.ts", "src/a.ts"]]
|
||||||
|
|
||||||
|
const context = buildInitialContext(structure, asts, metas, {
|
||||||
|
circularDeps,
|
||||||
|
includeDepsGraph: true,
|
||||||
|
includeCircularDeps: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(context).toContain("## Dependency Graph")
|
||||||
|
expect(context).toContain("## ⚠️ Circular Dependencies")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ describe("ContextConfigSchema", () => {
|
|||||||
compressionMethod: "llm-summary",
|
compressionMethod: "llm-summary",
|
||||||
includeSignatures: true,
|
includeSignatures: true,
|
||||||
includeDepsGraph: true,
|
includeDepsGraph: true,
|
||||||
|
includeCircularDeps: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ describe("ContextConfigSchema", () => {
|
|||||||
compressionMethod: "llm-summary",
|
compressionMethod: "llm-summary",
|
||||||
includeSignatures: true,
|
includeSignatures: true,
|
||||||
includeDepsGraph: true,
|
includeDepsGraph: true,
|
||||||
|
includeCircularDeps: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -168,6 +170,7 @@ describe("ContextConfigSchema", () => {
|
|||||||
compressionMethod: "llm-summary",
|
compressionMethod: "llm-summary",
|
||||||
includeSignatures: true,
|
includeSignatures: true,
|
||||||
includeDepsGraph: true,
|
includeDepsGraph: true,
|
||||||
|
includeCircularDeps: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -183,6 +186,7 @@ describe("ContextConfigSchema", () => {
|
|||||||
compressionMethod: "llm-summary",
|
compressionMethod: "llm-summary",
|
||||||
includeSignatures: true,
|
includeSignatures: true,
|
||||||
includeDepsGraph: true,
|
includeDepsGraph: true,
|
||||||
|
includeCircularDeps: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -199,6 +203,7 @@ describe("ContextConfigSchema", () => {
|
|||||||
compressionMethod: "truncate",
|
compressionMethod: "truncate",
|
||||||
includeSignatures: true,
|
includeSignatures: true,
|
||||||
includeDepsGraph: true,
|
includeDepsGraph: true,
|
||||||
|
includeCircularDeps: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -212,6 +217,7 @@ describe("ContextConfigSchema", () => {
|
|||||||
compressionMethod: "truncate" as const,
|
compressionMethod: "truncate" as const,
|
||||||
includeSignatures: false,
|
includeSignatures: false,
|
||||||
includeDepsGraph: false,
|
includeDepsGraph: false,
|
||||||
|
includeCircularDeps: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = ContextConfigSchema.parse(config)
|
const result = ContextConfigSchema.parse(config)
|
||||||
@@ -226,6 +232,7 @@ describe("ContextConfigSchema", () => {
|
|||||||
compressionMethod: "llm-summary" as const,
|
compressionMethod: "llm-summary" as const,
|
||||||
includeSignatures: true,
|
includeSignatures: true,
|
||||||
includeDepsGraph: true,
|
includeDepsGraph: true,
|
||||||
|
includeCircularDeps: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = ContextConfigSchema.parse(config)
|
const result = ContextConfigSchema.parse(config)
|
||||||
@@ -282,4 +289,29 @@ describe("ContextConfigSchema", () => {
|
|||||||
expect(() => ContextConfigSchema.parse({ includeDepsGraph: 1 })).toThrow()
|
expect(() => ContextConfigSchema.parse({ includeDepsGraph: 1 })).toThrow()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("includeCircularDeps", () => {
|
||||||
|
it("should accept true", () => {
|
||||||
|
const result = ContextConfigSchema.parse({ includeCircularDeps: true })
|
||||||
|
expect(result.includeCircularDeps).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should accept false", () => {
|
||||||
|
const result = ContextConfigSchema.parse({ includeCircularDeps: false })
|
||||||
|
expect(result.includeCircularDeps).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should default to true", () => {
|
||||||
|
const result = ContextConfigSchema.parse({})
|
||||||
|
expect(result.includeCircularDeps).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should reject non-boolean", () => {
|
||||||
|
expect(() => ContextConfigSchema.parse({ includeCircularDeps: "true" })).toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should reject number", () => {
|
||||||
|
expect(() => ContextConfigSchema.parse({ includeCircularDeps: 1 })).toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user