@runfusion/fusion 0.2.7 → 0.3.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 (55) hide show
  1. package/dist/bin.js +8436 -7001
  2. package/dist/client/assets/{AgentDetailView-BMrHuWGs.css → AgentDetailView-C1b9PC5l.css} +1 -1
  3. package/dist/client/assets/{AgentDetailView-B4lRk--v.js → AgentDetailView-CJIxNRq-.js} +3 -3
  4. package/dist/client/assets/{AgentsView-yCYBY2km.js → AgentsView-BS17exn3.js} +5 -5
  5. package/dist/client/assets/ChatView-BUlq3WNJ.js +1 -0
  6. package/dist/client/assets/{DevServerView-jXXtoQUx.js → DevServerView-qMPpnXRb.js} +2 -2
  7. package/dist/client/assets/{DirectoryPicker-izgMlS27.js → DirectoryPicker-CTwgv9LY.js} +1 -1
  8. package/dist/client/assets/DirectoryPicker-DzKVmxOf.css +1 -0
  9. package/dist/client/assets/{DocumentsView-DkkoHRwL.js → DocumentsView-DOz1KFGN.js} +1 -1
  10. package/dist/client/assets/{InsightsView-DaRtUPHX.js → InsightsView-CHZTJUic.js} +2 -2
  11. package/dist/client/assets/MemoryView-V0QdeO3e.js +2 -0
  12. package/dist/client/assets/{NodesView-BsUk_oiU.js → NodesView-BtGNRj2z.js} +1 -1
  13. package/dist/client/assets/PiExtensionsManager-D9Ye2Vak.js +11 -0
  14. package/dist/client/assets/PiExtensionsManager-kgTOHPE9.css +1 -0
  15. package/dist/client/assets/PluginManager-DRiIqol2.css +1 -0
  16. package/dist/client/assets/PluginManager-LeHp0jJ_.js +1 -0
  17. package/dist/client/assets/{RoadmapsView-SQol126Y.js → RoadmapsView-C413ISVU.js} +2 -2
  18. package/dist/client/assets/SettingsModal--vWmKBpT.css +1 -0
  19. package/dist/client/assets/SettingsModal-BZLL2xAP.js +31 -0
  20. package/dist/client/assets/SettingsModal-CDWvhvrd.css +1 -0
  21. package/dist/client/assets/SettingsModal-olTBmYJs.js +1 -0
  22. package/dist/client/assets/SetupWizardModal-BMa6p24b.css +1 -0
  23. package/dist/client/assets/{SetupWizardModal-CQc1uGSq.js → SetupWizardModal-WdaR2eQQ.js} +1 -1
  24. package/dist/client/assets/SkillsView-BcE57w8i.js +1 -0
  25. package/dist/client/assets/{folder-open-CI4TCD7P.js → folder-open-Ec4hU1xL.js} +1 -1
  26. package/dist/client/assets/index-CCYdhck-.js +616 -0
  27. package/dist/client/assets/index-lJ5WOmO9.css +1 -0
  28. package/dist/client/assets/{upload-CAlKC4qI.js → upload-BksRDuGJ.js} +1 -1
  29. package/dist/client/assets/users-EFU4n9Qr.js +6 -0
  30. package/dist/client/index.html +2 -2
  31. package/dist/extension.js +1266 -205
  32. package/dist/pi-claude-cli/index.ts +72 -28
  33. package/dist/pi-claude-cli/package.json +1 -1
  34. package/dist/pi-claude-cli/src/__tests__/event-bridge.test.ts +34 -0
  35. package/dist/pi-claude-cli/src/__tests__/mcp-config.test.ts +22 -0
  36. package/dist/pi-claude-cli/src/__tests__/prompt-builder.test.ts +72 -10
  37. package/dist/pi-claude-cli/src/__tests__/provider.test.ts +9 -9
  38. package/dist/pi-claude-cli/src/event-bridge.ts +17 -6
  39. package/dist/pi-claude-cli/src/mcp-config.ts +36 -3
  40. package/dist/pi-claude-cli/src/prompt-builder.ts +111 -7
  41. package/dist/pi-claude-cli/src/provider.ts +18 -2
  42. package/package.json +6 -5
  43. package/skill/fusion/SKILL.md +6 -1
  44. package/skill/fusion/references/engine-tools.md +54 -0
  45. package/skill/fusion/references/extension-tools.md +83 -84
  46. package/skill/fusion/references/fusion-capabilities.md +33 -31
  47. package/dist/client/assets/ChatView-CH9T0dDs.js +0 -1
  48. package/dist/client/assets/MemoryView-85NKuU3h.js +0 -2
  49. package/dist/client/assets/PiExtensionsManager-BF5pxrSE.js +0 -11
  50. package/dist/client/assets/PiExtensionsManager-K7HQ08L4.css +0 -1
  51. package/dist/client/assets/PluginManager-ccq3uK50.css +0 -1
  52. package/dist/client/assets/PluginManager-s6btydh5.js +0 -1
  53. package/dist/client/assets/SkillsView-BtUhs_QW.js +0 -1
  54. package/dist/client/assets/index-Ct-OqLpP.css +0 -1
  55. package/dist/client/assets/index-rNf7s96d.js +0 -649
@@ -2,191 +2,187 @@
2
2
 
3
3
  All tools are registered via the pi extension. They are available in any pi agent session when the Fusion extension is installed.
4
4
 
5
- > Naming contract: all externally exposed Fusion extension tools are `fn_*` (for example `fn_task_create`). Internal engine/executor runtime tools (`task_create`, `task_update`, `task_log`, `task_done`, etc.) are separate and intentionally out of scope for this skill surface.
5
+ > Naming contract: all externally exposed Fusion extension tools are `fn_*` (for example `fn_task_create`). Engine runtime sessions also inject additional `fn_*` tools (for example `fn_review_step`, `fn_spawn_agent`, `fn_task_document_write`) that are separate from this extension surface and documented in `engine-tools.md`.
6
+
7
+ <!-- BEGIN: extension-tools (auto-generated by scripts/sync-fusion-skill-tools.mjs — do not edit by hand) -->
6
8
 
7
9
  ## Task Tools
8
10
 
9
11
  ### fn_task_create
10
12
 
11
- Create a new task on the Fusion board. Enters triage for AI specification.
13
+ Create a new task on the Fusion task board. The task enters the triage column where the AI triage agent will specify it into a full prompt with steps, file scope, and acceptance criteria.
12
14
 
13
15
  | Parameter | Type | Required | Description |
14
16
  |-----------|------|----------|-------------|
15
17
  | `description` | string | ✓ | What needs to be done — be descriptive |
16
- | `depends` | string[] | — | Task IDs this depends on (e.g., ["FN-001"]) |
17
-
18
- Returns: task ID, column, dependencies, path
18
+ | `depends` | array | — | Task IDs this depends on (e.g. ['FN-001', 'FN-002']) |
19
+ | `agentId` | string | — | Agent ID to assign this task to (e.g. 'agent-abc123') |
19
20
 
20
21
  ### fn_task_update
21
22
 
22
- Update fields on an existing task (title, description, dependencies).
23
+ Update fields on an existing task. Supports modifying the title, description, dependencies, and assigned agent after task creation.
23
24
 
24
25
  | Parameter | Type | Required | Description |
25
26
  |-----------|------|----------|-------------|
26
- | `id` | string | ✓ | Task ID (e.g., FN-001) |
27
+ | `id` | string | ✓ | Task ID (e.g. FN-001) |
27
28
  | `title` | string | — | New task title |
28
29
  | `description` | string | — | New task description |
29
- | `depends` | string[] | — | New dependency list — replaces existing |
30
-
31
- Returns: task ID, list of updated fields
30
+ | `depends` | array | — | New dependency list — replaces existing dependencies (e.g. ['FN-001', 'FN-002']) |
31
+ | `agentId` | union | — | Agent ID to assign this task to, or null to clear (e.g. 'agent-abc123') |
32
32
 
33
33
  ### fn_task_list
34
34
 
35
- List all tasks grouped by column.
35
+ List all tasks on the Fusion board, grouped by column.
36
36
 
37
37
  | Parameter | Type | Required | Description |
38
38
  |-----------|------|----------|-------------|
39
- | `column` | string | — | Filter to specific column |
40
- | `limit` | number | — | Max tasks per column (default: 10) |
41
-
42
- Returns: formatted task list grouped by column
39
+ | `column` | string(enum) | — | Filter to a specific column |
40
+ | `limit` | number | — | Max tasks to show per column (default: 10) |
43
41
 
44
42
  ### fn_task_show
45
43
 
46
- Show full task details including steps, progress, prompt preview, and log.
44
+ Show full details for a task including steps, progress, and log entries.
47
45
 
48
46
  | Parameter | Type | Required | Description |
49
47
  |-----------|------|----------|-------------|
50
- | `id` | string | ✓ | Task ID (e.g., FN-001) |
51
-
52
- Returns: task details with steps, prompt preview (500 chars), last 5 log entries
48
+ | `id` | string | ✓ | Task ID (e.g. FN-001) |
53
49
 
54
50
  ### fn_task_attach
55
51
 
56
- Attach a file to a task. Copies file to task's attachments directory.
52
+ Attach a file to a task. Supports images (png, jpg, gif, webp) and text files (txt, log, json, yaml, yml, toml, csv, xml).
57
53
 
58
54
  | Parameter | Type | Required | Description |
59
55
  |-----------|------|----------|-------------|
60
- | `id` | string | ✓ | Task ID |
61
- | `path` | string | ✓ | Path to file to attach |
62
-
63
- Supported formats: png, jpg, jpeg, gif, webp, txt, log, json, yaml, yml, toml, csv, xml
56
+ | `id` | string | ✓ | Task ID (e.g. FN-001) |
57
+ | `path` | string | ✓ | Path to the file to attach |
64
58
 
65
59
  ### fn_task_pause
66
60
 
67
- Pause automation for a task. Scheduler and executor will skip this task.
61
+ Pause a task stops all automated agent and scheduler interaction for this task.
68
62
 
69
63
  | Parameter | Type | Required | Description |
70
64
  |-----------|------|----------|-------------|
71
- | `id` | string | ✓ | Task ID |
65
+ | `id` | string | ✓ | Task ID (e.g. FN-001) |
72
66
 
73
67
  ### fn_task_unpause
74
68
 
75
- Resume automation for a paused task.
69
+ Unpause a task resumes automated agent and scheduler interaction.
76
70
 
77
71
  | Parameter | Type | Required | Description |
78
72
  |-----------|------|----------|-------------|
79
- | `id` | string | ✓ | Task ID |
73
+ | `id` | string | ✓ | Task ID (e.g. FN-001) |
80
74
 
81
75
  ### fn_task_retry
82
76
 
83
- Retry a failed task. Clears error state, moves to todo for re-execution.
77
+ Retry a failed task clears the error state and moves it back to the todo column for re-execution.
84
78
 
85
79
  | Parameter | Type | Required | Description |
86
80
  |-----------|------|----------|-------------|
87
- | `id` | string | ✓ | Task ID (must be in failed state) |
81
+ | `id` | string | ✓ | Task ID to retry (e.g. FN-001). Must be in 'failed' state. |
88
82
 
89
83
  ### fn_task_duplicate
90
84
 
91
- Duplicate a task. Creates a fresh copy in triage with same title and description.
85
+ Duplicate an existing task, creating a fresh copy in triage. Copies the title and description but resets all execution state. The AI triage agent will re-specify the new task.
92
86
 
93
87
  | Parameter | Type | Required | Description |
94
88
  |-----------|------|----------|-------------|
95
- | `id` | string | ✓ | Source task ID to duplicate |
89
+ | `id` | string | ✓ | Source task ID to duplicate (e.g. FN-001) |
96
90
 
97
91
  ### fn_task_refine
98
92
 
99
- Create a follow-up task for a completed task. New task depends on the original.
93
+ Request a refinement of a completed or in-review task. Creates a new follow-up task in triage that references the original task as a dependency. Use this when a done or in-review task needs additional work, improvements, or follow-up changes.
100
94
 
101
95
  | Parameter | Type | Required | Description |
102
96
  |-----------|------|----------|-------------|
