[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-105275":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":8,"htmlUrl":8,"language":9,"languages":8,"totalLinesOfCode":8,"stars":10,"forks":11,"watchers":12,"openIssues":13,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":15,"stars7d":15,"stars30d":15,"stars90d":14,"forks30d":14,"starsTrendScore":13,"compositeScore":16,"rankGlobal":8,"rankLanguage":8,"license":17,"archived":18,"fork":18,"defaultBranch":19,"hasWiki":20,"hasPages":18,"topics":21,"createdAt":8,"pushedAt":8,"updatedAt":22,"readmeContent":23,"aiSummary":8,"trendingCount":14,"starSnapshotCount":14,"syncStatus":15,"lastSyncTime":24,"discoverSource":25},105275,"dryer","unclebob\u002Fdryer","unclebob",null,"Python",71,37,1,6,0,2,42.76,"BSD Zero Clause License",false,"master",true,[],"2026-11-01 00:13:14","# dryer\n\ndryer finds candidate duplicate code in Clojure, Java, Go, TypeScript, Rust, and Python. One run detects the language of each source file, normalizes that language's forms, and reports pairs whose structure is close enough to review.\n\nClojure follows [dry4clj](https:\u002F\u002Fgithub.com\u002Funclebob\u002Fdry4clj). The other languages use the same idea on the functions and methods [crapper](https:\u002F\u002Fgithub.com\u002Funclebob\u002Fcrapper) and [mutator](https:\u002F\u002Fgithub.com\u002Funclebob\u002Fmutator) already treat as one unit. A form is only compared with forms written in the same language.\n\nThe score is Jaccard similarity of structural fingerprints:\n\n```text\nscore = shared fingerprints \u002F all fingerprints seen in either form\n```\n\nA score of `1.0` means the normalized structures have the same fingerprint set. The default `--threshold 0.82` reports candidates close enough to be worth review.\n\nThese two functions score `1.0`. The names, locals, predicates, and mapped functions differ, and those incidental symbols normalize away. A default run still skips them: they are three lines, and the default minimum is four.\n\n```clojure\n(defn alpha [xs]\n  (let [ys (filter odd? xs)]\n    (map inc ys)))\n\n(defn beta [items]\n  (let [kept (filter even? items)]\n    (map dec kept)))\n```\n\nThe same shape in the other languages scores `1.0` too: the called function or method stays, and the locals do not.\n\n```java\nint alpha(int[] xs) {\n  int[] ys = filter(xs, odd);\n  return map(ys, inc);\n}\n```\n\n```python\ndef beta(items):\n    kept = filter(items, even)\n    return map(kept, dec)\n```\n\nWhat stays is the call or method name, the operator, and the shape of the tree. What drops out is the local name, the field name, the literal, and the keyword. A Clojure keyword call `(:kind m)` and `(:id row)` is the same shape. So is `order.total` and `row.amount`.\n\n## Run\n\n```bash\n.\u002Fdryer\n```\n\nThe first run creates `.venv` and installs the tool. From a project root it walks the tree, skips `test`, `spec`, `vendor`, `node_modules`, and `target`, and writes two results:\n\n- a report on stdout, closest match first\n- `.metrics\u002Fdry.edn`, replaced on every successful analysis\n\n```bash\n.\u002Fdryer src\u002Fdemo src\u002Fui          # these files and trees\n.\u002Fdryer --changed                # git additions and edits\n.\u002Fdryer billing                  # path fragment\n.\u002Fdryer --threshold 0.9 src\n.\u002Fdryer --edn --min-lines 3 src\n```\n\n```text\n--threshold N    Minimum structural similarity score, default 0.82\n--min-lines N    Minimum source lines in a candidate form, default 4\n--min-nodes N    Minimum normalized syntax nodes, default 20\n--format F       text or edn, default text\n--edn            Same as --format edn\n--text           Same as --format text\n```\n\nNormalized syntax nodes are the structural pieces left after incidental names and literals are replaced. `--min-nodes` drops forms smaller than that before comparison.\n\nDefault text output for the invoice and receipt from dry4clj. They differ by one extra binding:\n\n```text\nDUPLICATE score=0.89\n  src\u002Fbilling\u002Finvoice.clj:3-13\n  src\u002Fbilling\u002Freceipt.clj:3-14\n```\n\n`.metrics\u002Fdry.edn` is the same report dry4clj prints as EDN, with the language added:\n\n```clojure\n{:candidates\n [{:score 0.890909090909\n   :language \"clojure\"\n   :left {:file \"src\u002Fbilling\u002Finvoice.clj\", :start-line 3, :end-line 13}\n   :right {:file \"src\u002Fbilling\u002Freceipt.clj\", :start-line 3, :end-line 14}\n   :left-nodes 158\n   :right-nodes 166}]}\n```\n\n## What each language compares\n\n| Language | Candidate | Left out |\n| --- | --- | --- |\n| Clojure | every top-level form except `ns` | reader-conditional branches other than `:clj` |\n| Java | methods with a body | constructors, abstract methods, methods inside anonymous or local classes |\n| Go | functions and methods with a body | function literals stay inside the enclosing function |\n| TypeScript | functions, methods, and top-level arrow functions | callbacks nested inside another function |\n| Rust | functions and methods with a body | functions inside `mod tests`, and closures |\n| Python | functions and methods | functions nested inside another function |\n\nClojure reader conditionals keep the `:clj` branch, as dry4clj does. Syntax-quote is kept as a `syntax-quote` form rather than expanded.\n\nA missing or unreadable Clojure form is reported on stderr. Forms read before it are still compared. The run exits `0` after a report, `1` on a usage error, and `2` on an unknown `--format`.\n\n## Development\n\n```bash\npython3 -m venv .venv\n.venv\u002Fbin\u002Fpip install -e '.[dev]'\n.venv\u002Fbin\u002Fpytest\n```\n","2026-10-31 04:26:18","CREATED_QUERY"]