@cat-factory/app 0.92.2 → 0.93.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.
Files changed (50) hide show
  1. package/app/components/auth/LoginScreen.vue +5 -1
  2. package/app/components/board/AddTaskModal.vue +25 -5
  3. package/app/components/board/CreateInitiativeModal.vue +9 -1
  4. package/app/components/board/RecurringPipelineModal.vue +20 -4
  5. package/app/components/bootstrap/BootstrapModal.vue +9 -1
  6. package/app/components/brainstorm/BrainstormWindow.vue +5 -1
  7. package/app/components/clarity/ClarityReviewWindow.vue +5 -1
  8. package/app/components/docs/DocInterviewWindow.vue +209 -0
  9. package/app/components/documents/DocumentTemplatesModal.vue +5 -1
  10. package/app/components/fragments/FragmentLibraryManager.vue +10 -2
  11. package/app/components/github/GitHubPanel.vue +25 -4
  12. package/app/components/layout/BoardSwitcher.vue +10 -1
  13. package/app/components/layout/BoardToolbar.vue +15 -3
  14. package/app/components/layout/GitHubPatBanner.vue +5 -1
  15. package/app/components/layout/ProviderConfigBanner.vue +5 -1
  16. package/app/components/panels/AgentStepDetail.vue +5 -1
  17. package/app/components/panels/InspectorPanel.vue +5 -1
  18. package/app/components/panels/StepRestartControl.vue +10 -2
  19. package/app/components/panels/StepResultViewHost.vue +4 -0
  20. package/app/components/panels/inspector/FrontendConfig.vue +10 -1
  21. package/app/components/panels/inspector/RecurringScheduleSettings.vue +11 -3
  22. package/app/components/pipeline/PipelineBuilder.vue +15 -2
  23. package/app/components/pipeline/PipelineProgress.vue +10 -2
  24. package/app/components/providers/AiPresetMismatchDialog.vue +10 -1
  25. package/app/components/providers/PersonalCredentialModal.vue +18 -2
  26. package/app/components/requirements/RequirementsReviewWindow.vue +5 -1
  27. package/app/components/sandbox/SandboxPanel.vue +10 -1
  28. package/app/components/settings/InfraHandlersConfigurator.vue +5 -1
  29. package/app/components/settings/IssueTrackerPanel.vue +20 -4
  30. package/app/components/settings/LocalModelEndpointsPanel.vue +5 -1
  31. package/app/components/settings/ModelConfigurationPanel.vue +20 -3
  32. package/app/components/settings/ProviderConnectionTab.vue +5 -1
  33. package/app/components/spec/ServiceSpecWindow.vue +15 -3
  34. package/app/components/testing/TestReportWindow.vue +10 -2
  35. package/app/composables/api/docInterview.ts +36 -0
  36. package/app/composables/useApi.ts +2 -0
  37. package/app/composables/useWorkspaceStream.ts +5 -0
  38. package/app/stores/docInterview.ts +89 -0
  39. package/app/stores/workspace.ts +1 -0
  40. package/app/types/doc-interview.ts +9 -0
  41. package/app/types/domain.ts +1 -0
  42. package/i18n/locales/en.json +16 -0
  43. package/i18n/locales/es.json +16 -0
  44. package/i18n/locales/fr.json +16 -0
  45. package/i18n/locales/he.json +16 -0
  46. package/i18n/locales/ja.json +16 -0
  47. package/i18n/locales/pl.json +16 -0
  48. package/i18n/locales/tr.json +16 -0
  49. package/i18n/locales/uk.json +16 -0
  50. package/package.json +5 -5
@@ -279,7 +279,16 @@ onUnmounted(() => preview.stopPolling(props.block.id))
279
279
  >
280
280
  {{ t('inspector.frontendConfig.detect.apply') }}
281
281
  </UButton>
282
- <UButton size="xs" variant="ghost" color="neutral" @click="detectResult = null">
282
+ <UButton
283
+ size="xs"
284
+ variant="ghost"
285
+ color="neutral"
286
+ @click="
287
+ () => {
288
+ detectResult = null
289
+ }
290
+ "
291
+ >
283
292
  {{ t('inspector.frontendConfig.detect.dismiss') }}
284
293
  </UButton>
285
294
  </div>
