Problem
Two clusters of near-identical classes, confirmed by jscpd.
Cluster 1 — install use-cases
src/application/use-cases/install/install-agents-use-case.ts, install-rules-use-case.ts, install-skills-use-case.ts, install-commands-use-case.ts (325 lines combined) implement the same walk-filter-transform pipeline over contentFiles, differing only in which capability they read and one extra argument agents' convertFrontmatter takes.
Cluster 2 — capability classes
src/domain/capabilities/agents-capability.ts, commands-capability.ts, rules-capability.ts, skills-capability.ts duplicate the same 8-method surface (buildOutputPath, buildInstallPath, convertFrontmatter, reverseConvertFrontmatter, acceptsFileName, serialize, accepts, equals).
Why this isn't just cosmetic
The capability duplication has already drifted: AgentsCapability.acceptsFileName(fileName, allToolSuffixes) (agents-capability.ts:91) takes an externally supplied suffix list, while the other three compute ALL_TOOL_SUFFIXES internally at module scope for the same behavior — the same contract implemented two incompatible ways. The next person to touch one capability class has to know to check the other three.
Fix
- Extract one generic
InstallContentUseCase<C> parameterized by a capability accessor; keep the one real behavioral difference (agents passing relativeFileName) as an options flag.
- Factor the capability classes' common surface into a shared base or composed helpers; keep only true per-capability differences (agents' toml/markdown format, skills'
prefix mode) as overrides/options, fixing the acceptsFileName signature drift as part of the same change.
Acceptance criteria
Found by a code-quality audit of cli/ — full report: cli/aidd_docs/tasks/2026_07/2026_07_22_audit/code-quality.md.
Problem
Two clusters of near-identical classes, confirmed by
jscpd.Cluster 1 — install use-cases
src/application/use-cases/install/install-agents-use-case.ts,install-rules-use-case.ts,install-skills-use-case.ts,install-commands-use-case.ts(325 lines combined) implement the same walk-filter-transform pipeline overcontentFiles, differing only in which capability they read and one extra argument agents'convertFrontmattertakes.Cluster 2 — capability classes
src/domain/capabilities/agents-capability.ts,commands-capability.ts,rules-capability.ts,skills-capability.tsduplicate the same 8-method surface (buildOutputPath,buildInstallPath,convertFrontmatter,reverseConvertFrontmatter,acceptsFileName,serialize,accepts,equals).Why this isn't just cosmetic
The capability duplication has already drifted:
AgentsCapability.acceptsFileName(fileName, allToolSuffixes)(agents-capability.ts:91) takes an externally supplied suffix list, while the other three computeALL_TOOL_SUFFIXESinternally at module scope for the same behavior — the same contract implemented two incompatible ways. The next person to touch one capability class has to know to check the other three.Fix
InstallContentUseCase<C>parameterized by a capability accessor; keep the one real behavioral difference (agents passingrelativeFileName) as an options flag.prefixmode) as overrides/options, fixing theacceptsFileNamesignature drift as part of the same change.Acceptance criteria
acceptsFileName's signature is consistent across all fourpnpm jscpdno longer flags these two clustersFound by a code-quality audit of
cli/— full report:cli/aidd_docs/tasks/2026_07/2026_07_22_audit/code-quality.md.