@synergenius/flow-weaver-pack-weaver 0.9.199 → 0.9.201

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 (181) hide show
  1. package/dist/ai-chat-provider.js +5 -5
  2. package/dist/ai-chat-provider.js.map +1 -1
  3. package/dist/bot/acceptance-merge.d.ts +21 -0
  4. package/dist/bot/acceptance-merge.d.ts.map +1 -0
  5. package/dist/bot/acceptance-merge.js +46 -0
  6. package/dist/bot/acceptance-merge.js.map +1 -0
  7. package/dist/bot/ai-client.d.ts +14 -2
  8. package/dist/bot/ai-client.d.ts.map +1 -1
  9. package/dist/bot/ai-client.js +71 -24
  10. package/dist/bot/ai-client.js.map +1 -1
  11. package/dist/bot/assistant-tools.js +3 -3
  12. package/dist/bot/assistant-tools.js.map +1 -1
  13. package/dist/bot/audit-logger.d.ts.map +1 -1
  14. package/dist/bot/audit-logger.js +34 -14
  15. package/dist/bot/audit-logger.js.map +1 -1
  16. package/dist/bot/audit-trail.d.ts +67 -0
  17. package/dist/bot/audit-trail.d.ts.map +1 -0
  18. package/dist/bot/audit-trail.js +153 -0
  19. package/dist/bot/audit-trail.js.map +1 -0
  20. package/dist/bot/behavior-defaults.d.ts +1 -1
  21. package/dist/bot/behavior-defaults.d.ts.map +1 -1
  22. package/dist/bot/behavior-defaults.js +7 -3
  23. package/dist/bot/behavior-defaults.js.map +1 -1
  24. package/dist/bot/capability-registry.d.ts +9 -0
  25. package/dist/bot/capability-registry.d.ts.map +1 -1
  26. package/dist/bot/capability-registry.js +81 -27
  27. package/dist/bot/capability-registry.js.map +1 -1
  28. package/dist/bot/capability-types.d.ts +10 -0
  29. package/dist/bot/capability-types.d.ts.map +1 -1
  30. package/dist/bot/cli-provider.d.ts.map +1 -1
  31. package/dist/bot/cli-provider.js +8 -7
  32. package/dist/bot/cli-provider.js.map +1 -1
  33. package/dist/bot/preflight.d.ts +48 -0
  34. package/dist/bot/preflight.d.ts.map +1 -0
  35. package/dist/bot/preflight.js +247 -0
  36. package/dist/bot/preflight.js.map +1 -0
  37. package/dist/bot/provider-shim.d.ts +74 -0
  38. package/dist/bot/provider-shim.d.ts.map +1 -0
  39. package/dist/bot/provider-shim.js +176 -0
  40. package/dist/bot/provider-shim.js.map +1 -0
  41. package/dist/bot/runner.d.ts +2 -0
  42. package/dist/bot/runner.d.ts.map +1 -1
  43. package/dist/bot/runner.js +60 -17
  44. package/dist/bot/runner.js.map +1 -1
  45. package/dist/bot/step-executor.d.ts.map +1 -1
  46. package/dist/bot/step-executor.js +72 -115
  47. package/dist/bot/step-executor.js.map +1 -1
  48. package/dist/bot/swarm-controller.d.ts +2 -0
  49. package/dist/bot/swarm-controller.d.ts.map +1 -1
  50. package/dist/bot/swarm-controller.js +92 -20
  51. package/dist/bot/swarm-controller.js.map +1 -1
  52. package/dist/bot/task-create-handler.d.ts +37 -0
  53. package/dist/bot/task-create-handler.d.ts.map +1 -0
  54. package/dist/bot/task-create-handler.js +124 -0
  55. package/dist/bot/task-create-handler.js.map +1 -0
  56. package/dist/bot/task-store.d.ts +1 -0
  57. package/dist/bot/task-store.d.ts.map +1 -1
  58. package/dist/bot/task-store.js +67 -0
  59. package/dist/bot/task-store.js.map +1 -1
  60. package/dist/bot/types.d.ts +1 -1
  61. package/dist/bot/types.d.ts.map +1 -1
  62. package/dist/bot/weaver-tools.d.ts.map +1 -1
  63. package/dist/bot/weaver-tools.js +7 -39
  64. package/dist/bot/weaver-tools.js.map +1 -1
  65. package/dist/node-types/agent-execute.d.ts +25 -8
  66. package/dist/node-types/agent-execute.d.ts.map +1 -1
  67. package/dist/node-types/agent-execute.js +89 -23
  68. package/dist/node-types/agent-execute.js.map +1 -1
  69. package/dist/node-types/bot-report.d.ts.map +1 -1
  70. package/dist/node-types/bot-report.js +24 -3
  71. package/dist/node-types/bot-report.js.map +1 -1
  72. package/dist/node-types/plan-task.d.ts +8 -17
  73. package/dist/node-types/plan-task.d.ts.map +1 -1
  74. package/dist/node-types/plan-task.js +217 -256
  75. package/dist/node-types/plan-task.js.map +1 -1
  76. package/dist/node-types/review-result.js +8 -6
  77. package/dist/node-types/review-result.js.map +1 -1
  78. package/dist/palindrome.d.ts +9 -0
  79. package/dist/palindrome.d.ts.map +1 -0
  80. package/dist/palindrome.js +14 -0
  81. package/dist/palindrome.js.map +1 -0
  82. package/dist/ui/approval-card.js +91 -82
  83. package/dist/ui/bot-activity.js +73 -56
  84. package/dist/ui/bot-config.js +48 -31
  85. package/dist/ui/bot-dashboard.js +52 -36
  86. package/dist/ui/bot-panel.js +230 -228
  87. package/dist/ui/bot-slot-card.js +100 -90
  88. package/dist/ui/bot-status.js +37 -15
  89. package/dist/ui/budget-bar.js +57 -31
  90. package/dist/ui/capability-editor.js +447 -378
  91. package/dist/ui/chat-task-result.js +78 -71
  92. package/dist/ui/decision-log.js +68 -81
  93. package/dist/ui/genesis-block.js +86 -95
  94. package/dist/ui/instance-stream-view.js +722 -0
  95. package/dist/ui/profile-card.js +96 -221
  96. package/dist/ui/profile-editor.js +532 -575
  97. package/dist/ui/settings-section.js +41 -45
  98. package/dist/ui/swarm-controls.js +212 -135
  99. package/dist/ui/swarm-dashboard.js +3992 -2715
  100. package/dist/ui/task-detail-view.js +415 -521
  101. package/dist/ui/task-editor.js +339 -390
  102. package/dist/ui/task-pool-list.js +60 -55
  103. package/dist/workflows/src/palindrome.d.ts +11 -0
  104. package/dist/workflows/src/palindrome.d.ts.map +1 -0
  105. package/dist/workflows/src/palindrome.js +16 -0
  106. package/dist/workflows/src/palindrome.js.map +1 -0
  107. package/dist/workflows/tests/palindrome.test.d.ts +2 -0
  108. package/dist/workflows/tests/palindrome.test.d.ts.map +1 -0
  109. package/dist/workflows/tests/palindrome.test.js +41 -0
  110. package/dist/workflows/tests/palindrome.test.js.map +1 -0
  111. package/dist/workflows/weaver-bot-batch.js +1 -1
  112. package/dist/workflows/weaver-bot-batch.js.map +1 -1
  113. package/dist/workflows/weaver-bot.js +1 -1
  114. package/dist/workflows/weaver-bot.js.map +1 -1
  115. package/flowweaver.manifest.json +1 -1
  116. package/package.json +8 -2
  117. package/src/ai-chat-provider.ts +5 -5
  118. package/src/bot/acceptance-merge.ts +62 -0
  119. package/src/bot/ai-client.ts +77 -21
  120. package/src/bot/assistant-tools.ts +3 -3
  121. package/src/bot/audit-logger.ts +42 -14
  122. package/src/bot/audit-trail.ts +211 -0
  123. package/src/bot/behavior-defaults.ts +7 -2
  124. package/src/bot/capability-registry.ts +84 -28
  125. package/src/bot/capability-types.ts +11 -0
  126. package/src/bot/cli-provider.ts +8 -7
  127. package/src/bot/preflight.ts +285 -0
  128. package/src/bot/provider-shim.ts +218 -0
  129. package/src/bot/runner.ts +68 -20
  130. package/src/bot/step-executor.ts +69 -127
  131. package/src/bot/swarm-controller.ts +94 -20
  132. package/src/bot/task-create-handler.ts +164 -0
  133. package/src/bot/task-store.ts +83 -0
  134. package/src/bot/types.ts +4 -1
  135. package/src/bot/weaver-tools.ts +7 -45
  136. package/src/node-types/agent-execute.ts +102 -16
  137. package/src/node-types/bot-report.ts +24 -3
  138. package/src/node-types/plan-task.ts +238 -280
  139. package/src/node-types/review-result.ts +8 -6
  140. package/src/palindrome.ts +14 -0
  141. package/src/ui/approval-card.tsx +78 -62
  142. package/src/ui/bot-activity.tsx +12 -10
  143. package/src/ui/bot-config.tsx +12 -10
  144. package/src/ui/bot-dashboard.tsx +13 -11
  145. package/src/ui/bot-panel.tsx +189 -171
  146. package/src/ui/bot-slot-card.tsx +125 -70
  147. package/src/ui/bot-status.tsx +4 -4
  148. package/src/ui/budget-bar.tsx +86 -25
  149. package/src/ui/capability-editor.tsx +392 -257
  150. package/src/ui/chat-task-result.tsx +81 -78
  151. package/src/ui/decision-log.tsx +76 -73
  152. package/src/ui/genesis-block.tsx +91 -61
  153. package/src/ui/instance-stream-view.tsx +861 -0
  154. package/src/ui/profile-card.tsx +195 -168
  155. package/src/ui/profile-editor.tsx +453 -370
  156. package/src/ui/settings-section.tsx +46 -39
  157. package/src/ui/swarm-controls.tsx +252 -123
  158. package/src/ui/swarm-dashboard.tsx +999 -466
  159. package/src/ui/task-detail-view.tsx +485 -428
  160. package/src/ui/task-editor.tsx +329 -271
  161. package/src/ui/task-pool-list.tsx +68 -62
  162. package/src/workflows/src/palindrome.ts +16 -0
  163. package/src/workflows/tests/palindrome.test.ts +49 -0
  164. package/src/workflows/weaver-bot-batch.ts +1 -1
  165. package/src/workflows/weaver-bot.ts +1 -1
  166. package/dist/ui/bot-constants.d.ts +0 -14
  167. package/dist/ui/bot-constants.d.ts.map +0 -1
  168. package/dist/ui/bot-constants.js +0 -189
  169. package/dist/ui/bot-constants.js.map +0 -1
  170. package/dist/ui/steer-api.d.ts +0 -7
  171. package/dist/ui/steer-api.d.ts.map +0 -1
  172. package/dist/ui/steer-api.js +0 -11
  173. package/dist/ui/steer-api.js.map +0 -1
  174. package/dist/ui/trace-to-timeline.d.ts +0 -91
  175. package/dist/ui/trace-to-timeline.d.ts.map +0 -1
  176. package/dist/ui/trace-to-timeline.js +0 -116
  177. package/dist/ui/trace-to-timeline.js.map +0 -1
  178. package/dist/ui/use-stream-timeline.d.ts +0 -50
  179. package/dist/ui/use-stream-timeline.d.ts.map +0 -1
  180. package/dist/ui/use-stream-timeline.js +0 -245
  181. package/dist/ui/use-stream-timeline.js.map +0 -1
