agent-facets 0.2.2 → 0.3.3

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 (59) hide show
  1. package/bin/facet +181 -0
  2. package/bin/package.json +3 -0
  3. package/package.json +17 -37
  4. package/postinstall.mjs +210 -0
  5. package/.package.json.bak +0 -44
  6. package/.turbo/turbo-build.log +0 -3
  7. package/CHANGELOG.md +0 -85
  8. package/bunfig.toml +0 -2
  9. package/dist/facet +0 -0
  10. package/src/__tests__/cli.test.ts +0 -195
  11. package/src/__tests__/create-build.test.ts +0 -227
  12. package/src/__tests__/edit-integration.test.ts +0 -171
  13. package/src/__tests__/resolve-dir.test.ts +0 -95
  14. package/src/commands/build.ts +0 -58
  15. package/src/commands/create/index.ts +0 -76
  16. package/src/commands/create/types.ts +0 -9
  17. package/src/commands/create/wizard.tsx +0 -75
  18. package/src/commands/create-scaffold.ts +0 -184
  19. package/src/commands/edit/index.ts +0 -144
  20. package/src/commands/edit/wizard.tsx +0 -74
  21. package/src/commands/resolve-dir.ts +0 -98
  22. package/src/commands.ts +0 -40
  23. package/src/help.ts +0 -43
  24. package/src/index.ts +0 -10
  25. package/src/run.ts +0 -82
  26. package/src/suggest.ts +0 -35
  27. package/src/tui/components/asset-description.tsx +0 -17
  28. package/src/tui/components/asset-field-picker.tsx +0 -78
  29. package/src/tui/components/asset-inline-input.tsx +0 -91
  30. package/src/tui/components/asset-item.tsx +0 -44
  31. package/src/tui/components/asset-section.tsx +0 -191
  32. package/src/tui/components/button.tsx +0 -92
  33. package/src/tui/components/editable-field.tsx +0 -172
  34. package/src/tui/components/exit-toast.tsx +0 -20
  35. package/src/tui/components/reconciliation-item.tsx +0 -129
  36. package/src/tui/components/stage-row.tsx +0 -45
  37. package/src/tui/components/version-selector.tsx +0 -79
  38. package/src/tui/context/focus-mode-context.ts +0 -36
  39. package/src/tui/context/focus-order-context.ts +0 -68
  40. package/src/tui/context/form-state-context.ts +0 -260
  41. package/src/tui/editor.ts +0 -40
  42. package/src/tui/gradient.ts +0 -1
  43. package/src/tui/hooks/use-exit-keys.ts +0 -75
  44. package/src/tui/hooks/use-navigation-keys.ts +0 -34
  45. package/src/tui/layouts/wizard-layout.tsx +0 -41
  46. package/src/tui/theme.ts +0 -1
  47. package/src/tui/views/build/build-view.tsx +0 -152
  48. package/src/tui/views/create/confirm-view.tsx +0 -74
  49. package/src/tui/views/create/create-view.tsx +0 -158
  50. package/src/tui/views/create/wizard.tsx +0 -97
  51. package/src/tui/views/edit/edit-confirm-view.tsx +0 -93
  52. package/src/tui/views/edit/edit-types.ts +0 -34
  53. package/src/tui/views/edit/edit-view.tsx +0 -140
  54. package/src/tui/views/edit/manifest-to-form.ts +0 -38
  55. package/src/tui/views/edit/reconciliation-view.tsx +0 -170
  56. package/src/tui/views/edit/use-edit-session.ts +0 -125
  57. package/src/tui/views/edit/wizard.tsx +0 -129
  58. package/src/version.ts +0 -3
  59. package/tsconfig.json +0 -4
