@dosu/cli 0.67.2 → 0.68.1

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 (3) hide show
  1. package/README.md +6 -4
  2. package/bin/dosu.js +136 -46
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -190,10 +190,12 @@ unanchored and reach topics immediately. Neither setup nor `enable` replaces a s
190
190
  already have; they print the one-liner to add to your own script instead.
191
191
 
192
192
  `incognito on` is a saved per-agent setting: none of that agent's sessions are studied until you
193
- turn it off. To keep a single chat out instead, type `/dosu-incognito` in it; setup installs that
194
- command with the hook. It marks the session's transcript so studying skips it (the whole session,
195
- and for the rest of it — start a new session to turn Dosu back on) and tells the model not to use
196
- Dosu tools. See [docs/studying.md](docs/studying.md).
193
+ turn it off. Turning it off resumes studying only new sessions going forward; sessions that
194
+ finished while incognito was on stay unstudied permanently, even if a later sync picks them up.
195
+ To keep a single chat out instead, type `/dosu-incognito` in it; setup installs that command with
196
+ the hook. It marks the session's transcript so studying skips it (the whole session, and for the
197
+ rest of it — start a new session to turn Dosu back on) and tells the model not to use Dosu
198
+ tools. See [docs/studying.md](docs/studying.md).
197
199
 
198
200
  ### Telemetry and privacy
199
201
 