@@ -4,15 +4,12 @@
4
4
  * Two-screen pattern:
5
5
  * List view → shows all capabilities (built-in read-only, custom editable)
6
6
  * Detail view → full form for creating or editing a capability
7
- *
8
- * Pattern: CommonJS require for platform deps, React.createElement throughout.
9
7
  */
10
- const React = require('react');
11
- const { useState, useCallback } = React;
12
- const {
8
+ import React, { useState, useCallback } from 'react';
9
+ import {
13
10
  Flex, Typography, Input, Button, IconButton, Icon, Field, Chip, Checkbox, Table, CodeBlock,
14
11
  ScrollArea, toast, usePackWorkspace,
15
- } = require('@fw/plugin-ui-kit');
12
+ } from '@fw/plugin-ui-kit';
16
13
 
17
14
  import { BUILT_IN_CAPABILITIES } from '../bot/capability-registry';
18
15
  import { PLAN_OPERATIONS } from '../bot/operations';
@@ -22,11 +19,18 @@ import { estimateTokens } from '../bot/estimate-tokens';
22
19
  // Types
23
20
  // ---------------------------------------------------------------------------
24
21
 
22
+ interface AcceptanceCheck {
23
+ name: string;
24
+ command: string;
25
+ condition?: string;
26
+ }
27
+
25
28
  interface CapabilityDef {
26
29
  name: string;
27
30
  description: string;
28
31
  prompt: string;
29
32
  tools?: string[];
33
+ acceptance?: AcceptanceCheck[];
30
34
  builtIn?: boolean;
31
35
  }