@@ -1,152 +0,0 @@
1
- import {
2
- BUILD_STAGES,
3
- type BuildProgress,
4
- type BuildStage,
5
- runBuildPipeline,
6
- writeBuildOutput,
7
- } from '@agent-facets/core'
8
- import { Box, Text, useApp } from 'ink'
9
- import { useCallback, useEffect, useMemo, useState } from 'react'
10
- import type { Stage } from '../../components/stage-row.tsx'
11
- import { StageRow } from '../../components/stage-row.tsx'
12
- import { THEME } from '../../theme.ts'
13
-
14
- interface BuildViewResult {
15
- name: string
16
- version: string
17
- files: string[]
18
- archiveFilename: string
19
- integrity: string
20
- warnings: string[]
21
- }
22
-
23
- export function BuildView({
24
- rootDir,
25
- onSuccess,
26
- onFailure,
27
- }: {
28
- rootDir: string
29
- onSuccess?: (name: string, version: string, fileCount: number, integrity: string) => void
30
- onFailure?: (errorCount: number) => void
31
- }) {
32
- const { exit } = useApp()
33
- const [stages, setStages] = useState<Stage[]>(BUILD_STAGES.map((label) => ({ label, status: 'pending' as const })))
34
- const [result, setResult] = useState<BuildViewResult | null>(null)
35
- const [warnings, setWarnings] = useState<string[]>([])
36
-
37
- // Deferred exit: set this to an Error to exit after the next render cycle,
38
- // ensuring error/stage state updates are painted before Ink unmounts.
39
- const [pendingExit, setPendingExit] = useState<Error | null>(null)
40
-
41
- useEffect(() => {
42
- if (pendingExit) {
43
- exit(pendingExit)
44
- }
45
- }, [pendingExit, exit])
46
-
47
- // Build a stable lookup from stage label to index
48
- const stageIndexMap = useMemo(() => Object.fromEntries(BUILD_STAGES.map((label, i) => [label, i])), [])
49
-
50
- const updateStage = useCallback(
51
- (label: BuildStage, update: Partial<Stage>) => {
52
- const index = stageIndexMap[label]
53
- if (index !== undefined) {
54
- setStages((prev) => prev.map((s, i) => (i === index ? { ...s, ...update } : s)))
55
- }
56
- },
57
- [stageIndexMap],
58
- )
59
-
60
- useEffect(() => {
61
- async function run() {
62
- const pipelineResult = await runBuildPipeline(rootDir, (progress: BuildProgress) => {
63
- updateStage(progress.stage, {
64
- status: progress.status === 'running' ? 'running' : progress.status === 'done' ? 'done' : 'failed',
65
- })
66
- })
67
-
68
- setWarnings(pipelineResult.warnings)
69
-
70
- if (!pipelineResult.ok) {
71
- const formatted = pipelineResult.errors.map((e) => e.message)
72
- // Find the stage that failed and attach errors to it
73
- setStages((prev) => prev.map((s) => (s.status === 'failed' ? { ...s, errors: formatted } : s)))
74
- onFailure?.(pipelineResult.errors.length)
75
- // Defer exit so React renders the errors and failed stage status first
76
- setPendingExit(new Error('Build failed'))
77
- return
78
- }
79
-
80
- // Writing output stage — handled here, not by the pipeline
81
- updateStage('Writing output', { status: 'running' })
82
- try {
83
- await writeBuildOutput(pipelineResult, rootDir)
84
-
85
- const files = Object.keys(pipelineResult.assetHashes).sort()
86
-
87
- updateStage('Writing output', { status: 'done' })
88
- setResult({
89
- name: pipelineResult.data.name,
90
- version: pipelineResult.data.version,
91
- files,
92
- archiveFilename: pipelineResult.archiveFilename,
93
- integrity: pipelineResult.integrity,
94
- warnings: pipelineResult.warnings,
95
- })
96
- onSuccess?.(pipelineResult.data.name, pipelineResult.data.version, files.length, pipelineResult.integrity)
97
- exit()
98
- } catch (err) {
99
- updateStage('Writing output', { status: 'failed', detail: String(err) })
100
- setPendingExit(err instanceof Error ? err : new Error(String(err)))
101
- }
102
- }
103
-
104
- run()
105
- }, [rootDir, exit, onSuccess, onFailure, updateStage])
106
-
107
- return (
108
- <Box flexDirection="column" padding={1} gap={1}>
109
- <Text bold color={THEME.brand}>
110
- Building facet...
111
- </Text>
112
-
113
- <Box flexDirection="column">
114
- {stages.map((s) => (
115
- <StageRow key={s.label} stage={s} />
116
- ))}
117
- </Box>
118
-
119
- {warnings.length > 0 && (
120
- <Box flexDirection="column">
121
- {warnings.map((w) => (
122
- <Text key={w} color={THEME.warning}>
123
- {' '}
124
- ⚠ {w}
125
- </Text>
126
- ))}
127
- </Box>
128
- )}
129
-
130
- {result && (
131
- <Box flexDirection="column">
132
- <Text color={THEME.success} bold>
133
- Built successfully → dist/
134
- </Text>
135
- <Text> {result.archiveFilename}</Text>
136
- <Text color={THEME.hint}> Archive contents:</Text>
137
- {result.files.map((f) => (
138
- <Text key={f}> {f}</Text>
139
- ))}
140
- <Box marginTop={1}>
141
- <Text color={THEME.hint}>
142
- {result.files.length} asset{result.files.length !== 1 ? 's' : ''} · {result.integrity}
143
- </Text>
144
- </Box>
145
- <Box marginTop={1}>
146
- <Text color={THEME.hint}>Next: facet publish (coming soon)</Text>
147
- </Box>
148
- </Box>
149
- )}
150
- </Box>
151
- )
152
- }
@@ -1,74 +0,0 @@
1
- import { Box, Text } from 'ink'
2
- import { useEffect } from 'react'
3
- import type { CreateOptions } from '../../../commands/create-scaffold.ts'
4
- import { previewFiles } from '../../../commands/create-scaffold.ts'
5
- import { Button } from '../../components/button.tsx'
6
- import { useFocusOrder } from '../../context/focus-order-context.ts'
7
- import { WizardLayout } from '../../layouts/wizard-layout.tsx'
8
- import { THEME } from '../../theme.ts'
9
-
10
- function SummaryField({ label, value }: { label: string; value: string }) {
11
- return (
12
- <Box gap={1}>
13
- <Text color={THEME.success}>✓</Text>
14
- <Text bold>{label}:</Text>
15
- <Text>{value}</Text>
16
- </Box>
17
- )
18
- }
19
-
20
- export function ConfirmView({
21
- opts,
22
- onConfirm,
23
- onBack,
24
- }: {
25
- opts: CreateOptions
26
- onConfirm: () => void
27
- onBack: () => void
28
- }) {
29
- const files = previewFiles(opts)
30
- const { setFocusIds, focus, focusedId } = useFocusOrder()
31
-
32
- useEffect(() => {
33
- setFocusIds(['confirm-yes', 'confirm-no'])
34
- focus('confirm-yes')
35
- }, [setFocusIds, focus])
36
-
37
- return (
38
- <WizardLayout>
39
- <Box flexDirection="column" marginLeft={2}>
40
- <SummaryField label="Name" value={opts.name} />
41
- <SummaryField label="Description" value={opts.description} />
42
- <SummaryField label="Version" value={opts.version} />
43
- {opts.skills.length > 0 && <SummaryField label="Skills" value={opts.skills.join(', ')} />}
44
- {opts.agents.length > 0 && <SummaryField label="Agents" value={opts.agents.join(', ')} />}
45
- {opts.commands.length > 0 && <SummaryField label="Commands" value={opts.commands.join(', ')} />}
46
- </Box>
47
-
48
- <Box flexDirection="column" borderStyle="round" borderColor={THEME.success} paddingX={2} paddingY={1} gap={0}>
49
- <Text bold color={THEME.success}>
50
- Files to create:
51
- </Text>
52
- {files.map((f) => (
53
- <Text key={f}> {f}</Text>
54
- ))}
55
- </Box>
56
-
57
- <Box gap={2} marginTop={1}>
58
- <Button
59
- id="confirm-yes"
60
- label="[ Yes, create ]"
61
- color={THEME.success}
62
- gradient={focusedId === 'confirm-yes'}
63
- animateGradient={focusedId === 'confirm-yes'}
64
- onPress={onConfirm}
65
- />
66
- <Button id="confirm-no" label="[ No, go back ]" color={THEME.warning} onPress={onBack} />
67
- </Box>
68
-
69
- <Box marginTop={1}>
70
- <Text dimColor>← → to switch, Enter to confirm</Text>
71
- </Box>
72
- </WizardLayout>
73
- )
74
- }
@@ -1,158 +0,0 @@
1
- import { Box, Text } from 'ink'
2
- import { useCallback, useEffect } from 'react'
3
- import { ASSET_LABELS, ASSET_TYPES } from '../../../commands/create/types.ts'
4
- import { DEFAULT_VERSION, isValidKebabCase } from '../../../commands/create-scaffold.ts'
5
- import { AssetSection } from '../../components/asset-section.tsx'
6
- import { Button } from '../../components/button.tsx'
7
- import { EditableField } from '../../components/editable-field.tsx'
8
- import { useFocusOrder } from '../../context/focus-order-context.ts'
9
- import { useFormState } from '../../context/form-state-context.ts'
10
- import { WizardLayout } from '../../layouts/wizard-layout.tsx'
11
-
12
- function computeFocusIds(form: ReturnType<typeof useFormState>['form']): string[] {
13
- const ids: string[] = ['field-name', 'field-description', 'field-version']
14
-
15
- for (const type of ASSET_TYPES) {
16
- const section = form.assets[type]
17
-
18
- for (let i = 0; i < section.items.length; i++) {
19
- ids.push(`item-${type}-${i}`)
20
- }
21
-
22
- ids.push(`add-${type}`)
23
- }
24
-
25
- ids.push('create-btn')
26
-
27
- return ids
28
- }
29
-
30
- export function CreateView({
31
- onSubmit,
32
- onEditDescription,
33
- }: {
34
- onSubmit: () => void
35
- onEditDescription?: (section: import('../../context/form-state-context.ts').AssetSectionKey, name: string) => void
36
- }) {
37
- const { form } = useFormState()
38
- const { setFocusIds, focus, focusedId } = useFocusOrder()
39
-
40
- const validateKebab = useCallback((v: string) => {
41
- if (!v) return undefined
42
- if (!isValidKebabCase(v)) return 'Must be kebab-case (e.g., my-facet)'
43
- return undefined
44
- }, [])
45
-
46
- // Derived state from context
47
- const nameConfirmed = form.fields.name.status === 'confirmed'
48
- const descriptionConfirmed = form.fields.description.status === 'confirmed'
49
- const versionConfirmed = form.fields.version.status === 'confirmed'
50
-
51
- // Settled = confirmed or has a value (being revised). Used for dimming.
52
- const nameSettled = nameConfirmed || !!form.fields.name.value
53
- const descriptionSettled = descriptionConfirmed || !!form.fields.description.value
54
- const versionSettled = versionConfirmed || !!form.fields.version.value
55
-
56
- const descriptionReady = nameSettled
57
- const versionReady = nameSettled && descriptionSettled
58
- const assetsReady = nameSettled && descriptionSettled && versionSettled
59
-
60
- const totalAssets = form.assets.skill.items.length + form.assets.command.items.length + form.assets.agent.items.length
61
- const canCreate = assetsReady && totalAssets > 0
62
-
63
- // Recompute focus order
64
- useEffect(() => {
65
- const ids = computeFocusIds(form)
66
- setFocusIds(ids)
67
-
68
- if (focusedId && !ids.includes(focusedId)) {
69
- focus(ids[0] ?? '')
70
- }
71
- }, [form, setFocusIds, focus, focusedId])
72
-
73
- // Auto-focus name field on mount
74
- useEffect(() => {
75
- if (!focusedId) {
76
- focus('field-name')
77
- }
78
- }, [focusedId, focus])
79
-
80
- return (
81
- <WizardLayout>
82
- <EditableField
83
- field="name"
84
- label="Name"
85
- placeholder="my-facet"
86
- hint="kebab-case"
87
- validate={validateKebab}
88
- onConfirm={() => focus('field-description')}
89
- />
90
-
91
- <EditableField
92
- field="description"
93
- label="Description"
94
- placeholder="A brief description of what this facet does"
95
- dimmed={!descriptionReady}
96
- onConfirm={() => focus('field-version')}
97
- />
98
-
99
- <EditableField
100
- field="version"
101
- label="Version"
102
- hint="SemVer N.N.N"
103
- defaultValue={DEFAULT_VERSION}
104
- dimmed={!versionReady}
105
- validate={(v) => (/^\d+\.\d+\.\d+$/.test(v) ? undefined : `Must be SemVer (e.g., ${DEFAULT_VERSION})`)}
106
- onConfirm={() => focus(`add-${ASSET_TYPES[0]}`)}
107
- />
108
-
109
- {ASSET_TYPES.map((type) => (
110
- <Box key={type} marginTop={0}>
111
- <AssetSection
112
- section={type}
113
- label={ASSET_LABELS[type]}
114
- defaultName={form.assets[type].items.length === 0 ? form.fields.name.value : undefined}
115
- dimmed={!assetsReady}
116
- onEditDescription={onEditDescription}
117
- validate={(v) => {
118
- if (!isValidKebabCase(v)) return 'Must be kebab-case'
119
- const editing = form.assets[type].editing
120
- if (form.assets[type].items.some((item) => item === v && item !== editing)) return `"${v}" already exists`
121
- return undefined
122
- }}
123
- />
124
- </Box>
125
- ))}
126
-
127
- <Box marginTop={1}>
128
- <Button
129
- id="create-btn"
130
- label="[ Create facet ]"
131
- color="green"
132
- disabled={!canCreate}
133
- gradient={canCreate}
134
- animateGradient={canCreate && focusedId === 'create-btn'}
135
- onPress={onSubmit}
136
- />
137
- </Box>
138
-
139
- {!canCreate && (
140
- <Box marginLeft={2}>
141
- <Text dimColor>
142
- {!nameConfirmed
143
- ? 'Enter a name to continue'
144
- : !descriptionConfirmed
145
- ? 'Enter a description to continue'
146
- : !versionConfirmed
147
- ? 'Enter a version to continue'
148
- : 'Add at least one skill, agent, or command'}
149
- </Text>
150
- </Box>
151
- )}
152
-
153
- <Box marginTop={1}>
154
- <Text dimColor>↑ ↓ to navigate, Enter to select/edit, Esc Esc to exit</Text>
155
- </Box>
156
- </WizardLayout>
157
- )
158
- }
@@ -1,97 +0,0 @@
1
- import { useApp } from 'ink'
2
- import { useCallback, useEffect, useState } from 'react'
3
- import type { CreateOptions } from '../../../commands/create-scaffold.ts'
4
- import { FocusModeProvider, useFocusMode } from '../../context/focus-mode-context.ts'
5
- import { FocusOrderProvider, useFocusOrder } from '../../context/focus-order-context.ts'
6
- import type { AssetSectionKey, FormState } from '../../context/form-state-context.ts'
7
- import { FormStateProvider, useFormState } from '../../context/form-state-context.ts'
8
- import { useExitKeys } from '../../hooks/use-exit-keys.ts'
9
- import { useNavigationKeys } from '../../hooks/use-navigation-keys.ts'
10
- import { ConfirmView } from './confirm-view.tsx'
11
- import { CreateView } from './create-view.tsx'
12
-
13
- export interface WizardSnapshot {
14
- form: FormState
15
- focusedId: string | null
16
- selectedItem?: {
17
- section: AssetSectionKey
18
- name: string
19
- field: 'name' | 'description'
20
- }
21
- }
22
-
23
- export interface CreateWizardProps {
24
- onComplete: (opts: CreateOptions) => void
25
- onCancel: () => void
26
- snapshot?: WizardSnapshot
27
- onSnapshot?: (snapshot: WizardSnapshot) => void
28
- onRequestEditor?: (section: AssetSectionKey, name: string, description: string) => void
29
- }
30
-
31
- function CreateWizardInner({ onComplete, onCancel, snapshot, onSnapshot, onRequestEditor }: CreateWizardProps) {
32
- const { exit } = useApp()
33
- const { setMode } = useFocusMode()
34
- const { form, toCreateOptions } = useFormState()
35
- const { focusedId } = useFocusOrder()
36
-
37
- const [confirming, setConfirming] = useState(false)
38
-
39
- // Keep the parent informed of current state for snapshot
40
- useEffect(() => {
41
- onSnapshot?.({ form, focusedId, selectedItem: snapshot?.selectedItem })
42
- }, [form, focusedId, onSnapshot, snapshot?.selectedItem])
43
-
44
- const cancel = useCallback(() => {
45
- onCancel()
46
- exit()
47
- }, [onCancel, exit])
48
-
49
- useExitKeys(cancel)
50
- useNavigationKeys()
51
-
52
- const handleEditDescription = useCallback(
53
- (section: AssetSectionKey, name: string) => {
54
- const description = form.assets[section].descriptions[name] ?? ''
55
- onRequestEditor?.(section, name, description)
56
- },
57
- [form, onRequestEditor],
58
- )
59
-
60
- if (confirming) {
61
- return (
62
- <ConfirmView
63
- opts={toCreateOptions()}
64
- onConfirm={() => {
65
- onComplete(toCreateOptions())
66
- exit()
67
- }}
68
- onBack={() => {
69
- setConfirming(false)
70
- setMode('form-navigation')
71
- }}
72
- />
73
- )
74
- }
75
-
76
- return (
77
- <CreateView
78
- onSubmit={() => {
79
- setConfirming(true)
80
- setMode('form-confirmation')
81
- }}
82
- onEditDescription={handleEditDescription}
83
- />
84
- )
85
- }
86
-
87
- export function CreateWizard(props: CreateWizardProps) {
88
- return (
89
- <FocusModeProvider>
90
- <FocusOrderProvider initialFocusId={props.snapshot?.focusedId}>
91
- <FormStateProvider initialState={props.snapshot?.form}>
92
- <CreateWizardInner {...props} />
93
- </FormStateProvider>
94
- </FocusOrderProvider>
95
- </FocusModeProvider>
96
- )
97
- }
@@ -1,93 +0,0 @@
1
- import { Box, Text } from 'ink'
2
- import { useEffect } from 'react'
3
- import { truncateDescription } from '../../components/asset-description.tsx'
4
- import { Button } from '../../components/button.tsx'
5
- import { useFocusOrder } from '../../context/focus-order-context.ts'
6
- import type { AssetSectionKey } from '../../context/form-state-context.ts'
7
- import { useFormState } from '../../context/form-state-context.ts'
8
- import { THEME } from '../../theme.ts'
9
-
10
- const ASSET_TYPES: AssetSectionKey[] = ['skill', 'command', 'agent']
11
- const ASSET_LABELS: Record<AssetSectionKey, string> = {
12
- skill: 'Skills',
13
- command: 'Commands',
14
- agent: 'Agents',
15
- }
16
-
17
- export function EditConfirmView({ onConfirm, onBack }: { onConfirm: () => void; onBack: () => void }) {
18
- const { form } = useFormState()
19
- const { setFocusIds, focus, focusedId } = useFocusOrder()
20
-
21
- useEffect(() => {
22
- setFocusIds(['edit-apply-btn', 'edit-back-btn'])
23
- focus('edit-apply-btn')
24
- }, [setFocusIds, focus])
25
-
26
- return (
27
- <Box flexDirection="column" padding={1} gap={1}>
28
- <Text bold color={THEME.brand}>
29
- Review changes
30
- </Text>
31
-
32
- <Box flexDirection="column">
33
- <Box gap={1}>
34
- <Text bold>Name:</Text>
35
- <Text>{form.fields.name.value || '(none)'}</Text>
36
- </Box>
37
- <Box gap={1}>
38
- <Text bold>Description:</Text>
39
- <Text>{truncateDescription(form.fields.description.value || '(none)')}</Text>
40
- </Box>
41
- <Box gap={1}>
42
- <Text bold>Version:</Text>
43
- <Text>{form.fields.version.value || '(none)'}</Text>
44
- </Box>
45
- </Box>
46
-
47
- {ASSET_TYPES.map((type) => {
48
- const section = form.assets[type]
49
- return (
50
- <Box key={type} flexDirection="column">
51
- <Text bold>{ASSET_LABELS[type]}:</Text>
52
- {section.items.length === 0 ? (
53
- <Box marginLeft={2}>
54
- <Text dimColor>(none)</Text>
55
- </Box>
56
- ) : (
57
- section.items.map((item) => {
58
- const desc = section.descriptions[item] ?? `A ${item} ${type}`
59
- return (
60
- <Box key={item} flexDirection="column" marginLeft={2}>
61
- <Box gap={1}>
62
- <Text color={THEME.success}>●</Text>
63
- <Text>{item}</Text>
64
- </Box>
65
- <Box marginLeft={3}>
66
- <Text dimColor>{truncateDescription(desc)}</Text>
67
- </Box>
68
- </Box>
69
- )
70
- })
71
- )}
72
- </Box>
73
- )
74
- })}
75
-
76
- <Box marginTop={1} gap={2}>
77
- <Button
78
- id="edit-apply-btn"
79
- label="[ Yes, apply ]"
80
- color={THEME.success}
81
- gradient={focusedId === 'edit-apply-btn'}
82
- animateGradient={focusedId === 'edit-apply-btn'}
83
- onPress={onConfirm}
84
- />
85
- <Button id="edit-back-btn" label="[ No, go back ]" color={THEME.warning} onPress={onBack} />
86
- </Box>
87
-
88
- <Box>
89
- <Text dimColor>← → switch · Enter confirm</Text>
90
- </Box>
91
- </Box>
92
- )
93
- }
@@ -1,34 +0,0 @@
1
- import type { AssetType, FacetManifest } from '@agent-facets/core'
2
-
3
- /** A reconciliation item that needs a resolution before editing can proceed. */
4
- export type ReconciliationItem =
5
- | { kind: 'addition'; type: AssetType; name: string; path: string }
6
- | { kind: 'missing'; type: AssetType; name: string; expectedPath: string }
7
- | { kind: 'front-matter'; type: AssetType; name: string; path: string }
8
-
9
- /** The resolution chosen for a reconciliation item. */
10
- export type ReconciliationResolution =
11
- | { action: 'add-to-manifest' }
12
- | { action: 'ignore' }
13
- | { action: 'scaffold-template' }
14
- | { action: 'remove-from-manifest' }
15
- | { action: 'strip-front-matter' }
16
-
17
- /** All data needed to run the edit TUI. */
18
- export interface EditContext {
19
- rootDir: string
20
- manifest: FacetManifest
21
- reconciliationItems: ReconciliationItem[]
22
- }
23
-
24
- /** The result of the edit wizard — either applied changes or cancelled. */
25
- export type EditResult =
26
- | { outcome: 'applied'; manifest: FacetManifest; operations: EditOperation[] }
27
- | { outcome: 'cancelled' }
28
-
29
- /** A file operation to perform on confirmation. */
30
- export type EditOperation =
31
- | { op: 'write-manifest' }
32
- | { op: 'scaffold'; type: AssetType; name: string }
33
- | { op: 'delete-file'; type: AssetType; name: string }
34
- | { op: 'strip-front-matter'; type: AssetType; name: string; path: string }