@doist/doistbot-cli 1.0.6 → 1.0.8

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 (36) hide show
  1. package/dist/actions/auth.js +43 -11
  2. package/dist/actions/doctor.js +4 -2
  3. package/dist/actions/review.js +11 -14
  4. package/dist/auth.js +23 -6
  5. package/dist/config.js +58 -12
  6. package/package.json +3 -3
  7. package/sandbox/_node_modules/doistbot-repo-config/dist/index.d.ts +1 -1
  8. package/sandbox/_node_modules/doistbot-repo-config/dist/index.js +1 -1
  9. package/sandbox/dist/core/pi.js +31 -1
  10. package/sandbox/dist/core/prompt-template.js +45 -3
  11. package/sandbox/dist/core/repository-map.js +24 -0
  12. package/sandbox/dist/core/tracing.js +9 -0
  13. package/sandbox/dist/providers/helpers.js +51 -0
  14. package/sandbox/dist/providers/pi-review.js +7 -1
  15. package/sandbox/dist/tasks/chat/chat.js +20 -8
  16. package/sandbox/dist/tasks/issue-summarize/model.js +119 -48
  17. package/sandbox/dist/tasks/issue-summarize/prompt.js +2 -2
  18. package/sandbox/dist/tasks/issue-summarize/summarize.js +104 -88
  19. package/sandbox/dist/tasks/issue-triage/fix-attempt.js +3 -2
  20. package/sandbox/dist/tasks/issue-triage/hero-group-map.js +2 -0
  21. package/sandbox/dist/tasks/issue-triage/pr-creator.js +2 -2
  22. package/sandbox/dist/tasks/issue-triage/prompt.js +2 -2
  23. package/sandbox/dist/tasks/issue-triage/triage.js +2 -2
  24. package/sandbox/dist/tasks/persist-logs/llm-query-planner.js +3 -3
  25. package/sandbox/dist/tasks/review/engines/dedupe.js +107 -42
  26. package/sandbox/dist/tasks/review/engines/multi-focus.js +229 -119
  27. package/sandbox/dist/tasks/review/engines/shared.js +36 -1
  28. package/sandbox/dist/tasks/review/github-comment-format.js +12 -4
  29. package/sandbox/dist/tasks/review/local.js +6 -1
  30. package/sandbox/dist/tasks/review/review-summary.js +3 -0
  31. package/sandbox/dist/tasks/review/review.js +253 -186
  32. package/sandbox/dist/tasks/review/runner.js +10 -5
  33. package/sandbox/dist/tasks/review/summary-model.js +40 -18
  34. package/sandbox/node_modules/doistbot-repo-config/dist/index.d.ts +1 -1
  35. package/sandbox/node_modules/doistbot-repo-config/dist/index.js +1 -1
  36. package/sandbox/src/tasks/review/prompts/review-multi-focus-base-prompt.md +1 -0
@@ -1,8 +1,8 @@
1
1
  import { createHash } from 'crypto';
2
2
  import fs from 'fs';
3
3
  import { logger } from '../../core/logging.js';
4
- import { hasReviewProviderCredential } from '../../providers/credentials.js';
5
4
  import { runMultiFocusReviewEngine } from './engines/multi-focus.js';
5
+ import { getEnabledReviewModels } from './engines/shared.js';
6
6
  import {} from './engines/types.js';
7
7
  import { buildAnnotatedDiff, buildDiffSection } from './diff.js';
8
8
  import { buildMultiFocusSharedPrompt } from './multi-focus-prompt.js';
@@ -49,9 +49,6 @@ function buildEnginePrompt({ input, diffSection, truncated, standardsSection, pr
49
49
  prompt: buildMultiFocusSharedPrompt(sharedPromptArgs),
50
50
  };
51
51
  }
52
- function getEnabledReviewModels({ apiKeys, reviewModelsEnabled, }) {
53
- return reviewModelsEnabled.filter((model) => hasReviewProviderCredential(apiKeys[model]));
54
- }
55
52
  function getMissingRequiredReviewModels(availableModels, requiredReviewModels = []) {
56
53
  return requiredReviewModels.filter((model) => !availableModels.includes(model));
57
54
  }
@@ -92,10 +89,11 @@ export async function runSharedReview(input) {
92
89
  providerFailures: [],
93
90
  };
94
91
  }
