nairon-bench 0.0.20 → 0.0.21
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/dist/index.js +386 -177
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3868,15 +3868,15 @@ __export(exports_client, {
|
|
|
3868
3868
|
getClerkId: () => getClerkId,
|
|
3869
3869
|
api: () => api
|
|
3870
3870
|
});
|
|
3871
|
-
import { existsSync as
|
|
3872
|
-
import { join as
|
|
3873
|
-
import { homedir as
|
|
3871
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
3872
|
+
import { join as join4 } from "node:path";
|
|
3873
|
+
import { homedir as homedir3 } from "node:os";
|
|
3874
3874
|
function loadConfig() {
|
|
3875
|
-
if (!
|
|
3875
|
+
if (!existsSync4(CONFIG_FILE)) {
|
|
3876
3876
|
return { github: undefined };
|
|
3877
3877
|
}
|
|
3878
3878
|
try {
|
|
3879
|
-
return JSON.parse(
|
|
3879
|
+
return JSON.parse(readFileSync4(CONFIG_FILE, "utf-8"));
|
|
3880
3880
|
} catch {
|
|
3881
3881
|
return { github: undefined };
|
|
3882
3882
|
}
|
|
@@ -3884,8 +3884,8 @@ function loadConfig() {
|
|
|
3884
3884
|
function saveConfig(config) {
|
|
3885
3885
|
const existing = loadConfig();
|
|
3886
3886
|
const merged = { ...existing, ...config };
|
|
3887
|
-
|
|
3888
|
-
|
|
3887
|
+
mkdirSync2(CONFIG_DIR, { recursive: true });
|
|
3888
|
+
writeFileSync2(CONFIG_FILE, JSON.stringify(merged, null, 2));
|
|
3889
3889
|
}
|
|
3890
3890
|
function getConfig() {
|
|
3891
3891
|
return loadConfig();
|
|
@@ -3929,8 +3929,8 @@ var init_client = __esm(() => {
|
|
|
3929
3929
|
init_index_node();
|
|
3930
3930
|
init_api2();
|
|
3931
3931
|
init_dist();
|
|
3932
|
-
CONFIG_DIR =
|
|
3933
|
-
CONFIG_FILE =
|
|
3932
|
+
CONFIG_DIR = join4(homedir3(), ".nairon-bench");
|
|
3933
|
+
CONFIG_FILE = join4(CONFIG_DIR, "config.json");
|
|
3934
3934
|
getClerkId = getGitHub;
|
|
3935
3935
|
requireClerkId = requireGitHub;
|
|
3936
3936
|
});
|
|
@@ -4446,8 +4446,8 @@ function defineCommand2(def) {
|
|
|
4446
4446
|
}
|
|
4447
4447
|
|
|
4448
4448
|
// src/commands/scan.ts
|
|
4449
|
-
import { existsSync as
|
|
4450
|
-
import { join as
|
|
4449
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
4450
|
+
import { join as join6 } from "node:path";
|
|
4451
4451
|
|
|
4452
4452
|
// ../../node_modules/simple-git/dist/esm/index.js
|
|
4453
4453
|
var import_file_exists = __toESM(require_dist(), 1);
|
|
@@ -8515,9 +8515,9 @@ async function collectGit(projectDir, since) {
|
|
|
8515
8515
|
}
|
|
8516
8516
|
|
|
8517
8517
|
// src/collectors/agents.ts
|
|
8518
|
-
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
8519
|
-
import { homedir } from "node:os";
|
|
8520
|
-
import { join, basename } from "node:path";
|
|
8518
|
+
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "node:fs";
|
|
8519
|
+
import { homedir as homedir2 } from "node:os";
|
|
8520
|
+
import { join as join2, basename } from "node:path";
|
|
8521
8521
|
// ../shared/src/constants.ts
|
|
8522
8522
|
var PHASE_WEIGHTS = {
|
|
8523
8523
|
requirements: 0.2,
|
|
@@ -8800,57 +8800,179 @@ function computeNaironScore(git, agents, tests) {
|
|
|
8800
8800
|
function clamp(value, min, max) {
|
|
8801
8801
|
return Math.min(Math.max(value, min), max);
|
|
8802
8802
|
}
|
|
8803
|
+
// src/lib/cache.ts
|
|
8804
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync } from "node:fs";
|
|
8805
|
+
import { join } from "node:path";
|
|
8806
|
+
import { homedir } from "node:os";
|
|
8807
|
+
var CACHE_VERSION = 1;
|
|
8808
|
+
var CACHE_DIR = join(homedir(), ".nairon", "cache");
|
|
8809
|
+
var SESSION_CACHE_FILE = join(CACHE_DIR, "sessions.json");
|
|
8810
|
+
function ensureCacheDir() {
|
|
8811
|
+
if (!existsSync(CACHE_DIR)) {
|
|
8812
|
+
mkdirSync(CACHE_DIR, { recursive: true });
|
|
8813
|
+
}
|
|
8814
|
+
}
|
|
8815
|
+
function loadCache() {
|
|
8816
|
+
try {
|
|
8817
|
+
if (existsSync(SESSION_CACHE_FILE)) {
|
|
8818
|
+
const content = readFileSync(SESSION_CACHE_FILE, "utf-8");
|
|
8819
|
+
const data = JSON.parse(content);
|
|
8820
|
+
if (data.version === CACHE_VERSION) {
|
|
8821
|
+
return data;
|
|
8822
|
+
}
|
|
8823
|
+
}
|
|
8824
|
+
} catch {}
|
|
8825
|
+
return { version: CACHE_VERSION, entries: {} };
|
|
8826
|
+
}
|
|
8827
|
+
function saveCache(data) {
|
|
8828
|
+
ensureCacheDir();
|
|
8829
|
+
writeFileSync(SESSION_CACHE_FILE, JSON.stringify(data));
|
|
8830
|
+
}
|
|
8831
|
+
function isCacheValid(filePath, cache2) {
|
|
8832
|
+
const entry = cache2.entries[filePath];
|
|
8833
|
+
if (!entry)
|
|
8834
|
+
return false;
|
|
8835
|
+
try {
|
|
8836
|
+
const stat = statSync(filePath);
|
|
8837
|
+
return entry.mtime === stat.mtimeMs && entry.size === stat.size;
|
|
8838
|
+
} catch {
|
|
8839
|
+
return false;
|
|
8840
|
+
}
|
|
8841
|
+
}
|
|
8842
|
+
function getFileStats(filePath) {
|
|
8843
|
+
try {
|
|
8844
|
+
const stat = statSync(filePath);
|
|
8845
|
+
return { mtime: stat.mtimeMs, size: stat.size };
|
|
8846
|
+
} catch {
|
|
8847
|
+
return null;
|
|
8848
|
+
}
|
|
8849
|
+
}
|
|
8850
|
+
class SessionCache {
|
|
8851
|
+
cache;
|
|
8852
|
+
dirty = false;
|
|
8853
|
+
constructor() {
|
|
8854
|
+
this.cache = loadCache();
|
|
8855
|
+
}
|
|
8856
|
+
isValid(filePath) {
|
|
8857
|
+
return isCacheValid(filePath, this.cache);
|
|
8858
|
+
}
|
|
8859
|
+
get(filePath) {
|
|
8860
|
+
if (this.isValid(filePath)) {
|
|
8861
|
+
return this.cache.entries[filePath].session;
|
|
8862
|
+
}
|
|
8863
|
+
return null;
|
|
8864
|
+
}
|
|
8865
|
+
set(filePath, session) {
|
|
8866
|
+
const stats = getFileStats(filePath);
|
|
8867
|
+
if (!stats)
|
|
8868
|
+
return;
|
|
8869
|
+
this.cache.entries[filePath] = {
|
|
8870
|
+
mtime: stats.mtime,
|
|
8871
|
+
size: stats.size,
|
|
8872
|
+
session
|
|
8873
|
+
};
|
|
8874
|
+
this.dirty = true;
|
|
8875
|
+
}
|
|
8876
|
+
save() {
|
|
8877
|
+
if (this.dirty) {
|
|
8878
|
+
saveCache(this.cache);
|
|
8879
|
+
this.dirty = false;
|
|
8880
|
+
}
|
|
8881
|
+
}
|
|
8882
|
+
getStats() {
|
|
8883
|
+
return {
|
|
8884
|
+
total: Object.keys(this.cache.entries).length,
|
|
8885
|
+
hits: 0,
|
|
8886
|
+
misses: 0
|
|
8887
|
+
};
|
|
8888
|
+
}
|
|
8889
|
+
prune() {
|
|
8890
|
+
const before = Object.keys(this.cache.entries).length;
|
|
8891
|
+
for (const filePath of Object.keys(this.cache.entries)) {
|
|
8892
|
+
if (!existsSync(filePath)) {
|
|
8893
|
+
delete this.cache.entries[filePath];
|
|
8894
|
+
this.dirty = true;
|
|
8895
|
+
}
|
|
8896
|
+
}
|
|
8897
|
+
return before - Object.keys(this.cache.entries).length;
|
|
8898
|
+
}
|
|
8899
|
+
}
|
|
8900
|
+
function clearCache() {
|
|
8901
|
+
try {
|
|
8902
|
+
if (existsSync(SESSION_CACHE_FILE)) {
|
|
8903
|
+
writeFileSync(SESSION_CACHE_FILE, JSON.stringify({ version: CACHE_VERSION, entries: {} }));
|
|
8904
|
+
}
|
|
8905
|
+
} catch {}
|
|
8906
|
+
}
|
|
8907
|
+
function getCacheInfo() {
|
|
8908
|
+
try {
|
|
8909
|
+
if (existsSync(SESSION_CACHE_FILE)) {
|
|
8910
|
+
const stat = statSync(SESSION_CACHE_FILE);
|
|
8911
|
+
const cache2 = loadCache();
|
|
8912
|
+
return {
|
|
8913
|
+
entries: Object.keys(cache2.entries).length,
|
|
8914
|
+
sizeBytes: stat.size
|
|
8915
|
+
};
|
|
8916
|
+
}
|
|
8917
|
+
} catch {}
|
|
8918
|
+
return { entries: 0, sizeBytes: 0 };
|
|
8919
|
+
}
|
|
8920
|
+
|
|
8803
8921
|
// src/collectors/agents.ts
|
|
8804
|
-
|
|
8922
|
+
var sessionCache = null;
|
|
8923
|
+
async function collectAgentSessions(since, useCache = true) {
|
|
8924
|
+
sessionCache = useCache ? new SessionCache : null;
|
|
8805
8925
|
const sessions = [];
|
|
8806
|
-
const claudeProjectsDir =
|
|
8807
|
-
if (
|
|
8926
|
+
const claudeProjectsDir = join2(homedir2(), ".claude", "projects");
|
|
8927
|
+
if (existsSync2(claudeProjectsDir)) {
|
|
8808
8928
|
const claudeSessions = collectClaudeSessions(claudeProjectsDir, since);
|
|
8809
8929
|
sessions.push(...claudeSessions);
|
|
8810
8930
|
}
|
|
8811
|
-
const openCodeDir =
|
|
8812
|
-
if (
|
|
8931
|
+
const openCodeDir = join2(homedir2(), ".local", "share", "opencode");
|
|
8932
|
+
if (existsSync2(openCodeDir)) {
|
|
8813
8933
|
const openCodeSessions = collectOpenCodeSessions(openCodeDir, since);
|
|
8814
8934
|
sessions.push(...openCodeSessions);
|
|
8815
8935
|
}
|
|
8816
8936
|
const cursorDirs = [
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8937
|
+
join2(homedir2(), ".cursor", "sessions"),
|
|
8938
|
+
join2(homedir2(), "Library", "Application Support", "Cursor", "sessions"),
|
|
8939
|
+
join2(homedir2(), ".config", "cursor", "sessions")
|
|
8820
8940
|
];
|
|
8821
8941
|
for (const cursorDir of cursorDirs) {
|
|
8822
|
-
if (
|
|
8942
|
+
if (existsSync2(cursorDir)) {
|
|
8823
8943
|
const cursorSessions = collectCursorSessions(cursorDir, since);
|
|
8824
8944
|
sessions.push(...cursorSessions);
|
|
8825
8945
|
break;
|
|
8826
8946
|
}
|
|
8827
8947
|
}
|
|
8828
8948
|
const windsurfDirs = [
|
|
8829
|
-
|
|
8830
|
-
|
|
8831
|
-
|
|
8949
|
+
join2(homedir2(), ".codeium", "chat_history"),
|
|
8950
|
+
join2(homedir2(), ".windsurf", "sessions"),
|
|
8951
|
+
join2(homedir2(), "Library", "Application Support", "Windsurf", "sessions")
|
|
8832
8952
|
];
|
|
8833
8953
|
for (const windsurfDir of windsurfDirs) {
|
|
8834
|
-
if (
|
|
8954
|
+
if (existsSync2(windsurfDir)) {
|
|
8835
8955
|
const windsurfSessions = collectWindsurfSessions(windsurfDir, since);
|
|
8836
8956
|
sessions.push(...windsurfSessions);
|
|
8837
8957
|
break;
|
|
8838
8958
|
}
|
|
8839
8959
|
}
|
|
8840
8960
|
const copilotDirs = [
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8961
|
+
join2(homedir2(), ".config", "github-copilot"),
|
|
8962
|
+
join2(homedir2(), "Library", "Application Support", "Code", "User", "globalStorage", "github.copilot"),
|
|
8963
|
+
join2(homedir2(), ".vscode", "extensions")
|
|
8844
8964
|
];
|
|
8845
8965
|
for (const copilotDir of copilotDirs) {
|
|
8846
|
-
if (
|
|
8966
|
+
if (existsSync2(copilotDir)) {
|
|
8847
8967
|
const copilotSessions = collectCopilotSessions(copilotDir, since);
|
|
8848
8968
|
sessions.push(...copilotSessions);
|
|
8849
8969
|
break;
|
|
8850
8970
|
}
|
|
8851
8971
|
}
|
|
8852
|
-
if (sessions.length === 0)
|
|
8972
|
+
if (sessions.length === 0) {
|
|
8973
|
+
sessionCache?.save();
|
|
8853
8974
|
return null;
|
|
8975
|
+
}
|
|
8854
8976
|
let totalTokens = 0;
|
|
8855
8977
|
let totalInputTokens = 0;
|
|
8856
8978
|
let totalOutputTokens = 0;
|
|
@@ -8873,6 +8995,7 @@ async function collectAgentSessions(since) {
|
|
|
8873
8995
|
costByModel[session.model] = (costByModel[session.model] ?? 0) + session.costUsd;
|
|
8874
8996
|
}
|
|
8875
8997
|
}
|
|
8998
|
+
sessionCache?.save();
|
|
8876
8999
|
return {
|
|
8877
9000
|
sessions,
|
|
8878
9001
|
totalSessions: sessions.length,
|
|
@@ -8890,9 +9013,9 @@ async function collectAgentSessions(since) {
|
|
|
8890
9013
|
function collectClaudeSessions(projectsDir, since) {
|
|
8891
9014
|
const sessions = [];
|
|
8892
9015
|
try {
|
|
8893
|
-
const projectDirs = readdirSync(projectsDir, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) =>
|
|
9016
|
+
const projectDirs = readdirSync(projectsDir, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) => join2(projectsDir, d2.name));
|
|
8894
9017
|
for (const projectDir of projectDirs) {
|
|
8895
|
-
const files = readdirSync(projectDir).filter((f3) => f3.endsWith(".jsonl")).map((f3) =>
|
|
9018
|
+
const files = readdirSync(projectDir).filter((f3) => f3.endsWith(".jsonl")).map((f3) => join2(projectDir, f3));
|
|
8896
9019
|
for (const file of files) {
|
|
8897
9020
|
const session = parseClaudeSessionFile(file, since);
|
|
8898
9021
|
if (session) {
|
|
@@ -8905,7 +9028,15 @@ function collectClaudeSessions(projectsDir, since) {
|
|
|
8905
9028
|
}
|
|
8906
9029
|
function parseClaudeSessionFile(filePath, since) {
|
|
8907
9030
|
try {
|
|
8908
|
-
const
|
|
9031
|
+
const cached = sessionCache?.get(filePath);
|
|
9032
|
+
if (cached) {
|
|
9033
|
+
const cachedEndedAt = cached.endedAt ? new Date(cached.endedAt) : null;
|
|
9034
|
+
if (cachedEndedAt && cachedEndedAt >= since) {
|
|
9035
|
+
return cached;
|
|
9036
|
+
}
|
|
9037
|
+
return null;
|
|
9038
|
+
}
|
|
9039
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
8909
9040
|
const lines = content.trim().split(`
|
|
8910
9041
|
`).filter(Boolean);
|
|
8911
9042
|
if (lines.length === 0)
|
|
@@ -8970,7 +9101,7 @@ function parseClaudeSessionFile(filePath, since) {
|
|
|
8970
9101
|
const durationMinutes = Math.round((endedAt.getTime() - startedAt.getTime()) / 60000);
|
|
8971
9102
|
const costUsd = calculateTokenCost(model, inputTokens, outputTokens, cachedTokens);
|
|
8972
9103
|
const patterns = detectPatterns(messages);
|
|
8973
|
-
|
|
9104
|
+
const session = {
|
|
8974
9105
|
agent: "claude",
|
|
8975
9106
|
sessionId: basename(filePath, ".jsonl"),
|
|
8976
9107
|
startedAt: startedAt.toISOString(),
|
|
@@ -8987,6 +9118,8 @@ function parseClaudeSessionFile(filePath, since) {
|
|
|
8987
9118
|
toolNames: Array.from(toolNamesSet),
|
|
8988
9119
|
patterns
|
|
8989
9120
|
};
|
|
9121
|
+
sessionCache?.set(filePath, session);
|
|
9122
|
+
return session;
|
|
8990
9123
|
} catch {
|
|
8991
9124
|
return null;
|
|
8992
9125
|
}
|
|
@@ -9157,15 +9290,15 @@ function similarity(a2, b2) {
|
|
|
9157
9290
|
}
|
|
9158
9291
|
function collectOpenCodeSessions(baseDir, since) {
|
|
9159
9292
|
const sessions = [];
|
|
9160
|
-
const sessionStorageDir =
|
|
9161
|
-
const messageStorageDir =
|
|
9162
|
-
const partStorageDir =
|
|
9163
|
-
if (!
|
|
9293
|
+
const sessionStorageDir = join2(baseDir, "storage", "session");
|
|
9294
|
+
const messageStorageDir = join2(baseDir, "storage", "message");
|
|
9295
|
+
const partStorageDir = join2(baseDir, "storage", "part");
|
|
9296
|
+
if (!existsSync2(sessionStorageDir))
|
|
9164
9297
|
return sessions;
|
|
9165
9298
|
try {
|
|
9166
|
-
const projectDirs = readdirSync(sessionStorageDir, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) =>
|
|
9299
|
+
const projectDirs = readdirSync(sessionStorageDir, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) => join2(sessionStorageDir, d2.name));
|
|
9167
9300
|
for (const projectDir of projectDirs) {
|
|
9168
|
-
const sessionFiles = readdirSync(projectDir).filter((f3) => f3.startsWith("ses_") && f3.endsWith(".json")).map((f3) =>
|
|
9301
|
+
const sessionFiles = readdirSync(projectDir).filter((f3) => f3.startsWith("ses_") && f3.endsWith(".json")).map((f3) => join2(projectDir, f3));
|
|
9169
9302
|
for (const sessionFile of sessionFiles) {
|
|
9170
9303
|
const session = parseOpenCodeSession(sessionFile, messageStorageDir, partStorageDir, since);
|
|
9171
9304
|
if (session) {
|
|
@@ -9178,20 +9311,28 @@ function collectOpenCodeSessions(baseDir, since) {
|
|
|
9178
9311
|
}
|
|
9179
9312
|
function parseOpenCodeSession(sessionFile, messageStorageDir, partStorageDir, since) {
|
|
9180
9313
|
try {
|
|
9181
|
-
const
|
|
9314
|
+
const cached = sessionCache?.get(sessionFile);
|
|
9315
|
+
if (cached) {
|
|
9316
|
+
const cachedEndedAt = cached.endedAt ? new Date(cached.endedAt) : null;
|
|
9317
|
+
if (cachedEndedAt && cachedEndedAt >= since) {
|
|
9318
|
+
return cached;
|
|
9319
|
+
}
|
|
9320
|
+
return null;
|
|
9321
|
+
}
|
|
9322
|
+
const sessionContent = readFileSync2(sessionFile, "utf-8");
|
|
9182
9323
|
const session = JSON.parse(sessionContent);
|
|
9183
9324
|
const sessionStart = new Date(session.time.created);
|
|
9184
9325
|
const sessionEnd = session.time.updated ? new Date(session.time.updated) : sessionStart;
|
|
9185
9326
|
if (sessionEnd < since)
|
|
9186
9327
|
return null;
|
|
9187
|
-
const messagesDir =
|
|
9188
|
-
if (!
|
|
9328
|
+
const messagesDir = join2(messageStorageDir, session.id);
|
|
9329
|
+
if (!existsSync2(messagesDir))
|
|
9189
9330
|
return null;
|
|
9190
|
-
const messageFiles = readdirSync(messagesDir).filter((f3) => f3.startsWith("msg_") && f3.endsWith(".json")).map((f3) =>
|
|
9331
|
+
const messageFiles = readdirSync(messagesDir).filter((f3) => f3.startsWith("msg_") && f3.endsWith(".json")).map((f3) => join2(messagesDir, f3));
|
|
9191
9332
|
const messages = [];
|
|
9192
9333
|
for (const msgFile of messageFiles) {
|
|
9193
9334
|
try {
|
|
9194
|
-
const msgContent =
|
|
9335
|
+
const msgContent = readFileSync2(msgFile, "utf-8");
|
|
9195
9336
|
messages.push(JSON.parse(msgContent));
|
|
9196
9337
|
} catch {}
|
|
9197
9338
|
}
|
|
@@ -9223,7 +9364,7 @@ function parseOpenCodeSession(sessionFile, messageStorageDir, partStorageDir, si
|
|
|
9223
9364
|
const durationMinutes = Math.round((sessionEnd.getTime() - sessionStart.getTime()) / 60000);
|
|
9224
9365
|
const costUsd = calculateTokenCost(model, inputTokens, outputTokens, cachedTokens);
|
|
9225
9366
|
const patterns = detectOpenCodePatterns(messages, partStorageDir);
|
|
9226
|
-
|
|
9367
|
+
const agentSession = {
|
|
9227
9368
|
agent: "opencode",
|
|
9228
9369
|
sessionId: session.id,
|
|
9229
9370
|
startedAt: sessionStart.toISOString(),
|
|
@@ -9240,6 +9381,8 @@ function parseOpenCodeSession(sessionFile, messageStorageDir, partStorageDir, si
|
|
|
9240
9381
|
toolNames: [],
|
|
9241
9382
|
patterns
|
|
9242
9383
|
};
|
|
9384
|
+
sessionCache?.set(sessionFile, agentSession);
|
|
9385
|
+
return agentSession;
|
|
9243
9386
|
} catch {
|
|
9244
9387
|
return null;
|
|
9245
9388
|
}
|
|
@@ -9251,14 +9394,14 @@ function detectOpenCodePatterns(messages, partStorageDir) {
|
|
|
9251
9394
|
const msg = messages[i2];
|
|
9252
9395
|
if (msg.role !== "user")
|
|
9253
9396
|
continue;
|
|
9254
|
-
const partDir =
|
|
9255
|
-
if (!
|
|
9397
|
+
const partDir = join2(partStorageDir, msg.id);
|
|
9398
|
+
if (!existsSync2(partDir))
|
|
9256
9399
|
continue;
|
|
9257
9400
|
try {
|
|
9258
9401
|
const partFiles = readdirSync(partDir).filter((f3) => f3.endsWith(".json"));
|
|
9259
9402
|
let text = "";
|
|
9260
9403
|
for (const partFile of partFiles) {
|
|
9261
|
-
const partContent =
|
|
9404
|
+
const partContent = readFileSync2(join2(partDir, partFile), "utf-8");
|
|
9262
9405
|
const part = JSON.parse(partContent);
|
|
9263
9406
|
if (part.type === "text" && part.text) {
|
|
9264
9407
|
text += part.text + " ";
|
|
@@ -9384,9 +9527,9 @@ function collectCursorSessions(sessionsDir, since) {
|
|
|
9384
9527
|
try {
|
|
9385
9528
|
const files = readdirSync(sessionsDir).filter((f3) => f3.endsWith(".json") || f3.endsWith(".jsonl"));
|
|
9386
9529
|
for (const file of files) {
|
|
9387
|
-
const filePath =
|
|
9530
|
+
const filePath = join2(sessionsDir, file);
|
|
9388
9531
|
try {
|
|
9389
|
-
const content =
|
|
9532
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
9390
9533
|
let data;
|
|
9391
9534
|
if (content.startsWith("[")) {
|
|
9392
9535
|
data = JSON.parse(content);
|
|
@@ -9431,7 +9574,7 @@ function collectCursorSessions(sessionsDir, since) {
|
|
|
9431
9574
|
outputTokens,
|
|
9432
9575
|
cachedTokens: 0,
|
|
9433
9576
|
totalTokens,
|
|
9434
|
-
costUsd: calculateTokenCost(
|
|
9577
|
+
costUsd: calculateTokenCost(firstEntry?.model || "gpt-4", inputTokens, outputTokens),
|
|
9435
9578
|
model: firstEntry?.model || "gpt-4",
|
|
9436
9579
|
toolCalls: 0,
|
|
9437
9580
|
messageCount,
|
|
@@ -9448,9 +9591,9 @@ function collectWindsurfSessions(sessionsDir, since) {
|
|
|
9448
9591
|
try {
|
|
9449
9592
|
const files = readdirSync(sessionsDir).filter((f3) => f3.endsWith(".json") || f3.endsWith(".jsonl"));
|
|
9450
9593
|
for (const file of files) {
|
|
9451
|
-
const filePath =
|
|
9594
|
+
const filePath = join2(sessionsDir, file);
|
|
9452
9595
|
try {
|
|
9453
|
-
const content =
|
|
9596
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
9454
9597
|
let data;
|
|
9455
9598
|
if (content.startsWith("[")) {
|
|
9456
9599
|
data = JSON.parse(content);
|
|
@@ -9494,7 +9637,7 @@ function collectWindsurfSessions(sessionsDir, since) {
|
|
|
9494
9637
|
outputTokens,
|
|
9495
9638
|
cachedTokens: 0,
|
|
9496
9639
|
totalTokens,
|
|
9497
|
-
costUsd: calculateTokenCost(inputTokens, outputTokens
|
|
9640
|
+
costUsd: calculateTokenCost(firstEntry?.model || "codeium", inputTokens, outputTokens),
|
|
9498
9641
|
model: firstEntry?.model || "codeium",
|
|
9499
9642
|
toolCalls: 0,
|
|
9500
9643
|
messageCount,
|
|
@@ -9510,19 +9653,19 @@ function collectCopilotSessions(copilotDir, since) {
|
|
|
9510
9653
|
const sessions = [];
|
|
9511
9654
|
try {
|
|
9512
9655
|
const searchPaths = [
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
|
|
9656
|
+
join2(copilotDir, "usage"),
|
|
9657
|
+
join2(copilotDir, "telemetry"),
|
|
9658
|
+
join2(copilotDir, "chat"),
|
|
9516
9659
|
copilotDir
|
|
9517
9660
|
];
|
|
9518
9661
|
for (const searchPath of searchPaths) {
|
|
9519
|
-
if (!
|
|
9662
|
+
if (!existsSync2(searchPath))
|
|
9520
9663
|
continue;
|
|
9521
9664
|
const files = readdirSync(searchPath).filter((f3) => f3.endsWith(".json") || f3.endsWith(".jsonl") || f3.endsWith(".log"));
|
|
9522
9665
|
for (const file of files) {
|
|
9523
|
-
const filePath =
|
|
9666
|
+
const filePath = join2(searchPath, file);
|
|
9524
9667
|
try {
|
|
9525
|
-
const content =
|
|
9668
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
9526
9669
|
let entries = [];
|
|
9527
9670
|
try {
|
|
9528
9671
|
if (content.startsWith("[")) {
|
|
@@ -9584,8 +9727,8 @@ function collectCopilotSessions(copilotDir, since) {
|
|
|
9584
9727
|
}
|
|
9585
9728
|
|
|
9586
9729
|
// src/collectors/tests.ts
|
|
9587
|
-
import { existsSync as
|
|
9588
|
-
import { join as
|
|
9730
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
9731
|
+
import { join as join3 } from "node:path";
|
|
9589
9732
|
async function collectTestResults(projectDir) {
|
|
9590
9733
|
const vitestResult = tryVitestReport(projectDir);
|
|
9591
9734
|
if (vitestResult)
|
|
@@ -9600,16 +9743,16 @@ async function collectTestResults(projectDir) {
|
|
|
9600
9743
|
}
|
|
9601
9744
|
function tryVitestReport(projectDir) {
|
|
9602
9745
|
const paths = [
|
|
9603
|
-
|
|
9604
|
-
|
|
9746
|
+
join3(projectDir, "test-results", "vitest-results.json"),
|
|
9747
|
+
join3(projectDir, "coverage", "coverage-summary.json")
|
|
9605
9748
|
];
|
|
9606
|
-
const hasVitest = ["vitest.config.ts", "vitest.config.js", "vitest.config.mts"].some((f3) =>
|
|
9749
|
+
const hasVitest = ["vitest.config.ts", "vitest.config.js", "vitest.config.mts"].some((f3) => existsSync3(join3(projectDir, f3)));
|
|
9607
9750
|
if (!hasVitest)
|
|
9608
9751
|
return null;
|
|
9609
9752
|
for (const p of paths) {
|
|
9610
|
-
if (
|
|
9753
|
+
if (existsSync3(p)) {
|
|
9611
9754
|
try {
|
|
9612
|
-
const data = JSON.parse(
|
|
9755
|
+
const data = JSON.parse(readFileSync3(p, "utf-8"));
|
|
9613
9756
|
return parseVitestJson(data);
|
|
9614
9757
|
} catch {}
|
|
9615
9758
|
}
|
|
@@ -9645,16 +9788,16 @@ function parseVitestJson(data) {
|
|
|
9645
9788
|
}
|
|
9646
9789
|
function tryJestReport(projectDir) {
|
|
9647
9790
|
const paths = [
|
|
9648
|
-
|
|
9649
|
-
|
|
9791
|
+
join3(projectDir, "test-results", "jest-results.json"),
|
|
9792
|
+
join3(projectDir, "jest-results.json")
|
|
9650
9793
|
];
|
|
9651
|
-
const hasJest = ["jest.config.ts", "jest.config.js", "jest.config.mjs"].some((f3) =>
|
|
9794
|
+
const hasJest = ["jest.config.ts", "jest.config.js", "jest.config.mjs"].some((f3) => existsSync3(join3(projectDir, f3)));
|
|
9652
9795
|
if (!hasJest)
|
|
9653
9796
|
return null;
|
|
9654
9797
|
for (const p of paths) {
|
|
9655
|
-
if (
|
|
9798
|
+
if (existsSync3(p)) {
|
|
9656
9799
|
try {
|
|
9657
|
-
const data = JSON.parse(
|
|
9800
|
+
const data = JSON.parse(readFileSync3(p, "utf-8"));
|
|
9658
9801
|
return {
|
|
9659
9802
|
framework: "jest",
|
|
9660
9803
|
totalTests: data.numTotalTests ?? 0,
|
|
@@ -9675,13 +9818,13 @@ function tryPlaywrightReport(projectDir) {
|
|
|
9675
9818
|
const hasPlaywright = [
|
|
9676
9819
|
"playwright.config.ts",
|
|
9677
9820
|
"playwright.config.js"
|
|
9678
|
-
].some((f3) =>
|
|
9821
|
+
].some((f3) => existsSync3(join3(projectDir, f3)));
|
|
9679
9822
|
if (!hasPlaywright)
|
|
9680
9823
|
return null;
|
|
9681
|
-
const reportPath =
|
|
9682
|
-
if (
|
|
9824
|
+
const reportPath = join3(projectDir, "test-results", "results.json");
|
|
9825
|
+
if (existsSync3(reportPath)) {
|
|
9683
9826
|
try {
|
|
9684
|
-
const data = JSON.parse(
|
|
9827
|
+
const data = JSON.parse(readFileSync3(reportPath, "utf-8"));
|
|
9685
9828
|
const suites = data.suites ?? [];
|
|
9686
9829
|
let passed = 0;
|
|
9687
9830
|
let failed = 0;
|
|
@@ -9722,9 +9865,9 @@ function tryPlaywrightReport(projectDir) {
|
|
|
9722
9865
|
init_client();
|
|
9723
9866
|
|
|
9724
9867
|
// src/lib/analyzer.ts
|
|
9725
|
-
import { existsSync as
|
|
9726
|
-
import { homedir as
|
|
9727
|
-
import { join as
|
|
9868
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "node:fs";
|
|
9869
|
+
import { homedir as homedir4 } from "node:os";
|
|
9870
|
+
import { join as join5 } from "node:path";
|
|
9728
9871
|
import { execSync } from "node:child_process";
|
|
9729
9872
|
function analyzeWorkflow(agents, projectDir) {
|
|
9730
9873
|
const sessionPatterns = analyzeSessionPatterns(agents);
|
|
@@ -9829,10 +9972,10 @@ function analyzeEfficiency(agents) {
|
|
|
9829
9972
|
};
|
|
9830
9973
|
}
|
|
9831
9974
|
function detectEnvironment(projectDir, agents) {
|
|
9832
|
-
const home =
|
|
9833
|
-
const claudeConfigDir =
|
|
9834
|
-
const hasClaudeRules =
|
|
9835
|
-
const hasAgentsmd =
|
|
9975
|
+
const home = homedir4();
|
|
9976
|
+
const claudeConfigDir = join5(home, ".claude");
|
|
9977
|
+
const hasClaudeRules = existsSync5(join5(projectDir, "CLAUDE.md")) || existsSync5(join5(projectDir, ".claude", "settings.local.json")) || existsSync5(join5(projectDir, ".cursorrules")) || existsSync5(join5(projectDir, ".clauderules"));
|
|
9978
|
+
const hasAgentsmd = existsSync5(join5(projectDir, "AGENTS.md"));
|
|
9836
9979
|
let hasSupermemory = false;
|
|
9837
9980
|
let hasContext7 = false;
|
|
9838
9981
|
let hasNia = false;
|
|
@@ -9886,13 +10029,13 @@ function detectEnvironment(projectDir, agents) {
|
|
|
9886
10029
|
}
|
|
9887
10030
|
}
|
|
9888
10031
|
}
|
|
9889
|
-
const transcriptsDir =
|
|
9890
|
-
if (!hasSupermemory &&
|
|
10032
|
+
const transcriptsDir = join5(home, ".claude", "transcripts");
|
|
10033
|
+
if (!hasSupermemory && existsSync5(transcriptsDir)) {
|
|
9891
10034
|
try {
|
|
9892
10035
|
const transcriptFiles = readdirSync2(transcriptsDir).filter((f3) => f3.endsWith(".jsonl")).slice(-20);
|
|
9893
10036
|
for (const file of transcriptFiles) {
|
|
9894
10037
|
try {
|
|
9895
|
-
const content =
|
|
10038
|
+
const content = readFileSync5(join5(transcriptsDir, file), "utf-8");
|
|
9896
10039
|
if (content.includes('"tool_name":"supermemory"') || content.includes('"name":"supermemory"') || content.includes("supermemory")) {
|
|
9897
10040
|
hasSupermemory = true;
|
|
9898
10041
|
if (!detectedMcps.includes("supermemory")) {
|
|
@@ -9905,18 +10048,18 @@ function detectEnvironment(projectDir, agents) {
|
|
|
9905
10048
|
}
|
|
9906
10049
|
} catch {}
|
|
9907
10050
|
}
|
|
9908
|
-
const claudeSettingsLocal =
|
|
9909
|
-
const claudeProjectSettings =
|
|
10051
|
+
const claudeSettingsLocal = join5(home, ".claude", "settings.local.json");
|
|
10052
|
+
const claudeProjectSettings = join5(projectDir, ".claude", "settings.local.json");
|
|
9910
10053
|
const mcpConfigPaths = [
|
|
9911
|
-
|
|
9912
|
-
|
|
10054
|
+
join5(home, ".claude", "claude_desktop_config.json"),
|
|
10055
|
+
join5(home, ".claude", "settings.json"),
|
|
9913
10056
|
claudeSettingsLocal,
|
|
9914
10057
|
claudeProjectSettings
|
|
9915
10058
|
];
|
|
9916
10059
|
for (const configPath of mcpConfigPaths) {
|
|
9917
|
-
if (
|
|
10060
|
+
if (existsSync5(configPath)) {
|
|
9918
10061
|
try {
|
|
9919
|
-
const config = JSON.parse(
|
|
10062
|
+
const config = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
9920
10063
|
const mcpServers = config.mcpServers || config.mcp_servers || {};
|
|
9921
10064
|
for (const [name, _3] of Object.entries(mcpServers)) {
|
|
9922
10065
|
const mcpName = name.toLowerCase();
|
|
@@ -9940,12 +10083,12 @@ function detectEnvironment(projectDir, agents) {
|
|
|
9940
10083
|
} catch {}
|
|
9941
10084
|
}
|
|
9942
10085
|
}
|
|
9943
|
-
const agentsMdPath =
|
|
9944
|
-
const globalAgentsMdPath =
|
|
10086
|
+
const agentsMdPath = join5(projectDir, "AGENTS.md");
|
|
10087
|
+
const globalAgentsMdPath = join5(home, ".config", "Claude", "AGENTS.md");
|
|
9945
10088
|
for (const agentsMd of [agentsMdPath, globalAgentsMdPath]) {
|
|
9946
|
-
if (
|
|
10089
|
+
if (existsSync5(agentsMd)) {
|
|
9947
10090
|
try {
|
|
9948
|
-
const content =
|
|
10091
|
+
const content = readFileSync5(agentsMd, "utf-8").toLowerCase();
|
|
9949
10092
|
if (content.includes("context7") || content.includes("context-7")) {
|
|
9950
10093
|
hasContext7 = true;
|
|
9951
10094
|
if (!detectedMcps.includes("context7")) {
|
|
@@ -10003,16 +10146,16 @@ function detectEnvironment(projectDir, agents) {
|
|
|
10003
10146
|
} catch {}
|
|
10004
10147
|
}
|
|
10005
10148
|
if (!hasBeads) {
|
|
10006
|
-
hasBeads =
|
|
10149
|
+
hasBeads = existsSync5(join5(projectDir, ".beads"));
|
|
10007
10150
|
}
|
|
10008
10151
|
let hasSkills = false;
|
|
10009
10152
|
let skillCount = 0;
|
|
10010
10153
|
const skillsDirs = [
|
|
10011
|
-
|
|
10012
|
-
|
|
10154
|
+
join5(claudeConfigDir, "skills"),
|
|
10155
|
+
join5(home, ".agents", "skills")
|
|
10013
10156
|
];
|
|
10014
10157
|
for (const skillsDir of skillsDirs) {
|
|
10015
|
-
if (
|
|
10158
|
+
if (existsSync5(skillsDir)) {
|
|
10016
10159
|
try {
|
|
10017
10160
|
const skills = readdirSync2(skillsDir, { withFileTypes: true }).filter((d2) => d2.isDirectory() || d2.isSymbolicLink());
|
|
10018
10161
|
skillCount += skills.length;
|
|
@@ -10223,7 +10366,7 @@ function analyzeRequirements(agents, git, projectDir) {
|
|
|
10223
10366
|
recommendations.push("Spend more time understanding the problem before implementing");
|
|
10224
10367
|
}
|
|
10225
10368
|
}
|
|
10226
|
-
const hasRequirementsDocs =
|
|
10369
|
+
const hasRequirementsDocs = existsSync5(join5(projectDir, "CLAUDE.md")) || existsSync5(join5(projectDir, "docs", "requirements.md")) || existsSync5(join5(projectDir, "PRD.md"));
|
|
10227
10370
|
if (hasRequirementsDocs) {
|
|
10228
10371
|
score += 10;
|
|
10229
10372
|
} else {
|
|
@@ -10250,18 +10393,18 @@ function analyzePlanning(agents, git, projectDir) {
|
|
|
10250
10393
|
const issues = [];
|
|
10251
10394
|
const recommendations = [];
|
|
10252
10395
|
let score = 50;
|
|
10253
|
-
const usesTaskTracking =
|
|
10396
|
+
const usesTaskTracking = existsSync5(join5(projectDir, ".beads"));
|
|
10254
10397
|
if (usesTaskTracking) {
|
|
10255
10398
|
score += 20;
|
|
10256
10399
|
} else {
|
|
10257
10400
|
issues.push("No task tracking system detected");
|
|
10258
10401
|
recommendations.push("Install Beads for persistent task tracking: bun add -g beads && bd init");
|
|
10259
10402
|
}
|
|
10260
|
-
const usesDesignDocs =
|
|
10403
|
+
const usesDesignDocs = existsSync5(join5(projectDir, "docs", "design.md")) || existsSync5(join5(projectDir, "ARCHITECTURE.md")) || existsSync5(join5(projectDir, "docs", "spec.md"));
|
|
10261
10404
|
if (usesDesignDocs) {
|
|
10262
10405
|
score += 15;
|
|
10263
10406
|
}
|
|
10264
|
-
const hasSystemDesign =
|
|
10407
|
+
const hasSystemDesign = existsSync5(join5(projectDir, "CLAUDE.md")) || existsSync5(join5(projectDir, "AGENTS.md")) || existsSync5(join5(projectDir, ".claude", "settings.local.json"));
|
|
10265
10408
|
if (hasSystemDesign) {
|
|
10266
10409
|
score += 15;
|
|
10267
10410
|
} else {
|
|
@@ -10363,7 +10506,7 @@ function analyzeTestingPhase(agents, tests, projectDir) {
|
|
|
10363
10506
|
const issues = [];
|
|
10364
10507
|
const recommendations = [];
|
|
10365
10508
|
let score = 40;
|
|
10366
|
-
const hasTestFramework = tests !== null ||
|
|
10509
|
+
const hasTestFramework = tests !== null || existsSync5(join5(projectDir, "vitest.config.ts")) || existsSync5(join5(projectDir, "jest.config.js")) || existsSync5(join5(projectDir, "playwright.config.ts"));
|
|
10367
10510
|
if (hasTestFramework) {
|
|
10368
10511
|
score += 15;
|
|
10369
10512
|
} else {
|
|
@@ -14023,6 +14166,11 @@ var scanCommand = defineCommand2({
|
|
|
14023
14166
|
alias: "q",
|
|
14024
14167
|
description: "Quiet mode - only output the score number",
|
|
14025
14168
|
default: false
|
|
14169
|
+
},
|
|
14170
|
+
"no-cache": {
|
|
14171
|
+
type: "boolean",
|
|
14172
|
+
description: "Disable session caching (slower but ensures fresh data)",
|
|
14173
|
+
default: false
|
|
14026
14174
|
}
|
|
14027
14175
|
},
|
|
14028
14176
|
async run({ args }) {
|
|
@@ -14030,6 +14178,7 @@ var scanCommand = defineCommand2({
|
|
|
14030
14178
|
const since = parseSince(args.since);
|
|
14031
14179
|
const jsonOutput = args.json;
|
|
14032
14180
|
const quietMode = args.quiet;
|
|
14181
|
+
const useCache = !args["no-cache"];
|
|
14033
14182
|
const silent = jsonOutput || quietMode;
|
|
14034
14183
|
if (!silent) {
|
|
14035
14184
|
console.log();
|
|
@@ -14047,7 +14196,7 @@ var scanCommand = defineCommand2({
|
|
|
14047
14196
|
}
|
|
14048
14197
|
if (!silent)
|
|
14049
14198
|
await thinkingStep("Scanning AI session logs...", 600);
|
|
14050
|
-
const agents = await collectAgentSessions(since);
|
|
14199
|
+
const agents = await collectAgentSessions(since, useCache);
|
|
14051
14200
|
if (agents && !silent) {
|
|
14052
14201
|
await revealDiscovery(icons.success, `Sessions: ${colors2.primary(agents.totalSessions.toString())} sessions loaded`, 200);
|
|
14053
14202
|
}
|
|
@@ -14142,7 +14291,7 @@ var scanCommand = defineCommand2({
|
|
|
14142
14291
|
console.log(colors2.dim(" " + "═".repeat(45)));
|
|
14143
14292
|
console.log();
|
|
14144
14293
|
if (!args["no-report"]) {
|
|
14145
|
-
const reportDir =
|
|
14294
|
+
const reportDir = join6(projectDir, args["report-dir"]);
|
|
14146
14295
|
const reportPath = generateReport(reportDir, score, git, agents, tests, args.since, sdlcAnalysis, scanCost, analysis);
|
|
14147
14296
|
console.log(` ${icons.success} Report saved: ${colors2.dim(reportPath)}`);
|
|
14148
14297
|
}
|
|
@@ -14302,12 +14451,12 @@ function parseSince(since) {
|
|
|
14302
14451
|
return new Date(Date.now() - ms);
|
|
14303
14452
|
}
|
|
14304
14453
|
function generateReport(reportDir, score, git, agents, tests, since, sdlcAnalysis, scanCost, analysis) {
|
|
14305
|
-
if (!
|
|
14306
|
-
|
|
14454
|
+
if (!existsSync6(reportDir)) {
|
|
14455
|
+
mkdirSync3(reportDir, { recursive: true });
|
|
14307
14456
|
}
|
|
14308
14457
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
14309
14458
|
const filename = `scan-${timestamp}.md`;
|
|
14310
|
-
const filepath =
|
|
14459
|
+
const filepath = join6(reportDir, filename);
|
|
14311
14460
|
const lines = [];
|
|
14312
14461
|
lines.push(`# NaironAI Scan Report`);
|
|
14313
14462
|
lines.push("");
|
|
@@ -14423,7 +14572,7 @@ function generateReport(reportDir, score, git, agents, tests, since, sdlcAnalysi
|
|
|
14423
14572
|
lines.push("---");
|
|
14424
14573
|
lines.push("");
|
|
14425
14574
|
lines.push("*Generated by [nairon-bench](https://github.com/ObaidUr-Rahmaan/nairon-bench)*");
|
|
14426
|
-
|
|
14575
|
+
writeFileSync3(filepath, lines.join(`
|
|
14427
14576
|
`));
|
|
14428
14577
|
return filepath;
|
|
14429
14578
|
}
|
|
@@ -14622,9 +14771,9 @@ function renderBar(value, width) {
|
|
|
14622
14771
|
}
|
|
14623
14772
|
|
|
14624
14773
|
// src/commands/doctor.ts
|
|
14625
|
-
import { existsSync as
|
|
14626
|
-
import { homedir as
|
|
14627
|
-
import { join as
|
|
14774
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
14775
|
+
import { homedir as homedir5 } from "node:os";
|
|
14776
|
+
import { join as join7 } from "node:path";
|
|
14628
14777
|
init_client();
|
|
14629
14778
|
var doctorCommand = defineCommand2({
|
|
14630
14779
|
meta: {
|
|
@@ -14642,16 +14791,16 @@ var doctorCommand = defineCommand2({
|
|
|
14642
14791
|
results.push({ label: "OS", status: "info", value: `${process.platform} ${process.arch}` });
|
|
14643
14792
|
results.push({ label: "Bun", status: "info", value: typeof Bun !== "undefined" ? Bun.version : "N/A" });
|
|
14644
14793
|
results.push({ label: "Node", status: "info", value: process.version });
|
|
14645
|
-
results.push({ label: "Home", status: "info", value:
|
|
14646
|
-
const gitDir =
|
|
14647
|
-
if (
|
|
14794
|
+
results.push({ label: "Home", status: "info", value: homedir5() });
|
|
14795
|
+
const gitDir = join7(process.cwd(), ".git");
|
|
14796
|
+
if (existsSync7(gitDir)) {
|
|
14648
14797
|
results.push({ label: "Git", status: "success", value: "Repository detected" });
|
|
14649
14798
|
} else {
|
|
14650
14799
|
results.push({ label: "Git", status: "warn", value: "No repository in current directory" });
|
|
14651
14800
|
}
|
|
14652
14801
|
for (const [agent, pathTemplate] of Object.entries(AGENT_LOG_PATHS)) {
|
|
14653
|
-
const resolvedPath = pathTemplate.replace("~",
|
|
14654
|
-
if (
|
|
14802
|
+
const resolvedPath = pathTemplate.replace("~", homedir5());
|
|
14803
|
+
if (existsSync7(resolvedPath)) {
|
|
14655
14804
|
results.push({ label: agent, status: "success", value: `found at ${resolvedPath}` });
|
|
14656
14805
|
}
|
|
14657
14806
|
}
|
|
@@ -14706,9 +14855,9 @@ var doctorCommand = defineCommand2({
|
|
|
14706
14855
|
|
|
14707
14856
|
// src/commands/init.ts
|
|
14708
14857
|
init_dist();
|
|
14709
|
-
import { existsSync as
|
|
14710
|
-
import { homedir as
|
|
14711
|
-
import { join as
|
|
14858
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
14859
|
+
import { homedir as homedir6, platform as platform2, arch } from "node:os";
|
|
14860
|
+
import { join as join8 } from "node:path";
|
|
14712
14861
|
init_client();
|
|
14713
14862
|
|
|
14714
14863
|
// src/lib/github-auth.ts
|
|
@@ -14878,8 +15027,8 @@ var initCommand = defineCommand2({
|
|
|
14878
15027
|
consola.start("Step 2/6: Detecting AI agents...");
|
|
14879
15028
|
const detectedAgents = [];
|
|
14880
15029
|
for (const [agent, pathTemplate] of Object.entries(AGENT_LOG_PATHS)) {
|
|
14881
|
-
const resolvedPath = pathTemplate.replace("~",
|
|
14882
|
-
if (
|
|
15030
|
+
const resolvedPath = pathTemplate.replace("~", homedir6());
|
|
15031
|
+
if (existsSync8(resolvedPath)) {
|
|
14883
15032
|
detectedAgents.push(agent);
|
|
14884
15033
|
consola.success(` Found: ${agent}`);
|
|
14885
15034
|
}
|
|
@@ -14982,7 +15131,7 @@ var initCommand = defineCommand2({
|
|
|
14982
15131
|
{ name: "playwright", configs: ["playwright.config.ts", "playwright.config.js"] }
|
|
14983
15132
|
];
|
|
14984
15133
|
for (const fw of testFrameworks) {
|
|
14985
|
-
if (fw.configs.some((c3) =>
|
|
15134
|
+
if (fw.configs.some((c3) => existsSync8(join8(process.cwd(), c3)))) {
|
|
14986
15135
|
toolStack.testing.push(fw.name);
|
|
14987
15136
|
}
|
|
14988
15137
|
}
|
|
@@ -15051,13 +15200,13 @@ var initCommand = defineCommand2({
|
|
|
15051
15200
|
});
|
|
15052
15201
|
function detectPackageManager() {
|
|
15053
15202
|
const cwd = process.cwd();
|
|
15054
|
-
if (
|
|
15203
|
+
if (existsSync8(join8(cwd, "bun.lock")) || existsSync8(join8(cwd, "bun.lockb")))
|
|
15055
15204
|
return "bun";
|
|
15056
|
-
if (
|
|
15205
|
+
if (existsSync8(join8(cwd, "pnpm-lock.yaml")))
|
|
15057
15206
|
return "pnpm";
|
|
15058
|
-
if (
|
|
15207
|
+
if (existsSync8(join8(cwd, "yarn.lock")))
|
|
15059
15208
|
return "yarn";
|
|
15060
|
-
if (
|
|
15209
|
+
if (existsSync8(join8(cwd, "package-lock.json")))
|
|
15061
15210
|
return "npm";
|
|
15062
15211
|
return;
|
|
15063
15212
|
}
|
|
@@ -15072,15 +15221,15 @@ var CLAUDE_CODE_SKILLS = [
|
|
|
15072
15221
|
];
|
|
15073
15222
|
function detectEasyWins(detectedAgents) {
|
|
15074
15223
|
const wins = [];
|
|
15075
|
-
const home =
|
|
15224
|
+
const home = homedir6();
|
|
15076
15225
|
const hasClaudeCode = detectedAgents.some((a2) => a2.toLowerCase().includes("claude") || a2.toLowerCase().includes("opencode"));
|
|
15077
|
-
const claudeConfigDir =
|
|
15078
|
-
const hasClaudeConfig =
|
|
15226
|
+
const claudeConfigDir = join8(home, ".claude");
|
|
15227
|
+
const hasClaudeConfig = existsSync8(claudeConfigDir);
|
|
15079
15228
|
if (hasClaudeCode || hasClaudeConfig) {
|
|
15080
|
-
const skillsDir =
|
|
15229
|
+
const skillsDir = join8(home, ".claude", "skills");
|
|
15081
15230
|
for (const skill of CLAUDE_CODE_SKILLS) {
|
|
15082
|
-
const skillPath =
|
|
15083
|
-
const isInstalled =
|
|
15231
|
+
const skillPath = join8(skillsDir, skill.id);
|
|
15232
|
+
const isInstalled = existsSync8(skillPath);
|
|
15084
15233
|
if (!isInstalled) {
|
|
15085
15234
|
wins.push({
|
|
15086
15235
|
icon: skill.icon,
|
|
@@ -15851,9 +16000,9 @@ function renderBar2(value, width) {
|
|
|
15851
16000
|
// src/commands/publish.ts
|
|
15852
16001
|
init_dist();
|
|
15853
16002
|
init_client();
|
|
15854
|
-
import { existsSync as
|
|
15855
|
-
import { join as
|
|
15856
|
-
import { homedir as
|
|
16003
|
+
import { existsSync as existsSync9, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "node:fs";
|
|
16004
|
+
import { join as join9 } from "node:path";
|
|
16005
|
+
import { homedir as homedir7 } from "node:os";
|
|
15857
16006
|
var publishCommand = defineCommand2({
|
|
15858
16007
|
meta: {
|
|
15859
16008
|
name: "publish",
|
|
@@ -15877,7 +16026,7 @@ var publishCommand = defineCommand2({
|
|
|
15877
16026
|
},
|
|
15878
16027
|
async run({ args }) {
|
|
15879
16028
|
const projectDir = args.project ?? process.cwd();
|
|
15880
|
-
const reportDir =
|
|
16029
|
+
const reportDir = join9(projectDir, args["report-dir"]);
|
|
15881
16030
|
consola.start(`Analyzing your AI workflow...
|
|
15882
16031
|
`);
|
|
15883
16032
|
consola.info("Reading scan reports...");
|
|
@@ -15957,12 +16106,12 @@ var publishCommand = defineCommand2({
|
|
|
15957
16106
|
}
|
|
15958
16107
|
});
|
|
15959
16108
|
function readReports(reportDir) {
|
|
15960
|
-
if (!
|
|
16109
|
+
if (!existsSync9(reportDir)) {
|
|
15961
16110
|
return [];
|
|
15962
16111
|
}
|
|
15963
16112
|
const files = readdirSync3(reportDir).filter((f3) => f3.endsWith(".md") && f3.startsWith("scan-")).sort();
|
|
15964
16113
|
return files.map((file) => {
|
|
15965
|
-
const content =
|
|
16114
|
+
const content = readFileSync6(join9(reportDir, file), "utf-8");
|
|
15966
16115
|
return parseReport(content, file);
|
|
15967
16116
|
});
|
|
15968
16117
|
}
|
|
@@ -15991,13 +16140,13 @@ function parseReport(content, filename) {
|
|
|
15991
16140
|
return { date, overall, tier, tokenEfficiency, tokenWaste, phases, models };
|
|
15992
16141
|
}
|
|
15993
16142
|
function detectConcepts(projectDir) {
|
|
15994
|
-
const home =
|
|
16143
|
+
const home = homedir7();
|
|
15995
16144
|
return {
|
|
15996
|
-
skills:
|
|
15997
|
-
mcpServers:
|
|
15998
|
-
customCommands:
|
|
15999
|
-
agentConfig:
|
|
16000
|
-
contextManagement:
|
|
16145
|
+
skills: existsSync9(join9(home, ".claude", "settings.json")) && readFileSync6(join9(home, ".claude", "settings.json"), "utf-8").includes("skill"),
|
|
16146
|
+
mcpServers: existsSync9(join9(home, ".config", "opencode", "config.json")) || existsSync9(join9(home, ".claude", "claude_desktop_config.json")) || existsSync9(join9(home, ".claude", "settings.json")) && readFileSync6(join9(home, ".claude", "settings.json"), "utf-8").includes("mcpServers"),
|
|
16147
|
+
customCommands: existsSync9(join9(home, ".claude", "commands")) || existsSync9(join9(projectDir, ".claude", "commands")),
|
|
16148
|
+
agentConfig: existsSync9(join9(projectDir, "AGENTS.md")) || existsSync9(join9(projectDir, ".cursorrules")) || existsSync9(join9(projectDir, ".github", "copilot-instructions.md")),
|
|
16149
|
+
contextManagement: existsSync9(join9(projectDir, "MEMORY.md")) || existsSync9(join9(projectDir, ".context")) || existsSync9(join9(projectDir, "docs", "architecture.md")) || existsSync9(join9(projectDir, ".beads"))
|
|
16001
16150
|
};
|
|
16002
16151
|
}
|
|
16003
16152
|
function computeConceptsScore(concepts) {
|
|
@@ -16018,18 +16167,18 @@ function computeConceptsScore(concepts) {
|
|
|
16018
16167
|
}
|
|
16019
16168
|
function detectTools(projectDir) {
|
|
16020
16169
|
const tools = [];
|
|
16021
|
-
const home =
|
|
16170
|
+
const home = homedir7();
|
|
16022
16171
|
const toolChecks = [
|
|
16023
|
-
{ name: "Claude Code", check: () =>
|
|
16024
|
-
{ name: "Cursor", check: () =>
|
|
16025
|
-
{ name: "GitHub Copilot", check: () =>
|
|
16026
|
-
{ name: "OpenCode", check: () =>
|
|
16027
|
-
{ name: "Windsurf", check: () =>
|
|
16028
|
-
{ name: "Aider", check: () =>
|
|
16029
|
-
{ name: "Codeium", check: () =>
|
|
16030
|
-
{ name: "Beads", check: () =>
|
|
16031
|
-
{ name: "Convex", check: () =>
|
|
16032
|
-
{ name: "Supabase", check: () =>
|
|
16172
|
+
{ name: "Claude Code", check: () => existsSync9(join9(home, ".claude")) },
|
|
16173
|
+
{ name: "Cursor", check: () => existsSync9(join9(projectDir, ".cursorrules")) },
|
|
16174
|
+
{ name: "GitHub Copilot", check: () => existsSync9(join9(projectDir, ".github", "copilot-instructions.md")) },
|
|
16175
|
+
{ name: "OpenCode", check: () => existsSync9(join9(home, ".config", "opencode")) },
|
|
16176
|
+
{ name: "Windsurf", check: () => existsSync9(join9(home, ".windsurf")) },
|
|
16177
|
+
{ name: "Aider", check: () => existsSync9(join9(projectDir, ".aider.conf.yml")) },
|
|
16178
|
+
{ name: "Codeium", check: () => existsSync9(join9(home, ".codeium")) },
|
|
16179
|
+
{ name: "Beads", check: () => existsSync9(join9(projectDir, ".beads")) },
|
|
16180
|
+
{ name: "Convex", check: () => existsSync9(join9(projectDir, "convex")) },
|
|
16181
|
+
{ name: "Supabase", check: () => existsSync9(join9(projectDir, "supabase")) }
|
|
16033
16182
|
];
|
|
16034
16183
|
for (const { name, check } of toolChecks) {
|
|
16035
16184
|
try {
|
|
@@ -16439,9 +16588,9 @@ function formatTokens3(tokens) {
|
|
|
16439
16588
|
}
|
|
16440
16589
|
|
|
16441
16590
|
// src/commands/session.ts
|
|
16442
|
-
import { existsSync as
|
|
16443
|
-
import { homedir as
|
|
16444
|
-
import { join as
|
|
16591
|
+
import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync7, statSync as statSync2 } from "node:fs";
|
|
16592
|
+
import { homedir as homedir8 } from "node:os";
|
|
16593
|
+
import { join as join10, basename as basename2 } from "node:path";
|
|
16445
16594
|
var sessionCommand = defineCommand2({
|
|
16446
16595
|
meta: {
|
|
16447
16596
|
name: "session",
|
|
@@ -16600,23 +16749,23 @@ var sessionCommand = defineCommand2({
|
|
|
16600
16749
|
});
|
|
16601
16750
|
async function collectSessionList() {
|
|
16602
16751
|
const sessions = [];
|
|
16603
|
-
const claudeProjectsDir =
|
|
16604
|
-
if (!
|
|
16752
|
+
const claudeProjectsDir = join10(homedir8(), ".claude", "projects");
|
|
16753
|
+
if (!existsSync10(claudeProjectsDir)) {
|
|
16605
16754
|
return sessions;
|
|
16606
16755
|
}
|
|
16607
16756
|
const projectDirs = readdirSync4(claudeProjectsDir);
|
|
16608
16757
|
for (const projectDir of projectDirs) {
|
|
16609
|
-
const projectPath =
|
|
16610
|
-
const stat =
|
|
16758
|
+
const projectPath = join10(claudeProjectsDir, projectDir);
|
|
16759
|
+
const stat = statSync2(projectPath);
|
|
16611
16760
|
if (!stat.isDirectory())
|
|
16612
16761
|
continue;
|
|
16613
16762
|
const projectName = decodeProjectName(projectDir);
|
|
16614
16763
|
const files = readdirSync4(projectPath).filter((f3) => f3.endsWith(".jsonl"));
|
|
16615
16764
|
for (const file of files) {
|
|
16616
|
-
const filePath =
|
|
16765
|
+
const filePath = join10(projectPath, file);
|
|
16617
16766
|
const sessionId = basename2(file, ".jsonl").slice(0, 8);
|
|
16618
16767
|
try {
|
|
16619
|
-
const content =
|
|
16768
|
+
const content = readFileSync7(filePath, "utf-8");
|
|
16620
16769
|
const lines = content.trim().split(`
|
|
16621
16770
|
`).filter(Boolean);
|
|
16622
16771
|
if (lines.length === 0)
|
|
@@ -16658,7 +16807,7 @@ async function collectSessionList() {
|
|
|
16658
16807
|
if (cost === 0 && totalTokens > 0) {
|
|
16659
16808
|
cost = totalTokens / 1e6 * 3;
|
|
16660
16809
|
}
|
|
16661
|
-
const fileStat =
|
|
16810
|
+
const fileStat = statSync2(filePath);
|
|
16662
16811
|
const duration = Math.round((fileStat.mtimeMs - startedAt.getTime()) / 60000);
|
|
16663
16812
|
sessions.push({
|
|
16664
16813
|
id: sessionId,
|
|
@@ -16690,7 +16839,7 @@ async function parseSessionDetails(filePath) {
|
|
|
16690
16839
|
patterns: []
|
|
16691
16840
|
};
|
|
16692
16841
|
try {
|
|
16693
|
-
const content =
|
|
16842
|
+
const content = readFileSync7(filePath, "utf-8");
|
|
16694
16843
|
const lines = content.trim().split(`
|
|
16695
16844
|
`).filter(Boolean);
|
|
16696
16845
|
const filesSet = new Set;
|
|
@@ -16772,7 +16921,7 @@ function decodeProjectName(encoded) {
|
|
|
16772
16921
|
}
|
|
16773
16922
|
|
|
16774
16923
|
// src/commands/export.ts
|
|
16775
|
-
import { writeFileSync as
|
|
16924
|
+
import { writeFileSync as writeFileSync4 } from "node:fs";
|
|
16776
16925
|
var exportCommand = defineCommand2({
|
|
16777
16926
|
meta: {
|
|
16778
16927
|
name: "export",
|
|
@@ -16890,12 +17039,12 @@ var exportCommand = defineCommand2({
|
|
|
16890
17039
|
for (const fmt of formats) {
|
|
16891
17040
|
if (fmt === "json") {
|
|
16892
17041
|
const jsonPath = `${basePath}.json`;
|
|
16893
|
-
|
|
17042
|
+
writeFileSync4(jsonPath, JSON.stringify(exportData, null, 2));
|
|
16894
17043
|
console.log(` ${icons.success} Exported: ${colors2.primary(jsonPath)}`);
|
|
16895
17044
|
} else if (fmt === "csv") {
|
|
16896
17045
|
const csvPath = `${basePath}.csv`;
|
|
16897
17046
|
const csv = generateCSV(exportData);
|
|
16898
|
-
|
|
17047
|
+
writeFileSync4(csvPath, csv);
|
|
16899
17048
|
console.log(` ${icons.success} Exported: ${colors2.primary(csvPath)}`);
|
|
16900
17049
|
}
|
|
16901
17050
|
}
|
|
@@ -17424,6 +17573,65 @@ var badgesCommand = defineCommand2({
|
|
|
17424
17573
|
}
|
|
17425
17574
|
});
|
|
17426
17575
|
|
|
17576
|
+
// src/commands/cache.ts
|
|
17577
|
+
var cacheCommand = defineCommand2({
|
|
17578
|
+
meta: {
|
|
17579
|
+
name: "cache",
|
|
17580
|
+
description: "Manage the session cache for faster scans"
|
|
17581
|
+
},
|
|
17582
|
+
args: {
|
|
17583
|
+
clear: {
|
|
17584
|
+
type: "boolean",
|
|
17585
|
+
description: "Clear the session cache",
|
|
17586
|
+
default: false
|
|
17587
|
+
},
|
|
17588
|
+
info: {
|
|
17589
|
+
type: "boolean",
|
|
17590
|
+
description: "Show cache statistics",
|
|
17591
|
+
default: false
|
|
17592
|
+
}
|
|
17593
|
+
},
|
|
17594
|
+
async run({ args }) {
|
|
17595
|
+
console.log();
|
|
17596
|
+
console.log(colors2.bold(colors2.primary(" Session Cache")));
|
|
17597
|
+
console.log(colors2.dim(" " + "─".repeat(40)));
|
|
17598
|
+
console.log();
|
|
17599
|
+
if (args.clear) {
|
|
17600
|
+
const spinner = createSpinner("Clearing cache...");
|
|
17601
|
+
spinner.start();
|
|
17602
|
+
try {
|
|
17603
|
+
clearCache();
|
|
17604
|
+
spinner.succeed("Cache cleared");
|
|
17605
|
+
} catch (err) {
|
|
17606
|
+
spinner.fail("Failed to clear cache");
|
|
17607
|
+
console.log(` ${colors2.dim(err instanceof Error ? err.message : String(err))}`);
|
|
17608
|
+
}
|
|
17609
|
+
console.log();
|
|
17610
|
+
return;
|
|
17611
|
+
}
|
|
17612
|
+
const info2 = getCacheInfo();
|
|
17613
|
+
console.log(` ${icons.info} Cache Status`);
|
|
17614
|
+
console.log();
|
|
17615
|
+
console.log(` ${colors2.dim("Cached sessions:")} ${colors2.primary(info2.entries.toString())}`);
|
|
17616
|
+
console.log(` ${colors2.dim("Cache size:")} ${colors2.primary(formatBytes(info2.sizeBytes))}`);
|
|
17617
|
+
console.log();
|
|
17618
|
+
if (info2.entries > 0) {
|
|
17619
|
+
console.log(` ${colors2.dim("Run")} ${colors2.primary("nb cache --clear")} ${colors2.dim("to clear the cache")}`);
|
|
17620
|
+
} else {
|
|
17621
|
+
console.log(` ${colors2.dim("Cache is empty. Run")} ${colors2.primary("nb scan")} ${colors2.dim("to populate it.")}`);
|
|
17622
|
+
}
|
|
17623
|
+
console.log();
|
|
17624
|
+
}
|
|
17625
|
+
});
|
|
17626
|
+
function formatBytes(bytes) {
|
|
17627
|
+
if (bytes === 0)
|
|
17628
|
+
return "0 B";
|
|
17629
|
+
const k2 = 1024;
|
|
17630
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
17631
|
+
const i3 = Math.floor(Math.log(bytes) / Math.log(k2));
|
|
17632
|
+
return `${parseFloat((bytes / Math.pow(k2, i3)).toFixed(1))} ${sizes[i3]}`;
|
|
17633
|
+
}
|
|
17634
|
+
|
|
17427
17635
|
// src/index.ts
|
|
17428
17636
|
var VERSION = "0.0.13";
|
|
17429
17637
|
var CYAN = "\x1B[36m";
|
|
@@ -17454,7 +17662,7 @@ function showBanner() {
|
|
|
17454
17662
|
log("");
|
|
17455
17663
|
}
|
|
17456
17664
|
var args = process.argv.slice(2);
|
|
17457
|
-
var subcommands = ["init", "scan", "report", "dashboard", "insights", "tools", "doctor", "publish", "history", "cost", "session", "export", "pr", "badges"];
|
|
17665
|
+
var subcommands = ["init", "scan", "report", "dashboard", "insights", "tools", "doctor", "publish", "history", "cost", "session", "export", "pr", "badges", "cache"];
|
|
17458
17666
|
var hasSubcommand = args.some((arg) => subcommands.includes(arg));
|
|
17459
17667
|
var hasHelp = args.includes("--help") || args.includes("-h");
|
|
17460
17668
|
var hasVersion = args.includes("--version");
|
|
@@ -17481,7 +17689,8 @@ if (!hasSubcommand && !hasHelp && !hasVersion && args.length === 0) {
|
|
|
17481
17689
|
session: sessionCommand,
|
|
17482
17690
|
export: exportCommand,
|
|
17483
17691
|
pr: prCommand,
|
|
17484
|
-
badges: badgesCommand
|
|
17692
|
+
badges: badgesCommand,
|
|
17693
|
+
cache: cacheCommand
|
|
17485
17694
|
}
|
|
17486
17695
|
});
|
|
17487
17696
|
runMain(main);
|