@cat-factory/app 0.46.8 → 0.46.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/app/components/board/AddTaskModal.vue +106 -88
- package/app/components/board/AgentFailureCard.vue +8 -5
- package/app/components/board/AgentStopButton.vue +8 -5
- package/app/components/board/BoardCanvas.vue +13 -9
- package/app/components/board/RecurringPipelineModal.vue +33 -23
- package/app/components/board/nodes/BlockNode.vue +50 -34
- package/app/components/board/nodes/DecisionBadge.vue +6 -3
- package/app/components/board/nodes/DraggableTask.vue +2 -1
- package/app/components/board/nodes/EpicNode.vue +10 -3
- package/app/components/board/nodes/ModuleFrame.vue +6 -5
- package/app/components/board/nodes/TaskCard.vue +40 -22
- package/app/components/board/nodes/TaskPipelineMini.vue +4 -3
- package/app/components/layout/BoardToolbar.vue +26 -19
- package/i18n/locales/en.json +204 -0
- package/i18n/locales/es.json +194 -2
- package/i18n/locales/fr.json +194 -2
- package/i18n/locales/pl.json +194 -2
- package/i18n/locales/uk.json +194 -2
- package/package.json +8 -8
|
@@ -13,6 +13,7 @@ const ui = useUiStore()
|
|
|
13
13
|
const agentRuns = useAgentRunsStore()
|
|
14
14
|
const reviews = useReviewStage()
|
|
15
15
|
const toast = useToast()
|
|
16
|
+
const { t } = useI18n()
|
|
16
17
|
|
|
17
18
|
const task = computed<Block | undefined>(() => board.getBlock(props.taskId))
|
|
18
19
|
const statusMeta = computed(() => (task.value ? STATUS_META[task.value.status] : null))
|
|
@@ -43,7 +44,9 @@ const defaultPipeline = computed(
|
|
|
43
44
|
|
|
44
45
|
/** The PR the implementer agent opened for this task, if any. */
|
|
45
46
|
const pr = computed(() => task.value?.pullRequest)
|
|
46
|
-
const prLabel = computed(() =>
|
|
47
|
+
const prLabel = computed(() =>
|
|
48
|
+
pr.value?.number ? t('board.task.prNumber', { number: pr.value.number }) : t('board.task.pr'),
|
|
49
|
+
)
|
|
47
50
|
|
|
48
51
|
// This task's current agent run (if any). A failed run must surface the shared
|
|
49
52
|
// failure banner + retry — NOT a stuck progress bar — so the card never looks
|
|
@@ -65,15 +68,18 @@ const starting = ref(false)
|
|
|
65
68
|
async function run() {
|
|
66
69
|
if (!runnable.value) {
|
|
67
70
|
toast.add({
|
|
68
|
-
title: '
|
|
69
|
-
description:
|
|
71
|
+
title: t('board.task.blockedByDependenciesTitle'),
|
|
72
|
+
description: t('board.task.waitingOn', { deps: unmet.value.map((d) => d.title).join(', ') }),
|
|
70
73
|
icon: 'i-lucide-lock',
|
|
71
74
|
})
|
|
72
75
|
return
|
|
73
76
|
}
|
|
74
77
|
const pipeline = defaultPipeline.value
|
|
75
78
|
if (!pipeline) {
|
|
76
|
-
toast.add({
|
|
79
|
+
toast.add({
|
|
80
|
+
title: t('board.task.noPipelineTitle'),
|
|
81
|
+
description: t('board.task.noPipelineBody'),
|
|
82
|
+
})
|
|
77
83
|
return
|
|
78
84
|
}
|
|
79
85
|
starting.value = true
|
|
@@ -108,11 +114,11 @@ const pendingDecision = computed(() =>
|
|
|
108
114
|
const reviewStage = computed(() => reviews.stageForBlock(props.taskId))
|
|
109
115
|
const reviewStageLabel = computed(() =>
|
|
110
116
|
reviewStage.value === 'incorporating'
|
|
111
|
-
? '
|
|
117
|
+
? t('board.task.incorporatingAnswers')
|
|
112
118
|
: reviewStage.value === 'reviewing'
|
|
113
|
-
? '
|
|
119
|
+
? t('board.task.reReviewing')
|
|
114
120
|
: reviewStage.value === 'recommending'
|
|
115
|
-
? '
|
|
121
|
+
? t('board.task.recommending')
|
|
116
122
|
: null,
|
|
117
123
|
)
|
|
118
124
|
const pendingApproval = computed(() => {
|
|
@@ -135,17 +141,17 @@ const attention = computed<{
|
|
|
135
141
|
const d = pendingDecision.value
|
|
136
142
|
if (d)
|
|
137
143
|
return {
|
|
138
|
-
label: '
|
|
144
|
+
label: t('board.task.decisionNeeded'),
|
|
139
145
|
icon: 'i-lucide-circle-help',
|
|
140
|
-
action: '
|
|
146
|
+
action: t('board.task.resolve'),
|
|
141
147
|
open: () => ui.openDecision(d.instanceId, d.decision.id),
|
|
142
148
|
}
|
|
143
149
|
const a = pendingApproval.value
|
|
144
150
|
if (a)
|
|
145
151
|
return {
|
|
146
|
-
label: '
|
|
152
|
+
label: t('board.task.approvalNeeded'),
|
|
147
153
|
icon: 'i-lucide-shield-check',
|
|
148
|
-
action: '
|
|
154
|
+
action: t('board.task.approve'),
|
|
149
155
|
open: () => ui.openApprovalDetail(a.instanceId, a.approval.id),
|
|
150
156
|
}
|
|
151
157
|
return null
|
|
@@ -155,7 +161,7 @@ const attention = computed<{
|
|
|
155
161
|
* decision/approval reason, otherwise the generic status label. */
|
|
156
162
|
const statusText = computed(() =>
|
|
157
163
|
runFailed.value
|
|
158
|
-
? '
|
|
164
|
+
? t('board.task.failed')
|
|
159
165
|
: (reviewStageLabel.value ?? attention.value?.label ?? statusMeta.value?.label ?? ''),
|
|
160
166
|
)
|
|
161
167
|
|
|
@@ -191,7 +197,11 @@ function selectTask() {
|
|
|
191
197
|
v-if="schedule"
|
|
192
198
|
name="i-lucide-repeat"
|
|
193
199
|
class="h-3 w-3 shrink-0 text-indigo-400"
|
|
194
|
-
:title="
|
|
200
|
+
:title="
|
|
201
|
+
schedule.enabled
|
|
202
|
+
? t('board.task.recurringPipeline')
|
|
203
|
+
: t('board.task.recurringPipelinePaused')
|
|
204
|
+
"
|
|
195
205
|
/>
|
|
196
206
|
<span
|
|
197
207
|
class="ml-auto truncate text-[9px] uppercase tracking-wide"
|
|
@@ -211,7 +221,7 @@ function selectTask() {
|
|
|
211
221
|
<button
|
|
212
222
|
type="button"
|
|
213
223
|
class="nodrag shrink-0 cursor-crosshair touch-none rounded-full p-0.5 text-slate-500 hover:bg-slate-800 hover:text-amber-400 pointer-coarse:p-2.5"
|
|
214
|
-
title="
|
|
224
|
+
:title="t('board.task.dragToConnect')"
|
|
215
225
|
@pointerdown.stop="startConnect(task.id, $event)"
|
|
216
226
|
@click.stop
|
|
217
227
|
>
|
|
@@ -302,12 +312,20 @@ function selectTask() {
|
|
|
302
312
|
:disabled="!runnable || starting"
|
|
303
313
|
:title="
|
|
304
314
|
runnable
|
|
305
|
-
?
|
|
306
|
-
|
|
315
|
+
? t('board.task.startPipeline', {
|
|
316
|
+
name: defaultPipeline?.name ?? t('board.task.pipelineFallback'),
|
|
317
|
+
})
|
|
318
|
+
: t('board.task.waitingOn', { deps: unmet.map((d) => d.title).join(', ') })
|
|
307
319
|
"
|
|
308
320
|
@click.stop="run"
|
|
309
321
|
>
|
|
310
|
-
{{
|
|
322
|
+
{{
|
|
323
|
+
starting
|
|
324
|
+
? t('board.task.starting')
|
|
325
|
+
: runnable
|
|
326
|
+
? t('board.task.start')
|
|
327
|
+
: t('board.task.blocked')
|
|
328
|
+
}}
|
|
311
329
|
</UButton>
|
|
312
330
|
<span
|
|
313
331
|
v-if="runnable && defaultPipeline"
|
|
@@ -328,7 +346,7 @@ function selectTask() {
|
|
|
328
346
|
variant="soft"
|
|
329
347
|
size="xs"
|
|
330
348
|
icon="i-lucide-git-pull-request"
|
|
331
|
-
:title="
|
|
349
|
+
:title="t('board.task.openPrOnGithub', { pr: prLabel })"
|
|
332
350
|
@click.stop
|
|
333
351
|
>
|
|
334
352
|
{{ prLabel }}
|
|
@@ -340,7 +358,7 @@ function selectTask() {
|
|
|
340
358
|
icon="i-lucide-scan-eye"
|
|
341
359
|
@click.stop="review"
|
|
342
360
|
>
|
|
343
|
-
|
|
361
|
+
{{ t('board.task.review') }}
|
|
344
362
|
</UButton>
|
|
345
363
|
<UButton
|
|
346
364
|
color="success"
|
|
@@ -349,7 +367,7 @@ function selectTask() {
|
|
|
349
367
|
icon="i-lucide-git-merge"
|
|
350
368
|
@click.stop="merge"
|
|
351
369
|
>
|
|
352
|
-
|
|
370
|
+
{{ t('board.task.merge') }}
|
|
353
371
|
</UButton>
|
|
354
372
|
</template>
|
|
355
373
|
|
|
@@ -357,7 +375,7 @@ function selectTask() {
|
|
|
357
375
|
v-else-if="task.status === 'done'"
|
|
358
376
|
class="inline-flex items-center gap-1 text-[9px] text-emerald-400"
|
|
359
377
|
>
|
|
360
|
-
<UIcon name="i-lucide-check-check" class="h-3 w-3" /> implemented
|
|
378
|
+
<UIcon name="i-lucide-check-check" class="h-3 w-3" /> {{ t('board.task.implemented') }}
|
|
361
379
|
</span>
|
|
362
380
|
</div>
|
|
363
381
|
|
|
@@ -368,7 +386,7 @@ function selectTask() {
|
|
|
368
386
|
>
|
|
369
387
|
<span
|
|
370
388
|
class="inline-flex items-center gap-1 rounded bg-violet-500/15 px-1.5 py-0.5 text-[9px] text-violet-200"
|
|
371
|
-
:title="
|
|
389
|
+
:title="t('board.task.module', { name: task.moduleName })"
|
|
372
390
|
>
|
|
373
391
|
<UIcon :name="MODULE_META.icon" class="h-3 w-3" :style="{ color: MODULE_META.color }" />
|
|
374
392
|
{{ task.moduleName }}
|
|
@@ -21,6 +21,7 @@ const execution = useExecutionStore()
|
|
|
21
21
|
const ui = useUiStore()
|
|
22
22
|
const expansion = useTaskExpansionStore()
|
|
23
23
|
const reviews = useReviewStage()
|
|
24
|
+
const { t } = useI18n()
|
|
24
25
|
const { lod } = useSemanticZoom()
|
|
25
26
|
|
|
26
27
|
const instance = computed(() => execution.getByBlock(props.taskId))
|
|
@@ -75,12 +76,12 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
75
76
|
<div v-if="showSteps" class="mt-2 space-y-1 border-t border-slate-800 pt-2">
|
|
76
77
|
<div class="flex items-center gap-1 text-[9px] uppercase tracking-wide text-slate-500">
|
|
77
78
|
<UIcon name="i-lucide-workflow" class="h-2.5 w-2.5" />
|
|
78
|
-
|
|
79
|
+
{{ t('board.task.buildSteps') }}
|
|
79
80
|
</div>
|
|
80
81
|
<div v-for="(s, i) in steps" :key="i" class="rounded bg-slate-900/60 px-1.5 py-1">
|
|
81
82
|
<div
|
|
82
83
|
class="flex cursor-pointer items-center gap-1"
|
|
83
|
-
:title="`${agentKindMeta(s.agentKind).label} — ${agentKindMeta(s.agentKind).description}\
|
|
84
|
+
:title="`${agentKindMeta(s.agentKind).label} — ${agentKindMeta(s.agentKind).description}\n${t('board.task.clickToViewStep')}`"
|
|
84
85
|
@click.stop="openStep(i)"
|
|
85
86
|
>
|
|
86
87
|
<UIcon
|
|
@@ -131,7 +132,7 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
131
132
|
@click.stop="ui.openApprovalDetail(instance.id, s.approval.id)"
|
|
132
133
|
>
|
|
133
134
|
<UIcon name="i-lucide-shield-check" class="h-2.5 w-2.5" />
|
|
134
|
-
|
|
135
|
+
{{ t('board.task.reviewAndApprove') }}
|
|
135
136
|
</button>
|
|
136
137
|
|
|
137
138
|
<!-- per-step subtask progress bar -->
|
|
@@ -8,15 +8,20 @@ const execution = useExecutionStore()
|
|
|
8
8
|
const workspace = useWorkspaceStore()
|
|
9
9
|
const services = useServicesStore()
|
|
10
10
|
const toast = useToast()
|
|
11
|
+
const { t, n } = useI18n()
|
|
11
12
|
const { fitView, zoomIn, zoomOut } = useBoardFlow()
|
|
12
13
|
|
|
13
14
|
async function mountService(serviceId: string, title: string) {
|
|
14
15
|
try {
|
|
15
16
|
await services.mount(serviceId)
|
|
16
|
-
toast.add({
|
|
17
|
+
toast.add({
|
|
18
|
+
title: t('board.toolbar.serviceAdded', { title }),
|
|
19
|
+
icon: 'i-lucide-box',
|
|
20
|
+
color: 'success',
|
|
21
|
+
})
|
|
17
22
|
} catch (e) {
|
|
18
23
|
toast.add({
|
|
19
|
-
title: '
|
|
24
|
+
title: t('board.toolbar.serviceAddFailed'),
|
|
20
25
|
description: e instanceof Error ? e.message : String(e),
|
|
21
26
|
color: 'error',
|
|
22
27
|
})
|
|
@@ -38,16 +43,16 @@ const mountableItems = computed(() =>
|
|
|
38
43
|
)
|
|
39
44
|
|
|
40
45
|
const zoomPct = computed(() => Math.round(ui.zoom * 100))
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
)
|
|
46
|
+
// Exhaustive (tier-2) map from level-of-detail → its label key, so adding an LOD
|
|
47
|
+
// without a label fails the typecheck rather than leaking a raw key.
|
|
48
|
+
const LOD_LABEL_KEYS = {
|
|
49
|
+
far: 'board.toolbar.lod.far',
|
|
50
|
+
mid: 'board.toolbar.lod.mid',
|
|
51
|
+
close: 'board.toolbar.lod.close',
|
|
52
|
+
steps: 'board.toolbar.lod.steps',
|
|
53
|
+
subtasks: 'board.toolbar.lod.subtasks',
|
|
54
|
+
} as const
|
|
55
|
+
const lodLabel = computed(() => t(LOD_LABEL_KEYS[ui.lod]))
|
|
51
56
|
|
|
52
57
|
// Live spend indicator: shown once any tokens have been metered this period.
|
|
53
58
|
const spend = computed(() => workspace.spend)
|
|
@@ -55,11 +60,11 @@ const showSpend = computed(() => !!spend.value && spend.value.costSpent > 0)
|
|
|
55
60
|
const spendLabel = computed(() => {
|
|
56
61
|
const s = spend.value
|
|
57
62
|
if (!s) return ''
|
|
58
|
-
const fmt = (
|
|
63
|
+
const fmt = (value: number) => {
|
|
59
64
|
try {
|
|
60
|
-
return
|
|
65
|
+
return n(value, { key: 'currency', currency: s.currency })
|
|
61
66
|
} catch {
|
|
62
|
-
return `${
|
|
67
|
+
return `${value.toFixed(2)} ${s.currency}`
|
|
63
68
|
}
|
|
64
69
|
}
|
|
65
70
|
return `${fmt(s.costSpent)} / ${fmt(s.costLimit)}`
|
|
@@ -70,7 +75,7 @@ const decisionItems = computed(() =>
|
|
|
70
75
|
execution.openDecisions.map((d) => {
|
|
71
76
|
const b = board.getBlock(d.blockId)
|
|
72
77
|
return {
|
|
73
|
-
label: b?.title ?? '
|
|
78
|
+
label: b?.title ?? t('common.block'),
|
|
74
79
|
description: d.decision.question,
|
|
75
80
|
icon: 'i-lucide-circle-help',
|
|
76
81
|
onSelect: () => ui.openDecision(d.instanceId, d.decision.id),
|
|
@@ -127,7 +132,7 @@ const decisionItems = computed(() =>
|
|
|
127
132
|
>
|
|
128
133
|
{{ execution.pendingDecisionCount
|
|
129
134
|
}}<span class="hidden sm:inline"
|
|
130
|
-
> {{
|
|
135
|
+
> {{ t('board.toolbar.decisionWord', execution.pendingDecisionCount) }}</span
|
|
131
136
|
>
|
|
132
137
|
</UButton>
|
|
133
138
|
</UDropdownMenu>
|
|
@@ -135,7 +140,7 @@ const decisionItems = computed(() =>
|
|
|
135
140
|
<!-- in-org sharing: add an existing org service to this board -->
|
|
136
141
|
<UDropdownMenu v-if="mountableItems.length" :items="mountableItems">
|
|
137
142
|
<UButton color="neutral" variant="ghost" size="sm" icon="i-lucide-plus-circle">
|
|
138
|
-
<span class="hidden sm:inline">{{
|
|
143
|
+
<span class="hidden sm:inline">{{ t('board.toolbar.addService') }}</span>
|
|
139
144
|
</UButton>
|
|
140
145
|
</UDropdownMenu>
|
|
141
146
|
|
|
@@ -149,7 +154,9 @@ const decisionItems = computed(() =>
|
|
|
149
154
|
variant="soft"
|
|
150
155
|
size="sm"
|
|
151
156
|
icon="i-lucide-wallet"
|
|
152
|
-
:title="
|
|
157
|
+
:title="
|
|
158
|
+
spend?.exceeded ? t('board.toolbar.spendLimitReached') : t('board.toolbar.spendTitle')
|
|
159
|
+
"
|
|
153
160
|
>
|
|
154
161
|
<span class="hidden sm:inline">{{ spendLabel }}</span>
|
|
155
162
|
</UButton>
|
package/i18n/locales/en.json
CHANGED
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
"save": "Save",
|
|
13
13
|
"cancel": "Cancel",
|
|
14
14
|
"retry": "Retry",
|
|
15
|
+
"block": "Block",
|
|
16
|
+
"@block": {
|
|
17
|
+
"description": "Generic fallback NOUN for a board item whose title is unknown (a service / module / task node). Not the verb 'to block'."
|
|
18
|
+
},
|
|
15
19
|
"actionFailed": "Action failed",
|
|
16
20
|
"close": "Close",
|
|
17
21
|
"@close": {
|
|
@@ -50,7 +54,207 @@
|
|
|
50
54
|
"decisionWord": "decision | decisions",
|
|
51
55
|
"@decisionWord": {
|
|
52
56
|
"description": "Count-based plural noun rendered AFTER a number, e.g. '3 decisions' (pending human decisions in the run queue). Resolved via t(key, count); count is always 1 or more. Provide ALL plural forms your language needs (English has 2; Polish/Ukrainian need 3 - one/few/many - and rely on the custom pluralRules wired in i18n.config.ts)."
|
|
57
|
+
},
|
|
58
|
+
"lod": {
|
|
59
|
+
"far": "Overview",
|
|
60
|
+
"mid": "Summary",
|
|
61
|
+
"close": "Detail",
|
|
62
|
+
"steps": "Build steps",
|
|
63
|
+
"subtasks": "Subtasks"
|
|
64
|
+
},
|
|
65
|
+
"spendTitle": "Token spend this month",
|
|
66
|
+
"spendLimitReached": "Spend limit reached — runs paused",
|
|
67
|
+
"serviceAdded": "Added {title}",
|
|
68
|
+
"serviceAddFailed": "Could not add service"
|
|
69
|
+
},
|
|
70
|
+
"canvas": {
|
|
71
|
+
"emptyTitle": "Your board is empty",
|
|
72
|
+
"emptyBody": "Add a service to get started: bootstrap a fresh repo or pull in one you already have.",
|
|
73
|
+
"addBlockFailedTitle": "Could not add block",
|
|
74
|
+
"addBlockFailedBody": "The backend rejected the request.",
|
|
75
|
+
"dropOntoTaskTitle": "Drop onto a task",
|
|
76
|
+
"dropOntoTaskBody": "Pipelines run against tasks, not services.",
|
|
77
|
+
"taskBlockedTitle": "Task is blocked",
|
|
78
|
+
"taskBlockedBody": "Its dependencies haven't merged yet."
|
|
79
|
+
},
|
|
80
|
+
"addTask": {
|
|
81
|
+
"title": "Add a task",
|
|
82
|
+
"newTaskIn": "New task in {container}",
|
|
83
|
+
"typeLabel": "Type",
|
|
84
|
+
"types": {
|
|
85
|
+
"feature": "Feature",
|
|
86
|
+
"bug": "Bug",
|
|
87
|
+
"document": "Document",
|
|
88
|
+
"spike": "Spike",
|
|
89
|
+
"recurring": "Recurring"
|
|
90
|
+
},
|
|
91
|
+
"recurringWithFrame": "A recurring task runs a pipeline on a cadence. Continue to set the schedule + prompt.",
|
|
92
|
+
"recurringNoFrame": "A recurring task must live on a service. Add it from a service frame (or a module inside one).",
|
|
93
|
+
"titleField": "Title",
|
|
94
|
+
"titlePlaceholder": "What needs to be done?",
|
|
95
|
+
"issueIncluded": "{title} (from issue, included)",
|
|
96
|
+
"loadingIssue": "Loading the linked issue's description…",
|
|
97
|
+
"additionalNotes": "Additional notes",
|
|
98
|
+
"description": "Description",
|
|
99
|
+
"notesPlaceholder": "Add anything else the agent should know — appended to the issue description above…",
|
|
100
|
+
"descriptionPlaceholder": "Describe the work — context, acceptance criteria, anything the agent should know…",
|
|
101
|
+
"technical": "Technical task",
|
|
102
|
+
"technicalHint": "A refactor / non-functional / internal change. The implementer treats the task definition as primary and the spec as a regression reference; leave off to let the spec phase decide.",
|
|
103
|
+
"severity": "Severity",
|
|
104
|
+
"stepsToReproduce": "Steps to reproduce",
|
|
105
|
+
"stepsToReproducePlaceholder": "Observed vs expected, and how to reproduce…",
|
|
106
|
+
"timebox": "Time-box (hours)",
|
|
107
|
+
"timeboxPlaceholder": "e.g. 8",
|
|
108
|
+
"documentKind": "Document kind",
|
|
109
|
+
"audience": "Audience",
|
|
110
|
+
"audiencePlaceholder": "e.g. platform engineers",
|
|
111
|
+
"targetPath": "Target path",
|
|
112
|
+
"targetPathPlaceholder": "e.g. docs/rfcs/0001-foo.md",
|
|
113
|
+
"outlineHints": "Outline hints",
|
|
114
|
+
"outlineHintsPlaceholder": "Sections or points the document should cover",
|
|
115
|
+
"optional": "optional",
|
|
116
|
+
"pipeline": "Pipeline",
|
|
117
|
+
"chooseAtRunTime": "Choose at run time",
|
|
118
|
+
"mergePolicy": "Merge policy",
|
|
119
|
+
"workspaceDefault": "Workspace default",
|
|
120
|
+
"defaultPreset": "Default ({name}) — {thresholds}",
|
|
121
|
+
"defaultModelPreset": "Default ({name})",
|
|
122
|
+
"modelPreset": "Model preset",
|
|
123
|
+
"agentConfiguration": "Agent configuration",
|
|
124
|
+
"contextDocuments": "Context documents",
|
|
125
|
+
"contextIssues": "Context issues",
|
|
126
|
+
"attach": "Attach",
|
|
127
|
+
"done": "Done",
|
|
128
|
+
"attachDocDisabledConnect": "Connect a document source first (Integrations)",
|
|
129
|
+
"attachDocDisabledEnable": "Enable the documents integration first",
|
|
130
|
+
"attachIssueDisabledConnect": "Connect an issue tracker first (Integrations)",
|
|
131
|
+
"attachIssueDisabledEnable": "Enable the issue-tracker integration first",
|
|
132
|
+
"importsOnAdd": "imports on add",
|
|
133
|
+
"noDocsHint": "Attach a requirement, RFC or PRD so agents see it while implementing this task.",
|
|
134
|
+
"noIssuesHint": "Attach a tracker issue so agents see its description and comments while implementing this task.",
|
|
135
|
+
"plannedHint": "The task is added in a planned state. It won't run until you start a pipeline on it — you can keep editing it until then.",
|
|
136
|
+
"continue": "Continue",
|
|
137
|
+
"submit": "Add task",
|
|
138
|
+
"addFailedTitle": "Could not add task",
|
|
139
|
+
"linkFailed": "Task added, but {count} attachment could not be linked | Task added, but {count} attachments could not be linked",
|
|
140
|
+
"@linkFailed": {
|
|
141
|
+
"description": "Count-based: how many context attachments (docs/issues) failed to link after the task was created (count is always >= 1). Provide ALL plural forms your language needs (English has 2; Polish/Ukrainian need 3 - one/few/many - via the custom pluralRules in i18n.config.ts)."
|
|
53
142
|
}
|
|
143
|
+
},
|
|
144
|
+
"recurring": {
|
|
145
|
+
"title": "Add a recurring pipeline",
|
|
146
|
+
"on": "Recurring pipeline on {frame}",
|
|
147
|
+
"name": "Name",
|
|
148
|
+
"namePlaceholder": "e.g. Weekly dependency updates",
|
|
149
|
+
"pipeline": "Pipeline",
|
|
150
|
+
"pickPipeline": "Pick a pipeline",
|
|
151
|
+
"prompt": "Prompt",
|
|
152
|
+
"promptPlaceholder": "What should each run do? Describe the work — the same prompt a normal task carries. Leave blank to use the pipeline's default.",
|
|
153
|
+
"issueTracker": "Issue tracker",
|
|
154
|
+
"issueTrackerHint": "The tech-debt pipeline files a ticket from its analysis before implementing. Choose where (saved for the whole workspace).",
|
|
155
|
+
"githubIssues": "GitHub Issues",
|
|
156
|
+
"jira": "Jira",
|
|
157
|
+
"linear": "Linear",
|
|
158
|
+
"jiraProjectKey": "Jira project key",
|
|
159
|
+
"jiraProjectKeyPlaceholder": "e.g. ENG",
|
|
160
|
+
"linearTeamId": "Linear team id",
|
|
161
|
+
"footerHint": "A single recurring task is added inside the service; each run replaces the last. Its run history is visible in the inspector.",
|
|
162
|
+
"submit": "Add recurring pipeline",
|
|
163
|
+
"addFailedTitle": "Could not add recurring pipeline"
|
|
164
|
+
},
|
|
165
|
+
"failure": {
|
|
166
|
+
"containerFailedToStart": "Container failed to start",
|
|
167
|
+
"bootstrapFailed": "Bootstrap failed",
|
|
168
|
+
"runFailed": "Run failed",
|
|
169
|
+
"retryBootstrap": "Retry bootstrap",
|
|
170
|
+
"retryRun": "Retry run",
|
|
171
|
+
"showDetail": "Show detail",
|
|
172
|
+
"retrying": "Retrying…"
|
|
173
|
+
},
|
|
174
|
+
"stop": {
|
|
175
|
+
"label": "Stop",
|
|
176
|
+
"bootstrapStopped": "Bootstrap stopped",
|
|
177
|
+
"runStopped": "Run stopped",
|
|
178
|
+
"stoppedDescription": "The container was killed and the run was cancelled.",
|
|
179
|
+
"stopFailed": "Stop failed"
|
|
180
|
+
},
|
|
181
|
+
"frame": {
|
|
182
|
+
"status": {
|
|
183
|
+
"planned": "No tasks",
|
|
184
|
+
"ready": "Live",
|
|
185
|
+
"in_progress": "Active",
|
|
186
|
+
"blocked": "Needs attention",
|
|
187
|
+
"pr_ready": "Active",
|
|
188
|
+
"done": "Live"
|
|
189
|
+
},
|
|
190
|
+
"shared": "Shared",
|
|
191
|
+
"sharedTitle": "Shared across workspaces in this org",
|
|
192
|
+
"bootstrapping": "Bootstrapping…",
|
|
193
|
+
"bootstrappingRepository": "Bootstrapping repository…",
|
|
194
|
+
"bootstrapStepsCount": "{completed}/{total} steps",
|
|
195
|
+
"runFailed": "Run failed",
|
|
196
|
+
"mergedOfTotal": "{merged}/{total} merged",
|
|
197
|
+
"noTasksYet": "No tasks yet",
|
|
198
|
+
"prCount": "{count} PR",
|
|
199
|
+
"implemented": "{merged}/{total} implemented",
|
|
200
|
+
"prReadyCount": "{count} PR ready",
|
|
201
|
+
"taskCount": "{count} task | {count} tasks",
|
|
202
|
+
"@taskCount": {
|
|
203
|
+
"description": "Count-based task tally rendered as e.g. '3 tasks' (count is always >= 0). Provide ALL plural forms your language needs (English has 2; Polish/Ukrainian need 3 - one/few/many - via the custom pluralRules in i18n.config.ts)."
|
|
204
|
+
},
|
|
205
|
+
"moduleCount": "{count} module | {count} modules",
|
|
206
|
+
"@moduleCount": {
|
|
207
|
+
"description": "Count-based module tally rendered as e.g. '2 modules' (count is always >= 1). Provide ALL plural forms your language needs (English has 2; Polish/Ukrainian need 3 - one/few/many - via the custom pluralRules in i18n.config.ts)."
|
|
208
|
+
},
|
|
209
|
+
"addTaskTitle": "Add task",
|
|
210
|
+
"createTaskFromIssueTitle": "Create task from issue",
|
|
211
|
+
"addRecurringTitle": "Add recurring pipeline",
|
|
212
|
+
"collapseTitle": "Collapse",
|
|
213
|
+
"dragService": "Drag service",
|
|
214
|
+
"dragTask": "Drag task",
|
|
215
|
+
"dragToResize": "Drag to resize",
|
|
216
|
+
"addFirstTask": "Add the first task"
|
|
217
|
+
},
|
|
218
|
+
"decisionBadge": {
|
|
219
|
+
"decisionNeeded": "Decision needed",
|
|
220
|
+
"approvalNeeded": "Approval needed"
|
|
221
|
+
},
|
|
222
|
+
"task": {
|
|
223
|
+
"recurringPipeline": "Recurring pipeline",
|
|
224
|
+
"recurringPipelinePaused": "Recurring pipeline (paused)",
|
|
225
|
+
"dragToConnect": "Drag onto another task to make it depend on this one",
|
|
226
|
+
"failed": "Failed",
|
|
227
|
+
"decisionNeeded": "Decision needed",
|
|
228
|
+
"approvalNeeded": "Approval needed",
|
|
229
|
+
"resolve": "Resolve",
|
|
230
|
+
"approve": "Approve",
|
|
231
|
+
"incorporatingAnswers": "Incorporating answers…",
|
|
232
|
+
"reReviewing": "Re-reviewing…",
|
|
233
|
+
"recommending": "Recommending…",
|
|
234
|
+
"starting": "Starting…",
|
|
235
|
+
"start": "Start",
|
|
236
|
+
"blocked": "Blocked",
|
|
237
|
+
"startPipeline": "Start {name}",
|
|
238
|
+
"pipelineFallback": "pipeline",
|
|
239
|
+
"waitingOn": "Waiting on: {deps}",
|
|
240
|
+
"blockedByDependenciesTitle": "Blocked by dependencies",
|
|
241
|
+
"noPipelineTitle": "No pipeline defined",
|
|
242
|
+
"noPipelineBody": "Create one in the builder first.",
|
|
243
|
+
"pr": "PR",
|
|
244
|
+
"prNumber": "PR #{number}",
|
|
245
|
+
"openPrOnGithub": "Open {pr} on GitHub",
|
|
246
|
+
"review": "Review",
|
|
247
|
+
"merge": "Merge",
|
|
248
|
+
"implemented": "implemented",
|
|
249
|
+
"module": "Module: {name}",
|
|
250
|
+
"buildSteps": "Build steps",
|
|
251
|
+
"reviewAndApprove": "Review & approve",
|
|
252
|
+
"clickToViewStep": "Click to view step details & output"
|
|
253
|
+
},
|
|
254
|
+
"epic": {
|
|
255
|
+
"label": "Epic",
|
|
256
|
+
"activeCount": "{count} active",
|
|
257
|
+
"noTasksYet": "No tasks yet"
|
|
54
258
|
}
|
|
55
259
|
},
|
|
56
260
|
"errors": {
|