95
- const configuredAvailableModels = getEnabledReviewModels(input);
92
+ const configuredAvailableModels = getEnabledReviewModels(input.apiKeys, input.reviewModelsEnabled);
96
93
  if (configuredAvailableModels.length === 0) {
97
94
  logger.error('No enabled review models with API keys are available, skipping review', {
98
95
  review_models_enabled: input.reviewModelsEnabled,
96
+ review_focuses_enabled: input.reviewFocusesEnabled,
99
97
  });
100
98
  return {
101
99
  status: 'skipped',
@@ -110,6 +108,7 @@ export async function runSharedReview(input) {
110
108
  if (missingRequiredModels.length > 0) {
111
109
  logger.error('Required review models with API keys are unavailable, skipping review', {
112
110
  review_models_enabled: input.reviewModelsEnabled,
111
+ review_focuses_enabled: input.reviewFocusesEnabled,
113
112
  available_models: configuredAvailableModels,
114
113
  missing_required_models: missingRequiredModels,
115
114
  });
@@ -152,6 +151,11 @@ export async function runSharedReview(input) {
152
151
  workspaceDir: input.workspaceDir,
153
152
  apiKeys: input.apiKeys,
154
153
  reviewModelsEnabled: input.reviewModelsEnabled,
154
+ ...(input.reviewModelsByFocus ? { reviewModelsByFocus: input.reviewModelsByFocus } : {}),
155
+ ...(input.geminiFallbackFocuses
156
+ ? { geminiFallbackFocuses: input.geminiFallbackFocuses }
157
+ : {}),
158
+ ...(input.reviewSummaryModel ? { reviewSummaryModel: input.reviewSummaryModel } : {}),
155
159
  openRouterSessionId: buildOpenRouterReviewSessionId({
156
160
  owner: input.owner,
157
161
  repo: input.repo,
@@ -167,6 +171,7 @@ export async function runSharedReview(input) {
167
171
  prTitle: input.prTitle,
168
172
  prBody: input.prBody,
169
173
  lowPriorityFindingPlacement: input.lowPriorityFindingPlacement,
174
+ enabledFocuses: input.reviewFocusesEnabled,
170
175
  onProgress: input.onProgress,
171
176
  });
172
177
  if (availableModels.length === 0) {
@@ -1,14 +1,14 @@
1
+ import { SpanStatusCode } from '@opentelemetry/api';
1
2
  import { logger } from '../../core/logging.js';
2
- import { DEFAULT_OPENROUTER_MODELS, DEFAULT_PI_MODELS, invokePiPrompt, PI_PROVIDER_BY_DOISTBOT_PROVIDER, } from '../../core/pi.js';
3
+ import { DEFAULT_GEMINI_FLASH_MODEL, DEFAULT_OPENROUTER_MODELS, DEFAULT_PI_MODELS, invokePiPrompt, PI_PROVIDER_BY_DOISTBOT_PROVIDER, } from '../../core/pi.js';
3
4
  import { WORKSPACE_DIR } from '../../core/shared.js';
5
+ import { genAiProviderName, withPhase } from '../../core/tracing.js';
4
6
  import { resolveReviewProviderCredential } from '../../providers/credentials.js';
5
7
  import { extractJsonCandidatesFromResponse } from '../../providers/helpers.js';
6
8
  const REVIEW_SUMMARY_TIMEOUT_MS = 5 * 60 * 1000;
7
9
  const REVIEW_SUMMARY_THINKING_LEVEL = 'medium';
8
- const GEMINI_FLASH_SUMMARY_MODEL_BY_PI_PROVIDER = {
9
- google: 'gemini-3-flash-preview',
10
- openrouter: 'google/gemini-3-flash-preview',
11
- };
10
+ // One model span per summary attempt; a fallback chain emits one span per model.
11
+ const SUMMARY_PHASE = 'review.summary';
12
12
  const DIFF_SIDE = {
13
13
  LEFT: 'LEFT',
14
14
  RIGHT: 'RIGHT',
@@ -32,13 +32,10 @@ export function buildGeminiFlashReviewSummaryModel(credential) {
32
32
  if (!resolvedCredential) {
33
33
  return undefined;
34
34
  }
35
- const piProviderId = resolvedCredential.piProviderId ?? 'google';
36
- const modelName = GEMINI_FLASH_SUMMARY_MODEL_BY_PI_PROVIDER[piProviderId] ??
37
- GEMINI_FLASH_SUMMARY_MODEL_BY_PI_PROVIDER.google;
38
35
  return {
39
36
  provider: 'gemini',
40
37
  credential,
41
- modelName,
38
+ modelName: DEFAULT_GEMINI_FLASH_MODEL,
42
39
  };
43
40
  }
44
41
  function failedReviewSummaryRequest({ reason, error, timedOut, durationMs, outputChars, }) {
@@ -60,17 +57,42 @@ function getRequestFailureReason(result) {
60
57
  : 'request_failed';
61
58
  }
62
59
  export async function requestReviewSummary({ credential, provider = 'zai', modelName, prompt, workspaceDir, }) {
63
- try {
64
- const resolvedCredential = resolveReviewProviderCredential(credential);
65
- if (!resolvedCredential) {
66
- return failedReviewSummaryRequest({ reason: 'missing_credential' });
67
- }
68
- const resolvedModelName = modelName ?? resolvedCredential.modelName;
69
- const logModelName = getReviewSummaryModelNameForLog({
60
+ const resolvedCredential = resolveReviewProviderCredential(credential);
61
+ if (!resolvedCredential) {
62
+ return failedReviewSummaryRequest({ reason: 'missing_credential' });
63
+ }
64
+ const resolvedModelName = modelName ?? resolvedCredential.modelName;
65
+ const logModelName = getReviewSummaryModelNameForLog({
66
+ provider,
67
+ piProviderId: resolvedCredential.piProviderId,
68
+ modelName: resolvedModelName,
69
+ });
70
+ // Span wraps the actual model invoke (after the credential check), so a
71
+ // missing-credential config error returns before any span and never shows a fake model attempt.
72
+ return withPhase(SUMMARY_PHASE, {
73
+ 'gen_ai.operation.name': 'chat',
74
+ 'gen_ai.provider.name': genAiProviderName(provider),
75
+ 'gen_ai.request.model': logModelName,
76
+ }, async (span) => {
77
+ const result = await runReviewSummaryRequest({
70
78
  provider,
71
- piProviderId: resolvedCredential.piProviderId,
72
- modelName: resolvedModelName,
79
+ resolvedCredential,
80
+ resolvedModelName,
81
+ logModelName,
82
+ prompt,
83
+ workspaceDir,
73
84
  });
85
+ if (result.status === 'failed') {
86
+ span.setStatus({
87
+ code: SpanStatusCode.ERROR,
88
+ message: result.error ?? result.reason,
89
+ });
90
+ }
91
+ return result;
92
+ });
93
+ }
94
+ async function runReviewSummaryRequest({ provider, resolvedCredential, resolvedModelName, logModelName, prompt, workspaceDir, }) {
95
+ try {
74
96
  logger.info('Review summary model request', {
75
97
  provider,
76
98
  modelName: logModelName,
@@ -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 ["gemini", "openai", "deepseek", "zai"];
4
+ export declare const DEFAULT_REVIEW_MODELS: readonly ["openai", "deepseek", "zai"];
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 = ['gemini', 'openai', 'deepseek', 'zai'];
5
+ export const DEFAULT_REVIEW_MODELS = ['openai', 'deepseek', 'zai'];
6
6
  function parseRepoConfigContent(content) {
7
7
  return yamlLoad(content);
8
8
  }
@@ -71,6 +71,7 @@ $DIFF_SECTION
71
71
  - Use one comment per distinct issue.
72
72
  - Ignore trivial style unless it obscures meaning or violates documented standards.
73
73
  - Keep code ranges tight and comments easy to understand without close reading.
74
+ - **Do not flag dependency or tooling versions as nonexistent, outdated, or "too new."** Your training data has a cutoff, so you can't know which versions of GitHub Actions, npm/PyPI/etc. packages, or their APIs are currently published. Never tell the author a version doesn't exist, is wrong, or should be downgraded based on your own knowledge — and never claim a library feature or API is unavailable because you assume the project uses an older major version. Raise a version-related finding only when the diff or checked-out repo contains concrete contradicting evidence (e.g., the manifest/lockfile pins a different version, or the referenced symbol is absent from the dependency's actual source on disk).
74
75
 
75
76
  ## Writing Style
76
77