@cat-factory/app 0.138.1 → 0.140.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/board/AddTaskModal.vue +12 -39
- package/app/components/brainstorm/BrainstormWindow.vue +338 -374
- package/app/components/clarity/ClarityReviewWindow.vue +323 -353
- package/app/components/consensus/ConsensusSessionWindow.vue +138 -150
- package/app/components/docs/DocInterviewWindow.vue +108 -136
- package/app/components/documents/ContextDocumentPicker.vue +89 -70
- package/app/components/documents/RepoContextDocPicker.vue +277 -0
- package/app/components/followUp/FollowUpWindow.vue +3 -5
- package/app/components/forkDecision/ForkDecisionWindow.vue +1 -3
- package/app/components/gates/GateResultView.vue +3 -5
- package/app/components/github/RepoTreeBrowser.vue +19 -10
- package/app/components/humanTest/HumanTestWindow.vue +3 -5
- package/app/components/initiative/InitiativePlanningWindow.vue +89 -112
- package/app/components/initiative/InitiativeTrackerWindow.vue +389 -424
- package/app/components/panels/GenericStructuredResultView.vue +3 -5
- package/app/components/panels/MergerResultView.vue +3 -5
- package/app/components/panels/ResultWindowShell.vue +1 -1
- package/app/components/panels/inspector/TaskRunSettings.vue +16 -23
- package/app/components/pipeline/PipelineBuilder.vue +10 -0
- package/app/components/pipeline/PipelinePicker.vue +120 -0
- package/app/components/pipeline/PipelinePreview.vue +49 -0
- package/app/components/prReview/PrReviewWindow.vue +181 -205
- package/app/components/ralph/RalphLoopResultView.vue +3 -5
- package/app/components/requirements/RequirementsReviewWindow.vue +512 -557
- package/app/components/spec/ServiceSpecWindow.vue +235 -266
- package/app/components/testing/TestReportWindow.vue +4 -6
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +3 -5
- package/app/composables/api/github.ts +8 -0
- package/app/composables/useContextLinking.spec.ts +138 -0
- package/app/composables/useContextLinking.ts +120 -8
- package/app/composables/useCopyToClipboard.spec.ts +27 -0
- package/app/composables/useCopyToClipboard.ts +17 -1
- package/app/composables/useResultView.ts +10 -21
- package/app/stores/github.ts +24 -0
- package/app/stores/pipelines.ts +9 -0
- package/app/utils/pipeline.ts +25 -1
- package/i18n/locales/de.json +20 -0
- package/i18n/locales/en.json +20 -0
- package/i18n/locales/es.json +20 -0
- package/i18n/locales/fr.json +20 -0
- package/i18n/locales/he.json +20 -0
- package/i18n/locales/it.json +20 -0
- package/i18n/locales/ja.json +20 -0
- package/i18n/locales/pl.json +20 -0
- package/i18n/locales/tr.json +20 -0
- package/i18n/locales/uk.json +20 -0
- package/package.json +2 -2
|
@@ -18,11 +18,9 @@ const agents = useAgentsStore()
|
|
|
18
18
|
const { t } = useI18n()
|
|
19
19
|
|
|
20
20
|
// Shared seam contract (open/blockId/close). No `onOpen` loader: this window reads its data
|
|
21
|
-
// straight off the execution step, so there's nothing to fetch on open. `
|
|
22
|
-
//
|
|
23
|
-
const { open, blockId, instanceId, stepIndex, close } = useResultView('generic-structured'
|
|
24
|
-
manageEscape: false,
|
|
25
|
-
})
|
|
21
|
+
// straight off the execution step, so there's nothing to fetch on open. `ResultWindowShell`
|
|
22
|
+
// owns Escape (and focus trap + scroll lock + stacking).
|
|
23
|
+
const { open, blockId, instanceId, stepIndex, close } = useResultView('generic-structured')
|
|
26
24
|
const block = computed(() => (blockId.value ? board.getBlock(blockId.value) : undefined))
|
|
27
25
|
|
|
28
26
|
const instance = computed(() =>
|
|
@@ -18,11 +18,9 @@ const agents = useAgentsStore()
|
|
|
18
18
|
const { t, n } = useI18n()
|
|
19
19
|
|
|
20
20
|
// Shared seam contract (open/blockId/close). No loader: the verdict is read straight off
|
|
21
|
-
// the execution step. `
|
|
22
|
-
//
|
|
23
|
-
const { open, blockId, instanceId, stepIndex, close } = useResultView('merger'
|
|
24
|
-
manageEscape: false,
|
|
25
|
-
})
|
|
21
|
+
// the execution step. `ResultWindowShell` owns Escape (and focus trap + scroll lock +
|
|
22
|
+
// stacking) via the shared overlay behaviour.
|
|
23
|
+
const { open, blockId, instanceId, stepIndex, close } = useResultView('merger')
|
|
26
24
|
const block = computed(() => (blockId.value ? board.getBlock(blockId.value) : undefined))
|
|
27
25
|
|
|
28
26
|
const instance = computed(() =>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// `useModalBehavior` (`@modular-vue/core`, the slice-5 overlay-host release): focus-trap
|
|
13
13
|
// + focus-return, body-scroll lock, and a shared overlay STACK so the top overlay closes
|
|
14
14
|
// first on Escape. A window becomes body-only markup wrapped in `<ResultWindowShell>`; it
|
|
15
|
-
// keeps its `useResultView` seam
|
|
15
|
+
// keeps its `useResultView` seam (Escape lives in this shell now, not in `useResultView`).
|
|
16
16
|
//
|
|
17
17
|
// The pick-one SELECTION of which window is active stays exactly the slice-2
|
|
18
18
|
// `resolveComponentRegistry` in `StepResultViewHost.vue` — this shell only owns the
|
|
@@ -142,20 +142,6 @@ const selectablePipelines = computed(() =>
|
|
|
142
142
|
pipelineAllowedForManualStart(p, taskFrame.value, board.blocks),
|
|
143
143
|
),
|
|
144
144
|
)
|
|
145
|
-
const pipelineMenu = computed(() => [
|
|
146
|
-
[
|
|
147
|
-
{
|
|
148
|
-
label: t('inspector.runSettings.noDefault'),
|
|
149
|
-
icon: 'i-lucide-rotate-ccw',
|
|
150
|
-
onSelect: () => setPipeline(''),
|
|
151
|
-
},
|
|
152
|
-
...selectablePipelines.value.map((p) => ({
|
|
153
|
-
label: p.name,
|
|
154
|
-
icon: 'i-lucide-workflow',
|
|
155
|
-
onSelect: () => setPipeline(p.id),
|
|
156
|
-
})),
|
|
157
|
-
],
|
|
158
|
-
])
|
|
159
145
|
function setPipeline(id: string) {
|
|
160
146
|
board.updateBlock(props.block.id, { pipelineId: id })
|
|
161
147
|
}
|
|
@@ -271,15 +257,22 @@ const technicalLabel = computed(() => {
|
|
|
271
257
|
<span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
272
258
|
{{ t('inspector.runSettings.pipeline') }}
|
|
273
259
|
</span>
|
|
274
|
-
<
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
260
|
+
<PipelinePicker
|
|
261
|
+
:model-value="block.pipelineId ?? ''"
|
|
262
|
+
:options="selectablePipelines"
|
|
263
|
+
:none-label="t('inspector.runSettings.noDefault')"
|
|
264
|
+
@update:model-value="setPipeline"
|
|
265
|
+
>
|
|
266
|
+
<template #trigger>
|
|
267
|
+
<UButton
|
|
268
|
+
size="xs"
|
|
269
|
+
variant="ghost"
|
|
270
|
+
color="neutral"
|
|
271
|
+
icon="i-lucide-workflow"
|
|
272
|
+
trailing-icon="i-lucide-chevron-down"
|
|
273
|
+
/>
|
|
274
|
+
</template>
|
|
275
|
+
</PipelinePicker>
|
|
283
276
|
</div>
|
|
284
277
|
<div v-if="selectedPipeline" class="flex items-center gap-1">
|
|
285
278
|
<UBadge
|
|
@@ -310,6 +310,16 @@ async function clone(p: Pipeline) {
|
|
|
310
310
|
class="mb-2"
|
|
311
311
|
/>
|
|
312
312
|
|
|
313
|
+
<!-- Description: the prose summary shown next to the step list in the pipeline pickers. -->
|
|
314
|
+
<UTextarea
|
|
315
|
+
v-model="pipelines.draftDescription"
|
|
316
|
+
:placeholder="t('pipeline.builder.descriptionPlaceholder')"
|
|
317
|
+
:rows="2"
|
|
318
|
+
autoresize
|
|
319
|
+
size="sm"
|
|
320
|
+
class="mb-2 w-full"
|
|
321
|
+
/>
|
|
322
|
+
|
|
313
323
|
<!-- Labels: organize the pipeline in the library (filter/search). -->
|
|
314
324
|
<div class="mb-3 flex flex-wrap items-center gap-1.5">
|
|
315
325
|
<UBadge
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// The rich pipeline picker used wherever a pipeline is chosen (add-task modal, inspector run
|
|
3
|
+
// settings). A master–detail popover: the left column lists the selectable pipelines (plus a
|
|
4
|
+
// "none / choose at run time" row), and hovering a row reveals that pipeline's full preview — its
|
|
5
|
+
// description + the ordered agent steps — in the right column, so a user sees exactly what a
|
|
6
|
+
// pipeline does before picking it. The trigger is customizable via the `#trigger` slot (the
|
|
7
|
+
// inspector uses a bare icon button; the modal a full-width labelled one).
|
|
8
|
+
import { computed, ref } from 'vue'
|
|
9
|
+
import type { Pipeline } from '~/types/domain'
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(
|
|
12
|
+
defineProps<{
|
|
13
|
+
/** Selected pipeline id, or '' for the "none" option. */
|
|
14
|
+
modelValue: string
|
|
15
|
+
/** The pipelines offered (already filtered for the surface, e.g. manual-start allowed). */
|
|
16
|
+
options: Pipeline[]
|
|
17
|
+
/** Label for the "none" row (e.g. "Choose at run time" / "No default"). */
|
|
18
|
+
noneLabel: string
|
|
19
|
+
/** Extra classes for the default trigger button (e.g. full-width in the modal). */
|
|
20
|
+
triggerClass?: string
|
|
21
|
+
}>(),
|
|
22
|
+
{ triggerClass: '' },
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
const emit = defineEmits<{ 'update:modelValue': [string] }>()
|
|
26
|
+
const { t } = useI18n()
|
|
27
|
+
|
|
28
|
+
const open = ref(false)
|
|
29
|
+
// The row currently hovered, driving the right-column preview. `undefined` ⇒ fall back to the
|
|
30
|
+
// selected pipeline; the sentinel '' means the "none" row is hovered (show the none hint).
|
|
31
|
+
const hoverId = ref<string | undefined>(undefined)
|
|
32
|
+
|
|
33
|
+
const selected = computed(() => props.options.find((p) => p.id === props.modelValue))
|
|
34
|
+
const triggerLabel = computed(() => selected.value?.name ?? props.noneLabel)
|
|
35
|
+
|
|
36
|
+
/** The pipeline the right pane previews: the hovered row, else the current selection. */
|
|
37
|
+
const previewPipeline = computed<Pipeline | null>(() => {
|
|
38
|
+
const id = hoverId.value ?? props.modelValue
|
|
39
|
+
return id ? (props.options.find((p) => p.id === id) ?? null) : null
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
function choose(id: string) {
|
|
43
|
+
emit('update:modelValue', id)
|
|
44
|
+
open.value = false
|
|
45
|
+
}
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<template>
|
|
49
|
+
<UPopover v-model:open="open" :content="{ align: 'start' }">
|
|
50
|
+
<slot name="trigger" :label="triggerLabel">
|
|
51
|
+
<UButton
|
|
52
|
+
color="neutral"
|
|
53
|
+
variant="subtle"
|
|
54
|
+
size="sm"
|
|
55
|
+
icon="i-lucide-workflow"
|
|
56
|
+
trailing-icon="i-lucide-chevron-down"
|
|
57
|
+
:class="triggerClass"
|
|
58
|
+
data-testid="pipeline-picker-trigger"
|
|
59
|
+
>
|
|
60
|
+
{{ triggerLabel }}
|
|
61
|
+
</UButton>
|
|
62
|
+
</slot>
|
|
63
|
+
|
|
64
|
+
<template #content>
|
|
65
|
+
<div
|
|
66
|
+
class="flex max-h-[24rem] w-[min(44rem,94vw)]"
|
|
67
|
+
data-testid="pipeline-picker-panel"
|
|
68
|
+
@mouseleave="hoverId = undefined"
|
|
69
|
+
>
|
|
70
|
+
<!-- left: selectable options -->
|
|
71
|
+
<ul class="w-1/2 shrink-0 overflow-y-auto border-e border-slate-800 p-1">
|
|
72
|
+
<li>
|
|
73
|
+
<button
|
|
74
|
+
type="button"
|
|
75
|
+
class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-start text-sm hover:bg-slate-800/60"
|
|
76
|
+
:class="modelValue ? 'text-slate-300' : 'text-slate-100'"
|
|
77
|
+
data-testid="pipeline-option-none"
|
|
78
|
+
@mouseenter="hoverId = ''"
|
|
79
|
+
@click="choose('')"
|
|
80
|
+
>
|
|
81
|
+
<UIcon name="i-lucide-rotate-ccw" class="h-4 w-4 shrink-0 text-slate-400" />
|
|
82
|
+
<span class="flex-1 truncate">{{ noneLabel }}</span>
|
|
83
|
+
<UIcon
|
|
84
|
+
v-if="!modelValue"
|
|
85
|
+
name="i-lucide-check"
|
|
86
|
+
class="h-4 w-4 shrink-0 text-primary-400"
|
|
87
|
+
/>
|
|
88
|
+
</button>
|
|
89
|
+
</li>
|
|
90
|
+
<li v-for="p in options" :key="p.id">
|
|
91
|
+
<button
|
|
92
|
+
type="button"
|
|
93
|
+
class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-start text-sm hover:bg-slate-800/60"
|
|
94
|
+
:class="modelValue === p.id ? 'text-slate-100' : 'text-slate-300'"
|
|
95
|
+
:data-testid="`pipeline-option-${p.id}`"
|
|
96
|
+
@mouseenter="hoverId = p.id"
|
|
97
|
+
@click="choose(p.id)"
|
|
98
|
+
>
|
|
99
|
+
<UIcon name="i-lucide-workflow" class="h-4 w-4 shrink-0 text-slate-400" />
|
|
100
|
+
<span class="flex-1 truncate">{{ p.name }}</span>
|
|
101
|
+
<UIcon
|
|
102
|
+
v-if="modelValue === p.id"
|
|
103
|
+
name="i-lucide-check"
|
|
104
|
+
class="h-4 w-4 shrink-0 text-primary-400"
|
|
105
|
+
/>
|
|
106
|
+
</button>
|
|
107
|
+
</li>
|
|
108
|
+
</ul>
|
|
109
|
+
|
|
110
|
+
<!-- right: preview of the hovered (or selected) pipeline -->
|
|
111
|
+
<div class="w-1/2 overflow-y-auto p-3">
|
|
112
|
+
<PipelinePreview v-if="previewPipeline" :pipeline="previewPipeline" />
|
|
113
|
+
<div v-else class="text-[12px] leading-snug text-slate-500">
|
|
114
|
+
{{ t('pipeline.picker.noneHint') }}
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</template>
|
|
119
|
+
</UPopover>
|
|
120
|
+
</template>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// A compact, read-only summary of a pipeline: its name, prose description (when authored), and the
|
|
3
|
+
// ordered list of enabled agent steps rendered as icon+label chips (a human-gated step is flagged).
|
|
4
|
+
// Shared by the pipeline pickers' hover preview so "what a pipeline consists of" is explained the
|
|
5
|
+
// same way everywhere. Resolves each step's display metadata through the single `agentKindMeta`
|
|
6
|
+
// path (via <AgentKindIcon>), so a system/custom kind can never blow up the renderer.
|
|
7
|
+
import { computed } from 'vue'
|
|
8
|
+
import type { Pipeline } from '~/types/domain'
|
|
9
|
+
import { pipelineDisplaySteps } from '~/utils/pipeline'
|
|
10
|
+
|
|
11
|
+
const props = defineProps<{ pipeline: Pipeline }>()
|
|
12
|
+
const { t } = useI18n()
|
|
13
|
+
|
|
14
|
+
const steps = computed(() => pipelineDisplaySteps(props.pipeline))
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<div class="space-y-2" data-testid="pipeline-preview">
|
|
19
|
+
<div class="text-sm font-semibold text-slate-100">{{ pipeline.name }}</div>
|
|
20
|
+
<p
|
|
21
|
+
v-if="pipeline.description"
|
|
22
|
+
class="text-[12px] leading-snug text-slate-400"
|
|
23
|
+
data-testid="pipeline-preview-description"
|
|
24
|
+
>
|
|
25
|
+
{{ pipeline.description }}
|
|
26
|
+
</p>
|
|
27
|
+
<div>
|
|
28
|
+
<div class="mb-1 flex items-center gap-1 text-[10px] uppercase tracking-wide text-slate-500">
|
|
29
|
+
<UIcon name="i-lucide-workflow" class="h-3 w-3" />
|
|
30
|
+
{{ t('pipeline.preview.stepCount', { count: steps.length }, steps.length) }}
|
|
31
|
+
</div>
|
|
32
|
+
<ol class="flex flex-wrap items-center gap-1">
|
|
33
|
+
<li
|
|
34
|
+
v-for="(s, i) in steps"
|
|
35
|
+
:key="i"
|
|
36
|
+
class="inline-flex items-center gap-1 rounded bg-slate-800/70 px-1.5 py-0.5"
|
|
37
|
+
>
|
|
38
|
+
<AgentKindIcon :kind="s.kind" show-label icon-class="h-3.5 w-3.5" />
|
|
39
|
+
<UIcon
|
|
40
|
+
v-if="s.gated"
|
|
41
|
+
name="i-lucide-shield-check"
|
|
42
|
+
class="h-3 w-3 text-amber-400"
|
|
43
|
+
:title="t('pipeline.preview.gated')"
|
|
44
|
+
/>
|
|
45
|
+
</li>
|
|
46
|
+
</ol>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</template>
|