@dosu/cli 0.53.0-beta.6 → 0.53.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/dosu.js +2078 -494
- package/package.json +3 -3
package/bin/dosu.js
CHANGED
|
@@ -2529,8 +2529,20 @@ function getSupabaseURL() {
|
|
|
2529
2529
|
function getSupabaseAnonKey() {
|
|
2530
2530
|
return process.env.SUPABASE_ANON_KEY_OVERRIDE ?? "sb_publishable_NePQHMbqqlFOp1aaHbp5Bg_zyA-kYd6" ?? "";
|
|
2531
2531
|
}
|
|
2532
|
+
function isAbsoluteHttpUrl(value) {
|
|
2533
|
+
try {
|
|
2534
|
+
const parsed = new URL(value);
|
|
2535
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
2536
|
+
} catch {
|
|
2537
|
+
return false;
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2532
2540
|
function getLlmGatewayURL() {
|
|
2533
|
-
|
|
2541
|
+
const override = process.env.DOSU_LLM_GATEWAY_URL_OVERRIDE;
|
|
2542
|
+
if (override)
|
|
2543
|
+
return override;
|
|
2544
|
+
const backend = getBackendURL().replace(/\/$/, "");
|
|
2545
|
+
return backend ? `${backend}/v1/llm-gateway` : "";
|
|
2534
2546
|
}
|
|
2535
2547
|
|
|
2536
2548
|
// src/client/client.ts
|
|
@@ -5006,7 +5018,7 @@ function isNpxInvocation(channel = INSTALL_CHANNEL, env = process.env) {
|
|
|
5006
5018
|
function getVersionString() {
|
|
5007
5019
|
return `v${VERSION}`;
|
|
5008
5020
|
}
|
|
5009
|
-
var VERSION = "0.53.0-beta.
|
|
5021
|
+
var VERSION = "0.53.0-beta.8", INSTALL_CHANNEL = "npm";
|
|
5010
5022
|
|
|
5011
5023
|
// src/debug/logger.ts
|
|
5012
5024
|
import {
|
|
@@ -5144,7 +5156,7 @@ var init_logger = __esm(() => {
|
|
|
5144
5156
|
});
|
|
5145
5157
|
|
|
5146
5158
|
// src/client/contract.ts
|
|
5147
|
-
var CLI_CONTRACT_HASH = "
|
|
5159
|
+
var CLI_CONTRACT_HASH = "d7c6f0f0e8d0";
|
|
5148
5160
|
|
|
5149
5161
|
// src/client/trpc.ts
|
|
5150
5162
|
var exports_trpc = {};
|
|
@@ -8346,161 +8358,16 @@ var init_agents = __esm(() => {
|
|
|
8346
8358
|
init_formats();
|
|
8347
8359
|
});
|
|
8348
8360
|
|
|
8349
|
-
// src/sessions/project-dir.ts
|
|
8350
|
-
import {
|
|
8351
|
-
closeSync as closeSync2,
|
|
8352
|
-
existsSync as existsSync9,
|
|
8353
|
-
mkdirSync as mkdirSync6,
|
|
8354
|
-
openSync as openSync2,
|
|
8355
|
-
readFileSync as readFileSync9,
|
|
8356
|
-
readSync,
|
|
8357
|
-
renameSync as renameSync2,
|
|
8358
|
-
statSync as statSync3,
|
|
8359
|
-
writeFileSync as writeFileSync6
|
|
8360
|
-
} from "node:fs";
|
|
8361
|
-
import { join as join8 } from "node:path";
|
|
8362
|
-
function cachePath(configDir) {
|
|
8363
|
-
return join8(configDir, CACHE_FILENAME2);
|
|
8364
|
-
}
|
|
8365
|
-
function loadCacheFile(configDir) {
|
|
8366
|
-
try {
|
|
8367
|
-
const raw = JSON.parse(readFileSync9(cachePath(configDir), "utf-8"));
|
|
8368
|
-
if (raw.schema_version !== CACHE_SCHEMA_VERSION || typeof raw.entries !== "object")
|
|
8369
|
-
return {};
|
|
8370
|
-
return raw.entries;
|
|
8371
|
-
} catch {
|
|
8372
|
-
return {};
|
|
8373
|
-
}
|
|
8374
|
-
}
|
|
8375
|
-
function saveCacheFile(configDir, entries) {
|
|
8376
|
-
try {
|
|
8377
|
-
if (!existsSync9(configDir))
|
|
8378
|
-
mkdirSync6(configDir, { recursive: true, mode: 448 });
|
|
8379
|
-
const path3 = cachePath(configDir);
|
|
8380
|
-
const tmp = `${path3}.${process.pid}.tmp`;
|
|
8381
|
-
const file = { schema_version: CACHE_SCHEMA_VERSION, entries };
|
|
8382
|
-
writeFileSync6(tmp, JSON.stringify(file), { mode: 384 });
|
|
8383
|
-
renameSync2(tmp, path3);
|
|
8384
|
-
} catch {}
|
|
8385
|
-
}
|
|
8386
|
-
function readHead(path3, bytes = HEAD_BYTES) {
|
|
8387
|
-
let fd;
|
|
8388
|
-
try {
|
|
8389
|
-
fd = openSync2(path3, "r");
|
|
8390
|
-
} catch {
|
|
8391
|
-
return null;
|
|
8392
|
-
}
|
|
8393
|
-
try {
|
|
8394
|
-
const buf = Buffer.alloc(bytes);
|
|
8395
|
-
const read = readSync(fd, buf, 0, bytes, 0);
|
|
8396
|
-
return buf.subarray(0, read).toString("utf-8");
|
|
8397
|
-
} catch {
|
|
8398
|
-
return null;
|
|
8399
|
-
} finally {
|
|
8400
|
-
closeSync2(fd);
|
|
8401
|
-
}
|
|
8402
|
-
}
|
|
8403
|
-
function cwdFromJsonlHead(text) {
|
|
8404
|
-
for (const line of text.split(`
|
|
8405
|
-
`).slice(0, HEAD_LINES)) {
|
|
8406
|
-
if (!line.includes('"cwd"'))
|
|
8407
|
-
continue;
|
|
8408
|
-
try {
|
|
8409
|
-
const obj = JSON.parse(line);
|
|
8410
|
-
const cwd = obj.cwd ?? obj.payload?.cwd;
|
|
8411
|
-
if (typeof cwd === "string" && cwd.startsWith("/"))
|
|
8412
|
-
return cwd;
|
|
8413
|
-
} catch {}
|
|
8414
|
-
}
|
|
8415
|
-
return null;
|
|
8416
|
-
}
|
|
8417
|
-
function unmungeSlug(slug, exists = existsSync9) {
|
|
8418
|
-
const tokens = slug.replace(/^-/, "").split("-");
|
|
8419
|
-
if (tokens.length === 0 || tokens[0] === "")
|
|
8420
|
-
return null;
|
|
8421
|
-
let budget = 5000;
|
|
8422
|
-
const walk = (prefix, index) => {
|
|
8423
|
-
if (budget-- <= 0)
|
|
8424
|
-
return null;
|
|
8425
|
-
if (index === tokens.length)
|
|
8426
|
-
return exists(prefix) ? prefix : null;
|
|
8427
|
-
const asSegment = prefix === "" || exists(prefix) ? walk(`${prefix}/${tokens[index]}`, index + 1) : null;
|
|
8428
|
-
if (asSegment)
|
|
8429
|
-
return asSegment;
|
|
8430
|
-
return prefix === "" ? null : walk(`${prefix}-${tokens[index]}`, index + 1);
|
|
8431
|
-
};
|
|
8432
|
-
return walk("", 0);
|
|
8433
|
-
}
|
|
8434
|
-
function fileMtime(path3) {
|
|
8435
|
-
try {
|
|
8436
|
-
return statSync3(path3).mtime.toISOString();
|
|
8437
|
-
} catch {
|
|
8438
|
-
return "";
|
|
8439
|
-
}
|
|
8440
|
-
}
|
|
8441
|
-
function createProjectDirResolver(configDir = getConfigDir(), deps = {}) {
|
|
8442
|
-
const exists = deps.exists ?? existsSync9;
|
|
8443
|
-
const head = deps.readHead ?? readHead;
|
|
8444
|
-
const mtime = deps.mtime ?? fileMtime;
|
|
8445
|
-
const entries = loadCacheFile(configDir);
|
|
8446
|
-
let dirty = false;
|
|
8447
|
-
const compute = (session) => {
|
|
8448
|
-
switch (session.harness) {
|
|
8449
|
-
case "opencode":
|
|
8450
|
-
return session.project ?? null;
|
|
8451
|
-
case "claude": {
|
|
8452
|
-
const text = head(session.path);
|
|
8453
|
-
const cwd = text ? cwdFromJsonlHead(text) : null;
|
|
8454
|
-
if (cwd)
|
|
8455
|
-
return cwd;
|
|
8456
|
-
return session.project ? unmungeSlug(session.project, exists) : null;
|
|
8457
|
-
}
|
|
8458
|
-
case "codex": {
|
|
8459
|
-
const text = head(session.path);
|
|
8460
|
-
return text ? cwdFromJsonlHead(text) : null;
|
|
8461
|
-
}
|
|
8462
|
-
case "cursor":
|
|
8463
|
-
return session.project ? unmungeSlug(session.project, exists) : null;
|
|
8464
|
-
default:
|
|
8465
|
-
return null;
|
|
8466
|
-
}
|
|
8467
|
-
};
|
|
8468
|
-
return {
|
|
8469
|
-
resolve(session) {
|
|
8470
|
-
const key = `${session.harness}/${session.id}`;
|
|
8471
|
-
const cached = entries[key];
|
|
8472
|
-
if (cached && (cached.dir !== null || cached.mtime === mtime(session.path))) {
|
|
8473
|
-
return cached.dir;
|
|
8474
|
-
}
|
|
8475
|
-
const dir = compute(session);
|
|
8476
|
-
entries[key] = { dir, mtime: mtime(session.path) };
|
|
8477
|
-
dirty = true;
|
|
8478
|
-
return dir;
|
|
8479
|
-
},
|
|
8480
|
-
flush() {
|
|
8481
|
-
if (!dirty)
|
|
8482
|
-
return;
|
|
8483
|
-
saveCacheFile(configDir, entries);
|
|
8484
|
-
dirty = false;
|
|
8485
|
-
}
|
|
8486
|
-
};
|
|
8487
|
-
}
|
|
8488
|
-
var CACHE_FILENAME2 = "project-dirs.json", CACHE_SCHEMA_VERSION = 1, HEAD_BYTES, HEAD_LINES = 50;
|
|
8489
|
-
var init_project_dir = __esm(() => {
|
|
8490
|
-
init_config();
|
|
8491
|
-
HEAD_BYTES = 128 * 1024;
|
|
8492
|
-
});
|
|
8493
|
-
|
|
8494
8361
|
// src/sessions/scan.ts
|
|
8495
|
-
import { existsSync as
|
|
8362
|
+
import { existsSync as existsSync9, readdirSync, statSync as statSync3 } from "node:fs";
|
|
8496
8363
|
import { createRequire as createRequire2 } from "node:module";
|
|
8497
8364
|
import { homedir as homedir3 } from "node:os";
|
|
8498
|
-
import { basename, join as
|
|
8365
|
+
import { basename, join as join8 } from "node:path";
|
|
8499
8366
|
function listDir(dir) {
|
|
8500
8367
|
try {
|
|
8501
8368
|
return readdirSync(dir, { withFileTypes: true }).map((entry) => ({
|
|
8502
8369
|
name: entry.name,
|
|
8503
|
-
path:
|
|
8370
|
+
path: join8(dir, entry.name),
|
|
8504
8371
|
isDir: entry.isDirectory()
|
|
8505
8372
|
}));
|
|
8506
8373
|
} catch {
|
|
@@ -8513,7 +8380,7 @@ function sessionFromFile(filePath, harness, project) {
|
|
|
8513
8380
|
return null;
|
|
8514
8381
|
let mtime;
|
|
8515
8382
|
try {
|
|
8516
|
-
mtime =
|
|
8383
|
+
mtime = statSync3(filePath).mtime;
|
|
8517
8384
|
} catch {
|
|
8518
8385
|
return null;
|
|
8519
8386
|
}
|
|
@@ -8527,7 +8394,7 @@ function sessionFromFile(filePath, harness, project) {
|
|
|
8527
8394
|
}
|
|
8528
8395
|
function scanClaude(home) {
|
|
8529
8396
|
const sessions = [];
|
|
8530
|
-
for (const project of listDir(
|
|
8397
|
+
for (const project of listDir(join8(home, ".claude", "projects"))) {
|
|
8531
8398
|
if (!project.isDir)
|
|
8532
8399
|
continue;
|
|
8533
8400
|
for (const entry of listDir(project.path)) {
|
|
@@ -8542,10 +8409,10 @@ function scanClaude(home) {
|
|
|
8542
8409
|
}
|
|
8543
8410
|
function scanCursor(home) {
|
|
8544
8411
|
const sessions = [];
|
|
8545
|
-
for (const project of listDir(
|
|
8412
|
+
for (const project of listDir(join8(home, ".cursor", "projects"))) {
|
|
8546
8413
|
if (!project.isDir)
|
|
8547
8414
|
continue;
|
|
8548
|
-
for (const entry of listDir(
|
|
8415
|
+
for (const entry of listDir(join8(project.path, "agent-transcripts"))) {
|
|
8549
8416
|
const files = entry.isDir ? listDir(entry.path).filter((f2) => !f2.isDir) : [entry];
|
|
8550
8417
|
for (const file of files) {
|
|
8551
8418
|
const session = sessionFromFile(file.path, "cursor", project.name);
|
|
@@ -8557,9 +8424,9 @@ function scanCursor(home) {
|
|
|
8557
8424
|
return sessions;
|
|
8558
8425
|
}
|
|
8559
8426
|
function scanCodex(home, env) {
|
|
8560
|
-
const codexHome2 = env.CODEX_HOME ??
|
|
8427
|
+
const codexHome2 = env.CODEX_HOME ?? join8(home, ".codex");
|
|
8561
8428
|
const sessions = [];
|
|
8562
|
-
for (const year of listDir(
|
|
8429
|
+
for (const year of listDir(join8(codexHome2, "sessions"))) {
|
|
8563
8430
|
if (!year.isDir)
|
|
8564
8431
|
continue;
|
|
8565
8432
|
for (const month of listDir(year.path)) {
|
|
@@ -8615,9 +8482,9 @@ function querySqlite(dbPath, sql) {
|
|
|
8615
8482
|
}
|
|
8616
8483
|
}
|
|
8617
8484
|
function scanOpencode(home, env) {
|
|
8618
|
-
const dataDir = env.XDG_DATA_HOME ??
|
|
8619
|
-
const dbPath =
|
|
8620
|
-
if (!
|
|
8485
|
+
const dataDir = env.XDG_DATA_HOME ?? join8(home, ".local", "share");
|
|
8486
|
+
const dbPath = join8(dataDir, "opencode", "opencode.db");
|
|
8487
|
+
if (!existsSync9(dbPath))
|
|
8621
8488
|
return [];
|
|
8622
8489
|
const rows = querySqlite(dbPath, "SELECT id, directory, time_updated FROM session WHERE parent_id IS NULL");
|
|
8623
8490
|
if (!rows)
|
|
@@ -8655,10 +8522,10 @@ function scanAgentSessions(options = {}) {
|
|
|
8655
8522
|
var init_scan = () => {};
|
|
8656
8523
|
|
|
8657
8524
|
// src/sync/watermark.ts
|
|
8658
|
-
import { existsSync as
|
|
8659
|
-
import { join as
|
|
8525
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync9, renameSync as renameSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
8526
|
+
import { join as join9 } from "node:path";
|
|
8660
8527
|
function syncStatePath(configDir = getConfigDir()) {
|
|
8661
|
-
return
|
|
8528
|
+
return join9(configDir, STATE_FILENAME);
|
|
8662
8529
|
}
|
|
8663
8530
|
function loadSyncState(configDir = getConfigDir()) {
|
|
8664
8531
|
const empty = {
|
|
@@ -8667,10 +8534,10 @@ function loadSyncState(configDir = getConfigDir()) {
|
|
|
8667
8534
|
consecutive_failures: 0
|
|
8668
8535
|
};
|
|
8669
8536
|
const path3 = syncStatePath(configDir);
|
|
8670
|
-
if (!
|
|
8537
|
+
if (!existsSync10(path3))
|
|
8671
8538
|
return empty;
|
|
8672
8539
|
try {
|
|
8673
|
-
const raw = JSON.parse(
|
|
8540
|
+
const raw = JSON.parse(readFileSync9(path3, "utf-8"));
|
|
8674
8541
|
if (raw.schema_version !== STATE_SCHEMA_VERSION)
|
|
8675
8542
|
return empty;
|
|
8676
8543
|
const minedSessions = Array.isArray(raw.mined_sessions) ? raw.mined_sessions.filter((record) => typeof record === "object" && record !== null && typeof record.at === "string" && typeof record.session === "string").map((record) => ({
|
|
@@ -8711,12 +8578,12 @@ function setSyncPaused(paused, configDir = getConfigDir()) {
|
|
|
8711
8578
|
saveSyncState(state, configDir);
|
|
8712
8579
|
}
|
|
8713
8580
|
function saveSyncState(state, configDir = getConfigDir()) {
|
|
8714
|
-
if (!
|
|
8715
|
-
|
|
8581
|
+
if (!existsSync10(configDir))
|
|
8582
|
+
mkdirSync6(configDir, { recursive: true, mode: 448 });
|
|
8716
8583
|
const path3 = syncStatePath(configDir);
|
|
8717
8584
|
const tmp = `${path3}.${process.pid}.tmp`;
|
|
8718
|
-
|
|
8719
|
-
|
|
8585
|
+
writeFileSync6(tmp, JSON.stringify(state, null, 2), { mode: 384 });
|
|
8586
|
+
renameSync2(tmp, path3);
|
|
8720
8587
|
}
|
|
8721
8588
|
function backoffUntil(state) {
|
|
8722
8589
|
if (state.consecutive_failures === 0 || !state.last_attempt_at)
|
|
@@ -8768,6 +8635,1668 @@ var init_watermark = __esm(() => {
|
|
|
8768
8635
|
BACKOFF_MAX_MS = 24 * 60 * 60 * 1000;
|
|
8769
8636
|
});
|
|
8770
8637
|
|
|
8638
|
+
// src/report/fetch.ts
|
|
8639
|
+
async function fetchReportNotes(cfg) {
|
|
8640
|
+
const orgId = cfg.active_account?.target?.org_id;
|
|
8641
|
+
if (!orgId || !cfg.active_account?.session.access_token) {
|
|
8642
|
+
throw new Error("Not signed in. Run `dosu setup` to connect an account first.");
|
|
8643
|
+
}
|
|
8644
|
+
const { createTypedClient: createTypedClient2 } = await Promise.resolve().then(() => (init_trpc(), exports_trpc));
|
|
8645
|
+
const client = createTypedClient2(cfg);
|
|
8646
|
+
const { notes } = await client.notes.listMine.query({
|
|
8647
|
+
org_id: orgId,
|
|
8648
|
+
limit: REPORT_NOTES_LIMIT
|
|
8649
|
+
});
|
|
8650
|
+
return notes.map((note) => ({
|
|
8651
|
+
title: note.title,
|
|
8652
|
+
content: note.body,
|
|
8653
|
+
at: note.created_at,
|
|
8654
|
+
...note.transcript_id ? { transcript_id: note.transcript_id } : {},
|
|
8655
|
+
...note.repo ? { repo: note.repo } : {},
|
|
8656
|
+
...note.branch ? { branch: note.branch } : {}
|
|
8657
|
+
}));
|
|
8658
|
+
}
|
|
8659
|
+
var REPORT_NOTES_LIMIT = 500;
|
|
8660
|
+
|
|
8661
|
+
// src/report/css.ts
|
|
8662
|
+
var REPORT_CSS = `
|
|
8663
|
+
:root {
|
|
8664
|
+
--ink: #14201c;
|
|
8665
|
+
--muted: #5c6b64;
|
|
8666
|
+
--line: #d5ddd8;
|
|
8667
|
+
--bg: #f4f7f5;
|
|
8668
|
+
--card: #ffffff;
|
|
8669
|
+
--accent: #0b6e4f;
|
|
8670
|
+
--accent-soft: #e3f2eb;
|
|
8671
|
+
--warn: #8a5a00;
|
|
8672
|
+
--warn-bg: #fff6e5;
|
|
8673
|
+
}
|
|
8674
|
+
* { box-sizing: border-box; }
|
|
8675
|
+
body {
|
|
8676
|
+
margin: 0;
|
|
8677
|
+
font-family: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
|
|
8678
|
+
color: var(--ink);
|
|
8679
|
+
background: var(--bg);
|
|
8680
|
+
line-height: 1.45;
|
|
8681
|
+
}
|
|
8682
|
+
.wrap { max-width: 880px; margin: 0 auto; padding: 2.5rem 1.5rem 4rem; }
|
|
8683
|
+
header.hero {
|
|
8684
|
+
border-bottom: 2px solid var(--ink);
|
|
8685
|
+
padding-bottom: 1.25rem;
|
|
8686
|
+
margin-bottom: 1.75rem;
|
|
8687
|
+
}
|
|
8688
|
+
.eyebrow {
|
|
8689
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
8690
|
+
text-transform: uppercase;
|
|
8691
|
+
letter-spacing: 0.08em;
|
|
8692
|
+
font-size: 0.72rem;
|
|
8693
|
+
color: var(--muted);
|
|
8694
|
+
margin: 0 0 0.4rem;
|
|
8695
|
+
}
|
|
8696
|
+
h1 { font-size: 2rem; font-weight: 600; margin: 0 0 0.5rem; letter-spacing: -0.02em; }
|
|
8697
|
+
h2 { font-size: 1.25rem; margin: 2rem 0 0.75rem; border-top: 1px solid var(--line); padding-top: 1.25rem; }
|
|
8698
|
+
h3 { margin: 0; font-size: 1.05rem; }
|
|
8699
|
+
.lede { color: var(--muted); margin: 0 0 1rem; }
|
|
8700
|
+
.meta-line { font-family: ui-sans-serif, system-ui, sans-serif; font-size: 0.85rem; color: var(--muted); }
|
|
8701
|
+
.toolbar { display: flex; flex-wrap: wrap; gap: 0.6rem; margin: 1rem 0 0; }
|
|
8702
|
+
button, .btn {
|
|
8703
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
8704
|
+
border: 1px solid var(--ink);
|
|
8705
|
+
background: var(--ink);
|
|
8706
|
+
color: #fff;
|
|
8707
|
+
padding: 0.55rem 0.9rem;
|
|
8708
|
+
border-radius: 4px;
|
|
8709
|
+
cursor: pointer;
|
|
8710
|
+
font-size: 0.9rem;
|
|
8711
|
+
text-decoration: none;
|
|
8712
|
+
display: inline-block;
|
|
8713
|
+
}
|
|
8714
|
+
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 0.75rem; margin: 1rem 0; }
|
|
8715
|
+
.stat { background: var(--card); border: 1px solid var(--line); padding: 0.85rem 1rem; border-radius: 6px; }
|
|
8716
|
+
.stat.highlight { background: var(--accent-soft); border-color: #b7d8c7; }
|
|
8717
|
+
.stat .label {
|
|
8718
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
8719
|
+
font-size: 0.72rem;
|
|
8720
|
+
text-transform: uppercase;
|
|
8721
|
+
letter-spacing: 0.06em;
|
|
8722
|
+
color: var(--muted);
|
|
8723
|
+
}
|
|
8724
|
+
.stat .value { font-size: 1.35rem; font-weight: 600; margin-top: 0.25rem; }
|
|
8725
|
+
.pct { font-size: 0.9rem; font-weight: 500; color: var(--accent); }
|
|
8726
|
+
table { width: 100%; border-collapse: collapse; font-family: ui-sans-serif, system-ui, sans-serif; font-size: 0.82rem; background: var(--card); }
|
|
8727
|
+
th, td { border-bottom: 1px solid var(--line); padding: 0.45rem 0.5rem; text-align: left; vertical-align: top; }
|
|
8728
|
+
th { color: var(--muted); font-weight: 600; }
|
|
8729
|
+
td.num { text-align: right; font-variant-numeric: tabular-nums; }
|
|
8730
|
+
.card { background: var(--card); border: 1px solid var(--line); border-radius: 8px; padding: 1rem 1.1rem; margin: 0.75rem 0; }
|
|
8731
|
+
.card header { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: baseline; }
|
|
8732
|
+
.badge {
|
|
8733
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
8734
|
+
font-size: 0.68rem;
|
|
8735
|
+
text-transform: uppercase;
|
|
8736
|
+
letter-spacing: 0.05em;
|
|
8737
|
+
border: 1px solid var(--line);
|
|
8738
|
+
padding: 0.1rem 0.4rem;
|
|
8739
|
+
border-radius: 3px;
|
|
8740
|
+
color: var(--muted);
|
|
8741
|
+
}
|
|
8742
|
+
.status-written { background: var(--accent-soft); color: var(--accent); border-color: #b7d8c7; }
|
|
8743
|
+
.status-pending { background: var(--warn-bg); color: var(--warn); border-color: #efd59a; }
|
|
8744
|
+
.status-proposed { background: #eef1f0; }
|
|
8745
|
+
.status-already_in_library { background: #eef1f0; }
|
|
8746
|
+
.note-body {
|
|
8747
|
+
white-space: pre-wrap;
|
|
8748
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
8749
|
+
font-size: 0.86rem;
|
|
8750
|
+
background: var(--bg);
|
|
8751
|
+
border-radius: 4px;
|
|
8752
|
+
padding: 0.75rem;
|
|
8753
|
+
overflow-x: auto;
|
|
8754
|
+
}
|
|
8755
|
+
.idea { font-size: 1.05rem; color: var(--ink); margin: 0.5rem 0 0.4rem; }
|
|
8756
|
+
.work { font-family: ui-sans-serif, system-ui, sans-serif; font-size: 0.85rem; color: var(--muted); margin: 0.25rem 0 0.5rem; }
|
|
8757
|
+
.work-label {
|
|
8758
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
8759
|
+
font-size: 0.68rem;
|
|
8760
|
+
text-transform: uppercase;
|
|
8761
|
+
letter-spacing: 0.06em;
|
|
8762
|
+
color: var(--muted);
|
|
8763
|
+
margin-right: 0.4rem;
|
|
8764
|
+
}
|
|
8765
|
+
.note-tech { margin-top: 0.6rem; }
|
|
8766
|
+
.note-tech summary { cursor: pointer; font-family: ui-sans-serif, system-ui, sans-serif; font-size: 0.82rem; color: var(--muted); margin-top: 0.5rem; }
|
|
8767
|
+
.trace { margin-top: 0.65rem; }
|
|
8768
|
+
.trace > summary { cursor: pointer; font-family: ui-sans-serif, system-ui, sans-serif; font-size: 0.82rem; color: var(--muted); }
|
|
8769
|
+
.trace-bar { display: flex; height: 8px; border-radius: 999px; overflow: hidden; background: #e8eee9; margin: 0.5rem 0 0.35rem; }
|
|
8770
|
+
.tb { display: block; height: 100%; }
|
|
8771
|
+
.tb.context { background: var(--accent); }
|
|
8772
|
+
.tb.planning { background: #8a97a3; }
|
|
8773
|
+
.tb.other { background: #b5a89a; }
|
|
8774
|
+
.trace-legend { font-family: ui-sans-serif, system-ui, sans-serif; font-size: 0.75rem; color: var(--muted); margin: 0 0 0.45rem; }
|
|
8775
|
+
.trace-steps { list-style: none; padding: 0; margin: 0.35rem 0 0; }
|
|
8776
|
+
.trace-steps li {
|
|
8777
|
+
display: grid;
|
|
8778
|
+
grid-template-columns: auto 1fr auto;
|
|
8779
|
+
column-gap: 0.4rem;
|
|
8780
|
+
align-items: baseline;
|
|
8781
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
8782
|
+
font-size: 0.78rem;
|
|
8783
|
+
padding: 0.16rem 0;
|
|
8784
|
+
border-bottom: 1px solid var(--line);
|
|
8785
|
+
}
|
|
8786
|
+
.trace-steps .chip { font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.04em; padding: 0.08rem 0.38rem; margin: 0; }
|
|
8787
|
+
.trace-steps .pv { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
8788
|
+
.trace-steps .tok { font-variant-numeric: tabular-nums; color: var(--muted); text-align: right; }
|
|
8789
|
+
.ts-head { color: var(--muted); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em; border-bottom: 1px solid var(--line); }
|
|
8790
|
+
.ts-head .chip { background: none; border: 0; padding-left: 0; color: inherit; }
|
|
8791
|
+
.ts-head .pv, .ts-head .tok { color: inherit; overflow: visible; text-overflow: unset; }
|
|
8792
|
+
.ts-user .chip { background: #eef1f0; }
|
|
8793
|
+
.ts-context .chip { background: var(--accent-soft); color: var(--accent); border-color: #b7d8c7; }
|
|
8794
|
+
.ts-planning .chip { background: #eef1f4; }
|
|
8795
|
+
.ts-other .chip { background: #f3efe9; }
|
|
8796
|
+
.ts-code { opacity: 0.55; color: var(--muted); }
|
|
8797
|
+
.ts-omit .pv { font-style: italic; }
|
|
8798
|
+
.meta, .query, .muted, .footnote {
|
|
8799
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
8800
|
+
font-size: 0.85rem;
|
|
8801
|
+
color: var(--muted);
|
|
8802
|
+
}
|
|
8803
|
+
code { font-size: 0.84em; }
|
|
8804
|
+
footer.cta { margin-top: 2.5rem; padding: 1.25rem; background: var(--accent-soft); border: 1px solid #b7d8c7; border-radius: 8px; }
|
|
8805
|
+
footer.cta h2 { border: 0; padding: 0; margin: 0 0 0.5rem; }
|
|
8806
|
+
@media print {
|
|
8807
|
+
body { background: #fff; }
|
|
8808
|
+
.toolbar, .no-print { display: none !important; }
|
|
8809
|
+
.wrap { max-width: none; padding: 0; }
|
|
8810
|
+
.card, .stat { break-inside: avoid; }
|
|
8811
|
+
a { color: inherit; text-decoration: none; }
|
|
8812
|
+
details.trace > *:not(summary) { display: none !important; }
|
|
8813
|
+
}
|
|
8814
|
+
`;
|
|
8815
|
+
|
|
8816
|
+
// src/report/html.ts
|
|
8817
|
+
function esc(value) {
|
|
8818
|
+
return String(value ?? "").replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
8819
|
+
}
|
|
8820
|
+
function fmtInt(n2) {
|
|
8821
|
+
const v = Number(n2);
|
|
8822
|
+
if (!Number.isFinite(v))
|
|
8823
|
+
return "—";
|
|
8824
|
+
return Math.trunc(v).toLocaleString("en-US");
|
|
8825
|
+
}
|
|
8826
|
+
function applyStatusDefaults(candidates, dryRun) {
|
|
8827
|
+
const fallback = dryRun ? "proposed" : "written";
|
|
8828
|
+
const known = new Set(["written", "pending", "proposed", "already_in_library"]);
|
|
8829
|
+
return candidates.map((c2) => {
|
|
8830
|
+
const current = (c2.status ?? "").trim().toLowerCase();
|
|
8831
|
+
return { ...c2, status: known.has(current) ? current : fallback };
|
|
8832
|
+
});
|
|
8833
|
+
}
|
|
8834
|
+
function tokenTotalsFromCandidates(candidates, inventory) {
|
|
8835
|
+
if (candidates.length === 0)
|
|
8836
|
+
return null;
|
|
8837
|
+
let replaced = 0;
|
|
8838
|
+
let hasRediscovery = false;
|
|
8839
|
+
let readCost = 0;
|
|
8840
|
+
for (const c2 of candidates) {
|
|
8841
|
+
const raw = c2.approx_rediscovery_tokens;
|
|
8842
|
+
if (raw != null) {
|
|
8843
|
+
hasRediscovery = true;
|
|
8844
|
+
replaced += Math.max(0, Number(raw) || 0);
|
|
8845
|
+
}
|
|
8846
|
+
const blob = `${c2.title ?? ""}
|
|
8847
|
+
${c2.content ?? ""}`;
|
|
8848
|
+
readCost += Math.round(blob.length / CHARS_PER_TOKEN);
|
|
8849
|
+
}
|
|
8850
|
+
if (!hasRediscovery)
|
|
8851
|
+
return null;
|
|
8852
|
+
let baseline = inventory.totals?.learning_tokens ?? 0;
|
|
8853
|
+
if (!baseline) {
|
|
8854
|
+
baseline = inventory.transcripts.reduce((sum, t2) => sum + (t2.learning_tokens || 0), 0);
|
|
8855
|
+
}
|
|
8856
|
+
const saved = Math.max(0, replaced - readCost);
|
|
8857
|
+
const pct = baseline ? Math.round(1000 * saved / baseline) / 10 : 0;
|
|
8858
|
+
return {
|
|
8859
|
+
baseline_tokens: baseline,
|
|
8860
|
+
replaced_baseline_tokens: replaced,
|
|
8861
|
+
read_knowledge_tokens: readCost,
|
|
8862
|
+
tokens_saved: saved,
|
|
8863
|
+
pct_saved: pct
|
|
8864
|
+
};
|
|
8865
|
+
}
|
|
8866
|
+
function presentationCopy(c2) {
|
|
8867
|
+
const idea = (c2.plain_english || "").trim() || (c2.content || "").trim();
|
|
8868
|
+
const how = (c2.how_found || "").trim();
|
|
8869
|
+
if (how)
|
|
8870
|
+
return [idea, how];
|
|
8871
|
+
const raw = c2.approx_rediscovery_tokens ?? c2.baseline_tokens;
|
|
8872
|
+
const n2 = raw == null ? 0 : Number(raw) || 0;
|
|
8873
|
+
const work = n2 ? `About ${n2.toLocaleString("en-US")} tokens of reading, searching, and tracing in this session before the conclusion.` : "Investigation stretch was not measured.";
|
|
8874
|
+
return [idea, work];
|
|
8875
|
+
}
|
|
8876
|
+
function parseLineSpec(spec) {
|
|
8877
|
+
const out = new Set;
|
|
8878
|
+
if (spec == null)
|
|
8879
|
+
return out;
|
|
8880
|
+
const raw = Array.isArray(spec) ? spec.map(String).join(",") : String(spec);
|
|
8881
|
+
if (!raw.trim())
|
|
8882
|
+
return out;
|
|
8883
|
+
for (const part of raw.split(",")) {
|
|
8884
|
+
const trimmed = part.trim();
|
|
8885
|
+
if (!trimmed)
|
|
8886
|
+
continue;
|
|
8887
|
+
if (trimmed.includes("-")) {
|
|
8888
|
+
const [a3, b2] = trimmed.split("-", 2);
|
|
8889
|
+
let start = Number.parseInt(a3.trim(), 10);
|
|
8890
|
+
let end = Number.parseInt(b2.trim(), 10);
|
|
8891
|
+
if (!Number.isFinite(start) || !Number.isFinite(end))
|
|
8892
|
+
continue;
|
|
8893
|
+
if (end < start)
|
|
8894
|
+
[start, end] = [end, start];
|
|
8895
|
+
for (let i2 = start;i2 <= end; i2++)
|
|
8896
|
+
out.add(i2);
|
|
8897
|
+
} else {
|
|
8898
|
+
const n2 = Number.parseInt(trimmed, 10);
|
|
8899
|
+
if (Number.isFinite(n2))
|
|
8900
|
+
out.add(n2);
|
|
8901
|
+
}
|
|
8902
|
+
}
|
|
8903
|
+
return out;
|
|
8904
|
+
}
|
|
8905
|
+
function expandDigestTurns(turns, spec) {
|
|
8906
|
+
if (turns.length === 0 || spec.size === 0)
|
|
8907
|
+
return [];
|
|
8908
|
+
const minLine = Math.min(...spec);
|
|
8909
|
+
const maxLine = Math.max(...spec);
|
|
8910
|
+
let start = minLine;
|
|
8911
|
+
for (const turn of turns) {
|
|
8912
|
+
const line = Number(turn.line) || 0;
|
|
8913
|
+
if (turn.role === "user" && line <= minLine)
|
|
8914
|
+
start = line;
|
|
8915
|
+
}
|
|
8916
|
+
return turns.filter((turn) => {
|
|
8917
|
+
const line = Number(turn.line) || 0;
|
|
8918
|
+
return start <= line && line <= maxLine;
|
|
8919
|
+
});
|
|
8920
|
+
}
|
|
8921
|
+
function collapsePreview(text, limit = PREVIEW_CHARS) {
|
|
8922
|
+
const cleaned = (text || "").replace(TAG_RE, " ").replace(WS_RE, " ").trim();
|
|
8923
|
+
if (cleaned.length > limit)
|
|
8924
|
+
return `${cleaned.slice(0, limit - 1).trimEnd()}…`;
|
|
8925
|
+
return cleaned;
|
|
8926
|
+
}
|
|
8927
|
+
function turnText(turn) {
|
|
8928
|
+
const texts = turn.text ?? [];
|
|
8929
|
+
if (typeof texts === "string")
|
|
8930
|
+
return texts;
|
|
8931
|
+
return texts.filter(Boolean).join(" ");
|
|
8932
|
+
}
|
|
8933
|
+
function innerToolName(tool) {
|
|
8934
|
+
const name = String(tool.name ?? "");
|
|
8935
|
+
for (const key of ["toolName", "tool_name"]) {
|
|
8936
|
+
const val = tool[key];
|
|
8937
|
+
if (val)
|
|
8938
|
+
return String(val);
|
|
8939
|
+
}
|
|
8940
|
+
if (tool.knowledge?.tool)
|
|
8941
|
+
return String(tool.knowledge.tool);
|
|
8942
|
+
if (name.startsWith("mcp:"))
|
|
8943
|
+
return name.split(":", 2)[1] ?? name;
|
|
8944
|
+
if (name.startsWith("mcp__"))
|
|
8945
|
+
return name.split("__").at(-1) ?? name;
|
|
8946
|
+
return name;
|
|
8947
|
+
}
|
|
8948
|
+
function displayToolName(tool) {
|
|
8949
|
+
const inner = innerToolName(tool);
|
|
8950
|
+
const wrapper = String(tool.name ?? "");
|
|
8951
|
+
if (inner === "GetMcpTools" || wrapper === "GetMcpTools")
|
|
8952
|
+
return "MCP schema";
|
|
8953
|
+
if (SQL_DISPLAY.has(inner))
|
|
8954
|
+
return "SQL";
|
|
8955
|
+
if (LOGS_DISPLAY.has(inner))
|
|
8956
|
+
return "Logs";
|
|
8957
|
+
return inner || wrapper || "tool";
|
|
8958
|
+
}
|
|
8959
|
+
function firstArgPreview(args, keys) {
|
|
8960
|
+
if (typeof args !== "object" || args === null)
|
|
8961
|
+
return "";
|
|
8962
|
+
const row = args;
|
|
8963
|
+
for (const key of keys) {
|
|
8964
|
+
if (row[key]) {
|
|
8965
|
+
const pv = collapsePreview(String(row[key]));
|
|
8966
|
+
if (pv)
|
|
8967
|
+
return pv;
|
|
8968
|
+
}
|
|
8969
|
+
}
|
|
8970
|
+
return "";
|
|
8971
|
+
}
|
|
8972
|
+
function toolPreview(tool, text) {
|
|
8973
|
+
const name = String(tool.name ?? "");
|
|
8974
|
+
const inner = innerToolName(tool);
|
|
8975
|
+
if (tool.path) {
|
|
8976
|
+
const raw = String(tool.path);
|
|
8977
|
+
const leaf = raw.includes("/") ? raw.split("/").at(-1) : raw;
|
|
8978
|
+
const pv = collapsePreview(leaf ?? "");
|
|
8979
|
+
if (pv)
|
|
8980
|
+
return pv;
|
|
8981
|
+
}
|
|
8982
|
+
if (tool.pattern) {
|
|
8983
|
+
const pv = collapsePreview(String(tool.pattern));
|
|
8984
|
+
if (pv)
|
|
8985
|
+
return pv;
|
|
8986
|
+
}
|
|
8987
|
+
if (tool.command_preview) {
|
|
8988
|
+
const pv = collapsePreview(String(tool.command_preview));
|
|
8989
|
+
if (pv)
|
|
8990
|
+
return pv;
|
|
8991
|
+
}
|
|
8992
|
+
if (tool.query) {
|
|
8993
|
+
const pv = collapsePreview(String(tool.query));
|
|
8994
|
+
if (pv)
|
|
8995
|
+
return pv;
|
|
8996
|
+
}
|
|
8997
|
+
if (tool.file_path) {
|
|
8998
|
+
const raw = String(tool.file_path);
|
|
8999
|
+
const leaf = raw.includes("/") ? raw.split("/").at(-1) : raw;
|
|
9000
|
+
const pv = collapsePreview(leaf ?? "");
|
|
9001
|
+
if (pv)
|
|
9002
|
+
return pv;
|
|
9003
|
+
}
|
|
9004
|
+
if (tool.prompt) {
|
|
9005
|
+
const pv = collapsePreview(String(tool.prompt));
|
|
9006
|
+
if (pv)
|
|
9007
|
+
return pv;
|
|
9008
|
+
}
|
|
9009
|
+
if (tool.knowledge) {
|
|
9010
|
+
const pv = firstArgPreview(tool.knowledge.arguments, ["query", "sql", "command"]);
|
|
9011
|
+
if (pv)
|
|
9012
|
+
return pv;
|
|
9013
|
+
}
|
|
9014
|
+
const fromArgs = firstArgPreview(tool.arguments, ["query", "sql", "command", "description"]);
|
|
9015
|
+
if (fromArgs)
|
|
9016
|
+
return fromArgs;
|
|
9017
|
+
const toolName = tool.toolName || tool.tool_name;
|
|
9018
|
+
if (toolName) {
|
|
9019
|
+
const combo = tool.server ? `${toolName} · ${tool.server}` : String(toolName);
|
|
9020
|
+
const pv = collapsePreview(combo);
|
|
9021
|
+
if (pv)
|
|
9022
|
+
return pv;
|
|
9023
|
+
}
|
|
9024
|
+
const fromText = collapsePreview(text);
|
|
9025
|
+
if (fromText)
|
|
9026
|
+
return fromText;
|
|
9027
|
+
if (name === "GetMcpTools" || inner === "GetMcpTools")
|
|
9028
|
+
return "Look up available MCP tools";
|
|
9029
|
+
if (name === "CallMcpTool") {
|
|
9030
|
+
const extra = toolName || (inner !== "CallMcpTool" ? inner : "");
|
|
9031
|
+
return extra ? `MCP call · ${extra}` : "MCP call";
|
|
9032
|
+
}
|
|
9033
|
+
return "No input recorded";
|
|
9034
|
+
}
|
|
9035
|
+
function classifyDigestTool(tool) {
|
|
9036
|
+
const name = String(tool.name ?? "");
|
|
9037
|
+
const inner = innerToolName(tool);
|
|
9038
|
+
const canonical = inner || name;
|
|
9039
|
+
if (["Write", "Edit", "MultiEdit", "StrReplace", "Delete"].includes(canonical))
|
|
9040
|
+
return "code";
|
|
9041
|
+
if (["TodoWrite", "update_plan", "SwitchMode"].includes(canonical))
|
|
9042
|
+
return "planning";
|
|
9043
|
+
if (["write_knowledge", "finalize_session_knowledge"].includes(canonical))
|
|
9044
|
+
return "other";
|
|
9045
|
+
return "context";
|
|
9046
|
+
}
|
|
9047
|
+
function usableTools(turn) {
|
|
9048
|
+
return (turn.tools ?? []).filter((tool) => !SKIP_TOOL_NAMES.has(String(tool.name ?? "")));
|
|
9049
|
+
}
|
|
9050
|
+
function buildTraceSteps(turns) {
|
|
9051
|
+
const steps = [];
|
|
9052
|
+
for (const turn of turns) {
|
|
9053
|
+
const role = turn.role ?? "";
|
|
9054
|
+
const est = Number(turn.est_tokens) || 0;
|
|
9055
|
+
const text = turnText(turn);
|
|
9056
|
+
const tools = usableTools(turn);
|
|
9057
|
+
if (role === "user") {
|
|
9058
|
+
if (!text)
|
|
9059
|
+
continue;
|
|
9060
|
+
steps.push({
|
|
9061
|
+
kind: "user",
|
|
9062
|
+
bucket: "other",
|
|
9063
|
+
label: "Question",
|
|
9064
|
+
preview: collapsePreview(text) || "No input recorded",
|
|
9065
|
+
tokens: est
|
|
9066
|
+
});
|
|
9067
|
+
continue;
|
|
9068
|
+
}
|
|
9069
|
+
if (tools.length > 0) {
|
|
9070
|
+
const share2 = Math.trunc(est / tools.length);
|
|
9071
|
+
const remainder = est - share2 * tools.length;
|
|
9072
|
+
tools.forEach((tool, i2) => {
|
|
9073
|
+
const bucket = classifyDigestTool(tool);
|
|
9074
|
+
const preview = toolPreview(tool, text);
|
|
9075
|
+
steps.push({
|
|
9076
|
+
kind: "tool",
|
|
9077
|
+
bucket,
|
|
9078
|
+
label: displayToolName(tool),
|
|
9079
|
+
preview: bucket === "code" ? `${preview} · implementation (not counted)` : preview,
|
|
9080
|
+
tokens: share2 + (i2 === 0 ? remainder : 0)
|
|
9081
|
+
});
|
|
9082
|
+
});
|
|
9083
|
+
continue;
|
|
9084
|
+
}
|
|
9085
|
+
if (text) {
|
|
9086
|
+
steps.push({
|
|
9087
|
+
kind: "reasoning",
|
|
9088
|
+
bucket: "other",
|
|
9089
|
+
label: "Reasoning",
|
|
9090
|
+
preview: collapsePreview(text),
|
|
9091
|
+
tokens: est
|
|
9092
|
+
});
|
|
9093
|
+
}
|
|
9094
|
+
}
|
|
9095
|
+
return steps;
|
|
9096
|
+
}
|
|
9097
|
+
function capTraceSteps(steps) {
|
|
9098
|
+
if (steps.length <= MAX_TRACE_STEPS)
|
|
9099
|
+
return steps;
|
|
9100
|
+
const omitted = steps.length - MAX_TRACE_STEPS;
|
|
9101
|
+
return [steps[0], { omitted }, ...steps.slice(-(MAX_TRACE_STEPS - 1))];
|
|
9102
|
+
}
|
|
9103
|
+
function renderTraceHtml(candidate, digests) {
|
|
9104
|
+
const tid = String(candidate.transcript_id ?? "").trim();
|
|
9105
|
+
const digest = tid ? digests?.[tid] : undefined;
|
|
9106
|
+
if (!digest)
|
|
9107
|
+
return "";
|
|
9108
|
+
const spec = parseLineSpec(candidate.investigation_lines);
|
|
9109
|
+
if (spec.size === 0)
|
|
9110
|
+
return "";
|
|
9111
|
+
const turns = expandDigestTurns(digest.turns, spec);
|
|
9112
|
+
if (turns.length === 0)
|
|
9113
|
+
return "";
|
|
9114
|
+
const steps = buildTraceSteps(turns);
|
|
9115
|
+
if (steps.length === 0)
|
|
9116
|
+
return "";
|
|
9117
|
+
const learning = { context: 0, planning: 0, other: 0 };
|
|
9118
|
+
const toolCounts = new Map;
|
|
9119
|
+
let reasoningN = 0;
|
|
9120
|
+
for (const step of steps) {
|
|
9121
|
+
const bucket = step.bucket;
|
|
9122
|
+
const tokens = Number(step.tokens) || 0;
|
|
9123
|
+
if (bucket === "context" || bucket === "planning" || bucket === "other") {
|
|
9124
|
+
learning[bucket] += tokens;
|
|
9125
|
+
}
|
|
9126
|
+
if (step.kind === "reasoning")
|
|
9127
|
+
reasoningN += 1;
|
|
9128
|
+
else if (step.kind === "tool" && bucket !== "code") {
|
|
9129
|
+
const label = String(step.label || "tool");
|
|
9130
|
+
toolCounts.set(label, (toolCounts.get(label) ?? 0) + 1);
|
|
9131
|
+
}
|
|
9132
|
+
}
|
|
9133
|
+
const learningTotal = learning.context + learning.planning + learning.other;
|
|
9134
|
+
const official = candidate.approx_rediscovery_tokens;
|
|
9135
|
+
const summaryTokens = official != null && Number.isFinite(Number(official)) ? Number(official) : learningTotal;
|
|
9136
|
+
const bits = [...toolCounts.entries()].sort((a3, b2) => b2[1] - a3[1]).slice(0, 5).map(([name, n2]) => `${n2} ${name}`);
|
|
9137
|
+
if (reasoningN)
|
|
9138
|
+
bits.push(`${reasoningN} reasoning`);
|
|
9139
|
+
bits.push(`~${fmtInt(summaryTokens)} tok`);
|
|
9140
|
+
const summary = `Work to learn this · ${bits.join(" · ")}`;
|
|
9141
|
+
const learnSum = learningTotal || 1;
|
|
9142
|
+
const bar = ["context", "planning", "other"].map((bucket) => {
|
|
9143
|
+
const pct = learningTotal ? 100 * learning[bucket] / learnSum : 0;
|
|
9144
|
+
return `<span class="tb ${bucket}" style="width:${pct.toFixed(1)}%"></span>`;
|
|
9145
|
+
}).join("");
|
|
9146
|
+
const legendParts = [];
|
|
9147
|
+
if (learning.context)
|
|
9148
|
+
legendParts.push(`Reads & search ${fmtInt(learning.context)}`);
|
|
9149
|
+
if (learning.planning)
|
|
9150
|
+
legendParts.push(`Planning ${fmtInt(learning.planning)}`);
|
|
9151
|
+
if (learning.other)
|
|
9152
|
+
legendParts.push(`Reasoning ${fmtInt(learning.other)}`);
|
|
9153
|
+
const rows = [
|
|
9154
|
+
'<li class="ts-head" aria-hidden="true"><span class="chip">Action</span><span class="pv">What it did</span><span class="tok">Tokens</span></li>'
|
|
9155
|
+
];
|
|
9156
|
+
for (const step of capTraceSteps(steps)) {
|
|
9157
|
+
if (step.omitted) {
|
|
9158
|
+
rows.push(`<li class="ts-omit"><span class="chip"></span><span class="pv">${esc(step.omitted)} earlier steps omitted</span><span class="tok"></span></li>`);
|
|
9159
|
+
continue;
|
|
9160
|
+
}
|
|
9161
|
+
const bucket = String(step.bucket || "other");
|
|
9162
|
+
const css = step.kind === "user" ? "ts-user" : { context: "ts-context", planning: "ts-planning", other: "ts-other", code: "ts-code" }[bucket] ?? "ts-other";
|
|
9163
|
+
const tok = step.tokens ? `<span class="tok">${esc(fmtInt(step.tokens))}</span>` : '<span class="tok"></span>';
|
|
9164
|
+
rows.push(`<li class="${css}"><span class="chip">${esc(step.label)}</span><span class="pv">${esc(step.preview)}</span>${tok}</li>`);
|
|
9165
|
+
}
|
|
9166
|
+
return `
|
|
9167
|
+
<details class="trace">
|
|
9168
|
+
<summary>${esc(summary)}</summary>
|
|
9169
|
+
<div class="trace-bar" title="context / planning / other">${bar}</div>
|
|
9170
|
+
<p class="trace-legend">${legendParts.join(" · ")}</p>
|
|
9171
|
+
<ol class="trace-steps">${rows.join("")}</ol>
|
|
9172
|
+
</details>
|
|
9173
|
+
`;
|
|
9174
|
+
}
|
|
9175
|
+
function notesSectionCopy(candidates) {
|
|
9176
|
+
const n2 = candidates.length;
|
|
9177
|
+
const written = candidates.filter((c2) => (c2.status || "written").toLowerCase() === "written").length;
|
|
9178
|
+
const pending = candidates.filter((c2) => (c2.status || "").toLowerCase() === "pending").length;
|
|
9179
|
+
const already = candidates.filter((c2) => (c2.status || "").toLowerCase() === "already_in_library").length;
|
|
9180
|
+
const proposed = n2 - written - pending - already;
|
|
9181
|
+
if (n2 > 0 && already === n2) {
|
|
9182
|
+
return [
|
|
9183
|
+
"Notes already in the Library",
|
|
9184
|
+
"These pages were already present — no new write_knowledge calls.",
|
|
9185
|
+
"Share",
|
|
9186
|
+
"Nothing new to write; the Library already had these pages."
|
|
9187
|
+
];
|
|
9188
|
+
}
|
|
9189
|
+
if (n2 > 0 && written === n2) {
|
|
9190
|
+
return [
|
|
9191
|
+
"Notes written to Dosu",
|
|
9192
|
+
"",
|
|
9193
|
+
"Share",
|
|
9194
|
+
"Notes are on the backfill branch and in the candidate-topic pipeline. Print / Save as PDF if you want a copy."
|
|
9195
|
+
];
|
|
9196
|
+
}
|
|
9197
|
+
if (n2 > 0 && proposed === n2) {
|
|
9198
|
+
return [
|
|
9199
|
+
"Proposed write_knowledge calls",
|
|
9200
|
+
"Plain-English takeaway plus what it took to find — not the original user prompts.",
|
|
9201
|
+
"Next step",
|
|
9202
|
+
"Run the skill (without dry-run) so these payloads are written via write_knowledge."
|
|
9203
|
+
];
|
|
9204
|
+
}
|
|
9205
|
+
if (n2 === 0) {
|
|
9206
|
+
return [
|
|
9207
|
+
"write_knowledge notes",
|
|
9208
|
+
"Plain-English takeaway plus what it took to find — not the original user prompts.",
|
|
9209
|
+
"Next step",
|
|
9210
|
+
"Run knowledge sync so learnings are extracted and written via write_knowledge."
|
|
9211
|
+
];
|
|
9212
|
+
}
|
|
9213
|
+
const bits = [];
|
|
9214
|
+
if (written)
|
|
9215
|
+
bits.push(`${written} written`);
|
|
9216
|
+
if (proposed)
|
|
9217
|
+
bits.push(`${proposed} proposed`);
|
|
9218
|
+
if (pending)
|
|
9219
|
+
bits.push(`${pending} pending`);
|
|
9220
|
+
if (already)
|
|
9221
|
+
bits.push(`${already} already in library`);
|
|
9222
|
+
return [
|
|
9223
|
+
"write_knowledge notes",
|
|
9224
|
+
`${bits.join(", ")} — Plain-English takeaway plus what it took to find — not the original user prompts.`,
|
|
9225
|
+
"Share",
|
|
9226
|
+
"Written notes are in the candidate-topic pipeline. Proposed/pending items still need a write."
|
|
9227
|
+
];
|
|
9228
|
+
}
|
|
9229
|
+
function buildReportHtml(options) {
|
|
9230
|
+
const inventory = options.inventory;
|
|
9231
|
+
const transcripts = inventory.transcripts ?? [];
|
|
9232
|
+
let candidates = applyStatusDefaults(options.candidates ?? [], Boolean(options.dryRun));
|
|
9233
|
+
candidates = [...candidates].sort((a3, b2) => {
|
|
9234
|
+
const av = Number(a3.approx_rediscovery_tokens) || 0;
|
|
9235
|
+
const bv = Number(b2.approx_rediscovery_tokens) || 0;
|
|
9236
|
+
return bv - av;
|
|
9237
|
+
});
|
|
9238
|
+
const org = options.orgName || "Your team";
|
|
9239
|
+
const repo = options.repo || inventory.cwd || "—";
|
|
9240
|
+
const branch = options.branch || "—";
|
|
9241
|
+
const summary = options.summary || "Local agent session logs were mined into Dosu notes so the next task can reuse them — reducing rediscovery cost.";
|
|
9242
|
+
const generated = `${(options.generatedAt ?? new Date).toISOString().replace("T", " ").slice(0, 16)} UTC`;
|
|
9243
|
+
const derived = tokenTotalsFromCandidates(candidates, inventory);
|
|
9244
|
+
const [notesHeading, notesLede, footerTitle, footerBody] = notesSectionCopy(candidates);
|
|
9245
|
+
const candidateRows = candidates.map((c2, i2) => {
|
|
9246
|
+
const [idea, work] = presentationCopy(c2);
|
|
9247
|
+
const traceHtml = renderTraceHtml(c2, options.digests);
|
|
9248
|
+
const content = (c2.content || "").trim();
|
|
9249
|
+
const ideaHtml = idea ? `<p class="idea">${esc(idea)}</p>` : "<p class='muted'>Extract a lean note before writing to Dosu.</p>";
|
|
9250
|
+
const workHtml = `<p class="work"><span class="work-label">To find this</span> ${esc(work)}</p>`;
|
|
9251
|
+
const techHtml = content && content !== idea ? `<details class="note-tech"><summary>Technical note</summary><pre class="note-body">${esc(content)}</pre></details>` : "";
|
|
9252
|
+
const status = c2.status || "written";
|
|
9253
|
+
const queryHtml = c2.user_query ? `<p class="query"><strong>Trigger:</strong> ${esc(c2.user_query)}</p>` : "";
|
|
9254
|
+
return `
|
|
9255
|
+
<article class="card" id="c-${i2 + 1}">
|
|
9256
|
+
<header>
|
|
9257
|
+
<span class="badge status-${esc(status)}">${esc(status)}</span>
|
|
9258
|
+
<h3>${esc(c2.title || "Untitled")}</h3>
|
|
9259
|
+
</header>
|
|
9260
|
+
<p class="meta">
|
|
9261
|
+
session ${esc(c2.session_title || c2.transcript_id || "—")}
|
|
9262
|
+
· rediscovery ~${fmtInt(c2.approx_rediscovery_tokens ?? c2.baseline_tokens)} tok
|
|
9263
|
+
</p>
|
|
9264
|
+
${ideaHtml}
|
|
9265
|
+
${workHtml}
|
|
9266
|
+
${traceHtml}
|
|
9267
|
+
${techHtml}
|
|
9268
|
+
${queryHtml}
|
|
9269
|
+
</article>`;
|
|
9270
|
+
});
|
|
9271
|
+
const heavy = [...transcripts].sort((a3, b2) => (b2.learning_tokens || 0) - (a3.learning_tokens || 0)).slice(0, 8);
|
|
9272
|
+
const heavyRows = heavy.map((t2) => {
|
|
9273
|
+
const title = (t2.title || "").trim() || t2.transcript_id;
|
|
9274
|
+
const query = (t2.user_queries?.[0] || "").slice(0, 100);
|
|
9275
|
+
return `<tr>
|
|
9276
|
+
<td>${esc(t2.source)}</td>
|
|
9277
|
+
<td>${esc(title)}</td>
|
|
9278
|
+
<td class="num">${fmtInt(t2.learning_tokens)}</td>
|
|
9279
|
+
<td class="num">${fmtInt(t2.rediscovery_tool_calls)}</td>
|
|
9280
|
+
<td>${esc(query)}</td>
|
|
9281
|
+
</tr>`;
|
|
9282
|
+
}).join("");
|
|
9283
|
+
let tokenSection;
|
|
9284
|
+
if (derived) {
|
|
9285
|
+
tokenSection = `
|
|
9286
|
+
<section>
|
|
9287
|
+
<h2>Estimated context savings</h2>
|
|
9288
|
+
<p class="lede">Counterfactual: replace rediscovery stretches with a Dosu <code>read_knowledge</code> hit.</p>
|
|
9289
|
+
<div class="stats">
|
|
9290
|
+
<div class="stat"><div class="label">Baseline (cost to learn)</div><div class="value">${fmtInt(derived.baseline_tokens)}</div></div>
|
|
9291
|
+
<div class="stat"><div class="label">Learning replaced</div><div class="value">${fmtInt(derived.replaced_baseline_tokens)}</div></div>
|
|
9292
|
+
<div class="stat"><div class="label">Read cost</div><div class="value">${fmtInt(derived.read_knowledge_tokens)}</div></div>
|
|
9293
|
+
<div class="stat highlight"><div class="label">Est. tokens saved</div><div class="value">${fmtInt(derived.tokens_saved)} <span class="pct">(${esc(`${derived.pct_saved}%`)})</span></div></div>
|
|
9294
|
+
</div>
|
|
9295
|
+
</section>`;
|
|
9296
|
+
} else {
|
|
9297
|
+
tokenSection = `
|
|
9298
|
+
<section>
|
|
9299
|
+
<h2>Estimated context savings</h2>
|
|
9300
|
+
<p class="muted">No rediscovery estimates on the notes yet — each candidate needs <code>approx_rediscovery_tokens</code>.</p>
|
|
9301
|
+
</section>`;
|
|
9302
|
+
}
|
|
9303
|
+
const notesBody = candidateRows.join("") || '<p class="muted">No write_knowledge payloads yet. Run knowledge sync so the miner extracts learnings.</p>';
|
|
9304
|
+
const lede = notesLede ? `<p class="lede">${esc(notesLede)}</p>` : "";
|
|
9305
|
+
return `<!DOCTYPE html>
|
|
9306
|
+
<html lang="en">
|
|
9307
|
+
<head>
|
|
9308
|
+
<meta charset="utf-8" />
|
|
9309
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
9310
|
+
<title>Dosu knowledge report — ${esc(org)}</title>
|
|
9311
|
+
<style>
|
|
9312
|
+
${REPORT_CSS}
|
|
9313
|
+
</style>
|
|
9314
|
+
</head>
|
|
9315
|
+
<body>
|
|
9316
|
+
<div class="wrap">
|
|
9317
|
+
<header class="hero">
|
|
9318
|
+
<p class="eyebrow">Dosu · Knowledge report</p>
|
|
9319
|
+
<h1>${esc(org)}</h1>
|
|
9320
|
+
<p class="lede">${esc(summary)}</p>
|
|
9321
|
+
<p class="meta-line">
|
|
9322
|
+
Repo <code>${esc(repo)}</code>
|
|
9323
|
+
· branch <code>${esc(branch)}</code>
|
|
9324
|
+
· generated ${esc(generated)}
|
|
9325
|
+
</p>
|
|
9326
|
+
<div class="toolbar no-print">
|
|
9327
|
+
<button type="button" onclick="window.print()">Print / Save as PDF</button>
|
|
9328
|
+
</div>
|
|
9329
|
+
</header>
|
|
9330
|
+
|
|
9331
|
+
${tokenSection}
|
|
9332
|
+
|
|
9333
|
+
<section>
|
|
9334
|
+
<h2>${esc(notesHeading)} <span class="muted">(${candidates.length})</span></h2>
|
|
9335
|
+
${lede}
|
|
9336
|
+
${notesBody}
|
|
9337
|
+
</section>
|
|
9338
|
+
|
|
9339
|
+
<section>
|
|
9340
|
+
<h2>Heaviest sessions</h2>
|
|
9341
|
+
<p class="lede">Where learning cost was highest — prime targets for Dosu cache hits.</p>
|
|
9342
|
+
<table>
|
|
9343
|
+
<thead>
|
|
9344
|
+
<tr>
|
|
9345
|
+
<th>Host</th><th>Session</th><th>Learning tokens</th><th>Rediscovery tools</th><th>Query</th>
|
|
9346
|
+
</tr>
|
|
9347
|
+
</thead>
|
|
9348
|
+
<tbody>
|
|
9349
|
+
${heavyRows || '<tr><td colspan="5" class="muted">No sessions</td></tr>'}
|
|
9350
|
+
</tbody>
|
|
9351
|
+
</table>
|
|
9352
|
+
</section>
|
|
9353
|
+
|
|
9354
|
+
<footer class="cta">
|
|
9355
|
+
<h2>${esc(footerTitle)}</h2>
|
|
9356
|
+
<p>${esc(footerBody)}</p>
|
|
9357
|
+
<p class="meta">
|
|
9358
|
+
Print tip: use <strong>Print / Save as PDF</strong> above (or ⌘P / Ctrl+P).
|
|
9359
|
+
Session logs stay between you and your agent — only note text is written to Dosu.
|
|
9360
|
+
</p>
|
|
9361
|
+
<div class="toolbar no-print">
|
|
9362
|
+
<button type="button" onclick="window.print()">Print / Save as PDF</button>
|
|
9363
|
+
</div>
|
|
9364
|
+
</footer>
|
|
9365
|
+
</div>
|
|
9366
|
+
</body>
|
|
9367
|
+
</html>
|
|
9368
|
+
`;
|
|
9369
|
+
}
|
|
9370
|
+
var CHARS_PER_TOKEN = 4, MAX_TRACE_STEPS = 50, PREVIEW_CHARS = 80, SQL_DISPLAY, LOGS_DISPLAY, SKIP_TOOL_NAMES, TAG_RE, WS_RE;
|
|
9371
|
+
var init_html = __esm(() => {
|
|
9372
|
+
SQL_DISPLAY = new Set(["execute_sql", "query_run", "list_tables"]);
|
|
9373
|
+
LOGS_DISPLAY = new Set(["query_logs"]);
|
|
9374
|
+
SKIP_TOOL_NAMES = new Set(["tool_result"]);
|
|
9375
|
+
TAG_RE = /<[^>]+>/g;
|
|
9376
|
+
WS_RE = /\s+/g;
|
|
9377
|
+
});
|
|
9378
|
+
|
|
9379
|
+
// src/sessions/read.ts
|
|
9380
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
9381
|
+
function asRecord(value) {
|
|
9382
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
9383
|
+
}
|
|
9384
|
+
function jsonlRecords(raw) {
|
|
9385
|
+
const records = [];
|
|
9386
|
+
for (const line of raw.split(`
|
|
9387
|
+
`)) {
|
|
9388
|
+
if (line.trim() === "")
|
|
9389
|
+
continue;
|
|
9390
|
+
try {
|
|
9391
|
+
const record = asRecord(JSON.parse(line));
|
|
9392
|
+
if (record)
|
|
9393
|
+
records.push(record);
|
|
9394
|
+
} catch {}
|
|
9395
|
+
}
|
|
9396
|
+
return records;
|
|
9397
|
+
}
|
|
9398
|
+
function textFromContent(content, textTypes) {
|
|
9399
|
+
if (typeof content === "string")
|
|
9400
|
+
return content;
|
|
9401
|
+
if (!Array.isArray(content))
|
|
9402
|
+
return "";
|
|
9403
|
+
const parts = [];
|
|
9404
|
+
for (const entry of content) {
|
|
9405
|
+
const item = asRecord(entry);
|
|
9406
|
+
if (!item)
|
|
9407
|
+
continue;
|
|
9408
|
+
if (typeof item.type === "string" && textTypes.has(item.type) && typeof item.text === "string") {
|
|
9409
|
+
parts.push(item.text);
|
|
9410
|
+
}
|
|
9411
|
+
}
|
|
9412
|
+
return parts.join(`
|
|
9413
|
+
`);
|
|
9414
|
+
}
|
|
9415
|
+
function pushTurn(turns, role, text) {
|
|
9416
|
+
if (text.trim() !== "")
|
|
9417
|
+
turns.push({ role, text });
|
|
9418
|
+
}
|
|
9419
|
+
function readClaude(raw) {
|
|
9420
|
+
const turns = [];
|
|
9421
|
+
for (const record of jsonlRecords(raw)) {
|
|
9422
|
+
const role = record.type;
|
|
9423
|
+
if (role !== "user" && role !== "assistant")
|
|
9424
|
+
continue;
|
|
9425
|
+
if (record.isSidechain === true)
|
|
9426
|
+
continue;
|
|
9427
|
+
const message = asRecord(record.message);
|
|
9428
|
+
if (!message)
|
|
9429
|
+
continue;
|
|
9430
|
+
pushTurn(turns, role, textFromContent(message.content, PLAIN_TEXT));
|
|
9431
|
+
}
|
|
9432
|
+
return turns;
|
|
9433
|
+
}
|
|
9434
|
+
function readCursor(raw) {
|
|
9435
|
+
const turns = [];
|
|
9436
|
+
for (const record of jsonlRecords(raw)) {
|
|
9437
|
+
const role = record.role;
|
|
9438
|
+
if (role !== "user" && role !== "assistant")
|
|
9439
|
+
continue;
|
|
9440
|
+
const message = asRecord(record.message);
|
|
9441
|
+
if (!message)
|
|
9442
|
+
continue;
|
|
9443
|
+
pushTurn(turns, role, textFromContent(message.content, PLAIN_TEXT));
|
|
9444
|
+
}
|
|
9445
|
+
return turns;
|
|
9446
|
+
}
|
|
9447
|
+
function isCodexInjectedBlock(text) {
|
|
9448
|
+
const head = text.trimStart().toLowerCase();
|
|
9449
|
+
return head.startsWith("<user_instructions>") || head.startsWith("<environment_context>") || head.startsWith("<recommended_plugins>");
|
|
9450
|
+
}
|
|
9451
|
+
function readCodex(raw) {
|
|
9452
|
+
const turns = [];
|
|
9453
|
+
for (const record of jsonlRecords(raw)) {
|
|
9454
|
+
if (record.type !== "response_item")
|
|
9455
|
+
continue;
|
|
9456
|
+
const payload = asRecord(record.payload);
|
|
9457
|
+
if (payload?.type !== "message")
|
|
9458
|
+
continue;
|
|
9459
|
+
const role = payload.role;
|
|
9460
|
+
if (role !== "user" && role !== "assistant")
|
|
9461
|
+
continue;
|
|
9462
|
+
const text = textFromContent(payload.content, CODEX_TEXT);
|
|
9463
|
+
if (role === "user" && isCodexInjectedBlock(text))
|
|
9464
|
+
continue;
|
|
9465
|
+
pushTurn(turns, role, text);
|
|
9466
|
+
}
|
|
9467
|
+
return turns;
|
|
9468
|
+
}
|
|
9469
|
+
function readOpencode(dbPath, sessionId) {
|
|
9470
|
+
if (!/^[A-Za-z0-9_-]+$/.test(sessionId))
|
|
9471
|
+
return [];
|
|
9472
|
+
const rows = querySqlite(dbPath, "SELECT m.data AS message, p.data AS part FROM part p " + "JOIN message m ON m.id = p.message_id " + `WHERE p.session_id = '${sessionId}' ORDER BY p.time_created, p.id`);
|
|
9473
|
+
if (!rows)
|
|
9474
|
+
return [];
|
|
9475
|
+
const turns = [];
|
|
9476
|
+
for (const row of rows) {
|
|
9477
|
+
if (typeof row.message !== "string" || typeof row.part !== "string")
|
|
9478
|
+
continue;
|
|
9479
|
+
try {
|
|
9480
|
+
const message = asRecord(JSON.parse(row.message));
|
|
9481
|
+
const part = asRecord(JSON.parse(row.part));
|
|
9482
|
+
const role = message?.role;
|
|
9483
|
+
if (role !== "user" && role !== "assistant")
|
|
9484
|
+
continue;
|
|
9485
|
+
if (part?.type !== "text" || typeof part.text !== "string")
|
|
9486
|
+
continue;
|
|
9487
|
+
pushTurn(turns, role, part.text);
|
|
9488
|
+
} catch {}
|
|
9489
|
+
}
|
|
9490
|
+
return turns;
|
|
9491
|
+
}
|
|
9492
|
+
function readSessionTurns(session) {
|
|
9493
|
+
try {
|
|
9494
|
+
switch (session.harness) {
|
|
9495
|
+
case "claude":
|
|
9496
|
+
return readClaude(readFileSync10(session.path, "utf8"));
|
|
9497
|
+
case "cursor":
|
|
9498
|
+
return readCursor(readFileSync10(session.path, "utf8"));
|
|
9499
|
+
case "codex":
|
|
9500
|
+
return readCodex(readFileSync10(session.path, "utf8"));
|
|
9501
|
+
case "opencode":
|
|
9502
|
+
return readOpencode(session.path, session.id);
|
|
9503
|
+
}
|
|
9504
|
+
} catch {
|
|
9505
|
+
return [];
|
|
9506
|
+
}
|
|
9507
|
+
}
|
|
9508
|
+
function isRediscoveryTool(name) {
|
|
9509
|
+
return REDISCOVERY_TOOLS.has(name) || name.startsWith("web_search");
|
|
9510
|
+
}
|
|
9511
|
+
function toolNamesFromContent(content) {
|
|
9512
|
+
if (!Array.isArray(content))
|
|
9513
|
+
return [];
|
|
9514
|
+
const names = [];
|
|
9515
|
+
for (const entry of content) {
|
|
9516
|
+
const item = asRecord(entry);
|
|
9517
|
+
if (item?.type === "tool_use" && typeof item.name === "string")
|
|
9518
|
+
names.push(item.name);
|
|
9519
|
+
}
|
|
9520
|
+
return names;
|
|
9521
|
+
}
|
|
9522
|
+
function countRediscoveryToolCalls(session) {
|
|
9523
|
+
try {
|
|
9524
|
+
switch (session.harness) {
|
|
9525
|
+
case "claude":
|
|
9526
|
+
case "cursor": {
|
|
9527
|
+
let n2 = 0;
|
|
9528
|
+
for (const record of jsonlRecords(readFileSync10(session.path, "utf8"))) {
|
|
9529
|
+
const message = asRecord(record.message);
|
|
9530
|
+
for (const name of toolNamesFromContent(message?.content)) {
|
|
9531
|
+
if (isRediscoveryTool(name))
|
|
9532
|
+
n2 += 1;
|
|
9533
|
+
}
|
|
9534
|
+
}
|
|
9535
|
+
return n2;
|
|
9536
|
+
}
|
|
9537
|
+
case "codex": {
|
|
9538
|
+
let n2 = 0;
|
|
9539
|
+
for (const record of jsonlRecords(readFileSync10(session.path, "utf8"))) {
|
|
9540
|
+
const payload = asRecord(record.payload);
|
|
9541
|
+
if (payload?.type === "function_call" && typeof payload.name === "string") {
|
|
9542
|
+
if (isRediscoveryTool(payload.name))
|
|
9543
|
+
n2 += 1;
|
|
9544
|
+
}
|
|
9545
|
+
}
|
|
9546
|
+
return n2;
|
|
9547
|
+
}
|
|
9548
|
+
case "opencode":
|
|
9549
|
+
return 0;
|
|
9550
|
+
}
|
|
9551
|
+
} catch {
|
|
9552
|
+
return 0;
|
|
9553
|
+
}
|
|
9554
|
+
}
|
|
9555
|
+
function estimateSessionTokens(session) {
|
|
9556
|
+
let chars = 0;
|
|
9557
|
+
for (const turn of readSessionTurns(session))
|
|
9558
|
+
chars += turn.text.length;
|
|
9559
|
+
return Math.round(chars / CHARS_PER_TOKEN2);
|
|
9560
|
+
}
|
|
9561
|
+
function isWorthMining(session) {
|
|
9562
|
+
const turns = readSessionTurns(session);
|
|
9563
|
+
if (turns.length < MIN_WORTH_TURNS)
|
|
9564
|
+
return false;
|
|
9565
|
+
let chars = 0;
|
|
9566
|
+
for (const turn of turns) {
|
|
9567
|
+
chars += turn.text.length;
|
|
9568
|
+
if (chars >= MIN_WORTH_CHARS)
|
|
9569
|
+
return true;
|
|
9570
|
+
}
|
|
9571
|
+
return false;
|
|
9572
|
+
}
|
|
9573
|
+
var PLAIN_TEXT, CODEX_TEXT, REDISCOVERY_TOOLS, CHARS_PER_TOKEN2 = 4, MIN_WORTH_TURNS = 4, MIN_WORTH_CHARS = 2000;
|
|
9574
|
+
var init_read = __esm(() => {
|
|
9575
|
+
init_scan();
|
|
9576
|
+
PLAIN_TEXT = new Set(["text"]);
|
|
9577
|
+
CODEX_TEXT = new Set(["input_text", "output_text"]);
|
|
9578
|
+
REDISCOVERY_TOOLS = new Set([
|
|
9579
|
+
"Read",
|
|
9580
|
+
"Grep",
|
|
9581
|
+
"Glob",
|
|
9582
|
+
"Shell",
|
|
9583
|
+
"WebSearch",
|
|
9584
|
+
"WebFetch",
|
|
9585
|
+
"Task",
|
|
9586
|
+
"SemanticSearch",
|
|
9587
|
+
"Bash",
|
|
9588
|
+
"Edit",
|
|
9589
|
+
"MultiEdit",
|
|
9590
|
+
"NotebookEdit",
|
|
9591
|
+
"Agent",
|
|
9592
|
+
"exec_command",
|
|
9593
|
+
"write_stdin",
|
|
9594
|
+
"web_search",
|
|
9595
|
+
"web_search_call",
|
|
9596
|
+
"open_page",
|
|
9597
|
+
"apply_patch",
|
|
9598
|
+
"update_plan",
|
|
9599
|
+
"list_dir",
|
|
9600
|
+
"grep_files",
|
|
9601
|
+
"read_file"
|
|
9602
|
+
]);
|
|
9603
|
+
});
|
|
9604
|
+
|
|
9605
|
+
// src/report/queries.ts
|
|
9606
|
+
function extractUserQueries(text) {
|
|
9607
|
+
const matches = [...text.matchAll(USER_QUERY_RE)].map((m3) => m3[1].trim()).filter(Boolean);
|
|
9608
|
+
if (matches.length > 0)
|
|
9609
|
+
return matches;
|
|
9610
|
+
const cleaned = text.replace(TIMESTAMP_RE, "").trim();
|
|
9611
|
+
if (cleaned.startsWith("# AGENTS.md") || cleaned.startsWith("<INSTRUCTIONS>") || cleaned.startsWith("<permissions instructions>")) {
|
|
9612
|
+
return [];
|
|
9613
|
+
}
|
|
9614
|
+
return cleaned ? [cleaned] : [];
|
|
9615
|
+
}
|
|
9616
|
+
function sessionTitleFromUserText(text, max = TITLE_CHARS) {
|
|
9617
|
+
const first = extractUserQueries(text)[0] ?? "";
|
|
9618
|
+
const collapsed = first.replace(/\s+/g, " ").trim();
|
|
9619
|
+
if (collapsed.length > max)
|
|
9620
|
+
return `${collapsed.slice(0, max - 1).trimEnd()}…`;
|
|
9621
|
+
return collapsed;
|
|
9622
|
+
}
|
|
9623
|
+
var TITLE_CHARS = 80, USER_QUERY_RE, TIMESTAMP_RE;
|
|
9624
|
+
var init_queries = __esm(() => {
|
|
9625
|
+
USER_QUERY_RE = /<user_query>\s*([\s\S]*?)\s*<\/user_query>/gi;
|
|
9626
|
+
TIMESTAMP_RE = /<timestamp>[\s\S]*?<\/timestamp>/gi;
|
|
9627
|
+
});
|
|
9628
|
+
|
|
9629
|
+
// src/report/digest.ts
|
|
9630
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
9631
|
+
function asRecord2(value) {
|
|
9632
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
9633
|
+
}
|
|
9634
|
+
function estimateTokens(text) {
|
|
9635
|
+
return Math.round(text.length / CHARS_PER_TOKEN3);
|
|
9636
|
+
}
|
|
9637
|
+
function digestEstTokens(texts, tools, resultChars) {
|
|
9638
|
+
const countable = tools.filter((t2) => t2.name !== "tool_result");
|
|
9639
|
+
let payload = texts.join(`
|
|
9640
|
+
`);
|
|
9641
|
+
if (countable.length > 0)
|
|
9642
|
+
payload += JSON.stringify(countable);
|
|
9643
|
+
let tokens = estimateTokens(payload);
|
|
9644
|
+
if (resultChars > 0)
|
|
9645
|
+
tokens += Math.max(1, Math.round(resultChars / CHARS_PER_TOKEN3));
|
|
9646
|
+
return tokens;
|
|
9647
|
+
}
|
|
9648
|
+
function copyToolInputFields(entry, inp) {
|
|
9649
|
+
const row = asRecord2(inp);
|
|
9650
|
+
if (!row)
|
|
9651
|
+
return;
|
|
9652
|
+
if (typeof row.path === "string")
|
|
9653
|
+
entry.path = row.path;
|
|
9654
|
+
if (typeof row.file_path === "string") {
|
|
9655
|
+
entry.file_path = row.file_path;
|
|
9656
|
+
entry.path ??= row.file_path;
|
|
9657
|
+
}
|
|
9658
|
+
if (typeof row.pattern === "string")
|
|
9659
|
+
entry.pattern = row.pattern;
|
|
9660
|
+
if (row.command != null)
|
|
9661
|
+
entry.command_preview = String(row.command).slice(0, 160);
|
|
9662
|
+
if (row.prompt != null) {
|
|
9663
|
+
entry.prompt = String(row.prompt).slice(0, 160);
|
|
9664
|
+
entry.command_preview ??= entry.prompt;
|
|
9665
|
+
}
|
|
9666
|
+
if (typeof row.query === "string")
|
|
9667
|
+
entry.query = row.query;
|
|
9668
|
+
if (typeof row.description === "string" && !entry.command_preview) {
|
|
9669
|
+
entry.command_preview = row.description.slice(0, 160);
|
|
9670
|
+
}
|
|
9671
|
+
}
|
|
9672
|
+
function copyMcpFields(entry, name, inp) {
|
|
9673
|
+
const row = asRecord2(inp);
|
|
9674
|
+
if (!row)
|
|
9675
|
+
return;
|
|
9676
|
+
const isMcp = MCP_DIGEST_TOOLS.has(name) || name.startsWith("mcp__") || Boolean(row.toolName || row.server);
|
|
9677
|
+
if (!isMcp)
|
|
9678
|
+
return;
|
|
9679
|
+
const toolName = row.toolName ?? row.tool_name;
|
|
9680
|
+
if (typeof toolName === "string")
|
|
9681
|
+
entry.toolName = toolName;
|
|
9682
|
+
if (typeof row.server === "string")
|
|
9683
|
+
entry.server = row.server;
|
|
9684
|
+
if (name === "GetMcpTools" && typeof row.pattern === "string")
|
|
9685
|
+
entry.pattern = row.pattern;
|
|
9686
|
+
const args = asRecord2(row.arguments);
|
|
9687
|
+
if (args)
|
|
9688
|
+
entry.arguments = args;
|
|
9689
|
+
else if (name.startsWith("mcp__"))
|
|
9690
|
+
entry.arguments = row;
|
|
9691
|
+
}
|
|
9692
|
+
function blocksToDigest(content, maxTextChars) {
|
|
9693
|
+
const texts = [];
|
|
9694
|
+
const tools = [];
|
|
9695
|
+
let resultChars = 0;
|
|
9696
|
+
const blocks = Array.isArray(content) ? content : typeof content === "string" && content ? [{ type: "text", text: content }] : [];
|
|
9697
|
+
for (const block2 of blocks) {
|
|
9698
|
+
const item = asRecord2(block2);
|
|
9699
|
+
if (!item)
|
|
9700
|
+
continue;
|
|
9701
|
+
const btype = item.type;
|
|
9702
|
+
if ((btype === "text" || btype === "input_text" || btype === "output_text") && typeof item.text === "string" && item.text) {
|
|
9703
|
+
let t2 = item.text;
|
|
9704
|
+
if (t2.length > maxTextChars)
|
|
9705
|
+
t2 = `${t2.slice(0, maxTextChars)}
|
|
9706
|
+
…[truncated]`;
|
|
9707
|
+
texts.push(t2);
|
|
9708
|
+
} else if (btype === "tool_use" && typeof item.name === "string") {
|
|
9709
|
+
const entry = { name: item.name };
|
|
9710
|
+
const inp = item.input ?? {};
|
|
9711
|
+
copyToolInputFields(entry, inp);
|
|
9712
|
+
copyMcpFields(entry, item.name, inp);
|
|
9713
|
+
tools.push(entry);
|
|
9714
|
+
} else if (btype === "tool_result") {
|
|
9715
|
+
const c2 = item.content;
|
|
9716
|
+
if (typeof c2 === "string")
|
|
9717
|
+
resultChars += c2.length;
|
|
9718
|
+
else if (c2 != null)
|
|
9719
|
+
resultChars += JSON.stringify(c2).length;
|
|
9720
|
+
tools.push({ name: "tool_result" });
|
|
9721
|
+
}
|
|
9722
|
+
}
|
|
9723
|
+
return { texts, tools, resultChars };
|
|
9724
|
+
}
|
|
9725
|
+
function pushTurn2(turns, line, role, texts, tools, resultChars) {
|
|
9726
|
+
if (role === "user" && texts.length > 0) {
|
|
9727
|
+
const qs = extractUserQueries(texts.join(`
|
|
9728
|
+
`));
|
|
9729
|
+
if (qs.length > 0)
|
|
9730
|
+
texts.splice(0, texts.length, ...qs);
|
|
9731
|
+
}
|
|
9732
|
+
if (role === "user" && texts.length === 0)
|
|
9733
|
+
return;
|
|
9734
|
+
if (texts.length === 0 && tools.length === 0)
|
|
9735
|
+
return;
|
|
9736
|
+
turns.push({
|
|
9737
|
+
line,
|
|
9738
|
+
role,
|
|
9739
|
+
text: texts,
|
|
9740
|
+
tools,
|
|
9741
|
+
est_tokens: digestEstTokens(texts, tools, resultChars)
|
|
9742
|
+
});
|
|
9743
|
+
}
|
|
9744
|
+
function digestCursor(raw) {
|
|
9745
|
+
const turns = [];
|
|
9746
|
+
const lines = raw.split(`
|
|
9747
|
+
`);
|
|
9748
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
9749
|
+
const line = lines[i2].trim();
|
|
9750
|
+
if (!line)
|
|
9751
|
+
continue;
|
|
9752
|
+
let obj;
|
|
9753
|
+
try {
|
|
9754
|
+
const parsed = asRecord2(JSON.parse(line));
|
|
9755
|
+
if (!parsed)
|
|
9756
|
+
continue;
|
|
9757
|
+
obj = parsed;
|
|
9758
|
+
} catch {
|
|
9759
|
+
continue;
|
|
9760
|
+
}
|
|
9761
|
+
const role = obj.role;
|
|
9762
|
+
if (role !== "user" && role !== "assistant")
|
|
9763
|
+
continue;
|
|
9764
|
+
const message = asRecord2(obj.message);
|
|
9765
|
+
const { texts, tools, resultChars } = blocksToDigest(message?.content, role === "assistant" ? MAX_ASSISTANT_CHARS : 2000);
|
|
9766
|
+
pushTurn2(turns, i2 + 1, role, texts, tools, resultChars);
|
|
9767
|
+
}
|
|
9768
|
+
return turns;
|
|
9769
|
+
}
|
|
9770
|
+
function digestClaude(raw) {
|
|
9771
|
+
const turns = [];
|
|
9772
|
+
const lines = raw.split(`
|
|
9773
|
+
`);
|
|
9774
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
9775
|
+
const line = lines[i2].trim();
|
|
9776
|
+
if (!line)
|
|
9777
|
+
continue;
|
|
9778
|
+
let obj;
|
|
9779
|
+
try {
|
|
9780
|
+
const parsed = asRecord2(JSON.parse(line));
|
|
9781
|
+
if (!parsed)
|
|
9782
|
+
continue;
|
|
9783
|
+
obj = parsed;
|
|
9784
|
+
} catch {
|
|
9785
|
+
continue;
|
|
9786
|
+
}
|
|
9787
|
+
const etype = obj.type;
|
|
9788
|
+
if (etype !== "user" && etype !== "assistant")
|
|
9789
|
+
continue;
|
|
9790
|
+
if (obj.isSidechain === true)
|
|
9791
|
+
continue;
|
|
9792
|
+
const message = asRecord2(obj.message);
|
|
9793
|
+
const { texts, tools, resultChars } = blocksToDigest(message?.content, etype === "assistant" ? MAX_ASSISTANT_CHARS : 2000);
|
|
9794
|
+
pushTurn2(turns, i2 + 1, etype, texts, tools, resultChars);
|
|
9795
|
+
}
|
|
9796
|
+
return turns;
|
|
9797
|
+
}
|
|
9798
|
+
function digestCodex(raw) {
|
|
9799
|
+
const turns = [];
|
|
9800
|
+
const lines = raw.split(`
|
|
9801
|
+
`);
|
|
9802
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
9803
|
+
const line = lines[i2].trim();
|
|
9804
|
+
if (!line)
|
|
9805
|
+
continue;
|
|
9806
|
+
let obj;
|
|
9807
|
+
try {
|
|
9808
|
+
const parsed = asRecord2(JSON.parse(line));
|
|
9809
|
+
if (!parsed)
|
|
9810
|
+
continue;
|
|
9811
|
+
obj = parsed;
|
|
9812
|
+
} catch {
|
|
9813
|
+
continue;
|
|
9814
|
+
}
|
|
9815
|
+
const payload = asRecord2(obj.payload) ?? {};
|
|
9816
|
+
const etype = obj.type;
|
|
9817
|
+
if (etype === "event_msg" && payload.type === "user_message") {
|
|
9818
|
+
const msg = String(payload.message ?? "").slice(0, 2000);
|
|
9819
|
+
if (msg) {
|
|
9820
|
+
turns.push({
|
|
9821
|
+
line: i2 + 1,
|
|
9822
|
+
role: "user",
|
|
9823
|
+
text: [msg],
|
|
9824
|
+
tools: [],
|
|
9825
|
+
est_tokens: estimateTokens(msg)
|
|
9826
|
+
});
|
|
9827
|
+
}
|
|
9828
|
+
} else if (etype === "event_msg" && payload.type === "agent_message") {
|
|
9829
|
+
let msg = String(payload.message ?? "");
|
|
9830
|
+
if (msg.length > MAX_ASSISTANT_CHARS)
|
|
9831
|
+
msg = `${msg.slice(0, MAX_ASSISTANT_CHARS)}
|
|
9832
|
+
…[truncated]`;
|
|
9833
|
+
if (msg) {
|
|
9834
|
+
turns.push({
|
|
9835
|
+
line: i2 + 1,
|
|
9836
|
+
role: "assistant",
|
|
9837
|
+
text: [msg],
|
|
9838
|
+
tools: [],
|
|
9839
|
+
est_tokens: estimateTokens(msg)
|
|
9840
|
+
});
|
|
9841
|
+
}
|
|
9842
|
+
} else if (etype === "response_item" && payload.type === "function_call") {
|
|
9843
|
+
const name = typeof payload.name === "string" ? payload.name : "function_call";
|
|
9844
|
+
const args = payload.arguments;
|
|
9845
|
+
const preview = typeof args === "string" ? args.slice(0, 160) : JSON.stringify(args ?? "").slice(0, 160);
|
|
9846
|
+
turns.push({
|
|
9847
|
+
line: i2 + 1,
|
|
9848
|
+
role: "assistant",
|
|
9849
|
+
text: [],
|
|
9850
|
+
tools: [{ name, command_preview: preview }],
|
|
9851
|
+
est_tokens: estimateTokens(preview)
|
|
9852
|
+
});
|
|
9853
|
+
}
|
|
9854
|
+
}
|
|
9855
|
+
return turns;
|
|
9856
|
+
}
|
|
9857
|
+
function sessionToDigest(session) {
|
|
9858
|
+
try {
|
|
9859
|
+
switch (session.harness) {
|
|
9860
|
+
case "claude":
|
|
9861
|
+
return { turns: digestClaude(readFileSync11(session.path, "utf8")) };
|
|
9862
|
+
case "cursor":
|
|
9863
|
+
return { turns: digestCursor(readFileSync11(session.path, "utf8")) };
|
|
9864
|
+
case "codex":
|
|
9865
|
+
return { turns: digestCodex(readFileSync11(session.path, "utf8")) };
|
|
9866
|
+
case "opencode": {
|
|
9867
|
+
const turns = readSessionTurns(session).map((turn, index) => ({
|
|
9868
|
+
role: turn.role,
|
|
9869
|
+
line: index + 1,
|
|
9870
|
+
est_tokens: Math.round(turn.text.length / CHARS_PER_TOKEN3),
|
|
9871
|
+
text: [turn.text],
|
|
9872
|
+
tools: []
|
|
9873
|
+
}));
|
|
9874
|
+
return { turns };
|
|
9875
|
+
}
|
|
9876
|
+
}
|
|
9877
|
+
} catch {
|
|
9878
|
+
return { turns: [] };
|
|
9879
|
+
}
|
|
9880
|
+
}
|
|
9881
|
+
function digestsForSessions(sessions) {
|
|
9882
|
+
const out = {};
|
|
9883
|
+
for (const session of sessions) {
|
|
9884
|
+
out[session.id] = sessionToDigest(session);
|
|
9885
|
+
}
|
|
9886
|
+
return out;
|
|
9887
|
+
}
|
|
9888
|
+
function digestTurnText(turn) {
|
|
9889
|
+
const texts = turn.text ?? [];
|
|
9890
|
+
if (typeof texts === "string")
|
|
9891
|
+
return texts;
|
|
9892
|
+
return texts.filter(Boolean).join(`
|
|
9893
|
+
`);
|
|
9894
|
+
}
|
|
9895
|
+
var CHARS_PER_TOKEN3 = 4, MAX_ASSISTANT_CHARS = 4000, MCP_DIGEST_TOOLS;
|
|
9896
|
+
var init_digest = __esm(() => {
|
|
9897
|
+
init_read();
|
|
9898
|
+
init_queries();
|
|
9899
|
+
MCP_DIGEST_TOOLS = new Set(["CallMcpTool", "GetMcpTools"]);
|
|
9900
|
+
});
|
|
9901
|
+
|
|
9902
|
+
// src/report/notes.ts
|
|
9903
|
+
function sessionIdFromReadInput(input) {
|
|
9904
|
+
if (typeof input !== "object" || input === null || Array.isArray(input))
|
|
9905
|
+
return;
|
|
9906
|
+
const id = input.id;
|
|
9907
|
+
return typeof id === "string" && id.trim() ? id : undefined;
|
|
9908
|
+
}
|
|
9909
|
+
function significantWords(text) {
|
|
9910
|
+
const out = new Set;
|
|
9911
|
+
for (const raw of text.toLowerCase().match(/[a-z][a-z0-9._-]{2,}/g) ?? []) {
|
|
9912
|
+
const word = raw.replace(/^\.+|\.+$/g, "");
|
|
9913
|
+
if (word.length >= 3 && !STOP_WORDS.has(word))
|
|
9914
|
+
out.add(word);
|
|
9915
|
+
}
|
|
9916
|
+
return out;
|
|
9917
|
+
}
|
|
9918
|
+
function overlapCount(a3, b2) {
|
|
9919
|
+
let n2 = 0;
|
|
9920
|
+
for (const word of a3)
|
|
9921
|
+
if (b2.has(word))
|
|
9922
|
+
n2 += 1;
|
|
9923
|
+
return n2;
|
|
9924
|
+
}
|
|
9925
|
+
function noteWords(note) {
|
|
9926
|
+
return { title: significantWords(note.title), content: significantWords(note.content) };
|
|
9927
|
+
}
|
|
9928
|
+
function sessionCycles(turns) {
|
|
9929
|
+
const starts = [];
|
|
9930
|
+
for (let i2 = 0;i2 < turns.length; i2++) {
|
|
9931
|
+
if (turns[i2].role === "user" && digestTurnText(turns[i2]).trim())
|
|
9932
|
+
starts.push(i2);
|
|
9933
|
+
}
|
|
9934
|
+
return starts.map((start, i2) => {
|
|
9935
|
+
const end = i2 + 1 < starts.length ? starts[i2 + 1] : turns.length;
|
|
9936
|
+
const text = turns.slice(start, end).map((t2) => {
|
|
9937
|
+
const paths = (t2.tools ?? []).map((tool) => tool.path || tool.pattern || tool.command_preview || "").filter(Boolean).join(" ");
|
|
9938
|
+
return `${digestTurnText(t2)} ${paths}`;
|
|
9939
|
+
}).join(`
|
|
9940
|
+
`);
|
|
9941
|
+
return { start, end, text };
|
|
9942
|
+
});
|
|
9943
|
+
}
|
|
9944
|
+
function cycleMatchScore(words, cycleText) {
|
|
9945
|
+
const cycleWords = significantWords(cycleText);
|
|
9946
|
+
return overlapCount(words.title, cycleWords) * 2 + overlapCount(words.content, cycleWords);
|
|
9947
|
+
}
|
|
9948
|
+
function cleanedUserQuery(text) {
|
|
9949
|
+
const queries = extractUserQueries(text);
|
|
9950
|
+
return (queries[0] ?? text).replace(/\s+/g, " ").trim();
|
|
9951
|
+
}
|
|
9952
|
+
function attributeRediscovery(notes, sessions) {
|
|
9953
|
+
const byId = new Map(sessions.map((s) => [s.id, s]));
|
|
9954
|
+
const candidates = notes.map((note) => ({
|
|
9955
|
+
title: note.title,
|
|
9956
|
+
content: note.content,
|
|
9957
|
+
transcript_id: note.transcript_id,
|
|
9958
|
+
repo: note.repo,
|
|
9959
|
+
branch: note.branch,
|
|
9960
|
+
status: "written"
|
|
9961
|
+
}));
|
|
9962
|
+
const indexesBySession = new Map;
|
|
9963
|
+
for (let i2 = 0;i2 < notes.length; i2++) {
|
|
9964
|
+
const id = notes[i2].transcript_id;
|
|
9965
|
+
if (!id || !byId.has(id))
|
|
9966
|
+
continue;
|
|
9967
|
+
const list = indexesBySession.get(id) ?? [];
|
|
9968
|
+
list.push(i2);
|
|
9969
|
+
indexesBySession.set(id, list);
|
|
9970
|
+
}
|
|
9971
|
+
for (const [sessionId, indexes] of indexesBySession) {
|
|
9972
|
+
const session = byId.get(sessionId);
|
|
9973
|
+
if (!session)
|
|
9974
|
+
continue;
|
|
9975
|
+
const turns = sessionToDigest(session).turns;
|
|
9976
|
+
const firstUser = turns.find((t2) => t2.role === "user");
|
|
9977
|
+
const firstText = firstUser ? digestTurnText(firstUser) : "";
|
|
9978
|
+
const sessionTitle = firstText ? sessionTitleFromUserText(firstText) : undefined;
|
|
9979
|
+
for (const i2 of indexes) {
|
|
9980
|
+
if (sessionTitle)
|
|
9981
|
+
candidates[i2].session_title = sessionTitle;
|
|
9982
|
+
if (firstText)
|
|
9983
|
+
candidates[i2].user_query = cleanedUserQuery(firstText).slice(0, 200);
|
|
9984
|
+
}
|
|
9985
|
+
if (turns.length === 0)
|
|
9986
|
+
continue;
|
|
9987
|
+
const cycles = sessionCycles(turns);
|
|
9988
|
+
const taken = new Set;
|
|
9989
|
+
const ranked = indexes.map((i2) => {
|
|
9990
|
+
const words = noteWords(notes[i2]);
|
|
9991
|
+
let bestScore = 0;
|
|
9992
|
+
for (const cycle of cycles) {
|
|
9993
|
+
const score = cycleMatchScore(words, cycle.text);
|
|
9994
|
+
if (score > bestScore)
|
|
9995
|
+
bestScore = score;
|
|
9996
|
+
}
|
|
9997
|
+
return { i: i2, words, bestScore };
|
|
9998
|
+
}).sort((a3, b2) => b2.bestScore - a3.bestScore);
|
|
9999
|
+
for (const item of ranked) {
|
|
10000
|
+
if (item.bestScore <= 0)
|
|
10001
|
+
continue;
|
|
10002
|
+
let pick = -1;
|
|
10003
|
+
let pickScore = 0;
|
|
10004
|
+
for (let ci = 0;ci < cycles.length; ci++) {
|
|
10005
|
+
if (taken.has(ci))
|
|
10006
|
+
continue;
|
|
10007
|
+
const score = cycleMatchScore(item.words, cycles[ci].text);
|
|
10008
|
+
if (score > pickScore) {
|
|
10009
|
+
pickScore = score;
|
|
10010
|
+
pick = ci;
|
|
10011
|
+
}
|
|
10012
|
+
}
|
|
10013
|
+
if (pick < 0 || pickScore <= 0)
|
|
10014
|
+
continue;
|
|
10015
|
+
taken.add(pick);
|
|
10016
|
+
const cycle = cycles[pick];
|
|
10017
|
+
const slice = turns.slice(cycle.start, cycle.end);
|
|
10018
|
+
const tokens = slice.reduce((sum, t2) => sum + (Number(t2.est_tokens) || 0), 0);
|
|
10019
|
+
candidates[item.i].approx_rediscovery_tokens = tokens;
|
|
10020
|
+
const startLine = slice[0]?.line ?? cycle.start + 1;
|
|
10021
|
+
const endLine = slice.at(-1)?.line ?? cycle.end;
|
|
10022
|
+
candidates[item.i].investigation_lines = `${startLine}-${endLine}`;
|
|
10023
|
+
const cycleUser = slice.find((t2) => t2.role === "user");
|
|
10024
|
+
if (cycleUser) {
|
|
10025
|
+
const q = digestTurnText(cycleUser);
|
|
10026
|
+
candidates[item.i].user_query = cleanedUserQuery(q).slice(0, 200);
|
|
10027
|
+
candidates[item.i].session_title = sessionTitleFromUserText(q);
|
|
10028
|
+
}
|
|
10029
|
+
}
|
|
10030
|
+
}
|
|
10031
|
+
return candidates;
|
|
10032
|
+
}
|
|
10033
|
+
function sessionsToInventory(sessions) {
|
|
10034
|
+
const transcripts = sessions.map((session) => {
|
|
10035
|
+
const turns = readSessionTurns(session);
|
|
10036
|
+
const queries = turns.filter((t2) => t2.role === "user").flatMap((t2) => extractUserQueries(t2.text));
|
|
10037
|
+
const title = queries[0] ? sessionTitleFromUserText(queries[0]) : undefined;
|
|
10038
|
+
return {
|
|
10039
|
+
source: session.harness,
|
|
10040
|
+
transcript_id: session.id,
|
|
10041
|
+
...title ? { title } : {},
|
|
10042
|
+
learning_tokens: estimateSessionTokens(session),
|
|
10043
|
+
rediscovery_tool_calls: countRediscoveryToolCalls(session),
|
|
10044
|
+
user_queries: queries.slice(0, 3)
|
|
10045
|
+
};
|
|
10046
|
+
});
|
|
10047
|
+
const learning_tokens = transcripts.reduce((sum, t2) => sum + t2.learning_tokens, 0);
|
|
10048
|
+
return { transcripts, totals: { learning_tokens } };
|
|
10049
|
+
}
|
|
10050
|
+
var STOP_WORDS;
|
|
10051
|
+
var init_notes = __esm(() => {
|
|
10052
|
+
init_read();
|
|
10053
|
+
init_digest();
|
|
10054
|
+
init_queries();
|
|
10055
|
+
init_digest();
|
|
10056
|
+
STOP_WORDS = new Set([
|
|
10057
|
+
"about",
|
|
10058
|
+
"after",
|
|
10059
|
+
"also",
|
|
10060
|
+
"and",
|
|
10061
|
+
"are",
|
|
10062
|
+
"but",
|
|
10063
|
+
"can",
|
|
10064
|
+
"does",
|
|
10065
|
+
"for",
|
|
10066
|
+
"from",
|
|
10067
|
+
"have",
|
|
10068
|
+
"into",
|
|
10069
|
+
"just",
|
|
10070
|
+
"must",
|
|
10071
|
+
"not",
|
|
10072
|
+
"that",
|
|
10073
|
+
"the",
|
|
10074
|
+
"this",
|
|
10075
|
+
"they",
|
|
10076
|
+
"with",
|
|
10077
|
+
"what",
|
|
10078
|
+
"when",
|
|
10079
|
+
"which",
|
|
10080
|
+
"will",
|
|
10081
|
+
"your"
|
|
10082
|
+
]);
|
|
10083
|
+
});
|
|
10084
|
+
|
|
10085
|
+
// src/report/write.ts
|
|
10086
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
|
|
10087
|
+
import { tmpdir } from "node:os";
|
|
10088
|
+
import { dirname as dirname4, join as join10, resolve } from "node:path";
|
|
10089
|
+
import { pathToFileURL } from "node:url";
|
|
10090
|
+
function defaultReportPath() {
|
|
10091
|
+
return join10(tmpdir(), "dosu-knowledge-report.html");
|
|
10092
|
+
}
|
|
10093
|
+
async function writeAndOpenReport(options) {
|
|
10094
|
+
const out = resolve(options.out ?? defaultReportPath());
|
|
10095
|
+
mkdirSync7(dirname4(out), { recursive: true });
|
|
10096
|
+
writeFileSync7(out, options.html, "utf8");
|
|
10097
|
+
if (options.open !== false) {
|
|
10098
|
+
const url = pathToFileURL(out).href;
|
|
10099
|
+
if (options.openUrl) {
|
|
10100
|
+
await options.openUrl(url);
|
|
10101
|
+
} else {
|
|
10102
|
+
const open2 = await Promise.resolve().then(() => (init_open(), exports_open));
|
|
10103
|
+
await open2.default(url);
|
|
10104
|
+
}
|
|
10105
|
+
}
|
|
10106
|
+
return out;
|
|
10107
|
+
}
|
|
10108
|
+
var init_write = () => {};
|
|
10109
|
+
|
|
10110
|
+
// src/report/generate.ts
|
|
10111
|
+
function sessionsForNotes(notes, sessions) {
|
|
10112
|
+
const ids = new Set(notes.map((n2) => n2.transcript_id).filter((id) => Boolean(id)));
|
|
10113
|
+
return sessions.filter((s) => ids.has(s.id));
|
|
10114
|
+
}
|
|
10115
|
+
function noteTime(note) {
|
|
10116
|
+
const parsed = Date.parse(note.at ?? "");
|
|
10117
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
10118
|
+
}
|
|
10119
|
+
async function emitKnowledgeReport(options = {}) {
|
|
10120
|
+
const cfg = loadConfig();
|
|
10121
|
+
const notes = [...options.notes ?? await (options.fetchNotes ?? fetchReportNotes)(cfg)].sort((a3, b2) => noteTime(a3) - noteTime(b2));
|
|
10122
|
+
const scanned = options.sessions ?? scanAgentSessions();
|
|
10123
|
+
const sessions = sessionsForNotes(notes, scanned);
|
|
10124
|
+
const candidates = attributeRediscovery(notes, sessions);
|
|
10125
|
+
const inventory = sessionsToInventory(sessions);
|
|
10126
|
+
const state = loadSyncState();
|
|
10127
|
+
if (inventory.totals && state.total_learning_tokens && !options.sessions) {
|
|
10128
|
+
inventory.totals.learning_tokens = state.total_learning_tokens;
|
|
10129
|
+
}
|
|
10130
|
+
const html = buildReportHtml({
|
|
10131
|
+
inventory,
|
|
10132
|
+
candidates,
|
|
10133
|
+
orgName: options.orgName ?? cfg.active_account?.target?.org_name ?? "Your team",
|
|
10134
|
+
repo: options.repo ?? notes.find((n2) => n2.repo)?.repo,
|
|
10135
|
+
branch: options.branch ?? notes.find((n2) => n2.branch)?.branch,
|
|
10136
|
+
generatedAt: options.generatedAt,
|
|
10137
|
+
digests: digestsForSessions(sessions)
|
|
10138
|
+
});
|
|
10139
|
+
return writeAndOpenReport({
|
|
10140
|
+
html,
|
|
10141
|
+
out: options.out ?? defaultReportPath(),
|
|
10142
|
+
open: options.open,
|
|
10143
|
+
openUrl: options.openUrl
|
|
10144
|
+
});
|
|
10145
|
+
}
|
|
10146
|
+
var init_generate = __esm(() => {
|
|
10147
|
+
init_config();
|
|
10148
|
+
init_scan();
|
|
10149
|
+
init_watermark();
|
|
10150
|
+
init_html();
|
|
10151
|
+
init_notes();
|
|
10152
|
+
init_write();
|
|
10153
|
+
});
|
|
10154
|
+
|
|
10155
|
+
// src/sessions/project-dir.ts
|
|
10156
|
+
import {
|
|
10157
|
+
closeSync as closeSync2,
|
|
10158
|
+
existsSync as existsSync11,
|
|
10159
|
+
mkdirSync as mkdirSync8,
|
|
10160
|
+
openSync as openSync2,
|
|
10161
|
+
readFileSync as readFileSync12,
|
|
10162
|
+
readSync,
|
|
10163
|
+
renameSync as renameSync3,
|
|
10164
|
+
statSync as statSync4,
|
|
10165
|
+
writeFileSync as writeFileSync8
|
|
10166
|
+
} from "node:fs";
|
|
10167
|
+
import { join as join11 } from "node:path";
|
|
10168
|
+
function cachePath(configDir) {
|
|
10169
|
+
return join11(configDir, CACHE_FILENAME2);
|
|
10170
|
+
}
|
|
10171
|
+
function loadCacheFile(configDir) {
|
|
10172
|
+
try {
|
|
10173
|
+
const raw = JSON.parse(readFileSync12(cachePath(configDir), "utf-8"));
|
|
10174
|
+
if (raw.schema_version !== CACHE_SCHEMA_VERSION || typeof raw.entries !== "object")
|
|
10175
|
+
return {};
|
|
10176
|
+
return raw.entries;
|
|
10177
|
+
} catch {
|
|
10178
|
+
return {};
|
|
10179
|
+
}
|
|
10180
|
+
}
|
|
10181
|
+
function saveCacheFile(configDir, entries) {
|
|
10182
|
+
try {
|
|
10183
|
+
if (!existsSync11(configDir))
|
|
10184
|
+
mkdirSync8(configDir, { recursive: true, mode: 448 });
|
|
10185
|
+
const path3 = cachePath(configDir);
|
|
10186
|
+
const tmp = `${path3}.${process.pid}.tmp`;
|
|
10187
|
+
const file = { schema_version: CACHE_SCHEMA_VERSION, entries };
|
|
10188
|
+
writeFileSync8(tmp, JSON.stringify(file), { mode: 384 });
|
|
10189
|
+
renameSync3(tmp, path3);
|
|
10190
|
+
} catch {}
|
|
10191
|
+
}
|
|
10192
|
+
function readHead(path3, bytes = HEAD_BYTES) {
|
|
10193
|
+
let fd;
|
|
10194
|
+
try {
|
|
10195
|
+
fd = openSync2(path3, "r");
|
|
10196
|
+
} catch {
|
|
10197
|
+
return null;
|
|
10198
|
+
}
|
|
10199
|
+
try {
|
|
10200
|
+
const buf = Buffer.alloc(bytes);
|
|
10201
|
+
const read = readSync(fd, buf, 0, bytes, 0);
|
|
10202
|
+
return buf.subarray(0, read).toString("utf-8");
|
|
10203
|
+
} catch {
|
|
10204
|
+
return null;
|
|
10205
|
+
} finally {
|
|
10206
|
+
closeSync2(fd);
|
|
10207
|
+
}
|
|
10208
|
+
}
|
|
10209
|
+
function cwdFromJsonlHead(text) {
|
|
10210
|
+
for (const line of text.split(`
|
|
10211
|
+
`).slice(0, HEAD_LINES)) {
|
|
10212
|
+
if (!line.includes('"cwd"'))
|
|
10213
|
+
continue;
|
|
10214
|
+
try {
|
|
10215
|
+
const obj = JSON.parse(line);
|
|
10216
|
+
const cwd = obj.cwd ?? obj.payload?.cwd;
|
|
10217
|
+
if (typeof cwd === "string" && cwd.startsWith("/"))
|
|
10218
|
+
return cwd;
|
|
10219
|
+
} catch {}
|
|
10220
|
+
}
|
|
10221
|
+
return null;
|
|
10222
|
+
}
|
|
10223
|
+
function unmungeSlug(slug, exists = existsSync11) {
|
|
10224
|
+
const tokens = slug.replace(/^-/, "").split("-");
|
|
10225
|
+
if (tokens.length === 0 || tokens[0] === "")
|
|
10226
|
+
return null;
|
|
10227
|
+
let budget = 5000;
|
|
10228
|
+
const walk = (prefix, index) => {
|
|
10229
|
+
if (budget-- <= 0)
|
|
10230
|
+
return null;
|
|
10231
|
+
if (index === tokens.length)
|
|
10232
|
+
return exists(prefix) ? prefix : null;
|
|
10233
|
+
const asSegment = prefix === "" || exists(prefix) ? walk(`${prefix}/${tokens[index]}`, index + 1) : null;
|
|
10234
|
+
if (asSegment)
|
|
10235
|
+
return asSegment;
|
|
10236
|
+
return prefix === "" ? null : walk(`${prefix}-${tokens[index]}`, index + 1);
|
|
10237
|
+
};
|
|
10238
|
+
return walk("", 0);
|
|
10239
|
+
}
|
|
10240
|
+
function fileMtime(path3) {
|
|
10241
|
+
try {
|
|
10242
|
+
return statSync4(path3).mtime.toISOString();
|
|
10243
|
+
} catch {
|
|
10244
|
+
return "";
|
|
10245
|
+
}
|
|
10246
|
+
}
|
|
10247
|
+
function createProjectDirResolver(configDir = getConfigDir(), deps = {}) {
|
|
10248
|
+
const exists = deps.exists ?? existsSync11;
|
|
10249
|
+
const head = deps.readHead ?? readHead;
|
|
10250
|
+
const mtime = deps.mtime ?? fileMtime;
|
|
10251
|
+
const entries = loadCacheFile(configDir);
|
|
10252
|
+
let dirty = false;
|
|
10253
|
+
const compute = (session) => {
|
|
10254
|
+
switch (session.harness) {
|
|
10255
|
+
case "opencode":
|
|
10256
|
+
return session.project ?? null;
|
|
10257
|
+
case "claude": {
|
|
10258
|
+
const text = head(session.path);
|
|
10259
|
+
const cwd = text ? cwdFromJsonlHead(text) : null;
|
|
10260
|
+
if (cwd)
|
|
10261
|
+
return cwd;
|
|
10262
|
+
return session.project ? unmungeSlug(session.project, exists) : null;
|
|
10263
|
+
}
|
|
10264
|
+
case "codex": {
|
|
10265
|
+
const text = head(session.path);
|
|
10266
|
+
return text ? cwdFromJsonlHead(text) : null;
|
|
10267
|
+
}
|
|
10268
|
+
case "cursor":
|
|
10269
|
+
return session.project ? unmungeSlug(session.project, exists) : null;
|
|
10270
|
+
default:
|
|
10271
|
+
return null;
|
|
10272
|
+
}
|
|
10273
|
+
};
|
|
10274
|
+
return {
|
|
10275
|
+
resolve(session) {
|
|
10276
|
+
const key = `${session.harness}/${session.id}`;
|
|
10277
|
+
const cached = entries[key];
|
|
10278
|
+
if (cached && (cached.dir !== null || cached.mtime === mtime(session.path))) {
|
|
10279
|
+
return cached.dir;
|
|
10280
|
+
}
|
|
10281
|
+
const dir = compute(session);
|
|
10282
|
+
entries[key] = { dir, mtime: mtime(session.path) };
|
|
10283
|
+
dirty = true;
|
|
10284
|
+
return dir;
|
|
10285
|
+
},
|
|
10286
|
+
flush() {
|
|
10287
|
+
if (!dirty)
|
|
10288
|
+
return;
|
|
10289
|
+
saveCacheFile(configDir, entries);
|
|
10290
|
+
dirty = false;
|
|
10291
|
+
}
|
|
10292
|
+
};
|
|
10293
|
+
}
|
|
10294
|
+
var CACHE_FILENAME2 = "project-dirs.json", CACHE_SCHEMA_VERSION = 1, HEAD_BYTES, HEAD_LINES = 50;
|
|
10295
|
+
var init_project_dir = __esm(() => {
|
|
10296
|
+
init_config();
|
|
10297
|
+
HEAD_BYTES = 128 * 1024;
|
|
10298
|
+
});
|
|
10299
|
+
|
|
8771
10300
|
// src/sync/backlog.ts
|
|
8772
10301
|
function listSessionBacklog() {
|
|
8773
10302
|
try {
|
|
@@ -8791,8 +10320,8 @@ var init_backlog = __esm(() => {
|
|
|
8791
10320
|
});
|
|
8792
10321
|
|
|
8793
10322
|
// src/sync/lock.ts
|
|
8794
|
-
import { existsSync as existsSync12, readFileSync as
|
|
8795
|
-
import { join as
|
|
10323
|
+
import { existsSync as existsSync12, readFileSync as readFileSync13, rmSync as rmSync2, statSync as statSync5, writeFileSync as writeFileSync9 } from "node:fs";
|
|
10324
|
+
import { join as join12 } from "node:path";
|
|
8796
10325
|
function processAlive(pid) {
|
|
8797
10326
|
try {
|
|
8798
10327
|
process.kill(pid, 0);
|
|
@@ -8802,7 +10331,7 @@ function processAlive(pid) {
|
|
|
8802
10331
|
}
|
|
8803
10332
|
}
|
|
8804
10333
|
function lockPath(configDir = getConfigDir()) {
|
|
8805
|
-
return
|
|
10334
|
+
return join12(configDir, LOCK_FILENAME);
|
|
8806
10335
|
}
|
|
8807
10336
|
function stopSyncRun(pid, configDir = getConfigDir(), kill = process.kill.bind(process)) {
|
|
8808
10337
|
if (!Number.isInteger(pid) || pid <= 0)
|
|
@@ -8819,7 +10348,7 @@ function stopSyncRun(pid, configDir = getConfigDir(), kill = process.kill.bind(p
|
|
|
8819
10348
|
}
|
|
8820
10349
|
try {
|
|
8821
10350
|
const path3 = lockPath(configDir);
|
|
8822
|
-
if (existsSync12(path3) &&
|
|
10351
|
+
if (existsSync12(path3) && readFileSync13(path3, "utf8") === String(pid)) {
|
|
8823
10352
|
rmSync2(path3, { force: true });
|
|
8824
10353
|
}
|
|
8825
10354
|
} catch {}
|
|
@@ -8830,7 +10359,7 @@ function fileLock(configDir = getConfigDir(), now = () => new Date) {
|
|
|
8830
10359
|
let held = false;
|
|
8831
10360
|
const tryCreate = () => {
|
|
8832
10361
|
try {
|
|
8833
|
-
|
|
10362
|
+
writeFileSync9(path3, String(process.pid), { flag: "wx" });
|
|
8834
10363
|
return true;
|
|
8835
10364
|
} catch {
|
|
8836
10365
|
return false;
|
|
@@ -8844,7 +10373,7 @@ function fileLock(configDir = getConfigDir(), now = () => new Date) {
|
|
|
8844
10373
|
}
|
|
8845
10374
|
try {
|
|
8846
10375
|
const age = now().getTime() - statSync5(path3).mtimeMs;
|
|
8847
|
-
const holder = Number.parseInt(
|
|
10376
|
+
const holder = Number.parseInt(readFileSync13(path3, "utf8"), 10);
|
|
8848
10377
|
const holderDead = Number.isInteger(holder) && holder > 0 && !processAlive(holder);
|
|
8849
10378
|
if (age > STALE_LOCK_MS || holderDead) {
|
|
8850
10379
|
rmSync2(path3, { force: true });
|
|
@@ -8859,7 +10388,7 @@ function fileLock(configDir = getConfigDir(), now = () => new Date) {
|
|
|
8859
10388
|
return;
|
|
8860
10389
|
held = false;
|
|
8861
10390
|
try {
|
|
8862
|
-
if (existsSync12(path3) &&
|
|
10391
|
+
if (existsSync12(path3) && readFileSync13(path3, "utf8") === String(process.pid)) {
|
|
8863
10392
|
rmSync2(path3, { force: true });
|
|
8864
10393
|
}
|
|
8865
10394
|
} catch {}
|
|
@@ -8873,7 +10402,7 @@ var init_lock = __esm(() => {
|
|
|
8873
10402
|
});
|
|
8874
10403
|
|
|
8875
10404
|
// src/sync/status.ts
|
|
8876
|
-
import { readFileSync as
|
|
10405
|
+
import { readFileSync as readFileSync14, statSync as statSync6 } from "node:fs";
|
|
8877
10406
|
function formatTokenCount(tokens) {
|
|
8878
10407
|
if (tokens < 1000)
|
|
8879
10408
|
return String(tokens);
|
|
@@ -8896,7 +10425,7 @@ function defaultIsProcessAlive2(pid) {
|
|
|
8896
10425
|
}
|
|
8897
10426
|
function defaultReadLog() {
|
|
8898
10427
|
try {
|
|
8899
|
-
return
|
|
10428
|
+
return readFileSync14(logger.getLogPath(), "utf-8");
|
|
8900
10429
|
} catch {
|
|
8901
10430
|
return "";
|
|
8902
10431
|
}
|
|
@@ -8904,7 +10433,7 @@ function defaultReadLog() {
|
|
|
8904
10433
|
function readLockFile(configDir) {
|
|
8905
10434
|
const path3 = lockPath(configDir);
|
|
8906
10435
|
try {
|
|
8907
|
-
const pid = Number.parseInt(
|
|
10436
|
+
const pid = Number.parseInt(readFileSync14(path3, "utf-8"), 10);
|
|
8908
10437
|
return { pid: Number.isNaN(pid) ? -1 : pid, mtime: statSync6(path3).mtime };
|
|
8909
10438
|
} catch {
|
|
8910
10439
|
return null;
|
|
@@ -8939,160 +10468,6 @@ var init_status = __esm(() => {
|
|
|
8939
10468
|
init_watermark();
|
|
8940
10469
|
});
|
|
8941
10470
|
|
|
8942
|
-
// src/sessions/read.ts
|
|
8943
|
-
import { readFileSync as readFileSync13 } from "node:fs";
|
|
8944
|
-
function asRecord(value) {
|
|
8945
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
8946
|
-
}
|
|
8947
|
-
function jsonlRecords(raw) {
|
|
8948
|
-
const records = [];
|
|
8949
|
-
for (const line of raw.split(`
|
|
8950
|
-
`)) {
|
|
8951
|
-
if (line.trim() === "")
|
|
8952
|
-
continue;
|
|
8953
|
-
try {
|
|
8954
|
-
const record = asRecord(JSON.parse(line));
|
|
8955
|
-
if (record)
|
|
8956
|
-
records.push(record);
|
|
8957
|
-
} catch {}
|
|
8958
|
-
}
|
|
8959
|
-
return records;
|
|
8960
|
-
}
|
|
8961
|
-
function textFromContent(content, textTypes) {
|
|
8962
|
-
if (typeof content === "string")
|
|
8963
|
-
return content;
|
|
8964
|
-
if (!Array.isArray(content))
|
|
8965
|
-
return "";
|
|
8966
|
-
const parts = [];
|
|
8967
|
-
for (const entry of content) {
|
|
8968
|
-
const item = asRecord(entry);
|
|
8969
|
-
if (!item)
|
|
8970
|
-
continue;
|
|
8971
|
-
if (typeof item.type === "string" && textTypes.has(item.type) && typeof item.text === "string") {
|
|
8972
|
-
parts.push(item.text);
|
|
8973
|
-
}
|
|
8974
|
-
}
|
|
8975
|
-
return parts.join(`
|
|
8976
|
-
`);
|
|
8977
|
-
}
|
|
8978
|
-
function pushTurn(turns, role, text) {
|
|
8979
|
-
if (text.trim() !== "")
|
|
8980
|
-
turns.push({ role, text });
|
|
8981
|
-
}
|
|
8982
|
-
function readClaude(raw) {
|
|
8983
|
-
const turns = [];
|
|
8984
|
-
for (const record of jsonlRecords(raw)) {
|
|
8985
|
-
const role = record.type;
|
|
8986
|
-
if (role !== "user" && role !== "assistant")
|
|
8987
|
-
continue;
|
|
8988
|
-
if (record.isSidechain === true)
|
|
8989
|
-
continue;
|
|
8990
|
-
const message = asRecord(record.message);
|
|
8991
|
-
if (!message)
|
|
8992
|
-
continue;
|
|
8993
|
-
pushTurn(turns, role, textFromContent(message.content, PLAIN_TEXT));
|
|
8994
|
-
}
|
|
8995
|
-
return turns;
|
|
8996
|
-
}
|
|
8997
|
-
function readCursor(raw) {
|
|
8998
|
-
const turns = [];
|
|
8999
|
-
for (const record of jsonlRecords(raw)) {
|
|
9000
|
-
const role = record.role;
|
|
9001
|
-
if (role !== "user" && role !== "assistant")
|
|
9002
|
-
continue;
|
|
9003
|
-
const message = asRecord(record.message);
|
|
9004
|
-
if (!message)
|
|
9005
|
-
continue;
|
|
9006
|
-
pushTurn(turns, role, textFromContent(message.content, PLAIN_TEXT));
|
|
9007
|
-
}
|
|
9008
|
-
return turns;
|
|
9009
|
-
}
|
|
9010
|
-
function isCodexInjectedBlock(text) {
|
|
9011
|
-
const head = text.trimStart().toLowerCase();
|
|
9012
|
-
return head.startsWith("<user_instructions>") || head.startsWith("<environment_context>") || head.startsWith("<recommended_plugins>");
|
|
9013
|
-
}
|
|
9014
|
-
function readCodex(raw) {
|
|
9015
|
-
const turns = [];
|
|
9016
|
-
for (const record of jsonlRecords(raw)) {
|
|
9017
|
-
if (record.type !== "response_item")
|
|
9018
|
-
continue;
|
|
9019
|
-
const payload = asRecord(record.payload);
|
|
9020
|
-
if (payload?.type !== "message")
|
|
9021
|
-
continue;
|
|
9022
|
-
const role = payload.role;
|
|
9023
|
-
if (role !== "user" && role !== "assistant")
|
|
9024
|
-
continue;
|
|
9025
|
-
const text = textFromContent(payload.content, CODEX_TEXT);
|
|
9026
|
-
if (role === "user" && isCodexInjectedBlock(text))
|
|
9027
|
-
continue;
|
|
9028
|
-
pushTurn(turns, role, text);
|
|
9029
|
-
}
|
|
9030
|
-
return turns;
|
|
9031
|
-
}
|
|
9032
|
-
function readOpencode(dbPath, sessionId) {
|
|
9033
|
-
if (!/^[A-Za-z0-9_-]+$/.test(sessionId))
|
|
9034
|
-
return [];
|
|
9035
|
-
const rows = querySqlite(dbPath, "SELECT m.data AS message, p.data AS part FROM part p " + "JOIN message m ON m.id = p.message_id " + `WHERE p.session_id = '${sessionId}' ORDER BY p.time_created, p.id`);
|
|
9036
|
-
if (!rows)
|
|
9037
|
-
return [];
|
|
9038
|
-
const turns = [];
|
|
9039
|
-
for (const row of rows) {
|
|
9040
|
-
if (typeof row.message !== "string" || typeof row.part !== "string")
|
|
9041
|
-
continue;
|
|
9042
|
-
try {
|
|
9043
|
-
const message = asRecord(JSON.parse(row.message));
|
|
9044
|
-
const part = asRecord(JSON.parse(row.part));
|
|
9045
|
-
const role = message?.role;
|
|
9046
|
-
if (role !== "user" && role !== "assistant")
|
|
9047
|
-
continue;
|
|
9048
|
-
if (part?.type !== "text" || typeof part.text !== "string")
|
|
9049
|
-
continue;
|
|
9050
|
-
pushTurn(turns, role, part.text);
|
|
9051
|
-
} catch {}
|
|
9052
|
-
}
|
|
9053
|
-
return turns;
|
|
9054
|
-
}
|
|
9055
|
-
function readSessionTurns(session) {
|
|
9056
|
-
try {
|
|
9057
|
-
switch (session.harness) {
|
|
9058
|
-
case "claude":
|
|
9059
|
-
return readClaude(readFileSync13(session.path, "utf8"));
|
|
9060
|
-
case "cursor":
|
|
9061
|
-
return readCursor(readFileSync13(session.path, "utf8"));
|
|
9062
|
-
case "codex":
|
|
9063
|
-
return readCodex(readFileSync13(session.path, "utf8"));
|
|
9064
|
-
case "opencode":
|
|
9065
|
-
return readOpencode(session.path, session.id);
|
|
9066
|
-
}
|
|
9067
|
-
} catch {
|
|
9068
|
-
return [];
|
|
9069
|
-
}
|
|
9070
|
-
}
|
|
9071
|
-
function estimateSessionTokens(session) {
|
|
9072
|
-
let chars = 0;
|
|
9073
|
-
for (const turn of readSessionTurns(session))
|
|
9074
|
-
chars += turn.text.length;
|
|
9075
|
-
return Math.round(chars / CHARS_PER_TOKEN);
|
|
9076
|
-
}
|
|
9077
|
-
function isWorthMining(session) {
|
|
9078
|
-
const turns = readSessionTurns(session);
|
|
9079
|
-
if (turns.length < MIN_WORTH_TURNS)
|
|
9080
|
-
return false;
|
|
9081
|
-
let chars = 0;
|
|
9082
|
-
for (const turn of turns) {
|
|
9083
|
-
chars += turn.text.length;
|
|
9084
|
-
if (chars >= MIN_WORTH_CHARS)
|
|
9085
|
-
return true;
|
|
9086
|
-
}
|
|
9087
|
-
return false;
|
|
9088
|
-
}
|
|
9089
|
-
var PLAIN_TEXT, CODEX_TEXT, CHARS_PER_TOKEN = 4, MIN_WORTH_TURNS = 4, MIN_WORTH_CHARS = 2000;
|
|
9090
|
-
var init_read = __esm(() => {
|
|
9091
|
-
init_scan();
|
|
9092
|
-
PLAIN_TEXT = new Set(["text"]);
|
|
9093
|
-
CODEX_TEXT = new Set(["input_text", "output_text"]);
|
|
9094
|
-
});
|
|
9095
|
-
|
|
9096
10471
|
// src/sync/sync.ts
|
|
9097
10472
|
function logGateResult(ready, inFlight, watermark) {
|
|
9098
10473
|
const preview = ready.slice(0, LOG_PREVIEW_LIMIT).map((s) => `${s.harness}/${s.id}`).join(", ");
|
|
@@ -9136,14 +10511,14 @@ async function runKnowledgeSync(options = {}) {
|
|
|
9136
10511
|
}));
|
|
9137
10512
|
let sessions = await listSessions();
|
|
9138
10513
|
if (state.project_filter?.length) {
|
|
9139
|
-
let
|
|
10514
|
+
let resolve2 = deps.resolveProjectDir;
|
|
9140
10515
|
let flush;
|
|
9141
|
-
if (!
|
|
10516
|
+
if (!resolve2) {
|
|
9142
10517
|
const resolver = createProjectDirResolver();
|
|
9143
|
-
|
|
10518
|
+
resolve2 = resolver.resolve;
|
|
9144
10519
|
flush = resolver.flush;
|
|
9145
10520
|
}
|
|
9146
|
-
sessions = filterSessionsByProject(sessions, state.project_filter,
|
|
10521
|
+
sessions = filterSessionsByProject(sessions, state.project_filter, resolve2);
|
|
9147
10522
|
flush?.();
|
|
9148
10523
|
logger.debug("sync", `project filter active: ${state.project_filter.join(", ")}`);
|
|
9149
10524
|
}
|
|
@@ -9303,13 +10678,141 @@ var init_sync = __esm(() => {
|
|
|
9303
10678
|
init_watermark();
|
|
9304
10679
|
});
|
|
9305
10680
|
|
|
10681
|
+
// src/report/backfill.ts
|
|
10682
|
+
function noteTime2(at) {
|
|
10683
|
+
const parsed = Date.parse(at ?? "");
|
|
10684
|
+
return Number.isNaN(parsed) ? Number.NaN : parsed;
|
|
10685
|
+
}
|
|
10686
|
+
function correlateBackfill(input) {
|
|
10687
|
+
const batches = new Map;
|
|
10688
|
+
for (const e of input.ledger) {
|
|
10689
|
+
const members = batches.get(e.at) ?? [];
|
|
10690
|
+
members.push(e.session);
|
|
10691
|
+
batches.set(e.at, members);
|
|
10692
|
+
}
|
|
10693
|
+
const batchTimes = [...batches.keys()].map((at) => ({ at, t: Date.parse(at) })).filter((b2) => !Number.isNaN(b2.t));
|
|
10694
|
+
const sessionByKey = new Map(input.sessions.map((s) => [`${s.harness}/${s.id}`, s]));
|
|
10695
|
+
const cyclesCache = new Map;
|
|
10696
|
+
const cyclesFor = (session) => {
|
|
10697
|
+
let cycles = cyclesCache.get(session.id);
|
|
10698
|
+
if (!cycles) {
|
|
10699
|
+
cycles = sessionCycles(sessionToDigest(session).turns);
|
|
10700
|
+
cyclesCache.set(session.id, cycles);
|
|
10701
|
+
}
|
|
10702
|
+
return cycles;
|
|
10703
|
+
};
|
|
10704
|
+
const mappings = [];
|
|
10705
|
+
let ambiguous = 0;
|
|
10706
|
+
let noBatch = 0;
|
|
10707
|
+
for (const note of input.notes) {
|
|
10708
|
+
const time = noteTime2(note.at);
|
|
10709
|
+
if (Number.isNaN(time)) {
|
|
10710
|
+
noBatch += 1;
|
|
10711
|
+
continue;
|
|
10712
|
+
}
|
|
10713
|
+
let batchAt;
|
|
10714
|
+
let bestD = Number.POSITIVE_INFINITY;
|
|
10715
|
+
for (const b2 of batchTimes) {
|
|
10716
|
+
const d = Math.abs(b2.t - time);
|
|
10717
|
+
if (d <= BATCH_TOLERANCE_MS && d < bestD) {
|
|
10718
|
+
bestD = d;
|
|
10719
|
+
batchAt = b2.at;
|
|
10720
|
+
}
|
|
10721
|
+
}
|
|
10722
|
+
if (!batchAt) {
|
|
10723
|
+
noBatch += 1;
|
|
10724
|
+
continue;
|
|
10725
|
+
}
|
|
10726
|
+
const members = (batches.get(batchAt) ?? []).map((k) => sessionByKey.get(k)).filter((s) => s !== undefined);
|
|
10727
|
+
if (members.length === 0) {
|
|
10728
|
+
noBatch += 1;
|
|
10729
|
+
continue;
|
|
10730
|
+
}
|
|
10731
|
+
const words = noteWords(note);
|
|
10732
|
+
let best;
|
|
10733
|
+
let second = 0;
|
|
10734
|
+
for (const session of members) {
|
|
10735
|
+
let score = 0;
|
|
10736
|
+
for (const cycle of cyclesFor(session)) {
|
|
10737
|
+
const s = cycleMatchScore(words, cycle.text);
|
|
10738
|
+
if (s > score)
|
|
10739
|
+
score = s;
|
|
10740
|
+
}
|
|
10741
|
+
if (!best || score > best.score) {
|
|
10742
|
+
second = best?.score ?? 0;
|
|
10743
|
+
best = { session, score };
|
|
10744
|
+
} else if (score > second) {
|
|
10745
|
+
second = score;
|
|
10746
|
+
}
|
|
10747
|
+
}
|
|
10748
|
+
if (!best || best.score < MIN_SCORE || second > 0 && best.score < second * CLEAR_WIN_RATIO) {
|
|
10749
|
+
ambiguous += 1;
|
|
10750
|
+
continue;
|
|
10751
|
+
}
|
|
10752
|
+
mappings.push({ note_id: note.id, transcript_id: best.session.id });
|
|
10753
|
+
}
|
|
10754
|
+
return { mappings, ambiguous, noBatch };
|
|
10755
|
+
}
|
|
10756
|
+
var BATCH_TOLERANCE_MS, CLEAR_WIN_RATIO = 1.5, MIN_SCORE = 3;
|
|
10757
|
+
var init_backfill = __esm(() => {
|
|
10758
|
+
init_digest();
|
|
10759
|
+
init_notes();
|
|
10760
|
+
BATCH_TOLERANCE_MS = 30 * 60 * 1000;
|
|
10761
|
+
});
|
|
10762
|
+
|
|
10763
|
+
// src/report/backfill-run.ts
|
|
10764
|
+
var exports_backfill_run = {};
|
|
10765
|
+
__export(exports_backfill_run, {
|
|
10766
|
+
runBackfill: () => runBackfill
|
|
10767
|
+
});
|
|
10768
|
+
async function defaultFetchUnattributed(cfg) {
|
|
10769
|
+
const orgId = cfg.active_account?.target?.org_id;
|
|
10770
|
+
if (!orgId || !cfg.active_account?.session.access_token) {
|
|
10771
|
+
throw new Error("Not signed in. Run `dosu setup` to connect an account first.");
|
|
10772
|
+
}
|
|
10773
|
+
const { createTypedClient: createTypedClient2 } = await Promise.resolve().then(() => (init_trpc(), exports_trpc));
|
|
10774
|
+
const client = createTypedClient2(cfg);
|
|
10775
|
+
const { notes } = await client.notes.listMine.query({ org_id: orgId, limit: 500 });
|
|
10776
|
+
return notes.filter((n2) => !n2.transcript_id).map((n2) => ({ id: n2.id, title: n2.title, content: n2.body, at: n2.created_at }));
|
|
10777
|
+
}
|
|
10778
|
+
async function defaultApply(cfg, mappings) {
|
|
10779
|
+
const { createTypedClient: createTypedClient2 } = await Promise.resolve().then(() => (init_trpc(), exports_trpc));
|
|
10780
|
+
const client = createTypedClient2(cfg);
|
|
10781
|
+
let updated = 0;
|
|
10782
|
+
for (let i2 = 0;i2 < mappings.length; i2 += MUTATION_CHUNK) {
|
|
10783
|
+
const chunk = mappings.slice(i2, i2 + MUTATION_CHUNK);
|
|
10784
|
+
const res = await client.notes.attributeTranscripts.mutate({ mappings: chunk });
|
|
10785
|
+
updated += res.updated;
|
|
10786
|
+
}
|
|
10787
|
+
return updated;
|
|
10788
|
+
}
|
|
10789
|
+
async function runBackfill(deps = {}) {
|
|
10790
|
+
const cfg = (deps.loadCfg ?? loadConfig)();
|
|
10791
|
+
const notes = await (deps.fetchUnattributed ?? defaultFetchUnattributed)(cfg);
|
|
10792
|
+
if (notes.length === 0) {
|
|
10793
|
+
return { candidates: 0, mappings: [], ambiguous: 0, noBatch: 0, updated: 0 };
|
|
10794
|
+
}
|
|
10795
|
+
const ledger = (deps.ledger ?? (() => loadSyncState().mined_sessions ?? []))();
|
|
10796
|
+
const sessions = (deps.scan ?? scanAgentSessions)();
|
|
10797
|
+
const { mappings, ambiguous, noBatch } = correlateBackfill({ notes, ledger, sessions });
|
|
10798
|
+
const updated = mappings.length > 0 ? await (deps.apply ?? defaultApply)(cfg, mappings) : 0;
|
|
10799
|
+
return { candidates: notes.length, mappings, ambiguous, noBatch, updated };
|
|
10800
|
+
}
|
|
10801
|
+
var MUTATION_CHUNK = 500;
|
|
10802
|
+
var init_backfill_run = __esm(() => {
|
|
10803
|
+
init_config();
|
|
10804
|
+
init_scan();
|
|
10805
|
+
init_watermark();
|
|
10806
|
+
init_backfill();
|
|
10807
|
+
});
|
|
10808
|
+
|
|
9306
10809
|
// src/miner/config-dir.ts
|
|
9307
|
-
import { mkdtempSync, rmSync as rmSync3, writeFileSync as
|
|
9308
|
-
import { tmpdir } from "node:os";
|
|
9309
|
-
import { join as
|
|
10810
|
+
import { mkdtempSync, rmSync as rmSync3, writeFileSync as writeFileSync10 } from "node:fs";
|
|
10811
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
10812
|
+
import { join as join13 } from "node:path";
|
|
9310
10813
|
function createRunConfigDir() {
|
|
9311
|
-
const path3 = mkdtempSync(
|
|
9312
|
-
|
|
10814
|
+
const path3 = mkdtempSync(join13(tmpdir2(), "dosu-miner-"));
|
|
10815
|
+
writeFileSync10(join13(path3, ".claude.json"), JSON.stringify({ hasCompletedOnboarding: true }));
|
|
9313
10816
|
return {
|
|
9314
10817
|
path: path3,
|
|
9315
10818
|
cleanup() {
|
|
@@ -9320,7 +10823,7 @@ function createRunConfigDir() {
|
|
|
9320
10823
|
var init_config_dir = () => {};
|
|
9321
10824
|
|
|
9322
10825
|
// src/miner/conflicts.ts
|
|
9323
|
-
import { existsSync as existsSync13, readFileSync as
|
|
10826
|
+
import { existsSync as existsSync13, readFileSync as readFileSync15 } from "node:fs";
|
|
9324
10827
|
function managedSettingsPaths(platform3 = process.platform) {
|
|
9325
10828
|
switch (platform3) {
|
|
9326
10829
|
case "darwin":
|
|
@@ -9353,7 +10856,7 @@ function detectSettingsConflicts(paths = managedSettingsPaths()) {
|
|
|
9353
10856
|
continue;
|
|
9354
10857
|
let settings2;
|
|
9355
10858
|
try {
|
|
9356
|
-
settings2 = JSON.parse(
|
|
10859
|
+
settings2 = JSON.parse(readFileSync15(file, "utf8"));
|
|
9357
10860
|
} catch {
|
|
9358
10861
|
conflicts.push({ file, keys: ["<unreadable or invalid JSON>"] });
|
|
9359
10862
|
continue;
|
|
@@ -9415,7 +10918,7 @@ var init_env = __esm(() => {
|
|
|
9415
10918
|
import { accessSync, constants, existsSync as existsSync14 } from "node:fs";
|
|
9416
10919
|
import { createRequire as createRequire3 } from "node:module";
|
|
9417
10920
|
import { homedir as homedir4 } from "node:os";
|
|
9418
|
-
import { delimiter as delimiter2, dirname as
|
|
10921
|
+
import { delimiter as delimiter2, dirname as dirname5, join as join14 } from "node:path";
|
|
9419
10922
|
function binaryNames(platform3 = process.platform) {
|
|
9420
10923
|
return platform3 === "win32" ? ["claude.exe", "claude.cmd"] : ["claude"];
|
|
9421
10924
|
}
|
|
@@ -9423,8 +10926,8 @@ function sdkNativeBinaryExists() {
|
|
|
9423
10926
|
try {
|
|
9424
10927
|
const requireRuntime = createRequire3(import.meta.url);
|
|
9425
10928
|
const pkg = `@anthropic-ai/claude-agent-sdk-${process.platform}-${process.arch}`;
|
|
9426
|
-
const pkgDir =
|
|
9427
|
-
return binaryNames().some((name) => existsSync14(
|
|
10929
|
+
const pkgDir = dirname5(requireRuntime.resolve(`${pkg}/package.json`));
|
|
10930
|
+
return binaryNames().some((name) => existsSync14(join14(pkgDir, name)));
|
|
9428
10931
|
} catch {
|
|
9429
10932
|
return false;
|
|
9430
10933
|
}
|
|
@@ -9440,10 +10943,10 @@ function isExecutable(path3) {
|
|
|
9440
10943
|
function findSystemClaude(env = process.env, homeDir = homedir4()) {
|
|
9441
10944
|
const names = binaryNames();
|
|
9442
10945
|
const dirs = (env.PATH ?? "").split(delimiter2).filter(Boolean);
|
|
9443
|
-
dirs.push(
|
|
10946
|
+
dirs.push(join14(homeDir, ".local", "bin"), join14(homeDir, ".claude", "local"));
|
|
9444
10947
|
for (const dir of dirs) {
|
|
9445
10948
|
for (const name of names) {
|
|
9446
|
-
const candidate =
|
|
10949
|
+
const candidate = join14(dir, name);
|
|
9447
10950
|
if (isExecutable(candidate))
|
|
9448
10951
|
return candidate;
|
|
9449
10952
|
}
|
|
@@ -52091,7 +53594,7 @@ __export(exports_util, {
|
|
|
52091
53594
|
extend: () => extend,
|
|
52092
53595
|
explicitlyAborted: () => explicitlyAborted,
|
|
52093
53596
|
escapeRegex: () => escapeRegex,
|
|
52094
|
-
esc: () =>
|
|
53597
|
+
esc: () => esc2,
|
|
52095
53598
|
defineLazyInternal: () => defineLazyInternal,
|
|
52096
53599
|
defineLazy: () => defineLazy,
|
|
52097
53600
|
createTransparentProxy: () => createTransparentProxy,
|
|
@@ -52242,7 +53745,7 @@ function randomString(length = 10) {
|
|
|
52242
53745
|
}
|
|
52243
53746
|
return str;
|
|
52244
53747
|
}
|
|
52245
|
-
function
|
|
53748
|
+
function esc2(str) {
|
|
52246
53749
|
return JSON.stringify(str);
|
|
52247
53750
|
}
|
|
52248
53751
|
function slugify(input) {
|
|
@@ -55363,7 +56866,7 @@ var init_schemas = __esm(() => {
|
|
|
55363
56866
|
if (key === "__proto__")
|
|
55364
56867
|
continue;
|
|
55365
56868
|
const id2 = ids[key];
|
|
55366
|
-
const k2 = typeof key === "symbol" ? `syms[${syms.indexOf(key)}]` :
|
|
56869
|
+
const k2 = typeof key === "symbol" ? `syms[${syms.indexOf(key)}]` : esc2(key);
|
|
55367
56870
|
const isPresent = `${k2} in input`;
|
|
55368
56871
|
const schema = shape[key];
|
|
55369
56872
|
const optin = schema?._zod?.optin;
|
|
@@ -64600,16 +66103,16 @@ function generateStringFormatCheck(doc, ctx, def, accessor) {
|
|
|
64600
66103
|
doc.write(`if (${accessor} !== ${accessor}.toUpperCase()) return INVALID;`);
|
|
64601
66104
|
break;
|
|
64602
66105
|
case "includes":
|
|
64603
|
-
doc.write(`if (!${accessor}.includes(${
|
|
66106
|
+
doc.write(`if (!${accessor}.includes(${esc2(def.includes)})) return INVALID;`);
|
|
64604
66107
|
break;
|
|
64605
66108
|
case "starts_with": {
|
|
64606
66109
|
const prefix = def.prefix;
|
|
64607
|
-
doc.write(`if (${accessor}.slice(0, ${prefix.length}) !== ${
|
|
66110
|
+
doc.write(`if (${accessor}.slice(0, ${prefix.length}) !== ${esc2(prefix)}) return INVALID;`);
|
|
64608
66111
|
break;
|
|
64609
66112
|
}
|
|
64610
66113
|
case "ends_with": {
|
|
64611
66114
|
const suffix = def.suffix;
|
|
64612
|
-
doc.write(`if (${accessor}.slice(-${suffix.length}) !== ${
|
|
66115
|
+
doc.write(`if (${accessor}.slice(-${suffix.length}) !== ${esc2(suffix)}) return INVALID;`);
|
|
64613
66116
|
break;
|
|
64614
66117
|
}
|
|
64615
66118
|
default: {
|
|
@@ -64813,8 +66316,8 @@ function generateObjectCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
64813
66316
|
const keys = Object.keys(shape);
|
|
64814
66317
|
const symbolKeys = Object.getOwnPropertySymbols(shape);
|
|
64815
66318
|
const allKeys = symbolKeys.length ? [...keys, ...symbolKeys] : keys;
|
|
64816
|
-
const keyExpr = (k2) => typeof k2 === "symbol" ? addConstant(ctx, k2) :
|
|
64817
|
-
const propKey = (k2) => typeof k2 === "symbol" ? `[${keyExpr(k2)}]` :
|
|
66319
|
+
const keyExpr = (k2) => typeof k2 === "symbol" ? addConstant(ctx, k2) : esc2(k2);
|
|
66320
|
+
const propKey = (k2) => typeof k2 === "symbol" ? `[${keyExpr(k2)}]` : esc2(k2);
|
|
64818
66321
|
const propShape = shape;
|
|
64819
66322
|
if (keys.includes("__proto__")) {
|
|
64820
66323
|
throw new ZodCompileUnsupportedError('object shape key "__proto__"');
|
|
@@ -64858,7 +66361,7 @@ function generateObjectCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
64858
66361
|
if (catchall) {
|
|
64859
66362
|
const catchallType = catchall._zod.def.type;
|
|
64860
66363
|
if (catchallType === "never") {
|
|
64861
|
-
const condition = keys.map((k2) => `k !== ${
|
|
66364
|
+
const condition = keys.map((k2) => `k !== ${esc2(k2)}`).join(" && ") || "true";
|
|
64862
66365
|
doc.write(`for (const k in ${accessor}) {`);
|
|
64863
66366
|
doc.indented((d3) => {
|
|
64864
66367
|
d3.write(`if (${condition}) return INVALID;`);
|
|
@@ -65120,7 +66623,7 @@ function generateLiteralCheck(doc, ctx, schema, accessor) {
|
|
|
65120
66623
|
return accessor;
|
|
65121
66624
|
}
|
|
65122
66625
|
if (typeof value === "string") {
|
|
65123
|
-
doc.write(`if (${accessor} !== ${
|
|
66626
|
+
doc.write(`if (${accessor} !== ${esc2(value)}) return INVALID;`);
|
|
65124
66627
|
} else if (typeof value === "number" || typeof value === "boolean") {
|
|
65125
66628
|
doc.write(`if (${accessor} !== ${value}) return INVALID;`);
|
|
65126
66629
|
} else if (value === null) {
|
|
@@ -65321,7 +66824,7 @@ function generateDiscriminatedUnionCheck(doc, ctx, def, accessor) {
|
|
|
65321
66824
|
}
|
|
65322
66825
|
const discVar = newVar(ctx);
|
|
65323
66826
|
const outputVar = newVar(ctx);
|
|
65324
|
-
doc.write(`const ${discVar} = ${accessor}?.[${
|
|
66827
|
+
doc.write(`const ${discVar} = ${accessor}?.[${esc2(def.discriminator)}];`);
|
|
65325
66828
|
doc.write(`let ${outputVar};`);
|
|
65326
66829
|
let firstBranch = true;
|
|
65327
66830
|
const claimed = new Set;
|
|
@@ -65351,7 +66854,7 @@ function generateDiscriminatedUnionCheck(doc, ctx, def, accessor) {
|
|
|
65351
66854
|
}
|
|
65352
66855
|
function literalEquality(ctx, accessor, value) {
|
|
65353
66856
|
if (typeof value === "string")
|
|
65354
|
-
return `${accessor} === ${
|
|
66857
|
+
return `${accessor} === ${esc2(value)}`;
|
|
65355
66858
|
if (typeof value === "number") {
|
|
65356
66859
|
if (Number.isNaN(value))
|
|
65357
66860
|
return `Number.isNaN(${accessor})`;
|
|
@@ -65465,7 +66968,7 @@ function generateRecordCheck(doc, ctx, schema, accessor) {
|
|
|
65465
66968
|
}
|
|
65466
66969
|
function literalPropertyKey(ctx, key) {
|
|
65467
66970
|
if (typeof key === "string")
|
|
65468
|
-
return
|
|
66971
|
+
return esc2(key);
|
|
65469
66972
|
return addConstant(ctx, key);
|
|
65470
66973
|
}
|
|
65471
66974
|
function generateMapCheck(doc, ctx, schema, accessor) {
|
|
@@ -71401,6 +72904,15 @@ async function runMiner(options) {
|
|
|
71401
72904
|
message: `Refusing to run: conflicting Claude Code settings would override the miner's auth (${detail})`
|
|
71402
72905
|
};
|
|
71403
72906
|
}
|
|
72907
|
+
const gatewayURL = options.gatewayURL ?? getLlmGatewayURL();
|
|
72908
|
+
if (!isAbsoluteHttpUrl(gatewayURL)) {
|
|
72909
|
+
return {
|
|
72910
|
+
outcome: "error",
|
|
72911
|
+
notesWritten: 0,
|
|
72912
|
+
turns: 0,
|
|
72913
|
+
message: "LLM gateway URL is not set. From source, use `bun run dev` (production endpoints) or `bun run dev:local` (local stack)."
|
|
72914
|
+
};
|
|
72915
|
+
}
|
|
71404
72916
|
const { query } = await Promise.resolve().then(() => (init_sdk(), exports_sdk));
|
|
71405
72917
|
const claudeExecutable = resolveClaudeExecutable();
|
|
71406
72918
|
if (claudeExecutable) {
|
|
@@ -71414,10 +72926,11 @@ async function runMiner(options) {
|
|
|
71414
72926
|
const abort = new AbortController;
|
|
71415
72927
|
const timer = setTimeout(() => abort.abort(), options.timeoutMs ?? DEFAULT_TIMEOUT_MS2);
|
|
71416
72928
|
let notesWritten = 0;
|
|
72929
|
+
let lastSessionId;
|
|
71417
72930
|
let turns = 0;
|
|
71418
72931
|
const env = buildMinerEnv({
|
|
71419
72932
|
apiKey: options.apiKey,
|
|
71420
|
-
gatewayURL
|
|
72933
|
+
gatewayURL,
|
|
71421
72934
|
configDir: configDir.path,
|
|
71422
72935
|
runID,
|
|
71423
72936
|
trigger: options.trigger,
|
|
@@ -71457,6 +72970,9 @@ async function runMiner(options) {
|
|
|
71457
72970
|
message: `Tool ${toolName} is not permitted in mining runs.`
|
|
71458
72971
|
};
|
|
71459
72972
|
}
|
|
72973
|
+
if (toolName === `mcp__${SESSIONS_SERVER_NAME}__read_session`) {
|
|
72974
|
+
lastSessionId = sessionIdFromReadInput(input2) ?? lastSessionId;
|
|
72975
|
+
}
|
|
71460
72976
|
if (toolName === `mcp__${KNOWLEDGE_SERVER_NAME}__write_knowledge`) {
|
|
71461
72977
|
if (notesWritten >= maxNotes) {
|
|
71462
72978
|
return {
|
|
@@ -71465,6 +72981,10 @@ async function runMiner(options) {
|
|
|
71465
72981
|
};
|
|
71466
72982
|
}
|
|
71467
72983
|
notesWritten += 1;
|
|
72984
|
+
return {
|
|
72985
|
+
behavior: "allow",
|
|
72986
|
+
updatedInput: lastSessionId ? { ...input2, transcript_id: lastSessionId } : input2
|
|
72987
|
+
};
|
|
71468
72988
|
}
|
|
71469
72989
|
return { behavior: "allow", updatedInput: input2 };
|
|
71470
72990
|
}
|
|
@@ -71525,6 +73045,7 @@ var DEFAULT_MAX_TURNS = 160, DEFAULT_MAX_NOTES = 80, DEFAULT_TIMEOUT_MS2, KNOWLE
|
|
|
71525
73045
|
var init_runner = __esm(() => {
|
|
71526
73046
|
init_logger();
|
|
71527
73047
|
init_config_helpers();
|
|
73048
|
+
init_notes();
|
|
71528
73049
|
init_config_dir();
|
|
71529
73050
|
init_conflicts();
|
|
71530
73051
|
init_env();
|
|
@@ -71548,17 +73069,17 @@ var init_runner = __esm(() => {
|
|
|
71548
73069
|
});
|
|
71549
73070
|
|
|
71550
73071
|
// src/version/skill-update-check.ts
|
|
71551
|
-
import { existsSync as existsSync17, mkdirSync as
|
|
71552
|
-
import { join as
|
|
73072
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync10, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "node:fs";
|
|
73073
|
+
import { join as join20 } from "node:path";
|
|
71553
73074
|
function getCachePath2() {
|
|
71554
|
-
return
|
|
73075
|
+
return join20(getConfigDir(), CACHE_FILENAME3);
|
|
71555
73076
|
}
|
|
71556
73077
|
function readSkillCache() {
|
|
71557
73078
|
try {
|
|
71558
73079
|
const path3 = getCachePath2();
|
|
71559
73080
|
if (!existsSync17(path3))
|
|
71560
73081
|
return null;
|
|
71561
|
-
const data = JSON.parse(
|
|
73082
|
+
const data = JSON.parse(readFileSync18(path3, "utf-8"));
|
|
71562
73083
|
if (typeof data.lastCheck === "number" && typeof data.latestSha === "string" && typeof data.installedSha === "string") {
|
|
71563
73084
|
return data;
|
|
71564
73085
|
}
|
|
@@ -71571,9 +73092,9 @@ function writeSkillCache(cache) {
|
|
|
71571
73092
|
try {
|
|
71572
73093
|
const dir = getConfigDir();
|
|
71573
73094
|
if (!existsSync17(dir)) {
|
|
71574
|
-
|
|
73095
|
+
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
71575
73096
|
}
|
|
71576
|
-
|
|
73097
|
+
writeFileSync11(getCachePath2(), JSON.stringify(cache), { mode: 384 });
|
|
71577
73098
|
} catch {}
|
|
71578
73099
|
}
|
|
71579
73100
|
function clearInstalledSha() {
|
|
@@ -71647,7 +73168,7 @@ var init_skill_update_check = __esm(() => {
|
|
|
71647
73168
|
// src/commands/skill.ts
|
|
71648
73169
|
import { exec as exec2, execSync as execSync2 } from "node:child_process";
|
|
71649
73170
|
import { homedir as homedir6 } from "node:os";
|
|
71650
|
-
import { join as
|
|
73171
|
+
import { join as join21 } from "node:path";
|
|
71651
73172
|
function skillAgentIDsForProviders(providerIDs) {
|
|
71652
73173
|
return [
|
|
71653
73174
|
...new Set(providerIDs.map((providerID) => SKILL_AGENT_BY_PROVIDER[providerID]).filter((agent) => Boolean(agent)))
|
|
@@ -71658,17 +73179,17 @@ function skillInstallTargetForProvider(providerID) {
|
|
|
71658
73179
|
if (!agentID)
|
|
71659
73180
|
return null;
|
|
71660
73181
|
if (agentID === "claude-code") {
|
|
71661
|
-
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR?.trim() ||
|
|
71662
|
-
return { path:
|
|
73182
|
+
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR?.trim() || join21(homedir6(), ".claude");
|
|
73183
|
+
return { path: join21(claudeConfigDir, "skills", SKILL_NAME), symlink: true };
|
|
71663
73184
|
}
|
|
71664
73185
|
if (agentID === "windsurf") {
|
|
71665
73186
|
return {
|
|
71666
|
-
path:
|
|
73187
|
+
path: join21(homedir6(), ".codeium", "windsurf", "skills", SKILL_NAME),
|
|
71667
73188
|
symlink: true
|
|
71668
73189
|
};
|
|
71669
73190
|
}
|
|
71670
73191
|
return {
|
|
71671
|
-
path:
|
|
73192
|
+
path: join21(homedir6(), ".agents", "skills", SKILL_NAME),
|
|
71672
73193
|
symlink: false
|
|
71673
73194
|
};
|
|
71674
73195
|
}
|
|
@@ -71677,12 +73198,12 @@ function skillAgentArgs(providerIDs) {
|
|
|
71677
73198
|
return agents.map((agent) => `-a ${agent}`).join(" ");
|
|
71678
73199
|
}
|
|
71679
73200
|
function execQuiet(command) {
|
|
71680
|
-
return new Promise((
|
|
73201
|
+
return new Promise((resolve6, reject) => {
|
|
71681
73202
|
exec2(command, { windowsHide: true }, (error61) => {
|
|
71682
73203
|
if (error61)
|
|
71683
73204
|
reject(error61);
|
|
71684
73205
|
else
|
|
71685
|
-
|
|
73206
|
+
resolve6();
|
|
71686
73207
|
});
|
|
71687
73208
|
});
|
|
71688
73209
|
}
|
|
@@ -71829,15 +73350,15 @@ import { randomUUID as randomUUID3 } from "node:crypto";
|
|
|
71829
73350
|
import {
|
|
71830
73351
|
existsSync as existsSync18,
|
|
71831
73352
|
lstatSync as lstatSync2,
|
|
71832
|
-
mkdirSync as
|
|
71833
|
-
readFileSync as
|
|
73353
|
+
mkdirSync as mkdirSync11,
|
|
73354
|
+
readFileSync as readFileSync19,
|
|
71834
73355
|
renameSync as renameSync5,
|
|
71835
73356
|
unlinkSync as unlinkSync3,
|
|
71836
|
-
writeFileSync as
|
|
73357
|
+
writeFileSync as writeFileSync12
|
|
71837
73358
|
} from "node:fs";
|
|
71838
|
-
import { join as
|
|
73359
|
+
import { join as join22 } from "node:path";
|
|
71839
73360
|
function getTelemetrySettingsPath() {
|
|
71840
|
-
return
|
|
73361
|
+
return join22(getConfigDir(), TELEMETRY_SETTINGS_FILENAME);
|
|
71841
73362
|
}
|
|
71842
73363
|
function loadTelemetrySettings() {
|
|
71843
73364
|
return readTelemetrySettings().settings;
|
|
@@ -71905,7 +73426,7 @@ function readTelemetrySettings() {
|
|
|
71905
73426
|
}
|
|
71906
73427
|
let content;
|
|
71907
73428
|
try {
|
|
71908
|
-
content =
|
|
73429
|
+
content = readFileSync19(path3, "utf-8");
|
|
71909
73430
|
} catch {
|
|
71910
73431
|
return { status: "error", settings: disabledTelemetrySettings() };
|
|
71911
73432
|
}
|
|
@@ -71938,8 +73459,8 @@ function writeTelemetrySettings(settings2) {
|
|
|
71938
73459
|
const temporaryPath = `${path3}.${process.pid}.${randomUUID3()}.tmp`;
|
|
71939
73460
|
try {
|
|
71940
73461
|
if (!existsSync18(dir))
|
|
71941
|
-
|
|
71942
|
-
|
|
73462
|
+
mkdirSync11(dir, { recursive: true, mode: 448 });
|
|
73463
|
+
writeFileSync12(temporaryPath, `${JSON.stringify(settings2, null, 2)}
|
|
71943
73464
|
`, {
|
|
71944
73465
|
flag: "wx",
|
|
71945
73466
|
mode: 384
|
|
@@ -71983,14 +73504,14 @@ var init_settings = __esm(() => {
|
|
|
71983
73504
|
});
|
|
71984
73505
|
|
|
71985
73506
|
// src/telemetry/debug-id.ts
|
|
71986
|
-
var BUNDLE_DEBUG_ID_PLACEHOLDER = "
|
|
73507
|
+
var BUNDLE_DEBUG_ID_PLACEHOLDER = "cf222ab5-3069-6407-6475-6e2164756e21";
|
|
71987
73508
|
|
|
71988
73509
|
// src/telemetry/telemetry.ts
|
|
71989
73510
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
71990
73511
|
import { existsSync as existsSync19 } from "node:fs";
|
|
71991
73512
|
import { request as httpRequest2 } from "node:http";
|
|
71992
73513
|
import { request as httpsRequest } from "node:https";
|
|
71993
|
-
import { isAbsolute as isAbsolute4, relative as relative2, resolve as
|
|
73514
|
+
import { isAbsolute as isAbsolute4, relative as relative2, resolve as resolve6, sep as sep5 } from "node:path";
|
|
71994
73515
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
71995
73516
|
function safeString(value, pattern, fallback) {
|
|
71996
73517
|
if (typeof value !== "string" || !pattern.test(value))
|
|
@@ -72116,7 +73637,7 @@ function parseOwnedStackLine(originalLine, packageRoot) {
|
|
|
72116
73637
|
}
|
|
72117
73638
|
if (!isAbsolute4(absolutePath))
|
|
72118
73639
|
return;
|
|
72119
|
-
const resolvedPath =
|
|
73640
|
+
const resolvedPath = resolve6(absolutePath);
|
|
72120
73641
|
const relativePath = relative2(packageRoot, resolvedPath);
|
|
72121
73642
|
if (relativePath === "" || relativePath === ".." || relativePath.startsWith(`..${sep5}`) || isAbsolute4(relativePath)) {
|
|
72122
73643
|
return;
|
|
@@ -72354,7 +73875,7 @@ function fetchWithoutRedirect(url2, init = {}) {
|
|
|
72354
73875
|
} catch (error61) {
|
|
72355
73876
|
return Promise.reject(error61);
|
|
72356
73877
|
}
|
|
72357
|
-
return new Promise((
|
|
73878
|
+
return new Promise((resolve7, reject) => {
|
|
72358
73879
|
let request;
|
|
72359
73880
|
let response;
|
|
72360
73881
|
let settled = false;
|
|
@@ -72378,7 +73899,7 @@ function fetchWithoutRedirect(url2, init = {}) {
|
|
|
72378
73899
|
response?.destroy();
|
|
72379
73900
|
request?.socket?.destroy();
|
|
72380
73901
|
request?.destroy();
|
|
72381
|
-
|
|
73902
|
+
resolve7(value);
|
|
72382
73903
|
};
|
|
72383
73904
|
const onAbort = () => {
|
|
72384
73905
|
const error61 = new Error("telemetry request aborted");
|
|
@@ -72851,7 +74372,7 @@ var init_antigravity = __esm(() => {
|
|
|
72851
74372
|
});
|
|
72852
74373
|
|
|
72853
74374
|
// src/mcp/providers/claude.ts
|
|
72854
|
-
import { join as
|
|
74375
|
+
import { join as join23 } from "node:path";
|
|
72855
74376
|
var ClaudeProvider = () => createJSONProvider({
|
|
72856
74377
|
providerName: "Claude Code",
|
|
72857
74378
|
providerID: "claude",
|
|
@@ -72860,24 +74381,24 @@ var ClaudeProvider = () => createJSONProvider({
|
|
|
72860
74381
|
paths: ["~/.claude"],
|
|
72861
74382
|
globalPath: "~/.claude.json",
|
|
72862
74383
|
topKey: "mcpServers",
|
|
72863
|
-
localConfigPath: (cwd) =>
|
|
74384
|
+
localConfigPath: (cwd) => join23(cwd, ".mcp.json")
|
|
72864
74385
|
});
|
|
72865
74386
|
var init_claude = __esm(() => {
|
|
72866
74387
|
init_base();
|
|
72867
74388
|
});
|
|
72868
74389
|
|
|
72869
74390
|
// src/mcp/providers/claude-desktop.ts
|
|
72870
|
-
import { join as
|
|
74391
|
+
import { join as join24 } from "node:path";
|
|
72871
74392
|
function configPath() {
|
|
72872
|
-
return
|
|
74393
|
+
return join24(appSupportDir(), "Claude", "claude_desktop_config.json");
|
|
72873
74394
|
}
|
|
72874
74395
|
var ClaudeDesktopProvider = () => ({
|
|
72875
74396
|
name: () => "Claude Desktop",
|
|
72876
74397
|
id: () => "claude-desktop",
|
|
72877
74398
|
supportsLocal: () => false,
|
|
72878
74399
|
priority: () => 2,
|
|
72879
|
-
detectPaths: () => [
|
|
72880
|
-
isInstalled: () => isInstalled([
|
|
74400
|
+
detectPaths: () => [join24(appSupportDir(), "Claude")],
|
|
74401
|
+
isInstalled: () => isInstalled([join24(appSupportDir(), "Claude")]),
|
|
72881
74402
|
globalConfigPath: () => configPath(),
|
|
72882
74403
|
isConfigured: () => isJSONKeyConfigured(configPath(), "mcpServers"),
|
|
72883
74404
|
install(cfg, global2) {
|
|
@@ -72907,14 +74428,14 @@ var init_claude_desktop = __esm(() => {
|
|
|
72907
74428
|
});
|
|
72908
74429
|
|
|
72909
74430
|
// src/mcp/providers/cline.ts
|
|
72910
|
-
import { join as
|
|
72911
|
-
var extensionDir = () =>
|
|
74431
|
+
import { join as join25 } from "node:path";
|
|
74432
|
+
var extensionDir = () => join25(appSupportDir(), "Code", "User", "globalStorage", "saoudrizwan.claude-dev"), ClineProvider = () => createJSONProvider({
|
|
72912
74433
|
providerName: "Cline",
|
|
72913
74434
|
providerID: "cline",
|
|
72914
74435
|
local: false,
|
|
72915
74436
|
priorityValue: 11,
|
|
72916
74437
|
paths: [extensionDir()],
|
|
72917
|
-
globalPath:
|
|
74438
|
+
globalPath: join25(extensionDir(), "settings", "cline_mcp_settings.json"),
|
|
72918
74439
|
topKey: "mcpServers",
|
|
72919
74440
|
buildServer: (cfg) => ({
|
|
72920
74441
|
url: mcpURL(cfg.active_account.target.deployment_id),
|
|
@@ -72930,7 +74451,7 @@ var init_cline = __esm(() => {
|
|
|
72930
74451
|
});
|
|
72931
74452
|
|
|
72932
74453
|
// src/mcp/providers/cline-cli.ts
|
|
72933
|
-
import { join as
|
|
74454
|
+
import { join as join26 } from "node:path";
|
|
72934
74455
|
function clineDir() {
|
|
72935
74456
|
return process.env.CLINE_DIR ?? expandHome("~/.cline");
|
|
72936
74457
|
}
|
|
@@ -72940,7 +74461,7 @@ var ClineCliProvider = () => createJSONProvider({
|
|
|
72940
74461
|
local: false,
|
|
72941
74462
|
priorityValue: 12,
|
|
72942
74463
|
paths: [clineDir()],
|
|
72943
|
-
globalPath:
|
|
74464
|
+
globalPath: join26(clineDir(), "data", "settings", "cline_mcp_settings.json"),
|
|
72944
74465
|
topKey: "mcpServers",
|
|
72945
74466
|
buildServer: (cfg) => ({
|
|
72946
74467
|
url: mcpURL(cfg.active_account.target.deployment_id),
|
|
@@ -72956,20 +74477,20 @@ var init_cline_cli = __esm(() => {
|
|
|
72956
74477
|
});
|
|
72957
74478
|
|
|
72958
74479
|
// src/mcp/providers/codex.ts
|
|
72959
|
-
import { existsSync as existsSync20, readFileSync as
|
|
72960
|
-
import { join as
|
|
74480
|
+
import { existsSync as existsSync20, readFileSync as readFileSync20 } from "node:fs";
|
|
74481
|
+
import { join as join27 } from "node:path";
|
|
72961
74482
|
function codexHome2() {
|
|
72962
74483
|
return process.env.CODEX_HOME ?? expandHome("~/.codex");
|
|
72963
74484
|
}
|
|
72964
74485
|
function getConfigPath2(global2) {
|
|
72965
74486
|
if (global2)
|
|
72966
|
-
return
|
|
72967
|
-
return
|
|
74487
|
+
return join27(codexHome2(), "config.toml");
|
|
74488
|
+
return join27(process.cwd(), ".codex", "config.toml");
|
|
72968
74489
|
}
|
|
72969
74490
|
function readTOML(path3) {
|
|
72970
74491
|
if (!existsSync20(path3))
|
|
72971
74492
|
return "";
|
|
72972
|
-
return
|
|
74493
|
+
return readFileSync20(path3, "utf-8");
|
|
72973
74494
|
}
|
|
72974
74495
|
function writeTOML(path3, content) {
|
|
72975
74496
|
writeSecureFile(path3, content);
|
|
@@ -73032,9 +74553,9 @@ var CodexProvider = () => ({
|
|
|
73032
74553
|
priority: () => 8,
|
|
73033
74554
|
detectPaths: () => ["~/.codex"],
|
|
73034
74555
|
isInstalled: () => isInstalled(["~/.codex"]),
|
|
73035
|
-
globalConfigPath: () =>
|
|
74556
|
+
globalConfigPath: () => join27(codexHome2(), "config.toml"),
|
|
73036
74557
|
isConfigured: () => {
|
|
73037
|
-
const content = readTOML(
|
|
74558
|
+
const content = readTOML(join27(codexHome2(), "config.toml"));
|
|
73038
74559
|
return content.includes("[mcp_servers.dosu]");
|
|
73039
74560
|
},
|
|
73040
74561
|
install(cfg, global2) {
|
|
@@ -73056,10 +74577,10 @@ var init_codex = __esm(() => {
|
|
|
73056
74577
|
});
|
|
73057
74578
|
|
|
73058
74579
|
// src/mcp/providers/copilot.ts
|
|
73059
|
-
import { join as
|
|
74580
|
+
import { join as join28 } from "node:path";
|
|
73060
74581
|
function globalPath() {
|
|
73061
74582
|
if (process.env.XDG_CONFIG_HOME) {
|
|
73062
|
-
return
|
|
74583
|
+
return join28(process.env.XDG_CONFIG_HOME, "mcp-config.json");
|
|
73063
74584
|
}
|
|
73064
74585
|
return expandHome("~/.copilot/mcp-config.json");
|
|
73065
74586
|
}
|
|
@@ -73090,7 +74611,7 @@ var CopilotProvider = () => ({
|
|
|
73090
74611
|
};
|
|
73091
74612
|
installJSONServer(globalPath(), "mcpServers", server);
|
|
73092
74613
|
} else {
|
|
73093
|
-
const configPath2 =
|
|
74614
|
+
const configPath2 = join28(process.cwd(), ".vscode", "mcp.json");
|
|
73094
74615
|
const server = {
|
|
73095
74616
|
type: "http",
|
|
73096
74617
|
url: url2,
|
|
@@ -73103,7 +74624,7 @@ var CopilotProvider = () => ({
|
|
|
73103
74624
|
if (global2) {
|
|
73104
74625
|
removeJSONServer(globalPath(), "mcpServers");
|
|
73105
74626
|
} else {
|
|
73106
|
-
const configPath2 =
|
|
74627
|
+
const configPath2 = join28(process.cwd(), ".vscode", "mcp.json");
|
|
73107
74628
|
removeJSONServer(configPath2, "servers");
|
|
73108
74629
|
}
|
|
73109
74630
|
}
|
|
@@ -73115,7 +74636,7 @@ var init_copilot = __esm(() => {
|
|
|
73115
74636
|
});
|
|
73116
74637
|
|
|
73117
74638
|
// src/mcp/providers/cursor.ts
|
|
73118
|
-
import { join as
|
|
74639
|
+
import { join as join29 } from "node:path";
|
|
73119
74640
|
var CursorProvider = () => createJSONProvider({
|
|
73120
74641
|
providerName: "Cursor",
|
|
73121
74642
|
providerID: "cursor",
|
|
@@ -73128,7 +74649,7 @@ var CursorProvider = () => createJSONProvider({
|
|
|
73128
74649
|
url: mcpURL(cfg.active_account.target.deployment_id),
|
|
73129
74650
|
headers: mcpHeaders(cfg.active_account.target.api_key)
|
|
73130
74651
|
}),
|
|
73131
|
-
localConfigPath: (cwd) =>
|
|
74652
|
+
localConfigPath: (cwd) => join29(cwd, ".cursor", "mcp.json")
|
|
73132
74653
|
});
|
|
73133
74654
|
var init_cursor = __esm(() => {
|
|
73134
74655
|
init_config_helpers();
|
|
@@ -73136,7 +74657,7 @@ var init_cursor = __esm(() => {
|
|
|
73136
74657
|
});
|
|
73137
74658
|
|
|
73138
74659
|
// src/mcp/providers/factory.ts
|
|
73139
|
-
import { join as
|
|
74660
|
+
import { join as join30 } from "node:path";
|
|
73140
74661
|
var FactoryProvider = () => createJSONProvider({
|
|
73141
74662
|
providerName: "Factory",
|
|
73142
74663
|
providerID: "factory",
|
|
@@ -73145,14 +74666,14 @@ var FactoryProvider = () => createJSONProvider({
|
|
|
73145
74666
|
paths: ["~/.factory"],
|
|
73146
74667
|
globalPath: "~/.factory/mcp.json",
|
|
73147
74668
|
topKey: "mcpServers",
|
|
73148
|
-
localConfigPath: (cwd) =>
|
|
74669
|
+
localConfigPath: (cwd) => join30(cwd, ".factory", "mcp.json")
|
|
73149
74670
|
});
|
|
73150
74671
|
var init_factory = __esm(() => {
|
|
73151
74672
|
init_base();
|
|
73152
74673
|
});
|
|
73153
74674
|
|
|
73154
74675
|
// src/mcp/providers/gemini.ts
|
|
73155
|
-
import { join as
|
|
74676
|
+
import { join as join31 } from "node:path";
|
|
73156
74677
|
var GeminiProvider = () => createJSONProvider({
|
|
73157
74678
|
providerName: "Gemini CLI",
|
|
73158
74679
|
providerID: "gemini",
|
|
@@ -73161,7 +74682,7 @@ var GeminiProvider = () => createJSONProvider({
|
|
|
73161
74682
|
paths: ["~/.gemini"],
|
|
73162
74683
|
globalPath: "~/.gemini/settings.json",
|
|
73163
74684
|
topKey: "mcpServers",
|
|
73164
|
-
localConfigPath: (cwd) =>
|
|
74685
|
+
localConfigPath: (cwd) => join31(cwd, ".gemini", "settings.json")
|
|
73165
74686
|
});
|
|
73166
74687
|
var init_gemini = __esm(() => {
|
|
73167
74688
|
init_base();
|
|
@@ -73212,7 +74733,7 @@ var init_manual = __esm(() => {
|
|
|
73212
74733
|
|
|
73213
74734
|
// src/mcp/providers/mcporter.ts
|
|
73214
74735
|
import { existsSync as existsSync21 } from "node:fs";
|
|
73215
|
-
import { join as
|
|
74736
|
+
import { join as join32 } from "node:path";
|
|
73216
74737
|
function resolveGlobalConfigPath() {
|
|
73217
74738
|
const jsonPath = expandHome("~/.mcporter/mcporter.json");
|
|
73218
74739
|
if (existsSync21(jsonPath))
|
|
@@ -73239,7 +74760,7 @@ var MCPorterProvider = () => ({
|
|
|
73239
74760
|
globalConfigPath: () => resolveGlobalConfigPath(),
|
|
73240
74761
|
isConfigured: () => isJSONKeyConfigured(resolveGlobalConfigPath(), "mcpServers"),
|
|
73241
74762
|
install(cfg, global2) {
|
|
73242
|
-
const configPath2 = global2 ? resolveGlobalConfigPath() :
|
|
74763
|
+
const configPath2 = global2 ? resolveGlobalConfigPath() : join32(process.cwd(), "config", "mcporter.json");
|
|
73243
74764
|
const server = {
|
|
73244
74765
|
type: "http",
|
|
73245
74766
|
url: mcpEndpoint4(cfg),
|
|
@@ -73248,7 +74769,7 @@ var MCPorterProvider = () => ({
|
|
|
73248
74769
|
installJSONServer(configPath2, "mcpServers", server);
|
|
73249
74770
|
},
|
|
73250
74771
|
remove(global2) {
|
|
73251
|
-
const configPath2 = global2 ? resolveGlobalConfigPath() :
|
|
74772
|
+
const configPath2 = global2 ? resolveGlobalConfigPath() : join32(process.cwd(), "config", "mcporter.json");
|
|
73252
74773
|
removeJSONServer(configPath2, "mcpServers");
|
|
73253
74774
|
}
|
|
73254
74775
|
});
|
|
@@ -73259,7 +74780,7 @@ var init_mcporter = __esm(() => {
|
|
|
73259
74780
|
});
|
|
73260
74781
|
|
|
73261
74782
|
// src/mcp/providers/opencode.ts
|
|
73262
|
-
import { join as
|
|
74783
|
+
import { join as join33 } from "node:path";
|
|
73263
74784
|
var OpenCodeProvider = () => createJSONProvider({
|
|
73264
74785
|
providerName: "OpenCode",
|
|
73265
74786
|
providerID: "opencode",
|
|
@@ -73274,7 +74795,7 @@ var OpenCodeProvider = () => createJSONProvider({
|
|
|
73274
74795
|
enabled: true,
|
|
73275
74796
|
headers: mcpHeaders(cfg.active_account.target.api_key)
|
|
73276
74797
|
}),
|
|
73277
|
-
localConfigPath: (cwd) =>
|
|
74798
|
+
localConfigPath: (cwd) => join33(cwd, "opencode.json")
|
|
73278
74799
|
});
|
|
73279
74800
|
var init_opencode = __esm(() => {
|
|
73280
74801
|
init_config_helpers();
|
|
@@ -73282,16 +74803,16 @@ var init_opencode = __esm(() => {
|
|
|
73282
74803
|
});
|
|
73283
74804
|
|
|
73284
74805
|
// src/mcp/providers/vscode.ts
|
|
73285
|
-
import { join as
|
|
74806
|
+
import { join as join34 } from "node:path";
|
|
73286
74807
|
var VSCodeProvider = () => createJSONProvider({
|
|
73287
74808
|
providerName: "VS Code",
|
|
73288
74809
|
providerID: "vscode",
|
|
73289
74810
|
local: true,
|
|
73290
74811
|
priorityValue: 6,
|
|
73291
|
-
paths: [
|
|
73292
|
-
globalPath:
|
|
74812
|
+
paths: [join34(appSupportDir(), "Code")],
|
|
74813
|
+
globalPath: join34(appSupportDir(), "Code", "User", "mcp.json"),
|
|
73293
74814
|
topKey: "servers",
|
|
73294
|
-
localConfigPath: (cwd) =>
|
|
74815
|
+
localConfigPath: (cwd) => join34(cwd, ".vscode", "mcp.json")
|
|
73295
74816
|
});
|
|
73296
74817
|
var init_vscode = __esm(() => {
|
|
73297
74818
|
init_detect();
|
|
@@ -73300,14 +74821,14 @@ var init_vscode = __esm(() => {
|
|
|
73300
74821
|
|
|
73301
74822
|
// src/mcp/providers/windsurf.ts
|
|
73302
74823
|
import { homedir as homedir8 } from "node:os";
|
|
73303
|
-
import { join as
|
|
74824
|
+
import { join as join35 } from "node:path";
|
|
73304
74825
|
var WindsurfProvider = () => createJSONProvider({
|
|
73305
74826
|
providerName: "Windsurf",
|
|
73306
74827
|
providerID: "windsurf",
|
|
73307
74828
|
local: false,
|
|
73308
74829
|
priorityValue: 9,
|
|
73309
|
-
paths: [
|
|
73310
|
-
globalPath:
|
|
74830
|
+
paths: [join35(homedir8(), ".codeium", "windsurf")],
|
|
74831
|
+
globalPath: join35(homedir8(), ".codeium", "windsurf", "mcp_config.json"),
|
|
73311
74832
|
topKey: "mcpServers"
|
|
73312
74833
|
});
|
|
73313
74834
|
var init_windsurf = __esm(() => {
|
|
@@ -73316,12 +74837,12 @@ var init_windsurf = __esm(() => {
|
|
|
73316
74837
|
|
|
73317
74838
|
// src/mcp/providers/zed.ts
|
|
73318
74839
|
import { platform as platform3 } from "node:os";
|
|
73319
|
-
import { join as
|
|
74840
|
+
import { join as join36 } from "node:path";
|
|
73320
74841
|
function zedConfigDir() {
|
|
73321
74842
|
const os3 = platform3();
|
|
73322
74843
|
if (os3 === "darwin" || os3 === "win32")
|
|
73323
|
-
return
|
|
73324
|
-
return
|
|
74844
|
+
return join36(appSupportDir(), "Zed");
|
|
74845
|
+
return join36(appSupportDir(), "zed");
|
|
73325
74846
|
}
|
|
73326
74847
|
var ZedProvider = () => createJSONProvider({
|
|
73327
74848
|
providerName: "Zed",
|
|
@@ -73329,7 +74850,7 @@ var ZedProvider = () => createJSONProvider({
|
|
|
73329
74850
|
local: true,
|
|
73330
74851
|
priorityValue: 10,
|
|
73331
74852
|
paths: [zedConfigDir()],
|
|
73332
|
-
globalPath:
|
|
74853
|
+
globalPath: join36(zedConfigDir(), "settings.json"),
|
|
73333
74854
|
topKey: "context_servers",
|
|
73334
74855
|
buildServer: (cfg) => ({
|
|
73335
74856
|
source: "custom",
|
|
@@ -73337,7 +74858,7 @@ var ZedProvider = () => createJSONProvider({
|
|
|
73337
74858
|
url: mcpURL(cfg.active_account.target.deployment_id),
|
|
73338
74859
|
headers: mcpHeaders(cfg.active_account.target.api_key)
|
|
73339
74860
|
}),
|
|
73340
|
-
localConfigPath: (cwd) =>
|
|
74861
|
+
localConfigPath: (cwd) => join36(cwd, ".zed", "settings.json")
|
|
73341
74862
|
});
|
|
73342
74863
|
var init_zed = __esm(() => {
|
|
73343
74864
|
init_config_helpers();
|
|
@@ -73396,8 +74917,8 @@ var init_providers = __esm(() => {
|
|
|
73396
74917
|
});
|
|
73397
74918
|
|
|
73398
74919
|
// src/version/update-check.ts
|
|
73399
|
-
import { existsSync as existsSync22, mkdirSync as
|
|
73400
|
-
import { join as
|
|
74920
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync21, writeFileSync as writeFileSync13 } from "node:fs";
|
|
74921
|
+
import { join as join37 } from "node:path";
|
|
73401
74922
|
function isValidVersion(value) {
|
|
73402
74923
|
return typeof value === "string" && SEMVER_PATTERN.test(value);
|
|
73403
74924
|
}
|
|
@@ -73419,14 +74940,14 @@ function isNewerVersion(latest, current) {
|
|
|
73419
74940
|
return false;
|
|
73420
74941
|
}
|
|
73421
74942
|
function getCachePath3() {
|
|
73422
|
-
return
|
|
74943
|
+
return join37(getConfigDir(), CACHE_FILENAME4);
|
|
73423
74944
|
}
|
|
73424
74945
|
function readCache() {
|
|
73425
74946
|
try {
|
|
73426
74947
|
const path3 = getCachePath3();
|
|
73427
74948
|
if (!existsSync22(path3))
|
|
73428
74949
|
return null;
|
|
73429
|
-
const data = JSON.parse(
|
|
74950
|
+
const data = JSON.parse(readFileSync21(path3, "utf-8"));
|
|
73430
74951
|
if (typeof data.lastCheck === "number" && isValidVersion(data.latestVersion)) {
|
|
73431
74952
|
return data;
|
|
73432
74953
|
}
|
|
@@ -73439,9 +74960,9 @@ function writeCache(cache) {
|
|
|
73439
74960
|
try {
|
|
73440
74961
|
const dir = getConfigDir();
|
|
73441
74962
|
if (!existsSync22(dir)) {
|
|
73442
|
-
|
|
74963
|
+
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
73443
74964
|
}
|
|
73444
|
-
|
|
74965
|
+
writeFileSync13(getCachePath3(), JSON.stringify(cache), { mode: 384 });
|
|
73445
74966
|
} catch {}
|
|
73446
74967
|
}
|
|
73447
74968
|
async function fetchLatestVersion() {
|
|
@@ -73551,14 +75072,14 @@ var init_update_check = __esm(() => {
|
|
|
73551
75072
|
});
|
|
73552
75073
|
|
|
73553
75074
|
// src/rules/installer.ts
|
|
73554
|
-
import { existsSync as existsSync23, mkdirSync as
|
|
75075
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync22, unlinkSync as unlinkSync4, writeFileSync as writeFileSync14 } from "node:fs";
|
|
73555
75076
|
import { homedir as homedir9 } from "node:os";
|
|
73556
|
-
import { dirname as
|
|
75077
|
+
import { dirname as dirname9, join as join38 } from "node:path";
|
|
73557
75078
|
function claudeConfigDir() {
|
|
73558
|
-
return process.env.CLAUDE_CONFIG_DIR ||
|
|
75079
|
+
return process.env.CLAUDE_CONFIG_DIR || join38(homedir9(), ".claude");
|
|
73559
75080
|
}
|
|
73560
75081
|
function codexHome3() {
|
|
73561
|
-
return process.env.CODEX_HOME ||
|
|
75082
|
+
return process.env.CODEX_HOME || join38(homedir9(), ".codex");
|
|
73562
75083
|
}
|
|
73563
75084
|
function isRuleAgent(agent) {
|
|
73564
75085
|
return Object.hasOwn(RULE_TARGETS, agent);
|
|
@@ -73584,17 +75105,17 @@ async function fetchDosuRule(fetchImpl = globalThis.fetch) {
|
|
|
73584
75105
|
return normalizeRule(FALLBACK_DOSU_RULE);
|
|
73585
75106
|
}
|
|
73586
75107
|
function ensureParent(path3) {
|
|
73587
|
-
|
|
75108
|
+
mkdirSync13(dirname9(path3), { recursive: true });
|
|
73588
75109
|
}
|
|
73589
75110
|
function writeIfChanged(path3, content) {
|
|
73590
75111
|
if (!existsSync23(path3)) {
|
|
73591
75112
|
ensureParent(path3);
|
|
73592
|
-
|
|
75113
|
+
writeFileSync14(path3, content, "utf-8");
|
|
73593
75114
|
return "created";
|
|
73594
75115
|
}
|
|
73595
|
-
if (
|
|
75116
|
+
if (readFileSync22(path3, "utf-8") === content)
|
|
73596
75117
|
return "unchanged";
|
|
73597
|
-
|
|
75118
|
+
writeFileSync14(path3, content, "utf-8");
|
|
73598
75119
|
return "updated";
|
|
73599
75120
|
}
|
|
73600
75121
|
function findSection(content) {
|
|
@@ -73623,11 +75144,11 @@ function upsertSection(path3, content) {
|
|
|
73623
75144
|
if (!existsSync23(path3)) {
|
|
73624
75145
|
const section2 = buildSection(content);
|
|
73625
75146
|
ensureParent(path3);
|
|
73626
|
-
|
|
75147
|
+
writeFileSync14(path3, `${section2}
|
|
73627
75148
|
`, "utf-8");
|
|
73628
75149
|
return "created";
|
|
73629
75150
|
}
|
|
73630
|
-
const existing =
|
|
75151
|
+
const existing = readFileSync22(path3, "utf-8");
|
|
73631
75152
|
const lineEnding = lineEndingFor(existing);
|
|
73632
75153
|
const section = buildSection(content, lineEnding);
|
|
73633
75154
|
const location = findSection(existing);
|
|
@@ -73640,7 +75161,7 @@ function upsertSection(path3, content) {
|
|
|
73640
75161
|
}
|
|
73641
75162
|
if (next === existing)
|
|
73642
75163
|
return "unchanged";
|
|
73643
|
-
|
|
75164
|
+
writeFileSync14(path3, next, "utf-8");
|
|
73644
75165
|
return "updated";
|
|
73645
75166
|
}
|
|
73646
75167
|
function installRuleForAgent(agent, content) {
|
|
@@ -73663,7 +75184,7 @@ function removeRuleForAgent(agent) {
|
|
|
73663
75184
|
unlinkSync4(path3);
|
|
73664
75185
|
return { agent, path: path3, action: "removed" };
|
|
73665
75186
|
}
|
|
73666
|
-
const existing =
|
|
75187
|
+
const existing = readFileSync22(path3, "utf-8");
|
|
73667
75188
|
const lineEnding = lineEndingFor(existing);
|
|
73668
75189
|
const location = findSection(existing);
|
|
73669
75190
|
if (!location)
|
|
@@ -73672,7 +75193,7 @@ function removeRuleForAgent(agent) {
|
|
|
73672
75193
|
if (!next) {
|
|
73673
75194
|
unlinkSync4(path3);
|
|
73674
75195
|
} else {
|
|
73675
|
-
|
|
75196
|
+
writeFileSync14(path3, `${next}${lineEnding}`, "utf-8");
|
|
73676
75197
|
}
|
|
73677
75198
|
return { agent, path: path3, action: "removed" };
|
|
73678
75199
|
}
|
|
@@ -73700,36 +75221,36 @@ var init_installer = __esm(() => {
|
|
|
73700
75221
|
RULE_TARGETS = {
|
|
73701
75222
|
claude: {
|
|
73702
75223
|
kind: "file",
|
|
73703
|
-
path: () =>
|
|
75224
|
+
path: () => join38(claudeConfigDir(), "rules", "dosu.md")
|
|
73704
75225
|
},
|
|
73705
75226
|
cursor: {
|
|
73706
75227
|
kind: "file",
|
|
73707
|
-
path: () =>
|
|
75228
|
+
path: () => join38(homedir9(), ".cursor", "rules", "dosu.mdc"),
|
|
73708
75229
|
cursorFrontmatter: true
|
|
73709
75230
|
},
|
|
73710
75231
|
codex: {
|
|
73711
75232
|
kind: "section",
|
|
73712
|
-
path: () =>
|
|
75233
|
+
path: () => join38(codexHome3(), "AGENTS.md")
|
|
73713
75234
|
},
|
|
73714
75235
|
opencode: {
|
|
73715
75236
|
kind: "section",
|
|
73716
|
-
path: () =>
|
|
75237
|
+
path: () => join38(homedir9(), ".config", "opencode", "AGENTS.md")
|
|
73717
75238
|
},
|
|
73718
75239
|
gemini: {
|
|
73719
75240
|
kind: "section",
|
|
73720
|
-
path: () =>
|
|
75241
|
+
path: () => join38(homedir9(), ".gemini", "GEMINI.md")
|
|
73721
75242
|
},
|
|
73722
75243
|
antigravity: {
|
|
73723
75244
|
kind: "section",
|
|
73724
|
-
path: () =>
|
|
75245
|
+
path: () => join38(homedir9(), ".gemini", "GEMINI.md")
|
|
73725
75246
|
}
|
|
73726
75247
|
};
|
|
73727
75248
|
});
|
|
73728
75249
|
|
|
73729
75250
|
// src/setup/agents-md-step.ts
|
|
73730
75251
|
import { execSync as execSync3 } from "node:child_process";
|
|
73731
|
-
import { existsSync as existsSync24, readFileSync as
|
|
73732
|
-
import { join as
|
|
75252
|
+
import { existsSync as existsSync24, readFileSync as readFileSync23, writeFileSync as writeFileSync15 } from "node:fs";
|
|
75253
|
+
import { join as join39 } from "node:path";
|
|
73733
75254
|
function inGitWorkTree(cwd = process.cwd()) {
|
|
73734
75255
|
try {
|
|
73735
75256
|
const stdout2 = execSync3("git rev-parse --is-inside-work-tree", {
|
|
@@ -73743,10 +75264,10 @@ function inGitWorkTree(cwd = process.cwd()) {
|
|
|
73743
75264
|
}
|
|
73744
75265
|
function dosuAgentsSectionState(cwd = process.cwd()) {
|
|
73745
75266
|
try {
|
|
73746
|
-
const path3 =
|
|
75267
|
+
const path3 = join39(cwd, "AGENTS.md");
|
|
73747
75268
|
if (!existsSync24(path3))
|
|
73748
75269
|
return "missing";
|
|
73749
|
-
const content =
|
|
75270
|
+
const content = readFileSync23(path3, "utf-8");
|
|
73750
75271
|
const startMatch = content.match(SECTION_START_RE);
|
|
73751
75272
|
if (!startMatch || !content.includes(DOSU_SECTION_END))
|
|
73752
75273
|
return "missing";
|
|
@@ -73781,14 +75302,14 @@ function findSection2(content) {
|
|
|
73781
75302
|
return { start, end };
|
|
73782
75303
|
}
|
|
73783
75304
|
function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RULE) {
|
|
73784
|
-
const path3 =
|
|
75305
|
+
const path3 = join39(cwd, "AGENTS.md");
|
|
73785
75306
|
if (!existsSync24(path3)) {
|
|
73786
75307
|
const section2 = buildDosuAgentsSection(content);
|
|
73787
|
-
|
|
75308
|
+
writeFileSync15(path3, `${section2}
|
|
73788
75309
|
`);
|
|
73789
75310
|
return { path: path3, action: "created" };
|
|
73790
75311
|
}
|
|
73791
|
-
const existing =
|
|
75312
|
+
const existing = readFileSync23(path3, "utf-8");
|
|
73792
75313
|
const lineEnding = lineEndingFor2(existing);
|
|
73793
75314
|
const section = renderDosuAgentsSection(content, lineEnding);
|
|
73794
75315
|
const location = findSection2(existing);
|
|
@@ -73800,7 +75321,7 @@ function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RU
|
|
|
73800
75321
|
}
|
|
73801
75322
|
if (next === existing)
|
|
73802
75323
|
return { path: path3, action: "unchanged" };
|
|
73803
|
-
|
|
75324
|
+
writeFileSync15(path3, next);
|
|
73804
75325
|
return { path: path3, action: "updated" };
|
|
73805
75326
|
}
|
|
73806
75327
|
async function stepUpdateAgentsMd(cwd = process.cwd(), content) {
|
|
@@ -73882,7 +75403,7 @@ var init_alt_screen = __esm(() => {
|
|
|
73882
75403
|
});
|
|
73883
75404
|
|
|
73884
75405
|
// src/tui/activity-view.ts
|
|
73885
|
-
import { readFileSync as
|
|
75406
|
+
import { readFileSync as readFileSync24 } from "node:fs";
|
|
73886
75407
|
function reduceActivityViewKey(key) {
|
|
73887
75408
|
if (key === "q" || key === ESC7 || key === CTRL_C4)
|
|
73888
75409
|
return "back";
|
|
@@ -74127,7 +75648,7 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
|
|
|
74127
75648
|
}
|
|
74128
75649
|
function defaultReadLog2() {
|
|
74129
75650
|
try {
|
|
74130
|
-
return
|
|
75651
|
+
return readFileSync24(logger.getLogPath(), "utf-8");
|
|
74131
75652
|
} catch {
|
|
74132
75653
|
return "";
|
|
74133
75654
|
}
|
|
@@ -74212,7 +75733,7 @@ ${CLEAR_BELOW}`);
|
|
|
74212
75733
|
const leaveAltScreen = enterAltScreen(output2);
|
|
74213
75734
|
output2.write(HIDE_CURSOR3);
|
|
74214
75735
|
draw();
|
|
74215
|
-
return new Promise((
|
|
75736
|
+
return new Promise((resolve7) => {
|
|
74216
75737
|
const wasRaw = input2.isRaw ?? false;
|
|
74217
75738
|
input2.setRawMode?.(true);
|
|
74218
75739
|
input2.resume();
|
|
@@ -74229,7 +75750,7 @@ ${CLEAR_BELOW}`);
|
|
|
74229
75750
|
input2.pause();
|
|
74230
75751
|
leaveAltScreen();
|
|
74231
75752
|
output2.write(SHOW_CURSOR3);
|
|
74232
|
-
|
|
75753
|
+
resolve7();
|
|
74233
75754
|
};
|
|
74234
75755
|
const onData = (chunk) => {
|
|
74235
75756
|
for (const key of parseKeys(chunk.toString())) {
|
|
@@ -74748,8 +76269,8 @@ h1 {
|
|
|
74748
76269
|
async function startCallbackServer(opts = {}) {
|
|
74749
76270
|
let resolveToken;
|
|
74750
76271
|
let rejectToken;
|
|
74751
|
-
const tokenPromise = new Promise((
|
|
74752
|
-
resolveToken =
|
|
76272
|
+
const tokenPromise = new Promise((resolve7, reject) => {
|
|
76273
|
+
resolveToken = resolve7;
|
|
74753
76274
|
rejectToken = reject;
|
|
74754
76275
|
});
|
|
74755
76276
|
const http = __require("node:http");
|
|
@@ -74807,8 +76328,8 @@ async function startCallbackServer(opts = {}) {
|
|
|
74807
76328
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
74808
76329
|
res.end(buildSuccessHtml(email3 ?? undefined, opts.successVariant ?? "auth"));
|
|
74809
76330
|
});
|
|
74810
|
-
await new Promise((
|
|
74811
|
-
httpServer.listen(0, "localhost", () =>
|
|
76331
|
+
await new Promise((resolve7) => {
|
|
76332
|
+
httpServer.listen(0, "localhost", () => resolve7());
|
|
74812
76333
|
});
|
|
74813
76334
|
httpServer.unref();
|
|
74814
76335
|
const addr = httpServer.address();
|
|
@@ -76112,7 +77633,7 @@ ${CLEAR_BELOW2}`);
|
|
|
76112
77633
|
pagesPending = false;
|
|
76113
77634
|
draw();
|
|
76114
77635
|
});
|
|
76115
|
-
return new Promise((
|
|
77636
|
+
return new Promise((resolve7) => {
|
|
76116
77637
|
const wasRaw = input2.isRaw ?? false;
|
|
76117
77638
|
input2.setRawMode?.(true);
|
|
76118
77639
|
input2.resume();
|
|
@@ -76127,7 +77648,7 @@ ${CLEAR_BELOW2}`);
|
|
|
76127
77648
|
input2.pause();
|
|
76128
77649
|
leaveAltScreen();
|
|
76129
77650
|
output2.write(SHOW_CURSOR4);
|
|
76130
|
-
|
|
77651
|
+
resolve7();
|
|
76131
77652
|
};
|
|
76132
77653
|
const onData = (chunk) => {
|
|
76133
77654
|
for (const key of parseKeys(chunk.toString())) {
|
|
@@ -76528,7 +78049,7 @@ ${CLEAR_BELOW3}`);
|
|
|
76528
78049
|
});
|
|
76529
78050
|
draw();
|
|
76530
78051
|
};
|
|
76531
|
-
return new Promise((
|
|
78052
|
+
return new Promise((resolve7) => {
|
|
76532
78053
|
const wasRaw = input2.isRaw ?? false;
|
|
76533
78054
|
input2.setRawMode?.(true);
|
|
76534
78055
|
input2.resume();
|
|
@@ -76540,7 +78061,7 @@ ${CLEAR_BELOW3}`);
|
|
|
76540
78061
|
input2.pause();
|
|
76541
78062
|
leaveAltScreen();
|
|
76542
78063
|
output2.write(SHOW_CURSOR5);
|
|
76543
|
-
|
|
78064
|
+
resolve7();
|
|
76544
78065
|
};
|
|
76545
78066
|
const onData = (chunk) => {
|
|
76546
78067
|
for (const key of parseKeys(chunk.toString())) {
|
|
@@ -76731,7 +78252,7 @@ async function runMainMenu() {
|
|
|
76731
78252
|
const buildOptions = () => {
|
|
76732
78253
|
if (!isAuthenticated(cfg)) {
|
|
76733
78254
|
return [
|
|
76734
|
-
{ label: "Log in / Sign up", hint: "opens your browser", value: "auth" },
|
|
78255
|
+
{ label: "Log in / Sign up", hint: "(opens your browser)", value: "auth" },
|
|
76735
78256
|
{ label: "Exit", value: "exit" }
|
|
76736
78257
|
];
|
|
76737
78258
|
}
|
|
@@ -76747,6 +78268,7 @@ async function runMainMenu() {
|
|
|
76747
78268
|
label: mining ? `Activity ⛏️ ${brand("mining sessions...")}` : "Activity",
|
|
76748
78269
|
value: "sync"
|
|
76749
78270
|
},
|
|
78271
|
+
{ label: "Knowledge report", hint: "(opens in browser)", value: "report" },
|
|
76750
78272
|
{ label: "Analytics", value: "analytics" },
|
|
76751
78273
|
{ label: "Pages", value: "pages" },
|
|
76752
78274
|
{ label: "Settings", value: "settings" },
|
|
@@ -76771,6 +78293,10 @@ async function runMainMenu() {
|
|
|
76771
78293
|
await runActivityView();
|
|
76772
78294
|
home();
|
|
76773
78295
|
break;
|
|
78296
|
+
case "report":
|
|
78297
|
+
await runKnowledgeReport();
|
|
78298
|
+
home();
|
|
78299
|
+
break;
|
|
76774
78300
|
case "analytics":
|
|
76775
78301
|
await runAnalyticsView();
|
|
76776
78302
|
home();
|
|
@@ -76798,6 +78324,17 @@ async function runMainMenu() {
|
|
|
76798
78324
|
}
|
|
76799
78325
|
}
|
|
76800
78326
|
}
|
|
78327
|
+
async function runKnowledgeReport() {
|
|
78328
|
+
const s = spinner2();
|
|
78329
|
+
s.start("Writing knowledge report...");
|
|
78330
|
+
try {
|
|
78331
|
+
const path3 = await emitKnowledgeReport({ open: true });
|
|
78332
|
+
s.stop(`Opened ${path3}`);
|
|
78333
|
+
} catch (err) {
|
|
78334
|
+
s.stop("Could not write the report");
|
|
78335
|
+
log2.error(err instanceof Error ? err.message : String(err));
|
|
78336
|
+
}
|
|
78337
|
+
}
|
|
76801
78338
|
async function runSettings(cfg) {
|
|
76802
78339
|
while (true) {
|
|
76803
78340
|
const target = cfg.active_account?.target;
|
|
@@ -76925,6 +78462,7 @@ var init_tui = __esm(() => {
|
|
|
76925
78462
|
init_config();
|
|
76926
78463
|
init_agents();
|
|
76927
78464
|
init_providers();
|
|
78465
|
+
init_generate();
|
|
76928
78466
|
init_project_dir();
|
|
76929
78467
|
init_scan();
|
|
76930
78468
|
init_agents_md_step();
|
|
@@ -77204,7 +78742,7 @@ Could not open a browser automatically.`);
|
|
|
77204
78742
|
throw new Error("Authentication timed out. Run 'dosu login' to try again.");
|
|
77205
78743
|
}
|
|
77206
78744
|
function sleep4(ms2) {
|
|
77207
|
-
return new Promise((
|
|
78745
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms2));
|
|
77208
78746
|
}
|
|
77209
78747
|
var POLL_INTERVAL_MS = 5000;
|
|
77210
78748
|
var init_device = __esm(() => {
|
|
@@ -77578,7 +79116,7 @@ import {
|
|
|
77578
79116
|
constants as constants3,
|
|
77579
79117
|
fstatSync,
|
|
77580
79118
|
openSync as openSync5,
|
|
77581
|
-
readFileSync as
|
|
79119
|
+
readFileSync as readFileSync25,
|
|
77582
79120
|
readSync as readSync4,
|
|
77583
79121
|
unlinkSync as unlinkSync5
|
|
77584
79122
|
} from "node:fs";
|
|
@@ -80708,6 +82246,7 @@ init_trpc();
|
|
|
80708
82246
|
init_config();
|
|
80709
82247
|
init_agents();
|
|
80710
82248
|
init_formats();
|
|
82249
|
+
init_generate();
|
|
80711
82250
|
init_backlog();
|
|
80712
82251
|
init_detach();
|
|
80713
82252
|
init_status();
|
|
@@ -80716,7 +82255,7 @@ init_watermark();
|
|
|
80716
82255
|
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
80717
82256
|
import { existsSync as existsSync16 } from "node:fs";
|
|
80718
82257
|
import { homedir as homedir5 } from "node:os";
|
|
80719
|
-
import { delimiter as delimiter4, join as
|
|
82258
|
+
import { delimiter as delimiter4, join as join19 } from "node:path";
|
|
80720
82259
|
function requireConfig7() {
|
|
80721
82260
|
const cfg = requireLoginConfig();
|
|
80722
82261
|
if (!cfg.active_account?.target?.org_id || !cfg.active_account?.target?.space_id) {
|
|
@@ -80823,7 +82362,7 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
80823
82362
|
section("Mined", minedRows, "Mined at", "No mined sessions recorded yet.");
|
|
80824
82363
|
}
|
|
80825
82364
|
});
|
|
80826
|
-
cmd.command("sync").description("Scan local agent session history and report the mining backlog").option("--quiet", "Background mode for hooks: honor backoff, exit 0, print nothing").option("--detach", "Re-spawn detached and return immediately (used by agent hooks)").option("--bootstrap", "Backfill mode: mine the full local session history regardless of age and drain the backlog (used by setup)").option("--status", "Show whether a sync is running now, plus watermark and recent activity").option("--json", "Output as JSON").action(async (opts) => {
|
|
82365
|
+
cmd.command("sync").description("Scan local agent session history and report the mining backlog").option("--quiet", "Background mode for hooks: honor backoff, exit 0, print nothing").option("--detach", "Re-spawn detached and return immediately (used by agent hooks)").option("--bootstrap", "Backfill mode: mine the full local session history regardless of age and drain the backlog (used by setup)").option("--status", "Show whether a sync is running now, plus watermark and recent activity").option("--report", "Write the same HTML harvest report as the log-to-dosu-knowledge skill and open it").option("--out <path>", "HTML report path (default: tmp/dosu-knowledge-report.html)").option("--json", "Output as JSON").action(async (opts) => {
|
|
80827
82366
|
if (opts.status) {
|
|
80828
82367
|
const status = getSyncStatus();
|
|
80829
82368
|
if (opts.json) {
|
|
@@ -80838,7 +82377,9 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
80838
82377
|
"knowledge",
|
|
80839
82378
|
"sync",
|
|
80840
82379
|
...opts.quiet ? ["--quiet"] : [],
|
|
80841
|
-
...opts.bootstrap ? ["--bootstrap"] : []
|
|
82380
|
+
...opts.bootstrap ? ["--bootstrap"] : [],
|
|
82381
|
+
...opts.report ? ["--report"] : [],
|
|
82382
|
+
...opts.out ? ["--out", opts.out] : []
|
|
80842
82383
|
]);
|
|
80843
82384
|
return;
|
|
80844
82385
|
}
|
|
@@ -80859,12 +82400,55 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
80859
82400
|
if (opts.quiet)
|
|
80860
82401
|
return;
|
|
80861
82402
|
if (opts.json) {
|
|
80862
|
-
printResult(outcome, opts);
|
|
80863
82403
|
if (outcome.status === "error")
|
|
80864
82404
|
process.exitCode = 1;
|
|
82405
|
+
if (opts.report) {
|
|
82406
|
+
try {
|
|
82407
|
+
const report = await emitKnowledgeReport({ out: opts.out, open: false });
|
|
82408
|
+
printResult({ ...outcome, report }, opts);
|
|
82409
|
+
} catch (err) {
|
|
82410
|
+
const report_error = err instanceof Error ? err.message : String(err);
|
|
82411
|
+
printResult({ ...outcome, report_error }, opts);
|
|
82412
|
+
}
|
|
82413
|
+
} else {
|
|
82414
|
+
printResult(outcome, opts);
|
|
82415
|
+
}
|
|
80865
82416
|
return;
|
|
80866
82417
|
}
|
|
80867
82418
|
printSyncOutcome(outcome);
|
|
82419
|
+
if (opts.report) {
|
|
82420
|
+
try {
|
|
82421
|
+
const report = await emitKnowledgeReport({ out: opts.out, open: true });
|
|
82422
|
+
console.log(`Wrote ${report}`);
|
|
82423
|
+
} catch (err) {
|
|
82424
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
82425
|
+
process.exitCode = 1;
|
|
82426
|
+
}
|
|
82427
|
+
}
|
|
82428
|
+
});
|
|
82429
|
+
cmd.command("report").description("Render the knowledge report from your Dosu notes and the local session logs").option("--out <path>", "HTML report path (default: tmp/dosu-knowledge-report.html)").option("--json", "Output the report path as JSON").option("--no-open", "Write the file without opening a browser").action(async (opts) => {
|
|
82430
|
+
const path3 = await emitKnowledgeReport({
|
|
82431
|
+
out: opts.out,
|
|
82432
|
+
open: opts.json ? false : opts.open
|
|
82433
|
+
});
|
|
82434
|
+
if (opts.json) {
|
|
82435
|
+
printResult({ report: path3 }, opts);
|
|
82436
|
+
return;
|
|
82437
|
+
}
|
|
82438
|
+
console.log(`Wrote ${path3}`);
|
|
82439
|
+
});
|
|
82440
|
+
cmd.command("backfill-transcripts").description("One-shot: attribute pre-existing notes to the local sessions that produced them, so the report can show their traces").option("--json", "Output the result as JSON").action(async (opts) => {
|
|
82441
|
+
const { runBackfill: runBackfill2 } = await Promise.resolve().then(() => (init_backfill_run(), exports_backfill_run));
|
|
82442
|
+
const result = await runBackfill2();
|
|
82443
|
+
if (opts.json) {
|
|
82444
|
+
printResult(result, opts);
|
|
82445
|
+
return;
|
|
82446
|
+
}
|
|
82447
|
+
if (result.candidates === 0) {
|
|
82448
|
+
console.log("All your notes already have a transcript — nothing to backfill.");
|
|
82449
|
+
return;
|
|
82450
|
+
}
|
|
82451
|
+
console.log(`Attributed ${result.updated} of ${result.candidates} notes (${result.ambiguous} ambiguous, ${result.noBatch} without a local mining batch). Run 'dosu knowledge report' to see their traces.`);
|
|
80868
82452
|
});
|
|
80869
82453
|
cmd.addCommand(hooksCommand());
|
|
80870
82454
|
return cmd;
|
|
@@ -81082,7 +82666,7 @@ function reportHookFailure(agent, err) {
|
|
|
81082
82666
|
}
|
|
81083
82667
|
function dosuOnPath() {
|
|
81084
82668
|
const bin = process.platform === "win32" ? "dosu.cmd" : "dosu";
|
|
81085
|
-
return (process.env.PATH ?? "").split(delimiter4).some((dir) => dir !== "" && existsSync16(
|
|
82669
|
+
return (process.env.PATH ?? "").split(delimiter4).some((dir) => dir !== "" && existsSync16(join19(dir, bin)));
|
|
81086
82670
|
}
|
|
81087
82671
|
|
|
81088
82672
|
// src/commands/libraries.ts
|
|
@@ -81422,7 +83006,7 @@ init_dist();
|
|
|
81422
83006
|
init_commander();
|
|
81423
83007
|
init_trpc();
|
|
81424
83008
|
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
81425
|
-
import { readFileSync as
|
|
83009
|
+
import { readFileSync as readFileSync17 } from "node:fs";
|
|
81426
83010
|
function requireConfig10() {
|
|
81427
83011
|
return requireLoginConfig();
|
|
81428
83012
|
}
|
|
@@ -81598,7 +83182,7 @@ function reviewCommand() {
|
|
|
81598
83182
|
let body = opts.body;
|
|
81599
83183
|
if (opts.bodyFile) {
|
|
81600
83184
|
try {
|
|
81601
|
-
body =
|
|
83185
|
+
body = readFileSync17(opts.bodyFile, "utf-8");
|
|
81602
83186
|
} catch (err) {
|
|
81603
83187
|
console.error(import_picocolors22.default.red(`Failed to read --body-file: ${err.message}`));
|
|
81604
83188
|
process.exit(1);
|
|
@@ -82387,8 +83971,8 @@ async function finishTelemetry(operation) {
|
|
|
82387
83971
|
try {
|
|
82388
83972
|
await Promise.race([
|
|
82389
83973
|
Promise.resolve().then(operation),
|
|
82390
|
-
new Promise((
|
|
82391
|
-
timeout = setTimeout(
|
|
83974
|
+
new Promise((resolve7) => {
|
|
83975
|
+
timeout = setTimeout(resolve7, TELEMETRY_FLUSH_TIMEOUT_MS);
|
|
82392
83976
|
})
|
|
82393
83977
|
]);
|
|
82394
83978
|
} catch {} finally {
|
|
@@ -82742,7 +84326,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
|
|
|
82742
84326
|
}
|
|
82743
84327
|
if (opts.follow) {
|
|
82744
84328
|
try {
|
|
82745
|
-
const lines =
|
|
84329
|
+
const lines = readFileSync25(logPath, "utf-8").split(`
|
|
82746
84330
|
`);
|
|
82747
84331
|
if (lines.at(-1) === "")
|
|
82748
84332
|
lines.pop();
|
|
@@ -82758,7 +84342,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
|
|
|
82758
84342
|
if (opts.tail !== undefined) {
|
|
82759
84343
|
const n5 = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
|
|
82760
84344
|
try {
|
|
82761
|
-
const content =
|
|
84345
|
+
const content = readFileSync25(logPath, "utf-8");
|
|
82762
84346
|
const lines = content.split(`
|
|
82763
84347
|
`);
|
|
82764
84348
|
console.log(lines.slice(-n5).join(`
|
|
@@ -82829,4 +84413,4 @@ execute().catch((err) => {
|
|
|
82829
84413
|
process.exit(1);
|
|
82830
84414
|
});
|
|
82831
84415
|
|
|
82832
|
-
//# debugId=
|
|
84416
|
+
//# debugId=cf222ab5-3069-6407-6475-6e2164756e21
|