You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Week’s special: burnt abstraction with a side of filesystem roulette 🤮. This repo is not beyond saving, but it is doing that adorable thing where every feature has three truth sources, four special cases, and a catch {} waiting behind the sofa with a knife.
I focused on the smells most likely to improve maintainability, robustness, security, performance, and developer sanity — because right now the codebase feels like it was assembled by a committee of caffeinated raccoons with write access. 💩
The supported-agent model is duplicated across docs, runtime registration, MCP path resolution, and skills routing, so adding one agent is a scavenger hunt instead of a change.
There is no canonical source of truth for agent metadata; there are just multiple hand-maintained lists pretending to be friends. That means renames, new agents, or capability changes have to land in several places, and drift is not a risk — it is the default operating mode.
This is how you get docs lying, CLI help aging badly, and one agent mysteriously supporting skills in one subsystem but not another. The repo is basically doing schema design by ritual sacrifice.
Introduce a single AgentDescriptor registry containing identifier, display name, rule outputs, MCP target, skills target, and capabilities.
Generate CLI help and README support tables from that registry instead of manually curating parallel universes.
Keep adapter classes for behavior only; move metadata out of switches and lists.
Symlink Safari Through the Entire Repo
Rule discovery happily follows symlinks and recursively scans the project tree with almost no guardrails, which is a delightful way to mix security bugs with performance cliffs.
readMarkdownFiles follows symlinked directories and files on purpose. Great: now a .ruler tree can pull content from outside the repo into agent prompts, and a symlink cycle can send traversal into the kind of recursive meltdown usually reserved for bad fantasy novels.
Nested .ruler discovery walks every non-hidden directory. Not “every relevant directory,” not “every safe directory,” just everything that isn’t dot-prefixed. Hello node_modules, build trash, vendored sludge, and accidental latency theater. 🤡
Do not follow symlinks by default; make that an explicit opt-in if someone truly craves danger.
Track visited realpaths/inodes to break cycles.
Enforce that resolved paths stay under projectRoot before reading anything.
Add ignore rules for heavyweight directories during nested discovery: node_modules, dist, build, coverage, vendor, .next, .turbo, etc.
Backup Guarantees Are Apparently Optional
The adapter contract says “support backups,” the base class implements it, and then several custom agents just freestyle direct writes like consequences are a myth.
The abstraction here is trying so hard to exist. IAgent exposes backup, AbstractAgent does the sane thing, and then custom adapters immediately go off-road with raw fs.writeFile calls and custom merge logic. Consistency has left the building.
That means backup semantics vary by agent, idempotency varies by agent, and revert safety varies by agent. So the user experience depends on which mascot you picked today. Magnificent engineering discipline, no notes. 🙄
constfinalConfig={ ...updatedConfig};// @iarna/toml should handle the formatting properlyconsttomlContent=stringify(finalConfig);awaitwriteGeneratedFile(configPath,tomlContent);
Suggestions
Introduce shared writeManagedTextFile, writeManagedJsonFile, and writeManagedTomlFile helpers that always do ensure-dir, diff, backup, and atomic write.
Push merge logic into helpers too, so adapters provide transforms instead of bespoke file surgery.
Add parameterized adapter-contract tests that verify backup/idempotency/revert behavior for every agent.
Corruption Gets Rewarded With Silent Defaults
When existing config is broken, this codebase repeatedly shrugs, invents an empty object, and marches on toward data loss with enormous confidence.
Missing files and invalid files are treated like the same problem in too many places. They are not. One means “nothing exists yet”; the other means “something exists and you’re about to bulldoze it.”
A config tool should fail loudly on parse corruption, preserve the bad file, and tell the user what to fix. This one often does the software equivalent of whistling while backing over a mailbox.
}catch(err){if(errinstanceofError&&(errasErrnoException).code!=='ENOENT'){if(err.message.includes('[ruler]')){throwerr;// Re-throw validation errors}console.warn(`[ruler] Warning: could not read config file at ${configFile}: ${err.message}`,);}raw={};}
Treat only ENOENT as “empty”; abort on parse or schema errors.
Preserve invalid files untouched and report exact remediation steps.
Centralize config read/merge/write behavior so parse failure policy is consistent.
Consider a --repair or --force-overwrite path for intentional recovery, instead of stealth resets.
SkillsProcessor Is a Copy Paste Theme Park
SkillsProcessor.ts is a giant copy-paste blob where each new agent gets another near-identical cleanup block and another near-identical propagation function because apparently loops were feeling too mainstream.
This file is doing the same work over and over with different constants jammed into the template. That bloats the code, bloats review burden, and makes “add one skills target” way more dangerous than it should be.
The repeated try/access/rm/copy/rename/catch pattern is exactly the kind of duplication that breeds one-off bugs and inconsistent behavior. It’s not explicitness; it’s clerical work wearing a fake mustache.
Replace per-agent skill functions with a SkillTargetDescriptor[] and two generic helpers: one for cleanup, one for propagate.
Keep the atomic replace logic in one place.
Derive skill targets from the same central agent registry instead of hard-coding yet another mapping.
Add table-driven tests over descriptors, not copy-pasted test cases for each target.
Fuzzy Agent Matching Is a CLI Booby Trap
The CLI and config loader accept case-insensitive substrings for agent names, which is cute until half the product line contains the word “code” and your selection logic turns into improv theatre.
The public API documents substring matching, and both selection and config normalization implement it literally. That means ambiguity is baked in as a feature, not treated as an error.
In a repo with Claude Code, Codex, AugmentCode, Kilo Code, OpenCode, Qwen Code, and RooCode, substring matching is not user-friendly — it’s a randomizer with better branding.
tests/unit/core/agent-selection.test.ts lines 143-165 — link
it('should handle partial name matches in CLI agents',()=>{constconfig: LoadedConfig={cliAgents: ['code'],// Should match "Claude Code"agentConfigs: {},};
...
});it('should handle partial name matches in default_agents',()=>{constconfig: LoadedConfig={defaultAgents: ['code'],// Should match "Claude Code"
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
This Codebase Smells!
Week’s special: burnt abstraction with a side of filesystem roulette 🤮. This repo is not beyond saving, but it is doing that adorable thing where every feature has three truth sources, four special cases, and a
catch {}waiting behind the sofa with a knife.I focused on the smells most likely to improve maintainability, robustness, security, performance, and developer sanity — because right now the codebase feels like it was assembled by a committee of caffeinated raccoons with write access. 💩
Table of Contents
One Registry to Confuse Them All
The supported-agent model is duplicated across docs, runtime registration, MCP path resolution, and skills routing, so adding one agent is a scavenger hunt instead of a change.
Relevant files
README.mdsrc/agents/index.tssrc/paths/mcp.tssrc/core/SkillsProcessor.tsQuotes
README.mdlines 57-63 — linksrc/agents/index.tslines 38-45 — linksrc/paths/mcp.tslines 10-18 — linksrc/core/SkillsProcessor.tslines 140-148 — linkSuggestions
AgentDescriptorregistry containing identifier, display name, rule outputs, MCP target, skills target, and capabilities.Symlink Safari Through the Entire Repo
Rule discovery happily follows symlinks and recursively scans the project tree with almost no guardrails, which is a delightful way to mix security bugs with performance cliffs.
readMarkdownFilesfollows symlinked directories and files on purpose. Great: now a.rulertree can pull content from outside the repo into agent prompts, and a symlink cycle can send traversal into the kind of recursive meltdown usually reserved for bad fantasy novels..rulerdiscovery walks every non-hidden directory. Not “every relevant directory,” not “every safe directory,” just everything that isn’t dot-prefixed. Hellonode_modules, build trash, vendored sludge, and accidental latency theater. 🤡Relevant files
src/core/FileSystemUtils.tstests/unit/core/FileSystemUtils.test.tsQuotes
src/core/FileSystemUtils.tslines 74-82 — linksrc/core/FileSystemUtils.tslines 86-95 — linksrc/core/FileSystemUtils.tslines 240-256 — linktests/unit/core/FileSystemUtils.test.tslines 65-78 — linkSuggestions
projectRootbefore reading anything.node_modules,dist,build,coverage,vendor,.next,.turbo, etc.Backup Guarantees Are Apparently Optional
The adapter contract says “support backups,” the base class implements it, and then several custom agents just freestyle direct writes like consequences are a myth.
IAgentexposesbackup,AbstractAgentdoes the sane thing, and then custom adapters immediately go off-road with rawfs.writeFilecalls and custom merge logic. Consistency has left the building.Relevant files
src/agents/IAgent.tssrc/agents/AbstractAgent.tssrc/agents/OpenCodeAgent.tssrc/agents/CodexCliAgent.tssrc/agents/GeminiCliAgent.tssrc/agents/QwenCodeAgent.tssrc/agents/ZedAgent.tsQuotes
src/agents/IAgent.tslines 37-43 — linksrc/agents/AbstractAgent.tslines 44-51 — linksrc/agents/OpenCodeAgent.tslines 21-38 — linksrc/agents/OpenCodeAgent.tslines 71-72 — linksrc/agents/CodexCliAgent.tslines 44-50 — linksrc/agents/CodexCliAgent.tslines 145-151 — linkSuggestions
writeManagedTextFile,writeManagedJsonFile, andwriteManagedTomlFilehelpers that always do ensure-dir, diff, backup, and atomic write.Corruption Gets Rewarded With Silent Defaults
When existing config is broken, this codebase repeatedly shrugs, invents an empty object, and marches on toward data loss with enormous confidence.
Relevant files
src/core/ConfigLoader.tssrc/paths/mcp.tssrc/agents/OpenCodeAgent.tssrc/agents/FirebenderAgent.tssrc/agents/CrushAgent.tsQuotes
src/core/ConfigLoader.tslines 165-175 — linksrc/paths/mcp.tslines 80-89 — linksrc/agents/OpenCodeAgent.tslines 62-68 — linksrc/agents/FirebenderAgent.tslines 107-117 — linksrc/agents/CrushAgent.tslines 97-104 — linkSuggestions
ENOENTas “empty”; abort on parse or schema errors.--repairor--force-overwritepath for intentional recovery, instead of stealth resets.SkillsProcessor Is a Copy Paste Theme Park
SkillsProcessor.tsis a giant copy-paste blob where each new agent gets another near-identical cleanup block and another near-identical propagation function because apparently loops were feeling too mainstream.try/access/rm/copy/rename/catchpattern is exactly the kind of duplication that breeds one-off bugs and inconsistent behavior. It’s not explicitness; it’s clerical work wearing a fake mustache.Relevant files
src/core/SkillsProcessor.tsQuotes
src/core/SkillsProcessor.tslines 177-194 — linksrc/core/SkillsProcessor.tslines 683-722 — linksrc/core/SkillsProcessor.tslines 741-780 — linkSuggestions
SkillTargetDescriptor[]and two generic helpers: one for cleanup, one for propagate.Fuzzy Agent Matching Is a CLI Booby Trap
The CLI and config loader accept case-insensitive substrings for agent names, which is cute until half the product line contains the word “code” and your selection logic turns into improv theatre.
Claude Code,Codex,AugmentCode,Kilo Code,OpenCode,Qwen Code, andRooCode, substring matching is not user-friendly — it’s a randomizer with better branding.Relevant files
src/lib.tssrc/core/agent-selection.tssrc/core/config-utils.tstests/unit/core/agent-selection.test.tsREADME.mdQuotes
src/lib.tslines 41-44 — linksrc/core/agent-selection.tslines 31-49 — linksrc/core/config-utils.tslines 21-31 — linktests/unit/core/agent-selection.test.tslines 143-165 — linkREADME.mdlines 77-82 — linkSuggestions
All reactions