32
36
 
@@ -60,6 +64,7 @@ function CapabilityDetail({
60
64
  const [description, setDescription] = useState(cap?.description ?? '');
61
65
  const [prompt, setPrompt] = useState(cap?.prompt ?? '');
62
66
  const [tools, setTools] = useState(cap?.tools ?? ([] as string[]));
67
+ const [acceptance, setAcceptance] = useState<AcceptanceCheck[]>(cap?.acceptance ?? []);
63
68
 
64
69
  const readOnly = mode === 'view';
65
70
  const isCreate = mode === 'create';
@@ -85,144 +90,258 @@ function CapabilityDetail({
85
90
  description: description.trim(),
86
91
  prompt: prompt.trim() || `## ${description.trim()}`,
87
92
  tools: tools.length > 0 ? tools : undefined,
93
+ acceptance: acceptance.length > 0 ? acceptance.filter(a => a.name && a.command) : undefined,
88
94
  });
89
- }, [name, description, prompt, tools, isCreate, allNames, cap, onSubmit]);
95
+ }, [name, description, prompt, tools, acceptance, isCreate, allNames, cap, onSubmit]);
90
96
 
91
97
  const title = isCreate ? 'New Capability' : readOnly ? cap?.name ?? '' : `Edit: ${cap?.name ?? ''}`;
92
98
 
