@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.
- package/app/components/guidedReview/GuidedReview.logic.spec.ts +123 -0
- package/app/components/guidedReview/GuidedReview.logic.ts +108 -0
- package/app/components/guidedReview/GuidedReviewFailure.vue +37 -0
- package/app/components/guidedReview/GuidedReviewModal.vue +243 -0
- package/app/components/guidedReview/GuidedReviewOverview.vue +160 -0
- package/app/components/guidedReview/GuidedReviewPicker.vue +95 -0
- package/app/components/guidedReview/GuidedReviewThread.vue +177 -0
- package/app/components/panels/inspector/TaskReviewTarget.vue +36 -0
- package/app/composables/useNavContributions.ts +1 -0
- package/app/modular/nav-contributions.ts +20 -0
- package/app/pages/index.vue +4 -0
- package/app/stores/ui/modals.ts +25 -1
- package/app/types/guided-review.ts +2 -0
- package/i18n/locales/de.json +78 -0
- package/i18n/locales/en.json +78 -0
- package/i18n/locales/es.json +78 -0
- package/i18n/locales/fr.json +78 -0
- package/i18n/locales/he.json +78 -0
- package/i18n/locales/it.json +78 -0
- package/i18n/locales/ja.json +78 -0
- package/i18n/locales/pl.json +78 -0
- package/i18n/locales/tr.json +78 -0
- package/i18n/locales/uk.json +78 -0
- package/package.json +2 -2
|
@@ -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',
|
package/app/pages/index.vue
CHANGED
|
@@ -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" />
|
package/app/stores/ui/modals.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { ref } from 'vue'
|
|
2
|
-
import type {
|
|
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,
|
package/i18n/locales/de.json
CHANGED
|
@@ -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",
|
package/i18n/locales/en.json
CHANGED
|
@@ -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",
|