heyio 0.42.0 → 1.0.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 (100) hide show
  1. package/README.md +40 -52
  2. package/dist/api/auth.js +35 -38
  3. package/dist/api/server.js +157 -1139
  4. package/dist/config.js +49 -32
  5. package/dist/copilot/agents.js +72 -1055
  6. package/dist/copilot/client.js +6 -17
  7. package/dist/copilot/io-scheduler.js +55 -139
  8. package/dist/copilot/model-router.js +100 -72
  9. package/dist/copilot/orchestrator.js +91 -515
  10. package/dist/copilot/scheduler.js +67 -189
  11. package/dist/copilot/skills.js +41 -366
  12. package/dist/copilot/system-message.js +40 -200
  13. package/dist/copilot/tools.js +191 -2042
  14. package/dist/daemon.js +54 -201
  15. package/dist/index.js +15 -133
  16. package/dist/mcp/config.js +23 -31
  17. package/dist/mcp/index.js +2 -3
  18. package/dist/mcp/registry.js +33 -88
  19. package/dist/notify.js +18 -100
  20. package/dist/paths.js +13 -24
  21. package/dist/setup.js +35 -0
  22. package/dist/store/db.js +111 -297
  23. package/dist/store/feed.js +29 -97
  24. package/dist/store/instances.js +56 -121
  25. package/dist/store/schedules.js +21 -73
  26. package/dist/store/squads.js +35 -186
  27. package/dist/store/tasks.js +25 -168
  28. package/dist/telegram/bot.js +20 -312
  29. package/dist/telegram/handlers.js +39 -3
  30. package/dist/watchdog.js +31 -45
  31. package/dist/wiki/fs.js +38 -155
  32. package/dist/wiki/search.js +31 -44
  33. package/package.json +5 -8
  34. package/web-dist/assets/ChatView-EFFiln1H.js +11 -0
  35. package/web-dist/assets/FeedView-bN4NMOL7.js +6 -0
  36. package/web-dist/assets/LoginView-CNtasq3n.js +1 -0
  37. package/web-dist/assets/McpView-C2CHiwsi.js +1 -0
  38. package/web-dist/assets/SchedulesView-CyilLban.js +1 -0
  39. package/web-dist/assets/SettingsView-1wLXKEF4.js +1 -0
  40. package/web-dist/assets/SkillsView-BLsD-0u0.js +1 -0
  41. package/web-dist/assets/SquadDetailView-CsCw2ZLp.js +21 -0
  42. package/web-dist/assets/SquadsView-DQ3vFlyO.js +6 -0
  43. package/web-dist/assets/WikiView-19M3oqnq.js +21 -0
  44. package/web-dist/assets/api-WGvTsXaE.js +1 -0
  45. package/web-dist/assets/index-D7M5O-_l.css +1 -0
  46. package/web-dist/assets/index-DZOS9syn.js +95 -0
  47. package/web-dist/assets/plus-BOvyX1BC.js +6 -0
  48. package/web-dist/assets/trash-2-DHoetkC4.js +6 -0
  49. package/web-dist/favicon.svg +4 -1
  50. package/web-dist/index.html +7 -10
  51. package/dist/api/logout.test.js +0 -129
  52. package/dist/api/mcp.test.js +0 -285
  53. package/dist/api/wiki.test.js +0 -283
  54. package/dist/auth/session-logic.js +0 -79
  55. package/dist/auth/session-logic.test.js +0 -201
  56. package/dist/copilot/auto-complete-instance.test.js +0 -104
  57. package/dist/copilot/cron.js +0 -136
  58. package/dist/copilot/event-summary.js +0 -286
  59. package/dist/copilot/instance-deactivate.test.js +0 -119
  60. package/dist/copilot/model-router.test.js +0 -71
  61. package/dist/copilot/review-backfill.js +0 -57
  62. package/dist/copilot/session-timeout.js +0 -112
  63. package/dist/copilot/session-timeout.test.js +0 -372
  64. package/dist/copilot/skills.test.js +0 -55
  65. package/dist/copilot/universes.js +0 -469
  66. package/dist/instance-watchdog.js +0 -104
  67. package/dist/instance-watchdog.test.js +0 -183
  68. package/dist/mcp/client.js +0 -109
  69. package/dist/mcp/client.test.js +0 -99
  70. package/dist/mcp/config.test.js +0 -49
  71. package/dist/mcp/registry.test.js +0 -79
  72. package/dist/notify.test.js +0 -232
  73. package/dist/store/feed.test.js +0 -279
  74. package/dist/store/instances.test.js +0 -310
  75. package/dist/store/io-schedules.js +0 -63
  76. package/dist/store/notifications.js +0 -79
  77. package/dist/store/notifications.test.js +0 -197
  78. package/dist/store/schedule-runs.js +0 -46
  79. package/dist/store/squads.test.js +0 -405
  80. package/dist/store/tasks.test.js +0 -150
  81. package/dist/store/worktrees.js +0 -83
  82. package/dist/tui/index.js +0 -286
  83. package/dist/update.js +0 -81
  84. package/dist/watchdog.test.js +0 -83
  85. package/dist/wiki/wiki-squad.test.js +0 -54
  86. package/web-dist/assets/AgentActivityView-CedxxE6K.js +0 -1
  87. package/web-dist/assets/ChatView-DMkYQo_V.js +0 -4
  88. package/web-dist/assets/FeedView-BH4q-31V.js +0 -1
  89. package/web-dist/assets/InboxView-BVwVP4EW.js +0 -1
  90. package/web-dist/assets/LoginView-DRPDhnwu.js +0 -1
  91. package/web-dist/assets/McpView-D8yWz-lq.js +0 -1
  92. package/web-dist/assets/SchedulesView-BzzyncGF.js +0 -1
  93. package/web-dist/assets/SettingsTabs.vue_vue_type_script_setup_true_lang-oW3ySu7Y.js +0 -1
  94. package/web-dist/assets/SkillsView-oxpYuhx7.js +0 -1
  95. package/web-dist/assets/SquadsView-CaKUIKlq.js +0 -1
  96. package/web-dist/assets/StatusIndicator.vue_vue_type_script_setup_true_lang-8U15Qp_Q.js +0 -1
  97. package/web-dist/assets/WikiView-C5jXUlfW.js +0 -1
  98. package/web-dist/assets/index-BrWzNw-N.css +0 -10
  99. package/web-dist/assets/index-f67odrrt.js +0 -81
  100. package/web-dist/icons.svg +0 -24
