@dosu/cli 0.16.2 → 0.18.0

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.
Files changed (2) hide show
  1. package/bin/dosu.js +46 -33
  2. package/package.json +1 -1
package/bin/dosu.js CHANGED
@@ -4525,7 +4525,7 @@ var init_dist4 = __esm(() => {
4525
4525
  function getVersionString() {
4526
4526
  return `v${VERSION}`;
4527
4527
  }
4528
- var VERSION = "0.16.2";
4528
+ var VERSION = "0.18.0";
4529
4529
 
4530
4530
  // src/debug/logger.ts
4531
4531
  import {
@@ -5491,8 +5491,8 @@ function buildReadyEnvelope(context, saveRecommended = false) {
5491
5491
  "How to use this:",
5492
5492
  "- Use it quietly to answer faster and with fewer broad searches. Keep working normally.",
5493
5493
  "- This is the known likely path, not the full boundary. Before finalizing, verify adjacent " + "public/API entrypoints and edge cases that this route map may not cover.",
5494
- "- Attribution: mention Dosu only if this context materially changed what you checked, helped " + "locate something non-obvious, or prevented a wrong conclusion. If it merely confirmed what " + "you already found, do not mention it.",
5495
- "- If you do attribute, use one brief quoted note placed next to the specific finding it helped " + 'with. Do not open with "Dosu told me…", do not add a closing praise paragraph, and do not ' + "reference any ticket or lookup id."
5494
+ "- Attribution: this context is your team's existing knowledge (docs, past PRs, discussions) " + "surfaced by Dosu — not the live code you're reading. For each main finding it speaks to, " + "add one brief cited note on how the team's knowledge relates: if it agrees, note it in a " + "line and cite the source; if it differs, flag it as worth confirming and reconcile before " + "finalizing; if it adds something the code alone wouldn't tell you (a rationale, a gotcha, " + "a past decision), surface it, cited.",
5495
+ "- Surface this at each main finding the context speaks to but skip the ones it is " + "genuinely silent on, and never invent agreement or disagreement. Keep each note brief and " + 'next to the finding; do not open with "Dosu told me…", do not add a closing praise ' + "paragraph, and do not reference any ticket or lookup id."
5496
5496
  ].join(`
5497
5497
  `));
5498
5498
  }
@@ -5505,7 +5505,7 @@ function buildReadyEnvelope(context, saveRecommended = false) {
5505
5505
  }
5506
5506
  var LOOKUP_STARTED_NOTE, STOP_PREFIX, SAVE_NUDGE;
5507
5507
  var init_prompts = __esm(() => {
5508
- LOOKUP_STARTED_NOTE = "Dosu started a background knowledge lookup for this request. Keep working normally — " + "do not wait or pause for it. If Dosu context arrives during this session, fold it in " + "quietly to work faster and avoid wrong assumptions. Mention Dosu only if it materially " + "changes your answer.";
5508
+ LOOKUP_STARTED_NOTE = "Dosu started a background knowledge lookup for this request. Keep working normally — " + "do not wait or pause for it. If Dosu context arrives during this session, fold it in " + "quietly to work faster and avoid wrong assumptions. Mention Dosu when its context " + "helps you.";
5509
5509
  STOP_PREFIX = "Dosu knowledge finished after your last action. Re-check your current conclusion against " + "it, then continue or finish — do not redo work it merely confirms.";
5510
5510
  SAVE_NUDGE = "Dosu has little or no existing knowledge on this topic. If you uncover something durable — " + "a non-obvious mechanism, a gotcha, or a route through the code — save it with the Dosu " + "`save_topic` tool when you finish, so future sessions start ahead. Only save genuinely " + "reusable knowledge; skip trivial or one-off details.";
5511
5511
  });
@@ -9418,7 +9418,7 @@ function hasNewVisibleRepository(previousRepos, nextRepos) {
9418
9418
  const previousRepoIds = new Set(previousRepos.map((repo) => repo.repository_id));
9419
9419
  return nextRepos.some((repo) => !previousRepoIds.has(repo.repository_id));
9420
9420
  }
9421
- function sleep2(ms) {
9421
+ function sleep3(ms) {
9422
9422
  return new Promise((resolve) => setTimeout(resolve, ms));
9423
9423
  }
9424
9424
  async function waitForRepositoryRefresh(trpc, orgID, previousRepos, opts) {
@@ -9432,7 +9432,7 @@ async function waitForRepositoryRefresh(trpc, orgID, previousRepos, opts) {
9432
9432
  if (hasNewVisibleRepository(previousRepos, latestRepos)) {
9433
9433
  return { repos: latestRepos, foundNew: true };
9434
9434
  }
9435
- await sleep2(intervalMs);
9435
+ await sleep3(intervalMs);
9436
9436
  }
9437
9437
  return { repos: latestRepos, foundNew: false };
9438
9438
  }
@@ -9565,7 +9565,7 @@ async function verifyDataSourcesPersist(trpc, orgID, expectedDataSourceIds, opts
9565
9565
  return { alive, dropped };
9566
9566
  if (timeoutMs === 0)
9567
9567
  break;
9568
- await sleep2(intervalMs);
9568
+ await sleep3(intervalMs);
9569
9569
  }
9570
9570
  return { alive, dropped };
9571
9571
  }
@@ -10165,7 +10165,7 @@ async function waitForImportTaskCompletion(trpc, taskID, spinner, expectedTotal)
10165
10165
  return null;
10166
10166
  }