103
- | `id` | string | ✓ | Task ID (must be done or in-review) |
104
- | `feedback` | string | ✓ | What needs to be refined (1-2000 chars) |
97
+ | `id` | string | ✓ | Task ID to refine (e.g. FN-001). Must be in 'done' or 'in-review' column. |
98
+ | `feedback` | string | ✓ | Description of what needs to be refined or improved |
105
99
 
106
100
  ### fn_task_archive
107
101
 
108
- Archive a done task. Moves from done → archived.
102
+ Archive a done task (move from done → archived). Archived tasks are preserved for historical reference but moved out of the main board view.
109
103
 
110
104
  | Parameter | Type | Required | Description |
111
105
  |-----------|------|----------|-------------|
112
- | `id` | string | ✓ | Task ID (must be in done column) |
106
+ | `id` | string | ✓ | Task ID to archive (e.g. FN-001). Must be in 'done' column. |
113
107
 
114
108
  ### fn_task_unarchive
115
109
 
116
- Restore an archived task. Moves from archived → done.
110
+ Unarchive an archived task (move from archived → done). Restores the task to the done column.
117
111
 
118
112
  | Parameter | Type | Required | Description |
119
113
  |-----------|------|----------|-------------|
120
- | `id` | string | ✓ | Task ID (must be in archived column) |
114
+ | `id` | string | ✓ | Task ID to unarchive (e.g. FN-001). Must be in 'archived' column. |
121
115
 
122
116
  ### fn_task_delete
123
117
 
124
- Permanently delete a task. Cannot be undone.
118
+ Permanently delete a task from the Fusion board. Tasks are deleted immediately and cannot be recovered.
125
119
 
126
120
  | Parameter | Type | Required | Description |
127
121
  |-----------|------|----------|-------------|
128
- | `id` | string | ✓ | Task ID |
122
+ | `id` | string | ✓ | Task ID to delete (e.g. FN-001) |
129
123
 
130
124
  ### fn_task_plan
131
125
 
132
- Create a task via AI-guided planning mode. Non-interactive when called from extension.
126
+ Create a task via AI-guided planning mode interactive conversation to refine your idea into a well-specified task.
133
127
 
134
128
  | Parameter | Type | Required | Description |
135
129
  |-----------|------|----------|-------------|
136
- | `description` | string | — | Initial plan description |
130
+ | `description` | string | — | Initial plan description (optional) — the AI will ask clarifying questions if not provided |
137
131
 
138
132
  ## GitHub Tools
139
133
 
140
134
  ### fn_task_import_github
141
135
 
142
- Batch import GitHub issues as Fusion tasks.
136
+ Import GitHub issues as Fusion tasks. Fetches open issues from a repository and creates tasks in the triage column. Each task includes the issue title and body with a link to the source issue.
143
137
 
144
138
  | Parameter | Type | Required | Description |
145
139
  |-----------|------|----------|-------------|
146
- | `ownerRepo` | string | ✓ | Repository (e.g., "owner/repo") |
147
- | `limit` | number | — | Max issues (default: 30, max: 100) |
148
- | `labels` | string[] | — | Label names to filter by |
140
+ | `ownerRepo` | string | ✓ | Repository in owner/repo format (e.g., 'dustinbyrne/fusion') |
141
+ | `limit` | number | — | Max issues to import (default: 30, max: 100) |
142
+ | `labels` | array | — | Label names to filter by |
149
143
 
150
144
  ### fn_task_import_github_issue
151
145
 
152
- Import a single GitHub issue by number.
146
+ Import a specific GitHub issue as a Fusion task. Fetches the issue by number and creates a single task in the triage column with the issue title and body.
153
147
 
154
148
  | Parameter | Type | Required | Description |
155
149
  |-----------|------|----------|-------------|
156
- | `owner` | string | ✓ | Repository owner |
157
- | `repo` | string | ✓ | Repository name |
158
- | `issueNumber` | number | ✓ | GitHub issue number |
150
+ | `owner` | string | ✓ | Repository owner (e.g., 'dustinbyrne') |
151
+ | `repo` | string | ✓ | Repository name (e.g., 'fusion') |
152
+ | `issueNumber` | number | ✓ | GitHub issue number to import |
159
153
 
160
154
  ### fn_task_browse_github_issues
161
155
 
162
- Browse open issues from a repository before importing.
156
+ List open GitHub issues from a repository to browse before importing. Returns issue numbers, titles, and URLs for selection. Use with fn_task_import_github_issue to import specific issues by number.
163
157
 
164
158
  | Parameter | Type | Required | Description |
165
159
  |-----------|------|----------|-------------|
166
- | `owner` | string | ✓ | Repository owner |
167
- | `repo` | string | ✓ | Repository name |
168
- | `limit` | number | — | Max issues (default: 30, max: 100) |
169
- | `labels` | string[] | — | Label names to filter by |
160
+ | `owner` | string | ✓ | Repository owner (e.g., 'dustinbyrne') |
161
+ | `repo` | string | ✓ | Repository name (e.g., 'fusion') |
162
+ | `limit` | number | — | Max issues to show (default: 30, max: 100) |
163
+ | `labels` | array | — | Label names to filter by |
170
164
 
171
165
  ## Mission Tools
172
166
 
173
167
  ### fn_mission_create
174
168
 
175
- Create a new mission — a high-level objective spanning multiple milestones.
169
+ Create a new mission — a high-level objective that can span multiple milestones. Missions contain milestones that break down work into phases.
176
170
 
177
171
  | Parameter | Type | Required | Description |
178
172
  |-----------|------|----------|-------------|
179
- | `title` | string | ✓ | Mission title |
180
- | `description` | string | — | Detailed objectives and context |
181
- | `autoAdvance` | boolean | — | Auto-activate next slice on completion |
173
+ | `title` | string | ✓ | Mission title — brief but descriptive |
174
+ | `description` | string | — | Detailed mission objectives and context |
175
+ | `autoAdvance` | boolean | — | Automatically activate the next pending slice when the current slice completes |
182
176
 
183
177
  ### fn_mission_list
184
178
 
185
- List all missions with current status. No parameters.
179
+ List all missions with their current status.
180
+
181
+ No parameters.
186
182
 
187
183
  ### fn_mission_show
188
184
 
189
- Show mission details with full hierarchy.
185
+ Show mission details with full hierarchy: milestones → slices → features.
190
186
 
191
187
  | Parameter | Type | Required | Description |
192
188
  |-----------|------|----------|-------------|
@@ -194,63 +190,65 @@ Show mission details with full hierarchy.
194
190
 
195
191
  ### fn_mission_delete
196
192
 
197
- Delete a mission and all children. Tasks are NOT deleted.
193
+ Delete a mission and all its milestones, slices, and features. Cannot be undone.
198
194
 
199
195
  | Parameter | Type | Required | Description |
200
196
  |-----------|------|----------|-------------|
201
- | `id` | string | ✓ | Mission ID |
197
+ | `id` | string | ✓ | Mission ID to delete (e.g., M-001) |
202
198
 
203
199
  ### fn_milestone_add
204
200
 
205
- Add a milestone to a mission.
201
+ Add a milestone to a mission. Milestones represent phases of work.
206
202
 
207
203
  | Parameter | Type | Required | Description |
208
204
  |-----------|------|----------|-------------|
209
- | `missionId` | string | ✓ | Parent mission ID |
205
+ | `missionId` | string | ✓ | Parent mission ID (e.g., M-001) |
210
206
  | `title` | string | ✓ | Milestone title |
211
207
  | `description` | string | — | Milestone description |
212
208
 
213
209
  ### fn_slice_add
214
210
 
215
- Add a slice to a milestone.
211
+ Add a slice to a milestone. Slices are work units that can be activated for implementation.
216
212
 
217
213
  | Parameter | Type | Required | Description |
218
214
  |-----------|------|----------|-------------|
219
- | `milestoneId` | string | ✓ | Parent milestone ID |
215
+ | `milestoneId` | string | ✓ | Parent milestone ID (e.g., MS-001) |
220
216
  | `title` | string | ✓ | Slice title |
221
217
  | `description` | string | — | Slice description |
222
218
 
223
219
  ### fn_feature_add
224
220
 
225
- Add a feature to a slice.
221
+ Add a feature to a slice. Features are deliverables that can be linked to tasks.
226
222
 
227
223
  | Parameter | Type | Required | Description |
228
224
  |-----------|------|----------|-------------|
229
- | `sliceId` | string | ✓ | Parent slice ID |
225
+ | `sliceId` | string | ✓ | Parent slice ID (e.g., SL-001) |
230
226
  | `title` | string | ✓ | Feature title |
231
227
  | `description` | string | — | Feature description |
232
- | `acceptanceCriteria` | string | — | Acceptance criteria |
228
+ | `acceptanceCriteria` | string | — | Acceptance criteria for completing the feature |
233
229
 
234
230
  ### fn_slice_activate
235
231
 
236
- Activate a pending slice for implementation.
232
+ Activate a pending slice for implementation. Sets status to 'active' and enables task linking for its features.
237
233
 
238
234
  | Parameter | Type | Required | Description |
239
235
  |-----------|------|----------|-------------|
240
- | `id` | string | ✓ | Slice ID (must be pending) |
236
+ | `id` | string | ✓ | Slice ID to activate (e.g., SL-001) |
241
237
 
242
238
  ### fn_feature_link_task
243
239
 
244
- Link a feature to a Fusion task. Updates feature status to triaged.
240
+ Link a feature to a fn task for implementation. Updates the feature status to 'triaged' and associates it with the task.
245
241
 
246
242
  | Parameter | Type | Required | Description |
247
243
  |-----------|------|----------|-------------|
248
- | `featureId` | string | ✓ | Feature ID (e.g., F-001) |
249
- | `taskId` | string | ✓ | Task ID (e.g., FN-001) |
244
+ | `featureId` | string | ✓ | Feature ID to link (e.g., F-001) |
245
+ | `taskId` | string | ✓ | Task ID to link to (e.g., FN-001) |
246
+
247
+ ## Agent Tools
250
248
 
251
249
  ### fn_agent_stop
252
250
 
253
- Stop (pause) a running agent. Transitions the agent from running/active to paused state.
251
+ Stop a running agent — pauses its execution. Transitions the agent from running/active to paused state.
254
252
 
255
253
  | Parameter | Type | Required | Description |
256
254
  |-----------|------|----------|-------------|
@@ -258,7 +256,7 @@ Stop (pause) a running agent. Transitions the agent from running/active to pause
258
256
 
259
257
  ### fn_agent_start
260
258
 
261
- Start (resume) a stopped agent. Transitions the agent from paused to active state.
259
+ Start a stopped agent — resumes its execution. Transitions the agent from paused to active state.
262
260
 
263
261
  | Parameter | Type | Required | Description |
264
262
  |-----------|------|----------|-------------|
@@ -268,22 +266,23 @@ Start (resume) a stopped agent. Transitions the agent from paused to active stat
268
266
 
269
267
  ### fn_skills_search
270
268
 
271
- Search the skills.sh directory for agent skills. Returns matching skills with names, sources, install counts, and install commands.
269
+ Search the skills.sh directory for agent skills. Returns matching skills with names, sources (owner/repo), install counts, and install commands. Use fn_skills_install to install a selected skill.
272
270
 
273
271
  | Parameter | Type | Required | Description |
274
272
  |-----------|------|----------|-------------|
275
- | `query` | string | ✓ | Search query — framework, technology, or capability (e.g., "react", "firebase", "testing", "docker") |
276
- | `limit` | number | — | Max results (default: 10, max: 50) |
273
+ | `query` | string | ✓ | Search query — framework name, technology, or capability (e.g., 'react', 'firebase', 'testing', 'docker') |
274
+ | `limit` | number | — | Max results to return (default: 10, max: 50) |
277
275
 
278
276
  ### fn_skills_install
279
277
 
280
- Install an agent skill from skills.sh into the current project. Downloads skill files into the project's skill directories.
278
+ Install an agent skill from skills.sh into the current project. Downloads skill files into the project's skill directories (.fusion/skills/, legacy .pi/skills/, .agents/skills/). The skill becomes available to AI agents in subsequent sessions.
281
279
 
282
280
  | Parameter | Type | Required | Description |
283
281
  |-----------|------|----------|-------------|