@@ -1,372 +0,0 @@
1
- /**
2
- * Tests for src/copilot/session-timeout.ts — sendWithIdleTimeout
3
- *
4
- * Strategy: build a FakeSession class whose sendAndWait() is controlled by
5
- * a deferred Promise. Event handlers registered via .on() are stored in a
6
- * subscriber list and can be triggered manually with .emit(). Timer
7
- * behaviour is driven by node:test's mock.timers so no real setTimeout fires.
8
- */
9
- import { describe, it, before, after, beforeEach, mock } from "node:test";
10
- import assert from "node:assert/strict";
11
- import { sendWithIdleTimeout, } from "./session-timeout.js";
12
- /** Minimal fake implementing only what sendWithIdleTimeout needs. */
13
- function makeFakeSession() {
14
- const broadcastHandlers = [];
15
- const deltaHandlers = [];
16
- let resolveWait;
17
- let rejectWait;
18
- let aborted = false;
19
- const session = {
20
- // Typed overload: session.on("assistant.message_delta", handler)
21
- // Generic overload: session.on(handler)
22
- on(typeOrHandler, handler) {
23
- if (typeof typeOrHandler === "function") {
24
- broadcastHandlers.push(typeOrHandler);
25
- return () => {
26
- const idx = broadcastHandlers.indexOf(typeOrHandler);
27
- if (idx !== -1)
28
- broadcastHandlers.splice(idx, 1);
29
- };
30
- }
31
- // typed: only care about delta for accumulation
32
- if (typeOrHandler === "assistant.message_delta" && handler) {
33
- deltaHandlers.push(handler);
34
- return () => {
35
- const idx = deltaHandlers.indexOf(handler);
36
- if (idx !== -1)
37
- deltaHandlers.splice(idx, 1);
38
- };
39
- }
40
- return () => { };
41
- },
42
- sendAndWait(_opts, _timeout) {
43
- return new Promise((resolve, reject) => {
44
- resolveWait = resolve;
45
- rejectWait = reject;
46
- });
47
- },
48
- abort() {
49
- aborted = true;
50
- return Promise.resolve();
51
- },
52
- // ── Test-only helpers ──────────────────────────────────────────────────
53
- /** Emit an event to all broad-listener handlers. */
54
- emit(event) {
55
- for (const h of broadcastHandlers)
56
- h(event);
57
- },
58
- /** Emit an assistant.message_delta event with the given text chunk. */
59
- emitDelta(deltaContent) {
60
- const deltaEvent = {
61
- type: "assistant.message_delta",
62
- id: "evt-1",
63
- timestamp: new Date().toISOString(),
64
- parentId: null,
65
- ephemeral: true,
66
- data: { messageId: "msg-1", deltaContent },
67
- };
68
- // Notify delta-specific handlers
69
- for (const h of deltaHandlers) {
70
- h(deltaEvent);
71
- }
72
- // Also broadcast to all-event handlers so idle timer resets
73
- this.emit(deltaEvent);
74
- },
75
- /** Emit a generic progress event (e.g. tool.execution_complete). */
76
- emitProgress(type) {
77
- const event = {
78
- type,
79
- id: "evt-2",
80
- timestamp: new Date().toISOString(),
81
- parentId: null,
82
- ephemeral: false,
83
- data: {},
84
- };
85
- this.emit(event);
86
- },
87
- /** Resolve the sendAndWait promise with a final content string. */
88
- resolve(content) {
89
- resolveWait?.({ data: { content } });
90
- },
91
- /** Resolve the sendAndWait promise with undefined (triggers accumulated fallback). */
92
- resolveUndefined() {
93
- resolveWait?.(undefined);
94
- },
95
- /** Reject the sendAndWait promise with a timeout-style error. */
96
- rejectWithTimeout() {
97
- rejectWait?.(new Error("Timeout after 600000ms waiting for session.idle"));
98
- },
99
- /** Reject the sendAndWait promise with a non-timeout error. */
100
- rejectWithError(msg) {
101
- rejectWait?.(new Error(msg));
102
- },
103
- get wasAborted() {
104
- return aborted;
105
- },
106
- };
107
- return session;
108
- }
109
- // Cast FakeSession to CopilotSession for type-compatibility with sendWithIdleTimeout.
110
- // The fake satisfies the structural subset used by the function.
111
- function asSession(fake) {
112
- return fake;
113
- }
114
- // ── Helpers ──────────────────────────────────────────────────────────────────
115
- const DEFAULT_OPTS = {
116
- idleMs: 5_000,
117
- hardCapMs: 30_000,
118
- };
119
- // ── Tests ─────────────────────────────────────────────────────────────────────
120
- describe("sendWithIdleTimeout", () => {
121
- before(() => {
122
- mock.timers.enable({ apis: ["setTimeout"] });
123
- });
124
- after(() => {
125
- mock.timers.reset();
126
- });
127
- beforeEach(() => {
128
- // Reset the fake timer state between tests without disabling
129
- mock.timers.reset();
130
- mock.timers.enable({ apis: ["setTimeout"] });
131
- });
132
- // ── happy path ──────────────────────────────────────────────────────────
133
- it("returns content and timedOut=false when session resolves normally", async () => {
134
- const fake = makeFakeSession();
135
- const promise = sendWithIdleTimeout(asSession(fake), "do something", DEFAULT_OPTS);
136
- // Let the initial idle timer be registered, then resolve immediately
137
- fake.resolve("task done successfully");
138
- const result = await promise;
139
- assert.equal(result.timedOut, false);
140
- assert.equal(result.content, "task done successfully");
141
- assert.equal(result.timeoutReason, undefined);
142
- });
143
- it("accumulates delta content during streaming (verified via resolveUndefined)", async () => {
144
- const fake = makeFakeSession();
145
- const promise = sendWithIdleTimeout(asSession(fake), "stream this", DEFAULT_OPTS);
146
- fake.emitDelta("Hello ");
147
- fake.emitDelta("world");
148
- fake.emitDelta("!");
149
- // resolveUndefined triggers `response?.data?.content ?? accumulated` → uses accumulated
150
- fake.resolveUndefined();
151
- const result = await promise;
152
- assert.equal(result.timedOut, false);
153
- assert.equal(result.content, "Hello world!");
154
- });
155
- it("falls back to accumulated delta when sendAndWait resolves with undefined", async () => {
156
- const fake = makeFakeSession();
157
- const promise = sendWithIdleTimeout(asSession(fake), "stream this", DEFAULT_OPTS);
158
- fake.emitDelta("partial ");
159
- fake.emitDelta("output");
160
- // Resolve with undefined to trigger the `response?.data?.content ?? accumulated` fallback
161
- fake.resolveUndefined();
162
- const result = await promise;
163
- assert.equal(result.timedOut, false);
164
- assert.equal(result.content, "partial output");
165
- });
166
- // ── idle timer reset ────────────────────────────────────────────────────
167
- it("resets idle timer on assistant.message_delta", async () => {
168
- const fake = makeFakeSession();
169
- const idleTimeoutFired = { value: false };
170
- const opts = {
171
- ...DEFAULT_OPTS,
172
- idleMs: 1_000,
173
- onIdleTimeout: () => { idleTimeoutFired.value = true; },
174
- };
175
- const promise = sendWithIdleTimeout(asSession(fake), "do work", opts);
176
- // Advance to just before idle timeout — timer should NOT fire yet
177
- mock.timers.tick(800);
178
- assert.equal(idleTimeoutFired.value, false);
179
- // Emit a progress event — should reset the idle timer
180
- fake.emitProgress("tool.execution_complete");
181
- // Advance another 800ms — if reset worked, timer still hasn't fired (800 < 1000)
182
- mock.timers.tick(800);
183
- assert.equal(idleTimeoutFired.value, false, "idle timer should have been reset by progress event");
184
- // Resolve and clean up
185
- fake.resolve("done");
186
- await promise;
187
- });
188
- it("fires idle timeout after idleMs of silence", async () => {
189
- const fake = makeFakeSession();
190
- let idleFired = false;
191
- const opts = {
192
- ...DEFAULT_OPTS,
193
- idleMs: 2_000,
194
- onIdleTimeout: () => { idleFired = true; },
195
- };
196
- const promise = sendWithIdleTimeout(asSession(fake), "long task", opts);
197
- // Advance past idle timeout — no events emitted
198
- mock.timers.tick(2_001);
199
- // Abort fires async; wait a tick then check
200
- await Promise.resolve();
201
- assert.equal(idleFired, true, "onIdleTimeout should have fired");
202
- assert.equal(fake.wasAborted, true, "session.abort() should have been called");
203
- // Simulate the sendAndWait timing out after abort
204
- fake.rejectWithTimeout();
205
- const result = await promise;
206
- assert.equal(result.timedOut, true);
207
- });
208
- it("resets idle timer on tool.execution_complete", async () => {
209
- const fake = makeFakeSession();
210
- let idleFired = false;
211
- const opts = {
212
- ...DEFAULT_OPTS,
213
- idleMs: 1_000,
214
- onIdleTimeout: () => { idleFired = true; },
215
- };
216
- const promise = sendWithIdleTimeout(asSession(fake), "tool task", opts);
217
- mock.timers.tick(700);
218
- fake.emitProgress("tool.execution_complete");
219
- mock.timers.tick(700); // 700ms since last reset — still under 1000ms
220
- assert.equal(idleFired, false, "idle timer should have been reset by tool event");
221
- fake.resolve("done");
222
- await promise;
223
- assert.equal(idleFired, false);
224
- });
225
- it("resets idle timer on assistant.turn_start", async () => {
226
- const fake = makeFakeSession();
227
- let idleFired = false;
228
- const opts = {
229
- ...DEFAULT_OPTS,
230
- idleMs: 1_000,
231
- onIdleTimeout: () => { idleFired = true; },
232
- };
233
- const promise = sendWithIdleTimeout(asSession(fake), "turn task", opts);
234
- mock.timers.tick(700);
235
- fake.emitProgress("assistant.turn_start");
236
- mock.timers.tick(700);
237
- assert.equal(idleFired, false, "idle timer should reset on assistant.turn_start");
238
- fake.resolve("done");
239
- await promise;
240
- });
241
- it("does NOT reset idle timer for unrecognised event types", async () => {
242
- const fake = makeFakeSession();
243
- let idleFired = false;
244
- const opts = {
245
- ...DEFAULT_OPTS,
246
- idleMs: 1_000,
247
- onIdleTimeout: () => { idleFired = true; },
248
- };
249
- const promise = sendWithIdleTimeout(asSession(fake), "noisy task", opts);
250
- mock.timers.tick(700);
251
- // This event type is not in PROGRESS_EVENT_TYPES
252
- fake.emitProgress("some.unknown.event");
253
- mock.timers.tick(400); // 700 + 400 = 1100ms since last real reset
254
- await Promise.resolve();
255
- assert.equal(idleFired, true, "idle timer should fire — unknown event should not reset it");
256
- fake.rejectWithTimeout();
257
- const result = await promise;
258
- assert.equal(result.timedOut, true);
259
- });
260
- // ── graceful timeout capture ─────────────────────────────────────────────
261
- it("captures partial content when idle timeout fires mid-stream", async () => {
262
- const fake = makeFakeSession();
263
- const opts = {
264
- ...DEFAULT_OPTS,
265
- idleMs: 1_000,
266
- };
267
- const promise = sendWithIdleTimeout(asSession(fake), "long output", opts);
268
- // Agent emits some content then goes silent
269
- fake.emitDelta("Step 1 done. ");
270
- fake.emitDelta("Step 2 in progress...");
271
- // Advance past idle timeout
272
- mock.timers.tick(1_001);
273
- await Promise.resolve();
274
- // sendAndWait throws a timeout error after abort
275
- fake.rejectWithTimeout();
276
- const result = await promise;
277
- assert.equal(result.timedOut, true);
278
- // idle timer fires first → abortReason="idle"; catch block sees aborted=true and preserves it
279
- assert.equal(result.timeoutReason, "idle", "idle fired before sendAndWait rejection → reason stays idle");
280
- assert.ok(result.content.includes("Step 1 done.") && result.content.includes("Step 2 in progress..."), `partial content should be captured; got: ${result.content}`);
281
- });
282
- it("returns fallback message when no content was accumulated before timeout", async () => {
283
- const fake = makeFakeSession();
284
- const opts = { ...DEFAULT_OPTS, idleMs: 500 };
285
- const promise = sendWithIdleTimeout(asSession(fake), "silent agent", opts);
286
- mock.timers.tick(501);
287
- await Promise.resolve();
288
- fake.rejectWithTimeout();
289
- const result = await promise;
290
- assert.equal(result.timedOut, true);
291
- assert.ok(result.content.includes("no output captured"), `should report no output captured; got: ${result.content}`);
292
- });
293
- it("sets timeoutReason=idle when abort fires before sendAndWait throws", async () => {
294
- const fake = makeFakeSession();
295
- let idleCallbackInfo;
296
- const opts = {
297
- ...DEFAULT_OPTS,
298
- idleMs: 500,
299
- onIdleTimeout: (info) => { idleCallbackInfo = info; },
300
- };
301
- const promise = sendWithIdleTimeout(asSession(fake), "silent", opts);
302
- mock.timers.tick(501);
303
- await Promise.resolve(); // let abort() fire
304
- // When aborted=true, the resolve branch returns timedOut:true with timeoutReason from abortReason
305
- fake.resolve("some final content");
306
- const result = await promise;
307
- assert.equal(result.timedOut, true);
308
- assert.equal(result.timeoutReason, "idle");
309
- assert.ok(idleCallbackInfo, "onIdleTimeout callback should have been called");
310
- assert.equal(idleCallbackInfo?.idleMs, 500);
311
- });
312
- it("calls onProgress for each recognised event type", async () => {
313
- const fake = makeFakeSession();
314
- const progressEvents = [];
315
- const opts = {
316
- ...DEFAULT_OPTS,
317
- onProgress: (type) => progressEvents.push(type),
318
- };
319
- const promise = sendWithIdleTimeout(asSession(fake), "track progress", opts);
320
- fake.emitProgress("tool.execution_start");
321
- fake.emitProgress("tool.execution_complete");
322
- fake.emitDelta("some output");
323
- fake.resolve("done");
324
- await promise;
325
- assert.ok(progressEvents.includes("tool.execution_start"));
326
- assert.ok(progressEvents.includes("tool.execution_complete"));
327
- assert.ok(progressEvents.includes("assistant.message_delta"));
328
- });
329
- it("tracks lastEventType in result", async () => {
330
- const fake = makeFakeSession();
331
- const promise = sendWithIdleTimeout(asSession(fake), "track last", DEFAULT_OPTS);
332
- fake.emitProgress("tool.execution_start");
333
- fake.emitProgress("tool.execution_complete");
334
- fake.resolve("done");
335
- const result = await promise;
336
- assert.equal(result.lastEventType, "tool.execution_complete");
337
- });
338
- // ── error handling ───────────────────────────────────────────────────────
339
- it("re-throws non-timeout errors", async () => {
340
- const fake = makeFakeSession();
341
- const promise = sendWithIdleTimeout(asSession(fake), "bad prompt", DEFAULT_OPTS);
342
- fake.rejectWithError("unexpected authentication failure");
343
- await assert.rejects(() => promise, (err) => {
344
- assert.ok(err.message.includes("unexpected authentication failure"));
345
- return true;
346
- });
347
- });
348
- it("calls onHardCap when sendAndWait throws timeout and aborted=false", async () => {
349
- const fake = makeFakeSession();
350
- let hardCapFired = false;
351
- const opts = {
352
- ...DEFAULT_OPTS,
353
- onHardCap: () => { hardCapFired = true; },
354
- };
355
- const promise = sendWithIdleTimeout(asSession(fake), "hard task", opts);
356
- // Don't advance past idle timer — just let the hard cap throw
357
- fake.rejectWithTimeout();
358
- const result = await promise;
359
- assert.equal(result.timedOut, true);
360
- assert.equal(result.timeoutReason, "hard_cap");
361
- assert.equal(hardCapFired, true);
362
- });
363
- it("cleans up subscriptions after normal completion (no memory leak)", async () => {
364
- const fake = makeFakeSession();
365
- const promise = sendWithIdleTimeout(asSession(fake), "cleanup test", DEFAULT_OPTS);
366
- fake.resolve("done");
367
- const result = await promise;
368
- assert.equal(result.timedOut, false);
369
- // If unsubscribe threw, the test would fail — we're asserting no throw
370
- });
371
- });
372
- //# sourceMappingURL=session-timeout.test.js.map
@@ -1,55 +0,0 @@
1
- /**
2
- * Tests for src/copilot/skills.ts — skill installation, listing, and management.
3
- */
4
- import { describe, it } from "node:test";
5
- import assert from "node:assert/strict";
6
- import { parseSkillUrl, removeSkill, updateSkill, deleteSkill, } from "./skills.js";
7
- // ── Tests ─────────────────────────────────────────────────────────────────────
8
- describe("parseSkillUrl", () => {
9
- it("parses GitHub blob URLs for SKILL.md", () => {
10
- const url = "https://github.com/owner/repo/blob/main/SKILL.md";
11
- const result = parseSkillUrl(url);
12
- assert.equal(result.type, "file");
13
- assert.ok("rawUrl" in result);
14
- assert.ok(result.rawUrl.includes("raw.githubusercontent.com"));
15
- });
16
- it("parses GitHub raw content URLs", () => {
17
- const url = "https://raw.githubusercontent.com/owner/repo/main/SKILL.md";
18
- const result = parseSkillUrl(url);
19
- assert.equal(result.type, "file");
20
- assert.ok("rawUrl" in result);
21
- });
22
- it("parses GitHub repo URLs as repo type", () => {
23
- const url = "https://github.com/owner/repo";
24
- const result = parseSkillUrl(url);
25
- assert.equal(result.type, "repo");
26
- assert.equal(result.url, url);
27
- });
28
- it("rejects non-https SKILL.md URLs", () => {
29
- const url = "http://example.com/SKILL.md";
30
- assert.throws(() => parseSkillUrl(url));
31
- });
32
- it("derives slug from repo name with path prefix", () => {
33
- const url = "https://github.com/owner/repo/blob/main/skills/ai-chat/SKILL.md";
34
- const result = parseSkillUrl(url);
35
- assert.equal(result.type, "file");
36
- assert.ok(result.slug.includes("repo"));
37
- });
38
- });
39
- describe("skill store functions", () => {
40
- it("exports updateSkill function", () => {
41
- assert.ok(typeof updateSkill === "function");
42
- });
43
- it("exports deleteSkill function", () => {
44
- assert.ok(typeof deleteSkill === "function");
45
- });
46
- it("exports removeSkill function", () => {
47
- assert.ok(typeof removeSkill === "function");
48
- });
49
- it("deleteSkill is callable (alias)", () => {
50
- // Just verify these are functions and importable
51
- assert.ok(deleteSkill !== undefined);
52
- assert.ok(removeSkill !== undefined);
53
- });
54
- });
55
- //# sourceMappingURL=skills.test.js.map