10167
10167
  }
10168
- await sleep3(IMPORT_STATUS_POLL_INTERVAL_MS);
10168
+ await sleep4(IMPORT_STATUS_POLL_INTERVAL_MS);
10169
10169
  }
10170
10170
  }
10171
10171
  function buildImportProgressMessage(status, expectedTotal) {
@@ -10216,7 +10216,7 @@ async function waitForImportableGithubFiles(trpc, orgID, spaceID, expectedDataSo
10216
10216
  spinner.stop(latestFiles.length > 0 ? "Docs ready" : "No markdown docs found");
10217
10217
  return latestFiles;
10218
10218
  }
10219
- await sleep3(DOC_SCAN_POLL_INTERVAL_MS);
10219
+ await sleep4(DOC_SCAN_POLL_INTERVAL_MS);
10220
10220
  continue;
10221
10221
  }
10222
10222
  latestFiles = await fetchImportableGithubFiles(trpc, spaceID);
@@ -10228,7 +10228,7 @@ async function waitForImportableGithubFiles(trpc, orgID, spaceID, expectedDataSo
10228
10228
  spinner.stop("No markdown docs found");
10229
10229
  return [];
10230
10230
  }
10231
- await sleep3(DOC_SCAN_POLL_INTERVAL_MS);
10231
+ await sleep4(DOC_SCAN_POLL_INTERVAL_MS);
10232
10232
  }
10233
10233
  spinner.stop("Timed out");
