@runfusion/fusion 0.25.0 → 0.26.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 (113) hide show
  1. package/README.md +6 -0
  2. package/dist/bin.js +47492 -45420
  3. package/dist/client/assets/{AgentDetailView-ZbHEbYRT.js → AgentDetailView-Cv-vgOj3.js} +3 -3
  4. package/dist/client/assets/{AgentsView-B3jYk8Kt.js → AgentsView-D6Zi5zfP.js} +4 -4
  5. package/dist/client/assets/ChatView-CAHjY9uO.js +1 -0
  6. package/dist/client/assets/{DevServerView-DyGDEiBP.js → DevServerView--_WBvIDQ.js} +1 -1
  7. package/dist/client/assets/{DirectoryPicker-D5UIeIl6.js → DirectoryPicker-xedtR-Rd.js} +1 -1
  8. package/dist/client/assets/{DocumentsView-DNHu1T8K.js → DocumentsView-Bg2oaZks.js} +1 -1
  9. package/dist/client/assets/{EvalsView-CpRobtDi.js → EvalsView-B3uOCXfr.js} +1 -1
  10. package/dist/client/assets/{ExperimentalAgentOnboardingModal-DOY_oZi7.js → ExperimentalAgentOnboardingModal-Bx6yXVS5.js} +1 -1
  11. package/dist/client/assets/{InsightsView-vp0RE8Mg.js → InsightsView-Q1zvtF4F.js} +1 -1
  12. package/dist/client/assets/{MemoryView-PSc5lGJt.js → MemoryView-xcN_eouf.js} +1 -1
  13. package/dist/client/assets/{NodesView-DMj6HGeC.js → NodesView-RxXg58_Q.js} +1 -1
  14. package/dist/client/assets/{PiExtensionsManager-DL_QcN56.js → PiExtensionsManager-Cc8aAZXg.js} +2 -2
  15. package/dist/client/assets/{PluginManager-BtYKm8IT.js → PluginManager-BEkyBajl.js} +1 -1
  16. package/dist/client/assets/{ResearchView-BzCcDAS4.css → ResearchView-BEI4ZSGs.css} +1 -1
  17. package/dist/client/assets/ResearchView-CERNf7sJ.js +1 -0
  18. package/dist/client/assets/{SettingsModal-CUCyaAyE.js → SettingsModal-B1r0yASu.js} +1 -1
  19. package/dist/client/assets/SettingsModal-BLsac7CJ.js +31 -0
  20. package/dist/client/assets/SettingsModal-Cis-4Lot.css +1 -0
  21. package/dist/client/assets/{SetupWizardModal-BKscasuh.js → SetupWizardModal-D1q548_L.js} +1 -1
  22. package/dist/client/assets/{SkillsView-BdELqTy7.js → SkillsView-ClLM6u6p.js} +1 -1
  23. package/dist/client/assets/StashRecoveryView-B_8WIQEo.css +1 -0
  24. package/dist/client/assets/StashRecoveryView-ze0pEZ5U.js +1 -0
  25. package/dist/client/assets/{TodoView-DFNGBDNV.js → TodoView-CTmIfy2M.js} +1 -1
  26. package/dist/client/assets/createLucideIcon-BazL2hk5.js +21 -0
  27. package/dist/client/assets/dashboard-view-4xAN3yO5.js +21 -0
  28. package/dist/client/assets/dashboard-view-BkTMSZYn.css +1 -0
  29. package/dist/client/assets/dashboard-view-CyWN-d02.js +63 -0
  30. package/dist/client/assets/dashboard-view-DdGlfuu-.css +1 -0
  31. package/dist/client/assets/{folder-open-k1xmUMyr.js → folder-open-BZuKESeq.js} +1 -1
  32. package/dist/client/assets/index-Bdw6llW6.js +692 -0
  33. package/dist/client/assets/index-CZGlyJuS.css +1 -0
  34. package/dist/client/assets/{star-ne32r3Y4.js → star-D75YKEq-.js} +1 -1
  35. package/dist/client/assets/{upload-MS-2Gx53.js → upload-BYYTgWFj.js} +1 -1
  36. package/dist/client/assets/{users-C519GSjH.js → users-RS90Aii3.js} +1 -1
  37. package/dist/client/index.html +2 -2
  38. package/dist/client/version.json +1 -1
  39. package/dist/droid-cli/package.json +1 -1
  40. package/dist/extension.js +4340 -3059
  41. package/dist/pi-claude-cli/package.json +1 -1
  42. package/dist/plugins/fusion-plugin-cli-printing-press/manifest.json +6 -0
  43. package/dist/plugins/fusion-plugin-cli-printing-press/package.json +26 -0
  44. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/manifest.test.ts +20 -0
  45. package/dist/plugins/fusion-plugin-cli-printing-press/src/index.ts +14 -0
  46. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  47. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  48. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  49. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  50. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  51. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  52. package/dist/plugins/fusion-plugin-reports/package.json +1 -1
  53. package/dist/plugins/fusion-plugin-roadmap/{src/dashboard/RoadmapsView.css → bundled.css} +13 -219
  54. package/dist/plugins/fusion-plugin-roadmap/bundled.js +29535 -0
  55. package/dist/plugins/fusion-plugin-roadmap/package.json +4 -41
  56. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
  57. package/package.json +2 -3
  58. package/dist/client/assets/ChatView-DhPkiEGs.js +0 -1
  59. package/dist/client/assets/ResearchView-BhWqfdV0.js +0 -1
  60. package/dist/client/assets/SettingsModal-BAgB4_AR.js +0 -31
  61. package/dist/client/assets/SettingsModal-DzsLquBu.css +0 -1
  62. package/dist/client/assets/index-Qq2JOOWx.css +0 -1
  63. package/dist/client/assets/index-TFYXEVpn.js +0 -692
  64. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/api-client.test.ts +0 -101
  65. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/index.test.ts +0 -92
  66. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/roadmap-routes.test.ts +0 -48
  67. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/roadmap-suggestions.test.ts +0 -31
  68. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.tsx +0 -2559
  69. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/__tests__/RoadmapsView.test.tsx +0 -1144
  70. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/__tests__/useRoadmaps.test.ts +0 -1756
  71. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/api.ts +0 -70
  72. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/test-setup.ts +0 -7
  73. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/types.ts +0 -1
  74. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useConfirm.ts +0 -8
  75. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useRoadmaps.ts +0 -1188
  76. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useViewportMode.ts +0 -20
  77. package/dist/plugins/fusion-plugin-roadmap/src/dashboard-view.tsx +0 -6
  78. package/dist/plugins/fusion-plugin-roadmap/src/index.ts +0 -74
  79. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-routes.ts +0 -1
  80. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-schema.ts +0 -41
  81. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-suggestions.d.ts +0 -15
  82. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-suggestions.ts +0 -15
  83. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.d.ts +0 -283
  84. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.d.ts.map +0 -1
  85. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.js +0 -21
  86. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.js.map +0 -1
  87. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.ts +0 -310
  88. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.d.ts +0 -5
  89. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.d.ts.map +0 -1
  90. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.js +0 -361
  91. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.js.map +0 -1
  92. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.ts +0 -408
  93. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.d.ts +0 -68
  94. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.d.ts.map +0 -1
  95. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.js +0 -300
  96. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.js.map +0 -1
  97. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.ts +0 -381
  98. package/dist/plugins/fusion-plugin-roadmap/src/server/index.d.ts +0 -3
  99. package/dist/plugins/fusion-plugin-roadmap/src/server/index.ts +0 -1
  100. package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-handoff.test.ts +0 -445
  101. package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-ordering.test.ts +0 -334
  102. package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-store.test.ts +0 -1318
  103. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-handoff.ts +0 -163
  104. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.d.ts +0 -37
  105. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.d.ts.map +0 -1
  106. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.js +0 -188
  107. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.js.map +0 -1
  108. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.ts +0 -311
  109. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.d.ts +0 -299
  110. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.d.ts.map +0 -1
  111. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.js +0 -765
  112. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.js.map +0 -1
  113. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.ts +0 -1001
