hermes

Untangle circular dependencies

Finds circular dependencies between modules and plans breaking each cycle with interfaces, inversion or extraction in safe steps. Use when import cycles cause build errors or tangled code.

context

A dependency cycle means two or more modules cannot be understood, tested, built or deployed apart. Cycles cause import-order bugs (a value undefined at load time), slow incremental builds and modules that can never be extracted. The fix is rarely "move the import inside the function"; that hides the cycle. The real fix depends on why the edge exists: a shared type that belongs lower down, a callback that should be inverted, a misplaced function, or two modules that are really one. The right break is the edge that is least essential, chosen so that dependencies point from volatile, high-level code toward stable, low-level code.

task

Analyse these dependencies:

dependency info

  1. List every cycle as a path (a → b → c → a). If the input is a large graph, list the strongly connected components and the shortest cycles inside each. If you can read the repository, confirm each edge by finding the import and what it uses; otherwise mark edges you could not confirm.
  2. For each edge in a cycle, record what crosses it: types only, a function call, a constant, a class to instantiate, a registry or event. Note whether the use is at load time (top-level) or at call time.
  3. Diagnose each cycle and pick a technique:
  • Move down: a shared type, constant or pure helper used by both belongs in a lower module (often a new types, contracts or shared module). Keep that module free of dependencies on its users.
  • Invert: the lower module calls back into the higher one. Define an interface or callback in the lower module and have the higher module provide the implementation (dependency injection, a port, an event).
  • Move the function: one function is in the wrong module; moving it removes the edge.
  • Merge: the modules change together and share invariants; merge them, then split along a better seam later if needed.
  • Extract: both depend on a cohesive piece that should become its own module. State why you chose the technique over the others, and which direction the dependency will point afterwards.
  1. Order the work so each step compiles, passes tests and could ship alone. Break the cheapest, most-shared edges first. For each step give the files touched, the change, and a small code sketch in the project's language for non-obvious moves.
  2. Propose a guardrail that fails CI if a cycle returns: a rule for the project's tool (dependency-cruiser no-circular, import-linter contracts, ArchUnit, eslint import/no-cycle, Go's compiler already forbids package cycles) or a layered-architecture rule that also fixes the intended direction.
constraints
  • Do not propose lazy or in-function imports, require inside functions, or forward-declaration tricks as the fix. Mention them only as a temporary unblocker, labelled as such.
  • Type-only imports (TypeScript import type, Python if TYPE_CHECKING:) are a legitimate fix when the edge carries types and nothing else: they remove the runtime cycle and its load-order bugs. Say that the design-level coupling remains, and whether the cycle tool will still report the edge (check its type-only setting).
  • Keep behaviour identical; this is a refactor. Flag any step that could change load order or initialisation side effects.
  • Do not rename or restructure beyond what breaking the cycles needs.
  • Base the analysis on the edges given or read; never invent modules or imports.
  • Do only what was asked. If you notice something else worth changing, mention it in one line at the end instead of changing it.
  • Keep the change as small as it can be while still being correct.
  • Separate what you verified from what you inferred. Mark inferences as such.
  • When you do not know, say "I don't know" once and state what would settle it.
output format

Cycles found

Numbered cycle paths, with what crosses each edge and whether it is load-time or call-time.

Diagnosis

Per cycle: the edge to break, the technique, why, and the dependency direction afterwards. Include a Mermaid graph LR showing before and after for the largest cycle.

Break plan

Numbered steps, each independently shippable: files, change, code sketch where needed, how to verify.

Guardrail

The CI rule or configuration, in a fenced block.

Questions

Anything you need to confirm, or "None".

1 required value still a placeholder; the assistant will ask for it.

details

kind
Prompt: a task you run by name to get one finished thing back
domain
Software engineering
category
Refactoring
level
Expert
made for
Software engineer, Tech lead / staff engineer, Software architect, Backend engineer
needs
repo-read
risk
read-only
version
v1.0.1 · incubating
reviewed
2026-10-03
works in
Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Antigravity, OpenCode, Windsurf, Zed, Continue, AGENTS.md

Edit on GitHubReport a problem

use in

Hodios CLI
npx @hermes-hq/hodios install untangle-circular-dependencies --target claude-code
Agent Skills
npx skills add hermes-hq/hodios-dist --skill untangle-circular-dependencies -a claude-code
Add the Hodios marketplace (once)
claude plugin marketplace add hermes-hq/hodios-dist
Install the software-engineering plugin
claude plugin install hodios-software-engineering@hodios

The plugin brings every entry in this domain at once.

pairs well with

All of Refactoring
PersonaArchitecture

Software architect

Acts as a pragmatic software architect who designs from requirements and constraints, names trade-offs and failure modes, and keeps designs as simple as the problem allows.

software-architect
PromptRefactoring

Extract a module

Moves one responsibility out of a large file or class into its own module in small, test-verified steps, without changing behaviour or the public API. Use when a file does too many things.

extract-module
PromptRefactoring

Plan splitting a large module

Maps the responsibilities and internal dependencies of an oversized file or class and plans its split into cohesive modules, in small steps that keep tests green. Use before breaking up a god class.

split-large-module
PromptRefactoring

Plan a large refactor in safe steps

Turns a large refactor into small, independently shippable steps that keep the build green, each with a rollback, using patterns like expand-contract. Use for refactors too big for one PR.

plan-large-refactor
PromptRefactoring

Improve naming in code

Proposes clearer names for variables, functions, types and modules, explains each rename and applies them without changing behaviour. Use when code reads poorly because of its names.

improve-naming
PromptRefactoring

Reduce code duplication

Finds duplicated logic, separates true duplication from code that only looks alike, and merges only true duplicates behind one well-named function. Use when one fix keeps landing in many places.

reduce-duplication