284
- | `source` | string | ✓ | GitHub source in owner/repo format (e.g., "firebase/agent-skills") |
285
- | `skill` | string | — | Specific skill name to install. Omit to install all skills from the source. |
282
+ | `source` | string | ✓ | GitHub source in owner/repo format (e.g., 'firebase/agent-skills') |
283
+ | `skill` | string | — | Specific skill name to install (e.g., 'firebase-basics'). Omit to install all skills from the source. |
286
284
 
285
+ <!-- END: extension-tools -->
287
286
  ## Dashboard Command
288
287
 
289
288
  ### /fn
@@ -7,40 +7,42 @@ Triage → Todo → In Progress → In Review → Done → Archived
7
7
 
8
8
  ## Pi Extension Tools (Available to Agents)
9
9
 
10
- All skill/extension tool invocations in this catalog use the public `fn_*` namespace. Engine runtime tools (for example `task_create`, `task_update`, `task_log`, `task_done`) are internal and intentionally not listed here.
10
+ All skill/extension tool invocations in this catalog use the public `fn_*` namespace. Engine runtime sessions also have additional runtime-only `fn_*` tools that are intentionally not listed here (see `references/engine-tools.md`).
11
11
 
12
+ <!-- BEGIN: fusion-capabilities-tool-table (auto-generated by scripts/sync-fusion-skill-tools.mjs — do not edit by hand) -->
12
13
  | Tool | Purpose |
13
14
  |------|---------|
14
- | `fn_task_create` | Create a new task in triage |
15
- | `fn_task_update` | Update task title, description, or dependencies |
16
- | `fn_task_list` | List all tasks grouped by column |
17
- | `fn_task_show` | Show full task details, steps, and log preview |
18
- | `fn_task_attach` | Attach a file to a task |
19
- | `fn_task_pause` | Pause automation for a task |
20
- | `fn_task_unpause` | Resume automation for a task |
21
- | `fn_task_retry` | Retry a failed task (clears error, moves to todo) |
22
- | `fn_task_duplicate` | Duplicate a task (copy to triage) |
23
- | `fn_task_refine` | Create refinement task for follow-up work |
24
- | `fn_task_archive` | Archive a done task |
25
- | `fn_task_unarchive` | Restore an archived task |
26
- | `fn_task_delete` | Permanently delete a task |
27
- | `fn_task_import_github` | Batch import GitHub issues as tasks |
28
- | `fn_task_import_github_issue` | Import a single GitHub issue |
29
- | `fn_task_browse_github_issues` | Browse GitHub issues before importing |
30
- | `fn_task_plan` | Create task via AI-guided planning mode |
31
- | `fn_mission_create` | Create a new mission |
32
- | `fn_mission_list` | List all missions |
33
- | `fn_mission_show` | Show mission hierarchy |
34
- | `fn_mission_delete` | Delete a mission |
35
- | `fn_milestone_add` | Add a milestone to a mission |
36
- | `fn_slice_add` | Add a slice to a milestone |
37
- | `fn_feature_add` | Add a feature to a slice |
38
- | `fn_slice_activate` | Activate a pending slice |
39
- | `fn_feature_link_task` | Link a feature to a task |
40
- | `fn_agent_stop` | Stop (pause) a running agent |
41
- | `fn_agent_start` | Start (resume) a stopped agent |
42
- | `fn_skills_search` | Search skills.sh for agent skills |
43
- | `fn_skills_install` | Install a skill from skills.sh |
15
+ | `fn_task_create` | Create a new task on the Fusion task board. The task enters the triage column where the AI triage agent will specify it into a full prompt with steps, file scope, and acceptance criteria. |
16
+ | `fn_task_update` | Update fields on an existing task. Supports modifying the title, description, dependencies, and assigned agent after task creation. |
17
+ | `fn_task_list` | List all tasks on the Fusion board, grouped by column. |
18
+ | `fn_task_show` | Show full details for a task including steps, progress, and log entries. |
19
+ | `fn_task_attach` | Attach a file to a task. Supports images (png, jpg, gif, webp) and text files (txt, log, json, yaml, yml, toml, csv, xml). |
20
+ | `fn_task_pause` | Pause a task — stops all automated agent and scheduler interaction for this task. |
21
+ | `fn_task_unpause` | Unpause a task — resumes automated agent and scheduler interaction. |
22
+ | `fn_task_retry` | Retry a failed task clears the error state and moves it back to the todo column for re-execution. |
23
+ | `fn_task_duplicate` | Duplicate an existing task, creating a fresh copy in triage. Copies the title and description but resets all execution state. The AI triage agent will re-specify the new task. |
24
+ | `fn_task_refine` | Request a refinement of a completed or in-review task. Creates a new follow-up task in triage that references the original task as a dependency. Use this when a done or in-review task needs additional work, improvements, or follow-up changes. |
25
+ | `fn_task_archive` | Archive a done task (move from done → archived). Archived tasks are preserved for historical reference but moved out of the main board view. |
26
+ | `fn_task_unarchive` | Unarchive an archived task (move from archived → done). Restores the task to the done column. |
27
+ | `fn_task_delete` | Permanently delete a task from the Fusion board. Tasks are deleted immediately and cannot be recovered. |
28
+ | `fn_task_import_github` | Import GitHub issues as Fusion tasks. Fetches open issues from a repository and creates tasks in the triage column. Each task includes the issue title and body with a link to the source issue. |
29
+ | `fn_task_import_github_issue` | Import a specific GitHub issue as a Fusion task. Fetches the issue by number and creates a single task in the triage column with the issue title and body. |
30
+ | `fn_task_browse_github_issues` | List open GitHub issues from a repository to browse before importing. Returns issue numbers, titles, and URLs for selection. Use with fn_task_import_github_issue to import specific issues by number. |
31
+ | `fn_task_plan` | Create a task via AI-guided planning mode — interactive conversation to refine your idea into a well-specified task. |
32
+ | `fn_mission_create` | Create a new mission — a high-level objective that can span multiple milestones. Missions contain milestones that break down work into phases. |
33
+ | `fn_mission_list` | List all missions with their current status. |
34
+ | `fn_mission_show` | Show mission details with full hierarchy: milestones → slices → features. |
35
+ | `fn_mission_delete` | Delete a mission and all its milestones, slices, and features. Cannot be undone. |
36
+ | `fn_milestone_add` | Add a milestone to a mission. Milestones represent phases of work. |
37
+ | `fn_slice_add` | Add a slice to a milestone. Slices are work units that can be activated for implementation. |
38
+ | `fn_feature_add` | Add a feature to a slice. Features are deliverables that can be linked to tasks. |
39
+ | `fn_slice_activate` | Activate a pending slice for implementation. Sets status to 'active' and enables task linking for its features. |
40
+ | `fn_feature_link_task` | Link a feature to a fn task for implementation. Updates the feature status to 'triaged' and associates it with the task. |
41
+ | `fn_agent_stop` | Stop a running agent — pauses its execution. Transitions the agent from running/active to paused state. |
42
+ | `fn_agent_start` | Start a stopped agent — resumes its execution. Transitions the agent from paused to active state. |
43
+ | `fn_skills_search` | Search the skills.sh directory for agent skills. Returns matching skills with names, sources (owner/repo), install counts, and install commands. Use fn_skills_install to install a selected skill. |
44
+ | `fn_skills_install` | Install an agent skill from skills.sh into the current project. Downloads skill files into the project's skill directories (.fusion/skills/, legacy .pi/skills/, .agents/skills/). The skill becomes available to AI agents in subsequent sessions. |
45
+ <!-- END: fusion-capabilities-tool-table -->
44
46
 
45
47
  ## CLI Commands (fn)
46
48
 