@@ -1,765 +0,0 @@
1
- /**
2
- * RoadmapStore - Data layer for standalone roadmap persistence.
3
- *
4
- * Manages CRUD operations for roadmaps, milestones, and features.
5
- * Provides deterministic ordering via covering indexes and atomic reorder/move operations.
6
- *
7
- * Ordering invariants:
8
- * - milestone ordering is scoped to a single roadmap and must be contiguous + 0-based
9
- * - feature ordering is scoped to a single milestone and must be contiguous + 0-based
10
- * - all list/read queries use deterministic ordering: ORDER BY orderIndex ASC, createdAt ASC, id ASC
11
- * - cross-milestone feature moves atomically renumber both affected milestone scopes
12
- */
13
- import { EventEmitter } from "node:events";
14
- import { applyRoadmapMilestoneReorder, applyRoadmapFeatureReorder, moveRoadmapFeature, } from "./roadmap-ordering.js";
15
- // ── RoadmapStore Class ──────────────────────────────────────────────
16
- export class RoadmapStore extends EventEmitter {
17
- db;
18
- /**
19
- * Creates a new RoadmapStore instance.
20
- *
21
- * @param db - Shared Database instance (same instance used by TaskStore)
22
- */
23
- constructor(db) {
24
- super();
25
- this.db = db;
26
- this.setMaxListeners(50);
27
- this.ensureSchema();
28
- }
29
- ensureSchema() {
30
- this.db.exec(`
31
- CREATE TABLE IF NOT EXISTS roadmaps (
32
- id TEXT PRIMARY KEY,
33
- title TEXT NOT NULL,
34
- description TEXT,
35
- createdAt TEXT NOT NULL,
36
- updatedAt TEXT NOT NULL
37
- );
38
-
39
- CREATE TABLE IF NOT EXISTS roadmap_milestones (
40
- id TEXT PRIMARY KEY,
41
- roadmapId TEXT NOT NULL,
42
- title TEXT NOT NULL,
43
- description TEXT,
44
- orderIndex INTEGER NOT NULL,
45
- createdAt TEXT NOT NULL,
46
- updatedAt TEXT NOT NULL,
47
- FOREIGN KEY (roadmapId) REFERENCES roadmaps(id) ON DELETE CASCADE
48
- );
49
-
50
- CREATE TABLE IF NOT EXISTS roadmap_features (
51
- id TEXT PRIMARY KEY,
52
- milestoneId TEXT NOT NULL,
53
- title TEXT NOT NULL,
54
- description TEXT,
55
- orderIndex INTEGER NOT NULL,
56
- createdAt TEXT NOT NULL,
57
- updatedAt TEXT NOT NULL,
58
- FOREIGN KEY (milestoneId) REFERENCES roadmap_milestones(id) ON DELETE CASCADE
59
- );
60
-
61
- CREATE INDEX IF NOT EXISTS idxRoadmapMilestonesRoadmapOrder
62
- ON roadmap_milestones(roadmapId, orderIndex, createdAt, id);
63
-
64
- CREATE INDEX IF NOT EXISTS idxRoadmapFeaturesMilestoneOrder
65
- ON roadmap_features(milestoneId, orderIndex, createdAt, id);
66
- `);
67
- }
68
- // ── ID Generators ───────────────────────────────────────────────────
69
- generateRoadmapId() {
70
- const timestamp = Date.now();
71
- const random = Math.random().toString(36).substring(2, 6).toUpperCase();
72
- return `RM-${timestamp.toString(36).toUpperCase()}-${random}`;
73
- }
74
- generateMilestoneId() {
75
- const timestamp = Date.now();
76
- const random = Math.random().toString(36).substring(2, 6).toUpperCase();
77
- return `RMS-${timestamp.toString(36).toUpperCase()}-${random}`;
78
- }
79
- generateFeatureId() {
80
- const timestamp = Date.now();
81
- const random = Math.random().toString(36).substring(2, 6).toUpperCase();
82
- return `RF-${timestamp.toString(36).toUpperCase()}-${random}`;
83
- }
84
- // ── Row-to-Object Converters ───────────────────────────────────────
85
- rowToRoadmap(row) {
86
- return {
87
- id: row.id,
88
- title: row.title,
89
- description: row.description || undefined,
90
- createdAt: row.createdAt,
91
- updatedAt: row.updatedAt,
92
- };
93
- }
94
- rowToMilestone(row) {
95
- return {
96
- id: row.id,
97
- roadmapId: row.roadmapId,
98
- title: row.title,
99
- description: row.description || undefined,
100
- orderIndex: row.orderIndex,
101
- createdAt: row.createdAt,
102
- updatedAt: row.updatedAt,
103
- };
104
- }
105
- rowToFeature(row) {
106
- return {
107
- id: row.id,
108
- milestoneId: row.milestoneId,
109
- title: row.title,
110
- description: row.description || undefined,
111
- orderIndex: row.orderIndex,
112
- createdAt: row.createdAt,
113
- updatedAt: row.updatedAt,
114
- };
115
- }
116
- // ── Roadmap CRUD ─────────────────────────────────────────────────
117
- /**
118
- * Create a new roadmap.
119
- *
120
- * @param input - Roadmap creation input
121
- * @returns The created roadmap
122
- */
123
- createRoadmap(input) {
124
- const now = new Date().toISOString();
125
- const id = this.generateRoadmapId();
126
- const roadmap = {
127
- id,
128
- title: input.title,
129
- description: input.description,
130
- createdAt: now,
131
- updatedAt: now,
132
- };
133
- this.db.prepare(`
134
- INSERT INTO roadmaps (id, title, description, createdAt, updatedAt)
135
- VALUES (?, ?, ?, ?, ?)
136
- `).run(roadmap.id, roadmap.title, roadmap.description ?? null, roadmap.createdAt, roadmap.updatedAt);
137
- this.db.bumpLastModified();
138
- this.emit("roadmap:created", roadmap);
139
- return roadmap;
140
- }
141
- /**
142
- * Get a roadmap by ID.
143
- *
144
- * @param id - Roadmap ID
145
- * @returns The roadmap, or undefined if not found
146
- */
147
- getRoadmap(id) {
148
- const row = this.db.prepare("SELECT * FROM roadmaps WHERE id = ?").get(id);
149
- if (!row)
150
- return undefined;
151
- return this.rowToRoadmap(row);
152
- }
153
- /**
154
- * List all roadmaps, ordered by creation date (newest first).
155
- *
156
- * @returns Array of roadmaps
157
- */
158
- listRoadmaps() {
159
- const rows = this.db.prepare("SELECT * FROM roadmaps ORDER BY createdAt DESC").all();
160
- return rows.map((row) => this.rowToRoadmap(row));
161
- }
162
- /**
163
- * Update a roadmap.
164
- *
165
- * @param id - Roadmap ID
166
- * @param updates - Partial roadmap updates
167
- * @returns The updated roadmap
168
- * @throws Error if roadmap not found
169
- */
170
- updateRoadmap(id, updates) {
171
- const roadmap = this.getRoadmap(id);
172
- if (!roadmap) {
173
- throw new Error(`Roadmap ${id} not found`);
174
- }
175
- const updated = {
176
- ...roadmap,
177
- ...updates,
178
- id, // Prevent changing ID
179
- createdAt: roadmap.createdAt, // Prevent changing creation time
180
- updatedAt: new Date().toISOString(),
181
- };
182
- this.db.prepare(`
183
- UPDATE roadmaps SET
184
- title = ?,
185
- description = ?,
186
- updatedAt = ?
187
- WHERE id = ?
188
- `).run(updated.title, updated.description ?? null, updated.updatedAt, updated.id);
189
- this.db.bumpLastModified();
190
- this.emit("roadmap:updated", updated);
191
- return updated;
192
- }
193
- /**
194
- * Delete a roadmap and all its milestones/features (cascading).
195
- *
196
- * @param id - Roadmap ID
197
- * @throws Error if roadmap not found
198
- */
199
- deleteRoadmap(id) {
200
- const roadmap = this.getRoadmap(id);
201
- if (!roadmap) {
202
- throw new Error(`Roadmap ${id} not found`);
203
- }
204
- // SQLite FK cascade will handle milestones and features
205
- this.db.prepare("DELETE FROM roadmaps WHERE id = ?").run(id);
206
- this.db.bumpLastModified();
207
- this.emit("roadmap:deleted", id);
208
- }
209
- // ── Milestone CRUD ────────────────────────────────────────────────
210
- /**
211
- * Add a milestone to a roadmap.
212
- * Automatically computes the orderIndex (max + 1).
213
- *
214
- * @param roadmapId - Parent roadmap ID
215
- * @param input - Milestone creation input
216
- * @returns The created milestone
217
- * @throws Error if roadmap not found
218
- */
219
- createMilestone(roadmapId, input) {
220
- const roadmap = this.getRoadmap(roadmapId);
221
- if (!roadmap) {
222
- throw new Error(`Roadmap ${roadmapId} not found`);
223
- }
224
- const now = new Date().toISOString();
225
- const id = this.generateMilestoneId();
226
- // Compute next orderIndex
227
- const existingMilestones = this.listMilestones(roadmapId);
228
- const orderIndex = existingMilestones.length > 0
229
- ? Math.max(...existingMilestones.map((m) => m.orderIndex)) + 1
230
- : 0;
231
- const milestone = {
232
- id,
233
- roadmapId,
234
- title: input.title,
235
- description: input.description,
236
- orderIndex,
237
- createdAt: now,
238
- updatedAt: now,
239
- };
240
- this.db.prepare(`
241
- INSERT INTO roadmap_milestones (id, roadmapId, title, description, orderIndex, createdAt, updatedAt)
242
- VALUES (?, ?, ?, ?, ?, ?, ?)
243
- `).run(milestone.id, milestone.roadmapId, milestone.title, milestone.description ?? null, milestone.orderIndex, milestone.createdAt, milestone.updatedAt);
244
- this.db.bumpLastModified();
245
- this.emit("milestone:created", milestone);
246
- return milestone;
247
- }
248
- /**
249
- * Get a milestone by ID.
250
- *
251
- * @param id - Milestone ID
252
- * @returns The milestone, or undefined if not found
253
- */
254
- getMilestone(id) {
255
- const row = this.db.prepare("SELECT * FROM roadmap_milestones WHERE id = ?").get(id);
256
- if (!row)
257
- return undefined;
258
- return this.rowToMilestone(row);
259
- }
260
- /**
261
- * List milestones for a roadmap, ordered deterministically.
262
- *
263
- * Uses deterministic ordering: ORDER BY orderIndex ASC, createdAt ASC, id ASC
264
- * to ensure consistent results when stored order data is incomplete or conflicting.
265
- *
266
- * @param roadmapId - Roadmap ID
267
- * @returns Array of milestones in deterministic order
268
- */
269
- listMilestones(roadmapId) {
270
- const rows = this.db.prepare("SELECT * FROM roadmap_milestones WHERE roadmapId = ? ORDER BY orderIndex ASC, createdAt ASC, id ASC").all(roadmapId);
271
- return rows.map((row) => this.rowToMilestone(row));
272
- }
273
- /**
274
- * Update a milestone.
275
- *
276
- * @param id - Milestone ID
277
- * @param updates - Partial milestone updates
278
- * @returns The updated milestone
279
- * @throws Error if milestone not found
280
- */
281
- updateMilestone(id, updates) {
282
- const milestone = this.getMilestone(id);
283
- if (!milestone) {
284
- throw new Error(`Milestone ${id} not found`);
285
- }
286
- const updated = {
287
- ...milestone,
288
- ...updates,
289
- id, // Prevent changing ID
290
- roadmapId: milestone.roadmapId, // Prevent moving to different roadmap
291
- createdAt: milestone.createdAt, // Prevent changing creation time
292
- updatedAt: new Date().toISOString(),
293
- };
294
- this.db.prepare(`
295
- UPDATE roadmap_milestones SET
296
- title = ?,
297
- description = ?,
298
- updatedAt = ?
299
- WHERE id = ?
300
- `).run(updated.title, updated.description ?? null, updated.updatedAt, updated.id);
301
- this.db.bumpLastModified();
302
- this.emit("milestone:updated", updated);
303
- return updated;
304
- }
305
- /**
306
- * Delete a milestone and all its features (cascading).
307
- *
308
- * @param id - Milestone ID
309
- * @throws Error if milestone not found
310
- */
311
- deleteMilestone(id) {
312
- const milestone = this.getMilestone(id);
313
- if (!milestone) {
314
- throw new Error(`Milestone ${id} not found`);
315
- }
316
- // SQLite FK cascade will handle features
317
- this.db.prepare("DELETE FROM roadmap_milestones WHERE id = ?").run(id);
318
- this.db.bumpLastModified();
319
- this.emit("milestone:deleted", id);
320
- }
321
- // ── Feature CRUD ─────────────────────────────────────────────────
322
- /**
323
- * Add a feature to a milestone.
324
- * Automatically computes the orderIndex (max + 1).
325
- *
326
- * @param milestoneId - Parent milestone ID
327
- * @param input - Feature creation input
328
- * @returns The created feature
329
- * @throws Error if milestone not found
330
- */
331
- createFeature(milestoneId, input) {
332
- const milestone = this.getMilestone(milestoneId);
333
- if (!milestone) {
334
- throw new Error(`Milestone ${milestoneId} not found`);
335
- }
336
- const now = new Date().toISOString();
337
- const id = this.generateFeatureId();
338
- // Compute next orderIndex
339
- const existingFeatures = this.listFeatures(milestoneId);
340
- const orderIndex = existingFeatures.length > 0
341
- ? Math.max(...existingFeatures.map((f) => f.orderIndex)) + 1
342
- : 0;
343
- const feature = {
344
- id,
345
- milestoneId,
346
- title: input.title,
347
- description: input.description,
348
- orderIndex,
349
- createdAt: now,
350
- updatedAt: now,
351
- };
352
- this.db.prepare(`
353
- INSERT INTO roadmap_features (id, milestoneId, title, description, orderIndex, createdAt, updatedAt)
354
- VALUES (?, ?, ?, ?, ?, ?, ?)
355
- `).run(feature.id, feature.milestoneId, feature.title, feature.description ?? null, feature.orderIndex, feature.createdAt, feature.updatedAt);
356
- this.db.bumpLastModified();
357
- this.emit("feature:created", feature);
358
- return feature;
359
- }
360
- /**
361
- * Get a feature by ID.
362
- *
363
- * @param id - Feature ID
364
- * @returns The feature, or undefined if not found
365
- */
366
- getFeature(id) {
367
- const row = this.db.prepare("SELECT * FROM roadmap_features WHERE id = ?").get(id);
368
- if (!row)
369
- return undefined;
370
- return this.rowToFeature(row);
371
- }
372
- /**
373
- * List features for a milestone, ordered deterministically.
374
- *
375
- * Uses deterministic ordering: ORDER BY orderIndex ASC, createdAt ASC, id ASC
376
- * to ensure consistent results when stored order data is incomplete or conflicting.
377
- *
378
- * @param milestoneId - Milestone ID
379
- * @returns Array of features in deterministic order
380
- */
381
- listFeatures(milestoneId) {
382
- const rows = this.db.prepare("SELECT * FROM roadmap_features WHERE milestoneId = ? ORDER BY orderIndex ASC, createdAt ASC, id ASC").all(milestoneId);
383
- return rows.map((row) => this.rowToFeature(row));
384
- }
385
- /**
386
- * Update a feature.
387
- *
388
- * @param id - Feature ID
389
- * @param updates - Partial feature updates
390
- * @returns The updated feature
391
- * @throws Error if feature not found
392
- */
393
- updateFeature(id, updates) {
394
- const feature = this.getFeature(id);
395
- if (!feature) {
396
- throw new Error(`Feature ${id} not found`);
397
- }
398
- const updated = {
399
- ...feature,
400
- ...updates,
401
- id, // Prevent changing ID
402
- milestoneId: feature.milestoneId, // Prevent moving via update (use moveFeature instead)
403
- createdAt: feature.createdAt, // Prevent changing creation time
404
- updatedAt: new Date().toISOString(),
405
- };
406
- this.db.prepare(`
407
- UPDATE roadmap_features SET
408
- title = ?,
409
- description = ?,
410
- updatedAt = ?
411
- WHERE id = ?
412
- `).run(updated.title, updated.description ?? null, updated.updatedAt, updated.id);
413
- this.db.bumpLastModified();
414
- this.emit("feature:updated", updated);
415
- return updated;
416
- }
417
- /**
418
- * Delete a feature.
419
- *
420
- * @param id - Feature ID
421
- * @throws Error if feature not found
422
- */
423
- deleteFeature(id) {
424
- const feature = this.getFeature(id);
425
- if (!feature) {
426
- throw new Error(`Feature ${id} not found`);
427
- }
428
- this.db.prepare("DELETE FROM roadmap_features WHERE id = ?").run(id);
429
- this.db.bumpLastModified();
430
- this.emit("feature:deleted", feature);
431
- }
432
- // ── Reorder Operations ────────────────────────────────────────────
433
- /**
434
- * Reorder milestones within a roadmap.
435
- *
436
- * Applies an explicit reorder input and persists the full normalized order.
437
- * The input must contain all milestone IDs exactly once.
438
- *
439
- * @param input - Reorder input with complete milestone ID list
440
- * @returns The reordered milestones in their new order
441
- * @throws Error if milestone set is incomplete, duplicate, or not found
442
- */
443
- reorderMilestones(input) {
444
- // Validate roadmap exists
445
- const roadmap = this.getRoadmap(input.roadmapId);
446
- if (!roadmap) {
447
- throw new Error(`Roadmap ${input.roadmapId} not found`);
448
- }
449
- // Load current milestones with deterministic ordering
450
- const milestones = this.listMilestones(input.roadmapId);
451
- // Apply the reorder using the pure ordering helper
452
- const reordered = applyRoadmapMilestoneReorder(milestones, input);
453
- // Persist in a transaction
454
- this.db.transaction(() => {
455
- for (const milestone of reordered) {
456
- this.db.prepare(`
457
- UPDATE roadmap_milestones SET orderIndex = ?, updatedAt = ? WHERE id = ?
458
- `).run(milestone.orderIndex, new Date().toISOString(), milestone.id);
459
- }
460
- });
461
- this.db.bumpLastModified();
462
- this.emit("milestone:reordered", { roadmapId: input.roadmapId, milestones: reordered });
463
- return reordered;
464
- }
465
- /**
466
- * Reorder features within a milestone.
467
- *
468
- * Applies an explicit reorder input and persists the full normalized order.
469
- * The input must contain all feature IDs for the milestone exactly once.
470
- *
471
- * @param input - Reorder input with complete feature ID list
472
- * @returns The reordered features in their new order
473
- * @throws Error if feature set is incomplete, duplicate, or not found
474
- */
475
- reorderFeatures(input) {
476
- // Validate milestone exists and belongs to the roadmap
477
- const milestone = this.getMilestone(input.milestoneId);
478
- if (!milestone) {
479
- throw new Error(`Milestone ${input.milestoneId} not found`);
480
- }
481
- if (milestone.roadmapId !== input.roadmapId) {
482
- throw new Error(`Milestone ${input.milestoneId} does not belong to roadmap ${input.roadmapId}`);
483
- }
484
- // Load current features with deterministic ordering
485
- const features = this.listFeatures(input.milestoneId);
486
- // Apply the reorder using the pure ordering helper
487
- const reordered = applyRoadmapFeatureReorder(features, input);
488
- // Persist in a transaction
489
- this.db.transaction(() => {
490
- for (const feature of reordered) {
491
- this.db.prepare(`
492
- UPDATE roadmap_features SET orderIndex = ?, updatedAt = ? WHERE id = ?
493
- `).run(feature.orderIndex, new Date().toISOString(), feature.id);
494
- }
495
- });
496
- this.db.bumpLastModified();
497
- this.emit("feature:reordered", { milestoneId: input.milestoneId, features: reordered });
498
- return reordered;
499
- }
500
- /**
501
- * Move a feature, including cross-milestone moves.
502
- *
503
- * Atomically renumbers both the source and destination milestone scopes.
504
- *
505
- * @param input - Move input with source/destination milestone info
506
- * @returns The moved feature and both affected milestone feature lists
507
- * @throws Error if feature or milestone not found, or scope validation fails
508
- */
509
- moveFeature(input) {
510
- // Validate roadmap exists
511
- const roadmap = this.getRoadmap(input.roadmapId);
512
- if (!roadmap) {
513
- throw new Error(`Roadmap ${input.roadmapId} not found`);
514
- }
515
- // Validate both milestones exist and belong to the roadmap
516
- const fromMilestone = this.getMilestone(input.fromMilestoneId);
517
- const toMilestone = this.getMilestone(input.toMilestoneId);
518
- if (!fromMilestone) {
519
- throw new Error(`Source milestone ${input.fromMilestoneId} not found`);
520
- }
521
- if (!toMilestone) {
522
- throw new Error(`Destination milestone ${input.toMilestoneId} not found`);
523
- }
524
- if (fromMilestone.roadmapId !== input.roadmapId) {
525
- throw new Error(`Source milestone ${input.fromMilestoneId} does not belong to roadmap ${input.roadmapId}`);
526
- }
527
- if (toMilestone.roadmapId !== input.roadmapId) {
528
- throw new Error(`Destination milestone ${input.toMilestoneId} does not belong to roadmap ${input.roadmapId}`);
529
- }
530
- // Load features from both milestones with deterministic ordering
531
- const sourceFeatures = this.listFeatures(input.fromMilestoneId);
532
- const targetFeatures = this.listFeatures(input.toMilestoneId);
533
- // For same-milestone moves, pass only one list to avoid duplication
534
- // For cross-milestone moves, pass the combined list
535
- const allFeatures = input.fromMilestoneId === input.toMilestoneId
536
- ? sourceFeatures
537
- : [...sourceFeatures, ...targetFeatures];
538
- // Apply the move using the pure ordering helper
539
- const result = moveRoadmapFeature(allFeatures, input);
540
- // Persist in a transaction
541
- this.db.transaction(() => {
542
- // Update all affected features
543
- for (const feature of result.affectedFeatures) {
544
- this.db.prepare(`
545
- UPDATE roadmap_features SET milestoneId = ?, orderIndex = ?, updatedAt = ? WHERE id = ?
546
- `).run(feature.milestoneId, feature.orderIndex, new Date().toISOString(), feature.id);
547
- }
548
- });
549
- this.db.bumpLastModified();
550
- this.emit("feature:moved", {
551
- feature: result.movedFeature,
552
- fromMilestoneId: input.fromMilestoneId,
553
- toMilestoneId: input.toMilestoneId,
554
- });
555
- return {
556
- movedFeature: result.movedFeature,
557
- sourceMilestoneFeatures: result.sourceMilestoneFeatures,
558
- targetMilestoneFeatures: result.targetMilestoneFeatures,
559
- };
560
- }
561
- // ── Hierarchy Operations ───────────────────────────────────────────
562
- /**
563
- * Get a milestone with all of its features in deterministic order.
564
- *
565
- * @param id - Milestone ID
566
- * @returns The milestone with features, or undefined if not found
567
- */
568
- getMilestoneWithFeatures(id) {
569
- const milestone = this.getMilestone(id);
570
- if (!milestone)
571
- return undefined;
572
- return {
573
- ...milestone,
574
- features: this.listFeatures(id),
575
- };
576
- }
577
- /**
578
- * Get a roadmap with its full hierarchy (milestones → features).
579
- *
580
- * @param id - Roadmap ID
581
- * @returns The roadmap with hierarchy, or undefined if not found
582
- */
583
- getRoadmapWithHierarchy(id) {
584
- const roadmap = this.getRoadmap(id);
585
- if (!roadmap)
586
- return undefined;
587
- return {
588
- ...roadmap,
589
- milestones: this.listMilestones(id).map((milestone) => ({
590
- ...milestone,
591
- features: this.listFeatures(milestone.id),
592
- })),
593
- };
594
- }
595
- // ── Export / Handoff Operations ────────────────────────────────────
596
- /**
597
- * Get a flat export bundle for a roadmap.
598
- *
599
- * Returns all roadmap data in a flat structure suitable for persistence,
600
- * APIs, import/export, and sync jobs. Entities are separated so downstream
601
- * persistence layers can upsert by table/collection.
602
- *
603
- * @param roadmapId - Roadmap ID
604
- * @returns The export bundle with ordered entities
605
- * @throws Error if roadmap not found
606
- */
607
- getRoadmapExport(roadmapId) {
608
- const roadmap = this.getRoadmap(roadmapId);
609
- if (!roadmap) {
610
- throw new Error(`Roadmap ${roadmapId} not found`);
611
- }
612
- const milestones = this.listMilestones(roadmapId);
613
- const allFeatures = [];
614
- for (const milestone of milestones) {
615
- const features = this.listFeatures(milestone.id);
616
- allFeatures.push(...features);
617
- }
618
- return {
619
- roadmap,
620
- milestones,
621
- features: allFeatures,
622
- };
623
- }
624
- /**
625
- * Get a mission planning handoff payload for a roadmap.
626
- *
627
- * Converts the roadmap into a mission planning structure while preserving
628
- * source IDs and deterministic order. Does not couple to MissionStore internals.
629
- *
630
- * @param roadmapId - Roadmap ID
631
- * @returns The mission planning handoff payload
632
- * @throws Error if roadmap not found
633
- */
634
- getRoadmapMissionHandoff(roadmapId) {
635
- const roadmap = this.getRoadmap(roadmapId);
636
- if (!roadmap) {
637
- throw new Error(`Roadmap ${roadmapId} not found`);
638
- }
639
- const milestones = this.listMilestones(roadmapId);
640
- return {
641
- sourceRoadmapId: roadmap.id,
642
- title: roadmap.title,
643
- description: roadmap.description,
644
- milestones: milestones.map((milestone) => {
645
- const features = this.listFeatures(milestone.id);
646
- return {
647
- sourceMilestoneId: milestone.id,
648
- title: milestone.title,
649
- description: milestone.description,
650
- orderIndex: milestone.orderIndex,
651
- features: features.map((feature) => ({
652
- sourceFeatureId: feature.id,
653
- title: feature.title,
654
- description: feature.description,
655
- orderIndex: feature.orderIndex,
656
- })),
657
- };
658
- }),
659
- };
660
- }
661
- /**
662
- * Get a task planning handoff payload for a single roadmap feature.
663
- *
664
- * Returns a self-contained handoff payload for converting a roadmap feature
665
- * into task planning flows without coupling to MissionStore internals.
666
- *
667
- * @param roadmapId - Parent roadmap ID (for validation)
668
- * @param milestoneId - Parent milestone ID (for validation)
669
- * @param featureId - Feature ID to generate handoff for
670
- * @returns The task planning handoff payload
671
- * @throws Error if any entity is not found or if ownership validation fails
672
- */
673
- getRoadmapFeatureHandoff(roadmapId, milestoneId, featureId) {
674
- // Validate roadmap exists
675
- const roadmap = this.getRoadmap(roadmapId);
676
- if (!roadmap) {
677
- throw new Error(`Roadmap ${roadmapId} not found`);
678
- }
679
- // Validate milestone exists and belongs to roadmap
680
- const milestone = this.getMilestone(milestoneId);
681
- if (!milestone) {
682
- throw new Error(`Milestone ${milestoneId} not found`);
683
- }
684
- if (milestone.roadmapId !== roadmapId) {
685
- throw new Error(`Milestone ${milestoneId} does not belong to roadmap ${roadmapId}`);
686
- }
687
- // Validate feature exists and belongs to milestone
688
- const feature = this.getFeature(featureId);
689
- if (!feature) {
690
- throw new Error(`Feature ${featureId} not found`);
691
- }
692
- if (feature.milestoneId !== milestoneId) {
693
- throw new Error(`Feature ${featureId} does not belong to milestone ${milestoneId}`);
694
- }
695
- // Build the source reference with ordering context
696
- const source = {
697
- roadmapId: roadmap.id,
698
- milestoneId: milestone.id,
699
- featureId: feature.id,
700
- roadmapTitle: roadmap.title,
701
- milestoneTitle: milestone.title,
702
- milestoneOrderIndex: milestone.orderIndex,
703
- featureOrderIndex: feature.orderIndex,
704
- };
705
- return {
706
- source,
707
- title: feature.title,
708
- description: feature.description,
709
- };
710
- }
711
- /**
712
- * Get a mission planning handoff payload for a roadmap.
713
- *
714
- * Alias for getRoadmapMissionHandoff() for API consistency.
715
- * Converts the roadmap into a mission planning structure while preserving
716
- * source IDs and deterministic order.
717
- *
718
- * @param roadmapId - Roadmap ID
719
- * @returns The mission planning handoff payload
720
- * @throws Error if roadmap not found
721
- */
722
- getMissionPlanningHandoff(roadmapId) {
723
- return this.getRoadmapMissionHandoff(roadmapId);
724
- }
725
- /**
726
- * List all task planning handoff payloads for a roadmap.
727
- *
728
- * Returns a flat list of all feature handoffs in deterministic order
729
- * (milestone order index, then feature order index).
730
- *
731
- * @param roadmapId - Roadmap ID
732
- * @returns Array of task planning handoff payloads for all features
733
- * @throws Error if roadmap not found
734
- */
735
- listFeatureTaskPlanningHandoffs(roadmapId) {
736
- // Validate roadmap exists
737
- const roadmap = this.getRoadmap(roadmapId);
738
- if (!roadmap) {
739
- throw new Error(`Roadmap ${roadmapId} not found`);
740
- }
741
- const milestones = this.listMilestones(roadmapId);
742
- const handoffs = [];
743
- for (const milestone of milestones) {
744
- const features = this.listFeatures(milestone.id);
745
- for (const feature of features) {
746
- const source = {
747
- roadmapId: roadmap.id,
748
- milestoneId: milestone.id,
749
- featureId: feature.id,
750
- roadmapTitle: roadmap.title,
751
- milestoneTitle: milestone.title,
752
- milestoneOrderIndex: milestone.orderIndex,
753
- featureOrderIndex: feature.orderIndex,
754
- };
755
- handoffs.push({
756
- source,
757
- title: feature.title,
758
- description: feature.description,
759
- });
760
- }
761
- }
762
- return handoffs;
763
- }
764
- }
765
- //# sourceMappingURL=roadmap-store.js.map