@rosh100yx/outlier 0.4.10 → 0.4.14
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 +40 -7
- package/bin/outlier.js +75 -140
- package/bin/postinstall.js +6 -3
- package/package.json +2 -2
- package/src/cli.ts +35 -63
package/README.md
CHANGED
|
@@ -18,8 +18,26 @@
|
|
|
18
18
|
</p>
|
|
19
19
|
|
|
20
20
|
<br/>
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
┌────────────────────────────────────────────────────────┐
|
|
24
|
+
│ █▀█ █░█ ▀█▀ █░░ █ █▀▀ █▀█ :: THERMAL AUDIT RECEIPT │
|
|
25
|
+
│ █▄█ █▄█ ░█░ █▄▄ █ ██▄ █▀▄ :: TIMESTAMP: 2026-06-23 │
|
|
26
|
+
├────────────────────────────────────────────────────────┤
|
|
27
|
+
│ [ COGNITIVE BUDGET ] │
|
|
28
|
+
│ AI Authorship ................. ▇▇▇▇░░░░░░ 40% │
|
|
29
|
+
│ Human Sovereignty ................. ▇▇▇▇▇▇░░░░ 60% │
|
|
30
|
+
│ │
|
|
31
|
+
│ ↳ Verdict: (=^ ◡ ^=) CENTAUR │
|
|
32
|
+
│ Healthy symbiosis. You orchestrate agents │
|
|
33
|
+
│ but maintain architectural authority. │
|
|
34
|
+
├────────────────────────────────────────────────────────┤
|
|
35
|
+
│ [ FINANCIAL & COMPUTE TOLL ] │
|
|
36
|
+
│ Tokens Burnt ................. 3.12M vs Human │
|
|
37
|
+
│ Cache Bloat ................. ▇▇▇▇▇▇▇▇░░ 80% │
|
|
38
|
+
│ Regional Grid ................. 1.54 kgCO2 │
|
|
39
|
+
└────────────────────────────────────────────────────────┘
|
|
40
|
+
```
|
|
23
41
|
</div>
|
|
24
42
|
|
|
25
43
|
## How It Works
|
|
@@ -50,13 +68,28 @@
|
|
|
50
68
|
## Commands
|
|
51
69
|
| Command | Purpose |
|
|
52
70
|
|---------|---------|
|
|
53
|
-
| `outlier` | Start the interactive
|
|
54
|
-
| `outlier
|
|
55
|
-
| `outlier
|
|
56
|
-
| `outlier authorship` | Scan git history for AI co-authorship ratio and Hallucination Risk |
|
|
71
|
+
| `outlier` | Start the interactive Interactive Menu |
|
|
72
|
+
| `outlier status` | Run the full AI reliance & capability audit |
|
|
73
|
+
| `outlier authorship` | Scan git history for AI co-authorship ratio |
|
|
57
74
|
| `outlier carbon` | Scan local logs for context waste & token costs |
|
|
75
|
+
| `outlier capabilities` | Audit active MCPs, skills, and orchestrations |
|
|
58
76
|
| `outlier policy` | Configure Personal, Team, or Enterprise guardrails in CI |
|
|
59
|
-
|
|
77
|
+
|
|
78
|
+
### Interactive Menu
|
|
79
|
+
If you run `outlier` directly, you'll be greeted with our frictionless UX:
|
|
80
|
+
```text
|
|
81
|
+
? Select outlier governance module:
|
|
82
|
+
── AUDIT ──
|
|
83
|
+
❯ Status Full audit (reliance + carbon + capabilities)
|
|
84
|
+
Authorship Human vs AI, per commit
|
|
85
|
+
Carbon Token waste + regional cost
|
|
86
|
+
Capabilities What your agents can reach
|
|
87
|
+
Policy Set guardrails / install the gate
|
|
88
|
+
── LEARN ──
|
|
89
|
+
Impact What happens over the next 5-10 years
|
|
90
|
+
Literature The academic foundation
|
|
91
|
+
Participate Contribute to the research
|
|
92
|
+
```
|
|
60
93
|
|
|
61
94
|
## Quickstart: Your First Audit
|
|
62
95
|
|
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.
|
|
168
|
+
version: "0.4.14",
|
|
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"
|
|
@@ -177,7 +177,7 @@ var require_package = __commonJS((exports, module) => {
|
|
|
177
177
|
"data"
|
|
178
178
|
],
|
|
179
179
|
scripts: {
|
|
180
|
-
build: "bun build ./src/cli.ts --target=node --outfile bin/outlier.js",
|
|
180
|
+
build: "bunx tsc --noEmit && bun build ./src/cli.ts --target=node --outfile bin/outlier.js",
|
|
181
181
|
test: "bun test",
|
|
182
182
|
start: "bun run src/cli.ts",
|
|
183
183
|
postinstall: "node bin/postinstall.js"
|
|
@@ -218,6 +218,9 @@ var require_package = __commonJS((exports, module) => {
|
|
|
218
218
|
};
|
|
219
219
|
});
|
|
220
220
|
|
|
221
|
+
// src/cli.ts
|
|
222
|
+
import os from "os";
|
|
223
|
+
|
|
221
224
|
// node_modules/@clack/core/dist/index.mjs
|
|
222
225
|
import { styleText } from "node:util";
|
|
223
226
|
import { stdout, stdin } from "node:process";
|
|
@@ -940,34 +943,16 @@ var T$1 = class T extends V {
|
|
|
940
943
|
}
|
|
941
944
|
}
|
|
942
945
|
};
|
|
943
|
-
|
|
944
|
-
class r extends V {
|
|
945
|
-
get cursor() {
|
|
946
|
-
return this.value ? 0 : 1;
|
|
947
|
-
}
|
|
948
|
-
get _value() {
|
|
949
|
-
return this.cursor === 0;
|
|
950
|
-
}
|
|
951
|
-
constructor(t2) {
|
|
952
|
-
super(t2, false), this.value = !!t2.initialValue, this.on("userInput", () => {
|
|
953
|
-
this.value = this._value;
|
|
954
|
-
}), this.on("confirm", (i) => {
|
|
955
|
-
this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = i, this.state = "submit", this.close();
|
|
956
|
-
}), this.on("cursor", () => {
|
|
957
|
-
this.value = !this.value;
|
|
958
|
-
});
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
946
|
var _ = {
|
|
962
947
|
Y: { type: "year", len: 4 },
|
|
963
948
|
M: { type: "month", len: 2 },
|
|
964
949
|
D: { type: "day", len: 2 }
|
|
965
950
|
};
|
|
966
|
-
function M(
|
|
967
|
-
return [...
|
|
951
|
+
function M(r) {
|
|
952
|
+
return [...r].map((t2) => _[t2]);
|
|
968
953
|
}
|
|
969
|
-
function P(
|
|
970
|
-
const i = new Intl.DateTimeFormat(
|
|
954
|
+
function P(r) {
|
|
955
|
+
const i = new Intl.DateTimeFormat(r, {
|
|
971
956
|
year: "numeric",
|
|
972
957
|
month: "2-digit",
|
|
973
958
|
day: "2-digit"
|
|
@@ -977,32 +962,32 @@ function P(r2) {
|
|
|
977
962
|
e.type === "literal" ? n = e.value.trim() || e.value : (e.type === "year" || e.type === "month" || e.type === "day") && s.push({ type: e.type, len: e.type === "year" ? 4 : 2 });
|
|
978
963
|
return { segments: s, separator: n };
|
|
979
964
|
}
|
|
980
|
-
function p(
|
|
981
|
-
return Number.parseInt((
|
|
965
|
+
function p(r) {
|
|
966
|
+
return Number.parseInt((r || "0").replace(/_/g, "0"), 10) || 0;
|
|
982
967
|
}
|
|
983
|
-
function f(
|
|
968
|
+
function f(r) {
|
|
984
969
|
return {
|
|
985
|
-
year: p(
|
|
986
|
-
month: p(
|
|
987
|
-
day: p(
|
|
970
|
+
year: p(r.year),
|
|
971
|
+
month: p(r.month),
|
|
972
|
+
day: p(r.day)
|
|
988
973
|
};
|
|
989
974
|
}
|
|
990
|
-
function c(
|
|
991
|
-
return new Date(
|
|
975
|
+
function c(r, t2) {
|
|
976
|
+
return new Date(r || 2001, t2 || 1, 0).getDate();
|
|
992
977
|
}
|
|
993
|
-
function b(
|
|
994
|
-
const { year: t2, month: i, day: s } = f(
|
|
978
|
+
function b(r) {
|
|
979
|
+
const { year: t2, month: i, day: s } = f(r);
|
|
995
980
|
if (!t2 || t2 < 0 || t2 > 9999 || !i || i < 1 || i > 12 || !s || s < 1)
|
|
996
981
|
return;
|
|
997
982
|
const n = new Date(Date.UTC(t2, i - 1, s));
|
|
998
983
|
if (!(n.getUTCFullYear() !== t2 || n.getUTCMonth() !== i - 1 || n.getUTCDate() !== s))
|
|
999
984
|
return { year: t2, month: i, day: s };
|
|
1000
985
|
}
|
|
1001
|
-
function C(
|
|
1002
|
-
const t2 = b(
|
|
986
|
+
function C(r) {
|
|
987
|
+
const t2 = b(r);
|
|
1003
988
|
return t2 ? new Date(Date.UTC(t2.year, t2.month - 1, t2.day)) : undefined;
|
|
1004
989
|
}
|
|
1005
|
-
function T2(
|
|
990
|
+
function T2(r, t2, i, s) {
|
|
1006
991
|
const n = i ? {
|
|
1007
992
|
year: i.getUTCFullYear(),
|
|
1008
993
|
month: i.getUTCMonth() + 1,
|
|
@@ -1012,7 +997,7 @@ function T2(r2, t2, i, s) {
|
|
|
1012
997
|
month: s.getUTCMonth() + 1,
|
|
1013
998
|
day: s.getUTCDate()
|
|
1014
999
|
} : null;
|
|
1015
|
-
return
|
|
1000
|
+
return r === "year" ? { min: n?.year ?? 1, max: e?.year ?? 9999 } : r === "month" ? {
|
|
1016
1001
|
min: n && t2.year === n.year ? n.month : 1,
|
|
1017
1002
|
max: e && t2.year === e.year ? e.month : 12
|
|
1018
1003
|
} : {
|
|
@@ -1185,26 +1170,26 @@ var u$1 = class u extends V {
|
|
|
1185
1170
|
cursor = 0;
|
|
1186
1171
|
#t;
|
|
1187
1172
|
getGroupItems(t2) {
|
|
1188
|
-
return this.options.filter((
|
|
1173
|
+
return this.options.filter((r) => r.group === t2);
|
|
1189
1174
|
}
|
|
1190
1175
|
isGroupSelected(t2) {
|
|
1191
|
-
const
|
|
1192
|
-
return e === undefined ? false :
|
|
1176
|
+
const r = this.getGroupItems(t2), e = this.value;
|
|
1177
|
+
return e === undefined ? false : r.every((s) => e.includes(s.value));
|
|
1193
1178
|
}
|
|
1194
1179
|
toggleValue() {
|
|
1195
1180
|
const t2 = this.options[this.cursor];
|
|
1196
1181
|
if (this.value === undefined && (this.value = []), t2.group === true) {
|
|
1197
|
-
const
|
|
1198
|
-
this.isGroupSelected(
|
|
1182
|
+
const r = t2.value, e = this.getGroupItems(r);
|
|
1183
|
+
this.isGroupSelected(r) ? this.value = this.value.filter((s) => e.findIndex((i) => i.value === s) === -1) : this.value = [...this.value, ...e.map((s) => s.value)], this.value = Array.from(new Set(this.value));
|
|
1199
1184
|
} else {
|
|
1200
|
-
const
|
|
1201
|
-
this.value =
|
|
1185
|
+
const r = this.value.includes(t2.value);
|
|
1186
|
+
this.value = r ? this.value.filter((e) => e !== t2.value) : [...this.value, t2.value];
|
|
1202
1187
|
}
|
|
1203
1188
|
}
|
|
1204
1189
|
constructor(t2) {
|
|
1205
1190
|
super(t2, false);
|
|
1206
|
-
const { options:
|
|
1207
|
-
this.#t = t2.selectableGroups !== false, this.options = Object.entries(
|
|
1191
|
+
const { options: r } = t2;
|
|
1192
|
+
this.#t = t2.selectableGroups !== false, this.options = Object.entries(r).flatMap(([e, s]) => [
|
|
1208
1193
|
{ value: e, group: true, label: e },
|
|
1209
1194
|
...s.map((i) => ({ ...i, group: e }))
|
|
1210
1195
|
]), this.value = [...t2.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: e }) => e === t2.cursorAt), this.#t ? 0 : 1), this.on("cursor", (e) => {
|
|
@@ -1242,10 +1227,10 @@ class h extends V {
|
|
|
1242
1227
|
const t2 = this.userInput;
|
|
1243
1228
|
if (this.cursor >= t2.length)
|
|
1244
1229
|
return `${t2}█`;
|
|
1245
|
-
const s = t2.slice(0, this.cursor),
|
|
1246
|
-
return
|
|
1230
|
+
const s = t2.slice(0, this.cursor), r = t2[this.cursor], i = t2.slice(this.cursor + 1);
|
|
1231
|
+
return r === `
|
|
1247
1232
|
` ? `${s}█
|
|
1248
|
-
${i}` : `${s}${styleText("inverse",
|
|
1233
|
+
${i}` : `${s}${styleText("inverse", r)}${i}`;
|
|
1249
1234
|
}
|
|
1250
1235
|
get cursor() {
|
|
1251
1236
|
return this._cursor;
|
|
@@ -1278,8 +1263,8 @@ ${i}` : `${s}${styleText("inverse", r2)}${i}`;
|
|
|
1278
1263
|
if (this.#s)
|
|
1279
1264
|
return this.focused === "submit" ? true : (this.#r(`
|
|
1280
1265
|
`), this._cursor++, false);
|
|
1281
|
-
const
|
|
1282
|
-
return this.#t = true,
|
|
1266
|
+
const r = this.#t;
|
|
1267
|
+
return this.#t = true, r && this.cursor === this.userInput.length ? (this.userInput[this.cursor - 1] === `
|
|
1283
1268
|
` && (this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--), true) : (this.#r(`
|
|
1284
1269
|
`), this._cursor++, false);
|
|
1285
1270
|
}
|
|
@@ -1288,12 +1273,12 @@ ${i}` : `${s}${styleText("inverse", r2)}${i}`;
|
|
|
1288
1273
|
super({
|
|
1289
1274
|
...t2,
|
|
1290
1275
|
initialUserInput: s
|
|
1291
|
-
}, false), s !== undefined && (this._cursor = s.length), this.#s = t2.showSubmit ?? false, this.on("key", (
|
|
1276
|
+
}, false), s !== undefined && (this._cursor = s.length), this.#s = t2.showSubmit ?? false, this.on("key", (r, i) => {
|
|
1292
1277
|
if (i?.name && o$1.has(i.name)) {
|
|
1293
1278
|
this.#t = false, this.#i(i.name);
|
|
1294
1279
|
return;
|
|
1295
1280
|
}
|
|
1296
|
-
if (
|
|
1281
|
+
if (r === "\t" && this.#s) {
|
|
1297
1282
|
this.focused = this.focused === "editor" ? "submit" : "editor";
|
|
1298
1283
|
return;
|
|
1299
1284
|
}
|
|
@@ -1306,10 +1291,10 @@ ${i}` : `${s}${styleText("inverse", r2)}${i}`;
|
|
|
1306
1291
|
this._setUserInput(this.userInput.slice(0, this.cursor) + this.userInput.slice(this.cursor + 1));
|
|
1307
1292
|
return;
|
|
1308
1293
|
}
|
|
1309
|
-
|
|
1294
|
+
r && (this.#s && this.focused === "submit" && (this.focused = "editor"), this.#r(r ?? ""), this._cursor++);
|
|
1310
1295
|
}
|
|
1311
|
-
}), this.on("userInput", (
|
|
1312
|
-
this._setValue(
|
|
1296
|
+
}), this.on("userInput", (r) => {
|
|
1297
|
+
this._setValue(r);
|
|
1313
1298
|
}), this.on("finalize", () => {
|
|
1314
1299
|
this.value || (this.value = t2.defaultValue), this.value === undefined && (this.value = "");
|
|
1315
1300
|
});
|
|
@@ -1349,8 +1334,8 @@ class n extends V {
|
|
|
1349
1334
|
const t2 = this.userInput;
|
|
1350
1335
|
if (this.cursor >= t2.length)
|
|
1351
1336
|
return `${this.userInput}█`;
|
|
1352
|
-
const e = t2.slice(0, this.cursor), [s, ...
|
|
1353
|
-
return `${e}${styleText("inverse", s)}${
|
|
1337
|
+
const e = t2.slice(0, this.cursor), [s, ...r] = t2.slice(this.cursor);
|
|
1338
|
+
return `${e}${styleText("inverse", s)}${r.join("")}`;
|
|
1354
1339
|
}
|
|
1355
1340
|
get cursor() {
|
|
1356
1341
|
return this._cursor;
|
|
@@ -1484,41 +1469,6 @@ var limitOptions = ({
|
|
|
1484
1469
|
b2.push(n2);
|
|
1485
1470
|
return u3 && b2.push(C2), b2;
|
|
1486
1471
|
};
|
|
1487
|
-
var confirm = (i) => {
|
|
1488
|
-
const a2 = i.active ?? "Yes", s = i.inactive ?? "No";
|
|
1489
|
-
return new r({
|
|
1490
|
-
active: a2,
|
|
1491
|
-
inactive: s,
|
|
1492
|
-
signal: i.signal,
|
|
1493
|
-
input: i.input,
|
|
1494
|
-
output: i.output,
|
|
1495
|
-
initialValue: i.initialValue ?? true,
|
|
1496
|
-
render() {
|
|
1497
|
-
const e = i.withGuide ?? settings.withGuide, u3 = `${symbol(this.state)} `, l2 = e ? `${styleText2("gray", S_BAR)} ` : "", f2 = wrapTextWithPrefix(i.output, i.message, l2, u3), o2 = `${e ? `${styleText2("gray", S_BAR)}
|
|
1498
|
-
` : ""}${f2}
|
|
1499
|
-
`, c2 = this.value ? a2 : s;
|
|
1500
|
-
switch (this.state) {
|
|
1501
|
-
case "submit": {
|
|
1502
|
-
const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
|
|
1503
|
-
return `${o2}${r2}${styleText2("dim", c2)}`;
|
|
1504
|
-
}
|
|
1505
|
-
case "cancel": {
|
|
1506
|
-
const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
|
|
1507
|
-
return `${o2}${r2}${styleText2(["strikethrough", "dim"], c2)}${e ? `
|
|
1508
|
-
${styleText2("gray", S_BAR)}` : ""}`;
|
|
1509
|
-
}
|
|
1510
|
-
default: {
|
|
1511
|
-
const r2 = e ? `${styleText2("cyan", S_BAR)} ` : "", g2 = e ? styleText2("cyan", S_BAR_END) : "";
|
|
1512
|
-
return `${o2}${r2}${this.value ? `${styleText2("green", S_RADIO_ACTIVE)} ${a2}` : `${styleText2("dim", S_RADIO_INACTIVE)} ${styleText2("dim", a2)}`}${i.vertical ? e ? `
|
|
1513
|
-
${styleText2("cyan", S_BAR)} ` : `
|
|
1514
|
-
` : ` ${styleText2("dim", "/")} `}${this.value ? `${styleText2("dim", S_RADIO_INACTIVE)} ${styleText2("dim", s)}` : `${styleText2("green", S_RADIO_ACTIVE)} ${s}`}
|
|
1515
|
-
${g2}
|
|
1516
|
-
`;
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
}).prompt();
|
|
1521
|
-
};
|
|
1522
1472
|
var MULTISELECT_INSTRUCTIONS = [
|
|
1523
1473
|
`${styleText2("dim", "↑/↓")} to navigate`,
|
|
1524
1474
|
`${styleText2("dim", "Space:")} select`,
|
|
@@ -1941,7 +1891,6 @@ async function getCapabilitiesStats(repoPath = process.cwd(), homeDirPath = home
|
|
|
1941
1891
|
// src/cli.ts
|
|
1942
1892
|
import { writeFileSync, chmodSync, existsSync as existsSync2 } from "fs";
|
|
1943
1893
|
import { join as join3 } from "path";
|
|
1944
|
-
import os from "os";
|
|
1945
1894
|
var ASCII_LOGO = `
|
|
1946
1895
|
____ _ _ _____ _ ___ _____ ____
|
|
1947
1896
|
/ __ \\| | | |_ _| | |_ _| ___| _ \\
|
|
@@ -1950,39 +1899,15 @@ var ASCII_LOGO = `
|
|
|
1950
1899
|
| |__| | _ | | | | _ || || |___| | \\ \\
|
|
1951
1900
|
\\____/|_| |_| |_| |_| |_|___|_____|_| \\_\\
|
|
1952
1901
|
`;
|
|
1902
|
+
var finalReceipt = "";
|
|
1953
1903
|
async function runOnboarding() {
|
|
1954
1904
|
console.clear();
|
|
1955
1905
|
console.log(import_picocolors.default.cyan(ASCII_LOGO));
|
|
1956
1906
|
intro(import_picocolors.default.inverse(" outlier: Welcome "));
|
|
1957
|
-
note(`Outlier is a local-first Policy Engine & Governance Framework for AI Engineering.
|
|
1958
|
-
|
|
1959
|
-
Our mission is AI Safety for developers:
|
|
1960
|
-
As agents (Cursor, Copilot, Claude) write more of our code, we lose visibility into:
|
|
1961
|
-
1. Deskilling Risk (Are we becoming spectators in our own codebase?)
|
|
1962
|
-
2. Carbon Cost (What is the true regional energy cost of token caching?)
|
|
1963
|
-
3. Capability Drift (What hidden skills and external tools are our agents using?)
|
|
1964
|
-
|
|
1965
|
-
We built Outlier to enforce Zero-Trust and protect Human Mastery. You are in control.`, "The Problem: AI Safety in Development");
|
|
1966
1907
|
note(`Outlier operates entirely on your machine.
|
|
1967
1908
|
- Local Only: No API keys. No cloud telemetry. No data leaves your machine.
|
|
1968
|
-
- Native Auditing: We
|
|
1969
|
-
- Actionable Policies:
|
|
1970
|
-
note(`Available Commands:
|
|
1971
|
-
- status: Run a full system audit (Reliance, Carbon, Capabilities)
|
|
1972
|
-
- policy: Configure team/enterprise guardrails and CLI blockers
|
|
1973
|
-
- carbon: View isolated token caching metrics and regional counterfactuals
|
|
1974
|
-
- authorship: View Git authorship ratio (Human vs AI)`, "How it is used");
|
|
1975
|
-
note(`When you start the audit, Outlier will locally parse your Git commits to identify AI co-authorship and cross-reference your agent logs to calculate token waste.
|
|
1976
|
-
|
|
1977
|
-
The results will assign you a "vibe" and evaluate if you are at risk of deskilling.`, "What to Expect");
|
|
1978
|
-
const ready = await confirm({
|
|
1979
|
-
message: "Are you ready to run your first Governance Audit and measure your AI reliance?",
|
|
1980
|
-
initialValue: true
|
|
1981
|
-
});
|
|
1982
|
-
if (isCancel(ready) || !ready) {
|
|
1983
|
-
cancel("Onboarding paused. Run outlier again when you are ready.");
|
|
1984
|
-
process.exit(0);
|
|
1985
|
-
}
|
|
1909
|
+
- Native Auditing: We read your local \`~/.claude\` logs and \`.git/\` commit history.
|
|
1910
|
+
- Actionable Policies: Enforce rules locally via terminal or Git hooks.`, "Privacy & Zero-Trust Principles");
|
|
1986
1911
|
const configPath = join3(os.homedir(), ".outlier_config");
|
|
1987
1912
|
writeFileSync(configPath, JSON.stringify({ onboarded: true, date: new Date().toISOString() }));
|
|
1988
1913
|
}
|
|
@@ -2016,22 +1941,29 @@ COMMANDS:`));
|
|
|
2016
1941
|
intro(import_picocolors.default.inverse(" outlier "));
|
|
2017
1942
|
if (!action || action === "audit") {
|
|
2018
1943
|
if (action !== "audit") {
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
1944
|
+
let menuLoop = true;
|
|
1945
|
+
while (menuLoop) {
|
|
1946
|
+
action = await select({
|
|
1947
|
+
message: "Select outlier governance module:",
|
|
1948
|
+
options: [
|
|
1949
|
+
{ value: "status", label: import_picocolors.default.bold("Status") + import_picocolors.default.dim(" Full audit (reliance + carbon + capabilities)") },
|
|
1950
|
+
{ value: "authorship", label: import_picocolors.default.bold("Authorship") + import_picocolors.default.dim(" Human vs AI, per commit") },
|
|
1951
|
+
{ value: "carbon", label: import_picocolors.default.bold("Carbon") + import_picocolors.default.dim(" Token waste + regional cost") },
|
|
1952
|
+
{ value: "capabilities", label: import_picocolors.default.bold("Capabilities") + import_picocolors.default.dim(" What your agents can reach") },
|
|
1953
|
+
{ value: "policy", label: import_picocolors.default.bold("Policy") + import_picocolors.default.dim(" Set guardrails / install the gate") },
|
|
1954
|
+
{ value: "_divider", label: import_picocolors.default.dim("───────────────────────────────────────────────────────") },
|
|
1955
|
+
{ value: "impact", label: "Impact" + import_picocolors.default.dim(" What happens over the next 5-10 years") },
|
|
1956
|
+
{ value: "knowledge", label: "Literature" + import_picocolors.default.dim(" The academic foundation") },
|
|
1957
|
+
{ value: "participate", label: "Participate" + import_picocolors.default.dim(" Contribute to the research") }
|
|
1958
|
+
]
|
|
1959
|
+
});
|
|
1960
|
+
if (isCancel(action)) {
|
|
1961
|
+
cancel("Operation cancelled.");
|
|
1962
|
+
process.exit(0);
|
|
1963
|
+
}
|
|
1964
|
+
if (action !== "_divider") {
|
|
1965
|
+
menuLoop = false;
|
|
1966
|
+
}
|
|
2035
1967
|
}
|
|
2036
1968
|
} else {
|
|
2037
1969
|
action = "status";
|
|
@@ -2202,7 +2134,7 @@ ${import_picocolors.default.bold("Governance:")} ${ruleFailures > 0 ? import_pic
|
|
|
2202
2134
|
const dateStr = new Date().toLocaleDateString("en-US", { month: "short", day: "2-digit", year: "numeric" }).toUpperCase();
|
|
2203
2135
|
const timeStr = new Date().toLocaleTimeString("en-US", { hour12: false });
|
|
2204
2136
|
const repoName = process.cwd().split("/").pop() || "Unknown";
|
|
2205
|
-
|
|
2137
|
+
finalReceipt = `
|
|
2206
2138
|
${import_picocolors.default.dim("┌────────────────────────────────────────────────────────")}
|
|
2207
2139
|
${import_picocolors.default.dim("│")} ${import_picocolors.default.cyan("█▀█ █░█ ▀█▀ █░░ █ █▀▀ █▀█")} ${import_picocolors.default.bold(":: THERMAL AUDIT RECEIPT")}
|
|
2208
2140
|
${import_picocolors.default.dim("│")} ${import_picocolors.default.cyan("█▄█ █▄█ ░█░ █▄▄ █ ██▄ █▀▄")} ${import_picocolors.default.dim(`:: TIMESTAMP: ${dateStr}`)}
|
|
@@ -2236,7 +2168,7 @@ ${import_picocolors.default.bold("Governance:")} ${ruleFailures > 0 ? import_pic
|
|
|
2236
2168
|
${import_picocolors.default.dim("│")} ${import_picocolors.default.italic(import_picocolors.default.dim("human mastery is the only true moat."))}
|
|
2237
2169
|
${import_picocolors.default.dim("│")}
|
|
2238
2170
|
${import_picocolors.default.dim("│")} ${import_picocolors.default.bold(import_picocolors.default.cyan("***STAY VIGILANT***"))}
|
|
2239
|
-
${import_picocolors.default.dim("└────────────────────────────────────────────────────────")}
|
|
2171
|
+
${import_picocolors.default.dim("└────────────────────────────────────────────────────────")}`;
|
|
2240
2172
|
} else {
|
|
2241
2173
|
note(`status: ${authPct} AI Reliance | ${cachePct}% Cache Bloat | ${co2Str}`, `${import_picocolors.default.bold("[outlier]")} CI/CD Audit`);
|
|
2242
2174
|
}
|
|
@@ -2429,6 +2361,9 @@ Read the full academic foundation at: ${import_picocolors.default.underline("htt
|
|
|
2429
2361
|
`);
|
|
2430
2362
|
}
|
|
2431
2363
|
outro("Local telemetry run completed. No data left your machine.");
|
|
2364
|
+
if (typeof finalReceipt !== "undefined" && finalReceipt) {
|
|
2365
|
+
console.log(finalReceipt);
|
|
2366
|
+
}
|
|
2432
2367
|
console.log(import_picocolors.default.dim(`└ Share your audit: https://x.com/intent/tweet?text=${encodeURIComponent(`I just audited my codebase for AI reliance and deskilling risk. What does your repo score?
|
|
2433
2368
|
|
|
2434
2369
|
\uD83D\uDCCF #Outlier`)}`));
|
package/bin/postinstall.js
CHANGED
|
@@ -9,9 +9,12 @@ console.log(dim('─────────────────────
|
|
|
9
9
|
console.log('To start the interactive wizard and audit your codebase, type:\n');
|
|
10
10
|
console.log(` ${cyan('outlier')}\n`);
|
|
11
11
|
console.log('Available Commands:');
|
|
12
|
-
console.log(` ${cyan('outlier status')}
|
|
13
|
-
console.log(` ${cyan('outlier
|
|
12
|
+
console.log(` ${cyan('outlier status')} Run full AI reliance & capability audit`);
|
|
13
|
+
console.log(` ${cyan('outlier authorship')} Scan git history for AI co-authorship ratio`);
|
|
14
|
+
console.log(` ${cyan('outlier carbon')} Scan local logs for token waste & carbon cost`);
|
|
15
|
+
console.log(` ${cyan('outlier capabilities')} Audit active MCPs, skills, and orchestrations`);
|
|
16
|
+
console.log(` ${cyan('outlier policy')} Configure CI/CD guardrails and thresholds`);
|
|
17
|
+
console.log(` ${cyan('outlier impact')} See the compounding horizon of AI Deskilling`);
|
|
14
18
|
console.log(` ${cyan('outlier knowledge')} Explore core literature and METR references`);
|
|
15
19
|
console.log(` ${cyan('outlier participate')} Help build the literature on AI deskilling`);
|
|
16
|
-
console.log(` ${cyan('outlier help')} See all available commands`);
|
|
17
20
|
console.log(dim('────────────────────────────────────────────────────────────\n'));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rosh100yx/outlier",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.14",
|
|
4
4
|
"description": "AI Code Governance & Capability Auditing for the Terminal. Measures AI reliance, context waste, and enforces local CI/CD policies.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"outlier": "bin/outlier.js"
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"data"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"build": "bun build ./src/cli.ts --target=node --outfile bin/outlier.js",
|
|
15
|
+
"build": "bunx tsc --noEmit && bun build ./src/cli.ts --target=node --outfile bin/outlier.js",
|
|
16
16
|
"test": "bun test",
|
|
17
17
|
"start": "bun run src/cli.ts",
|
|
18
18
|
"postinstall": "node bin/postinstall.js"
|
package/src/cli.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import os from 'os';
|
|
2
3
|
import { intro, outro, select, spinner, isCancel, cancel, note, text, confirm } from '@clack/prompts';
|
|
3
4
|
import pc from 'picocolors';
|
|
4
5
|
import { getAuthorshipStats } from './git';
|
|
@@ -6,7 +7,6 @@ import { getCarbonStats } from './carbon';
|
|
|
6
7
|
import { getCapabilitiesStats } from './capabilities';
|
|
7
8
|
import { writeFileSync, chmodSync, existsSync } from 'fs';
|
|
8
9
|
import { join } from 'path';
|
|
9
|
-
import { execSync } from 'child_process';
|
|
10
10
|
|
|
11
11
|
const ASCII_LOGO = `
|
|
12
12
|
____ _ _ _____ _ ___ _____ ____
|
|
@@ -17,61 +17,21 @@ const ASCII_LOGO = `
|
|
|
17
17
|
\\____/|_| |_| |_| |_| |_|___|_____|_| \\_\\
|
|
18
18
|
`;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
import { confirm } from '@clack/prompts';
|
|
20
|
+
let finalReceipt = '';
|
|
22
21
|
|
|
23
22
|
async function runOnboarding() {
|
|
24
23
|
console.clear();
|
|
25
24
|
console.log(pc.cyan(ASCII_LOGO));
|
|
26
25
|
intro(pc.inverse(' outlier: Welcome '));
|
|
27
26
|
|
|
28
|
-
note(
|
|
29
|
-
`Outlier is a local-first Policy Engine & Governance Framework for AI Engineering.
|
|
30
|
-
|
|
31
|
-
Our mission is AI Safety for developers:
|
|
32
|
-
As agents (Cursor, Copilot, Claude) write more of our code, we lose visibility into:
|
|
33
|
-
1. Deskilling Risk (Are we becoming spectators in our own codebase?)
|
|
34
|
-
2. Carbon Cost (What is the true regional energy cost of token caching?)
|
|
35
|
-
3. Capability Drift (What hidden skills and external tools are our agents using?)
|
|
36
|
-
|
|
37
|
-
We built Outlier to enforce Zero-Trust and protect Human Mastery. You are in control.`,
|
|
38
|
-
'The Problem: AI Safety in Development'
|
|
39
|
-
);
|
|
40
|
-
|
|
41
27
|
note(
|
|
42
28
|
`Outlier operates entirely on your machine.
|
|
43
29
|
- Local Only: No API keys. No cloud telemetry. No data leaves your machine.
|
|
44
|
-
- Native Auditing: We
|
|
45
|
-
- Actionable Policies:
|
|
30
|
+
- Native Auditing: We read your local \`~/.claude\` logs and \`.git/\` commit history.
|
|
31
|
+
- Actionable Policies: Enforce rules locally via terminal or Git hooks.`,
|
|
46
32
|
'Privacy & Zero-Trust Principles'
|
|
47
33
|
);
|
|
48
34
|
|
|
49
|
-
note(
|
|
50
|
-
`Available Commands:
|
|
51
|
-
- status: Run a full system audit (Reliance, Carbon, Capabilities)
|
|
52
|
-
- policy: Configure team/enterprise guardrails and CLI blockers
|
|
53
|
-
- carbon: View isolated token caching metrics and regional counterfactuals
|
|
54
|
-
- authorship: View Git authorship ratio (Human vs AI)`,
|
|
55
|
-
'How it is used'
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
note(
|
|
59
|
-
`When you start the audit, Outlier will locally parse your Git commits to identify AI co-authorship and cross-reference your agent logs to calculate token waste.
|
|
60
|
-
|
|
61
|
-
The results will assign you a "vibe" and evaluate if you are at risk of deskilling.`,
|
|
62
|
-
'What to Expect'
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
const ready = await confirm({
|
|
66
|
-
message: 'Are you ready to run your first Governance Audit and measure your AI reliance?',
|
|
67
|
-
initialValue: true,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
if (isCancel(ready) || !ready) {
|
|
71
|
-
cancel('Onboarding paused. Run outlier again when you are ready.');
|
|
72
|
-
process.exit(0);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
35
|
const configPath = join(os.homedir(), '.outlier_config');
|
|
76
36
|
writeFileSync(configPath, JSON.stringify({ onboarded: true, date: new Date().toISOString() }));
|
|
77
37
|
}
|
|
@@ -108,23 +68,31 @@ async function main() {
|
|
|
108
68
|
|
|
109
69
|
if (!action || action === 'audit') {
|
|
110
70
|
if (action !== 'audit') {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
71
|
+
let menuLoop = true;
|
|
72
|
+
while (menuLoop) {
|
|
73
|
+
action = await select({
|
|
74
|
+
message: 'Select outlier governance module:',
|
|
75
|
+
options: [
|
|
76
|
+
{ value: 'status', label: pc.bold('Status') + pc.dim(' Full audit (reliance + carbon + capabilities)') },
|
|
77
|
+
{ value: 'authorship', label: pc.bold('Authorship') + pc.dim(' Human vs AI, per commit') },
|
|
78
|
+
{ value: 'carbon', label: pc.bold('Carbon') + pc.dim(' Token waste + regional cost') },
|
|
79
|
+
{ value: 'capabilities', label: pc.bold('Capabilities') + pc.dim(' What your agents can reach') },
|
|
80
|
+
{ value: 'policy', label: pc.bold('Policy') + pc.dim(' Set guardrails / install the gate') },
|
|
81
|
+
{ value: '_divider', label: pc.dim('───────────────────────────────────────────────────────') },
|
|
82
|
+
{ value: 'impact', label: 'Impact' + pc.dim(' What happens over the next 5-10 years') },
|
|
83
|
+
{ value: 'knowledge', label: 'Literature' + pc.dim(' The academic foundation') },
|
|
84
|
+
{ value: 'participate', label: 'Participate' + pc.dim(' Contribute to the research') }
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (isCancel(action)) {
|
|
89
|
+
cancel('Operation cancelled.');
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (action !== '_divider') {
|
|
94
|
+
menuLoop = false;
|
|
95
|
+
}
|
|
128
96
|
}
|
|
129
97
|
} else {
|
|
130
98
|
action = 'status'; // Map the 'audit' alias directly to status for CI
|
|
@@ -323,7 +291,7 @@ ${pc.bold('Governance:')} ${ruleFailures > 0 ? pc.red(`${failIcon} ${ruleFailure
|
|
|
323
291
|
const timeStr = new Date().toLocaleTimeString('en-US', { hour12: false });
|
|
324
292
|
const repoName = process.cwd().split('/').pop() || 'Unknown';
|
|
325
293
|
|
|
326
|
-
|
|
294
|
+
finalReceipt = `
|
|
327
295
|
${pc.dim('┌────────────────────────────────────────────────────────')}
|
|
328
296
|
${pc.dim('│')} ${pc.cyan('█▀█ █░█ ▀█▀ █░░ █ █▀▀ █▀█')} ${pc.bold(':: THERMAL AUDIT RECEIPT')}
|
|
329
297
|
${pc.dim('│')} ${pc.cyan('█▄█ █▄█ ░█░ █▄▄ █ ██▄ █▀▄')} ${pc.dim(`:: TIMESTAMP: ${dateStr}`)}
|
|
@@ -353,7 +321,7 @@ ${pc.bold('Governance:')} ${ruleFailures > 0 ? pc.red(`${failIcon} ${ruleFailure
|
|
|
353
321
|
${pc.dim('│')} ${pc.italic(pc.dim('human mastery is the only true moat.'))}
|
|
354
322
|
${pc.dim('│')}
|
|
355
323
|
${pc.dim('│')} ${pc.bold(pc.cyan('***STAY VIGILANT***'))}
|
|
356
|
-
${pc.dim('└────────────────────────────────────────────────────────')}
|
|
324
|
+
${pc.dim('└────────────────────────────────────────────────────────')}`;
|
|
357
325
|
} else {
|
|
358
326
|
note(
|
|
359
327
|
`status: ${authPct} AI Reliance | ${cachePct}% Cache Bloat | ${co2Str}`,
|
|
@@ -558,6 +526,10 @@ Artifact: ${pc.cyan(reportPath)}`,
|
|
|
558
526
|
|
|
559
527
|
outro('Local telemetry run completed. No data left your machine.');
|
|
560
528
|
|
|
529
|
+
if (typeof finalReceipt !== 'undefined' && finalReceipt) {
|
|
530
|
+
console.log(finalReceipt);
|
|
531
|
+
}
|
|
532
|
+
|
|
561
533
|
// (Old artifact storytelling block removed to unify receipt UX)
|
|
562
534
|
|
|
563
535
|
console.log(
|