@rosh100yx/outlier 0.4.24 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/outlier.js CHANGED
@@ -165,7 +165,7 @@ var require_picocolors = __commonJS((exports, module) => {
165
165
  var require_package = __commonJS((exports, module) => {
166
166
  module.exports = {
167
167
  name: "@rosh100yx/outlier",
168
- version: "0.4.24",
168
+ version: "0.7.0",
169
169
  description: "AI Code Governance & Capability Auditing for the Terminal. Measures AI reliance, context waste, and enforces local CI/CD policies.",
170
170
  bin: {
171
171
  outlier: "bin/outlier.js"
@@ -1798,37 +1798,236 @@ async function getAuthorshipStats(repoPath = process.cwd()) {
1798
1798
  }
1799
1799
 
1800
1800
  // src/carbon.ts
1801
- import { homedir } from "os";
1802
- import { join } from "path";
1803
- import { readFile, access } from "fs/promises";
1801
+ import { homedir as homedir2 } from "os";
1802
+ import { join as join2 } from "path";
1803
+ import { readFile, access, readdir } from "fs/promises";
1804
1804
  // data/grid-factors.json
1805
1805
  var grid_factors_default = {
1806
1806
  vietnam: 681,
1807
- france: 21.7,
1808
- us_east: 380,
1807
+ india_average: 715,
1808
+ indonesia: 650,
1809
+ china: 581,
1809
1810
  singapore: 408,
1810
- india_average: 715
1811
+ japan: 470,
1812
+ south_korea: 415,
1813
+ australia: 510,
1814
+ us_east: 380,
1815
+ us_west: 210,
1816
+ canada: 120,
1817
+ brazil: 95,
1818
+ uk: 210,
1819
+ germany: 350,
1820
+ france: 21.7,
1821
+ norway: 28,
1822
+ sweden: 41,
1823
+ global_average: 450
1811
1824
  };
1812
1825
 
1826
+ // src/emissions.ts
1827
+ var MODEL_ENERGY_KWH_PER_M_OUTPUT = {
1828
+ opus: 0.66,
1829
+ sonnet: 0.3,
1830
+ haiku: 0.1,
1831
+ "gpt-4": 0.55,
1832
+ "gpt-4o": 0.3,
1833
+ "gpt-5": 0.45,
1834
+ gemini: 0.35,
1835
+ flash: 0.1,
1836
+ local: 0.5,
1837
+ default: 0.45
1838
+ };
1839
+ function modelClass(modelId) {
1840
+ const m2 = (modelId || "").toLowerCase();
1841
+ if (m2.includes("opus"))
1842
+ return "opus";
1843
+ if (m2.includes("sonnet"))
1844
+ return "sonnet";
1845
+ if (m2.includes("haiku"))
1846
+ return "haiku";
1847
+ if (m2.includes("flash") || m2.includes("mini"))
1848
+ return "haiku";
1849
+ if (m2.includes("gpt-5"))
1850
+ return "gpt-5";
1851
+ if (m2.includes("gpt-4o"))
1852
+ return "gpt-4o";
1853
+ if (m2.includes("gpt-4"))
1854
+ return "gpt-4";
1855
+ if (m2.includes("gemini"))
1856
+ return "gemini";
1857
+ if (m2.includes("llama") || m2.includes("qwen") || m2.includes("mistral") || m2.includes("local"))
1858
+ return "local";
1859
+ return "default";
1860
+ }
1861
+ function energyKwhForModel(modelId, outputTokens) {
1862
+ const cls = modelClass(modelId);
1863
+ const coeff = MODEL_ENERGY_KWH_PER_M_OUTPUT[cls] ?? 0.45;
1864
+ return outputTokens / 1e6 * coeff;
1865
+ }
1866
+ function energyKwhByModel(outputByModel) {
1867
+ let kwh = 0;
1868
+ for (const [model, out] of Object.entries(outputByModel)) {
1869
+ kwh += energyKwhForModel(model, out);
1870
+ }
1871
+ return kwh;
1872
+ }
1873
+
1874
+ // src/sources.ts
1875
+ import { homedir } from "os";
1876
+ import { join } from "path";
1877
+ import { existsSync } from "fs";
1878
+ import { execSync } from "child_process";
1879
+ var HOME = homedir();
1880
+ function hasCli(cmd) {
1881
+ try {
1882
+ execSync(`command -v ${cmd}`, { stdio: "ignore" });
1883
+ return true;
1884
+ } catch {
1885
+ return false;
1886
+ }
1887
+ }
1888
+ function hasPath(p2) {
1889
+ try {
1890
+ return existsSync(p2);
1891
+ } catch {
1892
+ return false;
1893
+ }
1894
+ }
1895
+ function detectSources(cwd = process.cwd()) {
1896
+ const tools = [];
1897
+ const add = (t2) => {
1898
+ if (!tools.includes(t2))
1899
+ tools.push(t2);
1900
+ };
1901
+ const cliTools = {
1902
+ claude: "claude",
1903
+ cursor: "cursor",
1904
+ aider: "aider",
1905
+ gemini: "gemini",
1906
+ opencode: "opencode",
1907
+ cody: "cody",
1908
+ continue: "continue",
1909
+ codex: "codex"
1910
+ };
1911
+ for (const [name, cmd] of Object.entries(cliTools)) {
1912
+ if (hasCli(cmd))
1913
+ add(name);
1914
+ }
1915
+ for (const [name, dir] of Object.entries({
1916
+ claude: ".claude",
1917
+ cursor: ".cursor",
1918
+ gemini: ".gemini",
1919
+ codeium: ".codeium",
1920
+ continue: ".continue",
1921
+ aider: ".aider.conf.yml"
1922
+ })) {
1923
+ if (hasPath(join(HOME, dir)))
1924
+ add(name);
1925
+ }
1926
+ if (hasCli("codecarbon"))
1927
+ add("codecarbon");
1928
+ if (hasCli("ccusage"))
1929
+ add("ccusage");
1930
+ const slug = cwd.replace(/\//g, "-");
1931
+ const claudeProjectDir = join(HOME, ".claude", "projects", slug);
1932
+ const tokenomicsLog = join(HOME, ".claude", "tokenomics-log.jsonl");
1933
+ let tokenSource;
1934
+ if (hasPath(tokenomicsLog)) {
1935
+ tokenSource = { name: "caveman tokenomics log", provenance: "measured" };
1936
+ } else if (hasPath(claudeProjectDir)) {
1937
+ tokenSource = { name: "Claude Code transcripts", provenance: "estimated" };
1938
+ } else if (tools.includes("ccusage")) {
1939
+ tokenSource = { name: "ccusage", provenance: "estimated" };
1940
+ } else {
1941
+ tokenSource = { name: "none", provenance: "none" };
1942
+ }
1943
+ let carbonSource;
1944
+ const codecarbonData = hasPath(join(cwd, "emissions.csv")) || hasPath(join(HOME, ".codecarbon", "emissions.csv"));
1945
+ if (codecarbonData) {
1946
+ carbonSource = { name: "CodeCarbon emissions.csv", provenance: "measured" };
1947
+ } else if (tokenSource.provenance !== "none") {
1948
+ carbonSource = { name: "model+grid estimate", provenance: "estimated" };
1949
+ } else {
1950
+ carbonSource = { name: "none", provenance: "none" };
1951
+ }
1952
+ let capabilitySource;
1953
+ if (hasPath(join(HOME, ".claude", "settings.json")) || hasPath(join(cwd, "AGENTS.md")) || hasPath(join(cwd, ".mcp.json"))) {
1954
+ capabilitySource = { name: "local config (settings/AGENTS/MCP)", provenance: "measured" };
1955
+ } else {
1956
+ capabilitySource = { name: "none", provenance: "none" };
1957
+ }
1958
+ return { tools, tokenSource, carbonSource, capabilitySource };
1959
+ }
1960
+ function provLabel(s) {
1961
+ if (s.provenance === "none")
1962
+ return "no local data";
1963
+ return `${s.provenance} · ${s.name}`;
1964
+ }
1965
+
1813
1966
  // src/carbon.ts
1814
1967
  class ClaudeLogParser {
1815
1968
  baseDir;
1816
- constructor(baseDir = homedir()) {
1969
+ cwd;
1970
+ constructor(baseDir = homedir2(), cwd = process.cwd()) {
1817
1971
  this.baseDir = baseDir;
1972
+ this.cwd = cwd;
1818
1973
  }
1819
1974
  async parse() {
1820
- const logPath = join(this.baseDir, ".claude", "tokenomics-log.jsonl");
1975
+ const slug = this.cwd.replace(/\//g, "-");
1976
+ const projectDir = join2(this.baseDir, ".claude", "projects", slug);
1977
+ try {
1978
+ const files = (await readdir(projectDir)).filter((f2) => f2.endsWith(".jsonl"));
1979
+ if (files.length > 0) {
1980
+ let total2 = 0, output2 = 0, cache2 = 0;
1981
+ const sessions2 = new Set;
1982
+ const outputByModel2 = {};
1983
+ for (const file of files) {
1984
+ let text3 = "";
1985
+ try {
1986
+ text3 = await readFile(join2(projectDir, file), "utf-8");
1987
+ } catch {
1988
+ continue;
1989
+ }
1990
+ for (const line of text3.split(`
1991
+ `)) {
1992
+ if (!line.trim())
1993
+ continue;
1994
+ try {
1995
+ const d = JSON.parse(line);
1996
+ const msg = d.message || {};
1997
+ const u4 = msg.usage || d.usage;
1998
+ if (u4) {
1999
+ const inp = u4.input_tokens || 0;
2000
+ const out = u4.output_tokens || 0;
2001
+ const cr = u4.cache_read_input_tokens || 0;
2002
+ const cw = u4.cache_creation_input_tokens || 0;
2003
+ total2 += inp + out + cr + cw;
2004
+ output2 += out;
2005
+ cache2 += cr;
2006
+ const model = msg.model || "default";
2007
+ outputByModel2[model] = (outputByModel2[model] || 0) + out;
2008
+ }
2009
+ if (d.sessionId)
2010
+ sessions2.add(d.sessionId);
2011
+ } catch {}
2012
+ }
2013
+ }
2014
+ return { total: total2, output: output2, cache: cache2, sessions: sessions2.size, cost: 0, outputByModel: outputByModel2 };
2015
+ }
2016
+ } catch {}
2017
+ const logPath = join2(this.baseDir, ".claude", "tokenomics-log.jsonl");
1821
2018
  try {
1822
2019
  await access(logPath);
1823
2020
  } catch {
1824
- return { total: 0, output: 0, cache: 0, sessions: 0, cost: 0 };
2021
+ return { total: 0, output: 0, cache: 0, sessions: 0, cost: 0, outputByModel: {} };
1825
2022
  }
1826
2023
  const text2 = await readFile(logPath, "utf-8");
1827
- const lines = text2.trim().split(`
1828
- `).filter((l2) => l2.length > 0);
1829
2024
  let total = 0, output = 0, cache = 0, cost = 0;
1830
2025
  const sessions = new Set;
1831
- for (const line of lines) {
2026
+ const outputByModel = {};
2027
+ for (const line of text2.split(`
2028
+ `)) {
2029
+ if (!line.trim())
2030
+ continue;
1832
2031
  try {
1833
2032
  const data = JSON.parse(line);
1834
2033
  total += data.total_tokens || 0;
@@ -1837,15 +2036,17 @@ class ClaudeLogParser {
1837
2036
  cost += data.cost_usd || 0;
1838
2037
  if (data.session_id)
1839
2038
  sessions.add(data.session_id);
1840
- } catch (e) {}
2039
+ const model = data.model || "default";
2040
+ outputByModel[model] = (outputByModel[model] || 0) + (data.output_tokens || 0);
2041
+ } catch {}
1841
2042
  }
1842
- return { total, output, cache, sessions: sessions.size, cost };
2043
+ return { total, output, cache, sessions: sessions.size, cost, outputByModel };
1843
2044
  }
1844
2045
  }
1845
2046
 
1846
2047
  class CursorLogParser {
1847
2048
  async parse() {
1848
- return { total: 0, output: 0, cache: 0, sessions: 0, cost: 0 };
2049
+ return { total: 0, output: 0, cache: 0, sessions: 0, cost: 0, outputByModel: {} };
1849
2050
  }
1850
2051
  }
1851
2052
  function estimateUsd(output, cacheRead, total) {
@@ -1866,11 +2067,12 @@ function getLocalGridFactor() {
1866
2067
  if (tz.includes("Calcutta") || tz.includes("Kolkata") || tz.includes("Asia/Kabul"))
1867
2068
  return { region: "India", factor: grid_factors_default.india_average };
1868
2069
  } catch (e) {}
1869
- return { region: "Global Average", factor: 450 };
2070
+ return { region: "Global Average", factor: grid_factors_default.global_average };
1870
2071
  }
1871
2072
  async function getCarbonStats() {
1872
2073
  const parsers = [new ClaudeLogParser, new CursorLogParser];
1873
2074
  let totalTokens = 0, outputTokens = 0, cacheReadTokens = 0, sessions = 0, loggedCost = 0;
2075
+ const outputByModel = {};
1874
2076
  for (const parser of parsers) {
1875
2077
  const stats = await parser.parse();
1876
2078
  totalTokens += stats.total;
@@ -1878,9 +2080,13 @@ async function getCarbonStats() {
1878
2080
  cacheReadTokens += stats.cache;
1879
2081
  sessions += stats.sessions;
1880
2082
  loggedCost += stats.cost;
2083
+ for (const [m2, out] of Object.entries(stats.outputByModel)) {
2084
+ outputByModel[m2] = (outputByModel[m2] || 0) + out;
2085
+ }
1881
2086
  }
1882
- const energyKwh = outputTokens / 1e6 * 0.662;
2087
+ const energyKwh = energyKwhByModel(outputByModel);
1883
2088
  const localGrid = getLocalGridFactor();
2089
+ const sources = detectSources();
1884
2090
  const costIsReal = loggedCost > 0;
1885
2091
  const estUsd = costIsReal ? loggedCost : estimateUsd(outputTokens, cacheReadTokens, totalTokens);
1886
2092
  return {
@@ -1894,80 +2100,152 @@ async function getCarbonStats() {
1894
2100
  localRegion: localGrid.region,
1895
2101
  sessions,
1896
2102
  estUsd,
1897
- costIsReal
2103
+ costIsReal,
2104
+ tokenProvenance: sources.tokenSource.provenance,
2105
+ carbonProvenance: sources.carbonSource.provenance,
2106
+ sourceLabel: provLabel(sources.tokenSource)
1898
2107
  };
1899
2108
  }
1900
2109
 
1901
2110
  // src/capabilities.ts
1902
- import { homedir as homedir2 } from "os";
1903
- import { join as join2 } from "path";
1904
- import { existsSync, readdirSync } from "fs";
1905
- async function getCapabilitiesStats(repoPath = process.cwd(), homeDirPath = homedir2()) {
1906
- const stats = {
1907
- mcps: [],
1908
- skills: [],
1909
- hasOrchestration: false
1910
- };
1911
- if (existsSync(join2(repoPath, "AGENTS.md"))) {
1912
- stats.hasOrchestration = true;
1913
- }
1914
- const projectSkillsPath = join2(repoPath, ".agents", "skills");
1915
- if (existsSync(projectSkillsPath)) {
1916
- try {
1917
- const skills = readdirSync(projectSkillsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
1918
- stats.skills.push(...skills);
1919
- } catch (e) {}
2111
+ import { homedir as homedir3 } from "os";
2112
+ import { join as join3 } from "path";
2113
+ import { existsSync as existsSync2, readdirSync, readFileSync } from "fs";
2114
+ function classifyReach(name) {
2115
+ const n2 = name.toLowerCase();
2116
+ if (/(stripe|payment|infinity|kucoin|wallet|billing|payout)/.test(n2))
2117
+ return "money";
2118
+ if (/(shell|bash|exec|terminal|command|sandbox)/.test(n2))
2119
+ return "exec";
2120
+ if (/(cloudflare|vercel|netlify|modal|deploy|fly\.io|render|heroku|aws|gcp|azure)/.test(n2))
2121
+ return "deploy";
2122
+ if (/(github|gitlab|git|bitbucket)/.test(n2))
2123
+ return "write-remote";
2124
+ if (/(filesystem|file|fs|disk)/.test(n2))
2125
+ return "write-local";
2126
+ if (/(memory|supermemory|mem|obsidian|notion|airtable|coda|database|sql|store)/.test(n2))
2127
+ return "data";
2128
+ if (/(openrouter|ollama|openai|anthropic|llm|model|hugging)/.test(n2))
2129
+ return "model";
2130
+ if (/(exa|web|fetch|search|brave|browser|http|scrape)/.test(n2))
2131
+ return "network";
2132
+ return "network";
2133
+ }
2134
+ var REACH_RISK = {
2135
+ read: 0,
2136
+ model: 1,
2137
+ network: 1,
2138
+ data: 2,
2139
+ "write-local": 2,
2140
+ "write-remote": 3,
2141
+ deploy: 3,
2142
+ exec: 4,
2143
+ money: 4
2144
+ };
2145
+ function scoreBlast(reaches) {
2146
+ const reasons = [];
2147
+ const has = (r2) => reaches.includes(r2);
2148
+ if (has("money"))
2149
+ reasons.push("can move money");
2150
+ if (has("exec"))
2151
+ reasons.push("can run shell commands");
2152
+ if (has("deploy"))
2153
+ reasons.push("can deploy to production");
2154
+ if (has("write-remote"))
2155
+ reasons.push("can push to your remote repos");
2156
+ if (has("write-local"))
2157
+ reasons.push("can write your local files");
2158
+ if (has("data"))
2159
+ reasons.push("can read/write your stored data");
2160
+ const netCount = reaches.filter((r2) => r2 === "network" || r2 === "model").length;
2161
+ if (netCount >= 3)
2162
+ reasons.push(`reaches ${netCount} external services`);
2163
+ const max = reaches.reduce((m2, r2) => Math.max(m2, REACH_RISK[r2]), 0);
2164
+ let radius = "LOW";
2165
+ if (max >= 4)
2166
+ radius = "CRITICAL";
2167
+ else if (max >= 3)
2168
+ radius = "HIGH";
2169
+ else if (max >= 2)
2170
+ radius = "MEDIUM";
2171
+ return { radius, reasons };
2172
+ }
2173
+ function readJson(path) {
2174
+ try {
2175
+ return JSON.parse(readFileSync(path, "utf-8"));
2176
+ } catch {
2177
+ return null;
1920
2178
  }
1921
- const geminiSkillsPath = join2(homeDirPath, ".gemini", "skills");
1922
- if (existsSync(geminiSkillsPath)) {
1923
- try {
1924
- const skills = readdirSync(geminiSkillsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
1925
- for (const skill of skills) {
1926
- if (!stats.skills.includes(skill))
1927
- stats.skills.push(skill);
1928
- }
1929
- } catch (e) {}
2179
+ }
2180
+ function countDir(path, ext = ".md") {
2181
+ try {
2182
+ return readdirSync(path).filter((f2) => f2.endsWith(ext)).length;
2183
+ } catch {
2184
+ return 0;
1930
2185
  }
1931
- const geminiMcpPath = join2(homeDirPath, ".gemini", "antigravity-cli", "mcp");
1932
- if (existsSync(geminiMcpPath)) {
2186
+ }
2187
+ async function getCapabilitiesStats(repoPath = process.cwd(), homeDirPath = homedir3()) {
2188
+ const mcpNames = new Set;
2189
+ for (const cfg of [
2190
+ join3(homeDirPath, ".claude.json"),
2191
+ join3(homeDirPath, ".claude", "settings.json"),
2192
+ join3(repoPath, ".mcp.json"),
2193
+ join3(repoPath, ".claude", "settings.json")
2194
+ ]) {
2195
+ const j = readJson(cfg);
2196
+ if (j?.mcpServers)
2197
+ Object.keys(j.mcpServers).forEach((k) => mcpNames.add(k));
2198
+ }
2199
+ const geminiMcp = join3(homeDirPath, ".gemini", "antigravity-cli", "mcp");
2200
+ if (existsSync2(geminiMcp)) {
1933
2201
  try {
1934
- const mcps = readdirSync(geminiMcpPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
1935
- stats.mcps.push(...mcps);
1936
- } catch (e) {}
2202
+ readdirSync(geminiMcp, { withFileTypes: true }).filter((d) => d.isDirectory()).forEach((d) => mcpNames.add(d.name));
2203
+ } catch {}
1937
2204
  }
1938
- const claudeSettingsPath = join2(homeDirPath, ".claude", "settings.json");
1939
- if (existsSync(claudeSettingsPath)) {
1940
- try {
1941
- const claudeSettings = __require(claudeSettingsPath);
1942
- if (claudeSettings.enabledPlugins) {
1943
- Object.keys(claudeSettings.enabledPlugins).forEach((plugin) => {
1944
- if (claudeSettings.enabledPlugins[plugin] && !stats.mcps.includes(plugin)) {
1945
- stats.mcps.push(`plugin:${plugin}`);
1946
- }
2205
+ const mcps = [...mcpNames].map((name) => ({ name, reach: classifyReach(name) }));
2206
+ const skills = [];
2207
+ for (const p2 of [join3(repoPath, ".agents", "skills"), join3(homeDirPath, ".claude", "skills"), join3(homeDirPath, ".gemini", "skills")]) {
2208
+ if (existsSync2(p2)) {
2209
+ try {
2210
+ readdirSync(p2, { withFileTypes: true }).filter((d) => d.isDirectory()).forEach((d) => {
2211
+ if (!skills.includes(d.name))
2212
+ skills.push(d.name);
1947
2213
  });
1948
- }
1949
- } catch (e) {}
2214
+ } catch {}
2215
+ }
2216
+ }
2217
+ const subagents = countDir(join3(homeDirPath, ".claude", "agents")) + countDir(join3(repoPath, ".claude", "agents"));
2218
+ const hooks = [];
2219
+ for (const cfg of [join3(homeDirPath, ".claude", "settings.json"), join3(repoPath, ".claude", "settings.json")]) {
2220
+ const j = readJson(cfg);
2221
+ if (j?.hooks)
2222
+ Object.keys(j.hooks).forEach((k) => {
2223
+ if (!hooks.includes(k))
2224
+ hooks.push(k);
2225
+ });
1950
2226
  }
1951
- return stats;
2227
+ const hasOrchestration = existsSync2(join3(repoPath, "AGENTS.md")) || existsSync2(join3(repoPath, ".mcp.json"));
2228
+ const { radius, reasons } = scoreBlast(mcps.map((m2) => m2.reach));
2229
+ return { mcps, skills, subagents, hooks, hasOrchestration, blastRadius: radius, blastReasons: reasons };
1952
2230
  }
1953
2231
 
1954
2232
  // src/cli.ts
1955
- import { writeFileSync, readFileSync as readFileSync2, chmodSync, existsSync as existsSync3 } from "fs";
1956
- import { join as join4 } from "path";
2233
+ import { writeFileSync, readFileSync as readFileSync3, chmodSync, existsSync as existsSync4 } from "fs";
2234
+ import { join as join5 } from "path";
1957
2235
 
1958
2236
  // src/agent.ts
1959
2237
  import { spawnSync as spawnSync2 } from "child_process";
1960
- import { readFileSync, existsSync as existsSync2 } from "fs";
1961
- import { join as join3 } from "path";
2238
+ import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
2239
+ import { join as join4 } from "path";
1962
2240
  import os from "os";
1963
2241
  function detectAgent() {
1964
2242
  const agents = ["claude", "cursor", "aider", "hermes", "cody", "continue", "opencode", "gemini"];
1965
2243
  try {
1966
2244
  const home = os.homedir();
1967
- const historyFiles = [join3(home, ".zsh_history"), join3(home, ".bash_history")];
2245
+ const historyFiles = [join4(home, ".zsh_history"), join4(home, ".bash_history")];
1968
2246
  for (const file of historyFiles) {
1969
- if (existsSync2(file)) {
1970
- const content = readFileSync(file, "utf8");
2247
+ if (existsSync3(file)) {
2248
+ const content = readFileSync2(file, "utf8");
1971
2249
  const lines = content.split(`
1972
2250
  `).filter(Boolean).slice(-500).reverse();
1973
2251
  for (const line of lines) {
@@ -2004,6 +2282,66 @@ var ASCII_LOGO = `
2004
2282
  \\____/|_| |_| |_| |_| |_|___|_____|_| \\_\\
2005
2283
  `;
2006
2284
  var finalReceipt = "";
2285
+ async function emitJson() {
2286
+ const pkg = require_package();
2287
+ const [gitStats, carbon, caps] = await Promise.all([
2288
+ getAuthorshipStats().catch(() => null),
2289
+ getCarbonStats().catch(() => null),
2290
+ getCapabilitiesStats().catch(() => null)
2291
+ ]);
2292
+ const aiRatio = gitStats ? gitStats.ratio : 0;
2293
+ const cap = 0.7;
2294
+ const writeOrDeploy = caps ? caps.mcps.filter((m2) => ["money", "exec", "deploy", "write-remote", "write-local"].includes(m2.reach)).length : 0;
2295
+ const out = {
2296
+ tool: "outlier",
2297
+ version: pkg.version,
2298
+ repo: process.cwd().split("/").pop(),
2299
+ generatedAt: new Date().toISOString(),
2300
+ localFirst: true,
2301
+ authorship: gitStats ? {
2302
+ aiPercent: +(gitStats.ratio * 100).toFixed(1),
2303
+ aiRatio: gitStats.ratio,
2304
+ totalCommits: gitStats.total,
2305
+ aiCommits: gitStats.ai,
2306
+ nonMergePercent: +(gitStats.ratioNoMerges * 100).toFixed(1),
2307
+ provenance: "proxy",
2308
+ note: "git Co-Authored-By trailers; under-counts if the agent omits the trailer"
2309
+ } : null,
2310
+ cost: carbon ? {
2311
+ totalTokens: carbon.totalTokens,
2312
+ outputTokens: carbon.outputTokens,
2313
+ cacheReusePercent: carbon.totalTokens ? +(carbon.cacheReadTokens / carbon.totalTokens * 100).toFixed(1) : 0,
2314
+ estUsd: +carbon.estUsd.toFixed(2),
2315
+ costIsReal: carbon.costIsReal,
2316
+ provenance: carbon.tokenProvenance,
2317
+ source: carbon.sourceLabel
2318
+ } : null,
2319
+ carbon: carbon ? {
2320
+ energyKwh: +carbon.energyKwh.toFixed(4),
2321
+ co2Kg: +carbon.localCo2Kg.toFixed(4),
2322
+ region: carbon.localRegion,
2323
+ provenance: carbon.carbonProvenance,
2324
+ note: "counterfactual: cloud inference runs on the provider grid, not yours"
2325
+ } : null,
2326
+ reach: caps ? {
2327
+ blastRadius: caps.blastRadius,
2328
+ reasons: caps.blastReasons,
2329
+ toolCount: caps.mcps.length,
2330
+ writeOrDeployCount: writeOrDeploy,
2331
+ tools: caps.mcps,
2332
+ subagents: caps.subagents,
2333
+ hooks: caps.hooks,
2334
+ skills: caps.skills.length,
2335
+ orchestration: caps.hasOrchestration
2336
+ } : null,
2337
+ policy: {
2338
+ aiCapPercent: cap * 100,
2339
+ status: aiRatio > cap ? "over" : "within"
2340
+ }
2341
+ };
2342
+ process.stdout.write(JSON.stringify(out, null, 2) + `
2343
+ `);
2344
+ }
2007
2345
  async function runOnboarding() {
2008
2346
  console.log(import_picocolors.default.cyan(ASCII_LOGO));
2009
2347
  intro(import_picocolors.default.inverse(" outlier: Welcome "));
@@ -2025,18 +2363,18 @@ As agents write more of our code, we lose visibility into:
2025
2363
  cancel("Onboarding paused. Run outlier again when you are ready.");
2026
2364
  process.exit(0);
2027
2365
  }
2028
- const configPath = join4(os2.homedir(), ".outlier_config");
2366
+ const configPath = join5(os2.homedir(), ".outlier_config");
2029
2367
  writeFileSync(configPath, JSON.stringify({ onboarded: true, date: new Date().toISOString() }));
2030
2368
  }
2031
2369
  async function main() {
2032
2370
  let action = process.argv[2];
2033
2371
  if (action === "daily-greeting") {
2034
- const configPath2 = join4(os2.homedir(), ".outlier_config");
2372
+ const configPath2 = join5(os2.homedir(), ".outlier_config");
2035
2373
  const today = new Date().toISOString().split("T")[0];
2036
2374
  let alreadyRun = false;
2037
- if (existsSync3(configPath2)) {
2375
+ if (existsSync4(configPath2)) {
2038
2376
  try {
2039
- const cfg = JSON.parse(readFileSync2(configPath2, "utf8"));
2377
+ const cfg = JSON.parse(readFileSync3(configPath2, "utf8"));
2040
2378
  if (cfg.lastGreetingDate === today) {
2041
2379
  alreadyRun = true;
2042
2380
  } else {
@@ -2049,29 +2387,41 @@ async function main() {
2049
2387
  process.exit(0);
2050
2388
  action = "status";
2051
2389
  }
2390
+ if (process.argv.includes("--json")) {
2391
+ await emitJson();
2392
+ process.exit(0);
2393
+ }
2052
2394
  console.log(import_picocolors.default.cyan(ASCII_LOGO));
2053
2395
  const pkg = require_package();
2054
2396
  console.log(import_picocolors.default.dim(` Outlier v${pkg.version} · AI Code Reliance & Telemetry Engine
2055
2397
  `));
2056
2398
  if (action === "--help" || action === "-h" || action === "help") {
2057
2399
  console.log(import_picocolors.default.bold(`
2058
- COMMANDS:`));
2059
- console.log(` ${import_picocolors.default.cyan("outlier")} Interactive menu (Onboarding for first-timers)`);
2060
- console.log(` ${import_picocolors.default.cyan("outlier status")} Run full AI reliance & capability audit`);
2061
- console.log(` ${import_picocolors.default.cyan("outlier authorship")} Scan git history for AI co-authorship ratio`);
2062
- console.log(` ${import_picocolors.default.cyan("outlier carbon")} Scan local logs for token waste & carbon cost`);
2063
- console.log(` ${import_picocolors.default.cyan("outlier policy")} Configure CI/CD guardrails and thresholds`);
2064
- console.log(` ${import_picocolors.default.cyan("outlier impact")} See the compounding horizon of AI Deskilling`);
2065
- console.log(` ${import_picocolors.default.cyan("outlier knowledge")} Explore references, graphs, and the core literature`);
2066
- console.log(` ${import_picocolors.default.cyan("outlier participate")} Help build the academic literature on AI deskilling`);
2067
- console.log(` ${import_picocolors.default.cyan("outlier init")} Install the once-per-day shell greeting`);
2068
- console.log(` ${import_picocolors.default.cyan("outlier uninit")} Remove the shell greeting`);
2400
+ WHAT OUTLIER DOES`));
2401
+ console.log(import_picocolors.default.dim(" Reads your local git history and AI logs — on your machine — to show"));
2402
+ console.log(import_picocolors.default.dim(` how much of your code AI wrote, what it cost, and how to keep your skill.
2403
+ `));
2404
+ console.log(import_picocolors.default.bold("COMMANDS:"));
2405
+ console.log(` ${import_picocolors.default.cyan("outlier")} Run the audit (the default — same as 'status')`);
2406
+ console.log(` ${import_picocolors.default.cyan("outlier status")} Full audit: who wrote the code, what it cost, your limit`);
2407
+ console.log(` ${import_picocolors.default.cyan("outlier status --save")} Save the audit to ./outlier-audit.txt`);
2408
+ console.log(` ${import_picocolors.default.cyan("outlier --json")} Machine-readable audit (for agents, CI, swarms)`);
2409
+ console.log(` ${import_picocolors.default.cyan("outlier authorship")} Just the AI-vs-human commit breakdown`);
2410
+ console.log(` ${import_picocolors.default.cyan("outlier carbon")} Just the token spend, cache waste & carbon`);
2411
+ console.log(` ${import_picocolors.default.cyan("outlier capabilities")} What tools & skills your agents can reach`);
2412
+ console.log(` ${import_picocolors.default.cyan("outlier policy")} Set an AI-authorship limit (local git hook / CI)`);
2413
+ console.log(` ${import_picocolors.default.cyan("outlier impact")} What AI reliance compounds to over time`);
2414
+ console.log(` ${import_picocolors.default.cyan("outlier knowledge")} The research behind the metrics`);
2415
+ console.log(` ${import_picocolors.default.cyan("outlier participate")} Share anonymous feedback for the deskilling study`);
2416
+ console.log(` ${import_picocolors.default.cyan("outlier init")} Show a once-per-day reliance greeting in new shells`);
2417
+ console.log(` ${import_picocolors.default.cyan("outlier uninit")} Remove that greeting`);
2069
2418
  console.log(`
2070
- ` + import_picocolors.default.dim("Run without arguments to start the interactive wizard."));
2419
+ ` + import_picocolors.default.dim("Local-first: nothing ever leaves your machine."));
2420
+ console.log(import_picocolors.default.dim("How it works → https://github.com/rosh100yx/outlier#how-it-works"));
2071
2421
  process.exit(0);
2072
2422
  }
2073
- const configPath = join4(os2.homedir(), ".outlier_config");
2074
- if (!existsSync3(configPath) && !action) {
2423
+ const configPath = join5(os2.homedir(), ".outlier_config");
2424
+ if (!existsSync4(configPath) && !action) {
2075
2425
  await runOnboarding();
2076
2426
  action = "status";
2077
2427
  }
@@ -2082,7 +2432,7 @@ COMMANDS:`));
2082
2432
  if (action === "init" || action === "uninit") {
2083
2433
  const shell = process.env.SHELL || "";
2084
2434
  const rcName = shell.includes("zsh") ? ".zshrc" : ".bashrc";
2085
- const rcPath = join4(os2.homedir(), rcName);
2435
+ const rcPath = join5(os2.homedir(), rcName);
2086
2436
  const START_MARKER = "# --- OUTLIER PRE-FLIGHT RITUAL START ---";
2087
2437
  const END_MARKER = "# --- OUTLIER PRE-FLIGHT RITUAL END ---";
2088
2438
  const BLOCK = `
@@ -2102,8 +2452,8 @@ ${END_MARKER}
2102
2452
  process.exit(0);
2103
2453
  }
2104
2454
  let content = "";
2105
- if (existsSync3(rcPath))
2106
- content = readFileSync2(rcPath, "utf8");
2455
+ if (existsSync4(rcPath))
2456
+ content = readFileSync3(rcPath, "utf8");
2107
2457
  if (content.includes(START_MARKER)) {
2108
2458
  note(`Outlier is already initialized in ${rcName}`);
2109
2459
  } else {
@@ -2112,8 +2462,8 @@ ${END_MARKER}
2112
2462
  }
2113
2463
  process.exit(0);
2114
2464
  } else if (action === "uninit") {
2115
- if (existsSync3(rcPath)) {
2116
- let content = readFileSync2(rcPath, "utf8");
2465
+ if (existsSync4(rcPath)) {
2466
+ let content = readFileSync3(rcPath, "utf8");
2117
2467
  if (content.includes(START_MARKER)) {
2118
2468
  const regex = new RegExp(`\\n?${START_MARKER}[\\s\\S]*?${END_MARKER}\\n?`, "g");
2119
2469
  content = content.replace(regex, `
@@ -2182,10 +2532,10 @@ Conservative Floor: ${color(nmPct + "%")}`, "Git Authorship Breakdown");
2182
2532
  let carbon = null;
2183
2533
  let capabilities = null;
2184
2534
  let skipDelay = false;
2185
- const configPath2 = join4(os2.homedir(), ".outlier_config");
2186
- if (existsSync3(configPath2)) {
2535
+ const configPath2 = join5(os2.homedir(), ".outlier_config");
2536
+ if (existsSync4(configPath2)) {
2187
2537
  try {
2188
- const cfg = JSON.parse(readFileSync2(configPath2, "utf8"));
2538
+ const cfg = JSON.parse(readFileSync3(configPath2, "utf8"));
2189
2539
  if (cfg.seenNarration)
2190
2540
  skipDelay = true;
2191
2541
  } catch (e) {}
@@ -2223,7 +2573,7 @@ Conservative Floor: ${color(nmPct + "%")}`, "Git Authorship Breakdown");
2223
2573
  await new Promise((r2) => setTimeout(r2, 600));
2224
2574
  if (!skipDelay) {
2225
2575
  try {
2226
- const cfg = existsSync3(configPath2) ? JSON.parse(readFileSync2(configPath2, "utf8")) : {};
2576
+ const cfg = existsSync4(configPath2) ? JSON.parse(readFileSync3(configPath2, "utf8")) : {};
2227
2577
  cfg.seenNarration = true;
2228
2578
  writeFileSync(configPath2, JSON.stringify(cfg));
2229
2579
  } catch (e) {}
@@ -2237,21 +2587,37 @@ Conservative Floor: ${color(nmPct + "%")}`, "Git Authorship Breakdown");
2237
2587
  s.stop("Audit complete");
2238
2588
  try {
2239
2589
  let authPct = "0%";
2590
+ let nmFloorStr = "";
2240
2591
  let ruleFailures = 0;
2241
2592
  if (gitStats) {
2242
2593
  authPct = `${(gitStats.ratio * 100).toFixed(1)}%`;
2594
+ nmFloorStr = ` ${import_picocolors.default.dim(`(${(gitStats.ratioNoMerges * 100).toFixed(0)}% excl. merges)`)}`;
2243
2595
  if (gitStats.ratio > 0.7)
2244
2596
  ruleFailures++;
2245
2597
  }
2598
+ const lowTrailerWarn = gitStats && gitStats.ratio < 0.1 && carbon && carbon.totalTokens > 1e6 ? `
2599
+ ${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("Low %? Your agent may not tag commits — outlier counts only")}
2600
+ ${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("commits with a Co-Authored-By trailer.")}` : "";
2246
2601
  let cachePct = "0";
2247
2602
  let co2Str = "0.0kg";
2248
2603
  let regionStr = "Global Average";
2604
+ let sourceLabel = "no local AI logs found";
2605
+ let noData = true;
2249
2606
  if (carbon) {
2250
2607
  if (carbon.totalTokens > 0) {
2251
2608
  cachePct = (carbon.cacheReadTokens / carbon.totalTokens * 100).toFixed(1);
2609
+ noData = false;
2252
2610
  }
2253
2611
  co2Str = `${carbon.localCo2Kg.toFixed(2)}kg CO2`;
2254
2612
  regionStr = carbon.localRegion;
2613
+ sourceLabel = carbon.sourceLabel;
2614
+ }
2615
+ let reachStr = import_picocolors.default.dim("run: outlier capabilities");
2616
+ if (capabilities) {
2617
+ const rc = capabilities.blastRadius;
2618
+ const col = rc === "CRITICAL" || rc === "HIGH" ? import_picocolors.default.red : rc === "MEDIUM" ? import_picocolors.default.yellow : import_picocolors.default.green;
2619
+ const risky = capabilities.mcps.filter((m2) => ["money", "exec", "deploy", "write-remote", "write-local"].includes(m2.reach)).length;
2620
+ reachStr = `${col(import_picocolors.default.bold(rc))} · ${capabilities.mcps.length} tools` + (risky ? import_picocolors.default.dim(`, ${risky} can write/deploy`) : "");
2255
2621
  }
2256
2622
  const isDanger = gitStats && gitStats.ratio > 0.7;
2257
2623
  const verdictZone = isDanger ? import_picocolors.default.red("Mostly AI") : import_picocolors.default.green("You're driving");
@@ -2288,29 +2654,36 @@ Conservative Floor: ${color(nmPct + "%")}`, "Git Authorship Breakdown");
2288
2654
  ${import_picocolors.default.dim("│")} ${import_picocolors.default.cyan("█▄█ █▄█ ░█░ █▄▄ █ ██▄ █▀▄")} ${import_picocolors.default.dim(`:: ${repoName} · ${dateStr}`)}
2289
2655
  ${import_picocolors.default.dim("├────────────────────────────────────────────────────────")}
2290
2656
  ${import_picocolors.default.dim("│")} ${import_picocolors.default.bold(import_picocolors.default.bgBlue(" WHO WROTE THE CODE "))}
2291
- ${import_picocolors.default.dim("│")} AI ${aiBar} ${authorshipStr}
2657
+ ${import_picocolors.default.dim("│")} AI ${aiBar} ${authorshipStr}${nmFloorStr}
2292
2658
  ${import_picocolors.default.dim("│")} You ${humanBar} ${import_picocolors.default.bold(humanSov)}
2659
+ ${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("Typical: solo devs 10–40% · AI-framework repos up to ~80%")}
2293
2660
  ${import_picocolors.default.dim("│")}
2294
2661
  ${import_picocolors.default.dim("│")} ${verdictZone} — ${verdictText.split(`
2295
2662
  `).join(`
2296
- ` + import_picocolors.default.dim("│") + " ")}
2663
+ ` + import_picocolors.default.dim("│") + " ")}${lowTrailerWarn}
2297
2664
  ${import_picocolors.default.dim("├────────────────────────────────────────────────────────")}
2298
2665
  ${import_picocolors.default.dim("│")} ${import_picocolors.default.bold(import_picocolors.default.bgMagenta(" WHAT IT COST "))}
2299
2666
  ${import_picocolors.default.dim("│")} Tokens used ${import_picocolors.default.bold(totalTokensStr)}
2300
2667
  ${import_picocolors.default.dim("│")} Est. spend ${import_picocolors.default.bold(estUsdStr)}
2301
2668
  ${import_picocolors.default.dim("│")} Re-used context ${cacheBar} ${import_picocolors.default.bold(cachePct + "%")}
2302
- ${import_picocolors.default.dim("│")} Energy ${import_picocolors.default.bold(co2Str)} ${import_picocolors.default.dim(`(${regionStr} grid, rough)`)}
2669
+ ${import_picocolors.default.dim("│")} Energy ${import_picocolors.default.bold(co2Str)} ${import_picocolors.default.dim(`(${regionStr} grid)`)}
2670
+ ${import_picocolors.default.dim("│")} ${import_picocolors.default.dim(`Source: ${sourceLabel}`)}
2303
2671
  ${import_picocolors.default.dim("│")}
2304
2672
  ${import_picocolors.default.dim("│")} ${cacheVerdict} — ${cacheText.split(`
2305
2673
  `).join(`
2306
2674
  ` + import_picocolors.default.dim("│") + " ")}
2307
2675
  ${import_picocolors.default.dim("├────────────────────────────────────────────────────────")}
2676
+ ${import_picocolors.default.dim("│")} ${import_picocolors.default.bold(import_picocolors.default.bgCyan(import_picocolors.default.black(" WHAT YOUR AGENTS CAN REACH ")))}
2677
+ ${import_picocolors.default.dim("│")} Blast radius ${reachStr}
2678
+ ${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("Full map (deploy/push/write tools): outlier capabilities")}
2679
+ ${import_picocolors.default.dim("├────────────────────────────────────────────────────────")}
2308
2680
  ${import_picocolors.default.dim("│")} ${import_picocolors.default.bold(import_picocolors.default.bgYellow(import_picocolors.default.black(" YOUR LIMIT ")))}
2309
2681
  ${import_picocolors.default.dim("│")} AI cap ${import_picocolors.default.bold("70%")} ${import_picocolors.default.dim("· change with: outlier policy")}
2310
2682
  ${import_picocolors.default.dim("│")} Status ${policyStatus} ${import_picocolors.default.dim("·")} ${policyAction}
2311
2683
  ${import_picocolors.default.dim("├────────────────────────────────────────────────────────")}
2312
- ${import_picocolors.default.dim("│")} ${import_picocolors.default.dim(import_picocolors.default.italic("Run this before you start. Keep the skill while you"))}
2313
- ${import_picocolors.default.dim("│")} ${import_picocolors.default.dim(import_picocolors.default.italic("use the speed."))}
2684
+ ${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("Numbers are local estimates authorship is a proxy and")}
2685
+ ${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("carbon is rough. How it works: outlier --help")}
2686
+ ${import_picocolors.default.dim("│")} ${import_picocolors.default.dim(import_picocolors.default.italic("Run this before you start. Keep the skill while you use the speed."))}
2314
2687
  ${import_picocolors.default.dim("└────────────────────────────────────────────────────────")}`;
2315
2688
  } else {
2316
2689
  note(`status: ${authPct} AI Reliance | ${cachePct}% Cache Bloat | ${co2Str}`, `${import_picocolors.default.bold("[outlier]")} CI/CD Audit`);
@@ -2320,23 +2693,42 @@ Conservative Floor: ${color(nmPct + "%")}`, "Git Authorship Breakdown");
2320
2693
  console.error(import_picocolors.default.red(e.message));
2321
2694
  }
2322
2695
  } else if (action === "capabilities") {
2323
- s.start("Auditing AI surface area (MCPs, Skills, Orchestrators)...");
2696
+ s.start("Mapping what your agents can reach...");
2324
2697
  try {
2325
2698
  const caps = await getCapabilitiesStats();
2326
- s.stop("Capabilities Scan Complete");
2327
- note(`Orchestration Policy: ${caps.hasOrchestration ? import_picocolors.default.green("Detected (AGENTS.md)") : import_picocolors.default.yellow("None")}
2699
+ s.stop("Reach map complete");
2700
+ const radiusColor = caps.blastRadius === "CRITICAL" ? import_picocolors.default.red : caps.blastRadius === "HIGH" ? import_picocolors.default.red : caps.blastRadius === "MEDIUM" ? import_picocolors.default.yellow : import_picocolors.default.green;
2701
+ const order = ["money", "exec", "deploy", "write-remote", "write-local", "data", "network", "model", "read"];
2702
+ const reachLabel = {
2703
+ money: "can move money",
2704
+ exec: "can run shell",
2705
+ deploy: "can deploy",
2706
+ "write-remote": "can push to repos",
2707
+ "write-local": "can write files",
2708
+ data: "data stores",
2709
+ network: "network",
2710
+ model: "models",
2711
+ read: "read-only"
2712
+ };
2713
+ const riskyReaches = new Set(["money", "exec", "deploy", "write-remote", "write-local"]);
2714
+ const toolLines = caps.mcps.length === 0 ? " None detected" : order.filter((r2) => caps.mcps.some((m2) => m2.reach === r2)).map((r2) => {
2715
+ const names = caps.mcps.filter((m2) => m2.reach === r2).map((m2) => m2.name).join(", ");
2716
+ const tag = riskyReaches.has(r2) ? import_picocolors.default.red(`[${reachLabel[r2]}]`) : import_picocolors.default.dim(`[${reachLabel[r2]}]`);
2717
+ return ` ${tag} ${names}`;
2718
+ }).join(`
2719
+ `);
2720
+ note(`${import_picocolors.default.bold("BLAST RADIUS:")} ${radiusColor(import_picocolors.default.bold(caps.blastRadius))} ${import_picocolors.default.dim("— if an agent or a prompt injection drives your tools")}
2721
+ ${caps.blastReasons.length ? caps.blastReasons.map((r2) => ` ${import_picocolors.default.red("•")} ${r2}`).join(`
2722
+ `) : import_picocolors.default.green(" • read-only — limited reach")}
2328
2723
 
2329
- Active Skills (${caps.skills.length}):
2330
- ${caps.skills.length > 0 ? import_picocolors.default.cyan(caps.skills.map((s2) => ` • ${s2}`).join(`
2331
- `)) : " None"}
2724
+ ${import_picocolors.default.bold(`What your agents can reach (${caps.mcps.length} MCP tools):`)}
2725
+ ${toolLines}
2332
2726
 
2333
- Active MCP Servers (${caps.mcps.length}):
2334
- ${caps.mcps.length > 0 ? import_picocolors.default.magenta(caps.mcps.map((m2) => ` • ${m2}`).join(`
2335
- `)) : " None"}
2727
+ ${import_picocolors.default.bold("Automation & agents:")}
2728
+ Hooks that fire for you: ${caps.hooks.length ? import_picocolors.default.yellow(caps.hooks.join(", ")) : "none"}
2729
+ Sub-agents: ${caps.subagents} Skills: ${caps.skills.length} Orchestration policy: ${caps.hasOrchestration ? import_picocolors.default.green("yes") : import_picocolors.default.yellow("no")}
2336
2730
 
2337
- ${import_picocolors.default.bold("Governance Assessment:")}
2338
- This repository provides agents with ${caps.mcps.length} toolsets and ${caps.skills.length} skills.
2339
- ${caps.skills.length > 5 ? import_picocolors.default.red("⚠ High Surface Area: Ensure strict authorship review is enabled.") : import_picocolors.default.green("✓ Low Surface Area: Risk contained.")}`, "AI Capabilities Map");
2731
+ ${import_picocolors.default.dim("This is your attack surface. Fewer write/deploy tools per session = smaller blast radius.")}`, "Agent Reach & Blast Radius");
2340
2732
  } catch (e) {
2341
2733
  s.stop("Audit failed");
2342
2734
  console.error(import_picocolors.default.red(e.message));
@@ -2370,8 +2762,8 @@ ${caps.skills.length > 5 ? import_picocolors.default.red("⚠ High Surface Area:
2370
2762
  process.exit(0);
2371
2763
  }
2372
2764
  s.start(`Applying ${tier} policy guardrails...`);
2373
- const gitDir = join4(process.cwd(), ".git");
2374
- const isRepo = existsSync3(gitDir);
2765
+ const gitDir = join5(process.cwd(), ".git");
2766
+ const isRepo = existsSync4(gitDir);
2375
2767
  if (!isRepo) {
2376
2768
  console.error(import_picocolors.default.red("Must be run inside a git repository"));
2377
2769
  process.exit(1);
@@ -2379,8 +2771,8 @@ ${caps.skills.length > 5 ? import_picocolors.default.red("⚠ High Surface Area:
2379
2771
  const isStrict = process.argv.includes("--strict");
2380
2772
  const bouncerMsg = isStrict ? `echo "⚠️ outlier policy warning: AI authorship ($CURRENT_RATIO%) exceeds threshold ($MAX_RATIO%)"` : `echo "\uD83D\uDEE1️ Outlier Bouncer: Repository AI-generation ($CURRENT_RATIO%) exceeds your defined mastery threshold ($MAX_RATIO%)."
2381
2773
  echo "Take a moment to review your recent architectural decisions. Ensure you still understand the system."`;
2382
- const hookPath = join4(gitDir, "hooks", "pre-commit");
2383
- if (existsSync3(hookPath)) {
2774
+ const hookPath = join5(gitDir, "hooks", "pre-commit");
2775
+ if (existsSync4(hookPath)) {
2384
2776
  const { copyFileSync } = __require("fs");
2385
2777
  copyFileSync(hookPath, `${hookPath}.backup`);
2386
2778
  }
@@ -2413,7 +2805,7 @@ Enforcement: ${import_picocolors.default.cyan("Local pre-commit hook installed
2413
2805
  } else if (tier === "regulatory") {
2414
2806
  s.start("Generating Regulatory Compliance Audit (Decree 142)...");
2415
2807
  await new Promise((resolve) => setTimeout(resolve, 1200));
2416
- const reportPath = join4(process.cwd(), "outlier-audit-report.jsonl");
2808
+ const reportPath = join5(process.cwd(), "outlier-audit-report.jsonl");
2417
2809
  writeFileSync(reportPath, JSON.stringify({ timestamp: new Date().toISOString(), status: "PREVIEW", policy: "Decree 142", simulatedOversight: true }) + `
2418
2810
  `);
2419
2811
  s.stop("Audit Generated");
@@ -2503,9 +2895,19 @@ ${import_picocolors.default.bold("Submit here (and drop your screenshot!):")} ${
2503
2895
  Read the full academic foundation at: ${import_picocolors.default.underline("https://github.com/rosh100yx/outlier")}
2504
2896
  `);
2505
2897
  }
2506
- outro("Local telemetry run completed. No data left your machine.");
2898
+ outro("Done nothing left your machine. (How it works: outlier --help)");
2507
2899
  if (typeof finalReceipt !== "undefined" && finalReceipt) {
2508
2900
  console.log(finalReceipt);
2901
+ if (process.argv.includes("--save")) {
2902
+ const stripAnsi = (s2) => s2.replace(/\x1b\[[0-9;]*m/g, "");
2903
+ const savePath = join5(process.cwd(), "outlier-audit.txt");
2904
+ try {
2905
+ writeFileSync(savePath, stripAnsi(finalReceipt).trimStart() + `
2906
+ `);
2907
+ console.log(import_picocolors.default.dim(`
2908
+ \uD83D\uDCBE Saved to ${savePath}`));
2909
+ } catch {}
2910
+ }
2509
2911
  }
2510
2912
  if (action === "status") {
2511
2913
  const agent = detectAgent();
@@ -2516,10 +2918,14 @@ Read the full academic foundation at: ${import_picocolors.default.underline("htt
2516
2918
  console.log(import_picocolors.default.bold(import_picocolors.default.magenta(" ↳ Ready to code? ")) + "Start your AI agent");
2517
2919
  }
2518
2920
  console.log("");
2519
- console.log(import_picocolors.default.bold(import_picocolors.default.cyan(" Research: ")) + "Contribute to the AI deskilling study ➔ " + import_picocolors.default.bold("outlier participate"));
2520
- console.log(import_picocolors.default.bold(import_picocolors.default.green(" Share: ")) + import_picocolors.default.underline("https://x.com/intent/tweet?text=I+just+audited+my+codebase+with+%23Outlier"));
2921
+ console.log(import_picocolors.default.bold(import_picocolors.default.green(" \uD83D\uDCF8 Share: ")) + "Screenshot this receipt, or post your score ➔ " + import_picocolors.default.underline("https://x.com/intent/tweet?text=I+just+audited+my+codebase+with+%23Outlier"));
2922
+ console.log(import_picocolors.default.bold(import_picocolors.default.cyan(" \uD83D\uDD2C Research: ")) + "Help the AI-deskilling study — type: " + import_picocolors.default.bold("outlier participate"));
2923
+ if (!process.argv.includes("--save")) {
2924
+ console.log(import_picocolors.default.dim(" \uD83D\uDCBE Save: outlier status --save"));
2925
+ }
2521
2926
  console.log(import_picocolors.default.dim(`
2522
- (To see all local governance modules, run: `) + import_picocolors.default.dim(import_picocolors.default.bold("outlier --help")) + import_picocolors.default.dim(")"));
2927
+ outlier does more than this audit see how you adopt AI, what it`));
2928
+ console.log(import_picocolors.default.dim(" costs, and what is actually working: ") + import_picocolors.default.bold(import_picocolors.default.cyan("outlier --help")));
2523
2929
  }
2524
2930
  }
2525
2931
  main().catch(console.error);