@dosu/cli 0.18.0 → 0.19.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.
- package/bin/dosu.js +367 -153
- package/package.json +1 -1
package/bin/dosu.js
CHANGED
|
@@ -2083,7 +2083,7 @@ var init_esm = __esm(() => {
|
|
|
2083
2083
|
});
|
|
2084
2084
|
|
|
2085
2085
|
// src/config/config.ts
|
|
2086
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2086
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2087
2087
|
import { join } from "node:path";
|
|
2088
2088
|
function getConfigDir() {
|
|
2089
2089
|
const dirName = process.env.DOSU_DEV === "true" ? "dosu-cli-dev" : "dosu-cli";
|
|
@@ -2118,7 +2118,9 @@ function saveConfig(cfg) {
|
|
|
2118
2118
|
if (!existsSync(dir)) {
|
|
2119
2119
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2120
2120
|
}
|
|
2121
|
-
|
|
2121
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
2122
|
+
writeFileSync(tmp, JSON.stringify(cfg, null, 2), { mode: 384 });
|
|
2123
|
+
renameSync(tmp, path);
|
|
2122
2124
|
}
|
|
2123
2125
|
function isAuthenticated(cfg) {
|
|
2124
2126
|
return cfg.access_token !== "";
|
|
@@ -2247,9 +2249,30 @@ class Client {
|
|
|
2247
2249
|
}
|
|
2248
2250
|
}
|
|
2249
2251
|
async refreshToken() {
|
|
2252
|
+
this.adoptNewerDiskTokens();
|
|
2250
2253
|
if (!this.config.refresh_token) {
|
|
2251
2254
|
throw new Error("no refresh token available");
|
|
2252
2255
|
}
|
|
2256
|
+
try {
|
|
2257
|
+
await this.refreshTokenOnce();
|
|
2258
|
+
} catch (err) {
|
|
2259
|
+
if (!this.adoptNewerDiskTokens()) {
|
|
2260
|
+
throw err;
|
|
2261
|
+
}
|
|
2262
|
+
await this.refreshTokenOnce();
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
adoptNewerDiskTokens() {
|
|
2266
|
+
const disk = loadConfig();
|
|
2267
|
+
if (!disk.refresh_token || disk.refresh_token === this.config.refresh_token) {
|
|
2268
|
+
return false;
|
|
2269
|
+
}
|
|
2270
|
+
this.config.access_token = disk.access_token;
|
|
2271
|
+
this.config.refresh_token = disk.refresh_token;
|
|
2272
|
+
this.config.expires_at = disk.expires_at;
|
|
2273
|
+
return true;
|
|
2274
|
+
}
|
|
2275
|
+
async refreshTokenOnce() {
|
|
2253
2276
|
const supabaseURL = getSupabaseURL();
|
|
2254
2277
|
const endpoint = `${supabaseURL}/auth/v1/token?grant_type=refresh_token`;
|
|
2255
2278
|
const resp = await fetch(endpoint, {
|
|
@@ -4525,7 +4548,7 @@ var init_dist4 = __esm(() => {
|
|
|
4525
4548
|
function getVersionString() {
|
|
4526
4549
|
return `v${VERSION}`;
|
|
4527
4550
|
}
|
|
4528
|
-
var VERSION = "0.
|
|
4551
|
+
var VERSION = "0.19.0";
|
|
4529
4552
|
|
|
4530
4553
|
// src/debug/logger.ts
|
|
4531
4554
|
import {
|
|
@@ -5421,6 +5444,45 @@ var init_dist6 = __esm(() => {
|
|
|
5421
5444
|
J2 = `${import_picocolors7.default.gray(o)} `;
|
|
5422
5445
|
});
|
|
5423
5446
|
|
|
5447
|
+
// src/hooks/prompts.ts
|
|
5448
|
+
var exports_prompts = {};
|
|
5449
|
+
__export(exports_prompts, {
|
|
5450
|
+
buildReadyEnvelope: () => buildReadyEnvelope,
|
|
5451
|
+
STOP_PREFIX: () => STOP_PREFIX,
|
|
5452
|
+
SAVE_NUDGE: () => SAVE_NUDGE,
|
|
5453
|
+
LOOKUP_STARTED_NOTE: () => LOOKUP_STARTED_NOTE
|
|
5454
|
+
});
|
|
5455
|
+
function buildReadyEnvelope(context, saveRecommended = false) {
|
|
5456
|
+
const trimmed = context.trim();
|
|
5457
|
+
const blocks = [];
|
|
5458
|
+
if (trimmed) {
|
|
5459
|
+
blocks.push([
|
|
5460
|
+
"Dosu knowledge context for this task:",
|
|
5461
|
+
"",
|
|
5462
|
+
trimmed,
|
|
5463
|
+
"",
|
|
5464
|
+
"How to use this:",
|
|
5465
|
+
"- Use it quietly to answer faster and with fewer broad searches. Keep working normally.",
|
|
5466
|
+
"- 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.",
|
|
5467
|
+
"- 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.",
|
|
5468
|
+
"- 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."
|
|
5469
|
+
].join(`
|
|
5470
|
+
`));
|
|
5471
|
+
}
|
|
5472
|
+
if (saveRecommended) {
|
|
5473
|
+
blocks.push(SAVE_NUDGE);
|
|
5474
|
+
}
|
|
5475
|
+
return blocks.join(`
|
|
5476
|
+
|
|
5477
|
+
`);
|
|
5478
|
+
}
|
|
5479
|
+
var LOOKUP_STARTED_NOTE, STOP_PREFIX, SAVE_NUDGE;
|
|
5480
|
+
var init_prompts = __esm(() => {
|
|
5481
|
+
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.";
|
|
5482
|
+
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.";
|
|
5483
|
+
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.";
|
|
5484
|
+
});
|
|
5485
|
+
|
|
5424
5486
|
// src/hooks/ticket-client.ts
|
|
5425
5487
|
var exports_ticket_client = {};
|
|
5426
5488
|
__export(exports_ticket_client, {
|
|
@@ -5471,45 +5533,6 @@ var init_ticket_client = __esm(() => {
|
|
|
5471
5533
|
};
|
|
5472
5534
|
});
|
|
5473
5535
|
|
|
5474
|
-
// src/hooks/prompts.ts
|
|
5475
|
-
var exports_prompts = {};
|
|
5476
|
-
__export(exports_prompts, {
|
|
5477
|
-
buildReadyEnvelope: () => buildReadyEnvelope,
|
|
5478
|
-
STOP_PREFIX: () => STOP_PREFIX,
|
|
5479
|
-
SAVE_NUDGE: () => SAVE_NUDGE,
|
|
5480
|
-
LOOKUP_STARTED_NOTE: () => LOOKUP_STARTED_NOTE
|
|
5481
|
-
});
|
|
5482
|
-
function buildReadyEnvelope(context, saveRecommended = false) {
|
|
5483
|
-
const trimmed = context.trim();
|
|
5484
|
-
const blocks = [];
|
|
5485
|
-
if (trimmed) {
|
|
5486
|
-
blocks.push([
|
|
5487
|
-
"Dosu knowledge context for this task:",
|
|
5488
|
-
"",
|
|
5489
|
-
trimmed,
|
|
5490
|
-
"",
|
|
5491
|
-
"How to use this:",
|
|
5492
|
-
"- Use it quietly to answer faster and with fewer broad searches. Keep working normally.",
|
|
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: 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
|
-
].join(`
|
|
5497
|
-
`));
|
|
5498
|
-
}
|
|
5499
|
-
if (saveRecommended) {
|
|
5500
|
-
blocks.push(SAVE_NUDGE);
|
|
5501
|
-
}
|
|
5502
|
-
return blocks.join(`
|
|
5503
|
-
|
|
5504
|
-
`);
|
|
5505
|
-
}
|
|
5506
|
-
var LOOKUP_STARTED_NOTE, STOP_PREFIX, SAVE_NUDGE;
|
|
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 when its context " + "helps you.";
|
|
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
|
-
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
|
-
});
|
|
5512
|
-
|
|
5513
5536
|
// src/agent/output.ts
|
|
5514
5537
|
var exports_output = {};
|
|
5515
5538
|
__export(exports_output, {
|
|
@@ -6321,6 +6344,135 @@ var init_claude_code = __esm(() => {
|
|
|
6321
6344
|
};
|
|
6322
6345
|
});
|
|
6323
6346
|
|
|
6347
|
+
// src/hooks/codex.ts
|
|
6348
|
+
var exports_codex = {};
|
|
6349
|
+
__export(exports_codex, {
|
|
6350
|
+
removeCodexHooks: () => removeCodexHooks,
|
|
6351
|
+
installCodexHooks: () => installCodexHooks,
|
|
6352
|
+
inspectCodexHooks: () => inspectCodexHooks,
|
|
6353
|
+
codexHooksPath: () => codexHooksPath,
|
|
6354
|
+
codexHookCommand: () => codexHookCommand
|
|
6355
|
+
});
|
|
6356
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "node:fs";
|
|
6357
|
+
import { join as join5 } from "node:path";
|
|
6358
|
+
function codexHooksPath(dir) {
|
|
6359
|
+
return join5(dir, ".codex", "hooks.json");
|
|
6360
|
+
}
|
|
6361
|
+
function codexHookCommand(subcommand) {
|
|
6362
|
+
return `dosu hooks ${subcommand} --agent codex`;
|
|
6363
|
+
}
|
|
6364
|
+
function dosuGroup2(event) {
|
|
6365
|
+
return {
|
|
6366
|
+
hooks: [
|
|
6367
|
+
{
|
|
6368
|
+
type: "command",
|
|
6369
|
+
command: codexHookCommand(EVENT_SUBCOMMAND2[event]),
|
|
6370
|
+
timeout: EVENT_TIMEOUT_SEC[event],
|
|
6371
|
+
statusMessage: EVENT_STATUS_MESSAGE[event]
|
|
6372
|
+
}
|
|
6373
|
+
]
|
|
6374
|
+
};
|
|
6375
|
+
}
|
|
6376
|
+
function readHooksFileOrThrow(path) {
|
|
6377
|
+
if (!existsSync6(path))
|
|
6378
|
+
return {};
|
|
6379
|
+
const raw = readFileSync7(path, "utf-8").trim();
|
|
6380
|
+
if (!raw)
|
|
6381
|
+
return {};
|
|
6382
|
+
try {
|
|
6383
|
+
return JSON.parse(raw);
|
|
6384
|
+
} catch {
|
|
6385
|
+
throw new Error(`refusing to modify ${path}: file exists but is not valid JSON`);
|
|
6386
|
+
}
|
|
6387
|
+
}
|
|
6388
|
+
function installCodexHooks(configPath, opts = {}) {
|
|
6389
|
+
const config = readHooksFileOrThrow(configPath);
|
|
6390
|
+
const events = DEFAULT_HOOK_EVENTS.filter((e2) => e2 !== "Stop" || opts.stop !== false);
|
|
6391
|
+
const hooks = typeof config.hooks === "object" && config.hooks ? config.hooks : {};
|
|
6392
|
+
for (const event of events) {
|
|
6393
|
+
const groups = Array.isArray(hooks[event]) ? hooks[event] : [];
|
|
6394
|
+
const kept = groups.filter((g2) => !isDosuGroup(g2));
|
|
6395
|
+
kept.push(dosuGroup2(event));
|
|
6396
|
+
hooks[event] = kept;
|
|
6397
|
+
}
|
|
6398
|
+
for (const event of DEFAULT_HOOK_EVENTS) {
|
|
6399
|
+
if (!events.includes(event) && Array.isArray(hooks[event])) {
|
|
6400
|
+
const kept = hooks[event].filter((g2) => !isDosuGroup(g2));
|
|
6401
|
+
if (kept.length > 0)
|
|
6402
|
+
hooks[event] = kept;
|
|
6403
|
+
else
|
|
6404
|
+
delete hooks[event];
|
|
6405
|
+
}
|
|
6406
|
+
}
|
|
6407
|
+
config.hooks = hooks;
|
|
6408
|
+
saveJSONConfig(configPath, config);
|
|
6409
|
+
return { events: [...events] };
|
|
6410
|
+
}
|
|
6411
|
+
function removeCodexHooks(configPath) {
|
|
6412
|
+
if (!existsSync6(configPath))
|
|
6413
|
+
return { removed: false };
|
|
6414
|
+
let config;
|
|
6415
|
+
try {
|
|
6416
|
+
config = readHooksFileOrThrow(configPath);
|
|
6417
|
+
} catch {
|
|
6418
|
+
return { removed: false };
|
|
6419
|
+
}
|
|
6420
|
+
const hooks = config.hooks;
|
|
6421
|
+
if (typeof hooks !== "object" || !hooks)
|
|
6422
|
+
return { removed: false };
|
|
6423
|
+
let removed = false;
|
|
6424
|
+
for (const event of Object.keys(hooks)) {
|
|
6425
|
+
if (!Array.isArray(hooks[event]))
|
|
6426
|
+
continue;
|
|
6427
|
+
const kept = hooks[event].filter((g2) => !isDosuGroup(g2));
|
|
6428
|
+
if (kept.length !== hooks[event].length) {
|
|
6429
|
+
removed = true;
|
|
6430
|
+
if (kept.length > 0)
|
|
6431
|
+
hooks[event] = kept;
|
|
6432
|
+
else
|
|
6433
|
+
delete hooks[event];
|
|
6434
|
+
}
|
|
6435
|
+
}
|
|
6436
|
+
if (removed)
|
|
6437
|
+
saveJSONConfig(configPath, config);
|
|
6438
|
+
return { removed };
|
|
6439
|
+
}
|
|
6440
|
+
function inspectCodexHooks(configPath) {
|
|
6441
|
+
if (!existsSync6(configPath))
|
|
6442
|
+
return { fileExists: false, events: [] };
|
|
6443
|
+
let config;
|
|
6444
|
+
try {
|
|
6445
|
+
config = readHooksFileOrThrow(configPath);
|
|
6446
|
+
} catch {
|
|
6447
|
+
return { fileExists: true, parseError: true, events: [] };
|
|
6448
|
+
}
|
|
6449
|
+
const hooks = config.hooks;
|
|
6450
|
+
if (typeof hooks !== "object" || !hooks)
|
|
6451
|
+
return { fileExists: true, events: [] };
|
|
6452
|
+
const events = Object.keys(hooks).filter((event) => Array.isArray(hooks[event]) && hooks[event].some((g2) => isDosuGroup(g2)));
|
|
6453
|
+
return { fileExists: true, events };
|
|
6454
|
+
}
|
|
6455
|
+
var EVENT_SUBCOMMAND2, EVENT_TIMEOUT_SEC, EVENT_STATUS_MESSAGE;
|
|
6456
|
+
var init_codex = __esm(() => {
|
|
6457
|
+
init_config_helpers();
|
|
6458
|
+
init_claude_code();
|
|
6459
|
+
EVENT_SUBCOMMAND2 = {
|
|
6460
|
+
UserPromptSubmit: "user-prompt-submit",
|
|
6461
|
+
PostToolUse: "post-tool-use",
|
|
6462
|
+
Stop: "stop"
|
|
6463
|
+
};
|
|
6464
|
+
EVENT_TIMEOUT_SEC = {
|
|
6465
|
+
UserPromptSubmit: 10,
|
|
6466
|
+
PostToolUse: 10,
|
|
6467
|
+
Stop: 30
|
|
6468
|
+
};
|
|
6469
|
+
EVENT_STATUS_MESSAGE = {
|
|
6470
|
+
UserPromptSubmit: "Dosu knowledge lookup",
|
|
6471
|
+
PostToolUse: "Dosu knowledge delivery",
|
|
6472
|
+
Stop: "Dosu final knowledge check"
|
|
6473
|
+
};
|
|
6474
|
+
});
|
|
6475
|
+
|
|
6324
6476
|
// src/insights/insights.ts
|
|
6325
6477
|
async function buildInsights({
|
|
6326
6478
|
client,
|
|
@@ -7409,17 +7561,17 @@ var init_insights2 = __esm(() => {
|
|
|
7409
7561
|
});
|
|
7410
7562
|
|
|
7411
7563
|
// src/version/skill-update-check.ts
|
|
7412
|
-
import { existsSync as
|
|
7413
|
-
import { join as
|
|
7564
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "node:fs";
|
|
7565
|
+
import { join as join6 } from "node:path";
|
|
7414
7566
|
function getCachePath() {
|
|
7415
|
-
return
|
|
7567
|
+
return join6(getConfigDir(), CACHE_FILENAME);
|
|
7416
7568
|
}
|
|
7417
7569
|
function readSkillCache() {
|
|
7418
7570
|
try {
|
|
7419
7571
|
const path = getCachePath();
|
|
7420
|
-
if (!
|
|
7572
|
+
if (!existsSync7(path))
|
|
7421
7573
|
return null;
|
|
7422
|
-
const data = JSON.parse(
|
|
7574
|
+
const data = JSON.parse(readFileSync9(path, "utf-8"));
|
|
7423
7575
|
if (typeof data.lastCheck === "number" && typeof data.latestSha === "string" && typeof data.installedSha === "string") {
|
|
7424
7576
|
return data;
|
|
7425
7577
|
}
|
|
@@ -7431,7 +7583,7 @@ function readSkillCache() {
|
|
|
7431
7583
|
function writeSkillCache(cache) {
|
|
7432
7584
|
try {
|
|
7433
7585
|
const dir = getConfigDir();
|
|
7434
|
-
if (!
|
|
7586
|
+
if (!existsSync7(dir)) {
|
|
7435
7587
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
7436
7588
|
}
|
|
7437
7589
|
writeFileSync4(getCachePath(), JSON.stringify(cache), { mode: 384 });
|
|
@@ -7593,21 +7745,21 @@ var init_skill = __esm(() => {
|
|
|
7593
7745
|
var MCP_PROVIDER_SLUG = "dosu_mcp";
|
|
7594
7746
|
|
|
7595
7747
|
// src/mcp/detect.ts
|
|
7596
|
-
import { existsSync as
|
|
7748
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
7597
7749
|
import { homedir, platform } from "node:os";
|
|
7598
|
-
import { join as
|
|
7750
|
+
import { join as join7 } from "node:path";
|
|
7599
7751
|
function isInstalled(paths) {
|
|
7600
|
-
return paths.some((p2) =>
|
|
7752
|
+
return paths.some((p2) => existsSync8(expandHome(p2)));
|
|
7601
7753
|
}
|
|
7602
7754
|
function expandHome(path) {
|
|
7603
7755
|
if (!path.startsWith("~"))
|
|
7604
7756
|
return path;
|
|
7605
|
-
return
|
|
7757
|
+
return join7(homedir(), path.slice(1));
|
|
7606
7758
|
}
|
|
7607
7759
|
function appSupportDir() {
|
|
7608
7760
|
switch (platform()) {
|
|
7609
7761
|
case "darwin": {
|
|
7610
|
-
return
|
|
7762
|
+
return join7(homedir(), "Library", "Application Support");
|
|
7611
7763
|
}
|
|
7612
7764
|
case "win32": {
|
|
7613
7765
|
return process.env.APPDATA ?? "";
|
|
@@ -7616,7 +7768,7 @@ function appSupportDir() {
|
|
|
7616
7768
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
7617
7769
|
if (xdg)
|
|
7618
7770
|
return xdg;
|
|
7619
|
-
return
|
|
7771
|
+
return join7(homedir(), ".config");
|
|
7620
7772
|
}
|
|
7621
7773
|
}
|
|
7622
7774
|
}
|
|
@@ -7697,7 +7849,7 @@ var init_antigravity = __esm(() => {
|
|
|
7697
7849
|
});
|
|
7698
7850
|
|
|
7699
7851
|
// src/mcp/providers/claude.ts
|
|
7700
|
-
import { join as
|
|
7852
|
+
import { join as join8 } from "node:path";
|
|
7701
7853
|
var ClaudeProvider = () => createJSONProvider({
|
|
7702
7854
|
providerName: "Claude Code",
|
|
7703
7855
|
providerID: "claude",
|
|
@@ -7706,22 +7858,22 @@ var ClaudeProvider = () => createJSONProvider({
|
|
|
7706
7858
|
paths: ["~/.claude"],
|
|
7707
7859
|
globalPath: "~/.claude.json",
|
|
7708
7860
|
topKey: "mcpServers",
|
|
7709
|
-
localConfigPath: (cwd) =>
|
|
7861
|
+
localConfigPath: (cwd) => join8(cwd, ".mcp.json")
|
|
7710
7862
|
});
|
|
7711
7863
|
var init_claude = __esm(() => {
|
|
7712
7864
|
init_base();
|
|
7713
7865
|
});
|
|
7714
7866
|
|
|
7715
7867
|
// src/mcp/providers/claude-desktop.ts
|
|
7716
|
-
import { join as
|
|
7868
|
+
import { join as join9 } from "node:path";
|
|
7717
7869
|
var ClaudeDesktopProvider = () => ({
|
|
7718
7870
|
name: () => "Claude Desktop",
|
|
7719
7871
|
id: () => "claude-desktop",
|
|
7720
7872
|
supportsLocal: () => false,
|
|
7721
7873
|
priority: () => 2,
|
|
7722
|
-
detectPaths: () => [
|
|
7723
|
-
isInstalled: () => isInstalled([
|
|
7724
|
-
globalConfigPath: () =>
|
|
7874
|
+
detectPaths: () => [join9(appSupportDir(), "Claude")],
|
|
7875
|
+
isInstalled: () => isInstalled([join9(appSupportDir(), "Claude")]),
|
|
7876
|
+
globalConfigPath: () => join9(appSupportDir(), "Claude", "claude_desktop_config.json"),
|
|
7725
7877
|
isConfigured: () => false,
|
|
7726
7878
|
install() {
|
|
7727
7879
|
throw new Error("this tool only supports local (stdio) servers and cannot be configured for remote MCP");
|
|
@@ -7735,14 +7887,14 @@ var init_claude_desktop = __esm(() => {
|
|
|
7735
7887
|
});
|
|
7736
7888
|
|
|
7737
7889
|
// src/mcp/providers/cline.ts
|
|
7738
|
-
import { join as
|
|
7739
|
-
var extensionDir = () =>
|
|
7890
|
+
import { join as join10 } from "node:path";
|
|
7891
|
+
var extensionDir = () => join10(appSupportDir(), "Code", "User", "globalStorage", "saoudrizwan.claude-dev"), ClineProvider = () => createJSONProvider({
|
|
7740
7892
|
providerName: "Cline",
|
|
7741
7893
|
providerID: "cline",
|
|
7742
7894
|
local: false,
|
|
7743
7895
|
priorityValue: 11,
|
|
7744
7896
|
paths: [extensionDir()],
|
|
7745
|
-
globalPath:
|
|
7897
|
+
globalPath: join10(extensionDir(), "settings", "cline_mcp_settings.json"),
|
|
7746
7898
|
topKey: "mcpServers",
|
|
7747
7899
|
buildServer: (cfg) => ({
|
|
7748
7900
|
url: mcpURL(cfg.deployment_id),
|
|
@@ -7758,7 +7910,7 @@ var init_cline = __esm(() => {
|
|
|
7758
7910
|
});
|
|
7759
7911
|
|
|
7760
7912
|
// src/mcp/providers/cline-cli.ts
|
|
7761
|
-
import { join as
|
|
7913
|
+
import { join as join11 } from "node:path";
|
|
7762
7914
|
function clineDir() {
|
|
7763
7915
|
return process.env.CLINE_DIR ?? expandHome("~/.cline");
|
|
7764
7916
|
}
|
|
@@ -7768,7 +7920,7 @@ var ClineCliProvider = () => createJSONProvider({
|
|
|
7768
7920
|
local: false,
|
|
7769
7921
|
priorityValue: 12,
|
|
7770
7922
|
paths: [clineDir()],
|
|
7771
|
-
globalPath:
|
|
7923
|
+
globalPath: join11(clineDir(), "data", "settings", "cline_mcp_settings.json"),
|
|
7772
7924
|
topKey: "mcpServers",
|
|
7773
7925
|
buildServer: (cfg) => ({
|
|
7774
7926
|
url: mcpURL(cfg.deployment_id),
|
|
@@ -7784,20 +7936,20 @@ var init_cline_cli = __esm(() => {
|
|
|
7784
7936
|
});
|
|
7785
7937
|
|
|
7786
7938
|
// src/mcp/providers/codex.ts
|
|
7787
|
-
import { existsSync as
|
|
7788
|
-
import { join as
|
|
7939
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10 } from "node:fs";
|
|
7940
|
+
import { join as join12 } from "node:path";
|
|
7789
7941
|
function codexHome() {
|
|
7790
7942
|
return process.env.CODEX_HOME ?? expandHome("~/.codex");
|
|
7791
7943
|
}
|
|
7792
7944
|
function getConfigPath2(global) {
|
|
7793
7945
|
if (global)
|
|
7794
|
-
return
|
|
7795
|
-
return
|
|
7946
|
+
return join12(codexHome(), "config.toml");
|
|
7947
|
+
return join12(process.cwd(), ".codex", "config.toml");
|
|
7796
7948
|
}
|
|
7797
7949
|
function readTOML(path) {
|
|
7798
|
-
if (!
|
|
7950
|
+
if (!existsSync9(path))
|
|
7799
7951
|
return "";
|
|
7800
|
-
return
|
|
7952
|
+
return readFileSync10(path, "utf-8");
|
|
7801
7953
|
}
|
|
7802
7954
|
function writeTOML(path, content) {
|
|
7803
7955
|
writeSecureFile(path, content);
|
|
@@ -7855,9 +8007,9 @@ var CodexProvider = () => ({
|
|
|
7855
8007
|
priority: () => 8,
|
|
7856
8008
|
detectPaths: () => ["~/.codex"],
|
|
7857
8009
|
isInstalled: () => isInstalled(["~/.codex"]),
|
|
7858
|
-
globalConfigPath: () =>
|
|
8010
|
+
globalConfigPath: () => join12(codexHome(), "config.toml"),
|
|
7859
8011
|
isConfigured: () => {
|
|
7860
|
-
const content = readTOML(
|
|
8012
|
+
const content = readTOML(join12(codexHome(), "config.toml"));
|
|
7861
8013
|
return content.includes("[mcp_servers.dosu]");
|
|
7862
8014
|
},
|
|
7863
8015
|
install(cfg, global) {
|
|
@@ -7872,17 +8024,17 @@ var CodexProvider = () => ({
|
|
|
7872
8024
|
writeTOML(path, removeDosuFromTOML(content));
|
|
7873
8025
|
}
|
|
7874
8026
|
});
|
|
7875
|
-
var
|
|
8027
|
+
var init_codex2 = __esm(() => {
|
|
7876
8028
|
init_config();
|
|
7877
8029
|
init_config_helpers();
|
|
7878
8030
|
init_detect();
|
|
7879
8031
|
});
|
|
7880
8032
|
|
|
7881
8033
|
// src/mcp/providers/copilot.ts
|
|
7882
|
-
import { join as
|
|
8034
|
+
import { join as join13 } from "node:path";
|
|
7883
8035
|
function globalPath() {
|
|
7884
8036
|
if (process.env.XDG_CONFIG_HOME) {
|
|
7885
|
-
return
|
|
8037
|
+
return join13(process.env.XDG_CONFIG_HOME, "mcp-config.json");
|
|
7886
8038
|
}
|
|
7887
8039
|
return expandHome("~/.copilot/mcp-config.json");
|
|
7888
8040
|
}
|
|
@@ -7913,7 +8065,7 @@ var CopilotProvider = () => ({
|
|
|
7913
8065
|
};
|
|
7914
8066
|
installJSONServer(globalPath(), "mcpServers", server);
|
|
7915
8067
|
} else {
|
|
7916
|
-
const configPath =
|
|
8068
|
+
const configPath = join13(process.cwd(), ".vscode", "mcp.json");
|
|
7917
8069
|
const server = {
|
|
7918
8070
|
type: "http",
|
|
7919
8071
|
url,
|
|
@@ -7926,7 +8078,7 @@ var CopilotProvider = () => ({
|
|
|
7926
8078
|
if (global) {
|
|
7927
8079
|
removeJSONServer(globalPath(), "mcpServers");
|
|
7928
8080
|
} else {
|
|
7929
|
-
const configPath =
|
|
8081
|
+
const configPath = join13(process.cwd(), ".vscode", "mcp.json");
|
|
7930
8082
|
removeJSONServer(configPath, "servers");
|
|
7931
8083
|
}
|
|
7932
8084
|
}
|
|
@@ -7938,7 +8090,7 @@ var init_copilot = __esm(() => {
|
|
|
7938
8090
|
});
|
|
7939
8091
|
|
|
7940
8092
|
// src/mcp/providers/cursor.ts
|
|
7941
|
-
import { join as
|
|
8093
|
+
import { join as join14 } from "node:path";
|
|
7942
8094
|
var CursorProvider = () => createJSONProvider({
|
|
7943
8095
|
providerName: "Cursor",
|
|
7944
8096
|
providerID: "cursor",
|
|
@@ -7951,7 +8103,7 @@ var CursorProvider = () => createJSONProvider({
|
|
|
7951
8103
|
url: mcpURL(cfg.deployment_id),
|
|
7952
8104
|
headers: mcpHeaders(cfg.api_key)
|
|
7953
8105
|
}),
|
|
7954
|
-
localConfigPath: (cwd) =>
|
|
8106
|
+
localConfigPath: (cwd) => join14(cwd, ".cursor", "mcp.json")
|
|
7955
8107
|
});
|
|
7956
8108
|
var init_cursor = __esm(() => {
|
|
7957
8109
|
init_config_helpers();
|
|
@@ -7959,7 +8111,7 @@ var init_cursor = __esm(() => {
|
|
|
7959
8111
|
});
|
|
7960
8112
|
|
|
7961
8113
|
// src/mcp/providers/gemini.ts
|
|
7962
|
-
import { join as
|
|
8114
|
+
import { join as join15 } from "node:path";
|
|
7963
8115
|
var GeminiProvider = () => createJSONProvider({
|
|
7964
8116
|
providerName: "Gemini CLI",
|
|
7965
8117
|
providerID: "gemini",
|
|
@@ -7968,7 +8120,7 @@ var GeminiProvider = () => createJSONProvider({
|
|
|
7968
8120
|
paths: ["~/.gemini"],
|
|
7969
8121
|
globalPath: "~/.gemini/settings.json",
|
|
7970
8122
|
topKey: "mcpServers",
|
|
7971
|
-
localConfigPath: (cwd) =>
|
|
8123
|
+
localConfigPath: (cwd) => join15(cwd, ".gemini", "settings.json")
|
|
7972
8124
|
});
|
|
7973
8125
|
var init_gemini = __esm(() => {
|
|
7974
8126
|
init_base();
|
|
@@ -8018,14 +8170,14 @@ var init_manual = __esm(() => {
|
|
|
8018
8170
|
});
|
|
8019
8171
|
|
|
8020
8172
|
// src/mcp/providers/mcporter.ts
|
|
8021
|
-
import { existsSync as
|
|
8022
|
-
import { join as
|
|
8173
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
8174
|
+
import { join as join16 } from "node:path";
|
|
8023
8175
|
function resolveGlobalConfigPath() {
|
|
8024
8176
|
const jsonPath = expandHome("~/.mcporter/mcporter.json");
|
|
8025
|
-
if (
|
|
8177
|
+
if (existsSync10(jsonPath))
|
|
8026
8178
|
return jsonPath;
|
|
8027
8179
|
const jsoncPath = expandHome("~/.mcporter/mcporter.jsonc");
|
|
8028
|
-
if (
|
|
8180
|
+
if (existsSync10(jsoncPath))
|
|
8029
8181
|
return jsoncPath;
|
|
8030
8182
|
return jsonPath;
|
|
8031
8183
|
}
|
|
@@ -8046,7 +8198,7 @@ var MCPorterProvider = () => ({
|
|
|
8046
8198
|
globalConfigPath: () => resolveGlobalConfigPath(),
|
|
8047
8199
|
isConfigured: () => isJSONKeyConfigured(resolveGlobalConfigPath(), "mcpServers"),
|
|
8048
8200
|
install(cfg, global) {
|
|
8049
|
-
const configPath = global ? resolveGlobalConfigPath() :
|
|
8201
|
+
const configPath = global ? resolveGlobalConfigPath() : join16(process.cwd(), "config", "mcporter.json");
|
|
8050
8202
|
const server = {
|
|
8051
8203
|
type: "http",
|
|
8052
8204
|
url: mcpEndpoint4(cfg),
|
|
@@ -8055,7 +8207,7 @@ var MCPorterProvider = () => ({
|
|
|
8055
8207
|
installJSONServer(configPath, "mcpServers", server);
|
|
8056
8208
|
},
|
|
8057
8209
|
remove(global) {
|
|
8058
|
-
const configPath = global ? resolveGlobalConfigPath() :
|
|
8210
|
+
const configPath = global ? resolveGlobalConfigPath() : join16(process.cwd(), "config", "mcporter.json");
|
|
8059
8211
|
removeJSONServer(configPath, "mcpServers");
|
|
8060
8212
|
}
|
|
8061
8213
|
});
|
|
@@ -8066,7 +8218,7 @@ var init_mcporter = __esm(() => {
|
|
|
8066
8218
|
});
|
|
8067
8219
|
|
|
8068
8220
|
// src/mcp/providers/opencode.ts
|
|
8069
|
-
import { join as
|
|
8221
|
+
import { join as join17 } from "node:path";
|
|
8070
8222
|
var OpenCodeProvider = () => createJSONProvider({
|
|
8071
8223
|
providerName: "OpenCode",
|
|
8072
8224
|
providerID: "opencode",
|
|
@@ -8081,7 +8233,7 @@ var OpenCodeProvider = () => createJSONProvider({
|
|
|
8081
8233
|
enabled: true,
|
|
8082
8234
|
headers: mcpHeaders(cfg.api_key)
|
|
8083
8235
|
}),
|
|
8084
|
-
localConfigPath: (cwd) =>
|
|
8236
|
+
localConfigPath: (cwd) => join17(cwd, "opencode.json")
|
|
8085
8237
|
});
|
|
8086
8238
|
var init_opencode = __esm(() => {
|
|
8087
8239
|
init_config_helpers();
|
|
@@ -8089,16 +8241,16 @@ var init_opencode = __esm(() => {
|
|
|
8089
8241
|
});
|
|
8090
8242
|
|
|
8091
8243
|
// src/mcp/providers/vscode.ts
|
|
8092
|
-
import { join as
|
|
8244
|
+
import { join as join18 } from "node:path";
|
|
8093
8245
|
var VSCodeProvider = () => createJSONProvider({
|
|
8094
8246
|
providerName: "VS Code",
|
|
8095
8247
|
providerID: "vscode",
|
|
8096
8248
|
local: true,
|
|
8097
8249
|
priorityValue: 6,
|
|
8098
|
-
paths: [
|
|
8099
|
-
globalPath:
|
|
8250
|
+
paths: [join18(appSupportDir(), "Code")],
|
|
8251
|
+
globalPath: join18(appSupportDir(), "Code", "User", "mcp.json"),
|
|
8100
8252
|
topKey: "servers",
|
|
8101
|
-
localConfigPath: (cwd) =>
|
|
8253
|
+
localConfigPath: (cwd) => join18(cwd, ".vscode", "mcp.json")
|
|
8102
8254
|
});
|
|
8103
8255
|
var init_vscode = __esm(() => {
|
|
8104
8256
|
init_detect();
|
|
@@ -8107,14 +8259,14 @@ var init_vscode = __esm(() => {
|
|
|
8107
8259
|
|
|
8108
8260
|
// src/mcp/providers/windsurf.ts
|
|
8109
8261
|
import { homedir as homedir2 } from "node:os";
|
|
8110
|
-
import { join as
|
|
8262
|
+
import { join as join19 } from "node:path";
|
|
8111
8263
|
var WindsurfProvider = () => createJSONProvider({
|
|
8112
8264
|
providerName: "Windsurf",
|
|
8113
8265
|
providerID: "windsurf",
|
|
8114
8266
|
local: false,
|
|
8115
8267
|
priorityValue: 9,
|
|
8116
|
-
paths: [
|
|
8117
|
-
globalPath:
|
|
8268
|
+
paths: [join19(homedir2(), ".codeium", "windsurf")],
|
|
8269
|
+
globalPath: join19(homedir2(), ".codeium", "windsurf", "mcp_config.json"),
|
|
8118
8270
|
topKey: "mcpServers"
|
|
8119
8271
|
});
|
|
8120
8272
|
var init_windsurf = __esm(() => {
|
|
@@ -8123,12 +8275,12 @@ var init_windsurf = __esm(() => {
|
|
|
8123
8275
|
|
|
8124
8276
|
// src/mcp/providers/zed.ts
|
|
8125
8277
|
import { platform as platform2 } from "node:os";
|
|
8126
|
-
import { join as
|
|
8278
|
+
import { join as join20 } from "node:path";
|
|
8127
8279
|
function zedConfigDir() {
|
|
8128
8280
|
const os = platform2();
|
|
8129
8281
|
if (os === "darwin" || os === "win32")
|
|
8130
|
-
return
|
|
8131
|
-
return
|
|
8282
|
+
return join20(appSupportDir(), "Zed");
|
|
8283
|
+
return join20(appSupportDir(), "zed");
|
|
8132
8284
|
}
|
|
8133
8285
|
var ZedProvider = () => createJSONProvider({
|
|
8134
8286
|
providerName: "Zed",
|
|
@@ -8136,7 +8288,7 @@ var ZedProvider = () => createJSONProvider({
|
|
|
8136
8288
|
local: true,
|
|
8137
8289
|
priorityValue: 10,
|
|
8138
8290
|
paths: [zedConfigDir()],
|
|
8139
|
-
globalPath:
|
|
8291
|
+
globalPath: join20(zedConfigDir(), "settings.json"),
|
|
8140
8292
|
topKey: "context_servers",
|
|
8141
8293
|
buildServer: (cfg) => ({
|
|
8142
8294
|
source: "custom",
|
|
@@ -8144,7 +8296,7 @@ var ZedProvider = () => createJSONProvider({
|
|
|
8144
8296
|
url: mcpURL(cfg.deployment_id),
|
|
8145
8297
|
headers: mcpHeaders(cfg.api_key)
|
|
8146
8298
|
}),
|
|
8147
|
-
localConfigPath: (cwd) =>
|
|
8299
|
+
localConfigPath: (cwd) => join20(cwd, ".zed", "settings.json")
|
|
8148
8300
|
});
|
|
8149
8301
|
var init_zed = __esm(() => {
|
|
8150
8302
|
init_config_helpers();
|
|
@@ -8188,7 +8340,7 @@ var init_providers = __esm(() => {
|
|
|
8188
8340
|
init_claude_desktop();
|
|
8189
8341
|
init_cline();
|
|
8190
8342
|
init_cline_cli();
|
|
8191
|
-
|
|
8343
|
+
init_codex2();
|
|
8192
8344
|
init_copilot();
|
|
8193
8345
|
init_cursor();
|
|
8194
8346
|
init_gemini();
|
|
@@ -10737,8 +10889,8 @@ __export(exports_flow2, {
|
|
|
10737
10889
|
isStdioOnly: () => isStdioOnly
|
|
10738
10890
|
});
|
|
10739
10891
|
import { randomUUID } from "node:crypto";
|
|
10740
|
-
import { existsSync as
|
|
10741
|
-
import { join as
|
|
10892
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
10893
|
+
import { join as join21 } from "node:path";
|
|
10742
10894
|
async function runSetup(opts = {}) {
|
|
10743
10895
|
const onboardingRunID = randomUUID();
|
|
10744
10896
|
logger.info("setup", `Setup flow started${opts.deploymentID ? ` deployment=${opts.deploymentID}` : ""}${opts.mode ? ` mode=${opts.mode}` : ""}`);
|
|
@@ -10910,7 +11062,7 @@ async function runSetup(opts = {}) {
|
|
|
10910
11062
|
showTryItOutPrompt({
|
|
10911
11063
|
mode: cfg.mode,
|
|
10912
11064
|
docsImported: choices.connectGitHub && githubOnboardingDone,
|
|
10913
|
-
hasAgentsMd:
|
|
11065
|
+
hasAgentsMd: existsSync11(join21(process.cwd(), "AGENTS.md"))
|
|
10914
11066
|
});
|
|
10915
11067
|
}
|
|
10916
11068
|
if (mcpCompleted || skillCompleted || docsImported) {
|
|
@@ -11404,8 +11556,8 @@ var init_flow2 = __esm(() => {
|
|
|
11404
11556
|
});
|
|
11405
11557
|
|
|
11406
11558
|
// src/commands/insights.ts
|
|
11407
|
-
import { existsSync as
|
|
11408
|
-
import { dirname as dirname3, join as
|
|
11559
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync, unlinkSync, writeFileSync as writeFileSync5 } from "node:fs";
|
|
11560
|
+
import { dirname as dirname3, join as join22 } from "node:path";
|
|
11409
11561
|
function shuffled(arr) {
|
|
11410
11562
|
const copy2 = [...arr];
|
|
11411
11563
|
for (let i = copy2.length - 1;i > 0; i--) {
|
|
@@ -11509,17 +11661,17 @@ function makeAskFn(cfg) {
|
|
|
11509
11661
|
};
|
|
11510
11662
|
}
|
|
11511
11663
|
function insightsDir() {
|
|
11512
|
-
return
|
|
11664
|
+
return join22(getConfigDir(), "insights");
|
|
11513
11665
|
}
|
|
11514
11666
|
function reportPath(timestamp) {
|
|
11515
11667
|
const dir = insightsDir();
|
|
11516
11668
|
if (!timestamp)
|
|
11517
|
-
return
|
|
11669
|
+
return join22(dir, "latest.html");
|
|
11518
11670
|
const iso = timestamp.toISOString().slice(0, 19).replace(/:/g, "-");
|
|
11519
|
-
return
|
|
11671
|
+
return join22(dir, `report-${iso}Z.html`);
|
|
11520
11672
|
}
|
|
11521
11673
|
function pruneOldReports(dir, keepN) {
|
|
11522
|
-
if (!
|
|
11674
|
+
if (!existsSync12(dir))
|
|
11523
11675
|
return;
|
|
11524
11676
|
let entries;
|
|
11525
11677
|
try {
|
|
@@ -11531,7 +11683,7 @@ function pruneOldReports(dir, keepN) {
|
|
|
11531
11683
|
const reports = entries.filter((f) => REPORT_FILE_PATTERN.test(f)).sort().reverse();
|
|
11532
11684
|
for (const f of reports.slice(keepN)) {
|
|
11533
11685
|
try {
|
|
11534
|
-
unlinkSync(
|
|
11686
|
+
unlinkSync(join22(dir, f));
|
|
11535
11687
|
} catch (err) {
|
|
11536
11688
|
logger.debug("insights", `failed to prune ${f}: ${err instanceof Error ? err.message : err}`);
|
|
11537
11689
|
}
|
|
@@ -11604,7 +11756,7 @@ function defaultRunner(cfg) {
|
|
|
11604
11756
|
render: renderHTML,
|
|
11605
11757
|
writeFile: (path2, content) => {
|
|
11606
11758
|
const dir = dirname3(path2);
|
|
11607
|
-
if (!
|
|
11759
|
+
if (!existsSync12(dir))
|
|
11608
11760
|
mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
11609
11761
|
writeFileSync5(path2, content, { mode: 384 });
|
|
11610
11762
|
},
|
|
@@ -12269,7 +12421,7 @@ var init_flow3 = __esm(() => {
|
|
|
12269
12421
|
// src/cli/cli.ts
|
|
12270
12422
|
init_esm();
|
|
12271
12423
|
init_client();
|
|
12272
|
-
import { readFileSync as
|
|
12424
|
+
import { readFileSync as readFileSync12, unlinkSync as unlinkSync2 } from "node:fs";
|
|
12273
12425
|
|
|
12274
12426
|
// src/commands/analytics.ts
|
|
12275
12427
|
init_esm();
|
|
@@ -12954,7 +13106,8 @@ ${page.body}`);
|
|
|
12954
13106
|
init_esm();
|
|
12955
13107
|
init_config();
|
|
12956
13108
|
init_logger();
|
|
12957
|
-
|
|
13109
|
+
init_prompts();
|
|
13110
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
12958
13111
|
import { basename } from "node:path";
|
|
12959
13112
|
|
|
12960
13113
|
// src/hooks/state.ts
|
|
@@ -12993,7 +13146,8 @@ var COOLDOWN_DEFAULT_MS = 3000;
|
|
|
12993
13146
|
var TTL_DEFAULT_MS = 10 * 60 * 1000;
|
|
12994
13147
|
var STOP_WAIT_DEFAULT_MS = 8000;
|
|
12995
13148
|
var STOP_POLL_DEFAULT_MS = 1000;
|
|
12996
|
-
var SUPPORTED_AGENTS = ["claude-code"];
|
|
13149
|
+
var SUPPORTED_AGENTS = ["claude-code", "codex"];
|
|
13150
|
+
var DEFAULT_AGENT = "claude-code";
|
|
12997
13151
|
function cooldownMs() {
|
|
12998
13152
|
const n = Number.parseInt(process.env.DOSU_HOOK_CHECK_COOLDOWN_MS ?? "", 10);
|
|
12999
13153
|
return Number.isFinite(n) && n >= 0 ? n : COOLDOWN_DEFAULT_MS;
|
|
@@ -13026,13 +13180,17 @@ function errMsg(err) {
|
|
|
13026
13180
|
function repoSlug(cwd) {
|
|
13027
13181
|
return cwd ? basename(cwd) : undefined;
|
|
13028
13182
|
}
|
|
13029
|
-
async function runUserPromptSubmit(input, now = Date.now()) {
|
|
13183
|
+
async function runUserPromptSubmit(input, now = Date.now(), agent = DEFAULT_AGENT) {
|
|
13030
13184
|
const sessionId = input.session_id;
|
|
13031
13185
|
if (!sessionId)
|
|
13032
13186
|
return;
|
|
13033
13187
|
const prompt = (input.prompt ?? "").trim();
|
|
13034
13188
|
if (!prompt)
|
|
13035
13189
|
return;
|
|
13190
|
+
if (prompt.startsWith(STOP_PREFIX)) {
|
|
13191
|
+
logger.debug("hooks", "submit skipped reason=own-stop-envelope");
|
|
13192
|
+
return;
|
|
13193
|
+
}
|
|
13036
13194
|
const existing = loadState(sessionId);
|
|
13037
13195
|
if (existing && existing.status === "pending" && now <= existing.expiresAt) {
|
|
13038
13196
|
logger.debug("hooks", `submit sid=${sid8(sessionId)} reuse tid=${existing.ticketId}`);
|
|
@@ -13049,7 +13207,7 @@ async function runUserPromptSubmit(input, now = Date.now()) {
|
|
|
13049
13207
|
try {
|
|
13050
13208
|
resp = await tc.requestCreateTicket(cfg, {
|
|
13051
13209
|
deployment_id: cfg.deployment_id,
|
|
13052
|
-
agent
|
|
13210
|
+
agent,
|
|
13053
13211
|
session_id: sessionId,
|
|
13054
13212
|
turn_id: input.turn_id ?? String(now),
|
|
13055
13213
|
prompt,
|
|
@@ -13196,7 +13354,7 @@ function runStatus(input, opts, now = Date.now()) {
|
|
|
13196
13354
|
console.log(`Status: ${data.status}`);
|
|
13197
13355
|
console.log(`Delivered: ${data.delivered ? "yes" : "no"}`);
|
|
13198
13356
|
}
|
|
13199
|
-
async function runHookEntrypoint(event, raw, now = Date.now()) {
|
|
13357
|
+
async function runHookEntrypoint(event, raw, now = Date.now(), agent = DEFAULT_AGENT) {
|
|
13200
13358
|
let input = {};
|
|
13201
13359
|
try {
|
|
13202
13360
|
input = raw.trim() ? JSON.parse(raw) : {};
|
|
@@ -13205,7 +13363,7 @@ async function runHookEntrypoint(event, raw, now = Date.now()) {
|
|
|
13205
13363
|
}
|
|
13206
13364
|
try {
|
|
13207
13365
|
if (event === "user-prompt-submit")
|
|
13208
|
-
await runUserPromptSubmit(input, now);
|
|
13366
|
+
await runUserPromptSubmit(input, now, agent);
|
|
13209
13367
|
else if (event === "post-tool-use")
|
|
13210
13368
|
await runPostToolUse(input, now);
|
|
13211
13369
|
else
|
|
@@ -13218,7 +13376,7 @@ async function runHookEntrypoint(event, raw, now = Date.now()) {
|
|
|
13218
13376
|
}
|
|
13219
13377
|
function readStdinRaw() {
|
|
13220
13378
|
try {
|
|
13221
|
-
return
|
|
13379
|
+
return readFileSync8(0, "utf8");
|
|
13222
13380
|
} catch {
|
|
13223
13381
|
return "";
|
|
13224
13382
|
}
|
|
@@ -13231,12 +13389,13 @@ async function emitErrorLine(step, reason, agentNextSteps) {
|
|
|
13231
13389
|
emitError2({ step, reason, agent_next_steps: agentNextSteps });
|
|
13232
13390
|
}
|
|
13233
13391
|
async function runInstall(agent, opts) {
|
|
13234
|
-
if (agent
|
|
13392
|
+
if (!SUPPORTED_AGENTS.includes(agent)) {
|
|
13235
13393
|
process.exitCode = 2;
|
|
13394
|
+
const supported = SUPPORTED_AGENTS.join(", ");
|
|
13236
13395
|
if (opts.json) {
|
|
13237
|
-
await emitErrorLine("hooks-install", "unsupported_agent", `
|
|
13396
|
+
await emitErrorLine("hooks-install", "unsupported_agent", `Supported agents: ${supported}.`);
|
|
13238
13397
|
} else {
|
|
13239
|
-
console.error(`Unsupported agent '${agent}'. Supported agents:
|
|
13398
|
+
console.error(`Unsupported agent '${agent}'. Supported agents: ${supported}.`);
|
|
13240
13399
|
}
|
|
13241
13400
|
return;
|
|
13242
13401
|
}
|
|
@@ -13249,13 +13408,28 @@ async function runInstall(agent, opts) {
|
|
|
13249
13408
|
}
|
|
13250
13409
|
return;
|
|
13251
13410
|
}
|
|
13252
|
-
const { installClaudeHooks: installClaudeHooks2, claudeLocalSettingsPath: claudeLocalSettingsPath2 } = await Promise.resolve().then(() => (init_claude_code(), exports_claude_code));
|
|
13253
|
-
const configPath = claudeLocalSettingsPath2(resolveDir(opts.dir));
|
|
13254
13411
|
try {
|
|
13412
|
+
if (agent === "codex") {
|
|
13413
|
+
const { installCodexHooks: installCodexHooks2, codexHooksPath: codexHooksPath2 } = await Promise.resolve().then(() => (init_codex(), exports_codex));
|
|
13414
|
+
const configPath2 = codexHooksPath2(resolveDir(opts.dir));
|
|
13415
|
+
const { events: events2 } = installCodexHooks2(configPath2, { stop: opts.stop });
|
|
13416
|
+
if (opts.json) {
|
|
13417
|
+
const { emitStep: emitStep2 } = await Promise.resolve().then(() => exports_output);
|
|
13418
|
+
emitStep2({ step: "hooks-install", agent, path: configPath2, events: events2 });
|
|
13419
|
+
} else {
|
|
13420
|
+
console.log(`✓ Installed Dosu hooks for Codex (${events2.join(", ")}).`);
|
|
13421
|
+
console.log(` → ${configPath2}`);
|
|
13422
|
+
console.log(" One-time step: open Codex in this project and run /hooks to review and");
|
|
13423
|
+
console.log(" trust the Dosu hooks — Codex skips untrusted hooks by design.");
|
|
13424
|
+
}
|
|
13425
|
+
return;
|
|
13426
|
+
}
|
|
13427
|
+
const { installClaudeHooks: installClaudeHooks2, claudeLocalSettingsPath: claudeLocalSettingsPath2 } = await Promise.resolve().then(() => (init_claude_code(), exports_claude_code));
|
|
13428
|
+
const configPath = claudeLocalSettingsPath2(resolveDir(opts.dir));
|
|
13255
13429
|
const { events } = installClaudeHooks2(configPath, { stop: opts.stop });
|
|
13256
13430
|
if (opts.json) {
|
|
13257
13431
|
const { emitStep: emitStep2 } = await Promise.resolve().then(() => exports_output);
|
|
13258
|
-
emitStep2({ step: "hooks-install", path: configPath, events });
|
|
13432
|
+
emitStep2({ step: "hooks-install", agent, path: configPath, events });
|
|
13259
13433
|
} else {
|
|
13260
13434
|
console.log(`✓ Installed Dosu hooks for Claude Code (${events.join(", ")}).`);
|
|
13261
13435
|
console.log(` → ${configPath}`);
|
|
@@ -13271,18 +13445,27 @@ async function runInstall(agent, opts) {
|
|
|
13271
13445
|
}
|
|
13272
13446
|
}
|
|
13273
13447
|
async function runUninstall(agent, opts) {
|
|
13274
|
-
if (agent
|
|
13448
|
+
if (!SUPPORTED_AGENTS.includes(agent)) {
|
|
13275
13449
|
process.exitCode = 2;
|
|
13450
|
+
const supported = SUPPORTED_AGENTS.join(", ");
|
|
13276
13451
|
if (opts.json) {
|
|
13277
|
-
await emitErrorLine("hooks-uninstall", "unsupported_agent",
|
|
13452
|
+
await emitErrorLine("hooks-uninstall", "unsupported_agent", `Supported agents: ${supported}.`);
|
|
13278
13453
|
} else {
|
|
13279
|
-
console.error(`Unsupported agent '${agent}'. Supported agents:
|
|
13454
|
+
console.error(`Unsupported agent '${agent}'. Supported agents: ${supported}.`);
|
|
13280
13455
|
}
|
|
13281
13456
|
return;
|
|
13282
13457
|
}
|
|
13283
|
-
|
|
13284
|
-
|
|
13285
|
-
|
|
13458
|
+
let configPath;
|
|
13459
|
+
let removed;
|
|
13460
|
+
if (agent === "codex") {
|
|
13461
|
+
const { removeCodexHooks: removeCodexHooks2, codexHooksPath: codexHooksPath2 } = await Promise.resolve().then(() => (init_codex(), exports_codex));
|
|
13462
|
+
configPath = codexHooksPath2(resolveDir(opts.dir));
|
|
13463
|
+
({ removed } = removeCodexHooks2(configPath));
|
|
13464
|
+
} else {
|
|
13465
|
+
const { removeClaudeHooks: removeClaudeHooks2, claudeLocalSettingsPath: claudeLocalSettingsPath2 } = await Promise.resolve().then(() => (init_claude_code(), exports_claude_code));
|
|
13466
|
+
configPath = claudeLocalSettingsPath2(resolveDir(opts.dir));
|
|
13467
|
+
({ removed } = removeClaudeHooks2(configPath));
|
|
13468
|
+
}
|
|
13286
13469
|
if (opts.json) {
|
|
13287
13470
|
const { emitStep: emitStep2 } = await Promise.resolve().then(() => exports_output);
|
|
13288
13471
|
emitStep2({ step: "hooks-uninstall", path: configPath, removed });
|
|
@@ -13295,12 +13478,16 @@ async function runUninstall(agent, opts) {
|
|
|
13295
13478
|
async function collectDoctorChecks(opts) {
|
|
13296
13479
|
const checks = [];
|
|
13297
13480
|
const { claudeLocalSettingsPath: claudeLocalSettingsPath2, inspectClaudeHooks: inspectClaudeHooks2 } = await Promise.resolve().then(() => (init_claude_code(), exports_claude_code));
|
|
13481
|
+
const { codexHooksPath: codexHooksPath2, inspectCodexHooks: inspectCodexHooks2 } = await Promise.resolve().then(() => (init_codex(), exports_codex));
|
|
13298
13482
|
const configPath = claudeLocalSettingsPath2(resolveDir(opts.dir));
|
|
13299
13483
|
const inspection = inspectClaudeHooks2(configPath);
|
|
13484
|
+
const codexPath = codexHooksPath2(resolveDir(opts.dir));
|
|
13485
|
+
const codex = inspectCodexHooks2(codexPath);
|
|
13486
|
+
const codexInstalled = codex.events.includes("UserPromptSubmit") && codex.events.includes("PostToolUse");
|
|
13300
13487
|
if (!inspection.fileExists) {
|
|
13301
13488
|
checks.push({
|
|
13302
13489
|
name: "config",
|
|
13303
|
-
status: "fail",
|
|
13490
|
+
status: codexInstalled ? "warn" : "fail",
|
|
13304
13491
|
detail: `not found: ${configPath} (run 'dosu hooks install claude-code')`
|
|
13305
13492
|
});
|
|
13306
13493
|
} else if (inspection.parseError) {
|
|
@@ -13319,10 +13506,32 @@ async function collectDoctorChecks(opts) {
|
|
|
13319
13506
|
} else {
|
|
13320
13507
|
checks.push({
|
|
13321
13508
|
name: "hooks",
|
|
13322
|
-
status: "fail",
|
|
13509
|
+
status: codexInstalled ? "warn" : "fail",
|
|
13323
13510
|
detail: "UserPromptSubmit + PostToolUse not both installed"
|
|
13324
13511
|
});
|
|
13325
13512
|
}
|
|
13513
|
+
if (codex.fileExists) {
|
|
13514
|
+
if (codex.parseError) {
|
|
13515
|
+
checks.push({ name: "codex-config", status: "fail", detail: `invalid JSON: ${codexPath}` });
|
|
13516
|
+
} else if (codexInstalled) {
|
|
13517
|
+
checks.push({
|
|
13518
|
+
name: "codex-config",
|
|
13519
|
+
status: "ok",
|
|
13520
|
+
detail: `installed: ${codex.events.join(", ")} (${codexPath})`
|
|
13521
|
+
});
|
|
13522
|
+
checks.push({
|
|
13523
|
+
name: "codex-trust",
|
|
13524
|
+
status: "warn",
|
|
13525
|
+
detail: "if hooks aren't firing, run /hooks inside Codex to review + trust them (one-time)"
|
|
13526
|
+
});
|
|
13527
|
+
} else {
|
|
13528
|
+
checks.push({
|
|
13529
|
+
name: "codex-config",
|
|
13530
|
+
status: "fail",
|
|
13531
|
+
detail: "UserPromptSubmit + PostToolUse not both installed"
|
|
13532
|
+
});
|
|
13533
|
+
}
|
|
13534
|
+
}
|
|
13326
13535
|
const cfg = loadConfig();
|
|
13327
13536
|
if (isAuthenticated(cfg)) {
|
|
13328
13537
|
checks.push({ name: "auth", status: "ok", detail: "logged in" });
|
|
@@ -13377,15 +13586,15 @@ async function runDoctor(opts) {
|
|
|
13377
13586
|
}
|
|
13378
13587
|
}
|
|
13379
13588
|
function hooksCommand() {
|
|
13380
|
-
const cmd = new Command("hooks").description("Dosu knowledge-injection hooks for Claude Code");
|
|
13381
|
-
cmd.command("user-prompt-submit").description("Hook entrypoint: create a knowledge ticket on prompt submit").action(async () => {
|
|
13382
|
-
await runHookEntrypoint("user-prompt-submit", readStdinRaw());
|
|
13589
|
+
const cmd = new Command("hooks").description("Dosu knowledge-injection hooks for coding agents (Claude Code, Codex)");
|
|
13590
|
+
cmd.command("user-prompt-submit").description("Hook entrypoint: create a knowledge ticket on prompt submit").option("--agent <agent>", "Calling agent for ticket attribution", DEFAULT_AGENT).action(async (opts) => {
|
|
13591
|
+
await runHookEntrypoint("user-prompt-submit", readStdinRaw(), Date.now(), opts.agent);
|
|
13383
13592
|
});
|
|
13384
|
-
cmd.command("post-tool-use").description("Hook entrypoint: poll and inject ready knowledge once").action(async () => {
|
|
13385
|
-
await runHookEntrypoint("post-tool-use", readStdinRaw());
|
|
13593
|
+
cmd.command("post-tool-use").description("Hook entrypoint: poll and inject ready knowledge once").option("--agent <agent>", "Calling agent for ticket attribution", DEFAULT_AGENT).action(async (opts) => {
|
|
13594
|
+
await runHookEntrypoint("post-tool-use", readStdinRaw(), Date.now(), opts.agent);
|
|
13386
13595
|
});
|
|
13387
|
-
cmd.command("stop").description("Hook entrypoint: last-chance knowledge delivery when the agent stops").action(async () => {
|
|
13388
|
-
await runHookEntrypoint("stop", readStdinRaw());
|
|
13596
|
+
cmd.command("stop").description("Hook entrypoint: last-chance knowledge delivery when the agent stops").option("--agent <agent>", "Calling agent for ticket attribution", DEFAULT_AGENT).action(async (opts) => {
|
|
13597
|
+
await runHookEntrypoint("stop", readStdinRaw(), Date.now(), opts.agent);
|
|
13389
13598
|
});
|
|
13390
13599
|
cmd.command("status").description("Show the active Dosu knowledge ticket for this session").option("--json", "Output JSON", false).action((opts) => {
|
|
13391
13600
|
let input = {};
|
|
@@ -13403,16 +13612,21 @@ function hooksCommand() {
|
|
|
13403
13612
|
"",
|
|
13404
13613
|
"Examples:",
|
|
13405
13614
|
" $ dosu hooks install claude-code",
|
|
13615
|
+
" $ dosu hooks install codex",
|
|
13406
13616
|
" $ dosu hooks install claude-code --no-stop",
|
|
13407
|
-
" $ dosu hooks install
|
|
13617
|
+
" $ dosu hooks install codex --dir ./my-project",
|
|
13618
|
+
"",
|
|
13619
|
+
"Codex requires a one-time trust step after install: open Codex in the",
|
|
13620
|
+
"project and run /hooks to review and trust the Dosu hooks."
|
|
13408
13621
|
].join(`
|
|
13409
13622
|
`)).action((agent, opts) => runInstall(agent, opts));
|
|
13410
13623
|
cmd.command("uninstall").description("Remove Dosu hooks from a coding agent's local config (Dosu-owned entries only)").addArgument(new Argument("<agent>", "coding agent to clean up").choices(SUPPORTED_AGENTS)).option("--scope <scope>", "Config scope (local only)", "local").option("--dir <path>", "Project root (defaults to current directory)").option("--json", "Emit machine-readable JSON", false).addHelpText("after", [
|
|
13411
13624
|
"",
|
|
13412
13625
|
`Supported agents: ${SUPPORTED_AGENTS.join(", ")}`,
|
|
13413
13626
|
"",
|
|
13414
|
-
"
|
|
13415
|
-
" $ dosu hooks uninstall claude-code"
|
|
13627
|
+
"Examples:",
|
|
13628
|
+
" $ dosu hooks uninstall claude-code",
|
|
13629
|
+
" $ dosu hooks uninstall codex"
|
|
13416
13630
|
].join(`
|
|
13417
13631
|
`)).action((agent, opts) => runUninstall(agent, opts));
|
|
13418
13632
|
cmd.command("doctor").description("Diagnose Dosu hook config, auth, deployment, and backend connectivity").option("--dir <path>", "Project root (defaults to current directory)").option("--json", "Emit machine-readable JSON", false).action((opts) => runDoctor(opts));
|
|
@@ -14248,8 +14462,8 @@ init_skill_update_check();
|
|
|
14248
14462
|
init_config();
|
|
14249
14463
|
init_logger();
|
|
14250
14464
|
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
14251
|
-
import { existsSync as
|
|
14252
|
-
import { join as
|
|
14465
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
|
|
14466
|
+
import { join as join23 } from "node:path";
|
|
14253
14467
|
var CACHE_FILENAME2 = "update-check.json";
|
|
14254
14468
|
var CHECK_INTERVAL_MS2 = 24 * 60 * 60 * 1000;
|
|
14255
14469
|
var FETCH_TIMEOUT_MS2 = 5000;
|
|
@@ -14272,14 +14486,14 @@ function isNewerVersion(latest, current) {
|
|
|
14272
14486
|
return false;
|
|
14273
14487
|
}
|
|
14274
14488
|
function getCachePath2() {
|
|
14275
|
-
return
|
|
14489
|
+
return join23(getConfigDir(), CACHE_FILENAME2);
|
|
14276
14490
|
}
|
|
14277
14491
|
function readCache() {
|
|
14278
14492
|
try {
|
|
14279
14493
|
const path2 = getCachePath2();
|
|
14280
|
-
if (!
|
|
14494
|
+
if (!existsSync13(path2))
|
|
14281
14495
|
return null;
|
|
14282
|
-
const data = JSON.parse(
|
|
14496
|
+
const data = JSON.parse(readFileSync11(path2, "utf-8"));
|
|
14283
14497
|
if (typeof data.lastCheck === "number" && typeof data.latestVersion === "string") {
|
|
14284
14498
|
return data;
|
|
14285
14499
|
}
|
|
@@ -14291,7 +14505,7 @@ function readCache() {
|
|
|
14291
14505
|
function writeCache(cache) {
|
|
14292
14506
|
try {
|
|
14293
14507
|
const dir = getConfigDir();
|
|
14294
|
-
if (!
|
|
14508
|
+
if (!existsSync13(dir)) {
|
|
14295
14509
|
mkdirSync7(dir, { recursive: true, mode: 448 });
|
|
14296
14510
|
}
|
|
14297
14511
|
writeFileSync6(getCachePath2(), JSON.stringify(cache), { mode: 384 });
|
|
@@ -14581,7 +14795,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
|
|
|
14581
14795
|
if (opts.tail !== undefined) {
|
|
14582
14796
|
const n = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
|
|
14583
14797
|
try {
|
|
14584
|
-
const content =
|
|
14798
|
+
const content = readFileSync12(logPath, "utf-8");
|
|
14585
14799
|
const lines = content.split(`
|
|
14586
14800
|
`);
|
|
14587
14801
|
console.log(lines.slice(-n).join(`
|