@@ -1 +0,0 @@
1
- import{r as s,j as e}from"./vendor-react-K0fH_qHe.js";import{i as Le,ax as lt,g as ct,ay as rt,a as ot,az as dt,aA as ut,aB as ht,aC as mt,aD as gt,aE as ft,s as pt,aF as xt,aG as St,f as vt,w as Pe,S as bt,I as Be,aj as wt,aH as Nt,B as Ce,aI as kt,aJ as Ct,aK as Mt,aL as jt,aM as yt,aN as Tt,h as At,j as $t}from"./index-rNf7s96d.js";import"./vendor-xterm-DzcZoU0P.js";const Re="kb-chat-active-session";function Dt(n){const i=n?.toolCalls;if(!Array.isArray(i))return;const r=i.map(l=>{if(!l||typeof l!="object")return null;const c=l,x=typeof c.toolName=="string"?c.toolName:"";if(!x)return null;const b=c.args;return{toolName:x,...b&&typeof b=="object"?{args:b}:{},isError:!!c.isError,result:c.result,status:"completed"}}).filter(l=>l!==null);return r.length>0?r:void 0}function He(n){return{id:n.id,sessionId:n.sessionId,role:n.role,content:n.content,thinkingOutput:n.thinkingOutput,toolCalls:Dt(n.metadata),createdAt:n.createdAt}}function Et(n){const[i,r]=s.useState([]),[l,c]=s.useState(null),[x,b]=s.useState(!0),[M,w]=s.useState([]),[X,z]=s.useState(!1),[O,L]=s.useState(!1),[re,D]=s.useState(""),[V,T]=s.useState(""),[pe,E]=s.useState([]),[S,P]=s.useState(""),[Z,U]=s.useState(""),[A,B]=s.useState(!0),[q,ne]=s.useState(new Map),k=s.useRef(null),J=s.useRef(!1),K=s.useRef(""),xe=s.useRef(i),H=s.useRef(l),we=s.useRef(O);xe.current=i,H.current=l,we.current=O,s.useEffect(()=>{K.current=S},[S]);const ae=s.useRef(new Set),ee=s.useRef(0),Ne=s.useRef(n);Ne.current!==n&&(Ne.current=n,ee.current++),s.useEffect(()=>{const o=ee.current;Le(void 0,n).then(h=>{if(ee.current!==o)return;const u=new Map;for(const N of h)u.set(N.id,N);ne(u)}).catch(()=>{})},[n]);const te=s.useCallback(async()=>{b(!0);try{const h=[...(await lt(n)).sessions].sort((u,N)=>new Date(N.updatedAt).getTime()-new Date(u.updatedAt).getTime());r(h)}catch{}finally{b(!1)}},[n]);s.useEffect(()=>{te()},[te]);const Q=s.useRef(()=>{});s.useEffect(()=>{if(x)return;const o=ct(Re,n);o&&i.find(u=>u.id===o)&&Q.current(o)},[x,i,n]);const I=s.useCallback(async(o,h)=>{z(!0);try{const u=await rt(o,{limit:50,...h},n),N=u.messages.map(He);h?.offset&&h.offset>0?w(W=>[...N,...W]):w(N),B(u.messages.length>=50)}catch{}finally{z(!1)}},[n]),oe=s.useCallback(o=>{k.current&&(k.current.close(),k.current=null);const h=i.find(u=>u.id===o);c(h||null),D(""),T(""),E([]),L(!1),B(!0),o?I(o):w([]),o?ot(Re,o,n):dt(Re,n)},[i,I,n]);Q.current=oe;const de=s.useCallback(async o=>{const h=await ut(o,n),u={id:h.session.id,title:h.session.title,agentId:h.session.agentId,status:h.session.status,modelProvider:h.session.modelProvider,modelId:h.session.modelId,createdAt:h.session.createdAt,updatedAt:h.session.updatedAt};return r(N=>[u,...N]),c(u),w([]),D(""),T(""),E([]),L(!1),B(!0),u},[n]),ue=s.useCallback(async o=>{await ht(o,{status:"archived"},n),r(h=>h.filter(u=>u.id!==o)),l?.id===o&&(c(null),w([]))},[l,n]),ie=s.useCallback(async o=>{l?.id===o&&k.current&&(k.current.close(),k.current=null),await mt(o,n),r(h=>h.filter(u=>u.id!==o)),l?.id===o&&(c(null),w([]))},[l,n]),he=s.useCallback(async()=>{!l||!A||await I(l.id,{offset:M.length})},[l,A,I,M.length]),se=s.useCallback(()=>{l&&(J.current=!0,k.current?.close(),k.current=null,gt(l.id,n).catch(()=>{}),L(!1),D(""),T(""),E([]))},[l,n]),_=s.useCallback(()=>{K.current="",P("")},[]),G=s.useCallback(o=>{if(!l)return;if(O){K.current=o,P(o);return}J.current=!1,k.current&&(k.current.close(),k.current=null);const h=`temp-${Date.now()}`,u={id:h,sessionId:l.id,role:"user",content:o,createdAt:new Date().toISOString()};w(p=>[...p,u]),D(""),T(""),E([]),L(!0);let N="",W="",R=[];const m={onThinking:p=>{W+=p,T(W)},onText:p=>{N+=p,D(N)},onToolStart:p=>{R=[...R,{toolName:p.toolName,args:p.args,isError:!1,status:"running"}],E(R)},onToolEnd:p=>{const C=[...R];for(let g=C.length-1;g>=0;g--){const v=C[g];if(v?.toolName===p.toolName&&v.status==="running"){C[g]={...v,status:"completed",isError:p.isError,result:p.result},R=C,E(C);return}}R=[...C,{toolName:p.toolName,isError:p.isError,result:p.result,status:"completed"}],E(R)},onDone:p=>{const C={id:p.messageId||`msg-${Date.now()}`,sessionId:l.id,role:"assistant",content:N,thinkingOutput:W,toolCalls:R.length>0?R:void 0,createdAt:new Date().toISOString()};ae.current.add(C.id),w(v=>[...v,C]),D(""),T(""),E([]),L(!1),k.current=null,setTimeout(()=>{ae.current.delete(C.id)},1e3),te();const g=K.current.trim();g&&(K.current="",P(""),G(g))},onError:p=>{if(w(C=>C.filter(g=>g.id!==h)),D(""),T(""),E([]),L(!1),k.current=null,console.error("[useChat] Stream error:",p),!J.current){const C=K.current.trim();C&&(K.current="",P(""),G(C))}}};k.current=ft(l.id,o,m,n)},[l,O,n,te]),Se=Z?i.filter(o=>o.title?.toLowerCase().includes(Z.toLowerCase())||o.agentId.toLowerCase().includes(Z.toLowerCase())):i;return s.useEffect(()=>{const o=ee.current,h=n?`?projectId=${encodeURIComponent(n)}`:"",u=()=>ee.current!==o,N=g=>{if(u())return;const v=JSON.parse(g.data);r(f=>f.some(j=>j.id===v.id)?f:[v,...f])},W=g=>{if(u())return;const v=JSON.parse(g.data);r(f=>[...f.map(ve=>ve.id===v.id?v:ve)]),H.current?.id===v.id&&c(v)},R=g=>{if(u())return;const{id:v}=JSON.parse(g.data);r(f=>f.filter(j=>j.id!==v)),H.current?.id===v&&(c(null),w([]))},m=g=>{if(u())return;const v=JSON.parse(g.data),f=He(v);ae.current.has(f.id)||H.current?.id===f.sessionId&&!we.current&&w(j=>j.some(ve=>ve.id===f.id)?j:[...j,f])},p=g=>{if(u())return;const{id:v}=JSON.parse(g.data);w(f=>f.filter(j=>j.id!==v))};return pt(`/api/events${h}`,{events:{"chat:session:created":N,"chat:session:updated":W,"chat:session:deleted":R,"chat:message:added":m,"chat:message:deleted":p}})},[n]),s.useEffect(()=>()=>{k.current&&(k.current.close(),k.current=null)},[]),{sessions:i,activeSession:l,sessionsLoading:x,messages:M,messagesLoading:X,isStreaming:O,streamingText:re,streamingThinking:V,streamingToolCalls:pe,pendingMessage:S,selectSession:oe,createSession:de,archiveSession:ue,deleteSession:ie,sendMessage:G,stopStreaming:se,clearPendingMessage:_,loadMoreMessages:he,hasMoreMessages:A,searchQuery:Z,setSearchQuery:U,filteredSessions:Se,refreshSessions:te,agentsMap:q}}function _e(n){const i=new Date(n),l=new Date().getTime()-i.getTime(),c=Math.floor(l/1e3),x=Math.floor(c/60),b=Math.floor(x/60),M=Math.floor(b/24);return c<60?"just now":x<60?`${x}m ago`:b<24?`${b}h ago`:M<7?`${M}d ago`:i.toLocaleDateString()}function Ge(n,i){if(!n||!i)return null;const r=i.toLowerCase();if(r.includes("claude")){let c=i.replace(/^claude[- ]/i,"Claude ").replace(/sonnet[- ](\d+)[- ](\d+)/i,"Sonnet $1.$2").replace(/sonnet[- ](\d+)/i,"Sonnet $1").replace(/haiku[- ](\d+)/i,"Haiku $1").replace(/opus[- ](\d+)/i,"Opus $1").replace(/sonnet/i,"Sonnet").replace(/haiku/i,"Haiku").replace(/opus/i,"Opus").replace(/-/g," ").trim();return c=c.replace(/\s+/g," "),c.length>30?c.slice(0,30)+"…":c}if(r.includes("gpt")||r.includes("openai")){const c=i.replace(/^gpt-4-turbo$/i,"GPT-4 Turbo").replace(/^gpt-4o-mini$/i,"GPT-4o Mini").replace(/^gpt-4o$/i,"GPT-4o").replace(/^gpt-4$/i,"GPT-4").replace(/^gpt-o1-preview$/i,"GPT-o1 Preview").replace(/^gpt-o1-mini$/i,"GPT-o1 Mini").replace(/^gpt-o1$/i,"GPT-o1").replace(/^gpt/i,"GPT").trim();return c.length>30?c.slice(0,30)+"…":c}if(r.includes("gemini")){const c=i.replace(/^gemini[- ]/i,"Gemini ").replace(/pro[- ](\d+)[- ](\d+)/i,"Pro $1.$2").replace(/pro[- ](\d+)/i,"Pro $1").replace(/-/g," ").replace(/\s+/g," ").trim();return c.length>30?c.slice(0,30)+"…":c}const l=i.replace(/-/g," ").replace(/^\w/,c=>c.toUpperCase()).replace(/\s+/g," ").trim();return l.length>30?l.slice(0,30)+"…":l}function Me(n,i){return n.length>i?`${n.slice(0,i)}…`:n}function Pt(n){if(!n)return null;const i=Object.entries(n);return i.length===0?null:i.map(([r,l])=>{let c="";if(typeof l=="string")c=l;else try{c=JSON.stringify(l)}catch{c=String(l)}return`${r}=${Me(c,50)}`}).join(", ")}function Rt(n){if(n===void 0)return null;if(typeof n=="string")return Me(n,200);try{return Me(JSON.stringify(n),200)}catch{return Me(String(n),200)}}function Ue(n){return!n||n.length===0?null:e.jsxs("div",{className:"chat-tool-calls","data-testid":"chat-tool-calls",children:[e.jsxs("div",{className:"chat-tool-calls-header",children:[e.jsx(Tt,{size:12,"aria-hidden":"true"}),e.jsx("span",{children:"Tool calls"})]}),n.map((i,r)=>{const l=i.status==="running",c=i.status==="completed"&&i.isError,x=Pt(i.args),b=Rt(i.result),M=l?x:b?`result: ${b}`:x?`args: ${x}`:null,w=l?"running":c?"error":"completed";return e.jsxs("details",{className:`chat-tool-call${l?" chat-tool-call--running":""}${c?" chat-tool-call--error":""}`,open:l,children:[e.jsxs("summary",{children:[e.jsx("span",{className:"chat-tool-call-status-dot","aria-hidden":"true"}),e.jsx("span",{className:"chat-tool-call-name",children:i.toolName}),M&&e.jsx("span",{className:"chat-tool-call-preview",title:M,children:M}),e.jsx("span",{className:"chat-tool-call-status-text",children:w})]}),e.jsxs("div",{className:"chat-tool-call-content",children:[x&&e.jsxs("div",{className:"chat-tool-call-row",children:[e.jsx("span",{className:"chat-tool-call-label",children:"args"}),e.jsx("span",{className:"chat-tool-call-value",children:x})]}),b&&e.jsxs("div",{className:`chat-tool-call-row${c?" chat-tool-call-row--error":""}`,children:[e.jsx("span",{className:"chat-tool-call-label",children:"result"}),e.jsx("span",{className:"chat-tool-call-value",children:b})]})]})]},`${i.toolName}-${r}`)})]})}const je="__fn_agent__";function qe(n){const i=/(^|[\s])\/([^\s]*)$/.exec(n);if(!i)return null;const r=i[1]??"",l=i[2]??"",c=i.index+r.length;return{filter:l,start:c,end:n.length}}function Lt(n,i){const r=n.slice(0,i),l=/(^|[\s\n])@([\w-]*)$/.exec(r);if(!l)return null;const c=l[2]??"",x=r.length-c.length-1;return{filter:c,start:x,end:i}}function It({projectId:n,onClose:i,onCreate:r}){const[l,c]=s.useState("agent"),[x,b]=s.useState([]),[M,w]=s.useState(!0),[X,z]=s.useState(""),[O,L]=s.useState([]),[re,D]=s.useState(!0),[V,T]=s.useState("");s.useEffect(()=>{let S=!1;return w(!0),Le(void 0,n).then(P=>{S||b(P)}).catch(()=>{S||b([])}).finally(()=>{S||w(!1)}),()=>{S=!0}},[n]),s.useEffect(()=>{D(!0),At().then(S=>{L(S.models)}).catch(()=>{L([])}).finally(()=>{D(!1)})},[]);const pe=S=>{if(S.preventDefault(),l==="agent"){if(!X)return;r({agentId:X});return}if(!V)return;const P=V.indexOf("/");if(P<=0)return;const Z=V.slice(0,P),U=V.slice(P+1);r({agentId:je,modelProvider:Z,modelId:U})},E=l==="agent"?!X:!V;return e.jsx("div",{className:"chat-new-dialog-backdrop",onClick:i,role:"dialog","aria-modal":"true",children:e.jsxs("div",{className:"chat-new-dialog",onClick:S=>S.stopPropagation(),children:[e.jsx("h3",{children:"New Chat"}),e.jsxs("div",{className:"chat-new-dialog-mode-toggle","data-testid":"chat-new-dialog-mode-toggle",children:[e.jsx("button",{type:"button",className:`chat-new-dialog-mode-btn${l==="agent"?" chat-new-dialog-mode-btn--active":""}`,"data-testid":"chat-new-dialog-mode-agent",onClick:()=>{c("agent"),T("")},children:"Agent"}),e.jsx("button",{type:"button",className:`chat-new-dialog-mode-btn${l==="model"?" chat-new-dialog-mode-btn--active":""}`,"data-testid":"chat-new-dialog-mode-model",onClick:()=>{c("model"),z("")},children:"Model"})]}),e.jsxs("form",{onSubmit:pe,children:[l==="agent"&&e.jsxs("label",{className:"chat-new-dialog-model-label",children:["Agent",M?e.jsx("div",{className:"chat-new-dialog-loading",children:"Loading agents..."}):x.length===0?e.jsx("div",{className:"chat-new-dialog-empty",children:"No agents available"}):e.jsx("div",{className:"chat-new-dialog-agent-list",children:x.map(S=>e.jsxs("button",{type:"button",className:`chat-new-dialog-agent-item${X===S.id?" chat-new-dialog-agent-item--selected":""}`,onClick:()=>z(S.id),"data-testid":`agent-option-${S.id}`,children:[e.jsx(Ce,{size:16}),e.jsx("span",{className:"chat-new-dialog-agent-name",children:S.name}),e.jsx("span",{className:"chat-new-dialog-agent-role",children:S.role})]},S.id))})]}),l==="model"&&e.jsx("div",{className:"chat-new-dialog-model-dropdown","data-testid":"chat-new-dialog-model-section",children:re?e.jsx("div",{className:"chat-new-dialog-loading",children:"Loading models..."}):e.jsx($t,{models:O,value:V,onChange:T,label:"Model",placeholder:"Select a model"})}),e.jsxs("div",{className:"chat-new-dialog-actions",children:[e.jsx("button",{type:"button",className:"btn btn-sm",onClick:i,children:"Cancel"}),e.jsx("button",{type:"submit",className:"btn btn-sm btn-primary",disabled:E,children:"Create"})]})]})]})})}function Vt({projectId:n,addToast:i}){const{activeSession:r,sessionsLoading:l,messages:c,messagesLoading:x,isStreaming:b,streamingText:M,streamingThinking:w,streamingToolCalls:X,selectSession:z,createSession:O,archiveSession:L,deleteSession:re,sendMessage:D,stopStreaming:V,pendingMessage:T,clearPendingMessage:pe,searchQuery:E,setSearchQuery:S,filteredSessions:P}=Et(n),[Z,U]=s.useState(!1),[A,B]=s.useState(""),[q,ne]=s.useState(null),[k,J]=s.useState(null),[K,xe]=s.useState(!0),[H,we]=s.useState(new Map),[ae,ee]=s.useState([]),[Ne,te]=s.useState(!0),[Q,I]=s.useState(!1),[oe,de]=s.useState(""),[ue,ie]=s.useState(0),[he,se]=s.useState(""),[_,G]=s.useState(!1),[Se,o]=s.useState(0),[h,u]=s.useState(-1),[,N]=s.useState(!1),[W,R]=s.useState({top:0,left:0}),m=xt({projectId:n}),p=s.useCallback(t=>{if(!t||!m.mentionActive)return;const a=t.getBoundingClientRect();R({top:a.top-260,left:a.left+8})},[m.mentionActive]),C=s.useRef(null),g=s.useRef(null),v=s.useRef(null),f=s.useRef(null),j=s.useRef(0),me=St()==="mobile",F=s.useMemo(()=>{const t=oe.trim().toLowerCase();return(t?ae.filter(d=>d.name.toLowerCase().includes(t)):ae).slice(0,10)},[ae,oe]),ge=s.useMemo(()=>Array.from(H.values()),[H]),le=s.useMemo(()=>{const t=he.trim().toLowerCase();return t?ge.filter(a=>a.name.toLowerCase().includes(t)):ge},[ge,he]),Ie=s.useMemo(()=>{const t=new Map;for(const a of ge)t.set(a.name.toLowerCase(),a);return t},[ge]);s.useEffect(()=>{ie(0)},[F]),s.useEffect(()=>{o(0)},[he,_]),s.useEffect(()=>()=>{g.current!==null&&window.clearTimeout(g.current)},[]),s.useEffect(()=>{C.current?.scrollIntoView({behavior:"smooth"})},[c,M]),s.useEffect(()=>{const t=()=>ne(null);if(q)return document.addEventListener("click",t),()=>document.removeEventListener("click",t)},[q]),s.useEffect(()=>{let t=!1;const a=n;return Le(void 0,n).then(d=>{if(t||a!==n)return;const y=new Map;for(const $ of d)y.set($.id,$);we(y)}).catch(()=>{}),()=>{t=!0}},[n]),s.useEffect(()=>{let t=!1;return te(!0),vt(n).then(a=>{t||ee(a)}).catch(()=>{t||ee([])}).finally(()=>{t||te(!1)}),()=>{t=!0}},[n]);const Je=s.useCallback(async t=>{try{await O(t),U(!1),me&&xe(!1)}catch{i("Failed to create chat session","error")}},[O,i,me]),ye=s.useCallback(()=>{const t=A.trim();!t||!r||(B(""),I(!1),de(""),G(!1),se(""),u(-1),D(t))},[A,r,D]),Te=s.useCallback(t=>{B(a=>{const d=qe(a);if(!d)return a;const y=`/skill:${t.name} `,$=a.slice(0,d.start)+y+a.slice(d.end);return window.requestAnimationFrame(()=>{f.current&&(f.current.style.height="auto",f.current.style.height=`${Math.min(f.current.scrollHeight,120)}px`,f.current.focus())}),$}),I(!1),de(""),ie(0)},[]),Ae=s.useCallback(t=>{const a=f.current;if(!a||h<0)return;const d=a.selectionStart??j.current,y=a.selectionEnd??d,$=Math.max(d,y),be=Math.min(h,$),ce=`${`@${t.name.replace(/\s+/g,"_")}`} `,Ee=A.slice(0,be)+ce+A.slice($),fe=be+ce.length;B(Ee),G(!1),se(""),o(0),u(-1),window.requestAnimationFrame(()=>{f.current&&(f.current.style.height="auto",f.current.style.height=`${Math.min(f.current.scrollHeight,120)}px`,f.current.focus(),f.current.setSelectionRange(fe,fe))})},[h,A]),Fe=s.useCallback(t=>{const a=/@([\w-]+)/g,d=[];let y=0,$=a.exec(t);for(;$;){const[be,Ve=""]=$,ce=$.index;ce>y&&d.push(t.slice(y,ce));const Ee=Ve.replace(/_/g," ").toLowerCase(),fe=Ie.get(Ee);fe?d.push(e.jsxs("span",{className:"chat-mention-chip",children:["@",fe.name.replace(/\s+/g,"_")]},`${fe.id}-${ce}`)):d.push(be),y=ce+be.length,$=a.exec(t)}return y<t.length&&d.push(t.slice(y)),d.length===0?t:d},[Ie]),Ke=s.useCallback(t=>{if(j.current=t.currentTarget.selectionStart??j.current,m.mentionActive&&m.files.length>0){if(m.handleKeyDown(t,A),t.key==="Enter"||t.key==="Tab"){const a=m.files[m.selectedIndex];if(a){const d=m.selectFile(a,A);B(d),m.dismissMention(),N(!1)}}return}if(_&&t.key==="ArrowDown"){t.preventDefault(),le.length>0&&o(a=>(a+1)%le.length);return}if(_&&t.key==="ArrowUp"){t.preventDefault(),le.length>0&&o(a=>a===0?le.length-1:a-1);return}if(_&&t.key==="Enter"){t.preventDefault();const a=le[Se]??le[0];a&&Ae(a);return}if(_&&t.key==="Escape"){t.preventDefault(),G(!1),se(""),u(-1);return}if(Q&&t.key==="ArrowDown"){t.preventDefault(),F.length>0&&ie(a=>(a+1)%F.length);return}if(Q&&t.key==="ArrowUp"){t.preventDefault(),F.length>0&&ie(a=>a===0?F.length-1:a-1);return}if(Q&&(t.key==="Enter"||t.key==="Tab")&&F.length>0){t.preventDefault();const a=F[ue]??F[0];a&&Te(a);return}if(Q&&t.key==="Escape"){t.preventDefault(),I(!1);return}t.key==="Enter"&&!t.shiftKey&&(t.preventDefault(),ye())},[_,le,Se,Ae,Q,F,ue,Te,ye,m,A]),ke=s.useCallback((t,a)=>{const d=Lt(t,a);if(d){G(!0),se(d.filter),u(d.start);return}G(!1),se(""),u(-1)},[]),Qe=s.useCallback(t=>{const a=t.target,d=a.value,y=a.selectionStart??d.length;j.current=y,B(d);const $=qe(d);$?(I(!0),de($.filter)):(I(!1),de("")),ke(d,y),m.detectMention(d,y),N(m.mentionActive),m.mentionActive&&p(a),a.style.height="auto",a.style.height=`${Math.min(a.scrollHeight,120)}px`},[ke]),$e=s.useCallback(t=>{const a=t.currentTarget,d=a.selectionStart??a.value.length;j.current=d,ke(a.value,d),m.detectMention(a.value,d),N(m.mentionActive),m.mentionActive&&p(a)},[ke,m,p]),We=s.useCallback(t=>{t.key!=="Escape"&&$e(t)},[$e]),Ye=s.useCallback(()=>{g.current!==null&&window.clearTimeout(g.current),g.current=window.setTimeout(()=>{I(!1),G(!1),se(""),u(-1),N(!1),m.dismissMention(),g.current=null},120)},[m]),Xe=s.useCallback(()=>{g.current!==null&&(window.clearTimeout(g.current),g.current=null)},[]),Ze=s.useCallback(async t=>{ne(null);try{await L(t),i("Conversation archived","success")}catch{i("Failed to archive conversation","error")}},[L,i]),et=s.useCallback(async t=>{J(null),ne(null);try{await re(t),i("Conversation deleted","success")}catch{i("Failed to delete conversation","error")}},[re,i]),tt=s.useCallback(t=>{z(t),me&&xe(!1)},[z,me]),st=s.useCallback(()=>{z(""),xe(!0)},[z]),nt=()=>e.jsxs("div",{className:"chat-empty-state",children:[e.jsx(yt,{size:48,strokeWidth:1.5}),e.jsx("h2",{children:"Start a new conversation"}),e.jsxs("button",{className:"btn btn-primary",onClick:()=>U(!0),children:[e.jsx(Pe,{size:16}),"New Chat"]})]}),Y=Ge(r?.modelProvider,r?.modelId),ze=r?.agentId===je?Y??"Fusion":r?.title||H.get(r?.agentId??"")?.name||r?.agentId||"Chat",at=!!(Y&&Y!==ze),De=H.get(r?.agentId??"")?.name||(r?.agentId===je?Y??"Fusion":r?.agentId?.slice(0,30)??"Fusion"),Oe=!!(Y&&Y!==De),it=T.length>50?`${T.slice(0,50)}…`:T;return e.jsxs("div",{className:"chat-view",children:[e.jsxs("div",{className:`chat-sidebar${K?"":" chat-sidebar--hidden"}`,children:[e.jsx("div",{className:"chat-sidebar-header",children:e.jsxs("button",{className:"btn btn-sm btn-primary",onClick:()=>U(!0),"data-testid":"chat-new-btn",children:[e.jsx(Pe,{size:14}),"New Chat"]})}),e.jsx("div",{className:"chat-sidebar-search",children:e.jsxs("div",{className:"chat-sidebar-search-wrapper",children:[e.jsx(bt,{size:14,className:"chat-sidebar-search-icon"}),e.jsx("input",{type:"text",className:"chat-sidebar-search",placeholder:"Search conversations...",value:E,onChange:t=>S(t.target.value),"data-testid":"chat-search-input"})]})}),e.jsx("div",{className:"chat-session-list chat-sidebar-list",children:l?e.jsx("div",{style:{padding:"12px",color:"var(--text-secondary)",fontSize:"13px"},children:"Loading..."}):P.length===0?e.jsx("div",{style:{padding:"12px",color:"var(--text-secondary)",fontSize:"13px"},children:"No conversations yet"}):P.map(t=>e.jsxs("div",{className:`chat-session-item${r?.id===t.id?" chat-session-item--active":""}`,onClick:()=>tt(t.id),onContextMenu:a=>{a.preventDefault(),ne({sessionId:t.id,x:a.clientX,y:a.clientY})},"data-testid":`chat-session-${t.id}`,children:[e.jsx("button",{className:"chat-session-delete-btn",onClick:a=>{a.stopPropagation(),J(t.id)},"data-testid":"chat-session-delete-btn","aria-label":"Delete conversation",children:e.jsx(Be,{size:14})}),e.jsx("div",{className:"chat-session-title",children:t.title||"Untitled"}),e.jsx("div",{className:"chat-session-preview",children:t.lastMessagePreview||"No messages"}),e.jsxs("div",{className:"chat-session-meta",children:[e.jsx("span",{children:H.get(t.agentId)?.name||(t.agentId===je?Ge(t.modelProvider,t.modelId)??"Fusion":t.agentId.slice(0,30))}),e.jsx("span",{children:t.updatedAt?_e(t.updatedAt):""})]})]},t.id))}),e.jsx("div",{className:"chat-sidebar-footer",children:e.jsxs("button",{className:"btn btn-sm btn-primary chat-sidebar-footer-btn",onClick:()=>U(!0),"data-testid":"chat-new-btn-mobile",children:[e.jsx(Pe,{size:14}),"New Chat"]})})]}),q&&e.jsxs("div",{className:"chat-session-context-menu",style:{top:q.y,left:q.x},onClick:t=>t.stopPropagation(),children:[e.jsxs("button",{onClick:()=>Ze(q.sessionId),"data-testid":"chat-context-archive",children:[e.jsx(wt,{size:14}),"Archive"]}),e.jsxs("button",{onClick:()=>{ne(null),J(q.sessionId)},"data-testid":"chat-context-delete",children:[e.jsx(Be,{size:14}),"Delete"]})]}),k&&e.jsx("div",{className:"chat-new-dialog-backdrop",onClick:()=>J(null),children:e.jsxs("div",{className:"chat-new-dialog",onClick:t=>t.stopPropagation(),children:[e.jsx("h3",{children:"Delete Conversation?"}),e.jsx("p",{style:{fontSize:"14px",color:"var(--text-secondary)",marginBottom:"16px"},children:"This action cannot be undone. All messages in this conversation will be permanently deleted."}),e.jsxs("div",{className:"chat-new-dialog-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:()=>J(null),children:"Cancel"}),e.jsx("button",{className:"btn btn-sm btn-danger",onClick:()=>void et(k),children:"Delete"})]})]})}),e.jsxs("div",{className:"chat-thread",children:[(r||!me)&&e.jsxs("div",{className:"chat-thread-header",children:[me&&r&&e.jsx("button",{className:"btn-icon",onClick:st,"data-testid":"chat-back-btn",children:e.jsx(Nt,{size:16})}),e.jsx(Ce,{size:16}),e.jsx("span",{className:"chat-thread-header-title",children:ze}),at&&e.jsx("span",{className:"chat-model-tag",children:Y})]}),e.jsxs("div",{className:"chat-messages",ref:v,children:[x?e.jsx("div",{style:{color:"var(--text-secondary)",fontSize:"13px"},children:"Loading messages..."}):c.length===0&&!r?nt():c.length===0&&r?e.jsx("div",{style:{color:"var(--text-secondary)",fontSize:"13px"},children:"No messages yet. Start the conversation!"}):e.jsxs(e.Fragment,{children:[c.map(t=>e.jsxs("div",{className:`chat-message chat-message--${t.role}`,"data-testid":`chat-message-${t.id}`,children:[t.role==="assistant"&&e.jsxs("div",{className:"chat-message-avatar",children:[e.jsx(Ce,{size:14}),e.jsx("span",{children:De}),Oe&&e.jsx("span",{className:"chat-model-tag",children:Y})]}),e.jsx("div",{className:"chat-message-content",children:Fe(t.content)}),Ue(t.toolCalls),t.thinkingOutput&&e.jsxs("details",{className:"chat-message-thinking",children:[e.jsx("summary",{children:"Thinking"}),e.jsx("pre",{className:"chat-message-thinking-content",children:t.thinkingOutput})]}),e.jsx("div",{className:"chat-message-time",children:_e(t.createdAt)})]},t.id)),b&&e.jsxs("div",{className:"chat-message chat-message--assistant chat-message--streaming",children:[e.jsxs("div",{className:"chat-message-avatar",children:[e.jsx(Ce,{size:14}),e.jsx("span",{children:De}),Oe&&e.jsx("span",{className:"chat-model-tag",children:Y})]}),M?e.jsx("div",{className:"chat-message-content",children:Fe(M)}):e.jsx("div",{className:"chat-message-content chat-message-content--waiting",children:w?"Thinking…":"Connecting…"}),Ue(X),w&&e.jsxs("details",{className:"chat-message-thinking",children:[e.jsx("summary",{children:"Thinking"}),e.jsx("pre",{className:"chat-message-thinking-content",children:w})]}),e.jsxs("div",{className:"chat-typing-indicator",children:[e.jsx("span",{}),e.jsx("span",{}),e.jsx("span",{})]})]})]}),e.jsx("div",{ref:C})]}),r&&e.jsxs("div",{className:"chat-input-area",children:[Q&&e.jsx("div",{className:"chat-skill-menu","data-testid":"chat-skill-menu",role:"listbox","aria-label":"Skill suggestions",children:Ne?e.jsx("div",{className:"chat-skill-menu-empty",children:"Loading skills…"}):F.length===0?e.jsx("div",{className:"chat-skill-menu-empty",children:oe?"No skills found":"No skills available"}):F.map((t,a)=>e.jsxs("button",{type:"button",role:"option","aria-selected":a===ue,className:`chat-skill-menu-item${a===ue?" chat-skill-menu-item--highlighted":""}`,onMouseDown:d=>d.preventDefault(),onMouseEnter:()=>ie(a),onClick:()=>Te(t),children:[e.jsx("span",{className:"chat-skill-menu-item-name",children:t.name}),e.jsx("span",{className:"chat-skill-menu-item-description",title:t.relativePath,children:t.relativePath})]},t.id))}),e.jsxs("div",{className:"chat-input-wrapper",children:[e.jsx("textarea",{ref:f,className:"chat-input-textarea",placeholder:"Type a message...",value:A,onChange:Qe,onKeyDown:Ke,onKeyUp:We,onClick:$e,onBlur:Ye,onFocus:Xe,rows:1,"data-testid":"chat-input"}),e.jsx(kt,{agents:ge,filter:he,highlightedIndex:Se,visible:_,onSelect:Ae,position:"below"}),e.jsx(Ct,{visible:m.mentionActive&&!_,position:W,files:m.files,selectedIndex:m.selectedIndex,onSelect:t=>{const a=m.selectFile(t,A);B(a),m.dismissMention(),N(!1),f.current?.focus()},loading:m.loading}),T&&e.jsxs("div",{className:"chat-pending-message","data-testid":"chat-pending-indicator",children:[e.jsx("span",{children:`Queued: ${it}`}),e.jsx("button",{type:"button",className:"chat-pending-message-dismiss","aria-label":"Dismiss queued message","data-testid":"chat-pending-dismiss",onClick:pe,children:"×"})]})]}),b?e.jsx("button",{className:"chat-input-stop",onClick:V,"aria-label":"Stop generation","data-testid":"chat-stop-btn",children:e.jsx(Mt,{size:14})}):e.jsx("button",{className:"chat-input-send",onClick:()=>void ye(),disabled:!A.trim(),"data-testid":"chat-send-btn",children:e.jsx(jt,{size:16})})]})]}),Z&&e.jsx(It,{projectId:n,onClose:()=>U(!1),onCreate:Je})]})}export{Vt as ChatView};
@@ -1,2 +0,0 @@
1
- import{r as a,j as e}from"./vendor-react-K0fH_qHe.js";import{b9 as Ae,ba as je,bb as Se,bc as Fe,o as ze,bd as De,be as Ie,bf as Le,bg as Ne,bh as Ce,bi as Re,bj as Te,bk as Ee,bl as We,n as qe,bm as Pe,L as w,bn as ke}from"./index-rNf7s96d.js";import"./vendor-xterm-DzcZoU0P.js";const U=".fusion/memory/MEMORY.md",_e=5e4,Oe="0 3 * * *",Ue="0 4 * * *";function oe(i){return{memoryEnabled:i.memoryEnabled!==!1,memoryAutoSummarizeEnabled:i.memoryAutoSummarizeEnabled??!1,memoryAutoSummarizeThresholdChars:i.memoryAutoSummarizeThresholdChars??_e,memoryAutoSummarizeSchedule:i.memoryAutoSummarizeSchedule??Oe,memoryDreamsEnabled:i.memoryDreamsEnabled??!1,memoryDreamsSchedule:i.memoryDreamsSchedule??Ue}}function Me(i,t){return i.some(m=>m.path===t)?t:i.find(m=>m.path===U)?.path??i[0]?.path??U}function Qe(i={}){const{projectId:t}=i,[m,x]=a.useState(""),[A,N]=a.useState(!0),[j,S]=a.useState(!1),[h,y]=a.useState(!1),[n,g]=a.useState(null),[Q,J]=a.useState(!0),[X,F]=a.useState(!1),[b,R]=a.useState(()=>oe({})),[C,ee]=a.useState(!0),[ce,B]=a.useState(!1),[c,f]=a.useState([]),[se,z]=a.useState(!0),[o,E]=a.useState(U),[T,u]=a.useState(""),[d,te]=a.useState(!1),[H,p]=a.useState(!1),[ae,$]=a.useState(!1),[ne,Y]=a.useState(!1),[G,W]=a.useState(null),[k,q]=a.useState(!0),[de,D]=a.useState(!1),[ue,V]=a.useState(!1),[re,Z]=a.useState(null),{status:K,loading:v,refresh:ie}=Ae({projectId:t}),I=a.useCallback(s=>{u(s),p(!0)},[]),P=a.useCallback(async s=>{te(!0);try{const{content:r}=await je(s,t);E(s),u(r),p(!1)}finally{te(!1)}},[t]),M=a.useCallback(async()=>{z(!0);try{const{files:s}=await Se(t);if(f(s),s.length===0){E(U),u(""),p(!1);return}const r=Me(s,o);r!==o&&await P(r)}finally{z(!1)}},[t,o,P]);a.useEffect(()=>{let s=!1;async function r(){try{const l=await We(t);s||(x(l.content),N(!1))}catch{s||(x(""),N(!1))}}return r(),()=>{s=!0}},[t]),a.useEffect(()=>{let s=!1;async function r(){try{const l=await Ce(t);s||(g(l.content),F(l.exists),J(!1))}catch{s||(g(null),F(!1),J(!1))}}return r(),()=>{s=!0}},[t]),a.useEffect(()=>{let s=!1;async function r(){ee(!0);try{const l=await qe(t);s||R(oe(l))}catch{s||R(oe({}))}finally{s||ee(!1)}}return r(),()=>{s=!0}},[t]),a.useEffect(()=>{let s=!1;async function r(){z(!0);try{const{files:l}=await Se(t);if(s)return;if(f(l),l.length===0){E(U),u(""),p(!1);return}const me=Me(l,o),{content:we}=await je(me,t);if(s)return;E(me),u(we),p(!1)}catch{s||(f([]),E(U),u(""),p(!1))}finally{s||z(!1)}}return r(),()=>{s=!0}},[t,o]),a.useEffect(()=>{let s=!1;async function r(){try{const l=await Ne(t);s||(W(l),q(!1))}catch{s||(W(null),q(!1))}}return r(),()=>{s=!0}},[t]),a.useEffect(()=>{let s=!1;async function r(){try{const l=await Pe(t);s||Z(l)}catch{s||Z(null)}}return r(),()=>{s=!0}},[t]);const le=a.useCallback(s=>{x(s),S(!0)},[]),he=a.useCallback(async()=>{if(j){y(!0);try{await Fe(m,t),S(!1)}finally{y(!1)}}},[m,j,t]),ye=a.useCallback(async s=>{B(!0);try{const r=await ze(s,t);R(oe(r))}finally{B(!1)}},[t]),ge=a.useCallback(async s=>{await P(s)},[P]),pe=a.useCallback(async()=>{if(H){$(!0);try{await De(o,T,t),p(!1),await M()}finally{$(!1)}}},[T,H,o,t,M]),xe=a.useCallback(async()=>{V(!0);try{const s=await Ie(t);return await ie(),s}finally{V(!1)}},[t,ie]),be=a.useCallback(async s=>Le(s,t),[t]),_=a.useCallback(async()=>{try{const s=await Ne(t);W(s)}catch{W(null)}},[t]),L=a.useCallback(async()=>{try{const s=await Ce(t);g(s.content),F(s.exists)}catch{g(null),F(!1)}},[t]),fe=a.useCallback(async s=>{await Re(s,t),await L()},[t,L]),ve=a.useCallback(async()=>{Y(!0);try{const s=await Te(t);return await Promise.all([L(),_()]),{success:s.success,summary:s.summary}}finally{Y(!1)}},[t,L,_]),O=a.useCallback(async s=>{D(!0);try{const r=s?await Ee(s,t):await Ee(t);if(s){const l=r.path??s;E(l),u(r.content),p(!1),await M();return}x(r.content),S(!0)}finally{D(!1)}},[t,M]);return{workingMemory:m,workingMemoryLoading:A,workingMemoryDirty:j,setWorkingMemory:le,saveWorkingMemory:he,savingWorkingMemory:h,insightsContent:n,insightsLoading:Q,insightsExists:X,refreshInsights:L,saveInsights:fe,memorySettings:b,settingsLoading:C,savingMemorySettings:ce,saveMemorySettings:ye,memoryFiles:c,memoryFilesLoading:se,selectedFilePath:o,selectedFileContent:T,selectedFileLoading:d,selectedFileDirty:H,setSelectedFileContent:I,selectFile:ge,saveSelectedFile:pe,savingSelectedFile:ae,reloadMemoryFiles:M,backendStatus:K,backendLoading:v,extractInsights:ve,extracting:ne,auditReport:G,auditLoading:k,refreshAudit:_,compactMemory:O,compacting:de,installQmdAction:xe,installingQmd:ue,testRetrieval:be,stats:re}}const Be={Patterns:"pattern",Principles:"principle",Conventions:"convention",Pitfalls:"pitfall",Context:"context"},He={"long-term":"Long-term",daily:"Daily",dreams:"Dreams"},$e={"long-term":"Curated durable decisions, conventions, constraints, and pitfalls promoted from dreams.",daily:"Raw daily observations, open loops, and running context for dream processing.",dreams:"Synthesized patterns and open loops promoted from daily memory."};function Ye(i){if(!i)return[];const t=[],m=i.split(/(?=^## )/m);for(const x of m){const A=x.trim();if(!A)continue;const N=A.match(/^##\s+(.+?)(\n|$)/);if(N){const j=N[1].trim(),S=Be[j]??j.toLowerCase(),h=A.slice(N[0].length).trim(),y=h.split(`
2
- `).map(n=>n.replace(/^-\s+/,"").trim()).filter(n=>n.length>0&&(n.startsWith("- ")||n.startsWith("* ")));(y.length>0||h.length>0)&&t.push({name:j,key:S,items:y.length>0?y:h.length>0?[h]:[],expanded:!0})}}return t}function Ge(i){if(!i)return null;const t=i.match(/##\s+Last\s+Updated:\s*(\d{4}-\d{2}-\d{2})/i);return t?t[1]:null}function Ve(i){return i.reduce((t,m)=>t+m.items.length,0)}function Ze(i){switch(i){case"file":return"File (.fusion/memory/)";case"readonly":return"Read-Only";case"qmd":return"QMD (Quantized Memory Distillation)";default:return i}}function Ke(i){switch(i){case"healthy":return"Healthy";case"warning":return"Warning";case"issues":return"Issues Found"}}function ss({projectId:i,addToast:t}){const[m,x]=a.useState("working"),[A,N]=a.useState(new Set),[j,S]=a.useState(!1),[h,y]=a.useState(null),[n,g]=a.useState({memoryEnabled:!0,memoryAutoSummarizeEnabled:!1,memoryAutoSummarizeThresholdChars:5e4,memoryAutoSummarizeSchedule:"0 3 * * *",memoryDreamsEnabled:!1,memoryDreamsSchedule:"0 4 * * *"}),[Q,J]=a.useState(""),[X,F]=a.useState(!1),[b,R]=a.useState(null),{insightsContent:C,insightsLoading:ee,insightsExists:ce,saveInsights:B,memorySettings:c,settingsLoading:f,saveMemorySettings:se,savingMemorySettings:z,backendStatus:o,backendLoading:E,extractInsights:T,extracting:u,auditReport:d,auditLoading:te,refreshAudit:H,compactMemory:p,compacting:ae,installQmdAction:$,installingQmd:ne,testRetrieval:Y,memoryFiles:G,memoryFilesLoading:W,selectedFilePath:k,selectedFileContent:q,selectedFileLoading:de,selectedFileDirty:D,setSelectedFileContent:ue,selectFile:V,saveSelectedFile:re,savingSelectedFile:Z}=Qe({projectId:i});a.useEffect(()=>{g(c)},[c]);const K=a.useMemo(()=>n.memoryEnabled!==c.memoryEnabled||n.memoryAutoSummarizeEnabled!==c.memoryAutoSummarizeEnabled||n.memoryAutoSummarizeThresholdChars!==c.memoryAutoSummarizeThresholdChars||n.memoryAutoSummarizeSchedule!==c.memoryAutoSummarizeSchedule||n.memoryDreamsEnabled!==c.memoryDreamsEnabled||n.memoryDreamsSchedule!==c.memoryDreamsSchedule,[n,c]),v=a.useMemo(()=>G.find(s=>s.path===k),[G,k]),ie=v?$e[v.layer]:"Edits the selected memory file.",I=a.useMemo(()=>Ye(C),[C]),P=a.useMemo(()=>Ve(I),[I]),M=a.useMemo(()=>Ge(C),[C]),le=a.useCallback(s=>{N(r=>{const l=new Set(r);return l.has(s)?l.delete(s):l.add(s),l})},[]),he=a.useCallback(async s=>{try{await V(s)}catch{t("Failed to load memory file","error")}},[V,t]),ye=a.useCallback(async()=>{try{await re(),t("Memory saved","success")}catch{t("Failed to save memory","error")}},[re,t]),ge=a.useCallback(async()=>{if(!K)return;const s={};n.memoryEnabled!==c.memoryEnabled&&(s.memoryEnabled=n.memoryEnabled),n.memoryAutoSummarizeEnabled!==c.memoryAutoSummarizeEnabled&&(s.memoryAutoSummarizeEnabled=n.memoryAutoSummarizeEnabled),n.memoryAutoSummarizeThresholdChars!==c.memoryAutoSummarizeThresholdChars&&(s.memoryAutoSummarizeThresholdChars=n.memoryAutoSummarizeThresholdChars),n.memoryAutoSummarizeSchedule!==c.memoryAutoSummarizeSchedule&&(s.memoryAutoSummarizeSchedule=n.memoryAutoSummarizeSchedule),n.memoryDreamsEnabled!==c.memoryDreamsEnabled&&(s.memoryDreamsEnabled=n.memoryDreamsEnabled),n.memoryDreamsSchedule!==c.memoryDreamsSchedule&&(s.memoryDreamsSchedule=n.memoryDreamsSchedule);try{await se(s),t("Memory settings saved","success")}catch{t("Failed to save memory settings","error")}},[K,n,c,se,t]),pe=a.useCallback(async()=>{try{const s=await $();t(s.qmdAvailable?"qmd installed successfully":"qmd install finished, but qmd is still unavailable",s.qmdAvailable?"success":"info")}catch{t("Failed to install qmd","error")}},[$,t]),xe=a.useCallback(async()=>{F(!0),R(null);try{const s=await Y(Q);R(s),t(s.qmdAvailable?"Memory retrieval test complete":"qmd is not installed; local fallback was used",s.qmdAvailable?"success":"info")}catch{t("Failed to test memory retrieval","error")}finally{F(!1)}},[Q,Y,t]),be=a.useCallback(async()=>{try{await p(k),t("Memory file compacted","success")}catch{t("Failed to compact memory","error")}},[p,k,t]),_=a.useCallback(async()=>{try{const s=await T();t(s.summary,"success")}catch(s){t(s instanceof Error?s.message:"Failed to extract insights","error")}},[T,t]),L=a.useCallback(async()=>{if(h!==null)try{await B(h),S(!1),y(null),t("Insights saved","success")}catch{t("Failed to save insights","error")}},[h,B,t]),fe=a.useCallback(()=>{y(C??""),S(!0)},[C]),ve=a.useCallback(()=>{S(!1),y(null)},[]),O=o?.capabilities?.writable??!1;return e.jsxs("div",{className:"memory-view",children:[e.jsx("div",{className:"memory-view-header",children:e.jsxs("div",{children:[e.jsx("h2",{children:"Memory"}),e.jsx("p",{className:"memory-view-description",children:"Working memory, long-term insights, and engine status"})]})}),e.jsxs("div",{className:"memory-view-tabs",role:"tablist",children:[e.jsx("button",{type:"button",role:"tab","aria-selected":m==="working",className:`memory-view-tab${m==="working"?" memory-view-tab--active":""}`,onClick:()=>x("working"),"data-testid":"memory-tab-working",children:"Working Memory"}),e.jsx("button",{type:"button",role:"tab","aria-selected":m==="insights",className:`memory-view-tab${m==="insights"?" memory-view-tab--active":""}`,onClick:()=>x("insights"),"data-testid":"memory-tab-insights",children:"Insights"}),e.jsx("button",{type:"button",role:"tab","aria-selected":m==="engines",className:`memory-view-tab${m==="engines"?" memory-view-tab--active":""}`,onClick:()=>x("engines"),"data-testid":"memory-tab-engines",children:"Engines"})]}),e.jsxs("div",{className:"memory-view-content",children:[m==="working"&&e.jsxs("div",{className:"memory-working-tab",children:[!O&&e.jsx("div",{className:"memory-readonly-banner",children:"This memory backend is read-only. Changes cannot be saved."}),W||de?e.jsxs("div",{className:"memory-empty-state",children:[e.jsx(w,{size:20,className:"animate-spin"}),e.jsx("span",{children:"Loading memory file…"})]}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"memory-editor-section",children:[e.jsxs("div",{className:"form-group",children:[e.jsx("label",{htmlFor:"memoryViewFilePath",children:"Memory File"}),e.jsx("select",{id:"memoryViewFilePath",className:"select",value:k,onChange:s=>{he(s.target.value)},disabled:D,children:G.map(s=>e.jsxs("option",{value:s.path,children:[s.label," - ",s.path]},s.path))}),e.jsx("small",{children:D?"Save or discard the current edits before switching files.":"Choose any project memory file to view or edit."})]}),v&&e.jsxs("div",{className:"memory-file-summary",children:[e.jsx("span",{children:He[v.layer]}),e.jsx("strong",{children:v.path}),e.jsxs("small",{children:[v.size.toLocaleString()," bytes · updated ",new Date(v.updatedAt).toLocaleString()]})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:v?.label||"Memory Editor"}),e.jsx("small",{children:ie}),e.jsx("div",{className:"memory-editor-container",children:e.jsx(ke,{content:q,onChange:ue,readOnly:!O,filePath:k})})]})]}),e.jsxs("div",{className:"memory-action-bar",children:[e.jsxs("span",{className:"memory-char-count",children:[q.length," characters"]}),e.jsx("div",{style:{flex:1}}),O&&q.length>0&&e.jsx("button",{type:"button",className:"btn btn-secondary btn-sm",onClick:be,disabled:ae||D,children:ae?e.jsxs(e.Fragment,{children:[e.jsx(w,{size:14,className:"animate-spin"}),"Compacting…"]}):"Compact Selected File"}),D&&O&&e.jsx("button",{type:"button",className:"btn btn-primary btn-sm",onClick:ye,disabled:Z,children:Z?e.jsxs(e.Fragment,{children:[e.jsx(w,{size:14,className:"animate-spin"}),"Saving…"]}):"Save"})]}),e.jsxs("div",{className:"memory-config-section",children:[e.jsxs("div",{className:"memory-settings-group",children:[e.jsxs("div",{className:"form-group",children:[e.jsxs("label",{htmlFor:"memoryDreamsEnabled",className:"checkbox-label",children:[e.jsx("input",{id:"memoryDreamsEnabled",type:"checkbox",checked:n.memoryDreamsEnabled,onChange:s=>{g(r=>({...r,memoryDreamsEnabled:s.target.checked}))},disabled:!n.memoryEnabled||f}),"Process dreams from daily memory"]}),e.jsx("small",{children:"Turns daily notes into DREAMS.md and promotes reusable lessons into MEMORY.md."})]}),n.memoryEnabled&&n.memoryDreamsEnabled&&e.jsxs("div",{className:"form-group",children:[e.jsx("label",{htmlFor:"memoryDreamsSchedule",children:"Dream Schedule"}),e.jsx("input",{id:"memoryDreamsSchedule",type:"text",className:"input",value:n.memoryDreamsSchedule,onChange:s=>{g(r=>({...r,memoryDreamsSchedule:s.target.value}))},placeholder:"0 4 * * *",disabled:f}),e.jsx("small",{children:"Cron expression for dream processing."})]})]}),e.jsxs("div",{className:"memory-settings-group",children:[e.jsxs("div",{className:"form-group",children:[e.jsxs("label",{htmlFor:"memoryAutoSummarizeEnabled",className:"checkbox-label",children:[e.jsx("input",{id:"memoryAutoSummarizeEnabled",type:"checkbox",checked:n.memoryAutoSummarizeEnabled,onChange:s=>{g(r=>({...r,memoryAutoSummarizeEnabled:s.target.checked}))},disabled:!n.memoryEnabled||f}),"Auto-Summarize Memory"]}),e.jsx("small",{children:"Automatically compact memory when it exceeds the threshold on a schedule"})]}),n.memoryEnabled&&n.memoryAutoSummarizeEnabled&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"form-group",children:[e.jsx("label",{htmlFor:"memoryAutoSummarizeThresholdChars",children:"Compaction Threshold (chars)"}),e.jsx("input",{id:"memoryAutoSummarizeThresholdChars",type:"number",className:"input",value:n.memoryAutoSummarizeThresholdChars,onChange:s=>{g(r=>({...r,memoryAutoSummarizeThresholdChars:parseInt(s.target.value,10)||5e4}))},min:1e3,disabled:f}),e.jsx("small",{children:"Memory will be compacted when it exceeds this character count"})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{htmlFor:"memoryAutoSummarizeSchedule",children:"Schedule (cron)"}),e.jsx("input",{id:"memoryAutoSummarizeSchedule",type:"text",className:"input",value:n.memoryAutoSummarizeSchedule,onChange:s=>{g(r=>({...r,memoryAutoSummarizeSchedule:s.target.value}))},placeholder:"0 3 * * *",disabled:f}),e.jsx("small",{children:"Cron expression for auto-summarize schedule (default: daily at 3 AM)"})]})]})]}),!n.memoryEnabled&&e.jsx("div",{className:"settings-empty-state memory-status-message",children:"Memory is currently disabled. Enable memory tools in Settings to edit these automations."}),K&&e.jsx("div",{className:"memory-action-bar",children:e.jsx("button",{type:"button",className:"btn btn-primary btn-sm",onClick:ge,disabled:z||f,children:z?e.jsxs(e.Fragment,{children:[e.jsx(w,{size:14,className:"animate-spin"}),"Saving…"]}):"Save Settings"})})]})]})]}),m==="insights"&&e.jsx("div",{className:"memory-insights-tab",children:ee?e.jsxs("div",{className:"memory-empty-state",children:[e.jsx(w,{size:20,className:"animate-spin"}),e.jsx("span",{children:"Loading insights…"})]}):j?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"memory-editor-container",children:e.jsx(ke,{content:h??"",onChange:y,readOnly:!1,filePath:".fusion/memory/INSIGHTS.md"})}),e.jsxs("div",{className:"memory-action-bar",children:[e.jsx("button",{type:"button",className:"btn btn-secondary btn-sm",onClick:ve,children:"Cancel"}),e.jsx("button",{type:"button",className:"btn btn-primary btn-sm",onClick:L,children:"Save Insights"})]})]}):!ce||I.length===0?e.jsxs("div",{className:"memory-empty-state",children:[e.jsx("p",{children:"No insights extracted yet."}),e.jsx("p",{children:'Insights are automatically extracted from working memory. Click "Extract Now" to trigger extraction manually.'}),e.jsx("button",{type:"button",className:"btn btn-primary btn-sm",onClick:_,disabled:u,style:{marginTop:"var(--space-md)"},children:u?e.jsxs(e.Fragment,{children:[e.jsx(w,{size:14,className:"animate-spin"}),"Extracting…"]}):"Extract Now"})]}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"memory-stats-row",children:[e.jsxs("div",{className:"memory-stat-card",children:[e.jsx("div",{className:"memory-stat-value",children:P}),e.jsx("div",{className:"memory-stat-label",children:"Total Insights"})]}),e.jsxs("div",{className:"memory-stat-card",children:[e.jsx("div",{className:"memory-stat-value",children:I.length}),e.jsx("div",{className:"memory-stat-label",children:"Categories"})]}),M&&e.jsxs("div",{className:"memory-stat-card",children:[e.jsx("div",{className:"memory-stat-value",style:{fontSize:"16px"},children:M}),e.jsx("div",{className:"memory-stat-label",children:"Last Updated"})]})]}),e.jsxs("div",{className:"memory-action-bar",children:[e.jsx("button",{type:"button",className:"btn btn-primary btn-sm",onClick:_,disabled:u,children:u?e.jsxs(e.Fragment,{children:[e.jsx(w,{size:14,className:"animate-spin"}),"Extracting…"]}):"Extract Now"}),e.jsx("button",{type:"button",className:"btn btn-secondary btn-sm",onClick:fe,children:"Edit Raw"})]}),e.jsx("div",{className:"memory-categories-list",children:I.map(s=>{const r=!A.has(s.key);return e.jsxs("div",{className:"memory-category-section",children:[e.jsxs("div",{className:"memory-category-header",onClick:()=>le(s.key),role:"button",tabIndex:0,onKeyDown:l=>{(l.key==="Enter"||l.key===" ")&&(l.preventDefault(),le(s.key))},children:[e.jsx("h4",{children:s.name}),e.jsx("span",{className:"memory-category-count",children:s.items.length})]}),r&&e.jsx("div",{className:"memory-category-items",children:s.items.map((l,me)=>e.jsx("div",{className:"memory-insight-item",children:l.replace(/^-\s+/,"").replace(/^\*\s+/,"")},me))})]},s.key)})})]})}),m==="engines"&&e.jsx("div",{className:"memory-engines-tab",children:E||te?e.jsxs("div",{className:"memory-empty-state",children:[e.jsx(w,{size:20,className:"animate-spin"}),e.jsx("span",{children:"Loading engine status…"})]}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"memory-engine-card memory-qmd-card",children:[e.jsx("h3",{children:"QMD Integration"}),o?.qmdAvailable?e.jsxs("div",{className:"memory-engine-status",children:[e.jsx("span",{className:"memory-health-badge memory-health-badge--healthy",children:"Installed"}),e.jsx("span",{className:"memory-char-count",children:"qmd is available on PATH."})]}):e.jsxs("div",{className:"settings-empty-state memory-status-message",children:[e.jsxs("span",{children:["qmd is not installed. Search will use local files. Install indexed retrieval: ",e.jsx("code",{children:o?.qmdInstallCommand||"bun install -g @tobilu/qmd"})]}),e.jsx("button",{type:"button",className:"btn btn-secondary btn-sm",onClick:pe,disabled:ne,children:ne?"Installing…":"Install qmd"})]}),e.jsxs("div",{style:{display:"flex",gap:"var(--space-xs)",marginTop:"var(--space-sm)",flexWrap:"wrap"},children:[o?.capabilities?.readable&&e.jsx("span",{className:"memory-capability-badge",children:"Readable"}),o?.capabilities?.writable&&e.jsx("span",{className:"memory-capability-badge",children:"Writable"}),o?.capabilities?.supportsAtomicWrite&&e.jsx("span",{className:"memory-capability-badge",children:"Atomic Writes"}),o?.capabilities?.persistent&&e.jsx("span",{className:"memory-capability-badge",children:"Persistent"})]})]}),e.jsxs("div",{className:"memory-engine-card memory-retrieval-card",children:[e.jsx("h3",{children:"Test Memory Search"}),e.jsxs("div",{className:"memory-retrieval-input-row",children:[e.jsx("input",{type:"text",className:"input",value:Q,onChange:s=>J(s.target.value),placeholder:"Search memory with qmd"}),e.jsx("button",{type:"button",className:"btn btn-secondary btn-sm",onClick:xe,disabled:X,children:X?"Testing…":"Test Retrieval"})]}),e.jsx("small",{className:"settings-muted",children:"Runs the same qmd-backed memory_search path agents use."}),b&&e.jsxs("div",{className:"memory-test-result",children:[e.jsxs("strong",{children:[b.results.length," result",b.results.length===1?"":"s"," ",'for "',b.query,'"']}),e.jsxs("small",{children:["qmd ",b.qmdAvailable?"available":"missing"," · ",b.usedFallback?"local fallback used":"qmd path used"]}),b.results.length>0?e.jsx("ul",{children:b.results.map((s,r)=>e.jsxs("li",{children:[e.jsxs("span",{children:[s.path,":",s.lineStart]}),e.jsx("p",{children:s.snippet})]},`${s.path}-${s.lineStart}-${r}`))}):e.jsx("small",{children:"No matching memory found."})]})]}),e.jsxs("div",{className:"memory-engine-card",children:[e.jsx("h3",{children:"Current Backend"}),e.jsx("div",{className:"memory-engine-status",children:e.jsx("span",{style:{fontWeight:500},children:Ze(o?.currentBackend??"unknown")})}),e.jsxs("div",{style:{display:"flex",gap:"var(--space-xs)",marginTop:"var(--space-sm)",flexWrap:"wrap"},children:[o?.capabilities?.readable&&e.jsx("span",{className:"memory-capability-badge",children:"Readable"}),o?.capabilities?.writable&&e.jsx("span",{className:"memory-capability-badge",children:"Writable"}),o?.capabilities?.supportsAtomicWrite&&e.jsx("span",{className:"memory-capability-badge",children:"Atomic Writes"}),o?.capabilities?.persistent&&e.jsx("span",{className:"memory-capability-badge",children:"Persistent"})]})]}),d&&e.jsxs("div",{className:"memory-engine-card",children:[e.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:"var(--space-md)"},children:[e.jsx("h3",{children:"Health Status"}),e.jsx("span",{className:`memory-health-badge memory-health-badge--${d.health}`,children:Ke(d.health)})]}),e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"var(--space-md)"},children:[e.jsxs("div",{children:[e.jsx("div",{style:{fontSize:"12px",color:"var(--text-muted)",textTransform:"uppercase",marginBottom:"var(--space-xs)"},children:"Working Memory"}),e.jsxs("div",{style:{fontWeight:500},children:[d.workingMemory.size," chars"]}),e.jsxs("div",{style:{fontSize:"13px",color:"var(--text-muted)"},children:[d.workingMemory.sectionCount," sections"]})]}),e.jsxs("div",{children:[e.jsx("div",{style:{fontSize:"12px",color:"var(--text-muted)",textTransform:"uppercase",marginBottom:"var(--space-xs)"},children:"Insights Memory"}),e.jsxs("div",{style:{fontWeight:500},children:[d.insightsMemory.size," chars"]}),e.jsxs("div",{style:{fontSize:"13px",color:"var(--text-muted)"},children:[d.insightsMemory.insightCount," insights"]})]})]}),e.jsxs("div",{style:{marginTop:"var(--space-md)",paddingTop:"var(--space-md)",borderTop:"1px solid var(--border)"},children:[e.jsx("div",{style:{fontSize:"12px",color:"var(--text-muted)",textTransform:"uppercase",marginBottom:"var(--space-xs)"},children:"Last Extraction"}),e.jsx("div",{style:{fontWeight:500},children:d.extraction.success?e.jsx("span",{style:{color:"var(--color-success)"},children:"Success"}):e.jsx("span",{style:{color:"var(--color-error)"},children:"Failed"})}),e.jsx("div",{style:{fontSize:"13px",color:"var(--text-muted)"},children:d.extraction.summary||`${d.extraction.insightCount} insights extracted`})]}),e.jsxs("div",{style:{marginTop:"var(--space-md)",paddingTop:"var(--space-md)",borderTop:"1px solid var(--border)"},children:[e.jsx("div",{style:{fontSize:"12px",color:"var(--text-muted)",textTransform:"uppercase",marginBottom:"var(--space-xs)"},children:"Pruning"}),e.jsx("div",{style:{fontWeight:500},children:d.pruning.applied?e.jsx("span",{style:{color:"var(--color-warning)"},children:"Applied"}):e.jsx("span",{style:{color:"var(--text-muted)"},children:"Not needed"})}),d.pruning.applied&&e.jsx("div",{style:{fontSize:"13px",color:"var(--text-muted)"},children:d.pruning.reason})]})]}),d&&d.checks.length>0&&e.jsxs("div",{className:"memory-engine-card",children:[e.jsx("h3",{children:"Audit Checks"}),e.jsx("div",{children:d.checks.map(s=>e.jsxs("div",{className:"memory-audit-check",children:[e.jsx("span",{className:s.passed?"memory-audit-check-passed":"memory-audit-check-failed",children:s.passed?"✓":"✗"}),e.jsxs("div",{style:{flex:1},children:[e.jsx("div",{style:{fontWeight:500},children:s.name}),e.jsx("div",{style:{fontSize:"13px",color:"var(--text-muted)"},children:s.details})]})]},s.id))})]}),e.jsx("div",{className:"memory-action-bar",children:e.jsx("button",{type:"button",className:"btn btn-secondary btn-sm",onClick:()=>H(),children:"Run Audit"})}),e.jsxs("div",{style:{marginTop:"var(--space-lg)",fontSize:"13px",color:"var(--text-muted)"},children:["Note: Change backend type in"," ",e.jsx("span",{style:{cursor:"pointer",textDecoration:"underline"},onClick:()=>{t("Open Settings → Memory to change backend type","info")},children:"Settings → Memory"})]})]})})]})]})}export{ss as MemoryView};