10234
10234
  const choice = await ve({
@@ -10322,7 +10322,7 @@ async function getKnowledgeStoreID(trpc, spaceID) {
10322
10322
  return null;
10323
10323
  }
10324
10324
  }
10325
- function sleep3(ms) {
10325
+ function sleep4(ms) {
10326
10326
  return new Promise((resolve) => setTimeout(resolve, ms));
10327
10327
  }
10328
10328
  var DOC_SCAN_POLL_INTERVAL_MS = 2000, DOC_SCAN_POLL_TIMEOUT_MS = 60000, IMPORT_STATUS_POLL_INTERVAL_MS = 2000, IMPORT_STATUS_MAX_ERRORS = 3;
@@ -12991,6 +12991,8 @@ function saveState(state) {
12991
12991
  // src/commands/hooks.ts
12992
12992
  var COOLDOWN_DEFAULT_MS = 3000;
12993
12993
  var TTL_DEFAULT_MS = 10 * 60 * 1000;
12994
+ var STOP_WAIT_DEFAULT_MS = 8000;
12995
+ var STOP_POLL_DEFAULT_MS = 1000;
12994
12996
  var SUPPORTED_AGENTS = ["claude-code"];
12995
12997
  function cooldownMs() {
12996
12998
  const n = Number.parseInt(process.env.DOSU_HOOK_CHECK_COOLDOWN_MS ?? "", 10);
@@ -13000,12 +13002,21 @@ function ttlMs() {
13000
13002
  const n = Number.parseInt(process.env.DOSU_HOOK_TTL_MS ?? "", 10);
13001
13003
  return Number.isFinite(n) && n > 0 ? n : TTL_DEFAULT_MS;
13002
13004
  }
13005
+ function stopWaitMs() {
13006
+ const n = Number.parseInt(process.env.DOSU_HOOK_STOP_WAIT_MS ?? "", 10);
13007
+ return Number.isFinite(n) && n >= 0 ? n : STOP_WAIT_DEFAULT_MS;
13008
+ }
13009
+ function stopPollMs() {
13010
+ const n = Number.parseInt(process.env.DOSU_HOOK_STOP_POLL_MS ?? "", 10);
13011
+ return Number.isFinite(n) && n > 0 ? n : STOP_POLL_DEFAULT_MS;
13012
+ }
13003
13013
  function printHookContext(event, additionalContext) {
13004
13014
  console.log(JSON.stringify({ hookSpecificOutput: { hookEventName: event, additionalContext } }));
13005
13015
  }
13006
13016
  function printContinue() {
13007
13017
  console.log(JSON.stringify({ continue: true }));
13008
13018
  }
13019
+ var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
13009
13020
  function sid8(sessionId) {
13010
13021
  return sessionId.slice(0, 8);
13011
13022
  }
@@ -13128,40 +13139,42 @@ async function runStop(input, now = Date.now()) {
13128
13139
  const sessionId = input.session_id;
13129
13140
  if (!sessionId)
13130
13141
  return printContinue();
13131
- if (input.stop_hook_active === true)
13132
- return printContinue();
13133
13142
  const state = loadState(sessionId);
13134
13143
  if (!state)
13135
13144
  return printContinue();
13136
13145
  if (state.status !== "pending" || now > state.expiresAt)
13137
13146
  return printContinue();
13138
- if (state.lastCheckedAt !== undefined && now - state.lastCheckedAt < cooldownMs()) {
13139
- return printContinue();
13140
- }
13141
13147
  const cfg = loadConfig();
13142
13148
  if (!cfg.api_key || !cfg.deployment_id)
13143
13149
  return printContinue();
13144
13150
  const tc = await Promise.resolve().then(() => (init_ticket_client(), exports_ticket_client));
13145
- let resp;
13146
- try {
13147
- resp = await tc.requestGetTicket(cfg, state.ticketId);
13148
- } catch {
13149
- saveState({ ...state, lastCheckedAt: now });
13150
- return printContinue();
13151
- }
13152
- if (resp.status === "ready" && resp.result) {
13153
- saveState({ ...state, status: "delivered", deliveredAt: now, lastCheckedAt: now });
13154
- if (resp.result.context.trim()) {
13155
- const { buildReadyEnvelope: buildReadyEnvelope2, STOP_PREFIX: STOP_PREFIX2 } = await Promise.resolve().then(() => (init_prompts(), exports_prompts));
13156
- const envelope = buildReadyEnvelope2(resp.result.context, resp.result.save_recommended ?? false);
13157
- console.log(JSON.stringify({ decision: "block", reason: `${STOP_PREFIX2}
13151
+ const pollMs = stopPollMs();
13152
+ const maxWaits = pollMs > 0 ? Math.floor(stopWaitMs() / pollMs) : 0;
13153
+ for (let waited = 0;; waited++) {
13154
+ let resp;
13155
+ try {
13156
+ resp = await tc.requestGetTicket(cfg, state.ticketId);
13157
+ } catch {
13158
+ saveState({ ...state, lastCheckedAt: now });
13159
+ return printContinue();
13160
+ }
13161
+ if (resp.status === "ready" && resp.result) {
13162
+ saveState({ ...state, status: "delivered", deliveredAt: now, lastCheckedAt: now });
13163
+ if (resp.result.context.trim()) {
13164
+ const { buildReadyEnvelope: buildReadyEnvelope2, STOP_PREFIX: STOP_PREFIX2 } = await Promise.resolve().then(() => (init_prompts(), exports_prompts));
13165
+ const envelope = buildReadyEnvelope2(resp.result.context, resp.result.save_recommended ?? false);
13166
+ console.log(JSON.stringify({ decision: "block", reason: `${STOP_PREFIX2}
13158
13167
 
13159
13168
  ${envelope}` }));
13160
- logger.info("hooks", `stop tid=${state.ticketId} delivered=true`);
13161
- return;
13169
+ logger.info("hooks", `stop tid=${state.ticketId} delivered=true`);
13170
+ return;
13171
+ }
13172
+ logger.debug("hooks", `stop tid=${state.ticketId} delivered=true gap-no-block`);
13173
+ return printContinue();
13162
13174
  }
13163
- logger.debug("hooks", `stop tid=${state.ticketId} delivered=true gap-no-block`);
13164
- return printContinue();
13175
+ if (resp.status !== "pending" || waited >= maxWaits)
13176
+ break;
13177
+ await sleep2(pollMs);
13165
13178
  }
13166
13179
  saveState({ ...state, lastCheckedAt: now });
13167
13180
  logger.debug("hooks", `stop tid=${state.ticketId} delivered=false`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosu/cli",
3
- "version": "0.16.2",
3
+ "version": "0.18.0",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Manage MCP servers for AI tools",
6
6
  "license": "MIT",