93
- return React.createElement(Flex, {
94
- variant: 'column-stretch-start-nowrap-0',
95
- style: { width: '100%', height: '100%', overflow: 'hidden' },
96
- },
97
- // Header
98
- React.createElement(Flex, {
99
- variant: 'row-center-space-between-nowrap-8',
100
- style: { padding: '8px 16px', flexShrink: 0, borderBottom: '1px solid var(--color-border-default)' },
101
- },
102
- React.createElement(Flex, { variant: 'row-center-start-nowrap-8' },
103
- React.createElement(IconButton, {
104
- icon: 'back', size: 'xs', variant: 'clear', onClick: onBack, title: 'Back to list',
105
- }),
106
- React.createElement(Typography, { variant: 'caption-thick', color: 'color-text-high' }, title),
107
- readOnly && React.createElement(Chip, {
108
- label: 'built-in', size: 'small', color: 'color-status-info',
109
- }),
110
- ),
111
- ),
112
-
113
- // Scrollable form
114
- React.createElement(ScrollArea, { style: { flex: 1, minHeight: 0 } },
115
- React.createElement(Flex, {
116
- variant: 'column-stretch-start-nowrap-10',
117
- style: { padding: '12px 16px' },
118
- },
119
- // Name
120
- React.createElement(Field, { label: 'Name' },
121
- readOnly
122
- ? React.createElement(Typography, {
123
- variant: 'smallCaption-thick', color: 'color-text-high',
124
- style: { padding: '4px 0' },
125
- }, cap?.name)
126
- : React.createElement(Input, {
127
- type: 'text', size: 'small',
128
- placeholder: 'e.g. api-design',
129
- value: name, onChange: (v: string) => setName(v),
130
- disabled: !isCreate,
131
- defaultBoxStyle: { flex: 1, minWidth: 0 }, inputBoxStyle: { maxWidth: 'none' },
132
- }),
133
- ),
134
-
135
- // Description
136
- React.createElement(Field, { label: 'Description' },
137
- readOnly
138
- ? React.createElement(Typography, {
139
- variant: 'smallCaption-regular', color: 'color-text-medium',
140
- style: { padding: '4px 0' },
141
- }, cap?.description)
142
- : React.createElement(Input, {
143
- type: 'text', size: 'small',
144
- placeholder: 'What this capability provides',
145
- value: description, onChange: (v: string) => setDescription(v),
146
- defaultBoxStyle: { flex: 1, minWidth: 0 }, inputBoxStyle: { maxWidth: 'none' },
147
- }),
148
- ),
149
-
150
- // Prompt
151
- React.createElement(Field, { label: 'Prompt', align: 'start' },
152
- readOnly
153
- ? React.createElement(Typography, {
154
- variant: 'smallCaption-regular', color: 'color-text-medium',
155
- style: { whiteSpace: 'pre-wrap', lineHeight: 1.5 },
156
- }, cap?.prompt)
157
- : React.createElement(Input, {
158
- type: 'text', size: 'small', multiline: true,
159
- placeholder: 'Knowledge content (markdown) injected into the system prompt',
160
- value: prompt, onChange: (v: string) => setPrompt(v),
161
- defaultBoxStyle: { flex: 1, minWidth: 0 },
162
- inputBoxStyle: { maxWidth: 'none', minHeight: 160 },
163
- }),
164
- ),
165
-
166
- // Tools
167
- React.createElement(Field, { label: 'Tools', align: 'start' },
168
- React.createElement(Table, {
169
- size: 'compact',
170
- showHeader: false,
171
- data: AVAILABLE_TOOLS.map((t: string) => ({
172
- tool: t,
173
- active: (readOnly ? (cap?.tools ?? []) : tools).includes(t),
174
- })),
175
- getRowKey: (row: { tool: string }) => row.tool,
176
- columns: [
177
- {
178
- key: 'check', header: '', width: '28px',
179
- render: (_v: unknown, row: { tool: string; active: boolean }) =>
180
- React.createElement('div', { style: { height: 20, display: 'flex', alignItems: 'center' } },
181
- React.createElement(Checkbox, {
182
- checked: row.active,
183
- disabled: readOnly,
184
- onChange: readOnly ? undefined : () => handleToggleTool(row.tool),
185
- size: 'sm',
186
- }),
99
+ return (
100
+ <Flex
101
+ variant="column-stretch-start-nowrap-0"
102
+ style={{ width: '100%', height: '100%', overflow: 'hidden' }}
103
+ >
104
+ {/* Header */}
105
+ <Flex
106
+ variant="row-center-space-between-nowrap-8"
107
+ style={{ padding: '8px 16px', flexShrink: 0, borderBottom: '1px solid var(--color-border-default)' }}
108
+ >
109
+ <Flex variant="row-center-start-nowrap-8">
110
+ <IconButton
111
+ icon="back"
112
+ size="xs"
113
+ variant="clear"
114
+ onClick={onBack}
115
+ title="Back to list"
116
+ />
117
+ <Typography variant="caption-thick" color="color-text-high">{title}</Typography>
118
+ {readOnly && (
119
+ <Chip label="built-in" size="small" color="color-status-info" />
120
+ )}
121
+ </Flex>
122
+ </Flex>
123
+
124
+ {/* Scrollable form */}
125
+ <ScrollArea style={{ flex: 1, minHeight: 0 }}>
126
+ <Flex
127
+ variant="column-stretch-start-nowrap-10"
128
+ style={{ padding: '12px 16px' }}
129
+ >
130
+ {/* Name */}
131
+ <Field label="Name">
132
+ {readOnly
133
+ ? (
134
+ <Typography
135
+ variant="smallCaption-thick"
136
+ color="color-text-high"
137
+ style={{ padding: '4px 0' }}
138
+ >
139
+ {cap?.name}
140
+ </Typography>
141
+ )
142
+ : (
143
+ <Input
144
+ type="text"
145
+ size="small"
146
+ placeholder="e.g. api-design"
147
+ value={name}
148
+ onChange={(v: string) => setName(v)}
149
+ disabled={!isCreate}
150
+ defaultBoxStyle={{ flex: 1, minWidth: 0 }}
151
+ inputBoxStyle={{ maxWidth: 'none' }}
152
+ />
153
+ )}
154
+ </Field>
155
+
156
+ {/* Description */}
157
+ <Field label="Description">
158
+ {readOnly
159
+ ? (
160
+ <Typography
161
+ variant="smallCaption-regular"
162
+ color="color-text-medium"
163
+ style={{ padding: '4px 0' }}
164
+ >
165
+ {cap?.description}
166
+ </Typography>
167
+ )
168
+ : (
169
+ <Input
170
+ type="text"
171
+ size="small"
172
+ placeholder="What this capability provides"
173
+ value={description}
174
+ onChange={(v: string) => setDescription(v)}
175
+ defaultBoxStyle={{ flex: 1, minWidth: 0 }}
176
+ inputBoxStyle={{ maxWidth: 'none' }}
177
+ />
178
+ )}
179
+ </Field>
180
+
181
+ {/* Prompt */}
182
+ <Field label="Prompt" align="start">
183
+ {readOnly
184
+ ? (
185
+ <Typography
186
+ variant="smallCaption-regular"
187
+ color="color-text-medium"
188
+ style={{ whiteSpace: 'pre-wrap', lineHeight: 1.5 }}
189
+ >
190
+ {cap?.prompt}
191
+ </Typography>
192
+ )
193
+ : (
194
+ <Input
195
+ type="text"
196
+ size="small"
197
+ multiline={true}
198
+ placeholder="Knowledge content (markdown) injected into the system prompt"
199
+ value={prompt}
200
+ onChange={(v: string) => setPrompt(v)}
201
+ defaultBoxStyle={{ flex: 1, minWidth: 0 }}
202
+ inputBoxStyle={{ maxWidth: 'none', minHeight: 160 }}
203
+ />
204
+ )}
205
+ </Field>
206
+
207
+ {/* Tools */}
208
+ <Field label="Tools" align="start">
209
+ <Table
210
+ size="compact"
211
+ showHeader={false}
212
+ data={AVAILABLE_TOOLS.map((t: string) => ({
213
+ tool: t,
214
+ active: (readOnly ? (cap?.tools ?? []) : tools).includes(t),
215
+ }))}
216
+ getRowKey={(row: { tool: string }) => row.tool}
217
+ columns={[
218
+ {
219
+ key: 'check', header: '', width: '28px',
220
+ render: (_v: unknown, row: { tool: string; active: boolean }) => (
221
+ <div style={{ height: 20, display: 'flex', alignItems: 'center' }}>
222
+ <Checkbox
223
+ checked={row.active}
224
+ disabled={readOnly}
225
+ onChange={readOnly ? undefined : () => handleToggleTool(row.tool)}
226
+ size="sm"
227
+ />
228
+ </div>
187
229
  ),
188
- },
189
- {
190
- key: 'tool', header: 'Tool',
191
- render: (_v: unknown, row: { tool: string; active: boolean }) =>
192
- React.createElement('div', { style: { height: 20, display: 'flex', alignItems: 'center' } },
193
- React.createElement(Typography, {
194
- variant: 'smallCaption-regular',
195
- color: row.active ? 'color-text-high' : 'color-text-subtle',
196
- }, row.tool),
230
+ },
231
+ {
232
+ key: 'tool', header: 'Tool',
233
+ render: (_v: unknown, row: { tool: string; active: boolean }) => (
234
+ <div style={{ height: 20, display: 'flex', alignItems: 'center' }}>
235
+ <Typography
236
+ variant="smallCaption-regular"
237
+ color={row.active ? 'color-text-high' : 'color-text-subtle'}
238
+ >
239
+ {row.tool}
240
+ </Typography>
241
+ </div>
197
242
  ),
198
- },
199
- ],
200
- }),
201
- ),
202
-
203
- // Token estimate
204
- React.createElement(Field, { label: 'Est. tokens' },
205
- React.createElement(Typography, {
206
- variant: 'smallCaption-regular', color: 'color-text-subtle',
207
- }, `~${estimateTokens(readOnly ? (cap?.prompt ?? '') : prompt)} tokens (approx.)`),
208
- ),
209
-
210
- // Submit button (not for view mode)
211
- !readOnly && React.createElement(Flex, {
212
- variant: 'row-center-end-nowrap-8',
213
- style: { paddingTop: 4 },
214
- },
215
- React.createElement(Button, {
216
- size: 'xs', variant: 'clear', color: 'secondary', onClick: onBack,
217
- }, 'Cancel'),
218
- React.createElement(Button, {
219
- size: 'xs', variant: 'fill', color: 'primary',
220
- onClick: handleSubmit,
221
- disabled: readOnly || (!isCreate && !description.trim()) || (isCreate && (!name.trim() || !description.trim())),
222
- }, isCreate ? 'Create' : 'Save'),
223
- ),
224
- ),
225
- ),
243
+ },
244
+ ]}
245
+ />
246
+ </Field>
247
+
248
+ {/* Acceptance Criteria */}
249
+ <Field label="Acceptance Checks">
250
+ <Typography variant="smallCaption-regular" color="color-text-subtle" style={{ marginBottom: 4 }}>
251
+ Shell commands that auto-run after every task using this capability. Exit 0 = pass.
252
+ </Typography>
253
+ {(readOnly ? (cap?.acceptance ?? []) : acceptance).map((check, idx) => (
254
+ <Flex key={idx} variant="row-center-start-nowrap-4" style={{ marginBottom: 4 }}>
255
+ <Input
256
+ size="xs"
257
+ placeholder="Check name"
258
+ value={check.name}
259
+ readOnly={readOnly}
260
+ style={{ width: 120 }}
261
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
262
+ const updated = [...acceptance];
263
+ updated[idx] = { ...updated[idx], name: e.target.value };
264
+ setAcceptance(updated);
265
+ }}
266
+ />
267
+ <Input
268
+ size="xs"
269
+ placeholder="Shell command (exit 0 = pass)"
270
+ value={check.command}
271
+ readOnly={readOnly}
272
+ style={{ flex: 1 }}
273
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
274
+ const updated = [...acceptance];
275
+ updated[idx] = { ...updated[idx], command: e.target.value };
276
+ setAcceptance(updated);
277
+ }}
278
+ />
279
+ <Input
280
+ size="xs"
281
+ placeholder="Condition (optional file)"
282
+ value={check.condition ?? ''}
283
+ readOnly={readOnly}
284
+ style={{ width: 140 }}
285
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
286
+ const updated = [...acceptance];
287
+ updated[idx] = { ...updated[idx], condition: e.target.value || undefined };
288
+ setAcceptance(updated);
289
+ }}
290
+ />
291
+ {!readOnly && (
292
+ <IconButton
293
+ size="xs"
294
+ variant="clear"
295
+ color="danger"
296
+ onClick={() => setAcceptance(acceptance.filter((_, i) => i !== idx))}
297
+ >
298
+ <Icon name="delete" size={14} />
299
+ </IconButton>
300
+ )}
301
+ </Flex>
302
+ ))}
303
+ {!readOnly && (
304
+ <Button
305
+ size="xs"
306
+ variant="clear"
307
+ color="primary"
308
+ onClick={() => setAcceptance([...acceptance, { name: '', command: '' }])}
309
+ >
310
+ + Add check
311
+ </Button>
312
+ )}
313
+ </Field>
314
+
315
+ {/* Token estimate */}
316
+ <Field label="Est. tokens">
317
+ <Typography variant="smallCaption-regular" color="color-text-subtle">
318
+ {`~${estimateTokens(readOnly ? (cap?.prompt ?? '') : prompt)} tokens (approx.)`}
319
+ </Typography>
320
+ </Field>
321
+
322
+ {/* Submit button (not for view mode) */}
323
+ {!readOnly && (
324
+ <Flex
325
+ variant="row-center-end-nowrap-8"
326
+ style={{ paddingTop: 4 }}
327
+ >
328
+ <Button size="xs" variant="clear" color="secondary" onClick={onBack}>
329
+ Cancel
330
+ </Button>
331
+ <Button
332
+ size="xs"
333
+ variant="fill"
334
+ color="primary"
335
+ onClick={handleSubmit}
336
+ disabled={readOnly || (!isCreate && !description.trim()) || (isCreate && (!name.trim() || !description.trim()))}
337
+ >
338
+ {isCreate ? 'Create' : 'Save'}
339
+ </Button>
340
+ </Flex>
341
+ )}
342
+ </Flex>
343
+ </ScrollArea>
344
+ </Flex>
226
345
  );
