@rosh100yx/outlier 0.4.25 → 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/README.md +18 -1
- package/bin/outlier.js +440 -103
- package/data/grid-factors.json +16 -3
- package/package.json +1 -1
- package/src/capabilities.ts +98 -58
- package/src/carbon.ts +35 -12
- package/src/cli.ts +126 -14
- package/src/emissions.ts +69 -0
- package/src/sources.ts +110 -0
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.
|
|
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,38 +1798,192 @@ 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";
|
|
1801
|
+
import { homedir as homedir2 } from "os";
|
|
1802
|
+
import { join as join2 } from "path";
|
|
1803
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
|
-
|
|
1808
|
-
|
|
1807
|
+
india_average: 715,
|
|
1808
|
+
indonesia: 650,
|
|
1809
|
+
china: 581,
|
|
1809
1810
|
singapore: 408,
|
|
1810
|
-
|
|
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
|
|
1824
|
+
};
|
|
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
|
|
1811
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
|
+
}
|
|
1812
1965
|
|
|
1813
1966
|
// src/carbon.ts
|
|
1814
1967
|
class ClaudeLogParser {
|
|
1815
1968
|
baseDir;
|
|
1816
1969
|
cwd;
|
|
1817
|
-
constructor(baseDir =
|
|
1970
|
+
constructor(baseDir = homedir2(), cwd = process.cwd()) {
|
|
1818
1971
|
this.baseDir = baseDir;
|
|
1819
1972
|
this.cwd = cwd;
|
|
1820
1973
|
}
|
|
1821
1974
|
async parse() {
|
|
1822
1975
|
const slug = this.cwd.replace(/\//g, "-");
|
|
1823
|
-
const projectDir =
|
|
1976
|
+
const projectDir = join2(this.baseDir, ".claude", "projects", slug);
|
|
1824
1977
|
try {
|
|
1825
1978
|
const files = (await readdir(projectDir)).filter((f2) => f2.endsWith(".jsonl"));
|
|
1826
1979
|
if (files.length > 0) {
|
|
1827
1980
|
let total2 = 0, output2 = 0, cache2 = 0;
|
|
1828
1981
|
const sessions2 = new Set;
|
|
1982
|
+
const outputByModel2 = {};
|
|
1829
1983
|
for (const file of files) {
|
|
1830
1984
|
let text3 = "";
|
|
1831
1985
|
try {
|
|
1832
|
-
text3 = await readFile(
|
|
1986
|
+
text3 = await readFile(join2(projectDir, file), "utf-8");
|
|
1833
1987
|
} catch {
|
|
1834
1988
|
continue;
|
|
1835
1989
|
}
|
|
@@ -1839,7 +1993,8 @@ class ClaudeLogParser {
|
|
|
1839
1993
|
continue;
|
|
1840
1994
|
try {
|
|
1841
1995
|
const d = JSON.parse(line);
|
|
1842
|
-
const
|
|
1996
|
+
const msg = d.message || {};
|
|
1997
|
+
const u4 = msg.usage || d.usage;
|
|
1843
1998
|
if (u4) {
|
|
1844
1999
|
const inp = u4.input_tokens || 0;
|
|
1845
2000
|
const out = u4.output_tokens || 0;
|
|
@@ -1848,24 +2003,27 @@ class ClaudeLogParser {
|
|
|
1848
2003
|
total2 += inp + out + cr + cw;
|
|
1849
2004
|
output2 += out;
|
|
1850
2005
|
cache2 += cr;
|
|
2006
|
+
const model = msg.model || "default";
|
|
2007
|
+
outputByModel2[model] = (outputByModel2[model] || 0) + out;
|
|
1851
2008
|
}
|
|
1852
2009
|
if (d.sessionId)
|
|
1853
2010
|
sessions2.add(d.sessionId);
|
|
1854
2011
|
} catch {}
|
|
1855
2012
|
}
|
|
1856
2013
|
}
|
|
1857
|
-
return { total: total2, output: output2, cache: cache2, sessions: sessions2.size, cost: 0 };
|
|
2014
|
+
return { total: total2, output: output2, cache: cache2, sessions: sessions2.size, cost: 0, outputByModel: outputByModel2 };
|
|
1858
2015
|
}
|
|
1859
2016
|
} catch {}
|
|
1860
|
-
const logPath =
|
|
2017
|
+
const logPath = join2(this.baseDir, ".claude", "tokenomics-log.jsonl");
|
|
1861
2018
|
try {
|
|
1862
2019
|
await access(logPath);
|
|
1863
2020
|
} catch {
|
|
1864
|
-
return { total: 0, output: 0, cache: 0, sessions: 0, cost: 0 };
|
|
2021
|
+
return { total: 0, output: 0, cache: 0, sessions: 0, cost: 0, outputByModel: {} };
|
|
1865
2022
|
}
|
|
1866
2023
|
const text2 = await readFile(logPath, "utf-8");
|
|
1867
2024
|
let total = 0, output = 0, cache = 0, cost = 0;
|
|
1868
2025
|
const sessions = new Set;
|
|
2026
|
+
const outputByModel = {};
|
|
1869
2027
|
for (const line of text2.split(`
|
|
1870
2028
|
`)) {
|
|
1871
2029
|
if (!line.trim())
|
|
@@ -1878,15 +2036,17 @@ class ClaudeLogParser {
|
|
|
1878
2036
|
cost += data.cost_usd || 0;
|
|
1879
2037
|
if (data.session_id)
|
|
1880
2038
|
sessions.add(data.session_id);
|
|
2039
|
+
const model = data.model || "default";
|
|
2040
|
+
outputByModel[model] = (outputByModel[model] || 0) + (data.output_tokens || 0);
|
|
1881
2041
|
} catch {}
|
|
1882
2042
|
}
|
|
1883
|
-
return { total, output, cache, sessions: sessions.size, cost };
|
|
2043
|
+
return { total, output, cache, sessions: sessions.size, cost, outputByModel };
|
|
1884
2044
|
}
|
|
1885
2045
|
}
|
|
1886
2046
|
|
|
1887
2047
|
class CursorLogParser {
|
|
1888
2048
|
async parse() {
|
|
1889
|
-
return { total: 0, output: 0, cache: 0, sessions: 0, cost: 0 };
|
|
2049
|
+
return { total: 0, output: 0, cache: 0, sessions: 0, cost: 0, outputByModel: {} };
|
|
1890
2050
|
}
|
|
1891
2051
|
}
|
|
1892
2052
|
function estimateUsd(output, cacheRead, total) {
|
|
@@ -1907,11 +2067,12 @@ function getLocalGridFactor() {
|
|
|
1907
2067
|
if (tz.includes("Calcutta") || tz.includes("Kolkata") || tz.includes("Asia/Kabul"))
|
|
1908
2068
|
return { region: "India", factor: grid_factors_default.india_average };
|
|
1909
2069
|
} catch (e) {}
|
|
1910
|
-
return { region: "Global Average", factor:
|
|
2070
|
+
return { region: "Global Average", factor: grid_factors_default.global_average };
|
|
1911
2071
|
}
|
|
1912
2072
|
async function getCarbonStats() {
|
|
1913
2073
|
const parsers = [new ClaudeLogParser, new CursorLogParser];
|
|
1914
2074
|
let totalTokens = 0, outputTokens = 0, cacheReadTokens = 0, sessions = 0, loggedCost = 0;
|
|
2075
|
+
const outputByModel = {};
|
|
1915
2076
|
for (const parser of parsers) {
|
|
1916
2077
|
const stats = await parser.parse();
|
|
1917
2078
|
totalTokens += stats.total;
|
|
@@ -1919,9 +2080,13 @@ async function getCarbonStats() {
|
|
|
1919
2080
|
cacheReadTokens += stats.cache;
|
|
1920
2081
|
sessions += stats.sessions;
|
|
1921
2082
|
loggedCost += stats.cost;
|
|
2083
|
+
for (const [m2, out] of Object.entries(stats.outputByModel)) {
|
|
2084
|
+
outputByModel[m2] = (outputByModel[m2] || 0) + out;
|
|
2085
|
+
}
|
|
1922
2086
|
}
|
|
1923
|
-
const energyKwh =
|
|
2087
|
+
const energyKwh = energyKwhByModel(outputByModel);
|
|
1924
2088
|
const localGrid = getLocalGridFactor();
|
|
2089
|
+
const sources = detectSources();
|
|
1925
2090
|
const costIsReal = loggedCost > 0;
|
|
1926
2091
|
const estUsd = costIsReal ? loggedCost : estimateUsd(outputTokens, cacheReadTokens, totalTokens);
|
|
1927
2092
|
return {
|
|
@@ -1935,80 +2100,152 @@ async function getCarbonStats() {
|
|
|
1935
2100
|
localRegion: localGrid.region,
|
|
1936
2101
|
sessions,
|
|
1937
2102
|
estUsd,
|
|
1938
|
-
costIsReal
|
|
2103
|
+
costIsReal,
|
|
2104
|
+
tokenProvenance: sources.tokenSource.provenance,
|
|
2105
|
+
carbonProvenance: sources.carbonSource.provenance,
|
|
2106
|
+
sourceLabel: provLabel(sources.tokenSource)
|
|
1939
2107
|
};
|
|
1940
2108
|
}
|
|
1941
2109
|
|
|
1942
2110
|
// src/capabilities.ts
|
|
1943
|
-
import { homedir as
|
|
1944
|
-
import { join as
|
|
1945
|
-
import { existsSync, readdirSync } from "fs";
|
|
1946
|
-
|
|
1947
|
-
const
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
if (
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
if (
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
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;
|
|
1961
2178
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
stats.skills.push(skill);
|
|
1969
|
-
}
|
|
1970
|
-
} 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;
|
|
1971
2185
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
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)) {
|
|
1974
2201
|
try {
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
} catch (e) {}
|
|
2202
|
+
readdirSync(geminiMcp, { withFileTypes: true }).filter((d) => d.isDirectory()).forEach((d) => mcpNames.add(d.name));
|
|
2203
|
+
} catch {}
|
|
1978
2204
|
}
|
|
1979
|
-
const
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
if (
|
|
1986
|
-
|
|
1987
|
-
}
|
|
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);
|
|
1988
2213
|
});
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
2214
|
+
} catch {}
|
|
2215
|
+
}
|
|
1991
2216
|
}
|
|
1992
|
-
|
|
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
|
+
});
|
|
2226
|
+
}
|
|
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 };
|
|
1993
2230
|
}
|
|
1994
2231
|
|
|
1995
2232
|
// src/cli.ts
|
|
1996
|
-
import { writeFileSync, readFileSync as
|
|
1997
|
-
import { join as
|
|
2233
|
+
import { writeFileSync, readFileSync as readFileSync3, chmodSync, existsSync as existsSync4 } from "fs";
|
|
2234
|
+
import { join as join5 } from "path";
|
|
1998
2235
|
|
|
1999
2236
|
// src/agent.ts
|
|
2000
2237
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
2001
|
-
import { readFileSync, existsSync as
|
|
2002
|
-
import { join as
|
|
2238
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
|
|
2239
|
+
import { join as join4 } from "path";
|
|
2003
2240
|
import os from "os";
|
|
2004
2241
|
function detectAgent() {
|
|
2005
2242
|
const agents = ["claude", "cursor", "aider", "hermes", "cody", "continue", "opencode", "gemini"];
|
|
2006
2243
|
try {
|
|
2007
2244
|
const home = os.homedir();
|
|
2008
|
-
const historyFiles = [
|
|
2245
|
+
const historyFiles = [join4(home, ".zsh_history"), join4(home, ".bash_history")];
|
|
2009
2246
|
for (const file of historyFiles) {
|
|
2010
|
-
if (
|
|
2011
|
-
const content =
|
|
2247
|
+
if (existsSync3(file)) {
|
|
2248
|
+
const content = readFileSync2(file, "utf8");
|
|
2012
2249
|
const lines = content.split(`
|
|
2013
2250
|
`).filter(Boolean).slice(-500).reverse();
|
|
2014
2251
|
for (const line of lines) {
|
|
@@ -2045,6 +2282,66 @@ var ASCII_LOGO = `
|
|
|
2045
2282
|
\\____/|_| |_| |_| |_| |_|___|_____|_| \\_\\
|
|
2046
2283
|
`;
|
|
2047
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
|
+
}
|
|
2048
2345
|
async function runOnboarding() {
|
|
2049
2346
|
console.log(import_picocolors.default.cyan(ASCII_LOGO));
|
|
2050
2347
|
intro(import_picocolors.default.inverse(" outlier: Welcome "));
|
|
@@ -2066,18 +2363,18 @@ As agents write more of our code, we lose visibility into:
|
|
|
2066
2363
|
cancel("Onboarding paused. Run outlier again when you are ready.");
|
|
2067
2364
|
process.exit(0);
|
|
2068
2365
|
}
|
|
2069
|
-
const configPath =
|
|
2366
|
+
const configPath = join5(os2.homedir(), ".outlier_config");
|
|
2070
2367
|
writeFileSync(configPath, JSON.stringify({ onboarded: true, date: new Date().toISOString() }));
|
|
2071
2368
|
}
|
|
2072
2369
|
async function main() {
|
|
2073
2370
|
let action = process.argv[2];
|
|
2074
2371
|
if (action === "daily-greeting") {
|
|
2075
|
-
const configPath2 =
|
|
2372
|
+
const configPath2 = join5(os2.homedir(), ".outlier_config");
|
|
2076
2373
|
const today = new Date().toISOString().split("T")[0];
|
|
2077
2374
|
let alreadyRun = false;
|
|
2078
|
-
if (
|
|
2375
|
+
if (existsSync4(configPath2)) {
|
|
2079
2376
|
try {
|
|
2080
|
-
const cfg = JSON.parse(
|
|
2377
|
+
const cfg = JSON.parse(readFileSync3(configPath2, "utf8"));
|
|
2081
2378
|
if (cfg.lastGreetingDate === today) {
|
|
2082
2379
|
alreadyRun = true;
|
|
2083
2380
|
} else {
|
|
@@ -2090,6 +2387,10 @@ async function main() {
|
|
|
2090
2387
|
process.exit(0);
|
|
2091
2388
|
action = "status";
|
|
2092
2389
|
}
|
|
2390
|
+
if (process.argv.includes("--json")) {
|
|
2391
|
+
await emitJson();
|
|
2392
|
+
process.exit(0);
|
|
2393
|
+
}
|
|
2093
2394
|
console.log(import_picocolors.default.cyan(ASCII_LOGO));
|
|
2094
2395
|
const pkg = require_package();
|
|
2095
2396
|
console.log(import_picocolors.default.dim(` Outlier v${pkg.version} · AI Code Reliance & Telemetry Engine
|
|
@@ -2104,6 +2405,7 @@ WHAT OUTLIER DOES`));
|
|
|
2104
2405
|
console.log(` ${import_picocolors.default.cyan("outlier")} Run the audit (the default — same as 'status')`);
|
|
2105
2406
|
console.log(` ${import_picocolors.default.cyan("outlier status")} Full audit: who wrote the code, what it cost, your limit`);
|
|
2106
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)`);
|
|
2107
2409
|
console.log(` ${import_picocolors.default.cyan("outlier authorship")} Just the AI-vs-human commit breakdown`);
|
|
2108
2410
|
console.log(` ${import_picocolors.default.cyan("outlier carbon")} Just the token spend, cache waste & carbon`);
|
|
2109
2411
|
console.log(` ${import_picocolors.default.cyan("outlier capabilities")} What tools & skills your agents can reach`);
|
|
@@ -2118,8 +2420,8 @@ WHAT OUTLIER DOES`));
|
|
|
2118
2420
|
console.log(import_picocolors.default.dim("How it works → https://github.com/rosh100yx/outlier#how-it-works"));
|
|
2119
2421
|
process.exit(0);
|
|
2120
2422
|
}
|
|
2121
|
-
const configPath =
|
|
2122
|
-
if (!
|
|
2423
|
+
const configPath = join5(os2.homedir(), ".outlier_config");
|
|
2424
|
+
if (!existsSync4(configPath) && !action) {
|
|
2123
2425
|
await runOnboarding();
|
|
2124
2426
|
action = "status";
|
|
2125
2427
|
}
|
|
@@ -2130,7 +2432,7 @@ WHAT OUTLIER DOES`));
|
|
|
2130
2432
|
if (action === "init" || action === "uninit") {
|
|
2131
2433
|
const shell = process.env.SHELL || "";
|
|
2132
2434
|
const rcName = shell.includes("zsh") ? ".zshrc" : ".bashrc";
|
|
2133
|
-
const rcPath =
|
|
2435
|
+
const rcPath = join5(os2.homedir(), rcName);
|
|
2134
2436
|
const START_MARKER = "# --- OUTLIER PRE-FLIGHT RITUAL START ---";
|
|
2135
2437
|
const END_MARKER = "# --- OUTLIER PRE-FLIGHT RITUAL END ---";
|
|
2136
2438
|
const BLOCK = `
|
|
@@ -2150,8 +2452,8 @@ ${END_MARKER}
|
|
|
2150
2452
|
process.exit(0);
|
|
2151
2453
|
}
|
|
2152
2454
|
let content = "";
|
|
2153
|
-
if (
|
|
2154
|
-
content =
|
|
2455
|
+
if (existsSync4(rcPath))
|
|
2456
|
+
content = readFileSync3(rcPath, "utf8");
|
|
2155
2457
|
if (content.includes(START_MARKER)) {
|
|
2156
2458
|
note(`Outlier is already initialized in ${rcName}`);
|
|
2157
2459
|
} else {
|
|
@@ -2160,8 +2462,8 @@ ${END_MARKER}
|
|
|
2160
2462
|
}
|
|
2161
2463
|
process.exit(0);
|
|
2162
2464
|
} else if (action === "uninit") {
|
|
2163
|
-
if (
|
|
2164
|
-
let content =
|
|
2465
|
+
if (existsSync4(rcPath)) {
|
|
2466
|
+
let content = readFileSync3(rcPath, "utf8");
|
|
2165
2467
|
if (content.includes(START_MARKER)) {
|
|
2166
2468
|
const regex = new RegExp(`\\n?${START_MARKER}[\\s\\S]*?${END_MARKER}\\n?`, "g");
|
|
2167
2469
|
content = content.replace(regex, `
|
|
@@ -2230,10 +2532,10 @@ Conservative Floor: ${color(nmPct + "%")}`, "Git Authorship Breakdown");
|
|
|
2230
2532
|
let carbon = null;
|
|
2231
2533
|
let capabilities = null;
|
|
2232
2534
|
let skipDelay = false;
|
|
2233
|
-
const configPath2 =
|
|
2234
|
-
if (
|
|
2535
|
+
const configPath2 = join5(os2.homedir(), ".outlier_config");
|
|
2536
|
+
if (existsSync4(configPath2)) {
|
|
2235
2537
|
try {
|
|
2236
|
-
const cfg = JSON.parse(
|
|
2538
|
+
const cfg = JSON.parse(readFileSync3(configPath2, "utf8"));
|
|
2237
2539
|
if (cfg.seenNarration)
|
|
2238
2540
|
skipDelay = true;
|
|
2239
2541
|
} catch (e) {}
|
|
@@ -2271,7 +2573,7 @@ Conservative Floor: ${color(nmPct + "%")}`, "Git Authorship Breakdown");
|
|
|
2271
2573
|
await new Promise((r2) => setTimeout(r2, 600));
|
|
2272
2574
|
if (!skipDelay) {
|
|
2273
2575
|
try {
|
|
2274
|
-
const cfg =
|
|
2576
|
+
const cfg = existsSync4(configPath2) ? JSON.parse(readFileSync3(configPath2, "utf8")) : {};
|
|
2275
2577
|
cfg.seenNarration = true;
|
|
2276
2578
|
writeFileSync(configPath2, JSON.stringify(cfg));
|
|
2277
2579
|
} catch (e) {}
|
|
@@ -2299,12 +2601,23 @@ Conservative Floor: ${color(nmPct + "%")}`, "Git Authorship Breakdown");
|
|
|
2299
2601
|
let cachePct = "0";
|
|
2300
2602
|
let co2Str = "0.0kg";
|
|
2301
2603
|
let regionStr = "Global Average";
|
|
2604
|
+
let sourceLabel = "no local AI logs found";
|
|
2605
|
+
let noData = true;
|
|
2302
2606
|
if (carbon) {
|
|
2303
2607
|
if (carbon.totalTokens > 0) {
|
|
2304
2608
|
cachePct = (carbon.cacheReadTokens / carbon.totalTokens * 100).toFixed(1);
|
|
2609
|
+
noData = false;
|
|
2305
2610
|
}
|
|
2306
2611
|
co2Str = `${carbon.localCo2Kg.toFixed(2)}kg CO2`;
|
|
2307
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`) : "");
|
|
2308
2621
|
}
|
|
2309
2622
|
const isDanger = gitStats && gitStats.ratio > 0.7;
|
|
2310
2623
|
const verdictZone = isDanger ? import_picocolors.default.red("Mostly AI") : import_picocolors.default.green("You're driving");
|
|
@@ -2353,12 +2666,17 @@ Conservative Floor: ${color(nmPct + "%")}`, "Git Authorship Breakdown");
|
|
|
2353
2666
|
${import_picocolors.default.dim("│")} Tokens used ${import_picocolors.default.bold(totalTokensStr)}
|
|
2354
2667
|
${import_picocolors.default.dim("│")} Est. spend ${import_picocolors.default.bold(estUsdStr)}
|
|
2355
2668
|
${import_picocolors.default.dim("│")} Re-used context ${cacheBar} ${import_picocolors.default.bold(cachePct + "%")}
|
|
2356
|
-
${import_picocolors.default.dim("│")} Energy ${import_picocolors.default.bold(co2Str)} ${import_picocolors.default.dim(`(${regionStr} grid
|
|
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}`)}
|
|
2357
2671
|
${import_picocolors.default.dim("│")}
|
|
2358
2672
|
${import_picocolors.default.dim("│")} ${cacheVerdict} — ${cacheText.split(`
|
|
2359
2673
|
`).join(`
|
|
2360
2674
|
` + import_picocolors.default.dim("│") + " ")}
|
|
2361
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("├────────────────────────────────────────────────────────")}
|
|
2362
2680
|
${import_picocolors.default.dim("│")} ${import_picocolors.default.bold(import_picocolors.default.bgYellow(import_picocolors.default.black(" YOUR LIMIT ")))}
|
|
2363
2681
|
${import_picocolors.default.dim("│")} AI cap ${import_picocolors.default.bold("70%")} ${import_picocolors.default.dim("· change with: outlier policy")}
|
|
2364
2682
|
${import_picocolors.default.dim("│")} Status ${policyStatus} ${import_picocolors.default.dim("·")} ${policyAction}
|
|
@@ -2375,23 +2693,42 @@ Conservative Floor: ${color(nmPct + "%")}`, "Git Authorship Breakdown");
|
|
|
2375
2693
|
console.error(import_picocolors.default.red(e.message));
|
|
2376
2694
|
}
|
|
2377
2695
|
} else if (action === "capabilities") {
|
|
2378
|
-
s.start("
|
|
2696
|
+
s.start("Mapping what your agents can reach...");
|
|
2379
2697
|
try {
|
|
2380
2698
|
const caps = await getCapabilitiesStats();
|
|
2381
|
-
s.stop("
|
|
2382
|
-
|
|
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")}
|
|
2383
2723
|
|
|
2384
|
-
|
|
2385
|
-
${
|
|
2386
|
-
`)) : " None"}
|
|
2724
|
+
${import_picocolors.default.bold(`What your agents can reach (${caps.mcps.length} MCP tools):`)}
|
|
2725
|
+
${toolLines}
|
|
2387
2726
|
|
|
2388
|
-
|
|
2389
|
-
${caps.
|
|
2390
|
-
|
|
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")}
|
|
2391
2730
|
|
|
2392
|
-
${import_picocolors.default.
|
|
2393
|
-
This repository provides agents with ${caps.mcps.length} toolsets and ${caps.skills.length} skills.
|
|
2394
|
-
${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");
|
|
2395
2732
|
} catch (e) {
|
|
2396
2733
|
s.stop("Audit failed");
|
|
2397
2734
|
console.error(import_picocolors.default.red(e.message));
|
|
@@ -2425,8 +2762,8 @@ ${caps.skills.length > 5 ? import_picocolors.default.red("⚠ High Surface Area:
|
|
|
2425
2762
|
process.exit(0);
|
|
2426
2763
|
}
|
|
2427
2764
|
s.start(`Applying ${tier} policy guardrails...`);
|
|
2428
|
-
const gitDir =
|
|
2429
|
-
const isRepo =
|
|
2765
|
+
const gitDir = join5(process.cwd(), ".git");
|
|
2766
|
+
const isRepo = existsSync4(gitDir);
|
|
2430
2767
|
if (!isRepo) {
|
|
2431
2768
|
console.error(import_picocolors.default.red("Must be run inside a git repository"));
|
|
2432
2769
|
process.exit(1);
|
|
@@ -2434,8 +2771,8 @@ ${caps.skills.length > 5 ? import_picocolors.default.red("⚠ High Surface Area:
|
|
|
2434
2771
|
const isStrict = process.argv.includes("--strict");
|
|
2435
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%)."
|
|
2436
2773
|
echo "Take a moment to review your recent architectural decisions. Ensure you still understand the system."`;
|
|
2437
|
-
const hookPath =
|
|
2438
|
-
if (
|
|
2774
|
+
const hookPath = join5(gitDir, "hooks", "pre-commit");
|
|
2775
|
+
if (existsSync4(hookPath)) {
|
|
2439
2776
|
const { copyFileSync } = __require("fs");
|
|
2440
2777
|
copyFileSync(hookPath, `${hookPath}.backup`);
|
|
2441
2778
|
}
|
|
@@ -2468,7 +2805,7 @@ Enforcement: ${import_picocolors.default.cyan("Local pre-commit hook installed
|
|
|
2468
2805
|
} else if (tier === "regulatory") {
|
|
2469
2806
|
s.start("Generating Regulatory Compliance Audit (Decree 142)...");
|
|
2470
2807
|
await new Promise((resolve) => setTimeout(resolve, 1200));
|
|
2471
|
-
const reportPath =
|
|
2808
|
+
const reportPath = join5(process.cwd(), "outlier-audit-report.jsonl");
|
|
2472
2809
|
writeFileSync(reportPath, JSON.stringify({ timestamp: new Date().toISOString(), status: "PREVIEW", policy: "Decree 142", simulatedOversight: true }) + `
|
|
2473
2810
|
`);
|
|
2474
2811
|
s.stop("Audit Generated");
|
|
@@ -2563,7 +2900,7 @@ Read the full academic foundation at: ${import_picocolors.default.underline("htt
|
|
|
2563
2900
|
console.log(finalReceipt);
|
|
2564
2901
|
if (process.argv.includes("--save")) {
|
|
2565
2902
|
const stripAnsi = (s2) => s2.replace(/\x1b\[[0-9;]*m/g, "");
|
|
2566
|
-
const savePath =
|
|
2903
|
+
const savePath = join5(process.cwd(), "outlier-audit.txt");
|
|
2567
2904
|
try {
|
|
2568
2905
|
writeFileSync(savePath, stripAnsi(finalReceipt).trimStart() + `
|
|
2569
2906
|
`);
|