@dosu/cli 0.53.0-beta.11 → 0.53.0-beta.13
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 +128 -77
- package/package.json +1 -1
package/bin/dosu.js
CHANGED
|
@@ -5018,7 +5018,7 @@ function isNpxInvocation(channel = INSTALL_CHANNEL, env = process.env) {
|
|
|
5018
5018
|
function getVersionString() {
|
|
5019
5019
|
return `v${VERSION}`;
|
|
5020
5020
|
}
|
|
5021
|
-
var VERSION = "0.53.0-beta.
|
|
5021
|
+
var VERSION = "0.53.0-beta.13", INSTALL_CHANNEL = "npm";
|
|
5022
5022
|
|
|
5023
5023
|
// src/debug/logger.ts
|
|
5024
5024
|
import {
|
|
@@ -5712,12 +5712,34 @@ function answeredEcho(message, answer) {
|
|
|
5712
5712
|
function optionLabel(option) {
|
|
5713
5713
|
return option.label ?? String(option.value);
|
|
5714
5714
|
}
|
|
5715
|
+
function viewport(count, cursor3, top, rows, chrome) {
|
|
5716
|
+
const visible = Math.min(count, Math.max(4, (rows ?? 24) - chrome));
|
|
5717
|
+
let next = top;
|
|
5718
|
+
if (cursor3 < next)
|
|
5719
|
+
next = cursor3;
|
|
5720
|
+
if (cursor3 >= next + visible)
|
|
5721
|
+
next = cursor3 - visible + 1;
|
|
5722
|
+
next = Math.max(0, Math.min(next, count - visible));
|
|
5723
|
+
return { top: next, visible, above: next, below: count - next - visible };
|
|
5724
|
+
}
|
|
5725
|
+
function moreAbove(n2) {
|
|
5726
|
+
return n2 > 0 ? [import_picocolors11.default.dim(` ↑ ${n2} more`)] : [];
|
|
5727
|
+
}
|
|
5728
|
+
function moreBelow(n2) {
|
|
5729
|
+
return n2 > 0 ? [import_picocolors11.default.dim(` ↓ ${n2} more`)] : [];
|
|
5730
|
+
}
|
|
5715
5731
|
function select(opts, io = {}) {
|
|
5732
|
+
const { output } = resolveIO(io);
|
|
5716
5733
|
let selected = 0;
|
|
5734
|
+
let top = 0;
|
|
5717
5735
|
const labelWidth = Math.max(...opts.options.map((o2) => optionLabel(o2).length));
|
|
5718
5736
|
return runInteractive({
|
|
5719
5737
|
render() {
|
|
5720
|
-
const
|
|
5738
|
+
const count = opts.options.length;
|
|
5739
|
+
const view = viewport(count, selected, top, output.rows, 8);
|
|
5740
|
+
top = view.top;
|
|
5741
|
+
const rows = opts.options.slice(top, top + view.visible).map((option, offset) => {
|
|
5742
|
+
const index = top + offset;
|
|
5721
5743
|
const label = optionLabel(option).padEnd(labelWidth);
|
|
5722
5744
|
const hint = option.hint ? ` ${import_picocolors11.default.dim(option.hint)}` : "";
|
|
5723
5745
|
return index === selected ? `${brand(POINTER2)} ${import_picocolors11.default.bold(brand(label))}${hint}` : ` ${import_picocolors11.default.dim(label)}${hint}`;
|
|
@@ -5725,7 +5747,9 @@ function select(opts, io = {}) {
|
|
|
5725
5747
|
return [
|
|
5726
5748
|
import_picocolors11.default.bold(opts.message),
|
|
5727
5749
|
"",
|
|
5750
|
+
...moreAbove(view.above),
|
|
5728
5751
|
...rows,
|
|
5752
|
+
...moreBelow(view.below),
|
|
5729
5753
|
"",
|
|
5730
5754
|
import_picocolors11.default.dim(`↑↓ move ${DOT} enter select ${DOT} esc cancel`)
|
|
5731
5755
|
];
|
|
@@ -5763,13 +5787,9 @@ function multiselect2(opts, io = {}) {
|
|
|
5763
5787
|
return runInteractive({
|
|
5764
5788
|
render() {
|
|
5765
5789
|
const count = opts.options.length;
|
|
5766
|
-
const
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
if (cursor3 >= top + visible)
|
|
5770
|
-
top = cursor3 - visible + 1;
|
|
5771
|
-
top = Math.max(0, Math.min(top, count - visible));
|
|
5772
|
-
const rows = opts.options.slice(top, top + visible).map((option, offset) => {
|
|
5790
|
+
const view = viewport(count, cursor3, top, output.rows, 9);
|
|
5791
|
+
top = view.top;
|
|
5792
|
+
const rows = opts.options.slice(top, top + view.visible).map((option, offset) => {
|
|
5773
5793
|
const index = top + offset;
|
|
5774
5794
|
const isPicked = picked.has(option.value);
|
|
5775
5795
|
const box = isPicked ? brand(BOX_ON) : import_picocolors11.default.dim(BOX_OFF);
|
|
@@ -5778,15 +5798,13 @@ function multiselect2(opts, io = {}) {
|
|
|
5778
5798
|
const suffix = status ? ` ${status}` : "";
|
|
5779
5799
|
return index === cursor3 ? `${brand(POINTER2)} ${box} ${import_picocolors11.default.bold(brand(label))}${suffix}` : ` ${box} ${import_picocolors11.default.dim(label)}${suffix}`;
|
|
5780
5800
|
});
|
|
5781
|
-
const above = top;
|
|
5782
|
-
const below = count - top - visible;
|
|
5783
5801
|
const summary = opts.summary?.(pickedValues());
|
|
5784
5802
|
return [
|
|
5785
5803
|
import_picocolors11.default.bold(opts.message),
|
|
5786
5804
|
"",
|
|
5787
|
-
...
|
|
5805
|
+
...moreAbove(view.above),
|
|
5788
5806
|
...rows,
|
|
5789
|
-
...
|
|
5807
|
+
...moreBelow(view.below),
|
|
5790
5808
|
"",
|
|
5791
5809
|
...error ? [import_picocolors11.default.yellow(error)] : summary ? [import_picocolors11.default.dim(summary)] : [],
|
|
5792
5810
|
import_picocolors11.default.dim(`↑↓ move ${DOT} space toggle ${DOT} a all ${DOT} enter confirm ${DOT} esc cancel`)
|
|
@@ -8577,6 +8595,17 @@ function setSyncPaused(paused, configDir = getConfigDir()) {
|
|
|
8577
8595
|
delete state.paused;
|
|
8578
8596
|
saveSyncState(state, configDir);
|
|
8579
8597
|
}
|
|
8598
|
+
function resetSyncState(configDir = getConfigDir()) {
|
|
8599
|
+
const previous = loadSyncState(configDir);
|
|
8600
|
+
const fresh = {
|
|
8601
|
+
schema_version: STATE_SCHEMA_VERSION,
|
|
8602
|
+
watermark: null,
|
|
8603
|
+
consecutive_failures: 0,
|
|
8604
|
+
...previous.project_filter ? { project_filter: previous.project_filter } : {},
|
|
8605
|
+
...previous.paused ? { paused: true } : {}
|
|
8606
|
+
};
|
|
8607
|
+
saveSyncState(fresh, configDir);
|
|
8608
|
+
}
|
|
8580
8609
|
function saveSyncState(state, configDir = getConfigDir()) {
|
|
8581
8610
|
if (!existsSync10(configDir))
|
|
8582
8611
|
mkdirSync6(configDir, { recursive: true, mode: 448 });
|
|
@@ -9238,7 +9267,7 @@ function buildReportHtml(options) {
|
|
|
9238
9267
|
const org = options.orgName || "Your team";
|
|
9239
9268
|
const repo = options.repo || inventory.cwd || "—";
|
|
9240
9269
|
const branch = options.branch || "—";
|
|
9241
|
-
const summary = options.summary || "Local agent session logs were
|
|
9270
|
+
const summary = options.summary || "Local agent session logs were studied and distilled into Dosu notes so the next task can reuse them — reducing rediscovery cost.";
|
|
9242
9271
|
const generated = `${(options.generatedAt ?? new Date).toISOString().replace("T", " ").slice(0, 16)} UTC`;
|
|
9243
9272
|
const derived = tokenTotalsFromCandidates(candidates, inventory);
|
|
9244
9273
|
const [notesHeading, notesLede, footerTitle, footerBody] = notesSectionCopy(candidates);
|
|
@@ -9300,7 +9329,7 @@ function buildReportHtml(options) {
|
|
|
9300
9329
|
<p class="muted">No rediscovery estimates on the notes yet — each candidate needs <code>approx_rediscovery_tokens</code>.</p>
|
|
9301
9330
|
</section>`;
|
|
9302
9331
|
}
|
|
9303
|
-
const notesBody = candidateRows.join("") || '<p class="muted">No write_knowledge payloads yet. Run knowledge sync so
|
|
9332
|
+
const notesBody = candidateRows.join("") || '<p class="muted">No write_knowledge payloads yet. Run knowledge sync so Dosu studies your sessions and extracts learnings.</p>';
|
|
9304
9333
|
const lede = notesLede ? `<p class="lede">${esc(notesLede)}</p>` : "";
|
|
9305
9334
|
return `<!DOCTYPE html>
|
|
9306
9335
|
<html lang="en">
|
|
@@ -10490,7 +10519,7 @@ async function runKnowledgeSync(options = {}) {
|
|
|
10490
10519
|
const state = loadState();
|
|
10491
10520
|
if (options.quiet) {
|
|
10492
10521
|
if (state.paused) {
|
|
10493
|
-
logger.debug("sync", "skipping quiet sync:
|
|
10522
|
+
logger.debug("sync", "skipping quiet sync: studying is paused");
|
|
10494
10523
|
return { status: "skipped-paused", readySessions: 0, inFlightSessions: 0, sessions: [] };
|
|
10495
10524
|
}
|
|
10496
10525
|
const retryAt = backoffUntil(state);
|
|
@@ -10500,7 +10529,7 @@ async function runKnowledgeSync(options = {}) {
|
|
|
10500
10529
|
}
|
|
10501
10530
|
} else if (state.paused) {
|
|
10502
10531
|
delete state.paused;
|
|
10503
|
-
logger.debug("sync", "manual sync resumes paused
|
|
10532
|
+
logger.debug("sync", "manual sync resumes paused studying");
|
|
10504
10533
|
}
|
|
10505
10534
|
let ready;
|
|
10506
10535
|
let open2;
|
|
@@ -10590,12 +10619,12 @@ async function runKnowledgeSync(options = {}) {
|
|
|
10590
10619
|
trivialSessions: trivial
|
|
10591
10620
|
};
|
|
10592
10621
|
}
|
|
10593
|
-
logger.debug("sync", `
|
|
10622
|
+
logger.debug("sync", `studying ${batch.length} of ${ready.length} ready sessions (${trivial} trivial skipped)`);
|
|
10594
10623
|
const miner = await deps.mine(batch);
|
|
10595
10624
|
switch (miner.outcome) {
|
|
10596
10625
|
case "completed": {
|
|
10597
10626
|
for (const s of batch) {
|
|
10598
|
-
logger.debug("sync", `
|
|
10627
|
+
logger.debug("sync", `studied session ${s.harness}/${s.id}`);
|
|
10599
10628
|
}
|
|
10600
10629
|
const minedAt = now().toISOString();
|
|
10601
10630
|
const history = [
|
|
@@ -10622,7 +10651,7 @@ async function runKnowledgeSync(options = {}) {
|
|
|
10622
10651
|
total_learning_tokens: (state.total_learning_tokens ?? 0) + batchTokens,
|
|
10623
10652
|
last_refusal: undefined
|
|
10624
10653
|
});
|
|
10625
|
-
logger.debug("sync", `
|
|
10654
|
+
logger.debug("sync", `studied ${batch.length} sessions, ${miner.notesWritten} suggested pages; watermark → ${watermark}`);
|
|
10626
10655
|
return {
|
|
10627
10656
|
status: "mined",
|
|
10628
10657
|
...base,
|
|
@@ -10642,10 +10671,10 @@ async function runKnowledgeSync(options = {}) {
|
|
|
10642
10671
|
last_refusal: {
|
|
10643
10672
|
at,
|
|
10644
10673
|
outcome: miner.outcome,
|
|
10645
|
-
message: miner.message ?? "
|
|
10674
|
+
message: miner.message ?? "Studying unavailable right now."
|
|
10646
10675
|
}
|
|
10647
10676
|
});
|
|
10648
|
-
logger.debug("sync", `
|
|
10677
|
+
logger.debug("sync", `studying skipped by gateway: ${miner.outcome}`);
|
|
10649
10678
|
return { status: "skipped-gateway", ...base, minedSessions: 0, miner };
|
|
10650
10679
|
}
|
|
10651
10680
|
default: {
|
|
@@ -10654,7 +10683,7 @@ async function runKnowledgeSync(options = {}) {
|
|
|
10654
10683
|
last_attempt_at: now().toISOString(),
|
|
10655
10684
|
consecutive_failures: state.consecutive_failures + 1
|
|
10656
10685
|
});
|
|
10657
|
-
logger.debug("sync", `
|
|
10686
|
+
logger.debug("sync", `studying failed: ${miner.outcome}; ${miner.message ?? ""}`);
|
|
10658
10687
|
return {
|
|
10659
10688
|
status: "mine-failed",
|
|
10660
10689
|
...base,
|
|
@@ -72901,7 +72930,7 @@ async function runMiner(options) {
|
|
|
72901
72930
|
outcome: "settings_conflict",
|
|
72902
72931
|
notesWritten: 0,
|
|
72903
72932
|
turns: 0,
|
|
72904
|
-
message: `Refusing to run: conflicting Claude Code settings would override the
|
|
72933
|
+
message: `Refusing to run: conflicting Claude Code settings would override the study run's auth (${detail})`
|
|
72905
72934
|
};
|
|
72906
72935
|
}
|
|
72907
72936
|
const gatewayURL = options.gatewayURL ?? getLlmGatewayURL();
|
|
@@ -72968,7 +72997,7 @@ async function runMiner(options) {
|
|
|
72968
72997
|
if (!allowed.has(toolName)) {
|
|
72969
72998
|
return {
|
|
72970
72999
|
behavior: "deny",
|
|
72971
|
-
message: `Tool ${toolName} is not permitted in
|
|
73000
|
+
message: `Tool ${toolName} is not permitted in study runs.`
|
|
72972
73001
|
};
|
|
72973
73002
|
}
|
|
72974
73003
|
if (toolName === `mcp__${SESSIONS_SERVER_NAME}__read_session`) {
|
|
@@ -73025,7 +73054,7 @@ async function runMiner(options) {
|
|
|
73025
73054
|
outcome: "error",
|
|
73026
73055
|
notesWritten,
|
|
73027
73056
|
turns,
|
|
73028
|
-
message: "
|
|
73057
|
+
message: "Study run failed; see debug log for details."
|
|
73029
73058
|
};
|
|
73030
73059
|
}
|
|
73031
73060
|
logger.debug("miner", `run ${runID} completed: ${turns} turns, ${notesWritten} suggested pages`);
|
|
@@ -73036,7 +73065,7 @@ async function runMiner(options) {
|
|
|
73036
73065
|
outcome: "error",
|
|
73037
73066
|
notesWritten,
|
|
73038
73067
|
turns,
|
|
73039
|
-
message: "
|
|
73068
|
+
message: "Study run ended without a result."
|
|
73040
73069
|
};
|
|
73041
73070
|
} catch (error61) {
|
|
73042
73071
|
const text = error61 instanceof Error ? error61.message : String(error61);
|
|
@@ -73049,7 +73078,7 @@ async function runMiner(options) {
|
|
|
73049
73078
|
outcome: "error",
|
|
73050
73079
|
notesWritten,
|
|
73051
73080
|
turns,
|
|
73052
|
-
message: abort.signal.aborted ? "
|
|
73081
|
+
message: abort.signal.aborted ? "Study run timed out and was aborted." : "Study run failed; see debug log for details."
|
|
73053
73082
|
};
|
|
73054
73083
|
} finally {
|
|
73055
73084
|
clearTimeout(timer);
|
|
@@ -73519,7 +73548,7 @@ var init_settings = __esm(() => {
|
|
|
73519
73548
|
});
|
|
73520
73549
|
|
|
73521
73550
|
// src/telemetry/debug-id.ts
|
|
73522
|
-
var BUNDLE_DEBUG_ID_PLACEHOLDER = "
|
|
73551
|
+
var BUNDLE_DEBUG_ID_PLACEHOLDER = "6b750395-441b-6515-6475-6e2164756e21";
|
|
73523
73552
|
|
|
73524
73553
|
// src/telemetry/telemetry.ts
|
|
73525
73554
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
@@ -75434,6 +75463,8 @@ function reduceActivityViewKey(key) {
|
|
|
75434
75463
|
return "sync";
|
|
75435
75464
|
if (key === "f")
|
|
75436
75465
|
return "full";
|
|
75466
|
+
if (key === "c")
|
|
75467
|
+
return "clear";
|
|
75437
75468
|
return "none";
|
|
75438
75469
|
}
|
|
75439
75470
|
function cycleTab(tab, delta = 1) {
|
|
@@ -75524,12 +75555,12 @@ function foldRunProgress(progress, chunk) {
|
|
|
75524
75555
|
let current = progress;
|
|
75525
75556
|
for (const line of chunk.split(`
|
|
75526
75557
|
`)) {
|
|
75527
|
-
const start = line.match(/\[sync\] mining (\d+) of \d+ ready sessions/);
|
|
75558
|
+
const start = line.match(/\[sync\] (?:studying|mining) (\d+) of \d+ ready sessions/);
|
|
75528
75559
|
if (start) {
|
|
75529
75560
|
current = { batch: Number.parseInt(start[1], 10), read: new Set, notes: 0 };
|
|
75530
75561
|
continue;
|
|
75531
75562
|
}
|
|
75532
|
-
if (/\[sync\] (mined \d+ sessions|mining failed|
|
|
75563
|
+
if (/\[sync\] (?:studied|mined) \d+ sessions|\[sync\] (?:studying|mining) (?:failed|skipped)/.test(line)) {
|
|
75533
75564
|
current = null;
|
|
75534
75565
|
continue;
|
|
75535
75566
|
}
|
|
@@ -75554,15 +75585,15 @@ function statusLine(status) {
|
|
|
75554
75585
|
if (status.running) {
|
|
75555
75586
|
const since = status.startedAt ? ` · since ${localTime(status.startedAt)}` : "";
|
|
75556
75587
|
const pid = status.pid !== undefined ? ` (pid ${status.pid})` : "";
|
|
75557
|
-
return
|
|
75588
|
+
return `\uD83D\uDCDA ${import_picocolors30.default.bold(brand("Studying sessions..."))}${import_picocolors30.default.dim(`${pid}${since}`)}`;
|
|
75558
75589
|
}
|
|
75559
75590
|
if (status.state.paused) {
|
|
75560
|
-
return `${import_picocolors30.default.yellow("○")} ${import_picocolors30.default.bold("Paused")} ${import_picocolors30.default.dim("·
|
|
75591
|
+
return `${import_picocolors30.default.yellow("○")} ${import_picocolors30.default.bold("Paused")} ${import_picocolors30.default.dim("· studying stays off until you resume")}`;
|
|
75561
75592
|
}
|
|
75562
75593
|
if (status.staleLock) {
|
|
75563
75594
|
return `${import_picocolors30.default.yellow("○")} ${import_picocolors30.default.bold("Not running")} ${import_picocolors30.default.dim("· a previous run exited without cleaning up")}`;
|
|
75564
75595
|
}
|
|
75565
|
-
return `${import_picocolors30.default.dim("○")} ${import_picocolors30.default.bold("Idle")} ${import_picocolors30.default.dim("· hooks
|
|
75596
|
+
return `${import_picocolors30.default.dim("○")} ${import_picocolors30.default.bold("Idle")} ${import_picocolors30.default.dim("· hooks study new sessions automatically")}`;
|
|
75566
75597
|
}
|
|
75567
75598
|
function progressLine(done, ready, width, notes = 0) {
|
|
75568
75599
|
const total = done + ready;
|
|
@@ -75574,12 +75605,12 @@ function progressLine(done, ready, width, notes = 0) {
|
|
|
75574
75605
|
const bar = brand("█".repeat(filled)) + import_picocolors30.default.dim("░".repeat(cells - filled));
|
|
75575
75606
|
const pct = Math.floor(ratio * 100);
|
|
75576
75607
|
const suffix = notes > 0 ? ` · ${notes} suggested page${notes === 1 ? "" : "s"}` : "";
|
|
75577
|
-
return `${bar} ${done}/${total}
|
|
75608
|
+
return `${bar} ${done}/${total} studied · ${pct}%${suffix}`;
|
|
75578
75609
|
}
|
|
75579
75610
|
function tabBar(tab, queuedCount, openCount, minedCount, width) {
|
|
75580
75611
|
return tabStrip([
|
|
75581
75612
|
["activity", "Activity"],
|
|
75582
|
-
["mined", `
|
|
75613
|
+
["mined", `Studied (${minedCount})`],
|
|
75583
75614
|
["queued", `Queued (${queuedCount})`],
|
|
75584
75615
|
["open", `Open (${openCount})`]
|
|
75585
75616
|
], tab, width);
|
|
@@ -75603,10 +75634,11 @@ function wrapLine(text, width, indent = " ") {
|
|
|
75603
75634
|
return out;
|
|
75604
75635
|
}
|
|
75605
75636
|
function confirmBox(queuedCount, backlog, width, mode = "start") {
|
|
75606
|
-
const inFlight = backlog && backlog.inFlight > 0 ? ` (+${backlog.inFlight} open
|
|
75607
|
-
const
|
|
75608
|
-
const
|
|
75609
|
-
const
|
|
75637
|
+
const inFlight = backlog && backlog.inFlight > 0 ? ` (+${backlog.inFlight} still open; ${backlog.inFlight === 1 ? "it joins" : "they join"} once quiet)` : "";
|
|
75638
|
+
const sessions = `${queuedCount} session${queuedCount === 1 ? "" : "s"}`;
|
|
75639
|
+
const scope = mode === "clear" ? "Forgets which local sessions Dosu has already studied, so the next run reads them all again. Use this to rebuild knowledge from scratch. Notes already saved in Dosu are kept." : mode === "stop" ? "Kills the run mid-batch and pauses studying, so new sessions pile up in the queue instead of being read. Nothing is lost; press s again to resume from where it left off." : mode === "resume" ? `Studying picks up where it stopped: ${sessions} queued${inFlight}. Runs in the background; new notes appear in Dosu as each batch finishes.` : queuedCount > 0 ? `Dosu reads ${sessions}${inFlight} and distills the durable decisions and gotchas into team knowledge. Runs in the background; new notes appear in Dosu as each batch finishes.` : `Queue is empty${inFlight}. A run now would only pick up sessions that finish from here; hooks already do that automatically.`;
|
|
75640
|
+
const title = mode === "clear" ? "Clear study history?" : mode === "stop" ? "Stop studying?" : mode === "resume" ? "Resume studying?" : "Start studying now?";
|
|
75641
|
+
const verb = mode === "clear" ? "clear" : mode === "stop" ? "stop" : mode === "resume" ? "resume" : "start";
|
|
75610
75642
|
const maxInner = Math.max(20, Math.min(width, contentWidth()) - 4);
|
|
75611
75643
|
const rows = [
|
|
75612
75644
|
import_picocolors30.default.bold(brand(title)),
|
|
@@ -75623,7 +75655,7 @@ function confirmBox(queuedCount, backlog, width, mode = "start") {
|
|
|
75623
75655
|
];
|
|
75624
75656
|
}
|
|
75625
75657
|
function renderActivityFrame(status, activity, width, backlog = null, pane = DEFAULT_PANE, queued = [], minedBeforeRun = 0, open5 = [], runProgress = null) {
|
|
75626
|
-
const mined = status.state.watermark ? `
|
|
75658
|
+
const mined = status.state.watermark ? `Studied sessions up to ${localTime(status.state.watermark)}` : "Nothing studied yet";
|
|
75627
75659
|
const queueDetail = [];
|
|
75628
75660
|
if (status.backoffUntil) {
|
|
75629
75661
|
queueDetail.push(`retrying after ${localTime(status.backoffUntil)} · s syncs now`);
|
|
@@ -75635,14 +75667,14 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
|
|
|
75635
75667
|
progress = progressLine(minedDelta + stepDone, Math.max(0, backlog.ready - stepDone), width, runProgress?.notes ?? 0);
|
|
75636
75668
|
}
|
|
75637
75669
|
const refusal = !status.running && status.state.last_refusal;
|
|
75638
|
-
const refusalLines = refusal ? wrapLine(`!
|
|
75670
|
+
const refusalLines = refusal ? wrapLine(`! Studying paused: ${refusal.message} (${localTime(refusal.at)})`, width).map((line) => import_picocolors30.default.yellow(line)) : [];
|
|
75639
75671
|
const fullRows = Boolean(pane.fullRows);
|
|
75640
75672
|
const minedRows = (status.state.mined_sessions ?? []).map((record2) => fullRows ? formatMinedRow(record2, true) : formatActivityLine(formatMinedRow(record2), width));
|
|
75641
75673
|
const sessionRow = (session) => fullRows ? formatQueuedRow(session, true) : formatActivityLine(formatQueuedRow(session), width);
|
|
75642
75674
|
const queuedRows = queued.map(sessionRow);
|
|
75643
75675
|
const openRows = open5.map(sessionRow);
|
|
75644
75676
|
const source = pane.tab === "activity" ? activity.map((line) => formatActivityLine(line, width, fullRows)) : pane.tab === "queued" ? queuedRows : pane.tab === "open" ? openRows : minedRows;
|
|
75645
|
-
const emptyMined = status.state.watermark ? "No sessions recorded yet. History starts with the next
|
|
75677
|
+
const emptyMined = status.state.watermark ? "No sessions recorded yet. History starts with the next study run." : "No studied sessions yet.";
|
|
75646
75678
|
const empty = pane.tab === "activity" ? "No sync activity in the log yet." : pane.tab === "queued" ? "Queue empty. Finished agent sessions appear here." : pane.tab === "open" ? "No open sessions. Live agent sessions sit here until they go quiet." : emptyMined;
|
|
75647
75679
|
const height = fullRows ? ACTIVITY_VIEW_FULL_LIST_ROWS : ACTIVITY_VIEW_LIST_LINES;
|
|
75648
75680
|
const { visible, above, below } = windowList(source, pane.scroll, height);
|
|
@@ -75667,13 +75699,14 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
|
|
|
75667
75699
|
...scrollParts.length > 0 ? [import_picocolors30.default.dim(clip(scrollParts.join(" · "), width))] : [],
|
|
75668
75700
|
"",
|
|
75669
75701
|
...pane.confirm ? centerBlock(confirmBox(queued.length, backlog, width, pane.confirm), width) : [
|
|
75670
|
-
|
|
75702
|
+
...wrapLine([
|
|
75671
75703
|
"tab switch",
|
|
75672
75704
|
"↑↓ scroll",
|
|
75673
75705
|
fullRows ? "f clip" : "f full rows",
|
|
75674
75706
|
status.running ? "s stop" : status.state.paused ? "s resume" : "s sync now",
|
|
75707
|
+
...!status.running && status.state.watermark ? ["c clear"] : [],
|
|
75675
75708
|
"esc back"
|
|
75676
|
-
].join(" · "))
|
|
75709
|
+
].join(" · "), width, "").map((line) => import_picocolors30.default.dim(line))
|
|
75677
75710
|
]
|
|
75678
75711
|
];
|
|
75679
75712
|
return lines.join(`
|
|
@@ -75697,6 +75730,7 @@ function runActivityView(io2 = {}) {
|
|
|
75697
75730
|
const startSync = io2.startSync ?? (() => spawnDetachedSelf(["knowledge", "sync", "--bootstrap"]));
|
|
75698
75731
|
const stopSync = io2.stopSync ?? stopSyncRun;
|
|
75699
75732
|
const setPaused = io2.setPaused ?? setSyncPaused;
|
|
75733
|
+
const clearHistory = io2.clearHistory ?? resetSyncState;
|
|
75700
75734
|
const listBacklog = io2.listBacklog ?? listSessionBacklog;
|
|
75701
75735
|
const pollMs = io2.pollMs ?? ACTIVITY_VIEW_POLL_MS;
|
|
75702
75736
|
const seed = readLog();
|
|
@@ -75743,7 +75777,7 @@ function runActivityView(io2 = {}) {
|
|
|
75743
75777
|
queuedWatermark = status.state.watermark;
|
|
75744
75778
|
sessions = listBacklog();
|
|
75745
75779
|
}
|
|
75746
|
-
if (
|
|
75780
|
+
if (confirmSync !== null && confirmSync !== "stop" && status.running)
|
|
75747
75781
|
confirmSync = null;
|
|
75748
75782
|
if (confirmSync === "stop" && !status.running)
|
|
75749
75783
|
confirmSync = null;
|
|
@@ -75795,11 +75829,15 @@ ${CLEAR_BELOW}`);
|
|
|
75795
75829
|
const mode = confirmSync;
|
|
75796
75830
|
confirmSync = null;
|
|
75797
75831
|
let note;
|
|
75798
|
-
if (mode === "
|
|
75832
|
+
if (mode === "clear") {
|
|
75833
|
+
clearHistory();
|
|
75834
|
+
minedBeforeRun = null;
|
|
75835
|
+
note = "[sync] study history cleared · the next run reads every local session again";
|
|
75836
|
+
} else if (mode === "stop") {
|
|
75799
75837
|
const ok2 = status.pid !== undefined && stopSync(status.pid);
|
|
75800
75838
|
if (ok2)
|
|
75801
75839
|
setPaused(true);
|
|
75802
|
-
note = ok2 ? "[sync]
|
|
75840
|
+
note = ok2 ? "[sync] studying stopped · paused until you resume" : "[sync] could not stop the run · it may have just finished";
|
|
75803
75841
|
} else {
|
|
75804
75842
|
if (mode === "resume")
|
|
75805
75843
|
setPaused(false);
|
|
@@ -75823,6 +75861,11 @@ ${CLEAR_BELOW}`);
|
|
|
75823
75861
|
if (action === "sync") {
|
|
75824
75862
|
confirmSync = syncConfirmMode(status);
|
|
75825
75863
|
draw();
|
|
75864
|
+
} else if (action === "clear") {
|
|
75865
|
+
if (!status.running && status.state.watermark) {
|
|
75866
|
+
confirmSync = "clear";
|
|
75867
|
+
draw();
|
|
75868
|
+
}
|
|
75826
75869
|
} else if (action === "tab" || action === "tab-back") {
|
|
75827
75870
|
tab = cycleTab(tab, action === "tab" ? 1 : -1);
|
|
75828
75871
|
scroll = 0;
|
|
@@ -76708,23 +76751,31 @@ async function stepOfferInitialSync(cfg) {
|
|
|
76708
76751
|
s.start("Checking for recent agent sessions...");
|
|
76709
76752
|
const outcome = await runKnowledgeSync({ bootstrap: true });
|
|
76710
76753
|
if (outcome.status !== "backlog" || outcome.readySessions === 0) {
|
|
76711
|
-
s.stop("No
|
|
76754
|
+
s.stop("No past agent sessions to study. Dosu will learn from new ones as you work.");
|
|
76712
76755
|
return;
|
|
76713
76756
|
}
|
|
76714
76757
|
const n5 = outcome.readySessions;
|
|
76715
|
-
|
|
76758
|
+
const them = n5 === 1 ? "it" : "them";
|
|
76759
|
+
s.stop(`Found ${n5} past agent session${n5 === 1 ? "" : "s"} on this machine that Dosu hasn't studied.`);
|
|
76760
|
+
log2.message([
|
|
76761
|
+
`Dosu can read ${them} and pull out the durable knowledge: decisions, gotchas, and`,
|
|
76762
|
+
"how-things-work that your team and agents would otherwise rediscover.",
|
|
76763
|
+
"Only distilled notes are saved to Dosu; your session logs stay on this machine.",
|
|
76764
|
+
"It runs in the background a few sessions at a time; notes appear in Dosu as each batch finishes."
|
|
76765
|
+
].join(`
|
|
76766
|
+
`));
|
|
76716
76767
|
const mineNow = await confirm2({
|
|
76717
|
-
message: `
|
|
76718
|
-
active: "
|
|
76719
|
-
inactive: "Skip",
|
|
76768
|
+
message: `Study ${them} now?`,
|
|
76769
|
+
active: "Study now \uD83D\uDCDA",
|
|
76770
|
+
inactive: "Skip for now",
|
|
76720
76771
|
initialValue: true
|
|
76721
76772
|
});
|
|
76722
76773
|
if (isCancel2(mineNow) || !mineNow) {
|
|
76723
|
-
log2.info(`Skipped. Dosu
|
|
76774
|
+
log2.info(`Skipped. Dosu studies new sessions in the background as you work; run ${info("dosu knowledge sync")} anytime to study these too.`);
|
|
76724
76775
|
return;
|
|
76725
76776
|
}
|
|
76726
76777
|
if (spawnDetachedSelf(["knowledge", "sync", "--quiet", "--bootstrap"])) {
|
|
76727
|
-
log2.success("
|
|
76778
|
+
log2.success(`\uD83D\uDCDA Studying ${n5} session${n5 === 1 ? "" : "s"} in the background. Watch progress on the Activity screen; when it finishes, the new notes are in Dosu and agents connected to this MCP can use them.`);
|
|
76728
76779
|
const watch = await confirm2({
|
|
76729
76780
|
message: "What next?",
|
|
76730
76781
|
active: "Watch it work",
|
|
@@ -77554,7 +77605,7 @@ function overviewRows(state) {
|
|
|
77554
77605
|
const tokens = state.total_learning_tokens ?? 0;
|
|
77555
77606
|
if (minedTotal === 0 && notes === 0)
|
|
77556
77607
|
return [];
|
|
77557
|
-
const rows = [`${label("Sessions
|
|
77608
|
+
const rows = [`${label("Sessions studied")}${minedTotal}`, `${label("Suggested pages")}${notes}`];
|
|
77558
77609
|
if (tokens > 0) {
|
|
77559
77610
|
rows.push(`${label("Investigation distilled")}${formatTokenCount(tokens)} tokens`);
|
|
77560
77611
|
}
|
|
@@ -77603,7 +77654,7 @@ function analyticsTabRows(tab, state, pageStats) {
|
|
|
77603
77654
|
function renderAnalyticsFrame(state, tab, scroll, pageStats = null, pagesPending = false, width = contentWidth()) {
|
|
77604
77655
|
const rows = analyticsTabRows(tab, state, pageStats);
|
|
77605
77656
|
const { visible, above, below } = windowReport(rows, scroll);
|
|
77606
|
-
const empty = tab === "overview" ? "No analytics yet. They appear after the first
|
|
77657
|
+
const empty = tab === "overview" ? "No analytics yet. They appear after the first study run." : tab === "projects" ? "No per-project history yet. It fills in as sessions are studied." : pagesPending ? "Loading page analytics..." : "No page analytics yet.";
|
|
77607
77658
|
const listRows = visible.length > 0 ? visible.map((row) => import_picocolors32.default.dim(row)) : [import_picocolors32.default.dim(empty)];
|
|
77608
77659
|
const scrollParts = [];
|
|
77609
77660
|
if (above > 0)
|
|
@@ -77782,7 +77833,7 @@ function checklistRows(ctx) {
|
|
|
77782
77833
|
if (ctx.mining) {
|
|
77783
77834
|
rows.push([
|
|
77784
77835
|
"sync",
|
|
77785
|
-
|
|
77836
|
+
`\uD83D\uDCDA ${brand("studying sessions...")} ${import_picocolors33.default.dim(`${DOT3} see Activity`)}`
|
|
77786
77837
|
]);
|
|
77787
77838
|
}
|
|
77788
77839
|
if (ctx.update) {
|
|
@@ -78326,7 +78377,7 @@ async function runMainMenu() {
|
|
|
78326
78377
|
const mining = isMining();
|
|
78327
78378
|
return [
|
|
78328
78379
|
{
|
|
78329
|
-
label: mining ? `Activity
|
|
78380
|
+
label: mining ? `Activity \uD83D\uDCDA ${brand("studying sessions...")}` : "Activity",
|
|
78330
78381
|
value: "sync"
|
|
78331
78382
|
},
|
|
78332
78383
|
{ label: "Knowledge report", hint: "(opens in browser)", value: "report" },
|
|
@@ -78406,7 +78457,7 @@ async function runSettings(cfg) {
|
|
|
78406
78457
|
const action = await menuSelect("Settings", [
|
|
78407
78458
|
{ label: "Switch organization", hint: target?.org_name, value: "switch-org" },
|
|
78408
78459
|
{ label: "Switch Library", hint: library, value: "switch-library" },
|
|
78409
|
-
{ label: "
|
|
78460
|
+
{ label: "Study scope", hint: scope, value: "projects" },
|
|
78410
78461
|
{ label: "Run setup", hint: "rerun the setup wizard", value: "setup" },
|
|
78411
78462
|
{ label: "Log out", hint: "clear stored credentials", value: "logout" },
|
|
78412
78463
|
{ label: "Back", value: "back" }
|
|
@@ -78460,7 +78511,7 @@ async function runMiningProjectsSetting() {
|
|
|
78460
78511
|
}
|
|
78461
78512
|
const current = loadSyncState().project_filter;
|
|
78462
78513
|
const selected = await multiselect2({
|
|
78463
|
-
message: "
|
|
78514
|
+
message: "Study sessions from which folders?",
|
|
78464
78515
|
options: dirs.map((dir) => ({
|
|
78465
78516
|
label: dir === UNKNOWN_PROJECT ? "(unknown folder)" : displayDir(dir),
|
|
78466
78517
|
value: dir
|
|
@@ -78475,7 +78526,7 @@ async function runMiningProjectsSetting() {
|
|
|
78475
78526
|
const all = selected.length === dirs.length;
|
|
78476
78527
|
saveSyncState(all ? state : { ...state, project_filter: [...selected] });
|
|
78477
78528
|
const scope = all ? "all folders" : selected.map(displayDir).join(", ");
|
|
78478
|
-
log2.success(`
|
|
78529
|
+
log2.success(`Study scope ${dim(`· ${scope}`)}`);
|
|
78479
78530
|
}
|
|
78480
78531
|
async function handleAuthenticate(cfg) {
|
|
78481
78532
|
const shouldLogin = await confirm2({ message: "Open browser to log in?" });
|
|
@@ -82381,7 +82432,7 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
82381
82432
|
console.log(` ID: ${store.id}`);
|
|
82382
82433
|
console.log(` Space ID: ${store.space_id}`);
|
|
82383
82434
|
});
|
|
82384
|
-
cmd.command("sessions").description("List local agent sessions with full project and session ids (the untruncated view of the Activity screen's tabs)").option("--queued", "Only sessions queued for
|
|
82435
|
+
cmd.command("sessions").description("List local agent sessions with full project and session ids (the untruncated view of the Activity screen's tabs)").option("--queued", "Only sessions queued for studying").option("--open", "Only live sessions still inside the quiet period").option("--mined", "Only recent studied-session history").option("--json", "Output as JSON").action((opts) => {
|
|
82385
82436
|
const all = !opts.queued && !opts.open && !opts.mined;
|
|
82386
82437
|
const wantQueued = all || Boolean(opts.queued);
|
|
82387
82438
|
const wantOpen = all || Boolean(opts.open);
|
|
@@ -82422,10 +82473,10 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
82422
82473
|
section("Open", sessionRows(backlog.open), "Updated", "No open sessions. Live agent sessions sit here until they go quiet.");
|
|
82423
82474
|
}
|
|
82424
82475
|
if (wantMined) {
|
|
82425
|
-
section("
|
|
82476
|
+
section("Studied", minedRows, "Studied at", "No studied sessions recorded yet.");
|
|
82426
82477
|
}
|
|
82427
82478
|
});
|
|
82428
|
-
cmd.command("sync").description("Scan local agent session history and report the
|
|
82479
|
+
cmd.command("sync").description("Scan local agent session history and report the study 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: study 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) => {
|
|
82429
82480
|
if (opts.status) {
|
|
82430
82481
|
const status = getSyncStatus();
|
|
82431
82482
|
if (opts.json) {
|
|
@@ -82511,7 +82562,7 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
82511
82562
|
console.log("All your notes already have a transcript — nothing to backfill.");
|
|
82512
82563
|
return;
|
|
82513
82564
|
}
|
|
82514
|
-
console.log(`Attributed ${result.updated} of ${result.candidates} notes (${result.ambiguous} ambiguous, ${result.noBatch} without a local
|
|
82565
|
+
console.log(`Attributed ${result.updated} of ${result.candidates} notes (${result.ambiguous} ambiguous, ${result.noBatch} without a local study batch). Run 'dosu knowledge report' to see their traces.`);
|
|
82515
82566
|
});
|
|
82516
82567
|
cmd.addCommand(hooksCommand());
|
|
82517
82568
|
return cmd;
|
|
@@ -82552,14 +82603,14 @@ function printSyncStatus(status, now = new Date) {
|
|
|
82552
82603
|
console.log("○ No sync running.");
|
|
82553
82604
|
}
|
|
82554
82605
|
if (status.state.paused) {
|
|
82555
|
-
console.log(import_picocolors18.default.yellow("
|
|
82606
|
+
console.log(import_picocolors18.default.yellow(" Studying paused: stopped by you. Resume from the Activity screen or run 'dosu knowledge sync'."));
|
|
82556
82607
|
}
|
|
82557
82608
|
const wm2 = status.state.watermark;
|
|
82558
|
-
console.log(`
|
|
82609
|
+
console.log(` Studied through: ${wm2 ? `${wm2} (${formatAge(wm2, now)})` : "nothing studied yet"}`);
|
|
82559
82610
|
if (status.state.project_filter?.length) {
|
|
82560
82611
|
const home = homedir5();
|
|
82561
82612
|
const scope = status.state.project_filter.map((dir) => dir.startsWith(`${home}/`) ? `~${dir.slice(home.length)}` : dir).join(", ");
|
|
82562
|
-
console.log(`
|
|
82613
|
+
console.log(` Study scope: ${scope}`);
|
|
82563
82614
|
}
|
|
82564
82615
|
if ((status.state.total_notes ?? 0) > 0) {
|
|
82565
82616
|
const tokens = status.state.total_learning_tokens ?? 0;
|
|
@@ -82574,7 +82625,7 @@ function printSyncStatus(status, now = new Date) {
|
|
|
82574
82625
|
console.log(import_picocolors18.default.yellow(` Backing off after ${n5} failure${n5 === 1 ? "" : "s"}; background syncs retry after ${status.backoffUntil}.`));
|
|
82575
82626
|
}
|
|
82576
82627
|
if (status.state.last_refusal) {
|
|
82577
|
-
console.log(import_picocolors18.default.yellow(`
|
|
82628
|
+
console.log(import_picocolors18.default.yellow(` Studying paused: ${status.state.last_refusal.message} (${formatAge(status.state.last_refusal.at, now)})`));
|
|
82578
82629
|
}
|
|
82579
82630
|
if (status.recentActivity.length > 0) {
|
|
82580
82631
|
console.log(`
|
|
@@ -82590,25 +82641,25 @@ function printSyncOutcome(outcome) {
|
|
|
82590
82641
|
switch (outcome.status) {
|
|
82591
82642
|
case "backlog": {
|
|
82592
82643
|
const inFlight = outcome.inFlightSessions > 0 ? import_picocolors18.default.dim(` (${outcome.inFlightSessions} more still in progress)`) : "";
|
|
82593
|
-
console.log(`✓ Scanned. ${outcome.readySessions} new session${outcome.readySessions === 1 ? "" : "s"} ready to
|
|
82594
|
-
console.log(import_picocolors18.default.dim("Sign in with 'dosu setup' to enable
|
|
82644
|
+
console.log(`✓ Scanned. ${outcome.readySessions} new session${outcome.readySessions === 1 ? "" : "s"} ready to study${inFlight}.`);
|
|
82645
|
+
console.log(import_picocolors18.default.dim("Sign in with 'dosu setup' to enable studying."));
|
|
82595
82646
|
break;
|
|
82596
82647
|
}
|
|
82597
82648
|
case "mined": {
|
|
82598
82649
|
const notes = outcome.miner?.notesWritten ?? 0;
|
|
82599
82650
|
const remaining = outcome.readySessions - (outcome.minedSessions ?? 0);
|
|
82600
|
-
console.log(`✓
|
|
82651
|
+
console.log(`✓ Studied ${outcome.minedSessions} session${outcome.minedSessions === 1 ? "" : "s"}, ${notes} suggested page${notes === 1 ? "" : "s"} created.`);
|
|
82601
82652
|
if (remaining > 0) {
|
|
82602
82653
|
console.log(import_picocolors18.default.dim(`${remaining} more in the backlog; run sync again to continue.`));
|
|
82603
82654
|
}
|
|
82604
82655
|
break;
|
|
82605
82656
|
}
|
|
82606
82657
|
case "skipped-gateway": {
|
|
82607
|
-
console.log(import_picocolors18.default.yellow(outcome.miner?.message ?? "
|
|
82658
|
+
console.log(import_picocolors18.default.yellow(outcome.miner?.message ?? "Studying unavailable right now."));
|
|
82608
82659
|
break;
|
|
82609
82660
|
}
|
|
82610
82661
|
case "mine-failed": {
|
|
82611
|
-
console.error(import_picocolors18.default.red(outcome.miner?.message ?? "
|
|
82662
|
+
console.error(import_picocolors18.default.red(outcome.miner?.message ?? "Study run failed."));
|
|
82612
82663
|
process.exitCode = 1;
|
|
82613
82664
|
break;
|
|
82614
82665
|
}
|
|
@@ -82631,7 +82682,7 @@ function printSyncOutcome(outcome) {
|
|
|
82631
82682
|
break;
|
|
82632
82683
|
}
|
|
82633
82684
|
case "skipped-paused": {
|
|
82634
|
-
console.log(import_picocolors18.default.dim("Skipped:
|
|
82685
|
+
console.log(import_picocolors18.default.dim("Skipped: studying is paused. Run 'dosu knowledge sync' to resume."));
|
|
82635
82686
|
break;
|
|
82636
82687
|
}
|
|
82637
82688
|
}
|
|
@@ -84636,4 +84687,4 @@ execute().catch((err) => {
|
|
|
84636
84687
|
process.exit(1);
|
|
84637
84688
|
});
|
|
84638
84689
|
|
|
84639
|
-
//# debugId=
|
|
84690
|
+
//# debugId=6b750395-441b-6515-6475-6e2164756e21
|