{
  "markdown": "# reuse-me\n\n**AI coding agents keep rebuilding the button you already have.**\n\nAsk an agent for a settings screen and it writes a button inline. Ask for a\ncheckout screen and it writes that button again, slightly different. Nothing\nis broken, no test fails, and six months later a brand colour change is a\nforty-file archaeology exercise instead of one token edit.\n\nThis is a local, deterministic CLI that **stops** those re-implementations\nbefore they are written, and finds the ones already there — in any language,\nwith no model calls.\n\n## Before / after\n\nAn agent writes a third screen. The language changes nothing about the\nproblem:\n\n```tsx\n// src/screens/Profile.tsx\nexport function Profile() {\n  return (\n    <div>\n      <button className=\"rounded px-4 py-2\" style={{ color: \"#3B82F6\" }}>\n        Profile\n      </button>\n    </div>\n  );\n}\n```\n\n```kotlin\n// ui/screens/ProfileScreen.kt\n@Composable\nfun ProfileScreen() {\n    Column {\n        Button(onClick = {}) {\n            Text(text = \"Profile\", color = Color(0xFF3B82F6))\n            Spacer(modifier = Modifier.width(8.dp))\n        }\n    }\n}\n```\n\n`reuse-me --health` says the same thing about each:\n\n```\nCompeting implementation: src/screens/Login.tsx, src/screens/Profile.tsx,\n  src/screens/Signup.tsx -> src/components/PrimaryButton.tsx (PrimaryButton),\n  confidence 0.8\n\nCompeting implementation: ui/screens/LoginScreen.kt, ui/screens/ProfileScreen.kt,\n  ui/screens/SignupScreen.kt -> ui/components/PrimaryButton.kt (PrimaryButton),\n  confidence 0.8\n```\n\nOne binary, no plugins, no per-language configuration. The same run finds the\nsame story in Swift, Dart, Java, C#, Go, Python and Vue.\n\n## Prevention, not just diagnosis\n\nFinding the third copy of a button is losing more slowly. The point is that\nthe third copy never gets written.\n\n**Before the agent writes**, a `PreToolUse` hook tells it what already exists:\n\n```\n$ reuse-me --inventory\nShared components — reuse these instead of re-implementing:\n  Button — src/components/Button.tsx (3 references)\n\nDesign tokens — reference these instead of hardcoding values:\n  brand-primary = #3b82f6 — src/styles/variables.css\n```\n\n**After it writes**, a `PostToolUse` hook checks the file and blocks on drift:\n\n```\n$ reuse-me --check src/screens/Login.tsx\nSource-of-truth warning: src/screens/Login.tsx ->\n  src/components/Button.tsx (Button), confidence 1\n$ echo $?\n2\n```\n\n`--check` works on a *single* file, unlike `--diff`, which needs two files\nrepeating each other before it says anything. Agents write one file at a time,\nso a check that waits for the second copy arrives one duplication too late.\n\nBoth hooks are in [`hooks/`](hooks). Start in advisory mode\n(`REUSE_ME_ADVISORY=1`) before letting them block. Full setup and\nlimits: [docs/prevention.md](docs/prevention.md).\n\n## The numbers\n\nFrom `npm run benchmark`, against the corpus in [`examples/`](examples):\n\n| Language | Expected | Result | Correct |\n| --- | --- | --- | --- |\n| Kotlin / Jetpack Compose | drift | drift reported | yes |\n| Swift / SwiftUI | drift | drift reported | yes |\n| Dart / Flutter | drift | drift reported | yes |\n| TypeScript / React | drift | drift reported | yes |\n| Kotlin (clean control) | clean | silent | yes |\n\n**4/4 languages detected. 0 false positives on the clean control. ~175ms per\nrepository.**\n\n### Context saved\n\nFrom `npm run benchmark:context`, against real public repositories pinned to\ntags in [`benchmarks/corpus.json`](benchmarks/corpus.json):\n\n| Repository | Language | Files | Source | Report | Ratio |\n| --- | --- | ---: | ---: | ---: | ---: |\n| preact `10.25.4` | TypeScript / JavaScript | 238 | 1267K | 15K | 87.2x |\n| vue-core `v3.5.13` | TypeScript | 525 | 3898K | 26K | 149.6x |\n| requests `v2.32.3` | Python | 36 | 368K | 10K | 38.0x |\n| okhttp `5.0.0-alpha.14` | Kotlin / Java | 549 | 4017K | 33K | 120.9x |\n| swift-composable-architecture `1.17.1` | Swift | 820 | 2273K | 24K | 94.7x |\n\n**Median 94.7x smaller across 5 repositories (range 38x–150x)**, 11.8MB of\nsource summarised into 107K, under a second each.\n\nThe denominator is every source file the analyzer can read — what an agent\nwould otherwise open to answer the questions the report answers. READMEs,\nlockfiles and binaries are excluded from both sides.\n\nOn the toy examples above, the report is *larger* than the source it\ndescribes. The ratio only becomes favourable at real size, and the benchmark\nprints both rather than quoting the flattering one.\n\n## Why it works in any language\n\nComponent drift is a shape problem, not a syntax problem. Two signals recover\nthat shape without a per-language parser:\n\n- **Constructed symbols** — Compose `Column(`, SwiftUI `VStack {`, Flutter\n  `Container(`, JSX `<button>`. A capitalised identifier applied to arguments\n  or a trailing block is the cross-language spelling of \"builds a component\".\n- **Colours and dimensions** — `#3B82F6`, `0xFF3B82F6`, `16dp`, `1.5rem`.\n  Identical in Kotlin, Swift, Dart, CSS and TypeScript, and exactly the values\n  a design token should own.\n\nFiles that share a structural signature form a pattern. A pattern is matched\nagainst shared components by **containment**, not similarity: the question is\nwhether the local code *contains* everything the shared component is, not\nwhether the two are the same size. A screen that wraps a copied button in a\ncard still contains the button.\n\nRoles come from the import graph and from directory names matched anywhere in\na path — `ui/components`, `lib/widgets`, `Sources/DesignSystem`, a top-level\n`components/`. Not from a hardcoded `src/components` prefix.\n\nReferences are not read from import statements alone. JavaScript, Python and\nRuby require an import for every use, so an import-only graph describes them\ncompletely; Kotlin, Java, C#, Scala, Go and Swift do not, and a file using its\nneighbour in the same package writes no statement at all. Those references are\ncounted from the scope itself, which is the difference between \"this component\nhas no callers\" and \"this analyzer speaks JavaScript\". Imports of frameworks\nand standard libraries are classed as external rather than unresolved, so a\nrepository is never judged on how many `androidx` or `react` names it could\nnot find inside itself.\n\n## The other half: tokens\n\nA duplicated button matters less if its colour was hardcoded in all four\nplaces to begin with. The same run reports design values written as literals:\n\n```\nToken bypassed: #3b82f6 hardcoded in src/screens/Login.tsx,\n  src/screens/Profile.tsx, src/screens/Signup.tsx\n  but declared as --brand-primary\nToken bypassed: 0xff3b82f6 hardcoded in ui/screens/LoginScreen.kt,\n  ui/screens/ProfileScreen.kt, ui/screens/SignupScreen.kt\n  but declared as BrandPrimary\nToken candidate: 11px repeated in 14 files with no token declaring it\n```\n\n`bypassed` means the repository already names that value and the code wrote\nthe literal anyway — so changing the token will not change this file, which is\nexactly how a design system stops working. `candidate` means the value repeats\noften enough to deserve a name.\n\nToken declarations are read by convention from `tailwind.config.*`,\n`colors.xml`, `tokens.json`, CSS custom properties, SCSS variables, Compose\ntheme files and Swift colour extensions.\n\n## What it refuses to do\n\nThe tool reports what it cannot see rather than reporting nothing and letting\nyou infer health:\n\n```\n[none] Unused Abstraction Analysis: unavailable\nUnused Abstraction Analysis: only 9/133 imports resolved,\n  so zero references is not evidence of disuse\n```\n\nOn a Next.js project that imports through a path alias, almost nothing\nresolves, so every component looks dead. Saying \"no unused abstractions\" there\nwould be a lie. Silence and \"I cannot tell\" are different answers, and agents\ncalibrate on the difference.\n\n## Install\n\n```bash\nnpm install\nnpm run build\nnpm run dev -- --health          # whole repository\nnpm run dev -- --diff            # current working-tree changes\nnpm run dev -- --inventory       # what exists, for reuse\nnpm run dev -- --check src/screens/Login.tsx   # does this duplicate something?\nnpm run dev -- --health --json   # for agents\nnpm run dev -- --health --markdown\n```\n\n### As an agent skill\n\nThe analyzer stays one CLI; agent files only expose instructions or command\naliases. Adapters ship for Claude Code (`.claude-plugin/`, `commands/`,\n`skills/`), Codex, Gemini / Antigravity, GitHub Copilot and Copilot CLI,\nOpenCode, pi, Hermes, Devin, Cursor, Windsurf, Cline, Kiro, Swival, OpenClaw,\nand anything reading `AGENTS.md`.\n\n```bash\nexport REUSE_ME_BIN=/absolute/path/to/dist/cli.js\n```\n\nSee [docs/agent-portability.md](docs/agent-portability.md).\n\n## Configuration\n\nOptional `reuse-me.json`. Missing fields inherit defaults; unknown\nfields are rejected.\n\n```json\n{\n  \"sharedDirNames\": [\"components\", \"ui\", \"design-system\", \"widgets\"],\n  \"localDirNames\": [\"screens\", \"pages\", \"routes\", \"views\"],\n  \"sharedSourceDirs\": [\"src/design-system\"],\n  \"localSourceDirs\": [\"src/routes\"],\n  \"ignore\": [\"**/*.test.tsx\", \"**/*.stories.tsx\"],\n  \"warningThreshold\": 0.7\n}\n```\n\n`sharedDirNames` / `localDirNames` match a directory name anywhere in a path\nand are what makes the defaults work outside JavaScript. `sharedSourceDirs` /\n`localSourceDirs` are exact path prefixes for when you need to override.\n\n## Limits\n\n- Static syntax evidence only. Results are review prompts, not proof.\n- Detection is heuristic and tuned on planted duplication, not a survey of\n  real repositories. The corpus in `examples/` is small and hand-built.\n- Generic declaration intelligence is best-effort across common languages; a\n  framework-specific provider still reads more.\n- No model calls, no autofix, no PR comments.\n\n## Development\n\n```bash\nnpm test                   # 134 tests\nnpm run benchmark          # detection across the example corpus\nnpm run benchmark:context  # context saved on real repositories (needs network)\nnpx tsc --noEmit\n```\n\nDocumentation lives in [docs/](docs): [prevention.md](docs/prevention.md) for\nthe hooks, [agent-portability.md](docs/agent-portability.md) for the adapters.\n\n## License\n\nMIT\n",
  "bytes": 10083,
  "sha": "5c02fd0c99c7dff8e9ae5a8c559ff31c9eabd074a563b3cb5aaf2fa755849988",
  "repo_slug": "72f-studio/reuse-me",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_72f_studio_reuse_me_11130e72/readme"
}