@dosu/cli 0.53.0-beta.5 → 0.53.0-beta.7
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 +1931 -488
- 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.7", 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 = "79ec0bbacf70";
|
|
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,179 +8635,749 @@ var init_watermark = __esm(() => {
|
|
|
8768
8635
|
BACKOFF_MAX_MS = 24 * 60 * 60 * 1000;
|
|
8769
8636
|
});
|
|
8770
8637
|
|
|
8771
|
-
// src/
|
|
8772
|
-
function
|
|
8773
|
-
|
|
8774
|
-
|
|
8775
|
-
|
|
8776
|
-
if (state.project_filter?.length) {
|
|
8777
|
-
const resolver = createProjectDirResolver();
|
|
8778
|
-
sessions = filterSessionsByProject(sessions, state.project_filter, resolver.resolve);
|
|
8779
|
-
resolver.flush();
|
|
8780
|
-
}
|
|
8781
|
-
const gate = gateSessions(sessions, state.watermark);
|
|
8782
|
-
return { queued: gate.ready.reverse(), open: gate.open.reverse() };
|
|
8783
|
-
} catch {
|
|
8784
|
-
return { queued: [], open: [] };
|
|
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.");
|
|
8785
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
|
+
}));
|
|
8786
8658
|
}
|
|
8787
|
-
var
|
|
8788
|
-
init_project_dir();
|
|
8789
|
-
init_scan();
|
|
8790
|
-
init_watermark();
|
|
8791
|
-
});
|
|
8659
|
+
var REPORT_NOTES_LIMIT = 500;
|
|
8792
8660
|
|
|
8793
|
-
// src/
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
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);
|
|
8802
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;
|
|
8803
8904
|
}
|
|
8804
|
-
function
|
|
8805
|
-
|
|
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;
|
|
8806
8947
|
}
|
|
8807
|
-
function
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
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;
|
|
8824
8968
|
}
|
|
8825
|
-
}
|
|
8826
|
-
return
|
|
8969
|
+
}
|
|
8970
|
+
return "";
|
|
8827
8971
|
}
|
|
8828
|
-
function
|
|
8829
|
-
const
|
|
8830
|
-
|
|
8831
|
-
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
|
|
8835
|
-
|
|
8836
|
-
return
|
|
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;
|
|
8837
9068
|
}
|
|
8838
|
-
|
|
8839
|
-
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
if (existsSync12(path3) && readFileSync11(path3, "utf8") === String(process.pid)) {
|
|
8863
|
-
rmSync2(path3, { force: true });
|
|
8864
|
-
}
|
|
8865
|
-
} catch {}
|
|
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
|
+
});
|
|
8866
9093
|
}
|
|
8867
|
-
};
|
|
8868
|
-
}
|
|
8869
|
-
var LOCK_FILENAME = "knowledge-sync.lock", STALE_LOCK_MS;
|
|
8870
|
-
var init_lock = __esm(() => {
|
|
8871
|
-
init_config();
|
|
8872
|
-
STALE_LOCK_MS = 15 * 60 * 1000;
|
|
8873
|
-
});
|
|
8874
|
-
|
|
8875
|
-
// src/sync/status.ts
|
|
8876
|
-
import { readFileSync as readFileSync12, statSync as statSync6 } from "node:fs";
|
|
8877
|
-
function formatTokenCount(tokens) {
|
|
8878
|
-
if (tokens < 1000)
|
|
8879
|
-
return String(tokens);
|
|
8880
|
-
if (tokens < 1e6) {
|
|
8881
|
-
const k = tokens / 1000;
|
|
8882
|
-
return `~${k >= 100 ? Math.round(k) : k.toFixed(1).replace(/\.0$/, "")}k`;
|
|
8883
9094
|
}
|
|
8884
|
-
|
|
8885
|
-
return `~${m3 >= 100 ? Math.round(m3) : m3.toFixed(1).replace(/\.0$/, "")}M`;
|
|
9095
|
+
return steps;
|
|
8886
9096
|
}
|
|
8887
|
-
function
|
|
8888
|
-
if (
|
|
8889
|
-
return
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
return true;
|
|
8893
|
-
} catch (err) {
|
|
8894
|
-
return err.code === "EPERM";
|
|
8895
|
-
}
|
|
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))];
|
|
8896
9102
|
}
|
|
8897
|
-
function
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
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)
|
|
8901
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>`);
|
|
8902
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
|
+
`;
|
|
8903
9174
|
}
|
|
8904
|
-
function
|
|
8905
|
-
const
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
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
|
+
];
|
|
8911
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
|
+
];
|
|
8912
9228
|
}
|
|
8913
|
-
function
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
const
|
|
8923
|
-
const
|
|
8924
|
-
const
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
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
|
+
`;
|
|
8933
9369
|
}
|
|
8934
|
-
var
|
|
8935
|
-
var
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
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;
|
|
8940
9377
|
});
|
|
8941
9378
|
|
|
8942
9379
|
// src/sessions/read.ts
|
|
8943
|
-
import { readFileSync as
|
|
9380
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
8944
9381
|
function asRecord(value) {
|
|
8945
9382
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
8946
9383
|
}
|
|
@@ -9056,11 +9493,11 @@ function readSessionTurns(session) {
|
|
|
9056
9493
|
try {
|
|
9057
9494
|
switch (session.harness) {
|
|
9058
9495
|
case "claude":
|
|
9059
|
-
return readClaude(
|
|
9496
|
+
return readClaude(readFileSync10(session.path, "utf8"));
|
|
9060
9497
|
case "cursor":
|
|
9061
|
-
return readCursor(
|
|
9498
|
+
return readCursor(readFileSync10(session.path, "utf8"));
|
|
9062
9499
|
case "codex":
|
|
9063
|
-
return readCodex(
|
|
9500
|
+
return readCodex(readFileSync10(session.path, "utf8"));
|
|
9064
9501
|
case "opencode":
|
|
9065
9502
|
return readOpencode(session.path, session.id);
|
|
9066
9503
|
}
|
|
@@ -9068,11 +9505,58 @@ function readSessionTurns(session) {
|
|
|
9068
9505
|
return [];
|
|
9069
9506
|
}
|
|
9070
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
|
+
}
|
|
9071
9555
|
function estimateSessionTokens(session) {
|
|
9072
9556
|
let chars = 0;
|
|
9073
9557
|
for (const turn of readSessionTurns(session))
|
|
9074
9558
|
chars += turn.text.length;
|
|
9075
|
-
return Math.round(chars /
|
|
9559
|
+
return Math.round(chars / CHARS_PER_TOKEN2);
|
|
9076
9560
|
}
|
|
9077
9561
|
function isWorthMining(session) {
|
|
9078
9562
|
const turns = readSessionTurns(session);
|
|
@@ -9086,11 +9570,902 @@ function isWorthMining(session) {
|
|
|
9086
9570
|
}
|
|
9087
9571
|
return false;
|
|
9088
9572
|
}
|
|
9089
|
-
var PLAIN_TEXT, CODEX_TEXT,
|
|
9573
|
+
var PLAIN_TEXT, CODEX_TEXT, REDISCOVERY_TOOLS, CHARS_PER_TOKEN2 = 4, MIN_WORTH_TURNS = 4, MIN_WORTH_CHARS = 2000;
|
|
9090
9574
|
var init_read = __esm(() => {
|
|
9091
9575
|
init_scan();
|
|
9092
9576
|
PLAIN_TEXT = new Set(["text"]);
|
|
9093
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
|
+
|
|
10300
|
+
// src/sync/backlog.ts
|
|
10301
|
+
function listSessionBacklog() {
|
|
10302
|
+
try {
|
|
10303
|
+
const state = loadSyncState();
|
|
10304
|
+
let sessions = scanAgentSessions({});
|
|
10305
|
+
if (state.project_filter?.length) {
|
|
10306
|
+
const resolver = createProjectDirResolver();
|
|
10307
|
+
sessions = filterSessionsByProject(sessions, state.project_filter, resolver.resolve);
|
|
10308
|
+
resolver.flush();
|
|
10309
|
+
}
|
|
10310
|
+
const gate = gateSessions(sessions, state.watermark);
|
|
10311
|
+
return { queued: gate.ready.reverse(), open: gate.open.reverse() };
|
|
10312
|
+
} catch {
|
|
10313
|
+
return { queued: [], open: [] };
|
|
10314
|
+
}
|
|
10315
|
+
}
|
|
10316
|
+
var init_backlog = __esm(() => {
|
|
10317
|
+
init_project_dir();
|
|
10318
|
+
init_scan();
|
|
10319
|
+
init_watermark();
|
|
10320
|
+
});
|
|
10321
|
+
|
|
10322
|
+
// src/sync/lock.ts
|
|
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";
|
|
10325
|
+
function processAlive(pid) {
|
|
10326
|
+
try {
|
|
10327
|
+
process.kill(pid, 0);
|
|
10328
|
+
return true;
|
|
10329
|
+
} catch (err) {
|
|
10330
|
+
return err.code === "EPERM";
|
|
10331
|
+
}
|
|
10332
|
+
}
|
|
10333
|
+
function lockPath(configDir = getConfigDir()) {
|
|
10334
|
+
return join12(configDir, LOCK_FILENAME);
|
|
10335
|
+
}
|
|
10336
|
+
function stopSyncRun(pid, configDir = getConfigDir(), kill = process.kill.bind(process)) {
|
|
10337
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
10338
|
+
return false;
|
|
10339
|
+
let killed = false;
|
|
10340
|
+
try {
|
|
10341
|
+
kill(-pid, "SIGTERM");
|
|
10342
|
+
killed = true;
|
|
10343
|
+
} catch {
|
|
10344
|
+
try {
|
|
10345
|
+
kill(pid, "SIGTERM");
|
|
10346
|
+
killed = true;
|
|
10347
|
+
} catch {}
|
|
10348
|
+
}
|
|
10349
|
+
try {
|
|
10350
|
+
const path3 = lockPath(configDir);
|
|
10351
|
+
if (existsSync12(path3) && readFileSync13(path3, "utf8") === String(pid)) {
|
|
10352
|
+
rmSync2(path3, { force: true });
|
|
10353
|
+
}
|
|
10354
|
+
} catch {}
|
|
10355
|
+
return killed;
|
|
10356
|
+
}
|
|
10357
|
+
function fileLock(configDir = getConfigDir(), now = () => new Date) {
|
|
10358
|
+
const path3 = lockPath(configDir);
|
|
10359
|
+
let held = false;
|
|
10360
|
+
const tryCreate = () => {
|
|
10361
|
+
try {
|
|
10362
|
+
writeFileSync9(path3, String(process.pid), { flag: "wx" });
|
|
10363
|
+
return true;
|
|
10364
|
+
} catch {
|
|
10365
|
+
return false;
|
|
10366
|
+
}
|
|
10367
|
+
};
|
|
10368
|
+
return {
|
|
10369
|
+
acquire() {
|
|
10370
|
+
if (tryCreate()) {
|
|
10371
|
+
held = true;
|
|
10372
|
+
return true;
|
|
10373
|
+
}
|
|
10374
|
+
try {
|
|
10375
|
+
const age = now().getTime() - statSync5(path3).mtimeMs;
|
|
10376
|
+
const holder = Number.parseInt(readFileSync13(path3, "utf8"), 10);
|
|
10377
|
+
const holderDead = Number.isInteger(holder) && holder > 0 && !processAlive(holder);
|
|
10378
|
+
if (age > STALE_LOCK_MS || holderDead) {
|
|
10379
|
+
rmSync2(path3, { force: true });
|
|
10380
|
+
held = tryCreate();
|
|
10381
|
+
return held;
|
|
10382
|
+
}
|
|
10383
|
+
} catch {}
|
|
10384
|
+
return false;
|
|
10385
|
+
},
|
|
10386
|
+
release() {
|
|
10387
|
+
if (!held)
|
|
10388
|
+
return;
|
|
10389
|
+
held = false;
|
|
10390
|
+
try {
|
|
10391
|
+
if (existsSync12(path3) && readFileSync13(path3, "utf8") === String(process.pid)) {
|
|
10392
|
+
rmSync2(path3, { force: true });
|
|
10393
|
+
}
|
|
10394
|
+
} catch {}
|
|
10395
|
+
}
|
|
10396
|
+
};
|
|
10397
|
+
}
|
|
10398
|
+
var LOCK_FILENAME = "knowledge-sync.lock", STALE_LOCK_MS;
|
|
10399
|
+
var init_lock = __esm(() => {
|
|
10400
|
+
init_config();
|
|
10401
|
+
STALE_LOCK_MS = 15 * 60 * 1000;
|
|
10402
|
+
});
|
|
10403
|
+
|
|
10404
|
+
// src/sync/status.ts
|
|
10405
|
+
import { readFileSync as readFileSync14, statSync as statSync6 } from "node:fs";
|
|
10406
|
+
function formatTokenCount(tokens) {
|
|
10407
|
+
if (tokens < 1000)
|
|
10408
|
+
return String(tokens);
|
|
10409
|
+
if (tokens < 1e6) {
|
|
10410
|
+
const k = tokens / 1000;
|
|
10411
|
+
return `~${k >= 100 ? Math.round(k) : k.toFixed(1).replace(/\.0$/, "")}k`;
|
|
10412
|
+
}
|
|
10413
|
+
const m3 = tokens / 1e6;
|
|
10414
|
+
return `~${m3 >= 100 ? Math.round(m3) : m3.toFixed(1).replace(/\.0$/, "")}M`;
|
|
10415
|
+
}
|
|
10416
|
+
function defaultIsProcessAlive2(pid) {
|
|
10417
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
10418
|
+
return false;
|
|
10419
|
+
try {
|
|
10420
|
+
process.kill(pid, 0);
|
|
10421
|
+
return true;
|
|
10422
|
+
} catch (err) {
|
|
10423
|
+
return err.code === "EPERM";
|
|
10424
|
+
}
|
|
10425
|
+
}
|
|
10426
|
+
function defaultReadLog() {
|
|
10427
|
+
try {
|
|
10428
|
+
return readFileSync14(logger.getLogPath(), "utf-8");
|
|
10429
|
+
} catch {
|
|
10430
|
+
return "";
|
|
10431
|
+
}
|
|
10432
|
+
}
|
|
10433
|
+
function readLockFile(configDir) {
|
|
10434
|
+
const path3 = lockPath(configDir);
|
|
10435
|
+
try {
|
|
10436
|
+
const pid = Number.parseInt(readFileSync14(path3, "utf-8"), 10);
|
|
10437
|
+
return { pid: Number.isNaN(pid) ? -1 : pid, mtime: statSync6(path3).mtime };
|
|
10438
|
+
} catch {
|
|
10439
|
+
return null;
|
|
10440
|
+
}
|
|
10441
|
+
}
|
|
10442
|
+
function recentSyncActivity(log3) {
|
|
10443
|
+
return log3.split(`
|
|
10444
|
+
`).filter((line) => line.includes("[sync]") || line.includes("[miner]")).slice(-ACTIVITY_LINES).map((line) => line.replace(/ \[(DEBUG|INFO|WARN|ERROR)\]/, ""));
|
|
10445
|
+
}
|
|
10446
|
+
function getSyncStatus(deps = {}) {
|
|
10447
|
+
const configDir = deps.configDir ?? getConfigDir();
|
|
10448
|
+
const isProcessAlive = deps.isProcessAlive ?? defaultIsProcessAlive2;
|
|
10449
|
+
const readLog = deps.readLog ?? defaultReadLog;
|
|
10450
|
+
const lock = readLockFile(configDir);
|
|
10451
|
+
const running = lock !== null && isProcessAlive(lock.pid);
|
|
10452
|
+
const state = loadSyncState(configDir);
|
|
10453
|
+
const retryAt = backoffUntil(state);
|
|
10454
|
+
return {
|
|
10455
|
+
running,
|
|
10456
|
+
...lock ? { pid: lock.pid, startedAt: lock.mtime.toISOString() } : {},
|
|
10457
|
+
...lock && !running ? { staleLock: true } : {},
|
|
10458
|
+
state,
|
|
10459
|
+
...retryAt ? { backoffUntil: retryAt.toISOString() } : {},
|
|
10460
|
+
recentActivity: recentSyncActivity(readLog())
|
|
10461
|
+
};
|
|
10462
|
+
}
|
|
10463
|
+
var ACTIVITY_LINES = 10;
|
|
10464
|
+
var init_status = __esm(() => {
|
|
10465
|
+
init_config();
|
|
10466
|
+
init_logger();
|
|
10467
|
+
init_lock();
|
|
10468
|
+
init_watermark();
|
|
9094
10469
|
});
|
|
9095
10470
|
|
|
9096
10471
|
// src/sync/sync.ts
|
|
@@ -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
|
}
|
|
@@ -9304,12 +10679,12 @@ var init_sync = __esm(() => {
|
|
|
9304
10679
|
});
|
|
9305
10680
|
|
|
9306
10681
|
// src/miner/config-dir.ts
|
|
9307
|
-
import { mkdtempSync, rmSync as rmSync3, writeFileSync as
|
|
9308
|
-
import { tmpdir } from "node:os";
|
|
9309
|
-
import { join as
|
|
10682
|
+
import { mkdtempSync, rmSync as rmSync3, writeFileSync as writeFileSync10 } from "node:fs";
|
|
10683
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
10684
|
+
import { join as join13 } from "node:path";
|
|
9310
10685
|
function createRunConfigDir() {
|
|
9311
|
-
const path3 = mkdtempSync(
|
|
9312
|
-
|
|
10686
|
+
const path3 = mkdtempSync(join13(tmpdir2(), "dosu-miner-"));
|
|
10687
|
+
writeFileSync10(join13(path3, ".claude.json"), JSON.stringify({ hasCompletedOnboarding: true }));
|
|
9313
10688
|
return {
|
|
9314
10689
|
path: path3,
|
|
9315
10690
|
cleanup() {
|
|
@@ -9320,7 +10695,7 @@ function createRunConfigDir() {
|
|
|
9320
10695
|
var init_config_dir = () => {};
|
|
9321
10696
|
|
|
9322
10697
|
// src/miner/conflicts.ts
|
|
9323
|
-
import { existsSync as existsSync13, readFileSync as
|
|
10698
|
+
import { existsSync as existsSync13, readFileSync as readFileSync15 } from "node:fs";
|
|
9324
10699
|
function managedSettingsPaths(platform3 = process.platform) {
|
|
9325
10700
|
switch (platform3) {
|
|
9326
10701
|
case "darwin":
|
|
@@ -9353,7 +10728,7 @@ function detectSettingsConflicts(paths = managedSettingsPaths()) {
|
|
|
9353
10728
|
continue;
|
|
9354
10729
|
let settings2;
|
|
9355
10730
|
try {
|
|
9356
|
-
settings2 = JSON.parse(
|
|
10731
|
+
settings2 = JSON.parse(readFileSync15(file, "utf8"));
|
|
9357
10732
|
} catch {
|
|
9358
10733
|
conflicts.push({ file, keys: ["<unreadable or invalid JSON>"] });
|
|
9359
10734
|
continue;
|
|
@@ -9415,7 +10790,7 @@ var init_env = __esm(() => {
|
|
|
9415
10790
|
import { accessSync, constants, existsSync as existsSync14 } from "node:fs";
|
|
9416
10791
|
import { createRequire as createRequire3 } from "node:module";
|
|
9417
10792
|
import { homedir as homedir4 } from "node:os";
|
|
9418
|
-
import { delimiter as delimiter2, dirname as
|
|
10793
|
+
import { delimiter as delimiter2, dirname as dirname5, join as join14 } from "node:path";
|
|
9419
10794
|
function binaryNames(platform3 = process.platform) {
|
|
9420
10795
|
return platform3 === "win32" ? ["claude.exe", "claude.cmd"] : ["claude"];
|
|
9421
10796
|
}
|
|
@@ -9423,8 +10798,8 @@ function sdkNativeBinaryExists() {
|
|
|
9423
10798
|
try {
|
|
9424
10799
|
const requireRuntime = createRequire3(import.meta.url);
|
|
9425
10800
|
const pkg = `@anthropic-ai/claude-agent-sdk-${process.platform}-${process.arch}`;
|
|
9426
|
-
const pkgDir =
|
|
9427
|
-
return binaryNames().some((name) => existsSync14(
|
|
10801
|
+
const pkgDir = dirname5(requireRuntime.resolve(`${pkg}/package.json`));
|
|
10802
|
+
return binaryNames().some((name) => existsSync14(join14(pkgDir, name)));
|
|
9428
10803
|
} catch {
|
|
9429
10804
|
return false;
|
|
9430
10805
|
}
|
|
@@ -9440,10 +10815,10 @@ function isExecutable(path3) {
|
|
|
9440
10815
|
function findSystemClaude(env = process.env, homeDir = homedir4()) {
|
|
9441
10816
|
const names = binaryNames();
|
|
9442
10817
|
const dirs = (env.PATH ?? "").split(delimiter2).filter(Boolean);
|
|
9443
|
-
dirs.push(
|
|
10818
|
+
dirs.push(join14(homeDir, ".local", "bin"), join14(homeDir, ".claude", "local"));
|
|
9444
10819
|
for (const dir of dirs) {
|
|
9445
10820
|
for (const name of names) {
|
|
9446
|
-
const candidate =
|
|
10821
|
+
const candidate = join14(dir, name);
|
|
9447
10822
|
if (isExecutable(candidate))
|
|
9448
10823
|
return candidate;
|
|
9449
10824
|
}
|
|
@@ -52091,7 +53466,7 @@ __export(exports_util, {
|
|
|
52091
53466
|
extend: () => extend,
|
|
52092
53467
|
explicitlyAborted: () => explicitlyAborted,
|
|
52093
53468
|
escapeRegex: () => escapeRegex,
|
|
52094
|
-
esc: () =>
|
|
53469
|
+
esc: () => esc2,
|
|
52095
53470
|
defineLazyInternal: () => defineLazyInternal,
|
|
52096
53471
|
defineLazy: () => defineLazy,
|
|
52097
53472
|
createTransparentProxy: () => createTransparentProxy,
|
|
@@ -52242,7 +53617,7 @@ function randomString(length = 10) {
|
|
|
52242
53617
|
}
|
|
52243
53618
|
return str;
|
|
52244
53619
|
}
|
|
52245
|
-
function
|
|
53620
|
+
function esc2(str) {
|
|
52246
53621
|
return JSON.stringify(str);
|
|
52247
53622
|
}
|
|
52248
53623
|
function slugify(input) {
|
|
@@ -55363,7 +56738,7 @@ var init_schemas = __esm(() => {
|
|
|
55363
56738
|
if (key === "__proto__")
|
|
55364
56739
|
continue;
|
|
55365
56740
|
const id2 = ids[key];
|
|
55366
|
-
const k2 = typeof key === "symbol" ? `syms[${syms.indexOf(key)}]` :
|
|
56741
|
+
const k2 = typeof key === "symbol" ? `syms[${syms.indexOf(key)}]` : esc2(key);
|
|
55367
56742
|
const isPresent = `${k2} in input`;
|
|
55368
56743
|
const schema = shape[key];
|
|
55369
56744
|
const optin = schema?._zod?.optin;
|
|
@@ -64600,16 +65975,16 @@ function generateStringFormatCheck(doc, ctx, def, accessor) {
|
|
|
64600
65975
|
doc.write(`if (${accessor} !== ${accessor}.toUpperCase()) return INVALID;`);
|
|
64601
65976
|
break;
|
|
64602
65977
|
case "includes":
|
|
64603
|
-
doc.write(`if (!${accessor}.includes(${
|
|
65978
|
+
doc.write(`if (!${accessor}.includes(${esc2(def.includes)})) return INVALID;`);
|
|
64604
65979
|
break;
|
|
64605
65980
|
case "starts_with": {
|
|
64606
65981
|
const prefix = def.prefix;
|
|
64607
|
-
doc.write(`if (${accessor}.slice(0, ${prefix.length}) !== ${
|
|
65982
|
+
doc.write(`if (${accessor}.slice(0, ${prefix.length}) !== ${esc2(prefix)}) return INVALID;`);
|
|
64608
65983
|
break;
|
|
64609
65984
|
}
|
|
64610
65985
|
case "ends_with": {
|
|
64611
65986
|
const suffix = def.suffix;
|
|
64612
|
-
doc.write(`if (${accessor}.slice(-${suffix.length}) !== ${
|
|
65987
|
+
doc.write(`if (${accessor}.slice(-${suffix.length}) !== ${esc2(suffix)}) return INVALID;`);
|
|
64613
65988
|
break;
|
|
64614
65989
|
}
|
|
64615
65990
|
default: {
|
|
@@ -64813,8 +66188,8 @@ function generateObjectCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
64813
66188
|
const keys = Object.keys(shape);
|
|
64814
66189
|
const symbolKeys = Object.getOwnPropertySymbols(shape);
|
|
64815
66190
|
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)}]` :
|
|
66191
|
+
const keyExpr = (k2) => typeof k2 === "symbol" ? addConstant(ctx, k2) : esc2(k2);
|
|
66192
|
+
const propKey = (k2) => typeof k2 === "symbol" ? `[${keyExpr(k2)}]` : esc2(k2);
|
|
64818
66193
|
const propShape = shape;
|
|
64819
66194
|
if (keys.includes("__proto__")) {
|
|
64820
66195
|
throw new ZodCompileUnsupportedError('object shape key "__proto__"');
|
|
@@ -64858,7 +66233,7 @@ function generateObjectCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
64858
66233
|
if (catchall) {
|
|
64859
66234
|
const catchallType = catchall._zod.def.type;
|
|
64860
66235
|
if (catchallType === "never") {
|
|
64861
|
-
const condition = keys.map((k2) => `k !== ${
|
|
66236
|
+
const condition = keys.map((k2) => `k !== ${esc2(k2)}`).join(" && ") || "true";
|
|
64862
66237
|
doc.write(`for (const k in ${accessor}) {`);
|
|
64863
66238
|
doc.indented((d3) => {
|
|
64864
66239
|
d3.write(`if (${condition}) return INVALID;`);
|
|
@@ -65120,7 +66495,7 @@ function generateLiteralCheck(doc, ctx, schema, accessor) {
|
|
|
65120
66495
|
return accessor;
|
|
65121
66496
|
}
|
|
65122
66497
|
if (typeof value === "string") {
|
|
65123
|
-
doc.write(`if (${accessor} !== ${
|
|
66498
|
+
doc.write(`if (${accessor} !== ${esc2(value)}) return INVALID;`);
|
|
65124
66499
|
} else if (typeof value === "number" || typeof value === "boolean") {
|
|
65125
66500
|
doc.write(`if (${accessor} !== ${value}) return INVALID;`);
|
|
65126
66501
|
} else if (value === null) {
|
|
@@ -65321,7 +66696,7 @@ function generateDiscriminatedUnionCheck(doc, ctx, def, accessor) {
|
|
|
65321
66696
|
}
|
|
65322
66697
|
const discVar = newVar(ctx);
|
|
65323
66698
|
const outputVar = newVar(ctx);
|
|
65324
|
-
doc.write(`const ${discVar} = ${accessor}?.[${
|
|
66699
|
+
doc.write(`const ${discVar} = ${accessor}?.[${esc2(def.discriminator)}];`);
|
|
65325
66700
|
doc.write(`let ${outputVar};`);
|
|
65326
66701
|
let firstBranch = true;
|
|
65327
66702
|
const claimed = new Set;
|
|
@@ -65351,7 +66726,7 @@ function generateDiscriminatedUnionCheck(doc, ctx, def, accessor) {
|
|
|
65351
66726
|
}
|
|
65352
66727
|
function literalEquality(ctx, accessor, value) {
|
|
65353
66728
|
if (typeof value === "string")
|
|
65354
|
-
return `${accessor} === ${
|
|
66729
|
+
return `${accessor} === ${esc2(value)}`;
|
|
65355
66730
|
if (typeof value === "number") {
|
|
65356
66731
|
if (Number.isNaN(value))
|
|
65357
66732
|
return `Number.isNaN(${accessor})`;
|
|
@@ -65465,7 +66840,7 @@ function generateRecordCheck(doc, ctx, schema, accessor) {
|
|
|
65465
66840
|
}
|
|
65466
66841
|
function literalPropertyKey(ctx, key) {
|
|
65467
66842
|
if (typeof key === "string")
|
|
65468
|
-
return
|
|
66843
|
+
return esc2(key);
|
|
65469
66844
|
return addConstant(ctx, key);
|
|
65470
66845
|
}
|
|
65471
66846
|
function generateMapCheck(doc, ctx, schema, accessor) {
|
|
@@ -71401,6 +72776,15 @@ async function runMiner(options) {
|
|
|
71401
72776
|
message: `Refusing to run: conflicting Claude Code settings would override the miner's auth (${detail})`
|
|
71402
72777
|
};
|
|
71403
72778
|
}
|
|
72779
|
+
const gatewayURL = options.gatewayURL ?? getLlmGatewayURL();
|
|
72780
|
+
if (!isAbsoluteHttpUrl(gatewayURL)) {
|
|
72781
|
+
return {
|
|
72782
|
+
outcome: "error",
|
|
72783
|
+
notesWritten: 0,
|
|
72784
|
+
turns: 0,
|
|
72785
|
+
message: "LLM gateway URL is not set. From source, use `bun run dev` (production endpoints) or `bun run dev:local` (local stack)."
|
|
72786
|
+
};
|
|
72787
|
+
}
|
|
71404
72788
|
const { query } = await Promise.resolve().then(() => (init_sdk(), exports_sdk));
|
|
71405
72789
|
const claudeExecutable = resolveClaudeExecutable();
|
|
71406
72790
|
if (claudeExecutable) {
|
|
@@ -71414,10 +72798,11 @@ async function runMiner(options) {
|
|
|
71414
72798
|
const abort = new AbortController;
|
|
71415
72799
|
const timer = setTimeout(() => abort.abort(), options.timeoutMs ?? DEFAULT_TIMEOUT_MS2);
|
|
71416
72800
|
let notesWritten = 0;
|
|
72801
|
+
let lastSessionId;
|
|
71417
72802
|
let turns = 0;
|
|
71418
72803
|
const env = buildMinerEnv({
|
|
71419
72804
|
apiKey: options.apiKey,
|
|
71420
|
-
gatewayURL
|
|
72805
|
+
gatewayURL,
|
|
71421
72806
|
configDir: configDir.path,
|
|
71422
72807
|
runID,
|
|
71423
72808
|
trigger: options.trigger,
|
|
@@ -71457,6 +72842,9 @@ async function runMiner(options) {
|
|
|
71457
72842
|
message: `Tool ${toolName} is not permitted in mining runs.`
|
|
71458
72843
|
};
|
|
71459
72844
|
}
|
|
72845
|
+
if (toolName === `mcp__${SESSIONS_SERVER_NAME}__read_session`) {
|
|
72846
|
+
lastSessionId = sessionIdFromReadInput(input2) ?? lastSessionId;
|
|
72847
|
+
}
|
|
71460
72848
|
if (toolName === `mcp__${KNOWLEDGE_SERVER_NAME}__write_knowledge`) {
|
|
71461
72849
|
if (notesWritten >= maxNotes) {
|
|
71462
72850
|
return {
|
|
@@ -71465,6 +72853,10 @@ async function runMiner(options) {
|
|
|
71465
72853
|
};
|
|
71466
72854
|
}
|
|
71467
72855
|
notesWritten += 1;
|
|
72856
|
+
return {
|
|
72857
|
+
behavior: "allow",
|
|
72858
|
+
updatedInput: lastSessionId ? { ...input2, transcript_id: lastSessionId } : input2
|
|
72859
|
+
};
|
|
71468
72860
|
}
|
|
71469
72861
|
return { behavior: "allow", updatedInput: input2 };
|
|
71470
72862
|
}
|
|
@@ -71525,6 +72917,7 @@ var DEFAULT_MAX_TURNS = 160, DEFAULT_MAX_NOTES = 80, DEFAULT_TIMEOUT_MS2, KNOWLE
|
|
|
71525
72917
|
var init_runner = __esm(() => {
|
|
71526
72918
|
init_logger();
|
|
71527
72919
|
init_config_helpers();
|
|
72920
|
+
init_notes();
|
|
71528
72921
|
init_config_dir();
|
|
71529
72922
|
init_conflicts();
|
|
71530
72923
|
init_env();
|
|
@@ -71548,17 +72941,17 @@ var init_runner = __esm(() => {
|
|
|
71548
72941
|
});
|
|
71549
72942
|
|
|
71550
72943
|
// src/version/skill-update-check.ts
|
|
71551
|
-
import { existsSync as existsSync17, mkdirSync as
|
|
71552
|
-
import { join as
|
|
72944
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync10, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "node:fs";
|
|
72945
|
+
import { join as join20 } from "node:path";
|
|
71553
72946
|
function getCachePath2() {
|
|
71554
|
-
return
|
|
72947
|
+
return join20(getConfigDir(), CACHE_FILENAME3);
|
|
71555
72948
|
}
|
|
71556
72949
|
function readSkillCache() {
|
|
71557
72950
|
try {
|
|
71558
72951
|
const path3 = getCachePath2();
|
|
71559
72952
|
if (!existsSync17(path3))
|
|
71560
72953
|
return null;
|
|
71561
|
-
const data = JSON.parse(
|
|
72954
|
+
const data = JSON.parse(readFileSync18(path3, "utf-8"));
|
|
71562
72955
|
if (typeof data.lastCheck === "number" && typeof data.latestSha === "string" && typeof data.installedSha === "string") {
|
|
71563
72956
|
return data;
|
|
71564
72957
|
}
|
|
@@ -71571,9 +72964,9 @@ function writeSkillCache(cache) {
|
|
|
71571
72964
|
try {
|
|
71572
72965
|
const dir = getConfigDir();
|
|
71573
72966
|
if (!existsSync17(dir)) {
|
|
71574
|
-
|
|
72967
|
+
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
71575
72968
|
}
|
|
71576
|
-
|
|
72969
|
+
writeFileSync11(getCachePath2(), JSON.stringify(cache), { mode: 384 });
|
|
71577
72970
|
} catch {}
|
|
71578
72971
|
}
|
|
71579
72972
|
function clearInstalledSha() {
|
|
@@ -71647,7 +73040,7 @@ var init_skill_update_check = __esm(() => {
|
|
|
71647
73040
|
// src/commands/skill.ts
|
|
71648
73041
|
import { exec as exec2, execSync as execSync2 } from "node:child_process";
|
|
71649
73042
|
import { homedir as homedir6 } from "node:os";
|
|
71650
|
-
import { join as
|
|
73043
|
+
import { join as join21 } from "node:path";
|
|
71651
73044
|
function skillAgentIDsForProviders(providerIDs) {
|
|
71652
73045
|
return [
|
|
71653
73046
|
...new Set(providerIDs.map((providerID) => SKILL_AGENT_BY_PROVIDER[providerID]).filter((agent) => Boolean(agent)))
|
|
@@ -71658,17 +73051,17 @@ function skillInstallTargetForProvider(providerID) {
|
|
|
71658
73051
|
if (!agentID)
|
|
71659
73052
|
return null;
|
|
71660
73053
|
if (agentID === "claude-code") {
|
|
71661
|
-
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR?.trim() ||
|
|
71662
|
-
return { path:
|
|
73054
|
+
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR?.trim() || join21(homedir6(), ".claude");
|
|
73055
|
+
return { path: join21(claudeConfigDir, "skills", SKILL_NAME), symlink: true };
|
|
71663
73056
|
}
|
|
71664
73057
|
if (agentID === "windsurf") {
|
|
71665
73058
|
return {
|
|
71666
|
-
path:
|
|
73059
|
+
path: join21(homedir6(), ".codeium", "windsurf", "skills", SKILL_NAME),
|
|
71667
73060
|
symlink: true
|
|
71668
73061
|
};
|
|
71669
73062
|
}
|
|
71670
73063
|
return {
|
|
71671
|
-
path:
|
|
73064
|
+
path: join21(homedir6(), ".agents", "skills", SKILL_NAME),
|
|
71672
73065
|
symlink: false
|
|
71673
73066
|
};
|
|
71674
73067
|
}
|
|
@@ -71677,12 +73070,12 @@ function skillAgentArgs(providerIDs) {
|
|
|
71677
73070
|
return agents.map((agent) => `-a ${agent}`).join(" ");
|
|
71678
73071
|
}
|
|
71679
73072
|
function execQuiet(command) {
|
|
71680
|
-
return new Promise((
|
|
73073
|
+
return new Promise((resolve6, reject) => {
|
|
71681
73074
|
exec2(command, { windowsHide: true }, (error61) => {
|
|
71682
73075
|
if (error61)
|
|
71683
73076
|
reject(error61);
|
|
71684
73077
|
else
|
|
71685
|
-
|
|
73078
|
+
resolve6();
|
|
71686
73079
|
});
|
|
71687
73080
|
});
|
|
71688
73081
|
}
|
|
@@ -71829,15 +73222,15 @@ import { randomUUID as randomUUID3 } from "node:crypto";
|
|
|
71829
73222
|
import {
|
|
71830
73223
|
existsSync as existsSync18,
|
|
71831
73224
|
lstatSync as lstatSync2,
|
|
71832
|
-
mkdirSync as
|
|
71833
|
-
readFileSync as
|
|
73225
|
+
mkdirSync as mkdirSync11,
|
|
73226
|
+
readFileSync as readFileSync19,
|
|
71834
73227
|
renameSync as renameSync5,
|
|
71835
73228
|
unlinkSync as unlinkSync3,
|
|
71836
|
-
writeFileSync as
|
|
73229
|
+
writeFileSync as writeFileSync12
|
|
71837
73230
|
} from "node:fs";
|
|
71838
|
-
import { join as
|
|
73231
|
+
import { join as join22 } from "node:path";
|
|
71839
73232
|
function getTelemetrySettingsPath() {
|
|
71840
|
-
return
|
|
73233
|
+
return join22(getConfigDir(), TELEMETRY_SETTINGS_FILENAME);
|
|
71841
73234
|
}
|
|
71842
73235
|
function loadTelemetrySettings() {
|
|
71843
73236
|
return readTelemetrySettings().settings;
|
|
@@ -71905,7 +73298,7 @@ function readTelemetrySettings() {
|
|
|
71905
73298
|
}
|
|
71906
73299
|
let content;
|
|
71907
73300
|
try {
|
|
71908
|
-
content =
|
|
73301
|
+
content = readFileSync19(path3, "utf-8");
|
|
71909
73302
|
} catch {
|
|
71910
73303
|
return { status: "error", settings: disabledTelemetrySettings() };
|
|
71911
73304
|
}
|
|
@@ -71938,8 +73331,8 @@ function writeTelemetrySettings(settings2) {
|
|
|
71938
73331
|
const temporaryPath = `${path3}.${process.pid}.${randomUUID3()}.tmp`;
|
|
71939
73332
|
try {
|
|
71940
73333
|
if (!existsSync18(dir))
|
|
71941
|
-
|
|
71942
|
-
|
|
73334
|
+
mkdirSync11(dir, { recursive: true, mode: 448 });
|
|
73335
|
+
writeFileSync12(temporaryPath, `${JSON.stringify(settings2, null, 2)}
|
|
71943
73336
|
`, {
|
|
71944
73337
|
flag: "wx",
|
|
71945
73338
|
mode: 384
|
|
@@ -71983,14 +73376,14 @@ var init_settings = __esm(() => {
|
|
|
71983
73376
|
});
|
|
71984
73377
|
|
|
71985
73378
|
// src/telemetry/debug-id.ts
|
|
71986
|
-
var BUNDLE_DEBUG_ID_PLACEHOLDER = "
|
|
73379
|
+
var BUNDLE_DEBUG_ID_PLACEHOLDER = "d438e963-a417-e96d-6475-6e2164756e21";
|
|
71987
73380
|
|
|
71988
73381
|
// src/telemetry/telemetry.ts
|
|
71989
73382
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
71990
73383
|
import { existsSync as existsSync19 } from "node:fs";
|
|
71991
73384
|
import { request as httpRequest2 } from "node:http";
|
|
71992
73385
|
import { request as httpsRequest } from "node:https";
|
|
71993
|
-
import { isAbsolute as isAbsolute4, relative as relative2, resolve as
|
|
73386
|
+
import { isAbsolute as isAbsolute4, relative as relative2, resolve as resolve6, sep as sep5 } from "node:path";
|
|
71994
73387
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
71995
73388
|
function safeString(value, pattern, fallback) {
|
|
71996
73389
|
if (typeof value !== "string" || !pattern.test(value))
|
|
@@ -72116,7 +73509,7 @@ function parseOwnedStackLine(originalLine, packageRoot) {
|
|
|
72116
73509
|
}
|
|
72117
73510
|
if (!isAbsolute4(absolutePath))
|
|
72118
73511
|
return;
|
|
72119
|
-
const resolvedPath =
|
|
73512
|
+
const resolvedPath = resolve6(absolutePath);
|
|
72120
73513
|
const relativePath = relative2(packageRoot, resolvedPath);
|
|
72121
73514
|
if (relativePath === "" || relativePath === ".." || relativePath.startsWith(`..${sep5}`) || isAbsolute4(relativePath)) {
|
|
72122
73515
|
return;
|
|
@@ -72354,7 +73747,7 @@ function fetchWithoutRedirect(url2, init = {}) {
|
|
|
72354
73747
|
} catch (error61) {
|
|
72355
73748
|
return Promise.reject(error61);
|
|
72356
73749
|
}
|
|
72357
|
-
return new Promise((
|
|
73750
|
+
return new Promise((resolve7, reject) => {
|
|
72358
73751
|
let request;
|
|
72359
73752
|
let response;
|
|
72360
73753
|
let settled = false;
|
|
@@ -72378,7 +73771,7 @@ function fetchWithoutRedirect(url2, init = {}) {
|
|
|
72378
73771
|
response?.destroy();
|
|
72379
73772
|
request?.socket?.destroy();
|
|
72380
73773
|
request?.destroy();
|
|
72381
|
-
|
|
73774
|
+
resolve7(value);
|
|
72382
73775
|
};
|
|
72383
73776
|
const onAbort = () => {
|
|
72384
73777
|
const error61 = new Error("telemetry request aborted");
|
|
@@ -72851,7 +74244,7 @@ var init_antigravity = __esm(() => {
|
|
|
72851
74244
|
});
|
|
72852
74245
|
|
|
72853
74246
|
// src/mcp/providers/claude.ts
|
|
72854
|
-
import { join as
|
|
74247
|
+
import { join as join23 } from "node:path";
|
|
72855
74248
|
var ClaudeProvider = () => createJSONProvider({
|
|
72856
74249
|
providerName: "Claude Code",
|
|
72857
74250
|
providerID: "claude",
|
|
@@ -72860,24 +74253,24 @@ var ClaudeProvider = () => createJSONProvider({
|
|
|
72860
74253
|
paths: ["~/.claude"],
|
|
72861
74254
|
globalPath: "~/.claude.json",
|
|
72862
74255
|
topKey: "mcpServers",
|
|
72863
|
-
localConfigPath: (cwd) =>
|
|
74256
|
+
localConfigPath: (cwd) => join23(cwd, ".mcp.json")
|
|
72864
74257
|
});
|
|
72865
74258
|
var init_claude = __esm(() => {
|
|
72866
74259
|
init_base();
|
|
72867
74260
|
});
|
|
72868
74261
|
|
|
72869
74262
|
// src/mcp/providers/claude-desktop.ts
|
|
72870
|
-
import { join as
|
|
74263
|
+
import { join as join24 } from "node:path";
|
|
72871
74264
|
function configPath() {
|
|
72872
|
-
return
|
|
74265
|
+
return join24(appSupportDir(), "Claude", "claude_desktop_config.json");
|
|
72873
74266
|
}
|
|
72874
74267
|
var ClaudeDesktopProvider = () => ({
|
|
72875
74268
|
name: () => "Claude Desktop",
|
|
72876
74269
|
id: () => "claude-desktop",
|
|
72877
74270
|
supportsLocal: () => false,
|
|
72878
74271
|
priority: () => 2,
|
|
72879
|
-
detectPaths: () => [
|
|
72880
|
-
isInstalled: () => isInstalled([
|
|
74272
|
+
detectPaths: () => [join24(appSupportDir(), "Claude")],
|
|
74273
|
+
isInstalled: () => isInstalled([join24(appSupportDir(), "Claude")]),
|
|
72881
74274
|
globalConfigPath: () => configPath(),
|
|
72882
74275
|
isConfigured: () => isJSONKeyConfigured(configPath(), "mcpServers"),
|
|
72883
74276
|
install(cfg, global2) {
|
|
@@ -72907,14 +74300,14 @@ var init_claude_desktop = __esm(() => {
|
|
|
72907
74300
|
});
|
|
72908
74301
|
|
|
72909
74302
|
// src/mcp/providers/cline.ts
|
|
72910
|
-
import { join as
|
|
72911
|
-
var extensionDir = () =>
|
|
74303
|
+
import { join as join25 } from "node:path";
|
|
74304
|
+
var extensionDir = () => join25(appSupportDir(), "Code", "User", "globalStorage", "saoudrizwan.claude-dev"), ClineProvider = () => createJSONProvider({
|
|
72912
74305
|
providerName: "Cline",
|
|
72913
74306
|
providerID: "cline",
|
|
72914
74307
|
local: false,
|
|
72915
74308
|
priorityValue: 11,
|
|
72916
74309
|
paths: [extensionDir()],
|
|
72917
|
-
globalPath:
|
|
74310
|
+
globalPath: join25(extensionDir(), "settings", "cline_mcp_settings.json"),
|
|
72918
74311
|
topKey: "mcpServers",
|
|
72919
74312
|
buildServer: (cfg) => ({
|
|
72920
74313
|
url: mcpURL(cfg.active_account.target.deployment_id),
|
|
@@ -72930,7 +74323,7 @@ var init_cline = __esm(() => {
|
|
|
72930
74323
|
});
|
|
72931
74324
|
|
|
72932
74325
|
// src/mcp/providers/cline-cli.ts
|
|
72933
|
-
import { join as
|
|
74326
|
+
import { join as join26 } from "node:path";
|
|
72934
74327
|
function clineDir() {
|
|
72935
74328
|
return process.env.CLINE_DIR ?? expandHome("~/.cline");
|
|
72936
74329
|
}
|
|
@@ -72940,7 +74333,7 @@ var ClineCliProvider = () => createJSONProvider({
|
|
|
72940
74333
|
local: false,
|
|
72941
74334
|
priorityValue: 12,
|
|
72942
74335
|
paths: [clineDir()],
|
|
72943
|
-
globalPath:
|
|
74336
|
+
globalPath: join26(clineDir(), "data", "settings", "cline_mcp_settings.json"),
|
|
72944
74337
|
topKey: "mcpServers",
|
|
72945
74338
|
buildServer: (cfg) => ({
|
|
72946
74339
|
url: mcpURL(cfg.active_account.target.deployment_id),
|
|
@@ -72956,20 +74349,20 @@ var init_cline_cli = __esm(() => {
|
|
|
72956
74349
|
});
|
|
72957
74350
|
|
|
72958
74351
|
// src/mcp/providers/codex.ts
|
|
72959
|
-
import { existsSync as existsSync20, readFileSync as
|
|
72960
|
-
import { join as
|
|
74352
|
+
import { existsSync as existsSync20, readFileSync as readFileSync20 } from "node:fs";
|
|
74353
|
+
import { join as join27 } from "node:path";
|
|
72961
74354
|
function codexHome2() {
|
|
72962
74355
|
return process.env.CODEX_HOME ?? expandHome("~/.codex");
|
|
72963
74356
|
}
|
|
72964
74357
|
function getConfigPath2(global2) {
|
|
72965
74358
|
if (global2)
|
|
72966
|
-
return
|
|
72967
|
-
return
|
|
74359
|
+
return join27(codexHome2(), "config.toml");
|
|
74360
|
+
return join27(process.cwd(), ".codex", "config.toml");
|
|
72968
74361
|
}
|
|
72969
74362
|
function readTOML(path3) {
|
|
72970
74363
|
if (!existsSync20(path3))
|
|
72971
74364
|
return "";
|
|
72972
|
-
return
|
|
74365
|
+
return readFileSync20(path3, "utf-8");
|
|
72973
74366
|
}
|
|
72974
74367
|
function writeTOML(path3, content) {
|
|
72975
74368
|
writeSecureFile(path3, content);
|
|
@@ -73032,9 +74425,9 @@ var CodexProvider = () => ({
|
|
|
73032
74425
|
priority: () => 8,
|
|
73033
74426
|
detectPaths: () => ["~/.codex"],
|
|
73034
74427
|
isInstalled: () => isInstalled(["~/.codex"]),
|
|
73035
|
-
globalConfigPath: () =>
|
|
74428
|
+
globalConfigPath: () => join27(codexHome2(), "config.toml"),
|
|
73036
74429
|
isConfigured: () => {
|
|
73037
|
-
const content = readTOML(
|
|
74430
|
+
const content = readTOML(join27(codexHome2(), "config.toml"));
|
|
73038
74431
|
return content.includes("[mcp_servers.dosu]");
|
|
73039
74432
|
},
|
|
73040
74433
|
install(cfg, global2) {
|
|
@@ -73056,10 +74449,10 @@ var init_codex = __esm(() => {
|
|
|
73056
74449
|
});
|
|
73057
74450
|
|
|
73058
74451
|
// src/mcp/providers/copilot.ts
|
|
73059
|
-
import { join as
|
|
74452
|
+
import { join as join28 } from "node:path";
|
|
73060
74453
|
function globalPath() {
|
|
73061
74454
|
if (process.env.XDG_CONFIG_HOME) {
|
|
73062
|
-
return
|
|
74455
|
+
return join28(process.env.XDG_CONFIG_HOME, "mcp-config.json");
|
|
73063
74456
|
}
|
|
73064
74457
|
return expandHome("~/.copilot/mcp-config.json");
|
|
73065
74458
|
}
|
|
@@ -73090,7 +74483,7 @@ var CopilotProvider = () => ({
|
|
|
73090
74483
|
};
|
|
73091
74484
|
installJSONServer(globalPath(), "mcpServers", server);
|
|
73092
74485
|
} else {
|
|
73093
|
-
const configPath2 =
|
|
74486
|
+
const configPath2 = join28(process.cwd(), ".vscode", "mcp.json");
|
|
73094
74487
|
const server = {
|
|
73095
74488
|
type: "http",
|
|
73096
74489
|
url: url2,
|
|
@@ -73103,7 +74496,7 @@ var CopilotProvider = () => ({
|
|
|
73103
74496
|
if (global2) {
|
|
73104
74497
|
removeJSONServer(globalPath(), "mcpServers");
|
|
73105
74498
|
} else {
|
|
73106
|
-
const configPath2 =
|
|
74499
|
+
const configPath2 = join28(process.cwd(), ".vscode", "mcp.json");
|
|
73107
74500
|
removeJSONServer(configPath2, "servers");
|
|
73108
74501
|
}
|
|
73109
74502
|
}
|
|
@@ -73115,7 +74508,7 @@ var init_copilot = __esm(() => {
|
|
|
73115
74508
|
});
|
|
73116
74509
|
|
|
73117
74510
|
// src/mcp/providers/cursor.ts
|
|
73118
|
-
import { join as
|
|
74511
|
+
import { join as join29 } from "node:path";
|
|
73119
74512
|
var CursorProvider = () => createJSONProvider({
|
|
73120
74513
|
providerName: "Cursor",
|
|
73121
74514
|
providerID: "cursor",
|
|
@@ -73128,7 +74521,7 @@ var CursorProvider = () => createJSONProvider({
|
|
|
73128
74521
|
url: mcpURL(cfg.active_account.target.deployment_id),
|
|
73129
74522
|
headers: mcpHeaders(cfg.active_account.target.api_key)
|
|
73130
74523
|
}),
|
|
73131
|
-
localConfigPath: (cwd) =>
|
|
74524
|
+
localConfigPath: (cwd) => join29(cwd, ".cursor", "mcp.json")
|
|
73132
74525
|
});
|
|
73133
74526
|
var init_cursor = __esm(() => {
|
|
73134
74527
|
init_config_helpers();
|
|
@@ -73136,7 +74529,7 @@ var init_cursor = __esm(() => {
|
|
|
73136
74529
|
});
|
|
73137
74530
|
|
|
73138
74531
|
// src/mcp/providers/factory.ts
|
|
73139
|
-
import { join as
|
|
74532
|
+
import { join as join30 } from "node:path";
|
|
73140
74533
|
var FactoryProvider = () => createJSONProvider({
|
|
73141
74534
|
providerName: "Factory",
|
|
73142
74535
|
providerID: "factory",
|
|
@@ -73145,14 +74538,14 @@ var FactoryProvider = () => createJSONProvider({
|
|
|
73145
74538
|
paths: ["~/.factory"],
|
|
73146
74539
|
globalPath: "~/.factory/mcp.json",
|
|
73147
74540
|
topKey: "mcpServers",
|
|
73148
|
-
localConfigPath: (cwd) =>
|
|
74541
|
+
localConfigPath: (cwd) => join30(cwd, ".factory", "mcp.json")
|
|
73149
74542
|
});
|
|
73150
74543
|
var init_factory = __esm(() => {
|
|
73151
74544
|
init_base();
|
|
73152
74545
|
});
|
|
73153
74546
|
|
|
73154
74547
|
// src/mcp/providers/gemini.ts
|
|
73155
|
-
import { join as
|
|
74548
|
+
import { join as join31 } from "node:path";
|
|
73156
74549
|
var GeminiProvider = () => createJSONProvider({
|
|
73157
74550
|
providerName: "Gemini CLI",
|
|
73158
74551
|
providerID: "gemini",
|
|
@@ -73161,7 +74554,7 @@ var GeminiProvider = () => createJSONProvider({
|
|
|
73161
74554
|
paths: ["~/.gemini"],
|
|
73162
74555
|
globalPath: "~/.gemini/settings.json",
|
|
73163
74556
|
topKey: "mcpServers",
|
|
73164
|
-
localConfigPath: (cwd) =>
|
|
74557
|
+
localConfigPath: (cwd) => join31(cwd, ".gemini", "settings.json")
|
|
73165
74558
|
});
|
|
73166
74559
|
var init_gemini = __esm(() => {
|
|
73167
74560
|
init_base();
|
|
@@ -73212,7 +74605,7 @@ var init_manual = __esm(() => {
|
|
|
73212
74605
|
|
|
73213
74606
|
// src/mcp/providers/mcporter.ts
|
|
73214
74607
|
import { existsSync as existsSync21 } from "node:fs";
|
|
73215
|
-
import { join as
|
|
74608
|
+
import { join as join32 } from "node:path";
|
|
73216
74609
|
function resolveGlobalConfigPath() {
|
|
73217
74610
|
const jsonPath = expandHome("~/.mcporter/mcporter.json");
|
|
73218
74611
|
if (existsSync21(jsonPath))
|
|
@@ -73239,7 +74632,7 @@ var MCPorterProvider = () => ({
|
|
|
73239
74632
|
globalConfigPath: () => resolveGlobalConfigPath(),
|
|
73240
74633
|
isConfigured: () => isJSONKeyConfigured(resolveGlobalConfigPath(), "mcpServers"),
|
|
73241
74634
|
install(cfg, global2) {
|
|
73242
|
-
const configPath2 = global2 ? resolveGlobalConfigPath() :
|
|
74635
|
+
const configPath2 = global2 ? resolveGlobalConfigPath() : join32(process.cwd(), "config", "mcporter.json");
|
|
73243
74636
|
const server = {
|
|
73244
74637
|
type: "http",
|
|
73245
74638
|
url: mcpEndpoint4(cfg),
|
|
@@ -73248,7 +74641,7 @@ var MCPorterProvider = () => ({
|
|
|
73248
74641
|
installJSONServer(configPath2, "mcpServers", server);
|
|
73249
74642
|
},
|
|
73250
74643
|
remove(global2) {
|
|
73251
|
-
const configPath2 = global2 ? resolveGlobalConfigPath() :
|
|
74644
|
+
const configPath2 = global2 ? resolveGlobalConfigPath() : join32(process.cwd(), "config", "mcporter.json");
|
|
73252
74645
|
removeJSONServer(configPath2, "mcpServers");
|
|
73253
74646
|
}
|
|
73254
74647
|
});
|
|
@@ -73259,7 +74652,7 @@ var init_mcporter = __esm(() => {
|
|
|
73259
74652
|
});
|
|
73260
74653
|
|
|
73261
74654
|
// src/mcp/providers/opencode.ts
|
|
73262
|
-
import { join as
|
|
74655
|
+
import { join as join33 } from "node:path";
|
|
73263
74656
|
var OpenCodeProvider = () => createJSONProvider({
|
|
73264
74657
|
providerName: "OpenCode",
|
|
73265
74658
|
providerID: "opencode",
|
|
@@ -73274,7 +74667,7 @@ var OpenCodeProvider = () => createJSONProvider({
|
|
|
73274
74667
|
enabled: true,
|
|
73275
74668
|
headers: mcpHeaders(cfg.active_account.target.api_key)
|
|
73276
74669
|
}),
|
|
73277
|
-
localConfigPath: (cwd) =>
|
|
74670
|
+
localConfigPath: (cwd) => join33(cwd, "opencode.json")
|
|
73278
74671
|
});
|
|
73279
74672
|
var init_opencode = __esm(() => {
|
|
73280
74673
|
init_config_helpers();
|
|
@@ -73282,16 +74675,16 @@ var init_opencode = __esm(() => {
|
|
|
73282
74675
|
});
|
|
73283
74676
|
|
|
73284
74677
|
// src/mcp/providers/vscode.ts
|
|
73285
|
-
import { join as
|
|
74678
|
+
import { join as join34 } from "node:path";
|
|
73286
74679
|
var VSCodeProvider = () => createJSONProvider({
|
|
73287
74680
|
providerName: "VS Code",
|
|
73288
74681
|
providerID: "vscode",
|
|
73289
74682
|
local: true,
|
|
73290
74683
|
priorityValue: 6,
|
|
73291
|
-
paths: [
|
|
73292
|
-
globalPath:
|
|
74684
|
+
paths: [join34(appSupportDir(), "Code")],
|
|
74685
|
+
globalPath: join34(appSupportDir(), "Code", "User", "mcp.json"),
|
|
73293
74686
|
topKey: "servers",
|
|
73294
|
-
localConfigPath: (cwd) =>
|
|
74687
|
+
localConfigPath: (cwd) => join34(cwd, ".vscode", "mcp.json")
|
|
73295
74688
|
});
|
|
73296
74689
|
var init_vscode = __esm(() => {
|
|
73297
74690
|
init_detect();
|
|
@@ -73300,14 +74693,14 @@ var init_vscode = __esm(() => {
|
|
|
73300
74693
|
|
|
73301
74694
|
// src/mcp/providers/windsurf.ts
|
|
73302
74695
|
import { homedir as homedir8 } from "node:os";
|
|
73303
|
-
import { join as
|
|
74696
|
+
import { join as join35 } from "node:path";
|
|
73304
74697
|
var WindsurfProvider = () => createJSONProvider({
|
|
73305
74698
|
providerName: "Windsurf",
|
|
73306
74699
|
providerID: "windsurf",
|
|
73307
74700
|
local: false,
|
|
73308
74701
|
priorityValue: 9,
|
|
73309
|
-
paths: [
|
|
73310
|
-
globalPath:
|
|
74702
|
+
paths: [join35(homedir8(), ".codeium", "windsurf")],
|
|
74703
|
+
globalPath: join35(homedir8(), ".codeium", "windsurf", "mcp_config.json"),
|
|
73311
74704
|
topKey: "mcpServers"
|
|
73312
74705
|
});
|
|
73313
74706
|
var init_windsurf = __esm(() => {
|
|
@@ -73316,12 +74709,12 @@ var init_windsurf = __esm(() => {
|
|
|
73316
74709
|
|
|
73317
74710
|
// src/mcp/providers/zed.ts
|
|
73318
74711
|
import { platform as platform3 } from "node:os";
|
|
73319
|
-
import { join as
|
|
74712
|
+
import { join as join36 } from "node:path";
|
|
73320
74713
|
function zedConfigDir() {
|
|
73321
74714
|
const os3 = platform3();
|
|
73322
74715
|
if (os3 === "darwin" || os3 === "win32")
|
|
73323
|
-
return
|
|
73324
|
-
return
|
|
74716
|
+
return join36(appSupportDir(), "Zed");
|
|
74717
|
+
return join36(appSupportDir(), "zed");
|
|
73325
74718
|
}
|
|
73326
74719
|
var ZedProvider = () => createJSONProvider({
|
|
73327
74720
|
providerName: "Zed",
|
|
@@ -73329,7 +74722,7 @@ var ZedProvider = () => createJSONProvider({
|
|
|
73329
74722
|
local: true,
|
|
73330
74723
|
priorityValue: 10,
|
|
73331
74724
|
paths: [zedConfigDir()],
|
|
73332
|
-
globalPath:
|
|
74725
|
+
globalPath: join36(zedConfigDir(), "settings.json"),
|
|
73333
74726
|
topKey: "context_servers",
|
|
73334
74727
|
buildServer: (cfg) => ({
|
|
73335
74728
|
source: "custom",
|
|
@@ -73337,7 +74730,7 @@ var ZedProvider = () => createJSONProvider({
|
|
|
73337
74730
|
url: mcpURL(cfg.active_account.target.deployment_id),
|
|
73338
74731
|
headers: mcpHeaders(cfg.active_account.target.api_key)
|
|
73339
74732
|
}),
|
|
73340
|
-
localConfigPath: (cwd) =>
|
|
74733
|
+
localConfigPath: (cwd) => join36(cwd, ".zed", "settings.json")
|
|
73341
74734
|
});
|
|
73342
74735
|
var init_zed = __esm(() => {
|
|
73343
74736
|
init_config_helpers();
|
|
@@ -73396,8 +74789,8 @@ var init_providers = __esm(() => {
|
|
|
73396
74789
|
});
|
|
73397
74790
|
|
|
73398
74791
|
// src/version/update-check.ts
|
|
73399
|
-
import { existsSync as existsSync22, mkdirSync as
|
|
73400
|
-
import { join as
|
|
74792
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync21, writeFileSync as writeFileSync13 } from "node:fs";
|
|
74793
|
+
import { join as join37 } from "node:path";
|
|
73401
74794
|
function isValidVersion(value) {
|
|
73402
74795
|
return typeof value === "string" && SEMVER_PATTERN.test(value);
|
|
73403
74796
|
}
|
|
@@ -73419,14 +74812,14 @@ function isNewerVersion(latest, current) {
|
|
|
73419
74812
|
return false;
|
|
73420
74813
|
}
|
|
73421
74814
|
function getCachePath3() {
|
|
73422
|
-
return
|
|
74815
|
+
return join37(getConfigDir(), CACHE_FILENAME4);
|
|
73423
74816
|
}
|
|
73424
74817
|
function readCache() {
|
|
73425
74818
|
try {
|
|
73426
74819
|
const path3 = getCachePath3();
|
|
73427
74820
|
if (!existsSync22(path3))
|
|
73428
74821
|
return null;
|
|
73429
|
-
const data = JSON.parse(
|
|
74822
|
+
const data = JSON.parse(readFileSync21(path3, "utf-8"));
|
|
73430
74823
|
if (typeof data.lastCheck === "number" && isValidVersion(data.latestVersion)) {
|
|
73431
74824
|
return data;
|
|
73432
74825
|
}
|
|
@@ -73439,9 +74832,9 @@ function writeCache(cache) {
|
|
|
73439
74832
|
try {
|
|
73440
74833
|
const dir = getConfigDir();
|
|
73441
74834
|
if (!existsSync22(dir)) {
|
|
73442
|
-
|
|
74835
|
+
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
73443
74836
|
}
|
|
73444
|
-
|
|
74837
|
+
writeFileSync13(getCachePath3(), JSON.stringify(cache), { mode: 384 });
|
|
73445
74838
|
} catch {}
|
|
73446
74839
|
}
|
|
73447
74840
|
async function fetchLatestVersion() {
|
|
@@ -73551,14 +74944,14 @@ var init_update_check = __esm(() => {
|
|
|
73551
74944
|
});
|
|
73552
74945
|
|
|
73553
74946
|
// src/rules/installer.ts
|
|
73554
|
-
import { existsSync as existsSync23, mkdirSync as
|
|
74947
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync22, unlinkSync as unlinkSync4, writeFileSync as writeFileSync14 } from "node:fs";
|
|
73555
74948
|
import { homedir as homedir9 } from "node:os";
|
|
73556
|
-
import { dirname as
|
|
74949
|
+
import { dirname as dirname9, join as join38 } from "node:path";
|
|
73557
74950
|
function claudeConfigDir() {
|
|
73558
|
-
return process.env.CLAUDE_CONFIG_DIR ||
|
|
74951
|
+
return process.env.CLAUDE_CONFIG_DIR || join38(homedir9(), ".claude");
|
|
73559
74952
|
}
|
|
73560
74953
|
function codexHome3() {
|
|
73561
|
-
return process.env.CODEX_HOME ||
|
|
74954
|
+
return process.env.CODEX_HOME || join38(homedir9(), ".codex");
|
|
73562
74955
|
}
|
|
73563
74956
|
function isRuleAgent(agent) {
|
|
73564
74957
|
return Object.hasOwn(RULE_TARGETS, agent);
|
|
@@ -73584,17 +74977,17 @@ async function fetchDosuRule(fetchImpl = globalThis.fetch) {
|
|
|
73584
74977
|
return normalizeRule(FALLBACK_DOSU_RULE);
|
|
73585
74978
|
}
|
|
73586
74979
|
function ensureParent(path3) {
|
|
73587
|
-
|
|
74980
|
+
mkdirSync13(dirname9(path3), { recursive: true });
|
|
73588
74981
|
}
|
|
73589
74982
|
function writeIfChanged(path3, content) {
|
|
73590
74983
|
if (!existsSync23(path3)) {
|
|
73591
74984
|
ensureParent(path3);
|
|
73592
|
-
|
|
74985
|
+
writeFileSync14(path3, content, "utf-8");
|
|
73593
74986
|
return "created";
|
|
73594
74987
|
}
|
|
73595
|
-
if (
|
|
74988
|
+
if (readFileSync22(path3, "utf-8") === content)
|
|
73596
74989
|
return "unchanged";
|
|
73597
|
-
|
|
74990
|
+
writeFileSync14(path3, content, "utf-8");
|
|
73598
74991
|
return "updated";
|
|
73599
74992
|
}
|
|
73600
74993
|
function findSection(content) {
|
|
@@ -73623,11 +75016,11 @@ function upsertSection(path3, content) {
|
|
|
73623
75016
|
if (!existsSync23(path3)) {
|
|
73624
75017
|
const section2 = buildSection(content);
|
|
73625
75018
|
ensureParent(path3);
|
|
73626
|
-
|
|
75019
|
+
writeFileSync14(path3, `${section2}
|
|
73627
75020
|
`, "utf-8");
|
|
73628
75021
|
return "created";
|
|
73629
75022
|
}
|
|
73630
|
-
const existing =
|
|
75023
|
+
const existing = readFileSync22(path3, "utf-8");
|
|
73631
75024
|
const lineEnding = lineEndingFor(existing);
|
|
73632
75025
|
const section = buildSection(content, lineEnding);
|
|
73633
75026
|
const location = findSection(existing);
|
|
@@ -73640,7 +75033,7 @@ function upsertSection(path3, content) {
|
|
|
73640
75033
|
}
|
|
73641
75034
|
if (next === existing)
|
|
73642
75035
|
return "unchanged";
|
|
73643
|
-
|
|
75036
|
+
writeFileSync14(path3, next, "utf-8");
|
|
73644
75037
|
return "updated";
|
|
73645
75038
|
}
|
|
73646
75039
|
function installRuleForAgent(agent, content) {
|
|
@@ -73663,7 +75056,7 @@ function removeRuleForAgent(agent) {
|
|
|
73663
75056
|
unlinkSync4(path3);
|
|
73664
75057
|
return { agent, path: path3, action: "removed" };
|
|
73665
75058
|
}
|
|
73666
|
-
const existing =
|
|
75059
|
+
const existing = readFileSync22(path3, "utf-8");
|
|
73667
75060
|
const lineEnding = lineEndingFor(existing);
|
|
73668
75061
|
const location = findSection(existing);
|
|
73669
75062
|
if (!location)
|
|
@@ -73672,7 +75065,7 @@ function removeRuleForAgent(agent) {
|
|
|
73672
75065
|
if (!next) {
|
|
73673
75066
|
unlinkSync4(path3);
|
|
73674
75067
|
} else {
|
|
73675
|
-
|
|
75068
|
+
writeFileSync14(path3, `${next}${lineEnding}`, "utf-8");
|
|
73676
75069
|
}
|
|
73677
75070
|
return { agent, path: path3, action: "removed" };
|
|
73678
75071
|
}
|
|
@@ -73700,36 +75093,36 @@ var init_installer = __esm(() => {
|
|
|
73700
75093
|
RULE_TARGETS = {
|
|
73701
75094
|
claude: {
|
|
73702
75095
|
kind: "file",
|
|
73703
|
-
path: () =>
|
|
75096
|
+
path: () => join38(claudeConfigDir(), "rules", "dosu.md")
|
|
73704
75097
|
},
|
|
73705
75098
|
cursor: {
|
|
73706
75099
|
kind: "file",
|
|
73707
|
-
path: () =>
|
|
75100
|
+
path: () => join38(homedir9(), ".cursor", "rules", "dosu.mdc"),
|
|
73708
75101
|
cursorFrontmatter: true
|
|
73709
75102
|
},
|
|
73710
75103
|
codex: {
|
|
73711
75104
|
kind: "section",
|
|
73712
|
-
path: () =>
|
|
75105
|
+
path: () => join38(codexHome3(), "AGENTS.md")
|
|
73713
75106
|
},
|
|
73714
75107
|
opencode: {
|
|
73715
75108
|
kind: "section",
|
|
73716
|
-
path: () =>
|
|
75109
|
+
path: () => join38(homedir9(), ".config", "opencode", "AGENTS.md")
|
|
73717
75110
|
},
|
|
73718
75111
|
gemini: {
|
|
73719
75112
|
kind: "section",
|
|
73720
|
-
path: () =>
|
|
75113
|
+
path: () => join38(homedir9(), ".gemini", "GEMINI.md")
|
|
73721
75114
|
},
|
|
73722
75115
|
antigravity: {
|
|
73723
75116
|
kind: "section",
|
|
73724
|
-
path: () =>
|
|
75117
|
+
path: () => join38(homedir9(), ".gemini", "GEMINI.md")
|
|
73725
75118
|
}
|
|
73726
75119
|
};
|
|
73727
75120
|
});
|
|
73728
75121
|
|
|
73729
75122
|
// src/setup/agents-md-step.ts
|
|
73730
75123
|
import { execSync as execSync3 } from "node:child_process";
|
|
73731
|
-
import { existsSync as existsSync24, readFileSync as
|
|
73732
|
-
import { join as
|
|
75124
|
+
import { existsSync as existsSync24, readFileSync as readFileSync23, writeFileSync as writeFileSync15 } from "node:fs";
|
|
75125
|
+
import { join as join39 } from "node:path";
|
|
73733
75126
|
function inGitWorkTree(cwd = process.cwd()) {
|
|
73734
75127
|
try {
|
|
73735
75128
|
const stdout2 = execSync3("git rev-parse --is-inside-work-tree", {
|
|
@@ -73743,10 +75136,10 @@ function inGitWorkTree(cwd = process.cwd()) {
|
|
|
73743
75136
|
}
|
|
73744
75137
|
function dosuAgentsSectionState(cwd = process.cwd()) {
|
|
73745
75138
|
try {
|
|
73746
|
-
const path3 =
|
|
75139
|
+
const path3 = join39(cwd, "AGENTS.md");
|
|
73747
75140
|
if (!existsSync24(path3))
|
|
73748
75141
|
return "missing";
|
|
73749
|
-
const content =
|
|
75142
|
+
const content = readFileSync23(path3, "utf-8");
|
|
73750
75143
|
const startMatch = content.match(SECTION_START_RE);
|
|
73751
75144
|
if (!startMatch || !content.includes(DOSU_SECTION_END))
|
|
73752
75145
|
return "missing";
|
|
@@ -73781,14 +75174,14 @@ function findSection2(content) {
|
|
|
73781
75174
|
return { start, end };
|
|
73782
75175
|
}
|
|
73783
75176
|
function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RULE) {
|
|
73784
|
-
const path3 =
|
|
75177
|
+
const path3 = join39(cwd, "AGENTS.md");
|
|
73785
75178
|
if (!existsSync24(path3)) {
|
|
73786
75179
|
const section2 = buildDosuAgentsSection(content);
|
|
73787
|
-
|
|
75180
|
+
writeFileSync15(path3, `${section2}
|
|
73788
75181
|
`);
|
|
73789
75182
|
return { path: path3, action: "created" };
|
|
73790
75183
|
}
|
|
73791
|
-
const existing =
|
|
75184
|
+
const existing = readFileSync23(path3, "utf-8");
|
|
73792
75185
|
const lineEnding = lineEndingFor2(existing);
|
|
73793
75186
|
const section = renderDosuAgentsSection(content, lineEnding);
|
|
73794
75187
|
const location = findSection2(existing);
|
|
@@ -73800,7 +75193,7 @@ function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RU
|
|
|
73800
75193
|
}
|
|
73801
75194
|
if (next === existing)
|
|
73802
75195
|
return { path: path3, action: "unchanged" };
|
|
73803
|
-
|
|
75196
|
+
writeFileSync15(path3, next);
|
|
73804
75197
|
return { path: path3, action: "updated" };
|
|
73805
75198
|
}
|
|
73806
75199
|
async function stepUpdateAgentsMd(cwd = process.cwd(), content) {
|
|
@@ -73882,7 +75275,7 @@ var init_alt_screen = __esm(() => {
|
|
|
73882
75275
|
});
|
|
73883
75276
|
|
|
73884
75277
|
// src/tui/activity-view.ts
|
|
73885
|
-
import { readFileSync as
|
|
75278
|
+
import { readFileSync as readFileSync24 } from "node:fs";
|
|
73886
75279
|
function reduceActivityViewKey(key) {
|
|
73887
75280
|
if (key === "q" || key === ESC7 || key === CTRL_C4)
|
|
73888
75281
|
return "back";
|
|
@@ -74074,7 +75467,7 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
|
|
|
74074
75467
|
const mined = status.state.watermark ? `Mined sessions up to ${localTime(status.state.watermark)}` : "Nothing mined yet";
|
|
74075
75468
|
const queueDetail = [];
|
|
74076
75469
|
if (status.backoffUntil) {
|
|
74077
|
-
queueDetail.push(`retrying after ${localTime(status.backoffUntil)}`);
|
|
75470
|
+
queueDetail.push(`retrying after ${localTime(status.backoffUntil)} · s syncs now`);
|
|
74078
75471
|
}
|
|
74079
75472
|
let progress = null;
|
|
74080
75473
|
if (status.running && backlog) {
|
|
@@ -74127,7 +75520,7 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
|
|
|
74127
75520
|
}
|
|
74128
75521
|
function defaultReadLog2() {
|
|
74129
75522
|
try {
|
|
74130
|
-
return
|
|
75523
|
+
return readFileSync24(logger.getLogPath(), "utf-8");
|
|
74131
75524
|
} catch {
|
|
74132
75525
|
return "";
|
|
74133
75526
|
}
|
|
@@ -74140,7 +75533,7 @@ function runActivityView(io2 = {}) {
|
|
|
74140
75533
|
const getStatus = io2.getStatus ?? (() => getSyncStatus({ readLog: () => "" }));
|
|
74141
75534
|
const readLog = io2.readLog ?? defaultReadLog2;
|
|
74142
75535
|
const createFollower = io2.createFollower ?? ((emit) => createLogFollower(logger.getLogPath(), emit));
|
|
74143
|
-
const startSync = io2.startSync ?? (() => spawnDetachedSelf(["knowledge", "sync", "--
|
|
75536
|
+
const startSync = io2.startSync ?? (() => spawnDetachedSelf(["knowledge", "sync", "--bootstrap"]));
|
|
74144
75537
|
const stopSync = io2.stopSync ?? stopSyncRun;
|
|
74145
75538
|
const setPaused = io2.setPaused ?? setSyncPaused;
|
|
74146
75539
|
const listBacklog = io2.listBacklog ?? listSessionBacklog;
|
|
@@ -74212,7 +75605,7 @@ ${CLEAR_BELOW}`);
|
|
|
74212
75605
|
const leaveAltScreen = enterAltScreen(output2);
|
|
74213
75606
|
output2.write(HIDE_CURSOR3);
|
|
74214
75607
|
draw();
|
|
74215
|
-
return new Promise((
|
|
75608
|
+
return new Promise((resolve7) => {
|
|
74216
75609
|
const wasRaw = input2.isRaw ?? false;
|
|
74217
75610
|
input2.setRawMode?.(true);
|
|
74218
75611
|
input2.resume();
|
|
@@ -74229,7 +75622,7 @@ ${CLEAR_BELOW}`);
|
|
|
74229
75622
|
input2.pause();
|
|
74230
75623
|
leaveAltScreen();
|
|
74231
75624
|
output2.write(SHOW_CURSOR3);
|
|
74232
|
-
|
|
75625
|
+
resolve7();
|
|
74233
75626
|
};
|
|
74234
75627
|
const onData = (chunk) => {
|
|
74235
75628
|
for (const key of parseKeys(chunk.toString())) {
|
|
@@ -74748,8 +76141,8 @@ h1 {
|
|
|
74748
76141
|
async function startCallbackServer(opts = {}) {
|
|
74749
76142
|
let resolveToken;
|
|
74750
76143
|
let rejectToken;
|
|
74751
|
-
const tokenPromise = new Promise((
|
|
74752
|
-
resolveToken =
|
|
76144
|
+
const tokenPromise = new Promise((resolve7, reject) => {
|
|
76145
|
+
resolveToken = resolve7;
|
|
74753
76146
|
rejectToken = reject;
|
|
74754
76147
|
});
|
|
74755
76148
|
const http = __require("node:http");
|
|
@@ -74807,8 +76200,8 @@ async function startCallbackServer(opts = {}) {
|
|
|
74807
76200
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
74808
76201
|
res.end(buildSuccessHtml(email3 ?? undefined, opts.successVariant ?? "auth"));
|
|
74809
76202
|
});
|
|
74810
|
-
await new Promise((
|
|
74811
|
-
httpServer.listen(0, "localhost", () =>
|
|
76203
|
+
await new Promise((resolve7) => {
|
|
76204
|
+
httpServer.listen(0, "localhost", () => resolve7());
|
|
74812
76205
|
});
|
|
74813
76206
|
httpServer.unref();
|
|
74814
76207
|
const addr = httpServer.address();
|
|
@@ -76112,7 +77505,7 @@ ${CLEAR_BELOW2}`);
|
|
|
76112
77505
|
pagesPending = false;
|
|
76113
77506
|
draw();
|
|
76114
77507
|
});
|
|
76115
|
-
return new Promise((
|
|
77508
|
+
return new Promise((resolve7) => {
|
|
76116
77509
|
const wasRaw = input2.isRaw ?? false;
|
|
76117
77510
|
input2.setRawMode?.(true);
|
|
76118
77511
|
input2.resume();
|
|
@@ -76127,7 +77520,7 @@ ${CLEAR_BELOW2}`);
|
|
|
76127
77520
|
input2.pause();
|
|
76128
77521
|
leaveAltScreen();
|
|
76129
77522
|
output2.write(SHOW_CURSOR4);
|
|
76130
|
-
|
|
77523
|
+
resolve7();
|
|
76131
77524
|
};
|
|
76132
77525
|
const onData = (chunk) => {
|
|
76133
77526
|
for (const key of parseKeys(chunk.toString())) {
|
|
@@ -76528,7 +77921,7 @@ ${CLEAR_BELOW3}`);
|
|
|
76528
77921
|
});
|
|
76529
77922
|
draw();
|
|
76530
77923
|
};
|
|
76531
|
-
return new Promise((
|
|
77924
|
+
return new Promise((resolve7) => {
|
|
76532
77925
|
const wasRaw = input2.isRaw ?? false;
|
|
76533
77926
|
input2.setRawMode?.(true);
|
|
76534
77927
|
input2.resume();
|
|
@@ -76540,7 +77933,7 @@ ${CLEAR_BELOW3}`);
|
|
|
76540
77933
|
input2.pause();
|
|
76541
77934
|
leaveAltScreen();
|
|
76542
77935
|
output2.write(SHOW_CURSOR5);
|
|
76543
|
-
|
|
77936
|
+
resolve7();
|
|
76544
77937
|
};
|
|
76545
77938
|
const onData = (chunk) => {
|
|
76546
77939
|
for (const key of parseKeys(chunk.toString())) {
|
|
@@ -76731,7 +78124,7 @@ async function runMainMenu() {
|
|
|
76731
78124
|
const buildOptions = () => {
|
|
76732
78125
|
if (!isAuthenticated(cfg)) {
|
|
76733
78126
|
return [
|
|
76734
|
-
{ label: "Log in / Sign up", hint: "opens your browser", value: "auth" },
|
|
78127
|
+
{ label: "Log in / Sign up", hint: "(opens your browser)", value: "auth" },
|
|
76735
78128
|
{ label: "Exit", value: "exit" }
|
|
76736
78129
|
];
|
|
76737
78130
|
}
|
|
@@ -76747,6 +78140,7 @@ async function runMainMenu() {
|
|
|
76747
78140
|
label: mining ? `Activity ⛏️ ${brand("mining sessions...")}` : "Activity",
|
|
76748
78141
|
value: "sync"
|
|
76749
78142
|
},
|
|
78143
|
+
{ label: "Knowledge report", hint: "(opens in browser)", value: "report" },
|
|
76750
78144
|
{ label: "Analytics", value: "analytics" },
|
|
76751
78145
|
{ label: "Pages", value: "pages" },
|
|
76752
78146
|
{ label: "Settings", value: "settings" },
|
|
@@ -76771,6 +78165,10 @@ async function runMainMenu() {
|
|
|
76771
78165
|
await runActivityView();
|
|
76772
78166
|
home();
|
|
76773
78167
|
break;
|
|
78168
|
+
case "report":
|
|
78169
|
+
await runKnowledgeReport();
|
|
78170
|
+
home();
|
|
78171
|
+
break;
|
|
76774
78172
|
case "analytics":
|
|
76775
78173
|
await runAnalyticsView();
|
|
76776
78174
|
home();
|
|
@@ -76798,6 +78196,17 @@ async function runMainMenu() {
|
|
|
76798
78196
|
}
|
|
76799
78197
|
}
|
|
76800
78198
|
}
|
|
78199
|
+
async function runKnowledgeReport() {
|
|
78200
|
+
const s = spinner2();
|
|
78201
|
+
s.start("Writing knowledge report...");
|
|
78202
|
+
try {
|
|
78203
|
+
const path3 = await emitKnowledgeReport({ open: true });
|
|
78204
|
+
s.stop(`Opened ${path3}`);
|
|
78205
|
+
} catch (err) {
|
|
78206
|
+
s.stop("Could not write the report");
|
|
78207
|
+
log2.error(err instanceof Error ? err.message : String(err));
|
|
78208
|
+
}
|
|
78209
|
+
}
|
|
76801
78210
|
async function runSettings(cfg) {
|
|
76802
78211
|
while (true) {
|
|
76803
78212
|
const target = cfg.active_account?.target;
|
|
@@ -76925,6 +78334,7 @@ var init_tui = __esm(() => {
|
|
|
76925
78334
|
init_config();
|
|
76926
78335
|
init_agents();
|
|
76927
78336
|
init_providers();
|
|
78337
|
+
init_generate();
|
|
76928
78338
|
init_project_dir();
|
|
76929
78339
|
init_scan();
|
|
76930
78340
|
init_agents_md_step();
|
|
@@ -77204,7 +78614,7 @@ Could not open a browser automatically.`);
|
|
|
77204
78614
|
throw new Error("Authentication timed out. Run 'dosu login' to try again.");
|
|
77205
78615
|
}
|
|
77206
78616
|
function sleep4(ms2) {
|
|
77207
|
-
return new Promise((
|
|
78617
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms2));
|
|
77208
78618
|
}
|
|
77209
78619
|
var POLL_INTERVAL_MS = 5000;
|
|
77210
78620
|
var init_device = __esm(() => {
|
|
@@ -77578,7 +78988,7 @@ import {
|
|
|
77578
78988
|
constants as constants3,
|
|
77579
78989
|
fstatSync,
|
|
77580
78990
|
openSync as openSync5,
|
|
77581
|
-
readFileSync as
|
|
78991
|
+
readFileSync as readFileSync25,
|
|
77582
78992
|
readSync as readSync4,
|
|
77583
78993
|
unlinkSync as unlinkSync5
|
|
77584
78994
|
} from "node:fs";
|
|
@@ -80708,6 +82118,7 @@ init_trpc();
|
|
|
80708
82118
|
init_config();
|
|
80709
82119
|
init_agents();
|
|
80710
82120
|
init_formats();
|
|
82121
|
+
init_generate();
|
|
80711
82122
|
init_backlog();
|
|
80712
82123
|
init_detach();
|
|
80713
82124
|
init_status();
|
|
@@ -80716,7 +82127,7 @@ init_watermark();
|
|
|
80716
82127
|
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
80717
82128
|
import { existsSync as existsSync16 } from "node:fs";
|
|
80718
82129
|
import { homedir as homedir5 } from "node:os";
|
|
80719
|
-
import { delimiter as delimiter4, join as
|
|
82130
|
+
import { delimiter as delimiter4, join as join19 } from "node:path";
|
|
80720
82131
|
function requireConfig7() {
|
|
80721
82132
|
const cfg = requireLoginConfig();
|
|
80722
82133
|
if (!cfg.active_account?.target?.org_id || !cfg.active_account?.target?.space_id) {
|
|
@@ -80823,7 +82234,7 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
80823
82234
|
section("Mined", minedRows, "Mined at", "No mined sessions recorded yet.");
|
|
80824
82235
|
}
|
|
80825
82236
|
});
|
|
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) => {
|
|
82237
|
+
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
82238
|
if (opts.status) {
|
|
80828
82239
|
const status = getSyncStatus();
|
|
80829
82240
|
if (opts.json) {
|
|
@@ -80838,7 +82249,9 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
80838
82249
|
"knowledge",
|
|
80839
82250
|
"sync",
|
|
80840
82251
|
...opts.quiet ? ["--quiet"] : [],
|
|
80841
|
-
...opts.bootstrap ? ["--bootstrap"] : []
|
|
82252
|
+
...opts.bootstrap ? ["--bootstrap"] : [],
|
|
82253
|
+
...opts.report ? ["--report"] : [],
|
|
82254
|
+
...opts.out ? ["--out", opts.out] : []
|
|
80842
82255
|
]);
|
|
80843
82256
|
return;
|
|
80844
82257
|
}
|
|
@@ -80859,12 +82272,42 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
80859
82272
|
if (opts.quiet)
|
|
80860
82273
|
return;
|
|
80861
82274
|
if (opts.json) {
|
|
80862
|
-
printResult(outcome, opts);
|
|
80863
82275
|
if (outcome.status === "error")
|
|
80864
82276
|
process.exitCode = 1;
|
|
82277
|
+
if (opts.report) {
|
|
82278
|
+
try {
|
|
82279
|
+
const report = await emitKnowledgeReport({ out: opts.out, open: false });
|
|
82280
|
+
printResult({ ...outcome, report }, opts);
|
|
82281
|
+
} catch (err) {
|
|
82282
|
+
const report_error = err instanceof Error ? err.message : String(err);
|
|
82283
|
+
printResult({ ...outcome, report_error }, opts);
|
|
82284
|
+
}
|
|
82285
|
+
} else {
|
|
82286
|
+
printResult(outcome, opts);
|
|
82287
|
+
}
|
|
80865
82288
|
return;
|
|
80866
82289
|
}
|
|
80867
82290
|
printSyncOutcome(outcome);
|
|
82291
|
+
if (opts.report) {
|
|
82292
|
+
try {
|
|
82293
|
+
const report = await emitKnowledgeReport({ out: opts.out, open: true });
|
|
82294
|
+
console.log(`Wrote ${report}`);
|
|
82295
|
+
} catch (err) {
|
|
82296
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
82297
|
+
process.exitCode = 1;
|
|
82298
|
+
}
|
|
82299
|
+
}
|
|
82300
|
+
});
|
|
82301
|
+
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) => {
|
|
82302
|
+
const path3 = await emitKnowledgeReport({
|
|
82303
|
+
out: opts.out,
|
|
82304
|
+
open: opts.json ? false : opts.open
|
|
82305
|
+
});
|
|
82306
|
+
if (opts.json) {
|
|
82307
|
+
printResult({ report: path3 }, opts);
|
|
82308
|
+
return;
|
|
82309
|
+
}
|
|
82310
|
+
console.log(`Wrote ${path3}`);
|
|
80868
82311
|
});
|
|
80869
82312
|
cmd.addCommand(hooksCommand());
|
|
80870
82313
|
return cmd;
|
|
@@ -81082,7 +82525,7 @@ function reportHookFailure(agent, err) {
|
|
|
81082
82525
|
}
|
|
81083
82526
|
function dosuOnPath() {
|
|
81084
82527
|
const bin = process.platform === "win32" ? "dosu.cmd" : "dosu";
|
|
81085
|
-
return (process.env.PATH ?? "").split(delimiter4).some((dir) => dir !== "" && existsSync16(
|
|
82528
|
+
return (process.env.PATH ?? "").split(delimiter4).some((dir) => dir !== "" && existsSync16(join19(dir, bin)));
|
|
81086
82529
|
}
|
|
81087
82530
|
|
|
81088
82531
|
// src/commands/libraries.ts
|
|
@@ -81422,7 +82865,7 @@ init_dist();
|
|
|
81422
82865
|
init_commander();
|
|
81423
82866
|
init_trpc();
|
|
81424
82867
|
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
81425
|
-
import { readFileSync as
|
|
82868
|
+
import { readFileSync as readFileSync17 } from "node:fs";
|
|
81426
82869
|
function requireConfig10() {
|
|
81427
82870
|
return requireLoginConfig();
|
|
81428
82871
|
}
|
|
@@ -81598,7 +83041,7 @@ function reviewCommand() {
|
|
|
81598
83041
|
let body = opts.body;
|
|
81599
83042
|
if (opts.bodyFile) {
|
|
81600
83043
|
try {
|
|
81601
|
-
body =
|
|
83044
|
+
body = readFileSync17(opts.bodyFile, "utf-8");
|
|
81602
83045
|
} catch (err) {
|
|
81603
83046
|
console.error(import_picocolors22.default.red(`Failed to read --body-file: ${err.message}`));
|
|
81604
83047
|
process.exit(1);
|
|
@@ -82387,8 +83830,8 @@ async function finishTelemetry(operation) {
|
|
|
82387
83830
|
try {
|
|
82388
83831
|
await Promise.race([
|
|
82389
83832
|
Promise.resolve().then(operation),
|
|
82390
|
-
new Promise((
|
|
82391
|
-
timeout = setTimeout(
|
|
83833
|
+
new Promise((resolve7) => {
|
|
83834
|
+
timeout = setTimeout(resolve7, TELEMETRY_FLUSH_TIMEOUT_MS);
|
|
82392
83835
|
})
|
|
82393
83836
|
]);
|
|
82394
83837
|
} catch {} finally {
|
|
@@ -82742,7 +84185,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
|
|
|
82742
84185
|
}
|
|
82743
84186
|
if (opts.follow) {
|
|
82744
84187
|
try {
|
|
82745
|
-
const lines =
|
|
84188
|
+
const lines = readFileSync25(logPath, "utf-8").split(`
|
|
82746
84189
|
`);
|
|
82747
84190
|
if (lines.at(-1) === "")
|
|
82748
84191
|
lines.pop();
|
|
@@ -82758,7 +84201,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
|
|
|
82758
84201
|
if (opts.tail !== undefined) {
|
|
82759
84202
|
const n5 = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
|
|
82760
84203
|
try {
|
|
82761
|
-
const content =
|
|
84204
|
+
const content = readFileSync25(logPath, "utf-8");
|
|
82762
84205
|
const lines = content.split(`
|
|
82763
84206
|
`);
|
|
82764
84207
|
console.log(lines.slice(-n5).join(`
|
|
@@ -82829,4 +84272,4 @@ execute().catch((err) => {
|
|
|
82829
84272
|
process.exit(1);
|
|
82830
84273
|
});
|
|
82831
84274
|
|
|
82832
|
-
//# debugId=
|
|
84275
|
+
//# debugId=d438e963-a417-e96d-6475-6e2164756e21
|