@cat-factory/app 0.304.0 → 0.305.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.
@@ -0,0 +1,95 @@
1
+ <script setup lang="ts">
2
+ // Pick a linked repository and one of its pull requests (an open one from the synced list, or any
3
+ // number typed in) to open a guided review on.
4
+ import type { OpenGuidedReviewInput } from '~/types/domain'
5
+
6
+ defineProps<{ opening: boolean; loading: boolean }>()
7
+ const emit = defineEmits<{ open: [target: OpenGuidedReviewInput] }>()
8
+ const { t } = useI18n()
9
+ const github = useGitHubStore()
10
+
11
+ const repoId = ref<number | undefined>(undefined)
12
+ const prNumber = ref<number | undefined>(undefined)
13
+
14
+ const repoItems = computed(() =>
15
+ github.repos.map((r) => ({ label: `${r.owner}/${r.name}`, value: r.githubId })),
16
+ )
17
+ const repo = computed(() => github.repos.find((r) => r.githubId === repoId.value))
18
+ const openPulls = computed(() =>
19
+ repo.value ? github.pullsForRepo(repo.value.githubId).filter((p) => p.state === 'open') : [],
20
+ )
21
+
22
+ function submit(number = prNumber.value): void {
23
+ const r = repo.value
24
+ if (!r || !number) return
25
+ emit('open', {
26
+ owner: r.owner,
27
+ repo: r.name,
28
+ prNumber: number,
29
+ ...(r.provider ? { provider: r.provider } : {}),
30
+ })
31
+ }
32
+ </script>
33
+
34
+ <template>
35
+ <div class="mx-auto max-w-xl space-y-4" data-testid="guided-review-picker">
36
+ <p class="text-sm text-muted">{{ t('guidedReview.picker.intro') }}</p>
37
+ <p v-if="loading" class="flex items-center gap-2 text-sm text-muted">
38
+ <UIcon name="i-lucide-loader-circle" class="h-4 w-4 animate-spin" />
39
+ {{ t('guidedReview.picker.loading') }}
40
+ </p>
41
+ <p v-else-if="!github.repos.length" class="text-sm text-toned">
42
+ {{ t('guidedReview.picker.noRepos') }}
43
+ </p>
44
+ <template v-else>
45
+ <UFormField :label="t('guidedReview.picker.repo')">
46
+ <USelectMenu
47
+ v-model="repoId"
48
+ :items="repoItems"
49
+ value-key="value"
50
+ class="w-full"
51
+ data-testid="guided-review-picker-repo"
52
+ />
53
+ </UFormField>
54
+ <div v-if="openPulls.length" class="space-y-1">
55
+ <p class="text-xs font-semibold uppercase text-dimmed">
56
+ {{ t('guidedReview.picker.openPulls') }}
57
+ </p>
58
+ <UButton
59
+ v-for="pr in openPulls"
60
+ :key="pr.number"
61
+ block
62
+ size="sm"
63
+ variant="ghost"
64
+ color="neutral"
65
+ class="justify-start"
66
+ :disabled="opening"
67
+ :data-testid="`guided-review-picker-pr-${pr.number}`"
68
+ @click="submit(pr.number)"
69
+ >
70
+ <span class="truncate">#{{ pr.number }} {{ pr.title }}</span>
71
+ </UButton>
72
+ </div>
73
+ <div class="flex items-end gap-2">
74
+ <UFormField :label="t('guidedReview.picker.number')" class="flex-1">
75
+ <UInputNumber
76
+ v-model="prNumber"
77
+ :min="1"
78
+ class="w-full"
79
+ data-testid="guided-review-picker-number"
80
+ />
81
+ </UFormField>
82
+ <UButton
83
+ color="primary"
84
+ icon="i-lucide-scan-search"
85
+ :loading="opening"
86
+ :disabled="!repo || !prNumber"
87
+ data-testid="guided-review-picker-open"
88
+ @click="submit()"
89
+ >
90
+ {{ t('guidedReview.picker.open') }}
91
+ </UButton>
92
+ </div>
93
+ </template>
94
+ </div>
95
+ </template>
@@ -0,0 +1,177 @@
1
+ <script setup lang="ts">
2
+ // One exploration thread: its messages, the comment drafts it produced, and a composer. While its
3
+ // own answer is pending the composer waits; other threads are unaffected.
4
+ import type { GuidedReviewCommentDraft, GuidedReviewThreadView } from '~/types/domain'
5
+ import MarkdownProse from '~/components/common/MarkdownProse.vue'
6
+ import GuidedReviewFailure from '~/components/guidedReview/GuidedReviewFailure.vue'
7
+ import {
8
+ canAsk,
9
+ citationLabel,
10
+ draftAnchor,
11
+ droppedAnchor,
12
+ isLive,
13
+ keptDrafts,
14
+ } from '~/components/guidedReview/GuidedReview.logic'
15
+
16
+ const props = defineProps<{
17
+ sessionId: string
18
+ /** Absent for the unsaved new-thread tab: its first question creates the thread. */
19
+ view: GuidedReviewThreadView | undefined
20
+ drafts: GuidedReviewCommentDraft[]
21
+ }>()
22
+ const emit = defineEmits<{ created: [threadId: string] }>()
23
+ const { t } = useI18n()
24
+ const store = useGuidedReviewStore()
25
+ const { present } = usePipelineErrorToast()
26
+
27
+ const prompt = ref('')
28
+ const sending = ref(false)
29
+ const messages = computed(() => props.view?.messages ?? [])
30
+ const ready = computed(() => canAsk(messages.value) && !sending.value)
31
+ const canSend = computed(() => ready.value && prompt.value.trim().length > 0)
32
+
33
+ async function send(): Promise<void> {
34
+ if (!canSend.value) return
35
+ const content = prompt.value.trim()
36
+ sending.value = true
37
+ try {
38
+ if (props.view) {
39
+ await store.ask(props.sessionId, props.view.thread.id, { content })
40
+ } else {
41
+ const created = await store.openThread(props.sessionId, { question: { content } })
42
+ emit('created', created.thread.id)
43
+ }
44
+ prompt.value = ''
45
+ } catch (error) {
46
+ present(error, 'guidedReview.errors.ask')
47
+ } finally {
48
+ sending.value = false
49
+ }
50
+ }
51
+
52
+ async function draftComments(): Promise<void> {
53
+ if (!props.view || !ready.value) return
54
+ sending.value = true
55
+ try {
56
+ await store.requestDrafts(props.sessionId, props.view.thread.id)
57
+ } catch (error) {
58
+ present(error, 'guidedReview.errors.drafts')
59
+ } finally {
60
+ sending.value = false
61
+ }
62
+ }
63
+ </script>
64
+
65
+ <template>
66
+ <div class="flex h-full min-h-0 flex-col gap-3" data-testid="guided-review-thread">
67
+ <div class="min-h-0 flex-1 space-y-3 overflow-y-auto pr-1">
68
+ <p v-if="!messages.length" class="text-sm text-muted">
69
+ {{ t('guidedReview.thread.empty') }}
70
+ </p>
71
+ <div
72
+ v-for="m in messages"
73
+ :key="m.id"
74
+ :class="m.role === 'user' ? 'ms-8 bg-elevated' : 'me-8 border border-default'"
75
+ class="rounded-lg p-3 text-sm"
76
+ :data-testid="`guided-review-message-${m.role}`"
77
+ >
78
+ <template v-if="m.role === 'user'">
79
+ <p v-if="m.kind === 'comment-drafts'" class="text-xs text-dimmed">
80
+ {{ t('guidedReview.thread.draftRequest') }}
81
+ </p>
82
+ <p class="whitespace-pre-wrap">{{ m.content }}</p>
83
+ </template>
84
+ <p v-else-if="isLive(m)" class="flex items-center gap-2 text-muted">
85
+ <UIcon name="i-lucide-loader-circle" class="h-4 w-4 animate-spin" />
86
+ {{ t('guidedReview.thread.waiting') }}
87
+ </p>
88
+ <GuidedReviewFailure v-else-if="m.status === 'failed' && m.failure" :failure="m.failure" />
89
+ <template v-else-if="m.kind === 'answer'">
90
+ <MarkdownProse :text="m.content" />
91
+ <div v-if="m.citations.length" class="mt-2 flex flex-wrap gap-1">
92
+ <UBadge
93
+ v-for="(c, i) in m.citations"
94
+ :key="i"
95
+ size="sm"
96
+ variant="outline"
97
+ color="neutral"
98
+ >
99
+ {{ citationLabel(c) }}
100
+ </UBadge>
101
+ </div>
102
+ </template>
103
+ <template v-else>
104
+ <p>
105
+ {{
106
+ t('guidedReview.drafts.summary', {
107
+ kept: keptDrafts(drafts, m.id),
108
+ proposed: m.draftReport?.proposed ?? 0,
109
+ })
110
+ }}
111
+ </p>
112
+ <ul v-if="m.draftReport?.dropped.length" class="mt-1 space-y-0.5 text-xs text-muted">
113
+ <li
114
+ v-for="(d, i) in m.draftReport.dropped"
115
+ :key="i"
116
+ data-testid="guided-review-dropped"
117
+ >
118
+ {{ droppedAnchor(d) }}:
119
+ {{ t(`guidedReview.dropped.${d.reason}`) }}
120
+ </li>
121
+ </ul>
122
+ </template>
123
+ </div>
124
+
125
+ <div v-if="drafts.length" class="space-y-2" data-testid="guided-review-drafts">
126
+ <h4 class="text-xs font-semibold uppercase text-dimmed">
127
+ {{ t('guidedReview.drafts.title') }}
128
+ </h4>
129
+ <div v-for="d in drafts" :key="d.id" class="rounded-md border border-default p-2 text-sm">
130
+ <p class="text-xs text-dimmed">
131
+ {{ draftAnchor(d) }}
132
+ ({{ t(`guidedReview.drafts.side.${d.side}`) }})
133
+ </p>
134
+ <MarkdownProse :text="d.body" />
135
+ <p v-if="d.rationale" class="mt-1 text-xs text-muted">{{ d.rationale }}</p>
136
+ </div>
137
+ </div>
138
+ </div>
139
+
140
+ <div class="space-y-2">
141
+ <UTextarea
142
+ v-model="prompt"
143
+ :rows="3"
144
+ autoresize
145
+ :maxrows="10"
146
+ class="w-full"
147
+ :disabled="!ready"
148
+ :placeholder="ready ? t('guidedReview.thread.placeholder') : t('guidedReview.thread.busy')"
149
+ data-testid="guided-review-composer"
150
+ @keydown.enter.meta.prevent="send"
151
+ @keydown.enter.ctrl.prevent="send"
152
+ />
153
+ <div class="flex flex-wrap items-center gap-2">
154
+ <UButton
155
+ color="primary"
156
+ icon="i-lucide-send"
157
+ :loading="sending"
158
+ :disabled="!canSend"
159
+ data-testid="guided-review-send"
160
+ @click="send"
161
+ >
162
+ {{ t('guidedReview.thread.send') }}
163
+ </UButton>
164
+ <UButton
165
+ v-if="view"
166
+ variant="soft"
167
+ icon="i-lucide-message-square-plus"
168
+ :disabled="!ready || !messages.length"
169
+ data-testid="guided-review-draft-comments"
170
+ @click="draftComments"
171
+ >
172
+ {{ t('guidedReview.drafts.request') }}
173
+ </UButton>
174
+ </div>
175
+ </div>
176
+ </div>
177
+ </template>
@@ -23,9 +23,13 @@ import {
23
23
  reviewQueueDirty,
24
24
  reviewSkillQueuePatch,
25
25
  } from '~/components/panels/inspector/TaskReviewTarget.logic'