227
346
  }
228
347
 
@@ -247,102 +366,107 @@ function CapabilityList({
247
366
  onClose?: () => void;
248
367
  hasCustom: boolean;
249
368
  }) {
250
- return React.createElement(Flex, {
251
- variant: 'column-stretch-start-nowrap-0',
252
- style: { width: '100%', height: '100%', overflow: 'hidden' },
253
- },
254
- // Header
255
- React.createElement(Flex, {
256
- variant: 'row-center-space-between-nowrap-8',
257
- style: { padding: '8px 16px', flexShrink: 0, borderBottom: '1px solid var(--color-border-default)' },
258
- },
259
- React.createElement(Flex, { variant: 'row-center-start-nowrap-8' },
260
- onClose && React.createElement(IconButton, {
261
- icon: 'back', size: 'xs', variant: 'clear', onClick: onClose, title: 'Back',
262
- }),
263
- React.createElement(Typography, { variant: 'caption-thick', color: 'color-text-high' }, 'Capabilities'),
264
- React.createElement(Chip, {
265
- label: `${allCaps.length}`,
266
- size: 'small',
267
- color: 'color-brand-main',
268
- }),
269
- ),
270
- React.createElement(Flex, { variant: 'row-center-end-nowrap-6' },
271
- onSaveAll && hasCustom && React.createElement(Button, {
272
- size: 'xs', variant: 'outlined', color: 'primary', onClick: onSaveAll,
273
- }, 'Save'),
274
- React.createElement(Button, {
275
- size: 'xs', variant: 'fill', color: 'primary', onClick: onCreate,
276
- }, 'New'),
277
- ),
278
- ),
279
-
280
- // Scrollable list
281
- React.createElement(ScrollArea, { style: { flex: 1, minHeight: 0 } },
282
- React.createElement(Flex, {
283
- variant: 'column-stretch-start-nowrap-2',
284
- style: { padding: '8px 16px' },
285
- },
286
- ...allCaps.map((cap: CapabilityDef) =>
287
- React.createElement(Flex, {
288
- key: cap.name,
289
- variant: 'row-center-space-between-nowrap-8',
290
- style: {
291
- padding: '7px 10px',
292
- borderRadius: 6,
293
- cursor: 'pointer',
294
- border: '1px solid var(--color-border-default)',
295
- transition: 'background-color 0.1s',
296
- },
297
- onClick: () => onOpen(cap.name),
298
- onMouseEnter: (e: { currentTarget: HTMLElement }) => {
299
- e.currentTarget.style.backgroundColor = 'var(--color-surface-hover)';
300
- },
301
- onMouseLeave: (e: { currentTarget: HTMLElement }) => {
302
- e.currentTarget.style.backgroundColor = 'transparent';
303
- },
304
- },
305
- // Left: icon + name + description
306
- React.createElement(Flex, { variant: 'row-center-start-nowrap-8', style: { flex: 1, minWidth: 0 } },
307
- React.createElement(Icon, {
308
- name: cap.builtIn ? 'lock' : 'edit',
309
- size: 13,
310
- color: cap.builtIn ? 'color-text-subtle' : 'color-brand-main',
311
- }),
312
- React.createElement(Typography, {
313
- variant: 'smallCaption-thick',
314
- color: 'color-text-high',
315
- style: { minWidth: 80, flexShrink: 0 },
316
- }, cap.name),
317
- React.createElement(Typography, {
318
- variant: 'smallCaption-regular',
319
- color: 'color-text-subtle',
320
- style: { flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },
321
- }, cap.description),
322
- ),
323
- // Right: chips + actions
324
- React.createElement(Flex, { variant: 'row-center-end-nowrap-4', style: { flexShrink: 0 } },
325
- cap.tools && cap.tools.length > 0 && React.createElement(Chip, {
326
- label: `${cap.tools.length} tools`,
327
- size: 'small',
328
- color: 'color-status-positive',
329
- }),
330
- !cap.builtIn && React.createElement(IconButton, {
331
- icon: 'outlinedDelete',
332
- size: 'xs',
333
- variant: 'clear',
334
- color: 'danger',
335
- onClick: (e: Event) => { e.stopPropagation(); onDelete(cap.name); },
336
- title: 'Delete capability',
337
- }),
338
- React.createElement(Icon, {
339
- name: 'chevronRight', size: 12, color: 'color-text-subtle',
340
- }),
341
- ),
342
- ),
343
- ),
344
- ),
345
- ),
369
+ return (
370
+ <Flex
371
+ variant="column-stretch-start-nowrap-0"
372
+ style={{ width: '100%', height: '100%', overflow: 'hidden' }}
373
+ >
374
+ {/* Header */}
375
+ <Flex
376
+ variant="row-center-space-between-nowrap-8"
377
+ style={{ padding: '8px 16px', flexShrink: 0, borderBottom: '1px solid var(--color-border-default)' }}
378
+ >
379
+ <Flex variant="row-center-start-nowrap-8">
380
+ {onClose && (
381
+ <IconButton icon="back" size="xs" variant="clear" onClick={onClose} title="Back" />
382
+ )}
383
+ <Typography variant="caption-thick" color="color-text-high">Capabilities</Typography>
384
+ <Chip
385
+ label={`${allCaps.length}`}
386
+ size="small"
387
+ color="color-brand-main"
388
+ />
389
+ </Flex>
390
+ <Flex variant="row-center-end-nowrap-6">
391
+ {onSaveAll && hasCustom && (
392
+ <Button size="xs" variant="outlined" color="primary" onClick={onSaveAll}>
393
+ Save
394
+ </Button>
395
+ )}
396
+ <Button size="xs" variant="fill" color="primary" onClick={onCreate}>
397
+ New
398
+ </Button>
399
+ </Flex>
400
+ </Flex>
401
+
402
+ {/* Scrollable list */}
403
+ <ScrollArea style={{ flex: 1, minHeight: 0 }}>
404
+ <Flex
405
+ variant="column-stretch-start-nowrap-2"
406
+ style={{ padding: '8px 16px' }}
407
+ >
408
+ {allCaps.map((cap: CapabilityDef) => (
409
+ <Flex
410
+ key={cap.name}
411
+ variant="row-center-space-between-nowrap-8"
412
+ style={{
413
+ padding: '7px 10px',
414
+ borderRadius: 6,
415
+ cursor: 'pointer',
416
+ border: '1px solid var(--color-border-default)',
417
+ transition: 'background-color 0.1s',
418
+ }}
419
+ onClick={() => onOpen(cap.name)}
420
+ onMouseEnter={(e: { currentTarget: HTMLElement }) => {
421
+ e.currentTarget.style.backgroundColor = 'var(--color-surface-hover)';
422
+ }}
423
+ onMouseLeave={(e: { currentTarget: HTMLElement }) => {
424
+ e.currentTarget.style.backgroundColor = 'transparent';
425
+ }}
426
+ >
427
+ {/* Left: name + description */}
428
+ <Flex variant="row-center-start-nowrap-8" style={{ flex: 1, minWidth: 0 }}>
429
+ <Typography
430
+ variant="smallCaption-thick"
431
+ color="color-text-high"
432
+ style={{ minWidth: 80, flexShrink: 0 }}
433
+ >
434
+ {cap.name}
435
+ </Typography>
436
+ <Typography
437
+ variant="smallCaption-regular"
438
+ color="color-text-subtle"
439
+ style={{ flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
440
+ >
441
+ {cap.description}
442
+ </Typography>
443
+ </Flex>
444
+ {/* Right: chips + actions */}
445
+ <Flex variant="row-center-end-nowrap-4" style={{ flexShrink: 0 }}>
446
+ {cap.tools && cap.tools.length > 0 && (
447
+ <Chip
448
+ label={`${cap.tools.length} tools`}
449
+ size="small"
450
+ color="color-status-positive"
451
+ />
452
+ )}
453
+ {!cap.builtIn && (
454
+ <IconButton
455
+ icon="outlinedDelete"
456
+ size="xs"
457
+ variant="clear"
458
+ color="danger"
459
+ onClick={(e: Event) => { e.stopPropagation(); onDelete(cap.name); }}
460
+ title="Delete capability"
461
+ />
462
+ )}
463
+ <Icon name="chevronRight" size={12} color="color-text-subtle" />
464
+ </Flex>
465
+ </Flex>
466
+ ))}
467
+ </Flex>
468
+ </ScrollArea>
469
+ </Flex>
346
470
  );
347
471
  }
348
472
 
@@ -365,7 +489,7 @@ function CapabilityEditor({ customCapabilities = [], onSave, onClose }: Capabili
365
489
  const handleOpen = useCallback((name: string) => {
366
490
  const cap = allCaps.find((c: CapabilityDef) => c.name === name);
367
491
  if (!cap) return;
368
- setScreen({ mode: cap.builtIn ? 'view' : 'edit', name });
492
+ setScreen({ mode: 'edit', name });
369
493
  }, [allCaps]);
370
494
 
371
495
  const handleDelete = useCallback((name: string) => {
@@ -375,16 +499,24 @@ function CapabilityEditor({ customCapabilities = [], onSave, onClose }: Capabili
375
499
 
376
500
  const handleSubmit = useCallback((cap: CapabilityDef) => {
377
501
  if (typeof screen === 'object' && screen.mode === 'edit') {
378
- setCustoms((prev: CapabilityDef[]) =>
379
- prev.map((c: CapabilityDef) => c.name === cap.name ? cap : c),
380
- );
381
- toast(`Capability "${cap.name}" updated`, { type: 'success' });
502
+ // Check if this is a custom capability being edited or a built-in being overridden
503
+ const isExistingCustom = customs.some((c: CapabilityDef) => c.name === cap.name);
504
+ if (isExistingCustom) {
505
+ setCustoms((prev: CapabilityDef[]) =>
506
+ prev.map((c: CapabilityDef) => c.name === cap.name ? cap : c),
507
+ );
508
+ toast(`Capability "${cap.name}" updated`, { type: 'success' });
509
+ } else {
510
+ // Built-in being overridden → add as custom
511
+ setCustoms((prev: CapabilityDef[]) => [...prev, cap]);
512
+ toast(`Capability "${cap.name}" customized`, { type: 'success' });
513
+ }
382
514
  } else {
383
515
  setCustoms((prev: CapabilityDef[]) => [...prev, cap]);
384
516
  toast(`Capability "${cap.name}" created`, { type: 'success' });
385
517
  }
386
518
  setScreen('list');
387
- }, [screen]);
519
+ }, [screen, customs]);
388
520
 
389
521
  const handleSaveAll = useCallback(() => {
390
522
  onSave?.(customs);
@@ -394,27 +526,30 @@ function CapabilityEditor({ customCapabilities = [], onSave, onClose }: Capabili
394
526
  // Detail screen
395
527
  if (typeof screen === 'object') {
396
528
  const cap = screen.name ? allCaps.find((c: CapabilityDef) => c.name === screen.name) : undefined;
397
- return React.createElement(CapabilityDetail, {
398
- cap,
399
- mode: screen.mode,
400
- allNames,
401
- onBack: () => setScreen('list'),
402
- onSubmit: handleSubmit,
403
- });
529
+ return (
530
+ <CapabilityDetail
531
+ cap={cap}
532
+ mode={screen.mode}
533
+ allNames={allNames}
534
+ onBack={() => setScreen('list')}
535
+ onSubmit={handleSubmit}
536
+ />
537
+ );
404
538
  }
405
539
 
406
540
  // List screen
407
- return React.createElement(CapabilityList, {
408
- allCaps,
409
- onOpen: handleOpen,
410
- onDelete: handleDelete,
411
- onCreate: () => setScreen({ mode: 'create' }),
412
- onSaveAll: onSave ? handleSaveAll : undefined,
413
- onClose,
414
- hasCustom: customs.length > 0,
415
- });
541
+ return (
542
+ <CapabilityList
543
+ allCaps={allCaps}
544
+ onOpen={handleOpen}
545
+ onDelete={handleDelete}
546
+ onCreate={() => setScreen({ mode: 'create' })}
547
+ onSaveAll={onSave ? handleSaveAll : undefined}
548
+ onClose={onClose}
549
+ hasCustom={customs.length > 0}
550
+ />
551
+ );
416
552
  }
417
553
 
418
554
  export { CapabilityEditor };
419
555
  export default CapabilityEditor;
420
- module.exports = CapabilityEditor;