package/bin/dosu.js CHANGED
@@ -1128,7 +1128,7 @@ function isNpxInvocation(channel = INSTALL_CHANNEL, env = process.env) {
1128
1128
  function getVersionString() {
1129
1129
  return `v${VERSION}`;
1130
1130
  }
1131
- var VERSION = "0.67.2", INSTALL_CHANNEL = "npm";
1131
+ var VERSION = "0.68.1", INSTALL_CHANNEL = "npm";
1132
1132
 
1133
1133
  // src/debug/logger.ts
1134
1134
  import {
@@ -2113,6 +2113,10 @@ function syncStatePath(configDir = getConfigDir()) {
2113
2113
  function stringsOf(values) {
2114
2114
  return values.filter((v) => typeof v === "string");
2115
2115
  }
2116
+ function incognitoPeriodsOf(values) {
2117
+ const optionalString = (v) => v === undefined || typeof v === "string";
2118
+ return values.filter((v) => typeof v === "object" && v !== null && typeof v.agent === "string" && optionalString(v.from) && optionalString(v.to)).map(({ agent, from, to }) => ({ agent, ...from ? { from } : {}, ...to ? { to } : {} }));
2119
+ }
2116
2120
  function loadSyncState(configDir = getConfigDir()) {
2117
2121
  const empty = {
2118
2122
  schema_version: STATE_SCHEMA_VERSION,
@@ -2149,6 +2153,7 @@ function loadSyncState(configDir = getConfigDir()) {
2149
2153
  ...run ? { run } : {},
2150
2154
  ...raw.paused === true ? { paused: true } : {},
2151
2155
  ...Array.isArray(raw.incognito_agents) ? { incognito_agents: stringsOf(raw.incognito_agents) } : {},
2156
+ ...Array.isArray(raw.incognito_periods) ? { incognito_periods: incognitoPeriodsOf(raw.incognito_periods) } : {},
2152
2157
  ...Array.isArray(raw.repo_filter) ? { repo_filter: stringsOf(raw.repo_filter) } : {},
2153
2158
  ...Array.isArray(raw.project_filter) ? { project_filter: stringsOf(raw.project_filter) } : {}
2154
2159
  };
@@ -2167,7 +2172,18 @@ function setSyncPaused(paused, configDir = getConfigDir()) {
2167
2172
  function setAgentsIncognito(agentIds, incognito, configDir = getConfigDir()) {
2168
2173
  const state = loadSyncState(configDir);
2169
2174
  const current = new Set(state.incognito_agents ?? []);
2175
+ const periods = state.incognito_periods ?? [];
2176
+ const at = new Date().toISOString();
2170
2177
  for (const id of agentIds) {
2178
+ if (incognito && !current.has(id))
2179
+ periods.push({ agent: id, from: at });
2180
+ if (!incognito && current.has(id)) {
2181
+ const open = periods.find((p) => p.agent === id && !p.to);
2182
+ if (open)
2183
+ open.to = at;
2184
+ else
2185
+ periods.push({ agent: id, to: at });
2186
+ }
2171
2187
  if (incognito)
2172
2188
  current.add(id);
2173
2189
  else
@@ -2177,8 +2193,16 @@ function setAgentsIncognito(agentIds, incognito, configDir = getConfigDir()) {
2177
2193
  state.incognito_agents = [...current].sort();
2178
2194
  else
2179
2195
  delete state.incognito_agents;
2196
+ if (periods.length > 0)
2197
+ state.incognito_periods = periods;
2180
2198
  saveSyncState(state, configDir);
2181
2199
  }
2200
+ function isAgentIncognito(state, session) {
2201
+ if (state.incognito_agents?.includes(session.harness))
2202
+ return true;
2203
+ const updated = Date.parse(session.updated);
2204
+ return (state.incognito_periods ?? []).some((p) => p.agent === session.harness && p.to !== undefined && (p.from === undefined || Date.parse(p.from) <= updated) && updated <= Date.parse(p.to));
2205
+ }
2182
2206
  function resetSyncState(configDir = getConfigDir()) {
2183
2207
  const previous = loadSyncState(configDir);
2184
2208
  const fresh = {
@@ -2188,7 +2212,8 @@ function resetSyncState(configDir = getConfigDir()) {
2188
2212
  ...previous.repo_filter ? { repo_filter: previous.repo_filter } : {},
2189
2213
  ...previous.project_filter ? { project_filter: previous.project_filter } : {},
2190
2214
  ...previous.paused ? { paused: true } : {},
2191
- ...previous.incognito_agents ? { incognito_agents: previous.incognito_agents } : {}
2215
+ ...previous.incognito_agents ? { incognito_agents: previous.incognito_agents } : {},
2216
+ ...previous.incognito_periods ? { incognito_periods: previous.incognito_periods } : {}
2192
2217
  };
2193
2218
  saveSyncState(fresh, configDir);
2194
2219
  }
@@ -13985,8 +14010,7 @@ function listSessionBacklog() {
13985
14010
  const sessions = filterSessionsByRepo(scanned, filter, resolver.resolveRepo);
13986
14011
  resolver.flush();
13987
14012
  const gate = gateSessions(sessions, state.watermark);
13988
- const incognitoAgents = new Set(state.incognito_agents ?? []);
13989
- const { kept, skipped } = partitionIncognitoSessions(gate.ready, (session) => incognitoAgents.has(session.harness) || isIncognitoSession(session));
14013
+ const { kept, skipped } = partitionIncognitoSessions(gate.ready, (session) => isAgentIncognito(state, session) || isIncognitoSession(session));
13990
14014
  return { queued: kept.reverse(), open: gate.open.reverse(), incognito: skipped.reverse() };
13991
14015
  } catch {
13992
14016
  return { queued: [], open: [], incognito: [] };
@@ -14125,8 +14149,7 @@ async function runKnowledgeSync(options = {}) {
14125
14149
  }
14126
14150
  const worthStudying = deps.worthStudying ?? isWorthStudying;
14127
14151
  const isIncognitoChat = deps.isIncognito ?? isIncognitoSession;
14128
- const incognitoAgents = new Set(state.incognito_agents ?? []);
14129
- const isIncognito = (session) => incognitoAgents.has(session.harness) || isIncognitoChat(session);
14152
+ const isIncognito = (session) => isAgentIncognito(state, session) || isIncognitoChat(session);
14130
14153
  const studiedSnapshot = lastStudiedSnapshot(state.mined_sessions);
14131
14154
  const examined = [];
14132
14155
  const batch = [];
@@ -14333,7 +14356,7 @@ var init_conflicts = __esm(() => {
14333
14356
  });
14334
14357
 
14335
14358
  // src/telemetry/debug-id.ts
14336
- var BUNDLE_DEBUG_ID_PLACEHOLDER = "fa693edb-14f4-abcb-6475-6e2164756e21";
14359
+ var BUNDLE_DEBUG_ID_PLACEHOLDER = "6315379c-189a-f368-6475-6e2164756e21";
14337
14360
 
14338
14361
  // src/telemetry/telemetry.ts
14339
14362
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -62521,10 +62544,13 @@ function hasShape(expected, actual) {
62521
62544
  }
62522
62545
  return expected === actual;
62523
62546
  }
62547
+ function isUserChoiceKey(key) {
62548
+ return USER_CHOICE_KEYS.has(key);
62549
+ }
62524
62550
  function withoutUserChoices(entry) {
62525
62551
  if (typeof entry !== "object" || entry === null || Array.isArray(entry))
62526
62552
  return entry;
62527
- return Object.fromEntries(Object.entries(entry).filter(([key]) => !USER_CHOICE_KEYS.has(key)));
62553
+ return Object.fromEntries(Object.entries(entry).filter(([key]) => !isUserChoiceKey(key)));
62528
62554
  }
62529
62555
  function entryHasShape(expected, actual) {
62530
62556
  return hasShape(withoutUserChoices(expected), withoutUserChoices(actual));
@@ -62723,7 +62749,7 @@ var init_cline_cli = __esm(() => {
62723
62749
  import { existsSync as existsSync22, readFileSync as readFileSync22 } from "node:fs";
62724
62750
  import { dirname as dirname11, join as join29 } from "node:path";
62725
62751
  function codexHome3() {
62726
- return process.env.CODEX_HOME ?? expandHome("~/.codex");
62752
+ return process.env.CODEX_HOME || expandHome("~/.codex");
62727
62753
  }
62728
62754
  function getConfigPath2(global2) {
62729
62755
  if (global2)
@@ -62745,66 +62771,130 @@ function tomlValue(value) {
62745
62771
  return Array.isArray(value) ? `[${value.map(tomlString).join(", ")}]` : tomlString(value);
62746
62772
  }
62747
62773
  function dosuEntry(url, apiKey, npx, path) {
62748
- return npxRemoteEntry(url, apiKey, npx, path);
62774
+ return { ...npxRemoteEntry(url, apiKey, npx, path), omit_tools_from: ["deferred"] };
62775
+ }
62776
+ function isUserKey(key) {
62777
+ return isUserChoiceKey(key) || key === "tools" || key.startsWith("tools.");
62778
+ }
62779
+ function isUserTable(name) {
62780
+ return name === `${DOSU_TABLE}.tools` || name.startsWith(`${DOSU_TABLE}.tools.`);
62749
62781
  }
62750
- function renderDosuEntry(entry) {
62751
- const tables = [["mcp_servers.dosu", {}]];
62782
+ function renderDosuEntry(entry, kept) {
62783
+ const tables = [[DOSU_TABLE, []]];
62752
62784
  for (const [key, value] of Object.entries(entry)) {
62753
- if (typeof value === "string" || Array.isArray(value))
62754
- tables[0][1][key] = value;
62755
- else
62756
- tables.push([`mcp_servers.dosu.${key}`, value]);
62785
+ if (typeof value === "string" || Array.isArray(value)) {
62786
+ tables[0][1].push(`${key} = ${tomlValue(value)}`);
62787
+ } else {
62788
+ const lines = Object.entries(value).map(([k, v]) => `${k} = ${tomlValue(v)}`);
62789
+ tables.push([`${DOSU_TABLE}.${key}`, lines]);
62790
+ }
62757
62791
  }
62758
- return tables.map(([name, keys]) => {
62759
- const lines = Object.entries(keys).map(([key, value]) => `${key} = ${tomlValue(value)}`);
62760
- return `
62792
+ tables[0][1].push(...kept.root);
62793
+ return tables.map(([name, lines]) => `
62761
62794
  [${name}]
62762
62795
  ${lines.join(`
62763
62796
  `)}
62764
- `;
62765
- }).join("");
62766
- }
62767
- function isToolApproval(name) {
62768
- return name.startsWith("mcp_servers.dosu.tools.");
62797
+ `).join("") + kept.tables.map((table) => `
62798
+ ${table}
62799
+ `).join("");
62800
+ }
62801
+ function openBrackets(text) {
62802
+ let depth = 0;
62803
+ let quote = null;
62804
+ for (let i = 0;i < text.length; i++) {
62805
+ const c = text[i];
62806
+ if (quote) {
62807
+ if (c === "\\" && quote === '"')
62808
+ i++;
62809
+ else if (c === quote)
62810
+ quote = null;
62811
+ } else if (c === '"' || c === "'")
62812
+ quote = c;
62813
+ else if (c === "#")
62814
+ break;
62815
+ else if (c === "[" || c === "{")
62816
+ depth++;
62817
+ else if (c === "]" || c === "}")
62818
+ depth--;
62819
+ }
62820
+ return depth;
62769
62821
  }
62770
- function readDosuEntry(content) {
62771
- let entry;
62822
+ function dosuTables(content) {
62823
+ const tables = [];
62772
62824
  let table;
62825
+ let open;
62826
+ let depth = 0;
62773
62827
  for (const line of content.split(`
62774
62828
  `)) {
62775
62829
  const name = sectionName(line);
62776
62830
  if (name !== null) {
62777
- table = undefined;
62778
- if (!isDosuSection(name) || isToolApproval(name))
62779
- continue;
62780
- entry ??= {};
62781
- if (name === "mcp_servers.dosu") {
62782
- table = entry;
62783
- } else {
62784
- table = {};
62785
- entry[name.slice("mcp_servers.dosu.".length)] = table;
62786
- }
62831
+ open = undefined;
62832
+ table = isDosuSection(name) ? { name, header: line.trim(), statements: [] } : undefined;
62833
+ if (table)
62834
+ tables.push(table);
62787
62835
  continue;
62788
62836
  }
62837
+ if (open && depth > 0) {
62838
+ open.value += `
62839
+ ${line}`;
62840
+ open.text += `
62841
+ ${line}`;
62842
+ depth += openBrackets(line);
62843
+ continue;
62844
+ }
62845
+ open = undefined;
62789
62846
  const text = line.trim();
62790
62847
  if (!table || !text || text.startsWith("#"))
62791
62848
  continue;
62792
62849
  const eq = text.indexOf("=");
62793
62850
  const key = eq === -1 ? text : text.slice(0, eq).trim();
62794
- const raw = eq === -1 ? "" : text.slice(eq + 1).trim();
62795
- try {
62796
- table[key] = JSON.parse(raw);
62797
- } catch {
62798
- table[key] = raw;
62851
+ const value = eq === -1 ? "" : text.slice(eq + 1).trim();
62852
+ open = { key, value, text: line };
62853
+ table.statements.push(open);
62854
+ depth = openBrackets(value);
62855
+ }
62856
+ return tables;
62857
+ }
62858
+ function readDosuEntry(content) {
62859
+ let entry;
62860
+ for (const { name, statements } of dosuTables(content)) {
62861
+ entry ??= {};
62862
+ if (isUserTable(name))
62863
+ continue;
62864
+ const isRoot = name === DOSU_TABLE;
62865
+ const table = isRoot ? entry : {};
62866
+ if (!isRoot)
62867
+ entry[name.slice(DOSU_TABLE.length + 1)] = table;
62868
+ for (const { key, value } of statements) {
62869
+ if (isRoot && isUserKey(key))
62870
+ continue;
62871
+ try {
62872
+ table[key] = JSON.parse(value);
62873
+ } catch {
62874
+ table[key] = value;
62875
+ }
62799
62876
  }
62800
62877
  }
62801
62878
  return entry;
62802
62879
  }
62880
+ function userSettings(content) {
62881
+ const kept = { root: [], tables: [] };
62882
+ for (const { name, header, statements } of dosuTables(content)) {
62883
+ if (name === DOSU_TABLE) {
62884
+ kept.root.push(...statements.filter(({ key }) => isUserKey(key)).map(({ text }) => text));
62885
+ } else if (isUserTable(name)) {
62886
+ kept.tables.push([header, ...statements.map(({ text }) => text)].join(`
62887
+ `));
62888
+ }
62889
+ }
62890
+ return kept;
62891
+ }
62803
62892
  function installDosuToTOML(path, cfg) {
62804
- const content = removeDosuFromTOML(readTOML(path));
62893
+ const existing = readTOML(path);
62894
+ const content = removeDosuFromTOML(existing);
62805
62895
  const npx = findNpx();
62806
62896
  const entry = dosuEntry(mcpEndpoint(cfg), cfg.active_account?.target?.api_key, npx, npxPathEnv(npx));
62807
- writeTOML(path, content + renderDosuEntry(entry));
62897
+ writeTOML(path, content + renderDosuEntry(entry, userSettings(existing)));
62808
62898
  }
62809
62899
  function sectionName(line) {
62810
62900
  const header = line.trim();
@@ -62812,7 +62902,7 @@ function sectionName(line) {
62812
62902
  return match ? match[1].trim() : null;
62813
62903
  }
62814
62904
  function isDosuSection(name) {
62815
- return name === "mcp_servers.dosu" || name.startsWith("mcp_servers.dosu.");
62905
+ return name === DOSU_TABLE || name.startsWith(`${DOSU_TABLE}.`);
62816
62906
  }
62817
62907
  function removeDosuFromTOML(content) {
62818
62908
  const result = [];
@@ -62870,7 +62960,7 @@ function pruneLegacyRemoteEntry(path) {
62870
62960
  return;
62871
62961
  writeTOML(path, removeDosuFromTOML(content));
62872
62962
  }
62873
- var REMOTE_HTTP_KEY, CodexProvider = () => ({
62963
+ var DOSU_TABLE = "mcp_servers.dosu", REMOTE_HTTP_KEY, CodexProvider = () => ({
62874
62964
  name: () => "Codex (CLI + Desktop)",
62875
62965
  id: () => "codex",
62876
62966
  supportsLocal: () => true,
@@ -69217,4 +69307,4 @@ main().catch((err) => {
69217
69307
  process.exit(1);
69218
69308
  });
69219
69309
 
69220
- //# debugId=fa693edb-14f4-abcb-6475-6e2164756e21
69310
+ //# debugId=6315379c-189a-f368-6475-6e2164756e21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosu/cli",
3
- "version": "0.67.2",
3
+ "version": "0.68.1",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Set up Dosu MCP for AI tools and work with Dosu Libraries, docs, and reviews",
6
6
  "license": "MIT",