26
+ import { reviewTaskTarget } from '~/components/guidedReview/GuidedReview.logic'
26
27
 
27
28
  const props = defineProps<{ block: Block }>()
28
29
  const board = useBoardStore()
30
+ const github = useGitHubStore()
31
+ const ui = useUiStore()
32
+ const access = useWorkspaceAccess()
29
33
  const { t } = useI18n()
30
34
 
31
35
  const isReview = computed(() => props.block.taskType === 'review')
@@ -33,6 +37,26 @@ const fields = computed(() => props.block.taskTypeFields ?? null)
33
37
  const url = computed(() => fields.value?.prUrl?.trim() || '')
34
38
  const focus = computed(() => fields.value?.reviewFocus?.trim() || '')
35
39
 
40
+ // Opening a guided review is a member-tier write, gated like the sidebar entry that opens one.
41
+ const canGuide = computed(() => isReview.value && access.canWriteBoard.value)
42
+
43
+ // Nothing loads the repository projection on board open, and the guided-review button below
44
+ // resolves the task's repository from it.
45
+ watch(
46
+ canGuide,
47
+ (can) => {
48
+ if (can) void github.ensureLoaded().catch(() => {})
49
+ },
50
+ { immediate: true },
51
+ )
52
+
53
+ /** The PR as a guided review opens it, when the task's service has a linked repository. */
54
+ const guidedTarget = computed(() => {
55
+ if (!canGuide.value) return null
56
+ const frame = board.serviceOf(props.block)
57
+ return reviewTaskTarget(fields.value, frame ? github.repoForBlock(frame.id) : undefined)
58
+ })
59
+
36
60
  /** `#123` when the number is known, else the raw link — never an empty affordance. */