@@ -213,9 +213,17 @@ function fmtTime(ms: number) {
213
213
  <template v-else-if="draft">
214
214
  <RecurringRecurrenceEditor v-model="draft" />
215
215
  <div class="flex justify-end gap-1.5 pt-1">
216
- <UButton size="xs" variant="ghost" color="neutral" @click="editing = false">{{
217
- t('common.cancel')
218
- }}</UButton>
216
+ <UButton
217
+ size="xs"
218
+ variant="ghost"
219
+ color="neutral"
220
+ @click="
221
+ () => {
222
+ editing = false
223
+ }
224
+ "
225
+ >{{ t('common.cancel') }}</UButton
226
+ >
219
227
  <UButton size="xs" color="primary" :loading="busy" @click="saveEdit">{{
220
228
  t('common.save')
221
229
  }}</UButton>
@@ -762,7 +762,11 @@ async function clone(p: Pipeline) {
762
762
  :color="showArchived ? 'primary' : 'neutral'"
763
763
  variant="ghost"
764
764
  size="xs"
765
- @click="showArchived = !showArchived"
765
+ @click="
766
+ () => {
767
+ showArchived = !showArchived
768
+ }
769
+ "
766
770
  >
767
771
  {{
768
772
  showArchived
@@ -987,7 +991,16 @@ async function clone(p: Pipeline) {
987
991
 
988
992
  <template #footer>
989
993
  <div class="flex w-full items-center justify-end gap-2">
990
- <UButton color="neutral" variant="ghost" size="sm" @click="addAgentOpen = false">
994
+ <UButton
995
+ color="neutral"
996
+ variant="ghost"
997
+ size="sm"
998
+ @click="
999
+ () => {
1000
+ addAgentOpen = false
1001
+ }
1002
+ "
1003
+ >
991
1004
  {{ t('common.cancel') }}
992
1005
  </UButton>
993
1006
  <UButton
@@ -324,7 +324,11 @@ const ITEM_ICON: Record<string, string> = {
324
324
  size="xs"
325
325
  class="shrink-0 opacity-0 transition-opacity group-hover:opacity-100"
326
326
  :title="t('pipeline.progress.restartTooltip')"
327
- @click.stop="restartArmed = i"
327
+ @click.stop="
328
+ () => {
329
+ restartArmed = i
330
+ }
331
+ "
328
332
  />
329
333
  <template v-else>
330
334
  <UButton
@@ -344,7 +348,11 @@ const ITEM_ICON: Record<string, string> = {
344
348
  size="xs"
345
349
  class="shrink-0"
346
350
  :disabled="restarting === i"
347
- @click.stop="restartArmed = null"
351
+ @click.stop="
352
+ () => {
353
+ restartArmed = null
354
+ }
355
+ "
348
356
  >
349
357
  {{ t('common.cancel') }}
350
358
  </UButton>
@@ -75,7 +75,16 @@ function go(action: () => void) {
75
75
  </p>
76
76
 
77
77
  <div class="flex flex-wrap justify-end gap-2">
78
- <UButton color="neutral" variant="ghost" size="sm" @click="open = false">
78
+ <UButton
79
+ color="neutral"
80
+ variant="ghost"
81
+ size="sm"
82
+ @click="
83
+ () => {
84
+ open = false
85
+ }
86
+ "
87
+ >
79
88
  {{ t('providers.presetMismatch.later') }}
80
89
  </UButton>
81
90
  <UButton
@@ -97,7 +97,15 @@ function goConnect() {
97
97
  {{ t('providers.personalCredential.connectBody', { vendor: vendorLabel }) }}
98
98
  </p>
99
99
  <div class="flex justify-end gap-2">
100
- <UButton color="neutral" variant="ghost" @click="open = false">
100
+ <UButton
101
+ color="neutral"
102
+ variant="ghost"
103
+ @click="
104
+ () => {
105
+ open = false
106
+ }
107
+ "
108
+ >
101
109
  {{ t('providers.personalCredential.cancel') }}
102
110
  </UButton>
103
111
  <UButton icon="i-lucide-shield-check" @click="goConnect()">
@@ -120,7 +128,15 @@ function goConnect() {
120
128
  />
121
129
  </UFormField>
122
130
  <div class="flex justify-end gap-2">
123
- <UButton color="neutral" variant="ghost" @click="open = false">
131
+ <UButton
132
+ color="neutral"
133
+ variant="ghost"
134
+ @click="
135
+ () => {
136
+ open = false
137
+ }
138
+ "
139
+ >
124
140
  {{ t('providers.personalCredential.cancel') }}
125
141
  </UButton>
126
142
  <UButton :loading="busy" :disabled="password.length < 6" @click="submit()">
@@ -979,7 +979,11 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
979
979
  size="sm"
980
980
  block
981
981
  icon="i-lucide-pencil"
982
- @click="showRedo = !showRedo"
982
+ @click="
983
+ () => {
984
+ showRedo = !showRedo
985
+ }
986
+ "
983
987
  >
984
988
  {{ t('requirements.actions.redoIncorporation') }}
985
989
  </UButton>
@@ -561,7 +561,16 @@ async function archive(prompt: SandboxPromptVersion) {
561
561
  </p>
562
562
  <UTextarea v-model="editText" :rows="16" class="w-full font-mono text-xs" autoresize />
563
563
  <div class="flex justify-end gap-2">
564
- <UButton color="neutral" variant="ghost" size="sm" @click="editing = null">
564
+ <UButton
565
+ color="neutral"
566
+ variant="ghost"
567
+ size="sm"
568
+ @click="
569
+ () => {
570
+ editing = null
571
+ }
572
+ "
573
+ >
565
574
  {{ t('common.cancel') }}
566
575
  </UButton>
567
576
  <UButton
@@ -443,7 +443,11 @@ function notifyError(e: unknown) {
443
443
  :color="selectedKubeEngine === e ? 'primary' : 'neutral'"
444
444
  :variant="selectedKubeEngine === e ? 'soft' : 'ghost'"
445
445
  size="xs"
446
- @click="selectedKubeEngine = e"
446
+ @click="
447
+ () => {
448
+ selectedKubeEngine = e
449
+ }
450
+ "
447
451
  >
448
452
  {{ t(KUBE_ENGINE_KEYS[e]) }}
449
453
  </UButton>
@@ -234,7 +234,11 @@ const STATUS_UI: Record<
234
234
  size="sm"
235
235
  :color="trackerKind === null ? 'primary' : 'neutral'"
236
236
  :variant="trackerKind === null ? 'solid' : 'subtle'"
237
- @click="trackerKind = null"
237
+ @click="
238
+ () => {
239
+ trackerKind = null
240
+ }
241
+ "
238
242
  >
239
243
  {{ t('settings.issueTracker.filing.none') }}
240
244
  </UButton>
@@ -243,7 +247,11 @@ const STATUS_UI: Record<
243
247
  size="sm"
244
248
  :color="trackerKind === 'github' ? 'primary' : 'neutral'"
245
249
  :variant="trackerKind === 'github' ? 'solid' : 'subtle'"
246
- @click="trackerKind = 'github'"
250
+ @click="
251
+ () => {
252
+ trackerKind = 'github'
253
+ }
254
+ "
247
255
  >
248
256
  {{ t('settings.issueTracker.vendor.github') }}
249
257
  </UButton>
@@ -252,7 +260,11 @@ const STATUS_UI: Record<
252
260
  size="sm"
253
261
  :color="trackerKind === 'jira' ? 'primary' : 'neutral'"
254
262
  :variant="trackerKind === 'jira' ? 'solid' : 'subtle'"
255
- @click="trackerKind = 'jira'"
263
+ @click="
264
+ () => {
265
+ trackerKind = 'jira'
266
+ }
267
+ "
256
268
  >
257
269
  Jira
258
270
  </UButton>
@@ -261,7 +273,11 @@ const STATUS_UI: Record<
261
273
  size="sm"
262
274
  :color="trackerKind === 'linear' ? 'primary' : 'neutral'"
263
275
  :variant="trackerKind === 'linear' ? 'solid' : 'subtle'"
264
- @click="trackerKind = 'linear'"
276
+ @click="
277
+ () => {
278
+ trackerKind = 'linear'
279
+ }
280
+ "
265
281
  >
266
282
  Linear
267
283
  </UButton>
@@ -225,7 +225,11 @@ async function remove(p: LocalRunner) {
225
225
  size="xs"
226
226
  :disabled="busy"
227
227
  :title="t('settings.localModelEndpoints.edit')"
228
- @click="provider = e.provider"
228
+ @click="
229
+ () => {
230
+ provider = e.provider
231
+ }
232
+ "
229
233
  />
230
234
  <UButton
231
235
  icon="i-lucide-trash-2"
@@ -281,7 +281,11 @@ function fail(title: string, e: unknown) {
281
281
  variant="ghost"
282
282
  size="sm"
283
283
  class="ms-auto"
284
- @click="editor = null"
284
+ @click="
285
+ () => {
286
+ editor = null
287
+ }
288
+ "
285
289
  >
286
290
  {{ t('settings.modelConfiguration.back') }}
287
291
  </UButton>
@@ -292,7 +296,11 @@ function fail(title: string, e: unknown) {
292
296
  size="sm"
293
297
  :class="editor ? '' : 'ms-auto'"
294
298
  :title="t('settings.modelConfiguration.closeEsc')"
295
- @click="open = false"
299
+ @click="
300
+ () => {
301
+ open = false
302
+ }
303
+ "
296
304
  />
297
305
  </header>
298
306
 
@@ -499,7 +507,16 @@ function fail(title: string, e: unknown) {
499
507
  </div>
500
508
 
501
509
  <div class="flex items-center justify-end gap-2">
502
- <UButton color="neutral" variant="ghost" size="sm" @click="editor = null">
510
+ <UButton
511
+ color="neutral"
512
+ variant="ghost"
513
+ size="sm"
514
+ @click="
515
+ () => {
516
+ editor = null
517
+ }
518
+ "
519
+ >
503
520
  {{ t('common.cancel') }}
504
521
  </UButton>
505
522
  <UButton color="primary" size="sm" :loading="busy" @click="save">
@@ -364,7 +364,11 @@ function fieldHelp(key: string): string | undefined {
364
364
  variant="ghost"
365
365
  size="xs"
366
366
  class="shrink-0"
367
- @click="showLogs = !showLogs"
367
+ @click="
368
+ () => {
369
+ showLogs = !showLogs
370
+ }
371
+ "
368
372
  >
369
373
  {{
370
374
  showLogs
@@ -147,7 +147,11 @@ function kindLabel(item: RequirementItem): string {
147
147
  :variant="mode === 'structured' ? 'soft' : 'ghost'"
148
148
  size="xs"
149
149
  icon="i-lucide-list-tree"
150
- @click="mode = 'structured'"
150
+ @click="
151
+ () => {
152
+ mode = 'structured'
153
+ }
154
+ "
151
155
  >
152
156
  {{ t('spec.mode.structured') }}
153
157
  </UButton>
@@ -158,7 +162,11 @@ function kindLabel(item: RequirementItem): string {
158
162
  icon="i-lucide-square-check-big"
159
163
  :disabled="!hasGherkin"
160
164
  :title="hasGherkin ? t('spec.mode.gherkinTooltip') : t('spec.mode.gherkinNone')"
161
- @click="mode = 'gherkin'"
165
+ @click="
166
+ () => {
167
+ mode = 'gherkin'
168
+ }
169
+ "
162
170
  >
163
171
  {{ t('spec.mode.gherkin') }}
164
172
  </UButton>
@@ -210,7 +218,11 @@ function kindLabel(item: RequirementItem): string {
210
218
  :variant="selected === null ? 'soft' : 'ghost'"
211
219
  size="xs"
212
220
  icon="i-lucide-info"
213
- @click="selected = null"
221
+ @click="
222
+ () => {
223
+ selected = null
224
+ }
225
+ "
214
226
  >
215
227
  {{ t('spec.overview') }}
216
228
  </UButton>
@@ -444,7 +444,11 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
444
444
  size="xs"
445
445
  class="mt-1.5"
446
446
  data-testid="tester-infra-setup-logs-toggle"
447
- @click="showInfraSetupLogs = !showInfraSetupLogs"
447
+ @click="
448
+ () => {
449
+ showInfraSetupLogs = !showInfraSetupLogs
450
+ }
451
+ "
448
452
  >
449
453
  {{
450
454
  showInfraSetupLogs
@@ -480,7 +484,11 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
480
484
  variant="ghost"
481
485
  size="xs"
482
486
  data-testid="tester-infra-attempts-toggle"
483
- @click="showProvisioning = !showProvisioning"
487
+ @click="
488
+ () => {
489
+ showProvisioning = !showProvisioning
490
+ }
491
+ "
484
492
  >
485
493
  {{
486
494
  showProvisioning
@@ -0,0 +1,36 @@
1
+ import {
2
+ answerDocInterviewContract,
3
+ continueDocInterviewContract,
4
+ getDocInterviewContract,
5
+ proceedDocInterviewContract,
6
+ } from '@cat-factory/contracts'
7
+ import type { ApiContext } from './context'
8
+
9
+ /** Interactive document-interview session (WS5): load + answer / continue / proceed. */
10
+ export function docInterviewApi({ send, ws }: ApiContext) {
11
+ return {
12
+ // The interview window's load path: the session anchored to a board block (or null).
13
+ getDocInterview: (workspaceId: string, blockId: string) =>
14
+ send(getDocInterviewContract, { pathPrefix: ws(workspaceId), pathParams: { blockId } }),
15
+
16
+ // Answer one interview question (no run resume), then continue (interviewer re-runs, may
17
+ // ask more) or proceed (skip remaining, synthesize the brief and advance to the writer).
18
+ answerDocInterview: (
19
+ workspaceId: string,
20
+ blockId: string,
21
+ questionId: string,
22
+ answer: string,
23
+ ) =>
24
+ send(answerDocInterviewContract, {
25
+ pathPrefix: ws(workspaceId),
26
+ pathParams: { blockId },
27
+ body: { questionId, answer },
28
+ }),
29
+
30
+ continueDocInterview: (workspaceId: string, blockId: string) =>
31
+ send(continueDocInterviewContract, { pathPrefix: ws(workspaceId), pathParams: { blockId } }),
32
+
33
+ proceedDocInterview: (workspaceId: string, blockId: string) =>
34
+ send(proceedDocInterviewContract, { pathPrefix: ws(workspaceId), pathParams: { blockId } }),
35
+ }
36
+ }
@@ -14,6 +14,7 @@ import { humanReviewApi } from './api/humanReview'
14
14
  import { humanTestApi } from './api/humanTest'
15
15
  import { infraHandlersApi } from './api/infraHandlers'
16
16
  import { initiativeApi } from './api/initiative'
17
+ import { docInterviewApi } from './api/docInterview'
17
18
  import { visualConfirmApi } from './api/visualConfirm'
18
19
  import { kaizenApi } from './api/kaizen'
19
20
  import { localSettingsApi } from './api/localSettings'
@@ -114,6 +115,7 @@ export function useApi() {
114
115
  ...providerConnectionsApi(ctx),
115
116
  ...infraHandlersApi(ctx),
116
117
  ...initiativeApi(ctx),
118
+ ...docInterviewApi(ctx),
117
119
  ...provisioningLogsApi(ctx),
118
120
  ...releaseHealthApi(ctx),
119
121
  ...packageRegistriesApi(ctx),
@@ -26,6 +26,7 @@ export function useWorkspaceStream() {
26
26
  const brainstorm = useBrainstormStore()
27
27
  const kaizen = useKaizenStore()
28
28
  const initiatives = useInitiativesStore()
29
+ const docInterview = useDocInterviewStore()
29
30
  const api = useApi()
30
31
  const apiBase = useRuntimeConfig().public.apiBase
31
32
 
@@ -108,6 +109,10 @@ export function useWorkspaceStream() {
108
109
  // An initiative changed (created, plan ingested, an item settled) — patch the cache
109
110
  // so an open tracker window / the board card reflects the transition live.
110
111
  initiatives.upsert(event.initiative)
112
+ } else if (event.type === 'docInterview') {
113
+ // The interactive document interview advanced (a fresh batch of questions, an answer, or
114
+ // convergence) — patch the cache so an open interview window reflects it live.
115
+ docInterview.upsert(event.session)
111
116
  }
112
117
  }
113
118
 
@@ -0,0 +1,89 @@
1
+ import { defineStore } from 'pinia'
2
+ import { ref } from 'vue'
3
+ import type { DocInterviewSession } from '~/types/domain'
4
+ import { useWorkspaceStore } from '~/stores/workspace'
5
+
6
+ /**
7
+ * Interactive document-interview sessions (WS5), keyed by their anchor BLOCK id. Loaded on
8
+ * demand when the interview window opens (`load`) and patched live from `docInterview` stream
9
+ * events (`upsert`), so an open window follows the interview as the interviewer asks / converges.
10
+ * Not carried in the workspace snapshot (a transient per-run gate, unlike initiatives).
11
+ * Per-workspace; nothing is persisted client-side.
12
+ */
13
+ export const useDocInterviewStore = defineStore('docInterview', () => {
14
+ const api = useApi()
15
+ const workspace = useWorkspaceStore()
16
+
17
+ /** The sessions keyed by their anchor block id. */
18
+ const byBlock = ref<Record<string, DocInterviewSession>>({})
19
+ /** True while a window action (continue/proceed) is resuming the run. */
20
+ const resuming = ref(false)
21
+
22
+ function forBlock(blockId: string): DocInterviewSession | null {
23
+ return byBlock.value[blockId] ?? null
24
+ }
25
+
26
+ /** Patch from a live `docInterview` stream event or a call response (newest write wins). */
27
+ function upsert(session: DocInterviewSession) {
28
+ const existing = byBlock.value[session.blockId]
29
+ if (existing && existing.updatedAt > session.updatedAt) return
30
+ byBlock.value = { ...byBlock.value, [session.blockId]: session }
31
+ }
32
+
33
+ /** Re-fetch one block's session (the interview window's load path). */
34
+ async function load(blockId: string) {
35
+ if (!workspace.workspaceId) return
36
+ const session = await api.getDocInterview(workspace.workspaceId, blockId)
37
+ if (session) upsert(session)
38
+ }
39
+
40
+ /** Record the human's answer to one pending interview question (no run resume). */
41
+ async function answerQuestion(blockId: string, questionId: string, answer: string) {
42
+ if (!workspace.workspaceId) throw new Error('No active workspace')
43
+ const updated = await api.answerDocInterview(workspace.workspaceId, blockId, questionId, answer)
44
+ upsert(updated)
45
+ return updated
46
+ }
47
+
48
+ /** Submit the answers and resume the interview (the interviewer re-runs, may ask more). */
49
+ async function continueInterview(blockId: string) {
50
+ if (!workspace.workspaceId) throw new Error('No active workspace')
51
+ resuming.value = true
52
+ try {
53
+ const updated = await api.continueDocInterview(workspace.workspaceId, blockId)
54
+ upsert(updated)
55
+ return updated
56
+ } finally {
57
+ resuming.value = false
58
+ }
59
+ }
60
+
61
+ /** Skip remaining questions: the interviewer converges and the run advances to the writer. */
62
+ async function proceedInterview(blockId: string) {
63
+ if (!workspace.workspaceId) throw new Error('No active workspace')
64
+ resuming.value = true
65
+ try {
66
+ const updated = await api.proceedDocInterview(workspace.workspaceId, blockId)
67
+ upsert(updated)
68
+ return updated
69
+ } finally {
70
+ resuming.value = false
71
+ }
72
+ }
73
+
74
+ function reset() {
75
+ byBlock.value = {}
76
+ }
77
+
78
+ return {
79
+ byBlock,
80
+ resuming,
81
+ forBlock,
82
+ upsert,
83
+ load,
84
+ answerQuestion,
85
+ continueInterview,
86
+ proceedInterview,
87
+ reset,
88
+ }
89
+ })
@@ -84,6 +84,7 @@ export const useWorkspaceStore = defineStore(
84
84
  useConsensusStore().reset()
85
85
  useGitHubStore().reset()
86
86
  useInitiativesStore().reset()
87
+ useDocInterviewStore().reset()
87
88
  // The fragment picker catalog is per-board (the merged tenant catalog), so drop
88
89
  // it too — the next inspector open re-fetches it for the switched-to board rather
89
90
  // than showing the previous board's (or a raw-id placeholder for) fragments.
@@ -0,0 +1,9 @@
1
+ // Interactive document-interview wire shapes (WS5), re-exported from the shared contracts
2
+ // package (the single source of truth across the wire boundary). The SPA imports these through
3
+ // `~/types/domain` like every other domain type.
4
+ export type {
5
+ AnswerDocInterviewInput,
6
+ DocInterviewQa,
7
+ DocInterviewSession,
8
+ DocInterviewStatus,
9
+ } from '@cat-factory/contracts'
@@ -138,3 +138,4 @@ export type * from './services'
138
138
  export type * from './recurring'
139
139
  export type * from './tracker'
140
140
  export type * from './initiative'
141
+ export type * from './doc-interview'
@@ -2718,6 +2718,22 @@
2718
2718
  "body": "Slack notifications will stop until you reconnect."
2719
2719
  }
2720
2720
  },
2721
+ "docInterview": {
2722
+ "title": "Refine the document",
2723
+ "subtitle": "Answer the interviewer's questions so it can shape the document",
2724
+ "intro": "The interviewer is refining this document before it's written. Answer its questions to shape the scope, audience and structure, then continue — or proceed to draft with what it has.",
2725
+ "empty": "No interview found for this block.",
2726
+ "brief": "Authoring brief",
2727
+ "converged": "No questions are pending. The interviewer has what it needs and the document is being drafted.",
2728
+ "answerPlaceholder": "Your answer",
2729
+ "hint": "Continue lets the interviewer ask follow-ups; Proceed drafts with the answers so far.",
2730
+ "proceed": "Proceed to draft",
2731
+ "continue": "Continue",
2732
+ "status": {
2733
+ "awaiting": "Awaiting answers",
2734
+ "done": "Done"
2735
+ }
2736
+ },
2721
2737
  "documents": {
2722
2738
  "picker": {
2723
2739
  "searchPlaceholder": "Search pages or paste a URL/ID…",
@@ -2645,6 +2645,22 @@
2645
2645
  "body": "Las notificaciones de Slack se detendrán hasta que vuelvas a conectar."
2646
2646
  }
2647
2647
  },
2648
+ "docInterview": {
2649
+ "title": "Refinar el documento",
2650
+ "subtitle": "Responde a las preguntas del entrevistador para dar forma al documento",
2651
+ "intro": "El entrevistador está refinando este documento antes de redactarlo. Responde a sus preguntas para definir el alcance, el público y la estructura, y luego continúa — o procede a redactar con lo que tiene.",
2652
+ "empty": "No se encontró ninguna entrevista para este bloque.",
2653
+ "brief": "Resumen de redacción",
2654
+ "converged": "No hay preguntas pendientes. El entrevistador tiene lo que necesita y el documento se está redactando.",
2655
+ "answerPlaceholder": "Tu respuesta",
2656
+ "hint": "Continuar permite al entrevistador hacer preguntas de seguimiento; Proceder redacta con las respuestas actuales.",
2657
+ "proceed": "Proceder a redactar",
2658
+ "continue": "Continuar",
2659
+ "status": {
2660
+ "awaiting": "Esperando respuestas",
2661
+ "done": "Hecho"
2662
+ }
2663
+ },
2648
2664
  "documents": {
2649
2665
  "picker": {
2650
2666
  "searchPlaceholder": "Busca páginas o pega una URL o ID…",
@@ -2645,6 +2645,22 @@
2645
2645
  "body": "Les notifications Slack seront interrompues jusqu'à la reconnexion."
2646
2646
  }
2647
2647
  },
2648
+ "docInterview": {
2649
+ "title": "Affiner le document",
2650
+ "subtitle": "Répondez aux questions de l'intervieweur pour façonner le document",
2651
+ "intro": "L'intervieweur affine ce document avant sa rédaction. Répondez à ses questions pour définir la portée, le public et la structure, puis continuez — ou procédez à la rédaction avec ce qu'il a.",
2652
+ "empty": "Aucun entretien trouvé pour ce bloc.",
2653
+ "brief": "Brief de rédaction",
2654
+ "converged": "Aucune question en attente. L'intervieweur a ce qu'il lui faut et le document est en cours de rédaction.",
2655
+ "answerPlaceholder": "Votre réponse",
2656
+ "hint": "Continuer permet à l'intervieweur de poser des questions complémentaires ; Procéder rédige avec les réponses actuelles.",
2657
+ "proceed": "Procéder à la rédaction",
2658
+ "continue": "Continuer",
2659
+ "status": {
2660
+ "awaiting": "En attente de réponses",
2661
+ "done": "Terminé"
2662
+ }
2663
+ },
2648
2664
  "documents": {
2649
2665
  "picker": {
2650
2666
  "searchPlaceholder": "Recherchez des pages ou collez une URL ou un ID…",