Skip to content

[Bug] dead-code, health, and workspace CLI commands report false-positive unreachable files for TS/JS projects using @/ path aliases #648

Description

@tingtingdisco

Describe the Bug

The dead-code, health, and workspace CLI commands rebuild the dependency graph in-memory without wiring up the
TsconfigResolver. As a result, TS/JS path aliases like @/components/Foo don't resolve to their target files
during the rebuild — they become external: nodes, so the real target files get in_degree = 0 and are falsely
flagged as unreachable.

The init pipeline does not have this bug: it runs a dedicated tsconfig phase that calls
graph_builder.set_tsconfig_resolver(...) before build()
(packages/core/src/repowise/core/pipeline/phases/ingestion.py:328-348). The CLI commands don't replicate this
wiring.

Steps to Reproduce

  1. In a TS project with @/*./src/* path alias in tsconfig.json, have src/routes/layout.tsx import
    Sensor from @/components/SensorProvider.
  2. Index the repo: repowise init . --no-workspace --index-only
  3. Run: repowise dead-code . --no-workspace --kind unreachable_file --format json

Expected Behavior

src/components/SensorProvider.tsx is not listed as unreachable (it's imported by src/routes/layout.tsx).

Actual Behavior

src/components/SensorProvider.tsx is reported as unreachable_file with "reason": "File has no importers (in_degree=0)", confidence: 0.7, safe_to_delete: true.

On a real 800-file TS project, 171 of 347 unreachable_file findings (49%) were false positives — every file
imported via @/ was misclassified.

The persisted graph in wiki.db is correct: graph_edges contains ('src/routes/layout.tsx', 'src/components/SensorProvider.tsx', 'imports'). The bug is only in the in-memory graph rebuild the CLI performs.

Affected files (all in packages/cli/src/repowise/cli/commands/):

  • dead_code_cmd.py (~line 143–156)
  • health_cmd.py (~line 189–210)
  • workspace_cmd.py (~line 681)

Environment

  • OS: macOS 15
  • Python version: 3.14.6
  • Repowise version: 0.25.0
  • Installation method: pipx

Additional Context

Root cause: TsconfigResolver is constructed and attached to GraphBuilder only in the init pipeline. The
resolver is read by resolve_ts_js_import
(packages/core/src/repowise/core/ingestion/resolvers/typescript.py:60-64) during build(). Without it attached,
ctx.tsconfig_resolver is None and non-relative imports fall through to add_external_node(module_path).

Suggested fix: mirror the pipeline's wiring in each CLI command before graph_builder.build(), or extract a shared
wire_tsconfig_resolver(graph_builder, repo_path, include_submodules, include_nested_repos) helper in
repowise.core.ingestion and call it from the pipeline and all three CLI commands.

Related: PR #71 introduced tsconfig alias resolution (pipeline-only); PRs #299 and #244 fixed the same class of
resolver-not-wired false-positive bug for CommonJS require() and Python imports respectively.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions