@doist/doistbot-cli 1.0.9 → 1.0.10
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/dist/actions/auth.js +1 -1
- package/dist/actions/review.js +1 -0
- package/dist/config.js +3 -2
- package/dist/terminal.js +1 -0
- package/package.json +2 -2
- package/sandbox/_node_modules/doistbot-repo-config/dist/index.d.ts +1 -1
- package/sandbox/_node_modules/doistbot-repo-config/dist/index.js +1 -1
- package/sandbox/dist/core/datadog-metrics.js +2 -0
- package/sandbox/dist/core/pi.js +4 -0
- package/sandbox/dist/core/tracing.js +1 -1
- package/sandbox/dist/providers/index.js +1 -0
- package/sandbox/dist/tasks/issue-summarize/model.js +4 -4
- package/sandbox/dist/tasks/review/engines/multi-focus.js +9 -9
- package/sandbox/dist/tasks/review/engines/shared.js +2 -0
- package/sandbox/dist/tasks/review/review-summary.js +1 -1
- package/sandbox/dist/tasks/review/review.js +5 -3
- package/sandbox/dist/tasks/review/summary-model.js +5 -4
- package/sandbox/dist/tasks/review/thinking-level.js +2 -0
- package/sandbox/node_modules/doistbot-repo-config/dist/index.d.ts +1 -1
- package/sandbox/node_modules/doistbot-repo-config/dist/index.js +1 -1
package/dist/actions/auth.js
CHANGED
|
@@ -138,7 +138,7 @@ export async function runAuthSetup(dependencies = {}) {
|
|
|
138
138
|
deps.writeLine('OpenRouter not configured. Skipping optional OpenRouter provider.');
|
|
139
139
|
return;
|
|
140
140
|
}
|
|
141
|
-
deps.writeLine(`Doistbot can use OpenRouter for DeepSeek,
|
|
141
|
+
deps.writeLine(`Doistbot can use OpenRouter for DeepSeek, Grok, and other open models. Paste an OpenRouter API key, or ${formatSkipInstruction()}.`);
|
|
142
142
|
const openRouterApiKey = (await deps.promptHiddenInput(API_KEY_PROMPT)).trim();
|
|
143
143
|
if (!openRouterApiKey) {
|
|
144
144
|
deps.writeLine();
|
package/dist/actions/review.js
CHANGED
package/dist/config.js
CHANGED
|
@@ -10,7 +10,7 @@ const ALL_REVIEW_FOCUSES = [
|
|
|
10
10
|
'tests',
|
|
11
11
|
'standards',
|
|
12
12
|
];
|
|
13
|
-
const OPENROUTER_REVIEW_MODELS = ['deepseek', '
|
|
13
|
+
const OPENROUTER_REVIEW_MODELS = ['deepseek', 'grok'];
|
|
14
14
|
const CLI_GEMINI_REVIEW_MODEL = 'gemini-3.7-flash';
|
|
15
15
|
function createCliGeminiCredential(apiKey) {
|
|
16
16
|
return {
|
|
@@ -49,6 +49,7 @@ export async function getApiKeys(env = process.env, auth = {
|
|
|
49
49
|
}
|
|
50
50
|
if (openRouterApiKey) {
|
|
51
51
|
apiKeys.deepseek = { apiKey: openRouterApiKey, piProviderId: 'openrouter' };
|
|
52
|
+
apiKeys.grok = { apiKey: openRouterApiKey, piProviderId: 'openrouter' };
|
|
52
53
|
apiKeys.zai = { apiKey: openRouterApiKey, piProviderId: 'openrouter' };
|
|
53
54
|
}
|
|
54
55
|
if (openAICodexAuthState === 'ready') {
|
|
@@ -76,7 +77,7 @@ export function formatMissingRequiredCliAuthProviders(missingProviders) {
|
|
|
76
77
|
}
|
|
77
78
|
export function getCliReviewRouting(apiKeys) {
|
|
78
79
|
const hasGemini = Boolean(apiKeys.gemini);
|
|
79
|
-
const hasOpenRouter = Boolean(apiKeys.deepseek && apiKeys.
|
|
80
|
+
const hasOpenRouter = Boolean(apiKeys.deepseek && apiKeys.grok);
|
|
80
81
|
if (hasGemini && hasOpenRouter) {
|
|
81
82
|
return {
|
|
82
83
|
reviewModelsEnabled: ['openai', ...OPENROUTER_REVIEW_MODELS],
|
package/dist/terminal.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doist/doistbot-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "Local self-review CLI for Doistbot",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"commander": "14.0.3",
|
|
51
51
|
"js-yaml": "4.3.1",
|
|
52
52
|
"octokit": "5.0.5",
|
|
53
|
-
"undici": "8.
|
|
53
|
+
"undici": "8.10.0",
|
|
54
54
|
"yocto-spinner": "1.2.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Logger } from 'doistbot-logging';
|
|
2
2
|
import type { Octokit } from 'octokit';
|
|
3
3
|
export declare const CONFIG_FILE_PATH = ".github/doistbot.yml";
|
|
4
|
-
export declare const DEFAULT_REVIEW_MODELS: readonly ["openai", "deepseek", "
|
|
4
|
+
export declare const DEFAULT_REVIEW_MODELS: readonly ["openai", "deepseek", "grok"];
|
|
5
5
|
export type DefaultReviewModel = (typeof DEFAULT_REVIEW_MODELS)[number];
|
|
6
6
|
export type RepoConfig = {
|
|
7
7
|
review?: {
|
|
@@ -2,7 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { load as yamlLoad } from 'js-yaml';
|
|
4
4
|
export const CONFIG_FILE_PATH = '.github/doistbot.yml';
|
|
5
|
-
export const DEFAULT_REVIEW_MODELS = ['openai', 'deepseek', '
|
|
5
|
+
export const DEFAULT_REVIEW_MODELS = ['openai', 'deepseek', 'grok'];
|
|
6
6
|
function parseRepoConfigContent(content) {
|
|
7
7
|
return yamlLoad(content);
|
|
8
8
|
}
|
package/sandbox/dist/core/pi.js
CHANGED
|
@@ -6,18 +6,21 @@ import { fileURLToPath } from 'url';
|
|
|
6
6
|
export const PI_PROVIDER_BY_DOISTBOT_PROVIDER = {
|
|
7
7
|
deepseek: 'openrouter',
|
|
8
8
|
gemini: 'google',
|
|
9
|
+
grok: 'openrouter',
|
|
9
10
|
zai: 'openrouter',
|
|
10
11
|
openai: 'openai',
|
|
11
12
|
};
|
|
12
13
|
export const DEFAULT_PI_MODELS = {
|
|
13
14
|
deepseek: 'deepseek-v4-pro',
|
|
14
15
|
gemini: 'gemini-3.1-pro-preview',
|
|
16
|
+
grok: 'grok-4.6',
|
|
15
17
|
zai: 'glm5.2',
|
|
16
18
|
openai: 'gpt-5.6-terra',
|
|
17
19
|
};
|
|
18
20
|
export const DEFAULT_OPENROUTER_MODELS = {
|
|
19
21
|
deepseek: 'deepseek/deepseek-v4-pro-0813',
|
|
20
22
|
gemini: 'google/gemini-3.1-pro-preview',
|
|
23
|
+
grok: 'x-ai/grok-4.6',
|
|
21
24
|
zai: 'z-ai/glm-5.2',
|
|
22
25
|
};
|
|
23
26
|
// Gemini flash fallback model.
|
|
@@ -25,6 +28,7 @@ export const DEFAULT_GEMINI_FLASH_MODEL = 'gemini-3.7-flash';
|
|
|
25
28
|
export const MAX_PI_THINKING_BY_PROVIDER = {
|
|
26
29
|
deepseek: 'high',
|
|
27
30
|
gemini: 'high',
|
|
31
|
+
grok: 'high',
|
|
28
32
|
zai: 'high',
|
|
29
33
|
openai: 'xhigh',
|
|
30
34
|
};
|
|
@@ -72,7 +72,7 @@ export function recordEvent(name, meta) {
|
|
|
72
72
|
/**
|
|
73
73
|
* Maps a doistbot provider key to the OTel `gen_ai.provider.name` value
|
|
74
74
|
* (docs/sandbox-instrumentation.md). Only `gemini` has a standard name
|
|
75
|
-
* (`gcp.gemini`); the OpenRouter-routed keys (`zai`, `deepseek`) have none, so
|
|
75
|
+
* (`gcp.gemini`); the OpenRouter-routed keys (`grok`, `zai`, `deepseek`) have none, so
|
|
76
76
|
* they pass through. Single source so every span site stays consistent.
|
|
77
77
|
*/
|
|
78
78
|
export function genAiProviderName(provider) {
|
|
@@ -4,11 +4,11 @@ import { buildPiImageContent, createPiInvoker, DEFAULT_OPENROUTER_MODELS } from
|
|
|
4
4
|
import { withPhase } from '../../core/tracing.js';
|
|
5
5
|
const ISSUE_SUMMARIZE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
6
6
|
const ISSUE_SUMMARIZE_MULTIMODAL_MODEL = 'google/gemini-2.5-pro';
|
|
7
|
-
const SUMMARIZE_TEXT_PROVIDER = '
|
|
8
|
-
// Single source of truth: inherit the repo-wide
|
|
7
|
+
const SUMMARIZE_TEXT_PROVIDER = 'grok';
|
|
8
|
+
// Single source of truth: inherit the repo-wide Grok default so a default-model
|
|
9
9
|
// bump in core/pi.ts moves summarize too, and the executed model matches the
|
|
10
10
|
// recorded gen_ai.request.model.
|
|
11
|
-
const SUMMARIZE_TEXT_MODEL = DEFAULT_OPENROUTER_MODELS.
|
|
11
|
+
const SUMMARIZE_TEXT_MODEL = DEFAULT_OPENROUTER_MODELS.grok;
|
|
12
12
|
const SUMMARIZE_MULTIMODAL_PROVIDER = 'gcp.gemini';
|
|
13
13
|
// One model span per actual model.invoke. The final summary uses
|
|
14
14
|
// `issue-summarize.model`; each chunk summary uses `issue-summarize.chunk`.
|
|
@@ -16,7 +16,7 @@ const SUMMARIZE_MODEL_PHASE = 'issue-summarize.model';
|
|
|
16
16
|
const SUMMARIZE_CHUNK_PHASE = 'issue-summarize.chunk';
|
|
17
17
|
export function createIssueSummarizeModel(apiKey) {
|
|
18
18
|
return createPiInvoker({
|
|
19
|
-
provider: '
|
|
19
|
+
provider: 'grok',
|
|
20
20
|
apiKey,
|
|
21
21
|
piProviderId: 'openrouter',
|
|
22
22
|
cwd: process.cwd(),
|
|
@@ -10,13 +10,13 @@ import { buildGeminiFlashReviewSummaryModel } from '../summary-model.js';
|
|
|
10
10
|
import { dedupeMultiFocusFindings } from './dedupe.js';
|
|
11
11
|
import { buildGeminiFlashCredential, getApiKeys, getAvailableModels, getEnabledReviewModels, getOpenRouterReviewCredential, getProviderFailures, requestModelReview, } from './shared.js';
|
|
12
12
|
const DEFAULT_REVIEW_MODELS_BY_FOCUS = {
|
|
13
|
-
general: ['deepseek', '
|
|
14
|
-
security: ['deepseek', '
|
|
15
|
-
reuse: ['deepseek', '
|
|
16
|
-
quality: ['deepseek', '
|
|
17
|
-
efficiency: ['deepseek', '
|
|
18
|
-
tests: ['deepseek', '
|
|
19
|
-
standards: ['deepseek', '
|
|
13
|
+
general: ['deepseek', 'grok', 'openai'],
|
|
14
|
+
security: ['deepseek', 'grok', 'openai'],
|
|
15
|
+
reuse: ['deepseek', 'grok', 'openai'],
|
|
16
|
+
quality: ['deepseek', 'grok', 'openai'],
|
|
17
|
+
efficiency: ['deepseek', 'grok', 'openai'],
|
|
18
|
+
tests: ['deepseek', 'grok'],
|
|
19
|
+
standards: ['deepseek', 'grok'],
|
|
20
20
|
};
|
|
21
21
|
// Default Gemini-flash pass on failure. tests/standards keep no fallback unless
|
|
22
22
|
// a caller opts into a wider fallback set.
|
|
@@ -28,7 +28,7 @@ const DEFAULT_GEMINI_FLASH_FALLBACK_FOCUSES = [
|
|
|
28
28
|
'efficiency',
|
|
29
29
|
];
|
|
30
30
|
// Open models whose failed pass triggers that fallback.
|
|
31
|
-
const GEMINI_FLASH_FALLBACK_MODELS = ['deepseek', '
|
|
31
|
+
const GEMINI_FLASH_FALLBACK_MODELS = ['deepseek', 'grok'];
|
|
32
32
|
function lowPrioritySummaryNotice(count) {
|
|
33
33
|
return count === 1
|
|
34
34
|
? 'I also left one optional follow-up note in the details below.'
|
|
@@ -347,7 +347,7 @@ export async function runMultiFocusReviewEngine({ ctx, prompt, lineLookup, prTit
|
|
|
347
347
|
const routedFindings = routeLowPriorityFindings(dedupedFindings, lowPriorityFindingPlacement);
|
|
348
348
|
const geminiSummaryFallback = buildGeminiFlashReviewSummaryModel(apiKeys.gemini);
|
|
349
349
|
const summaryModel = ctx.reviewSummaryModel ??
|
|
350
|
-
(apiKeys.
|
|
350
|
+
(apiKeys.grok ? { provider: 'grok', credential: apiKeys.grok } : undefined);
|
|
351
351
|
const review = await buildMultiFocusReviewOutput({
|
|
352
352
|
inlineFindings: routedFindings.inlineFindings,
|
|
353
353
|
summaryFindings: routedFindings.summaryFindings,
|
|
@@ -22,6 +22,8 @@ export function getAvailableModels(apiKeys) {
|
|
|
22
22
|
models.push('openai');
|
|
23
23
|
if (hasReviewProviderCredential(apiKeys.deepseek))
|
|
24
24
|
models.push('deepseek');
|
|
25
|
+
if (hasReviewProviderCredential(apiKeys.grok))
|
|
26
|
+
models.push('grok');
|
|
25
27
|
if (hasReviewProviderCredential(apiKeys.zai))
|
|
26
28
|
models.push('zai');
|
|
27
29
|
return models;
|
|
@@ -51,7 +51,7 @@ Keep it human and compact: the opener should be 1 sentence, or 2 only if the PR
|
|
|
51
51
|
Respond with only valid JSON in this exact shape:
|
|
52
52
|
{"summary":"<your summary>","comments":[]}`;
|
|
53
53
|
}
|
|
54
|
-
export async function summarizeReviewFindings({ credential, provider = '
|
|
54
|
+
export async function summarizeReviewFindings({ credential, provider = 'grok', modelName, fallbackModels = [], prTitle, prBody, findings, reviewObservations, workspaceDir, requestReview = requestReviewSummary, }) {
|
|
55
55
|
const prompt = buildReviewSummaryPrompt({
|
|
56
56
|
prTitle,
|
|
57
57
|
prBody,
|
|
@@ -15,7 +15,8 @@ import { REVIEW_FOCUS_DEFINITIONS } from './multi-focus-prompt.js';
|
|
|
15
15
|
import { appendReviewFeedbackLink } from './review-feedback.js';
|
|
16
16
|
import { runSharedReview } from './runner.js';
|
|
17
17
|
const REVIEW_EVENT = 'COMMENT';
|
|
18
|
-
const
|
|
18
|
+
const DEFAULT_PRODUCTION_REVIEW_MODELS = getDefaultReviewModels();
|
|
19
|
+
const SUPPORTED_PRODUCTION_REVIEW_MODELS = ['openai', 'deepseek', 'grok', 'zai'];
|
|
19
20
|
const REVIEW_FOCUSES = REVIEW_FOCUS_DEFINITIONS.map((focus) => focus.name);
|
|
20
21
|
function optionalEnv(name) {
|
|
21
22
|
const value = process.env[name]?.trim();
|
|
@@ -43,8 +44,8 @@ function parseContext() {
|
|
|
43
44
|
export function parseReviewModelsEnv(value) {
|
|
44
45
|
return parseCsvAllowList({
|
|
45
46
|
value,
|
|
46
|
-
allowedValues:
|
|
47
|
-
fallback:
|
|
47
|
+
allowedValues: SUPPORTED_PRODUCTION_REVIEW_MODELS,
|
|
48
|
+
fallback: DEFAULT_PRODUCTION_REVIEW_MODELS,
|
|
48
49
|
envName: 'REVIEW_MODELS_ENABLED',
|
|
49
50
|
});
|
|
50
51
|
}
|
|
@@ -253,6 +254,7 @@ async function main() {
|
|
|
253
254
|
}
|
|
254
255
|
if (ctx.openRouterApiKey) {
|
|
255
256
|
reviewApiKeys.deepseek = createOpenRouterCredential(ctx.openRouterApiKey);
|
|
257
|
+
reviewApiKeys.grok = createOpenRouterCredential(ctx.openRouterApiKey);
|
|
256
258
|
reviewApiKeys.zai = createOpenRouterCredential(ctx.openRouterApiKey);
|
|
257
259
|
}
|
|
258
260
|
if (ctx.openaiApiKey) {
|
|
@@ -6,7 +6,7 @@ import { genAiProviderName, withPhase } from '../../core/tracing.js';
|
|
|
6
6
|
import { resolveReviewProviderCredential } from '../../providers/credentials.js';
|
|
7
7
|
import { extractJsonCandidatesFromResponse } from '../../providers/helpers.js';
|
|
8
8
|
const REVIEW_SUMMARY_TIMEOUT_MS = 5 * 60 * 1000;
|
|
9
|
-
const
|
|
9
|
+
const DEFAULT_REVIEW_SUMMARY_THINKING_LEVEL = 'medium';
|
|
10
10
|
// One model span per summary attempt; a fallback chain emits one span per model.
|
|
11
11
|
const SUMMARY_PHASE = 'review.summary';
|
|
12
12
|
const DIFF_SIDE = {
|
|
@@ -56,7 +56,7 @@ function getRequestFailureReason(result) {
|
|
|
56
56
|
? 'no_assistant_response'
|
|
57
57
|
: 'request_failed';
|
|
58
58
|
}
|
|
59
|
-
export async function requestReviewSummary({ credential, provider = '
|
|
59
|
+
export async function requestReviewSummary({ credential, provider = 'grok', modelName, prompt, workspaceDir, }) {
|
|
60
60
|
const resolvedCredential = resolveReviewProviderCredential(credential);
|
|
61
61
|
if (!resolvedCredential) {
|
|
62
62
|
return failedReviewSummaryRequest({ reason: 'missing_credential' });
|
|
@@ -93,12 +93,13 @@ export async function requestReviewSummary({ credential, provider = 'zai', model
|
|
|
93
93
|
}
|
|
94
94
|
async function runReviewSummaryRequest({ provider, resolvedCredential, resolvedModelName, logModelName, prompt, workspaceDir, }) {
|
|
95
95
|
try {
|
|
96
|
+
const thinkingLevel = provider === 'grok' ? 'high' : DEFAULT_REVIEW_SUMMARY_THINKING_LEVEL;
|
|
96
97
|
logger.info('Review summary model request', {
|
|
97
98
|
provider,
|
|
98
99
|
modelName: logModelName,
|
|
99
100
|
promptLength: prompt.length,
|
|
100
101
|
timeoutMs: REVIEW_SUMMARY_TIMEOUT_MS,
|
|
101
|
-
thinkingLevel
|
|
102
|
+
thinkingLevel,
|
|
102
103
|
});
|
|
103
104
|
const result = await invokePiPrompt({
|
|
104
105
|
provider,
|
|
@@ -111,7 +112,7 @@ async function runReviewSummaryRequest({ provider, resolvedCredential, resolvedM
|
|
|
111
112
|
cwd: workspaceDir ?? getWorkspaceDir(),
|
|
112
113
|
timeoutMs: REVIEW_SUMMARY_TIMEOUT_MS,
|
|
113
114
|
capabilityPreset: 'read-only',
|
|
114
|
-
thinkingLevel
|
|
115
|
+
thinkingLevel,
|
|
115
116
|
});
|
|
116
117
|
if (!result.ok) {
|
|
117
118
|
const reason = getRequestFailureReason(result);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Logger } from 'doistbot-logging';
|
|
2
2
|
import type { Octokit } from 'octokit';
|
|
3
3
|
export declare const CONFIG_FILE_PATH = ".github/doistbot.yml";
|
|
4
|
-
export declare const DEFAULT_REVIEW_MODELS: readonly ["openai", "deepseek", "
|
|
4
|
+
export declare const DEFAULT_REVIEW_MODELS: readonly ["openai", "deepseek", "grok"];
|
|
5
5
|
export type DefaultReviewModel = (typeof DEFAULT_REVIEW_MODELS)[number];
|
|
6
6
|
export type RepoConfig = {
|
|
7
7
|
review?: {
|
|
@@ -2,7 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { load as yamlLoad } from 'js-yaml';
|
|
4
4
|
export const CONFIG_FILE_PATH = '.github/doistbot.yml';
|
|
5
|
-
export const DEFAULT_REVIEW_MODELS = ['openai', 'deepseek', '
|
|
5
|
+
export const DEFAULT_REVIEW_MODELS = ['openai', 'deepseek', 'grok'];
|
|
6
6
|
function parseRepoConfigContent(content) {
|
|
7
7
|
return yamlLoad(content);
|
|
8
8
|
}
|