{"_id":"@amarnotcool/repotool-apex","name":"@amarnotcool/repotool-apex","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@amarnotcool/repotool-apex","version":"1.0.0","description":"Zero-dependency CLI that reads, graphs, queries and diffs a git repository using only the Node.js standard library.","keywords":["git","cli","graph","diff","myers","zero-dependency","no-dependencies","stdlib"],"homepage":"https://github.com/amarnotcool/TeamApex_repotool#readme","bugs":{"url":"https://github.com/amarnotcool/TeamApex_repotool/issues"},"repository":{"type":"git","url":"git+https://github.com/amarnotcool/TeamApex_repotool.git"},"bin":{"repotool":"bin/repotool.js"},"main":"src/index.js","scripts":{"test":"node --test test/*.test.js","verify":"sh verify-zero-deps.sh"},"engines":{"node":">=18"},"license":"MIT","dependencies":{},"_id":"@amarnotcool/repotool-apex@1.0.0","gitHead":"0f674a11e902ad40a4b438f6a8591c9409eb411d","_nodeVersion":"22.20.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-2mEbxANOUt7Fp5Q3gdT/BtBfAAFHd5tUh0P8CUG56aq4v2guSxf6blqi0HXqimZoiEgHSVij1zoU8Nm3ea1GjQ==","shasum":"ff1d1f8546126f4a3e8ffe331022262b5101fcce","tarball":"https://registry.npmjs.org/@amarnotcool/repotool-apex/-/repotool-apex-1.0.0.tgz","fileCount":29,"unpackedSize":221706,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCLwmmmVPoAIjho7X9EhGxQO1ZLaG1qgCOjhUUjv/TM/gIhAK4LRJKKwr/KksdG9yUJRyoSl7x13ZRDy3GMhFqgG/DC"}]},"_npmUser":{"name":"amarnotcool","email":"devprataprathore346@gmail.com"},"directories":{},"maintainers":[{"name":"amarnotcool","email":"devprataprathore346@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/repotool-apex_1.0.0_1788166641098_0.4904912852688197"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-31T08:57:20.942Z","1.0.0":"2026-08-31T08:57:21.229Z","modified":"2026-08-31T08:57:21.446Z"},"maintainers":[{"name":"amarnotcool","email":"devprataprathore346@gmail.com"}],"description":"Zero-dependency CLI that reads, graphs, queries and diffs a git repository using only the Node.js standard library.","homepage":"https://github.com/amarnotcool/TeamApex_repotool#readme","keywords":["git","cli","graph","diff","myers","zero-dependency","no-dependencies","stdlib"],"repository":{"type":"git","url":"git+https://github.com/amarnotcool/TeamApex_repotool.git"},"bugs":{"url":"https://github.com/amarnotcool/TeamApex_repotool/issues"},"license":"MIT","readme":"# repotool\n\nA CLI that reads, graphs, questions and diffs a git repository — built on the\nNode.js standard library and nothing else. No `chalk`, no `commander`, no\n`diff`, no `simple-git`: `dependencies` in `package.json` is empty, and two\n`verify-zero-deps` scripts prove it. Every number it prints is counted from\ngit history, and every score it prints is followed by the formula that produced\nit.\n\n```sh\ngit clone https://github.com/amarnotcool/TeamApex_repotool.git\nnode TeamApex_repotool/bin/repotool.js stats\n```\n\n## Commands at a glance\n\n| Command | What it does |\n|---|---|\n| [`graph`](#graph) | Draw the commit and merge history as a lane graph (ASCII or SVG) |\n| [`stats`](#stats) | One screen of size, people, branches and churn |\n| [`hotspots`](#hotspots) | Rank files by how much attention they attract |\n| [`ask`](#ask) | Answer fixed questions about the repository, deterministically |\n| [`diff`](#diff) | Colourised unified diff, computed with our own Myers diff |\n| [`compare`](#compare) | Show what each of two refs has that the other does not |\n| [`health`](#health) | Four scored measurements, each with its formula printed |\n| [`timeline`](#timeline) | Chart commit activity per day or week |\n| [`completion`](#completion) | Print a bash or zsh completion script |\n\nEvery command also takes `--repo PATH`, `--color`, `--no-color` and `--help`.\nRun `repotool help <command>` for the full flag list and examples.\n\n## Run it\n\nNothing to install, nothing to build — clone the repository and run it\nstraight from source:\n\n```sh\ngit clone https://github.com/amarnotcool/TeamApex_repotool.git\ncd TeamApex_repotool\nnode bin/repotool.js graph\n```\n\nThere is no install step because there is nothing to install: `dependencies` is\nempty, so a clone is already everything the tool needs.\n\nTo run it against another repository, point `--repo` at that checkout:\n\n```sh\nnode bin/repotool.js graph --repo /path/to/other/repo\n```\n\nNode 18 or newer and a `git` binary on `PATH` are the only requirements.\n\n---\n\n## `graph`\n\nParses the full commit and merge history and draws it as a lane graph.\n`*` is a commit, `M` a merge, `o` a root commit.\n\n```sh\nrepotool graph\nrepotool graph --limit 20\nrepotool graph --format svg --output history.svg\n```\n\n```\n8 commit(s), on branch main\n\nM      f27040a 2026-08-20 Ada Lovelace (main) Merge hotfix\n|\\\n| *    2c2040d 2026-08-20 Ada Lovelace (hotfix) Hotfix\n| |\nM |    0eb3d2e 2026-08-20 Ada Lovelace Merge feature into main\n|\\|\\\n* | |  5a22eb4 2026-08-20 Ada Lovelace Add readme\n| |/\n| *    ab19254 2026-08-20 Grace Hopper (feature) Extend feature\n| |\n| *    ef60ae3 2026-08-20 Grace Hopper Add feature file\n|/\n*      01f599d 2026-08-20 Ada Lovelace Tweak app\n|\no      231d066 2026-08-20 Ada Lovelace Initial commit\n```\n\nFlags: `--limit N`, `--branch REF`, `--no-dates`, `--format ascii|svg`,\n`--output PATH`.\n\n<details>\n<summary><b>SVG export</b> — how the second renderer stays in step with the first</summary>\n\n`--format svg` renders the *same* layout through a second renderer — commit,\nmerge and root nodes, lane colours, connectors and labels — as an SVG document\non stdout, or into a file with `--output`:\n\n```sh\nrepotool graph --format svg > history.svg\nrepotool graph --format svg --output history.svg     # status line on stderr\nrepotool graph --branch main --limit 50 --format svg --output main.svg\n```\n\nLane assignment happens once, in `src/graph/build-graph.js`; the ASCII and SVG\nrenderers only draw what it decided, so they cannot disagree about structure.\nA parent outside the loaded history (`--limit`, or a shallow clone) is drawn as\na dashed stub rather than dropped.\n\n</details>\n\n## `stats`\n\nOne screen describing the repository: size, people, branches, churn, and where\nthe changes land.\n\n```sh\nrepotool stats\nrepotool stats --json\n```\n\n```\nrepotool stats — Zero Dependency - Team Apex  on branch master\n\ncommits        16\ncontributors   1\nbranches       1 local, 1 remote\nfiles touched  45\nline churn     10,829  +9,996 / -833\nhistory        2026-08-29 → 2026-08-31  (2 days)\nlast commit    13 minutes ago\n\nTop 3 contributors\n──────────────────\n16  ████████████████  Devpratap Singh\n\nTop 3 most-changed files\n────────────────────────\n9 commits  1,019 lines  README.md\n6 commits  808 lines    bin/repotool.js\n6 commits  57 lines     package.json\n```\n\nFlags: `--limit N`, `--json`.\n\n## `hotspots`\n\nFiles ranked by how much attention they attract — change frequency, how many\ndifferent people touch them, and how many lines move. The score weights all\nthree and the weighting is printed with the table rather than hidden.\n\n```sh\nrepotool hotspots\nrepotool hotspots --limit 25 --sort churn\n```\n\n```\nrepotool hotspots — top 3 of 45 files\nranked by score (commits 50% · churn 30% · authors 20%), each scaled against the busiest file\n\nrank  score     commits  authors  churn  added/removed  file\n  1.  ████████        9        1  1,019    +825 / -194  README.md\n  2.  ██████          6        1    808     +738 / -70  bin/repotool.js\n  3.  █████           5        1    702    +588 / -114  src/query/handlers.js\n```\n\nFlags: `--limit N` (default 10), `--sort score|commits|churn|authors`, `--json`.\n\n<details>\n<summary><b>Where the churn numbers come from</b> — and why merges are excluded</summary>\n\nEvery line-count figure in `stats`, `hotspots`, `health`, `timeline`, `compare`\nand `ask` comes from the single `git log --numstat` pass described in\n[Design](#design). Git emits no numstat for merge commits, so **merges are\nexcluded from churn**. That avoids counting the same lines twice on a\nmerge-heavy history, but it does mean a repository that squash-merges\neverything will report less churn than the branch history suggests.\n\nBecause every file-level answer reads that same pass, `hotspots` and\n`ask \"which file changed the most often\"` cannot disagree with each other.\n\n</details>\n\n## `ask`\n\nDeterministic answers to fixed question shapes — a keyword matcher over parsed\ngit data, not a language model, so the same question always gives the same\nanswer and nothing leaves your machine.\n\n```sh\nrepotool ask \"who last touched src/git-reader.js\"\nrepotool ask \"why is this repository changing so much\"\nrepotool ask \"which file changed the most often\" --json\n```\n\nAsk something unsupported and it prints the list of what it can answer.\n\nFlags: `--json`.\n\n<details>\n<summary><b>Every question repotool can answer</b></summary>\n\n```sh\nrepotool ask \"who last touched src/git-reader.js\"\nrepotool ask \"who works most on src/git-reader.js\"\nrepotool ask \"what has changed recently\"\nrepotool ask \"why is this repository changing so much\"\nrepotool ask \"how many commits by Ada\"\nrepotool ask \"what files changed in HEAD~2\"\nrepotool ask \"show the last 10 commits\"\nrepotool ask \"which file changed the most often\"\nrepotool ask \"what branches exist\"\nrepotool ask \"when was 01f599d committed\"\n```\n\n`why is this repository changing so much` is arithmetic, not opinion: it\ncompares the recent commit rate against the earlier baseline, names the files\nrecent churn concentrates in, and says who is driving it — and it declines to\nquote a per-day rate when the history is too compressed in time to support one.\n`health`'s Activity score reuses that same comparison and the same refusal.\n\n</details>\n\n## `diff`\n\nColourised unified diff between two revisions, computed by our own Myers diff\nimplementation.\n\n```sh\nrepotool diff HEAD~3 HEAD\nrepotool diff main feature --context 5\nrepotool diff HEAD~1 HEAD --stat\n```\n\nLines that were *edited* rather than replaced get a second, character-level\nMyers pass — the same algorithm, run again over the two lines' characters — and\nonly the span that actually changed is underlined inside the usual red/green\nline.\n\nFlags: `--context N` (default 3), `--stat`.\n\n<details>\n<summary><b>When intra-line highlighting applies</b> — the three guards</summary>\n\nA `-`/`+` pair is only treated as an edit when it clears all three:\n\n| Guard | Threshold | Why |\n|---|---|---|\n| Similarity | at least 30% of characters in common | Below it, the lines are neighbours rather than an edit of one into the other |\n| Changed runs | no more than 3 per side | A rewrite keeps punctuation and indentation, so its character diff comes back as scattered fragments |\n| Average run length | at least 2 characters | Single letters shared by accident (`try` against `if (!x)`) are not an edit |\n\nAnything else is a rewrite and stays whole-line coloured, because\ncharacter-level detail on unrelated lines is noise rather than information.\nLines longer than 1,000 characters skip the second pass regardless. With\n`--no-color` (or `NO_COLOR`) it is skipped entirely: there is nothing to show\na highlight with.\n\n</details>\n\n## `compare`\n\nWhat each of two refs has that the other does not — branches, tags or commits.\n\n```sh\nrepotool compare main feature\nrepotool compare v1.0 v2.0\nrepotool compare HEAD~10 HEAD --json\n```\n\n```\nrepotool compare — main vs feature\n\n                 main   feature\ncommits ahead       1   2\nmerges              —   —\nfiles changed       1   1\nchurn               1   3 +3 / -0\ncontributors        1   1\n\nonly on main: Ada Lovelace (1)\nonly on feature: Grace Hopper (2)\n```\n\nEach side is git's own `A..B` range — commits reachable from one ref and not\nthe other — folded through the same model `stats` and `hotspots` are built on,\nso \"ahead by\" here counts what git counts. A ref compared with itself reports\nzero difference and exits 0; refs with no common ancestor are reported as\nunrelated rather than treated as an error.\n\nFlags: `--json`.\n\n## `health`\n\nFour measurements of the repository, each printed next to the formula that\nproduced it. Nothing here is a judgement or an estimate — every number is\narithmetic over the same history `stats` and `hotspots` read.\n\n```sh\nrepotool health\nrepotool health --json\n```\n\n```\nrepotool health — Zero Dependency - Team Apex\n\nActivity         —                   this history spans under a day, so per-day rates would be meaningless\nConcentration   77  ███████████████  23% of 10,829 churned lines are in the 3 busiest files\nStability       75  ███████████████  4 commits of 16 mention a fix, bug, revert or regression\nCollaboration    0                   Devpratap Singh made 100% of 16 commits\n\nOverall   51  FAIR (mean of 3 measurable dimensions)\nequal-weighted mean of the scores above · bands: 80+ EXCELLENT · 60+ GOOD · 40+ FAIR · 0+ NEEDS ATTENTION\n\nWarnings\n────────\n! README.md changed in 9 of 16 commits (threshold: more than 5)\n! Devpratap Singh made 100% of all commits (threshold: above 70%)\n\nFormulas\n  Activity      min(recent commits/day ÷ baseline commits/day, 3) ÷ 3 × 100\n  Concentration 100 − (churn in the 3 busiest files ÷ total churn × 100)\n  Stability     100 − (commit subjects matching the fix pattern ÷ total commits × 100)\n  Collaboration 100 − (top contributor's commits ÷ total commits × 100)\n  warnings      one file in more than 5 commits · top-3 churn above 50% · one author above 70%\n```\n\nFlags: `--json`. `repotool help health` prints the formulas, the bands and the\nwarning thresholds in full.\n\n<details>\n<summary><b>The four formulas, the bands and the warning thresholds</b></summary>\n\n| Dimension | Formula (higher is better) |\n|---|---|\n| Activity | `min(recent commits/day ÷ baseline commits/day, 3) ÷ 3 × 100` |\n| Concentration | `100 − (churn in the 3 busiest files ÷ total churn × 100)` |\n| Stability | `100 − (subjects matching the fix pattern ÷ commits × 100)` |\n| Collaboration | `100 − (top contributor's commits ÷ total commits × 100)` |\n\nThe fix pattern is `\\b(fix|fixes|fixed|fixing|bug|bugs|bugfix|hotfix|revert|reverts|reverted|regression)\\b`,\ncase-insensitive — word boundaries, so `prefix` is not a fix and `debugger` is\nnot a bug.\n\nOverall is the equal-weighted mean of the dimensions that could be measured:\n`80+ EXCELLENT`, `60+ GOOD`, `40+ FAIR`, below 40 `NEEDS ATTENTION`.\n\n**Activity refuses to answer rather than guess.** It reuses the comparison\nbehind [`ask \"why is this repository changing so much\"`](#ask): when either\nperiod spans under a day, or there are fewer than four commits, no ratio is\nprinted and the dimension is left out of the overall mean instead of being\nfilled in with a plausible number.\n\nWarnings print only when they trigger: one file changed in more than\n`max(5, 25% of all commits)` commits, more than 50% of all churn in the three\nbusiest files, or one contributor above 70% of all commits.\n\n</details>\n\n## `timeline`\n\nCommit activity per day (or week), scaled against the busiest bucket on screen.\n\n```sh\nrepotool timeline --limit 6\nrepotool timeline --by week\nrepotool timeline --metric lines --json\n```\n\n```\nRepository Activity\n───────────────────\n\nAug 29  ████████████\nAug 30  ████████████████████████████████\nAug 31  ████████████████████\n\nCommits: 16  (2026-08-29 → 2026-08-31)\nPeak: Aug 30  (8 commits)\n```\n\nFlags: `--limit N` (recent buckets, default 30), `--by day|week`,\n`--metric commits|lines|contributors`, `--json`.\n\nDays with no commits are shown as `·` rather than skipped — a gap in the\nhistory is information, and dropping it would quietly compress the time axis.\nBuckets use each commit's local calendar day, so an evening commit stays on its\nown day instead of sliding into the next one.\n\n## `completion`\n\nPrints a bash or zsh completion script on stdout, generated from one\ndescription of the CLI surface in `src/completion.js` — so the two shells\ncannot drift apart from each other or from the tool.\n\n```sh\n# bash — for the current shell\neval \"$(repotool completion bash)\"\n\n# bash — permanently\nrepotool completion bash > ~/.local/share/bash-completion/completions/repotool\n\n# zsh — into a directory on your fpath, then restart the shell\nrepotool completion zsh > \"${fpath[1]}/_repotool\"\n```\n\nBoth scripts complete commands, each command's flags, and the fixed values a\nflag accepts (`--sort score|commits|churn|authors`, `--format ascii|svg`,\n`--by day|week`, `--metric commits|lines|contributors`); `--repo` completes\ndirectories and `--output` completes files.\n\nArguments: `bash` or `zsh`. No flags of its own beyond the common options.\n\n---\n\n## Scripting / JSON output\n\n`stats`, `hotspots`, `health`, `timeline`, `ask` and `compare` take `--json`.\nThe JSON goes to stdout and nothing else does, errors still go to stderr, and\nexit codes are unchanged — so `repotool stats --json | …` is safe to pipe.\n`graph` and `diff` have no `--json`: their value is the picture, and\n`--format svg` is graph's structured export.\n\n```sh\nrepotool stats --json\nrepotool hotspots --json --limit 25 --sort churn\nrepotool health --json\nrepotool timeline --json --by week\nrepotool compare main feature --json\nrepotool ask \"who last touched src/app.js\" --json\n```\n\nField names are stable; new fields may be added, existing ones are not renamed.\n\n<details>\n<summary><b>Full JSON schemas</b> — stats, hotspots, health, timeline, compare, ask</summary>\n\n`stats --json`:\n\n```\n{\n  \"repository\": { \"path\", \"name\", \"head\": { \"empty\", \"detached\", \"branch\", \"hash\" } },\n  \"empty\":        boolean,\n  \"commits\":      { \"total\", \"merges\", \"first\", \"last\", \"spanDays\" },\n  \"contributors\": [ { \"name\", \"email\", \"commits\", \"merges\", \"added\", \"removed\",\n                      \"firstDate\", \"lastDate\" } ],\n  \"branches\":     { \"local\": [name], \"remote\": [name] },\n  \"totals\":       { \"filesTouched\", \"added\", \"removed\", \"churn\" },\n  \"topFiles\":     [ file ]\n}\n```\n\n`hotspots --json`:\n\n```\n{\n  \"repository\": { … as above … },\n  \"empty\":      boolean,\n  \"sort\":       \"score\" | \"commits\" | \"churn\" | \"authors\",\n  \"weights\":    { \"commits\", \"churn\", \"authors\" },\n  \"totalFiles\": number,\n  \"files\":      [ { \"rank\", \"score\", …file } ]\n}\n```\n\nA `file` everywhere above is\n`{ path, commits, authors, added, removed, churn, binary, firstDate, lastDate }`.\n\n`health --json` carries the evidence behind each score, not just the score, so\na script can see what produced it:\n\n```\n{\n  \"repository\": { … },\n  \"empty\":         boolean,\n  \"overall\":       { \"score\", \"band\", \"dimensions\": [measured dimension names] },\n  \"activity\":      { \"score\", \"formula\", \"ratio\", \"recentPerDay\", \"baselinePerDay\",\n                     \"comparable\", \"reason\" },\n  \"concentration\": { \"score\", \"formula\", \"share\", \"topChurn\", \"totalChurn\", \"files\" },\n  \"stability\":     { \"score\", \"formula\", \"pattern\", \"fixCommits\", \"totalCommits\", \"share\" },\n  \"collaboration\": { \"score\", \"formula\", \"topContributor\", \"topCommits\",\n                     \"totalCommits\", \"contributors\", \"share\" },\n  \"warnings\":      [ { \"code\", \"message\", \"value\", \"threshold\" } ]\n}\n```\n\nA dimension that could not be measured has `\"score\": null` and a `\"reason\"`,\nand its name is absent from `overall.dimensions`.\n\n`timeline --json`:\n\n```\n{\n  \"repository\": { … },\n  \"empty\":        boolean,\n  \"by\":           \"day\" | \"week\",\n  \"metric\":       \"commits\" | \"lines\" | \"contributors\",\n  \"buckets\":      [ { \"date\", \"commits\", \"added\", \"removed\", \"contributors\" } ],\n  \"peak\":         { \"date\", \"commits\" } | null,\n  \"totalCommits\": number\n}\n```\n\n`compare --json` describes both directions with the same shape, so a script can\nread them symmetrically:\n\n```\n{\n  \"refA\", \"refB\", \"hashA\", \"hashB\",\n  \"identical\":  boolean,\n  \"mergeBase\":  hash | null,\n  \"a\":          side,          // what refA has that refB does not\n  \"b\":          side,          // and the reverse\n  \"sharedContributors\": [name],\n  \"sharedFiles\":        [path]\n}\n\nside = { ref, range, commits, merges, filesChanged, added, removed, churn,\n         first, last, contributors, onlyContributors, files }\n```\n\n`ask --json` wraps the answer in the question that produced it, so a script\ndoes not have to keep its own note of what it asked:\n\n```\n{\n  \"question\": \"who last touched src/app.js\",\n  \"intent\":   \"who-touched\",\n  \"argument\": \"src/app.js\",\n  \"answer\":   { … shape depends on the intent … }\n}\n```\n\nThe text and JSON answers are built side by side from the same values in\n`src/query/handlers.js`, so the two can never report different facts.\n\n</details>\n\n## Zero-dependency proof\n\n```sh\nnode verify-zero-deps.js     # runs anywhere Node runs\nsh verify-zero-deps.sh       # same checks, POSIX shell\n```\n\nBoth check the same three things: the `dependencies` block is empty, every\n`require` in `src/`, `bin/` and `test/` resolves to a Node built-in or a\nrelative path, and no unexpected `node_modules` tree is present. The Node\nversion needs no shell, so it works on Windows without Git Bash.\n\nThe proof is itself tested: the suite plants a real third-party import and a\ndeclared dependency in a copy of the project and asserts the checker fails.\n\nSee [STDLIB.md](STDLIB.md) for the full package-by-package substitution log —\nwhat each replaced package would have given us, and what writing it ourselves\ncost.\n\n## Tests\n\n```sh\nnpm test        # or: node --test \"test/*.test.js\"\n```\n\nThe suite builds real throwaway git repositories and covers empty repos, a\nsingle root commit, merge commits, detached HEAD, malformed git output, a\nrandomised round-trip property for the diff algorithm, SVG well-formedness\n(checked with a small XML parser written for the tests, since Node has none),\n`--json` shapes, health scores asserted as exact hand-checkable numbers,\ntimeline bucketing across timezones and week boundaries, compare's ahead/behind\ncounts, and generated completion scripts (`bash -n`).\n\n## Design\n\nNine commands over one shared reading layer. Feature modules never reach into\neach other's internals.\n\n```\nbin/repotool.js       argv routing, per-command help\nsrc/index.js          public API for using repotool as a library\nsrc/git-reader.js     the only file that talks to git\nsrc/analysis/         shared repo model + stats, hotspots, health, timeline,\n                      compare, and the JSON renderers\nsrc/graph/            DAG layout + ASCII and SVG renderers\nsrc/query/            question -> intent -> answer (text and JSON together)\nsrc/diff/             Myers diff + unified-diff renderer\nsrc/completion.js     generated bash and zsh completion scripts\nsrc/format.js         column alignment, number grouping, relative dates\nsrc/ansi.js           escape codes, TTY/NO_COLOR aware\n```\n\n**Every output format is a renderer over a shared model, never a second\nderivation of the same facts.** ASCII and SVG share one lane layout; the human\nand JSON reports share one repo model; each `ask` answer produces its text and\nits JSON side by side; `compare` folds a commit range through the same model\n`stats` uses; one Myers implementation runs at both line and character\ngranularity; one description of the CLI generates both completion scripts.\nThat is what makes \"these two views cannot disagree\" checkable rather than\nmerely claimed.\n\n`src/analysis/repo-model.js` reads the history once — commits, contributors,\nbranches and per-file churn, the last of these from a single\n`git log --numstat` — and every report and aggregate question is folded from\nthat one pass rather than re-deriving its own. (What that pass does and does\nnot count is described under [hotspots](#hotspots).)\n\nFeature modules are required lazily, both in the CLI and in `src/index.js`, so\na problem in one feature cannot stop the others from running.\n\n## Use it as a library\n\n```js\nconst {\n  readCommits, buildGraph, renderAscii, renderSvg,\n  buildRepoModel, statsJson, hotspotsJson,\n  diffLines, parseQuestion, answerQuestionJson,\n} = require('/path/to/TeamApex_repotool/src/index.js');\n\nconst { commits } = readCommits({ cwd: '/path/to/repo' });\nconsole.log(renderAscii(buildGraph(commits)));\nconsole.log(renderSvg(buildGraph(commits)));\nconsole.log(statsJson(buildRepoModel({ cwd: '/path/to/repo' })));\n```\n\n`computeHealth`, `buildTimeline`, `compareRefs` and their renderers and JSON\nserialisers are exported the same way.\n\n`git-reader` shells out to the `git` binary for raw data only — commit\nmetadata, file lists, blob contents. Every piece of interpretation (graph\nlayout, question answering, diffing) is our own code.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","readmeFilename":"README.md","_rev":"1-fbb7871e54e15a76a90fb1f91e2058b0"}