37
61
  const label = computed(() => {
38
62
  const number = fields.value?.prNumber
@@ -107,6 +131,18 @@ function revert() {
107
131
  >
108
132
  {{ label }}
109
133
  </p>
134
+ <UButton
135
+ v-if="guidedTarget"
136
+ color="primary"
137
+ variant="soft"
138
+ size="sm"
139
+ icon="i-lucide-scan-search"
140
+ block
141
+ data-testid="inspector-review-target-guided"
142
+ @click="ui.openGuidedReview({ sessionId: null, target: guidedTarget })"
143
+ >
144
+ {{ t('guidedReview.openFromTask') }}
145
+ </UButton>
110
146
  <p v-if="focus" class="text-xs leading-relaxed text-dimmed">
111
147
  {{ t('inspector.reviewTarget.focus', { focus }) }}
112
148
  </p>
@@ -41,6 +41,7 @@ export function useNavContributions() {
41
41
  // Consumer items bypass this map entirely via their own `run` closure.
42
42
  const actions: Record<NavActionId, () => void> = {
43
43
  assistant: () => ui.openAssistant(),
44
+ guidedReview: () => ui.openGuidedReview(),
44
45
  buildPipeline: () => ui.openBuilder(),
45
46
  addFromRepo: () => ui.openAddService(),
46
47
  bootstrapRepo: () => ui.openBootstrap(),
@@ -197,6 +197,7 @@ export interface NavCommandSpec {
197
197
  */
198
198
  export const NAV_ACTIONS = [
199
199
  'assistant',
200
+ 'guidedReview',
200
201
  'buildPipeline',
201
202
  'addFromRepo',
202
203
  'bootstrapRepo',
@@ -395,6 +396,25 @@ export const NAV_CONTRIBUTIONS: readonly NavContribution[] = [
395
396
  keywordsKey: 'layout.commandBar.keywords.addFromRepo',
396
397
  },
397
398
  },
399
+ {
400
+ // Guided PR review. Basic tier, because reviewing pull requests is part of the everyday
401
+ // delivery loop; not `intake`, because the intake role brings work in rather than reviewing
402
+ // the code that delivers it.
403
+ id: 'guided-review',
404
+ labelKey: 'nav.guidedReview',
405
+ icon: 'i-lucide-scan-search',
406
+ surfaces: S('sidebar', 'command'),
407
+ gate: (g) => g.githubAvailable && g.canWriteBoard,
408
+ action: 'guidedReview',
409
+ testId: 'nav-guided-review',
410
+ sidebar: { group: 'repositories', order: 15 },
411
+ command: {
412
+ group: 'repositories',
413
+ order: 15,
414
+ labelKey: 'layout.commandBar.cmd.guidedReview',
415
+ keywordsKey: 'layout.commandBar.keywords.guidedReview',
416
+ },
417
+ },
398
418
  {
399
419
  id: 'bootstrap-repo',
400
420
  labelKey: 'nav.bootstrapRepo',
@@ -49,6 +49,9 @@ const TaskSourceConnectModal = defineAsyncView(
49
49
  const TaskImportModal = defineAsyncView(() => import('~/components/tasks/TaskImportModal.vue'))
50
50
  const BugHuntModal = defineAsyncView(() => import('~/components/tasks/BugHuntModal.vue'))
51
51
  const AssistantModal = defineAsyncView(() => import('~/components/assistant/AssistantModal.vue'))
52
+ const GuidedReviewModal = defineAsyncView(
53
+ () => import('~/components/guidedReview/GuidedReviewModal.vue'),
54
+ )
52
55
  const RecurringPipelineModal = defineAsyncView(
53
56
  () => import('~/components/board/RecurringPipelineModal.vue'),
54
57
  )
@@ -503,6 +506,7 @@ watch(
503
506
  <TaskImportModal v-if="ui.taskImport" />
504
507
  <BugHuntModal v-if="ui.bugHunt" />
505
508
  <AssistantModal v-if="ui.assistantOpen" />
509
+ <GuidedReviewModal v-if="ui.guidedReview" />
506
510
  <RecurringPipelineModal v-if="ui.addRecurringFrameId" />
507
511
  <ObservabilityPanel v-if="ui.observabilityInstanceId" />
508
512
  <OperatorDashboardPanel v-if="ui.operatorDashboardOpen" />
@@ -1,5 +1,10 @@
1
1
  import { ref } from 'vue'
2
- import type { DocumentSourceKind, InfraSetupArea, TaskSourceKind } from '~/types/domain'
2
+ import type {
3
+ DocumentSourceKind,
4
+ InfraSetupArea,
5
+ OpenGuidedReviewInput,
6
+ TaskSourceKind,
7
+ } from '~/types/domain'
3
8
  import type {
4
9
  InfrastructureScrollTarget,
5
10
  InfrastructureTab,
@@ -185,6 +190,12 @@ function createMiscModals() {
185
190
  return { builderOpen, decisionContext, openBuilder, openDecision, closeDecision }
186
191
  }
187
192
 
193
+ /** What the guided review window opens on: a session, a pull request, or neither (the picker). */
194
+ export interface GuidedReviewOpen {
195
+ sessionId: string | null
196
+ target: OpenGuidedReviewInput | null
197
+ }
198
+
188
199
  /**
189
200
  * Document- and task-source integration modals (keyed by source), plus the add-task /
190
201
  * add-recurring / create-initiative surfaces. The `open*` connect/import handlers reset the hub
@@ -220,6 +231,9 @@ function createDocumentTaskModals(resetHubReturn: ResetHubReturn) {
220
231
  // subject (a turn resolves every name it needs from the board itself), so a plain flag says
221
232
  // everything the host needs to know.
222
233
  const assistantOpen = ref(false)
234
+ // Guided PR review: either an existing session to show, or a PR to open one for. Both null
235
+ // opens the PR picker.
236
+ const guidedReview = ref<GuidedReviewOpen | null>(null)
223
237
 
224
238
  // Bug hunt: pick a tracker + one of its boards, rank its open unassigned bugs, adopt one.
225
239
  // `containerId` (a service frame or module) preselects where an adopted bug lands; null →
@@ -305,6 +319,13 @@ function createDocumentTaskModals(resetHubReturn: ResetHubReturn) {
305
319
  function closeAssistant() {
306
320
  assistantOpen.value = false
307
321
  }
322
+ function openGuidedReview(open: GuidedReviewOpen = { sessionId: null, target: null }) {
323
+ resetHubReturn()
324
+ guidedReview.value = open
325
+ }
326
+ function closeGuidedReview() {
327
+ guidedReview.value = null
328
+ }
308
329
  function openBugHunt(source: TaskSourceKind | null = null, containerId: string | null = null) {
309
330
  resetHubReturn()
310
331
  bugHunt.value = { source, containerId }
@@ -354,6 +375,7 @@ function createDocumentTaskModals(resetHubReturn: ResetHubReturn) {
354
375
  taskConnect,
355
376
  taskImport,
356
377
  assistantOpen,
378
+ guidedReview,
357
379
  bugHunt,
358
380
  startFromDesign,
359
381
  addTaskContainerId,
@@ -375,6 +397,8 @@ function createDocumentTaskModals(resetHubReturn: ResetHubReturn) {
375
397
  closeTaskImport,
376
398
  openAssistant,
377
399
  closeAssistant,
400
+ openGuidedReview,
401
+ closeGuidedReview,
378
402
  openBugHunt,
379
403
  closeBugHunt,
380
404
  openStartFromDesign,
@@ -4,6 +4,8 @@ export type {
4
4
  AskGuidedReviewInput,
5
5
  GuidedReviewChange,
6
6
  GuidedReviewCommentDraft,
7
+ GuidedReviewDraftReport,
8
+ GuidedReviewFailure,
7
9
  GuidedReviewExchange,
8
10
  GuidedReviewMessage,
9
11
  GuidedReviewSession,
@@ -2740,6 +2740,7 @@
2740
2740
  "cmd": {
2741
2741
  "newPipeline": "Eine Pipeline bauen",
2742
2742
  "assistant": "Assistenten öffnen",
2743
+ "guidedReview": "Einen Pull Request erkunden",
2743
2744
  "addFromRepo": "Service aus vorhandenem Repo hinzufügen",
2744
2745
  "bootstrapRepo": "Ein neues Repo bootstrappen",
2745
2746
  "githubManage": "GitHub-Verbindung verwalten",
@@ -2768,6 +2769,7 @@
2768
2769
  "keywords": {
2769
2770
  "newPipeline": "pipeline agents chain",
2770
2771
  "assistant": "ki assistent fragen eingabe für mich erledigen",
2772
+ "guidedReview": "geführtes review pull request pr erkunden erklären fragen",
2771
2773
  "addFromRepo": "github import existing",
2772
2774
  "bootstrapRepo": "scaffold create reference architecture",
2773
2775
  "github": "git repos pull requests issues",
@@ -4591,6 +4593,81 @@
4591
4593
  "unresolved_issue_service": "Der Assistent konnte nicht erkennen, unter welchem Service das angelegt werden soll. Nennen Sie den Service in Ihrer Anfrage."
4592
4594
  }
4593
4595
  },
4596
+ "guidedReview": {
4597
+ "title": "Geführtes Review",
4598
+ "titleFor": "{repo} #{number}: {title}",
4599
+ "loading": "Review wird geladen…",
4600
+ "outOfDate": "Live-Updates sind fehlgeschlagen, daher ist diese Ansicht möglicherweise veraltet.",
4601
+ "openFromTask": "Mit geführtem Review erkunden",
4602
+ "picker": {
4603
+ "intro": "Wähle ein Repository und einen Pull Request. Du erhältst einen Überblick über die Änderung und kannst in separaten Threads Fragen dazu stellen.",
4604
+ "loading": "Repositories werden geladen…",
4605
+ "noRepos": "Mit diesem Workspace sind noch keine Repositories verknüpft.",
4606
+ "repo": "Repository",
4607
+ "openPulls": "Offene Pull Requests",
4608
+ "number": "Nummer des Pull Requests",
4609
+ "open": "Review öffnen"
4610
+ },
4611
+ "overview": {
4612
+ "title": "Überblick",
4613
+ "stale": "Der Pull Request hat neue Commits",
4614
+ "refresh": "Aktualisieren",
4615
+ "working": "Überblick wird erstellt…",
4616
+ "intent": "Absicht",
4617
+ "changes": "Wesentliche Änderungen",
4618
+ "consequences": "Folgen",
4619
+ "risks": "Risiken",
4620
+ "focus": "Lohnt einen Blick",
4621
+ "suggested": "Vorgeschlagene Fragen"
4622
+ },
4623
+ "severity": {
4624
+ "low": "Niedrig",
4625
+ "medium": "Mittel",
4626
+ "high": "Hoch"
4627
+ },
4628
+ "failure": {
4629
+ "budget_exhausted": "Dieser Workspace hat sein Modellbudget aufgebraucht.",
4630
+ "model_unavailable": "Für das geführte Review ist kein Modell verfügbar.",
4631
+ "repo_unavailable": "Der Pull Request konnte nicht aus dem Repository gelesen werden.",
4632
+ "generation_failed": "Der Modellaufruf ist fehlgeschlagen.",
4633
+ "unreadable_reply": "Das Modell hat in einer nicht lesbaren Form geantwortet.",
4634
+ "depth_unavailable": "Tiefe Antworten mit einem Checkout sind noch nicht verfügbar.",
4635
+ "head_moved": "Der Pull Request ist über den geprüften Commit hinaus. Aktualisiere, um den neuesten Commit zu prüfen.",
4636
+ "detail": "Details"
4637
+ },
4638
+ "dropped": {
4639
+ "outside_diff": "die Zeile liegt außerhalb des Diffs",
4640
+ "not_in_pr": "die Datei gehört nicht zu diesem Pull Request",
4641
+ "incomplete": "der Vorschlag war unvollständig"
4642
+ },
4643
+ "thread": {
4644
+ "new": "Neuer Thread",
4645
+ "untitled": "Thread ohne Titel",
4646
+ "empty": "Frag alles zu diesem Pull Request.",
4647
+ "waiting": "Antwort wird erstellt…",
4648
+ "placeholder": "Stell eine Frage zur Änderung",
4649
+ "busy": "Warte auf die Antwort auf die vorherige Frage",
4650
+ "send": "Fragen",
4651
+ "draftRequest": "Review-Kommentare angefordert"
4652
+ },
4653
+ "drafts": {
4654
+ "title": "Kommentarentwürfe",
4655
+ "request": "Review-Kommentare entwerfen",
4656
+ "summary": "{kept} von {proposed} vorgeschlagenen Kommentaren als Entwurf behalten.",
4657
+ "side": {
4658
+ "RIGHT": "neuer Code",
4659
+ "LEFT": "alter Code"
4660
+ }
4661
+ },
4662
+ "errors": {
4663
+ "load": "Das Review konnte nicht geladen werden",
4664
+ "repos": "Die verknüpften Repositories konnten nicht geladen werden",
4665
+ "open": "Das Review konnte nicht geöffnet werden",
4666
+ "ask": "Die Frage konnte nicht gestellt werden",
4667
+ "drafts": "Kommentarentwürfe konnten nicht angefordert werden",
4668
+ "refresh": "Das Review konnte nicht aktualisiert werden"
4669
+ }
4670
+ },
4594
4671
  "pipeline": {
4595
4672
  "iterationCap": {
4596
4673
  "extraRound": "Noch eine Runde",
@@ -6173,6 +6250,7 @@
6173
6250
  "commandBar": "Suchen oder einen Befehl ausführen…",
6174
6251
  "create": "Erstellen",
6175
6252
  "assistant": "Assistent",
6253
+ "guidedReview": "Geführtes Review",
6176
6254
  "buildPipeline": "Eine Pipeline erstellen",
6177
6255
  "repositories": "Repositories",
6178
6256
  "addFromRepo": "Aus vorhandenem Repo hinzufügen",
@@ -153,6 +153,7 @@
153
153
  "commandBar": "Search or run a command…",
154
154
  "create": "Create",
155
155
  "assistant": "Assistant",
156
+ "guidedReview": "Guided review",
156
157
  "buildPipeline": "Build a pipeline",
157
158
  "repositories": "Repositories",
158
159
  "addFromRepo": "Add from existing repo",
@@ -2760,6 +2761,7 @@
2760
2761
  "cmd": {
2761
2762
  "newPipeline": "Build a pipeline",
2762
2763
  "assistant": "Open the assistant",
2764
+ "guidedReview": "Explore a pull request",
2763
2765
  "addFromRepo": "Add service from existing repo",
2764
2766
  "bootstrapRepo": "Bootstrap a new repo",
2765
2767
  "githubManage": "Manage GitHub connection",
@@ -2788,6 +2790,7 @@
2788
2790
  "keywords": {
2789
2791
  "newPipeline": "pipeline agents chain",
2790
2792
  "assistant": "ai assistant ask prompt do it for me",
2793
+ "guidedReview": "guided review pull request pr explore explain questions",
2791
2794
  "addFromRepo": "github import existing",
2792
2795
  "bootstrapRepo": "scaffold create reference architecture",
2793
2796
  "github": "git repos pull requests issues",
@@ -5308,6 +5311,81 @@
5308
5311
  "unresolved_issue_service": "The assistant could not tell which service to file this under. Name the service in your request."
5309
5312
  }
5310
5313
  },
5314
+ "guidedReview": {
5315
+ "title": "Guided review",
5316
+ "titleFor": "{repo} #{number}: {title}",
5317
+ "loading": "Loading the review…",
5318
+ "outOfDate": "Live updates failed, so this view may be out of date.",
5319
+ "openFromTask": "Explore with guided review",
5320
+ "picker": {
5321
+ "intro": "Pick a repository and a pull request. You get an overview of the change and can ask questions about it in separate threads.",
5322
+ "loading": "Loading repositories…",
5323
+ "noRepos": "No repositories are linked to this workspace yet.",
5324
+ "repo": "Repository",
5325
+ "openPulls": "Open pull requests",
5326
+ "number": "Pull request number",
5327
+ "open": "Open review"
5328
+ },
5329
+ "overview": {
5330
+ "title": "Overview",
5331
+ "stale": "The pull request has new commits",
5332
+ "refresh": "Refresh",
5333
+ "working": "Preparing the overview…",
5334
+ "intent": "Intent",
5335
+ "changes": "Meaningful changes",
5336
+ "consequences": "Consequences",
5337
+ "risks": "Risks",
5338
+ "focus": "Worth reviewing",
5339
+ "suggested": "Suggested questions"
5340
+ },
5341
+ "severity": {
5342
+ "low": "Low",
5343
+ "medium": "Medium",
5344
+ "high": "High"
5345
+ },
5346
+ "failure": {
5347
+ "budget_exhausted": "This workspace has spent its model budget.",
5348
+ "model_unavailable": "No model is available for guided review.",
5349
+ "repo_unavailable": "The pull request could not be read from the repository.",
5350
+ "generation_failed": "The model call failed.",
5351
+ "unreadable_reply": "The model replied in a form that could not be read.",
5352
+ "depth_unavailable": "Deep answers that use a checkout are not available yet.",
5353
+ "head_moved": "The pull request moved past the reviewed commit. Refresh to review its latest commit.",
5354
+ "detail": "Details"
5355
+ },
5356
+ "dropped": {
5357
+ "outside_diff": "the line is outside the diff",
5358
+ "not_in_pr": "the file is not part of this pull request",
5359
+ "incomplete": "the proposal was incomplete"
5360
+ },
5361
+ "thread": {
5362
+ "new": "New thread",
5363
+ "untitled": "Untitled thread",
5364
+ "empty": "Ask anything about this pull request.",
5365
+ "waiting": "Working on an answer…",
5366
+ "placeholder": "Ask a question about the change",
5367
+ "busy": "Waiting for the answer to the previous question",
5368
+ "send": "Ask",
5369
+ "draftRequest": "Asked for review comments"
5370
+ },
5371
+ "drafts": {
5372
+ "title": "Comment drafts",
5373
+ "request": "Draft review comments",
5374
+ "summary": "{kept} of {proposed} proposed comments kept as drafts.",
5375
+ "side": {
5376
+ "RIGHT": "new code",
5377
+ "LEFT": "old code"
5378
+ }
5379
+ },
5380
+ "errors": {
5381
+ "load": "Could not load the review",
5382
+ "repos": "Could not load the linked repositories",
5383
+ "open": "Could not open the review",
5384
+ "ask": "Could not ask the question",
5385
+ "drafts": "Could not request comment drafts",
5386
+ "refresh": "Could not refresh the review"
5387
+ }
5388
+ },
5311
5389
  "pipeline": {
5312
5390
  "iterationCap": {
5313
5391
  "extraRound": "One more round",