code.manage

Analysis, scaffolding, location, and refactoring tasks.

code.manage is the operational interface for maintaining this codebase. It wraps source analysis, grep-like location, usage discovery, test import, scaffolding, formatting, and structural refactors behind task-style commands.

1    Motivation

The codebase is large enough that plain text search is not always enough. code.manage understands namespaces, test files, fact metadata, and parsed Clojure blocks, making it suitable for documentation discovery and source maintenance.

2    Common workflows

Use locate-code to find structural forms, find-usages to identify where a var is referenced through aliases or direct names, import to pull examples from tests, and scaffold to create missing tests. For docs work, find-usages and locate-code should be used to collect the internal usage summaries on each page.

(code.manage/find-usages '[code]
                         {:var 'code.framework/analyse})
(code.manage/locate-code '[hara]
                         {:query ['l/script]})

3    Internal usage

The repository exposes code.manage through Leiningen tasks, JVM tool helpers, and MCP tools. It is also the safest way to gather examples for documentation without inventing usage narratives by hand.

4    Walkthrough

4.1    Analysing namespaces

code.manage tasks operate on namespaces, sets of namespaces, or :all. analyse returns structured metadata about source or test files, and vars lists the public vars.

analyse a single namespace

(manage/analyse 'code.manage)
=> code.framework.common.Entry

list vars in a namespace

(manage/vars 'code.manage)
=> vector?

4.2    Finding code

find-usages discovers where a var is referenced, while locate-code and grep search for structural patterns and raw text.

find usages of a var

(manage/find-usages 'code.manage
                    {:var 'code.framework/analyse
                     :print {:result false :summary false}})
=> map?

locate forms matching a structural query

(manage/locate-code 'code.manage
                    {:query '[ns | {:first :require}]
                     :print {:result false :summary false}})
=> seq?

grep for text in files

(manage/grep 'code.manage
             {:query "analyse"
              :print {:result false :summary false}})
=> seq?

4.3    Test hygiene

missing, orphaned, and incomplete help keep tests in sync with source code. They report source vars without tests, tests without source, and both problems together.

find source vars without tests

(manage/missing 'code.manage
                {:print {:result false :summary false}})
=> map?

find tests without corresponding source

(manage/orphaned 'code.manage
                 {:print {:result false :summary false}})
=> map?

4.4    End-to-end: preview a safe refactor

grep-replace performs text replacement across files. Set :write false to preview diffs before committing changes.

preview a no-op replacement

(manage/grep-replace 'code.manage
                     {:query "definvoke"
                      :replace "definvoke"
                      :write false
                      :print {:result false :summary false}})
=> (contains {:updated false})

5    API



+tasks+ ^

Added 4.1

registers isolate, snapto and factcheck tasks in the available manage tasks

v 4.1
(def +tasks+)
link
[(task/task? (-> +tasks+ :isolate)) (task/task? (-> +tasks+ :snapto)) (task/task? (-> +tasks+ :factcheck-remove)) (task/task? (-> +tasks+ :factcheck-generate))] => [true true true true]

-main ^

[& [cmd & args]]
Added 4.0

main entry point for code.manage

v 4.0
(defn -main
  [& [cmd & args]]
  (let [print-fn (fn []
                   (do (env/p "Available Tasks:")
                       (doseq [cmd  (map name (sort (keys +tasks+)))]
                         (env/p (str "  - " cmd)))))]
    (if (not cmd)
      (print-fn)
      
      (let [opts (task/process-ns-args (task/collapse-only args))
            func (ns-resolve (find-ns 'code.manage) (symbol cmd))
            args (mapv (fn [x] (try (read-string x) (catch Throwable _ x))) args)
            result (if func
                     (func (or (:ns opts) :all)
                           (merge {:print {:function true
                                           :summary true
                                           :result true
                                           :item true}}
                                  (dissoc opts :ns)))
                     (print-fn))]
        (if-not (get opts :no-exit)
          (System/exit (int (or (when (map? result) (:exit result)) 0))))))))
link
(env/with-out-str (code.manage/-main)) => string? (code.manage/-main "vars" ":with" "code.manage" ":no-exit" "true") => anything

analyse ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

analyse either a source or test file

v 3.0
(invoke/definvoke analyse
  [:task {:template :code
          :params  {:title "ANALYSE NAMESPACE"
                    :parallel true
                    :print {:result false :summary false}
                    :sorted true}
          :main    {:fn #'base/analyse}
          :item    {:display #(->> (vals %) (mapcat keys) sort vec)}
          :result  {:ignore  nil
                    :keys    {:count #(->> (vals %) (mapcat keys) count)
                              :functions #(->> (vals %) (mapcat keys) sort vec)}
                    :columns (template/code-default-columns :functions #{:bold})}}])
link
(analyse 'code.manage) ;;#code{:test {code.manage [analyse .... vars]}} => code.framework.common.Entry (analyse '#{code.manage} {:return :summary}) ;; {:errors 0, :warnings 0, :items 1, :results 1, :total 16} => map?

arrange ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

arranges the test corresponding to function order

v 3.0
(invoke/definvoke arrange
  [:task {:template :code.transform
          :params {:title "ARRANGE TESTS"
                   :parallel true
                   :write true}
          :main {:fn #'unit/arrange}
          :item {:list template/test-namespaces}
          :result (template/code-transform-result :changed)}])
link
(arrange {:print {:function false} :write false}) (arrange '[code.manage] {:print {:item true} :write false})

commented ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

returns tests that are in comment blocks

v 3.0
(invoke/definvoke commented
  [:task {:template :code
          :params {:title "REFERENCED VAR IN COMMENT FORM"
                   :parallel true}
          :main {:fn #'unit/commented}
          :item {:list template/source-namespaces}
          :result {:columns (template/code-info-columns #{:white :bold})}}])
link
(commented) (commented '[code.manage])

compare-columns ^

[orig compare]

NONE
(defn- compare-columns
  [orig compare]
  [{:key    :key
    :align  :left}
   {:key    :count
    :length 8
    :align  :center
    :color  #{:bold}}
   {:key    :test
    :align  :left
    :length 60
    :color  orig}
   {:key    :source
    :align  :left
    :length 60
    :color  compare}])
link

compare-status ^

[arr]

NONE
(defn- compare-status [arr]
  (cond (empty? arr)
        (res/result {:status :info
                     :data :ok})

        :else
        (res/result {:status :warn
                     :data :not-in-order})))
link

create-tests ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

creates and arranges the tests

v 3.0
(invoke/definvoke create-tests
  [:task {:template :code.transform
          :params {:title "CREATE TESTS"
                   :parallel true
                   :write true}
          :main {:fn #'unit/create-tests}
          :item {:list template/source-namespaces
                 :display (template/empty-result :new :info :no-new)}
          :result (template/code-transform-result :new)}])
link

docstrings ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

returns docstrings

v 3.0
(invoke/definvoke docstrings
  [:task {:template :code
          :params {:title "VAR DOCSTRINGS"
                   :parallel true
                   :print {:result false :summary false}}
          :main   {:fn #'base/docstrings}
          :item   {:display (comp (template/empty-status :info :none) vec keys)}
          :result {:keys  {:count (comp count keys)
                           :functions (comp vec keys)}
                   :columns (template/code-default-columns :functions #{:bold})}}])
link
(docstrings '#{code.manage.unit} {:return :results}) ;;{:errors 0, :warnings 0, :items 1, :results 1, :total 14} => map?

extract ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 4.0

returns the list of vars in a namespace

v 4.0
(invoke/definvoke extract
  [:task {:template :code
          :params {:title "PROCESS"
                   :parallel true
                   :sorted true
                   :process identity
                   :print {:result false :summary false}}
          :main {:fn #'base/extract}
          :item {:display identity}
          :result {:columns (template/code-default-columns :data #{:bold})}}])
link
(extract 'code.manage) => string?

factcheck-generate ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 4.1

regenerates `=>` expectations for fact tests

v 4.1
(invoke/definvoke factcheck-generate
  [:task {:template :code.transform
          :params {:title "GENERATE FACT CHECKS"
                   :parallel true
                   :write true}
          :main {:fn #'unit.factcheck/factcheck-generate}
          :item {:list template/test-namespaces}
          :result (template/code-transform-result :changed)}])
link
(task/task? factcheck-generate) => true (let [task (into {} factcheck-generate)] [(:template task) (-> task :params :title) (fn? (-> task :main :fn))]) => [:code.transform "GENERATE FACT CHECKS" true]

factcheck-remove ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 4.1

removes `=>` expectations from fact tests

v 4.1
(invoke/definvoke factcheck-remove
  [:task {:template :code.transform
          :params {:title "REMOVE FACT CHECKS"
                   :parallel true
                   :write true}
          :main {:fn #'unit.factcheck/factcheck-remove}
          :item {:list template/test-namespaces}
          :result (template/code-transform-result :changed)}])
link
(task/task? factcheck-remove) => true (let [task (into {} factcheck-remove)] [(:template task) (-> task :params :title) (fn? (-> task :main :fn))]) => [:code.transform "REMOVE FACT CHECKS" true] (factcheck-remove '[code.manage] {:write false}) => map?

find-usages ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

find usages of a var

v 3.0
(invoke/definvoke find-usages
  [:task {:template :code.locate
          :params {:title "ALL VAR USAGES"
                   :parallel true
                   :highlight? true}
          :main {:fn #'var/find-usages}}])
link
(find-usages '[code.manage] {:var 'code.framework/analyse}) => map?

grep ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

finds a string or regular expression in files

v 3.0
(invoke/definvoke grep
  [:task {:template :code.locate
          :params {:title "GREP"
                   :parallel true
                   :highlight true}
          :main {:fn #'base/grep-search}}])
link
(grep '[code.manage] {:query "hello"})

grep-replace ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

grep and replaces in files

v 3.0
(invoke/definvoke grep-replace
  [:task {:template :code.transform
          :params {:title "GREP REPLACE"
                   :parallel true
                   :print {:function true}}
          :main {:fn #'base/grep-replace}
          :result (template/code-transform-result :changed)}])
link
(grep-replace '[code.manage] {:query "hello" :replace "HELLO"})

heal-code ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 4.1

heal-code is a transform task for fixing code formatting

v 4.1
(invoke/definvoke heal-code
  [:task {:template :code.transform
          :params {:title "HEAL CODE"
                   :transform block/heal
                   :no-analysis true
                   :print {:function true :result true :summary true}
                   :parallel true}
          :main   {:fn #'base/transform-code}
          :result template/base-transform-result}])
link
(task/task? heal-code) => true

import ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

import docstrings from tests

v 3.0
(invoke/definvoke import
  [:task {:template :code.transform
          :main   {:fn #'unit.import/import}
          :params {:title "IMPORT DOCSTRINGS"
                   :parallel true
                   :write true}
          :item   {:list template/source-namespaces}
          :result (template/code-transform-result :changed)}])
link
(import {:write false}) (import {:full true :write false :print {:function false}}) (import '[code.manage.unit] {:print {:summary true :result true :item true} :write false})

in-order? ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

checks if tests are in order

v 3.0
(invoke/definvoke in-order?
  [:task {:template :code
          :params {:title "TESTS IN ORDER"
                   :parallel true}
          :main {:fn #'unit/in-order?}
          :item {:list template/source-namespaces
                 :display compare-status}
          :result {:keys {:count first
                          :source second
                          :test #(nth % 2)}
                   :columns (compare-columns #{:bold :cyan} #{:cyan})}}])
link
(in-order?) (in-order? '[code.manage] {:print {:item true}})

incomplete ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

both functions missing tests or tests with todos

v 3.0
(invoke/definvoke incomplete
  [:task {:template :code
          :params {:title "INCOMPLETE TESTS"
                   :parallel true
                   :print {:item false}}
          :main   {:fn #'unit/incomplete}
          :item   {:list template/source-namespaces}
          :result {:columns (template/code-default-columns #{:bold :green})}}])
link
(incomplete) (incomplete '[code.manage] {:print {:item true}})

incomplete-report ^

[target opts]
Added 4.1

delegates machine-readable reporting through the code.manage task surface

v 4.1
(defn incomplete-report
  [target opts]
  (automation/incomplete-report target opts))
link
(with-redefs [automation/incomplete-report (fn [target opts] {:target target :section (:section opts)})] (incomplete-report :all {:section :code})) => {:target :all :section :code}

isolate ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 4.1

builds the isolate task

v 4.1
(invoke/definvoke isolate
  [:task {:template :code.transform
          :params {:title "ISOLATE TESTS"
                   :parallel true
                   :write true}
          :main {:fn #'unit.isolate/isolate}
          :item {:list template/test-namespaces
                 :display (template/empty-result :functions :info :no-failures)}
          :result (template/code-transform-result :functions)}])
link
(let [task (into {} isolate)] [(:template task) (-> task :params :title) (fn? (-> task :main :fn))]) => [:code.transform "ISOLATE TESTS" true]

locate-code ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

locates code base upon query

v 3.0
(invoke/definvoke locate-code
  [:task {:template :code.locate
          :params {:title "LOCATE CODE"
                   :parallel true}
          :main {:fn #'base/locate-code}}])
link
(locate-code '[code.manage] {:query '[ns | {:first :import}]})

locate-test ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 4.0

locates test based upon query

v 4.0
(invoke/definvoke locate-test
  [:task {:template :code.locate
          :params {:title "LOCATE TEST"
                   :parallel true}
          :item {:list template/test-namespaces}
          :main {:fn #'base/locate-code}}])
link
(locate-test '[code.manage] {:query '[ns | {:first :import}]})

missing ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

checks for functions with missing tests

v 3.0
(invoke/definvoke missing
  [:task {:template :code
          :params {:title "MISSING TESTS"}
          :main {:fn #'unit/missing}
          :item {:list template/source-namespaces}
          :result {:columns (template/code-default-columns :data #{:green})}}])
link
(missing) (missing '[platform] {:print {:result false :summary false} :return :all})

ns-format ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

formats ns forms

v 3.0
(invoke/definvoke ns-format
  [:task {:template :code.transform
          :params {:title "FORMAT NS FORMS"
                   :parallel true
                   :print {:function true}}
          :main   {:fn #'ns-format/ns-format}
          :result template/base-transform-result}])
link

ns-rename ^

[[old new] {:keys [write print], :as params} lookup project]
Added 3.0

top-level ns rename function

v 3.0
(defn ns-rename
  ([[old new] {:keys [write print] :as params} lookup project]
   (let [changes (change-list [old new] params lookup project)
         moves   (move-list [old new] params lookup project)]
     {:changes changes
      :moves moves})))
link
(with-redefs [change-list (constantly []) move-list (constantly [])] (ns-rename ['old 'new] {} nil nil)) => {:changes [], :moves []}

orphaned ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

tests without corresponding source code

v 3.0
(invoke/definvoke orphaned
  [:task {:template :code
          :params {:title "ORPHANED TESTS"
                   :parallel true}
          :main {:fn #'unit/orphaned}
          :item {:list template/test-namespaces}
          :result {:columns (template/code-info-columns #{:bold :blue})}}])
link
(orphaned) (orphaned '[code.manage] {:print {:item true}})

pedantic ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

returns tests that may be improved

v 3.0
(invoke/definvoke pedantic
  [:task {:template :code
          :params {:title "FUNCTIONS THAT COULD BE IMPROVED UPON"
                   :parallel true}
          :main {:fn #'unit/pedantic}
          :item {:list template/source-namespaces}
          :result {:columns (template/code-default-columns
                             :data #{:warn :bold}
                             (fn [items]
                               (->> items
                                    (map (fn [sym]
                                           (let [{:keys [row tag]
                                                  :or {row 0}} (meta sym)]
                                             (if (= tag :N)
                                               [row sym]
                                               [row sym (symbol (name tag))]))))
                                    (sort-by first)
                                    (vec))))}}])
link
(pedantic) (pedantic '[code.manage])

purge ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

removes docstrings from source code

v 3.0
(invoke/definvoke purge
  [:task {:template :code.transform
          :main   {:fn #'unit/purge}
          :params {:title "PURGE DOCSTRINGS"
                   :parallel true}
          :item {:list template/source-namespaces}
          :result (assoc (template/code-transform-result :changed)
                         :columns (template/code-transform-columns #{:bold :red}))}])
link
(purge {:write false}) (purge {:full true :write false}) (purge '[platform.unit] {:return :summary :write false}) ;;{:items 38, :results 32, :deletes 1272, :total 169} => map?

refactor-code ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

refactors code based on given `:edits`

v 3.0
(invoke/definvoke refactor-code
  [:task {:template :code.transform
          :params {:title "REPLACE VAR USAGES"
                   :parallel true
                   :print {:function true}}
          :main   {:fn #'base/refactor-code}
          :result template/base-transform-result}])
link
(refactor-code '[code.manage] {:edits []})

refactor-swap ^

[ns params narrow update-fn]
Added 4.0

refactors by providing a list of symbols to swap

v 4.0
(defn refactor-swap
  [ns params narrow update-fn]
  (refactor-code
   ns
   (collection/merge-nested
    {:print {:function true}
     :edits [(fn [nav]
               (code.query/modify
                nav
                [narrow]
                (fn [nav]
                  (-> nav
                      (std.block.navigate/swap update-fn)))))]}
    params)))
link

refactor-test ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 4.0

refactors code tests based on given `:edits`

v 4.0
(invoke/definvoke refactor-test
  [:task {:template :code.transform
          :params {:title "REPLACE VAR USAGES"
                   :parallel true
                   :print {:function true}}
          :main   {:fn #'base/refactor-code}
          :item {:list template/test-namespaces}
          :result template/base-transform-result}])
link

require-file ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 4.0

requires the file and returns public vars

v 4.0
(invoke/definvoke require-file
  [:task {:template :code
          :params {:title "REQUIRE FILE"
                   :parallel true}
          :main {:fn #'unit.require/require-file}
          :item {:display identity}
          :result {:columns (template/code-default-columns :data #{:bold})}}])
link
(require-file 'code.manage) => (contains ['analyse 'extract 'vars] :in-any-order :gaps-ok)

scaffold ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

creates a scaffold for a new or existing set of tests

v 3.0
(invoke/definvoke scaffold
  [:task {:template :code.transform
          :params {:title "CREATE TEST SCAFFOLD"
                   :parallel true
                   :write true}
          :main {:fn #'unit/scaffold}
          :item {:list template/source-namespaces
                 :display (template/empty-result :new :info :no-new)}
          :result (template/code-transform-result :new)}])
link
(scaffold {:write false}) (scaffold '[code.manage] {:print {:item true} :write false})

snapto ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 4.1

formats fact tests into snap-to layout

v 4.1
(invoke/definvoke snapto
  [:task {:template :code.transform
          :params {:title "SNAPTO TESTS"
                   :parallel true
                   :write true}
          :main {:fn #'unit.snapto/snapto}
          :item {:list template/test-namespaces}
          :result (template/code-transform-result :changed)}])
link
(snapto {:write false}) (snapto '[code.manage] {:print {:item true} :write false})

todos ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

checks for tests with `TODO` as docstring

v 3.0
(invoke/definvoke todos
  [:task {:template :code
          :params {:title "TODO TESTS"}
          :main {:fn #'unit/todos}
          :item {:list template/test-namespaces}
          :result {:columns (template/code-info-columns #{:green})}}])
link
(todos) (todos '[platform] {:print {:result false :summary false} :return :all})

transform-code ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

helper function for any arbitrary transformation of text

v 3.0
(invoke/definvoke transform-code
  [:task {:template :code.transform
          :params {:title "TRANSFORM CODE"
                   :parallel true}
          :main   {:fn #'base/transform-code}
          :result template/base-transform-result}])
link
(transform-code {:transform #(str % "nnnnhello world")}) ;; {:deletes 0, :inserts 5, :changed [arrange], :updated false} => map? (transform-code '#{code.manage.unit} {:print {:summary true :result true :item true :function true} :transform #(str % "nnnnhello world") :full true})

unchecked ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

returns tests without `=>` checks

v 3.0
(invoke/definvoke unchecked
  [:task {:template :code
          :params {:title "FACT HAS NO `=>` FORMS"
                   :parallel true}
          :main {:fn #'unit/unchecked}
          :item {:list template/source-namespaces}
          :result {:columns (template/code-info-columns #{:magenta :bold})}}])
link
(unchecked) (unchecked '[code.manage])

unclean ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

finds source code that has top-level comments

v 3.0
(invoke/definvoke unclean
  [:task {:template :code.locate
          :params {:title "SOURCE CODE WITH COMMENTS"
                   :parallel true
                   :query '[comment]}
          :main {:fn #'base/locate-code}
          :result {:columns (template/code-default-columns :source #{:red :bold})}}])
link
(unclean 'code.manage) (unclean '[hara])

vars ^

[] [ns] [ns params] [ns params project] [ns params lookup project]
Added 3.0

returns the list of vars in a namespace

v 3.0
(invoke/definvoke vars
  [:task {:template :code
          :params {:title "NAMESPACE VARS"
                   :parallel true
                   :sorted true
                   :print {:result false :summary false}}
          :main {:fn #'base/vars}
          :item {:display identity}
          :result {:columns (template/code-default-columns :data #{:bold})}}])
link
(vars 'code.manage) (vars 'code.manage {:sorted false}) (vars '#{code.manage} {:return #{:errors :summary}}) => (contains-in {:errors any :summary {:errors 0 :warnings 0 :items 1 :results 1 :total number?}})


create-candidates ^

[nsform var sym]
Added 3.0

creates candidates for search

v 3.0
(defn create-candidates
  ([nsform var sym]
   (if-let [[_ & {:keys [as refer]}] nsform]
     (-> (set [var
               (if as (symbol (str as) (str sym)))
               (if (or (= refer :all)
                       ((set (if (coll? refer) refer [refer])) sym))
                 sym)])
         (disj nil))
     #{var})))
link
(create-candidates '[a :as b] 'a/c 'c) => #{'a/c 'b/c}

find-candidates ^

[nav var]
Added 3.0

finds candidates in current namespace

v 3.0
(defn find-candidates
  ([nav var]
   (let [nsp    (symbol (namespace var))
         sym    (symbol (name var))
         nsform (-> (query/$* nav
                              [{:first :require} '| {:first nsp}])
                    (first))
         candidates (create-candidates nsform var sym)]
     [candidates nsp sym])))
link
(find-candidates (nav/parse-string "(ns a (:require [b :as c]))") 'b/d) => [#{'b/d 'c/d} 'b 'd]

find-usages ^

[ns {:keys [var], :as params} lookup project]
Added 3.0

top-level find-usage query

v 3.0
(defn find-usages
  ([ns {:keys [var] :as params} lookup project]
   (let [path   (lookup ns)
         code   (slurp path)
         nav   (nav/parse-root code)
         [candidates nsp sym] (find-candidates nav var)]
     (base/locate-code ns
                       (assoc params
                              :query [{:or (map #(hash-map :is %) candidates)}])
                       lookup
                       project))))
link
(with-redefs [base/locate-code (constantly [])] (find-usages 'code.manage.var {:var 'code.manage.var/create-candidates} {'code.manage.var "src/code/manage/var.clj"} nil)) => vector?


arrange ^

[ns params lookup project]
Added 3.0

arranges the test code to be in the same order as the source code

v 3.0
(defn arrange
  ([ns params lookup project]
   (if (not (base/no-test ns params lookup project))
     (let [ret (in-order? ns params lookup project)
           source-ns   (project/source-ns ns)
           test-ns     (project/test-ns ns)
           source-file (lookup source-ns)
           test-file   (lookup test-ns)
           params  (task/single-function-print params)]
       (cond (res/result? ret)
             ret

             (empty? ret)
             {:changes [] :updated false :path test-file}

             (nil? source-file)
             (res/result {:status :error
                          :data :no-source-file})

             (nil? test-file)
             (res/result {:status :error
                          :data :no-test-file})
             :else
             (let [source-vars  (base/vars source-ns {:sorted false} lookup project)
                   transform-fn (fn [original]
                                  (scaffold/scaffold-arrange original source-vars))]
               (base/transform-code test-ns (assoc params :transform transform-fn) lookup project)))))))
link
(project/in-context (arrange)) => map?

commented ^

[ns params lookup project]
Added 3.0

returns tests that are in a comment block

v 3.0
(defn commented
  ([ns params lookup project]
   (if (not (base/no-test ns params lookup project))
     (let [source-ns (project/source-ns ns)
           test-ns   (project/test-ns ns)
           analysis  (base/analyse test-ns params lookup project)]
       (cond (res/result? analysis)
             analysis

             :else
             (let [entries (get analysis source-ns)]
               (vec (keep (fn [[var entry]]
                            (if (->> (get-in entry [:test :form])
                                     (= 'comment))
                              (with-meta var (get-in entry [:test :line]))))
                          entries))))))))
link
(project/in-context (commented)) => (any vector? nil?)

create-tests ^

[ns params lookup project]
Added 3.0

scaffolds and arranges the test file

v 3.0
(defn create-tests
  ([ns params lookup project]
   (if (not (base/no-test ns params lookup project))
     (let [new (scaffold ns params lookup project)]
       (arrange ns params lookup project)
       new))))
link
(project/in-context (create-tests)) => map?

import ^

[ns params lookup project]
Added 3.0

imports unit tests as docstrings

v 3.0
(defn import
  ([ns params lookup project]
   (let [source-ns   (project/source-ns ns)
         test-ns     (project/test-ns ns)
         source-file (lookup source-ns)
         test-file   (lookup test-ns)
         params  (task/single-function-print params)]
     (cond (nil? source-file)
           (res/result {:status :error
                        :data :no-source-file})

           (nil? test-file)
           (res/result {:status :error
                        :data :no-test-file})

           :else
           (let [import-fn    (fn [nsp refers]
                                (fn [zloc]
                                  (docstring/insert-docstring zloc nsp refers)))
                 refers       (base/analyse test-ns params lookup project)
                 transform-fn (fn [text] (walk/walk-string text '_ refers import-fn))]
             (base/transform-code source-ns (assoc params :transform transform-fn) lookup project))))))
link
(project/in-context (import {:print {:function true}})) => map?

in-order? ^

[ns params lookup project]
Added 3.0

determines if the test code is in the same order as the source code

v 3.0
(defn in-order?
  ([ns params lookup project]
   (if (not (base/no-test ns params lookup project))
     (let [source-ns (project/source-ns ns)
           test-ns   (project/test-ns ns)
           source-file (lookup source-ns)
           test-file   (lookup test-ns)
           params (merge {:full true} params)]
       (cond (nil? (or test-file source-file))
             (res/result {:status :error
                          :data :invalid-namespace})

             (nil? test-file)
             (res/result {:status :error
                          :data :no-test-file})

             (nil? source-file)
             (res/result {:status :error
                          :data :no-source-file})

             :else
             (let [source-vars (base/vars source-ns params lookup project)
                   test-vars   (base/vars test-ns params lookup project)
                   orphaned    (clojure.set/difference (set test-vars) (set source-vars))
                   test-vars   (vec (remove orphaned test-vars))]
               (cond (= source-vars test-vars)
                     []

                     :else
                     (let [[count source-marked] (mark-vars source-vars test-vars)]
                       (if (pos? count)
                         [count source-marked (second (mark-vars test-vars source-vars))]
                         [])))))))))
link
(project/in-context (in-order?)) => (any vector? nil?)

incomplete ^

[ns params lookup project]
Added 3.0

returns functions with todos all missing tests

v 3.0
(defn incomplete
  ([ns params lookup project]
   (if (not (base/no-test ns params lookup project))
     (let [source-ns   (project/source-ns ns)
           test-ns     (project/test-ns ns)
           source-file (lookup source-ns)
           test-file   (lookup test-ns)]
       (-> (concat
            (if source-file (missing source-ns params lookup project))
            (if test-file   (todos test-ns params lookup project)))
           sort
           vec)))))
link
(project/in-context (incomplete)) => (any vector? nil?)

mark-vars ^

[vars comp-vars]
Added 3.0

captures changed vars in a set

v 3.0
(defn mark-vars
  ([vars comp-vars]
   (let [marked-set (->> (second (diff.seq/diff vars comp-vars))
                         (filter (comp #{:+} first))
                         (mapcat #(nth % 2))
                         set)
         results (mapv (fn [var]
                         (let [marked? (marked-set var)
                               sym (symbol (name var))]
                           (if marked?
                             (set [sym])
                             sym)))
                       vars)]
     [(count (filter set? results)) results])))
link
(mark-vars '[a1 a2 a3 a4 a5] '[a1 a4 a3 a2 a5]) => '[2 [a1 #{a2} #{a3} a4 a5]]

missing ^

[ns params lookup project]
Added 3.0

returns all functions missing unit tests

v 3.0
(defn missing
  ([ns params lookup project]
   (let [source-ns (project/source-ns ns)
         test-ns   (project/test-ns ns)
         source-file (lookup source-ns)
         test-file   (lookup test-ns)]
     (cond (nil? source-file)
           (res/result {:status :error
                        :data :no-source-file})

           :else
           (let [source-vars (if source-file (base/vars source-ns params lookup project))
                 test-vars   (if test-file   (base/vars test-ns params lookup project))]
             (vec (sort (clojure.set/difference (set source-vars) (set test-vars)))))))))
link
(project/in-context (missing)) => (any vector? nil?)

orphaned ^

[ns params lookup project]
Added 3.0

returns unit tests that do not have an associated function

v 3.0
(defn orphaned
  ([ns params lookup project]
   (let [source-ns (project/source-ns ns)
         test-ns (project/test-ns ns)
         source-file (lookup source-ns)
         test-file   (lookup test-ns)
         params (merge {:full true} params)]
     (cond (nil? test-file)
           (res/result {:status :error
                        :data :no-test-file})

           :else
           (let [source-vars (if source-file (base/vars source-ns params lookup project))
                 test-vars   (if test-file   (base/vars test-ns params lookup project))
                 orphaned    (clojure.set/difference (set test-vars) (set source-vars))
                 orphaned    (remove (comp (partial orphaned-meta params)
                                           meta)
                                     orphaned)]
             (mapv (fn [sym]
                     (if (= (namespace sym) (str source-ns))
                       (symbol (name sym))
                       sym))
                   (sort orphaned)))))))
link
(project/in-context (orphaned)) => (any vector? nil?)

orphaned-meta ^

[params m]
Added 3.0

returns true if meta satisfies the orphaned criteria

v 3.0
(defn orphaned-meta
  ([params m]
   (boolean (and (-> m :meta :adopt)
                 (or (not (:strict params))
                     (try
                       (require (:ns m))
                       (resolve (symbol (str (:ns m))
                                        (first (clojure.string/split (str (:var m)) #"."))))
                       (catch Exception e)))))))
link
(orphaned-meta {} {}) => false (orphaned-meta {:strict true} {:meta {:adopt true} :ns 'clojure.core :var 'slurp}) => true (orphaned-meta {:strict true} {:meta {:adopt true} :ns 'clojure.core :var 'NONE}) => false

pedantic ^

[ns params lookup project]
Added 3.0

returns all probable improvements on tests

v 3.0
(defn pedantic
  ([ns params lookup project]
   (if (not (base/no-test ns params lookup project))
     (let [source-ns (project/source-ns ns)
           tag (fn [k inputs]
                 (if-not (res/result? inputs)
                   (mapv (fn [sym]
                           (with-meta sym
                             (assoc (meta sym) :tag k)))
                         inputs)))]
       (-> (concat (tag :M (missing ns params lookup project))
                   (tag :T (todos ns params lookup project))
                   (tag :N (unchecked ns params lookup project))
                   (tag :C (commented ns params lookup project))))))))
link
(project/in-context (pedantic)) => (any seq? nil?)

purge ^

[ns params lookup project]
Added 3.0

purge docstrings and meta from file

v 3.0
(defn purge
  ([ns params lookup project]
   (let [source-ns  (project/source-ns ns)
         source-file (lookup source-ns)
         params  (task/single-function-print params)]
     (cond (nil? source-file)
           (res/result {:status :info
                        :data :no-source-file})

           :else
           (let [purge-fn (fn [nsp references] identity)
                 transform-fn (fn [text] (walk/walk-string text '_ {} purge-fn))]
             (base/transform-code source-ns (assoc params :transform transform-fn) lookup project))))))
link
(project/in-context (purge {:print {:function true}})) => map?

scaffold ^

[ns {:keys [write print], :as params} lookup project]
Added 3.0

creates a set of tests for a given source

v 3.0
(defn scaffold
  ([ns {:keys [write print] :as params} lookup project]
   (if (not (base/no-test ns params lookup project))
     (let [source-ns (project/source-ns ns)
           test-ns   (project/test-ns ns)
           source-file (lookup source-ns)
           test-file   (lookup test-ns)
           version (->> (clojure.string/split (:version project) #".")
                        (take 2)
                        (clojure.string/join "."))
           source-vars (if source-file (base/vars source-ns {:sorted false} lookup project))
           test-vars   (if test-file   (base/vars test-ns {:sorted false} lookup project))
           new-vars    (seq (remove (set test-vars) source-vars))
           params      (task/single-function-print params)]
       (cond (nil? source-file)
             (res/result {:status :error
                          :data :no-source-file})
             
             (empty? source-vars)
             (res/result {:status :info
                          :data :no-source-vars})

              :else
              (let [transform-fn (if test-file
                                   (fn [original] (scaffold/scaffold-append original source-ns new-vars version))
                                   (fn [_] (scaffold/scaffold-new source-ns test-ns source-vars version)))
                    [original test-file]  (if test-file
                                            [(slurp test-file) test-file]
                                            ["" (scaffold/new-filename source-file test-ns project write)])
                    params (assoc params :transform transform-fn)
                    result (base/transform-code test-ns params (assoc lookup test-ns test-file) project)]
                (assoc result :new (vec new-vars))))))))
link
(project/in-context (scaffold)) => map?

todos ^

[ns params lookup project]
Added 3.0

returns all unit tests with TODOs

v 3.0
(defn todos
  ([ns params lookup project]
   (let [source-ns (project/source-ns ns)
         test-ns   (project/test-ns ns)
         test-file (lookup test-ns)]
     (cond (nil? test-file)
           (res/result {:status :error
                        :data :no-test-file})
           
           :else
           (let [analysis  (base/analyse test-ns params lookup project)
                 entries (get analysis source-ns)]
             (vec (keep (fn [[var entry]]
                          (if (= "TODO" (second  (get-in entry [:test :sexp])))
                            (with-meta var (get-in entry [:test :line]))))
                        entries)))))))
link
(project/in-context (todos)) => (any vector? nil?)

unchecked ^

[ns params lookup project]
Added 3.0

returns tests that does not contain a `=>`

v 3.0
(defn unchecked
  ([ns params lookup project]
   (if (not (base/no-test ns params lookup project))
       (let [source-ns (project/source-ns ns)
             test-ns   (project/test-ns ns)
             test-file (lookup test-ns)
             analysis  (base/analyse test-ns params lookup project)]
         (cond (res/result? analysis)
               analysis

               :else
               (let [entries (get analysis source-ns)]
                 (vec (keep (fn [[var entry]]
                              (if (and (->> (get-in entry [:test :form])
                                            (not= 'comment))
                                       (->> (get-in entry [:test :sexp])
                                            (flatten)
                                            (filter '#{=>})
                                            (empty?))
                                       (not (:unchecked (:meta entry))))
                                (with-meta var (get-in entry [:test :line]))))
                            entries))))))))
link
(project/in-context (unchecked)) => (any vector? nil?)

6    code.manage Guide

code.manage provides a suite of tasks for maintaining code quality, managing tests, and refactoring. It is typically invoked via lein manage or from the REPL.

6.1    Core Concepts

  • Tasks: Operations that run over a set of namespaces (e.g., analyse, grep).n- Templates: Preset configurations for tasks (e.g., :code.transform, :code.locate).n- Selectors: Arguments to target specific namespaces (e.g., vector of symbols ['my.ns]).

6.2    Usage

6.2.1    Invocation

# From CLI\nlein manage <task> <namespaces> <options>\n\n# Example\nlein manage analyse \"['code.manage]\" \"{:print {:summary true}}\"
;; From REPL\n(require '[code.manage :as manage])\n(manage/analyse ['code.manage] {:print {:summary true}})

6.2.2    Scenarios

6.2.2.1    1. Codebase Cleanup Workflow

A typical cleanup session might involve identifying messy code and then standardizing it.

Step A: Identify "Unclean" CodenFind files with top-level comment blocks (often used for debugging/scratch) that shouldn't be committed.

(manage/unclean ['my.project] {:print {:item true}})

Step B: Remove Docstrings from SourcenIf you prefer keeping docstrings in tests or external docs, you can purge them.

(manage/purge ['my.project] {:write true})

Step C: Standardize Namespace DeclarationsnEnsure ns forms are formatted consistently (requires sorting, indentation).

(manage/ns-format ['my.project] {:write true})

6.2.2.2    2. Test Coverage & Management

code.manage integrates tightly with code.test to ensure coverage.

Step A: Find Missing TestsnList functions that have no corresponding fact.

(manage/missing ['my.project])

Step B: Scaffold New TestsnGenerate test files and stubs for the missing functions.

(manage/scaffold ['my.project] {:write true})

Step C: Identify "Orphaned" TestsnFind tests that refer to non-existent functions (e.g., after a rename/delete).

(manage/orphaned ['my.project])

6.2.2.3    3. Large Scale Refactoring

Use refactor-code or grep-replace for bulk changes.

Scenario: Renaming a function across the codebase

If simple grep isn't enough (e.g., context sensitive), you can write a custom transform script. However, for simple string replacement:

(manage/grep-replace ['my.project]\n                     {:query \"old-fn-name\"\n                      :replace \"new-fn-name\"\n                      :write true})

Scenario: Custom AST Modification

You can use refactor-code with a custom edit function that operates on the zipper.

(require '[code.query :as query]\n         '[std.block.navigate :as edit])\n\n(manage/refactor-code ['my.project]\n  {:edits [(fn [zloc]\n             ;; Use code.query to find/modify\n             (query/modify zloc\n                           '[defn old-name]\n                           (fn [node]\n                             (edit/set-value node 'new-name))))]\n   :write true})

6.2.2.4    4. Search and Analysis

Scenario: Find all usages of a specific var

Useful when checking impact before a change.

(manage/find-usages ['my.project]\n                    {:var 'my.project.core/my-func})

Scenario: Grep with highlighting

Quickly scan for a pattern.

(manage/grep ['my.project]\n             {:query \"TODO\"\n              :highlight true})