clay-server 2.7.1 → 2.8.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.
- package/lib/project.js +176 -20
- package/lib/public/app.js +846 -92
- package/lib/public/apple-touch-icon-dark.png +0 -0
- package/lib/public/apple-touch-icon.png +0 -0
- package/lib/public/clay-logo.png +0 -0
- package/lib/public/css/base.css +10 -0
- package/lib/public/css/filebrowser.css +1 -0
- package/lib/public/css/home-hub.css +455 -0
- package/lib/public/css/icon-strip.css +6 -5
- package/lib/public/css/loop.css +86 -29
- package/lib/public/css/messages.css +2 -0
- package/lib/public/css/mobile-nav.css +38 -12
- package/lib/public/css/overlays.css +205 -169
- package/lib/public/css/playbook.css +264 -0
- package/lib/public/css/profile.css +268 -0
- package/lib/public/css/scheduler-modal.css +883 -0
- package/lib/public/css/scheduler.css +379 -18
- package/lib/public/css/sidebar.css +305 -11
- package/lib/public/css/sticky-notes.css +23 -19
- package/lib/public/css/stt.css +155 -0
- package/lib/public/css/title-bar.css +14 -6
- package/lib/public/favicon-banded-32.png +0 -0
- package/lib/public/favicon-banded.png +0 -0
- package/lib/public/icon-192-dark.png +0 -0
- package/lib/public/icon-192.png +0 -0
- package/lib/public/icon-512-dark.png +0 -0
- package/lib/public/icon-512.png +0 -0
- package/lib/public/icon-banded-76.png +0 -0
- package/lib/public/icon-banded-96.png +0 -0
- package/lib/public/index.html +252 -32
- package/lib/public/modules/ascii-logo.js +389 -0
- package/lib/public/modules/filebrowser.js +2 -1
- package/lib/public/modules/markdown.js +108 -0
- package/lib/public/modules/notifications.js +50 -63
- package/lib/public/modules/playbook.js +578 -0
- package/lib/public/modules/profile.js +357 -0
- package/lib/public/modules/project-settings.js +4 -9
- package/lib/public/modules/scheduler.js +1620 -34
- package/lib/public/modules/server-settings.js +1 -1
- package/lib/public/modules/sidebar.js +378 -31
- package/lib/public/modules/sticky-notes.js +2 -0
- package/lib/public/modules/stt.js +272 -0
- package/lib/public/modules/terminal.js +32 -0
- package/lib/public/modules/theme.js +3 -10
- package/lib/public/modules/tools.js +2 -1
- package/lib/public/style.css +4 -0
- package/lib/public/sw.js +82 -3
- package/lib/public/wordmark-banded-20.png +0 -0
- package/lib/public/wordmark-banded-32.png +0 -0
- package/lib/public/wordmark-banded-64.png +0 -0
- package/lib/public/wordmark-banded-80.png +0 -0
- package/lib/scheduler.js +43 -3
- package/lib/sdk-bridge.js +3 -2
- package/lib/server.js +124 -3
- package/lib/sessions.js +34 -1
- package/package.json +1 -1
package/lib/project.js
CHANGED
|
@@ -76,6 +76,10 @@ function createProjectContext(opts) {
|
|
|
76
76
|
var lanHost = opts.lanHost || null;
|
|
77
77
|
var getProjectCount = opts.getProjectCount || function () { return 1; };
|
|
78
78
|
var getProjectList = opts.getProjectList || function () { return []; };
|
|
79
|
+
var getHubSchedules = opts.getHubSchedules || function () { return []; };
|
|
80
|
+
var moveScheduleToProject = opts.moveScheduleToProject || function () { return { ok: false, error: "Not supported" }; };
|
|
81
|
+
var moveAllSchedulesToProject = opts.moveAllSchedulesToProject || function () { return { ok: false, error: "Not supported" }; };
|
|
82
|
+
var getScheduleCount = opts.getScheduleCount || function () { return 0; };
|
|
79
83
|
var onProcessingChanged = opts.onProcessingChanged || function () {};
|
|
80
84
|
var latestVersion = null;
|
|
81
85
|
|
|
@@ -438,29 +442,48 @@ function createProjectContext(opts) {
|
|
|
438
442
|
console.log("[loop-registry] Skipping trigger — loop already active");
|
|
439
443
|
return;
|
|
440
444
|
}
|
|
445
|
+
|
|
446
|
+
// For schedule records, resolve the linked task to get loop files
|
|
447
|
+
var loopId = record.id;
|
|
448
|
+
if (record.source === "schedule") {
|
|
449
|
+
if (!record.linkedTaskId) {
|
|
450
|
+
console.error("[loop-registry] Schedule has no linked task: " + record.name);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
loopId = record.linkedTaskId;
|
|
454
|
+
console.log("[loop-registry] Schedule triggered: " + record.name + " → linked task " + loopId);
|
|
455
|
+
}
|
|
456
|
+
|
|
441
457
|
// Verify the loop directory and files exist
|
|
442
|
-
var recDir = path.join(cwd, ".claude", "loops",
|
|
458
|
+
var recDir = path.join(cwd, ".claude", "loops", loopId);
|
|
443
459
|
try {
|
|
444
460
|
fs.accessSync(path.join(recDir, "PROMPT.md"));
|
|
445
461
|
fs.accessSync(path.join(recDir, "JUDGE.md"));
|
|
446
462
|
} catch (e) {
|
|
447
|
-
console.error("[loop-registry] Loop files missing for " +
|
|
463
|
+
console.error("[loop-registry] Loop files missing for " + loopId);
|
|
448
464
|
return;
|
|
449
465
|
}
|
|
450
466
|
// Set the loopId and start
|
|
451
|
-
loopState.loopId =
|
|
467
|
+
loopState.loopId = loopId;
|
|
452
468
|
activeRegistryId = record.id;
|
|
453
|
-
console.log("[loop-registry] Auto-starting loop: " + record.name);
|
|
469
|
+
console.log("[loop-registry] Auto-starting loop: " + record.name + " (" + loopId + ")");
|
|
454
470
|
send({ type: "schedule_run_started", recordId: record.id });
|
|
455
471
|
startLoop();
|
|
456
472
|
},
|
|
457
|
-
onChange: function (
|
|
458
|
-
send({ type: "loop_registry_updated", records:
|
|
473
|
+
onChange: function () {
|
|
474
|
+
send({ type: "loop_registry_updated", records: getHubSchedules() });
|
|
459
475
|
},
|
|
460
476
|
});
|
|
461
477
|
loopRegistry.load();
|
|
462
478
|
loopRegistry.startTimer();
|
|
463
479
|
|
|
480
|
+
// Wire loop info resolution for session list broadcasts
|
|
481
|
+
sm.setResolveLoopInfo(function (loopId) {
|
|
482
|
+
var rec = loopRegistry.getById(loopId);
|
|
483
|
+
if (!rec) return null;
|
|
484
|
+
return { name: rec.name || null, source: rec.source || null };
|
|
485
|
+
});
|
|
486
|
+
|
|
464
487
|
function startLoop(opts) {
|
|
465
488
|
var loopOpts = opts || {};
|
|
466
489
|
var dir = loopDir();
|
|
@@ -534,9 +557,12 @@ function createProjectContext(opts) {
|
|
|
534
557
|
}
|
|
535
558
|
|
|
536
559
|
var session = sm.createSession();
|
|
537
|
-
session.loop = { active: true, iteration: loopState.iteration, role: "coder" };
|
|
538
560
|
var loopName = (loopState.wizardData && loopState.wizardData.name) || "";
|
|
539
|
-
|
|
561
|
+
var loopSource = loopRegistry.getById(loopState.loopId);
|
|
562
|
+
var loopSourceTag = (loopSource && loopSource.source) || null;
|
|
563
|
+
var isRalphLoop = loopSourceTag === "ralph";
|
|
564
|
+
session.loop = { active: true, iteration: loopState.iteration, role: "coder", loopId: loopState.loopId, name: loopName, source: loopSourceTag, startedAt: loopState.startedAt };
|
|
565
|
+
session.title = (isRalphLoop ? "Ralph" : "Task") + (loopName ? " " + loopName : "") + " #" + loopState.iteration;
|
|
540
566
|
sm.saveSessionFile(session);
|
|
541
567
|
sm.broadcastSessionList();
|
|
542
568
|
|
|
@@ -621,9 +647,12 @@ function createProjectContext(opts) {
|
|
|
621
647
|
"Do NOT use any tools. Just analyze and respond.";
|
|
622
648
|
|
|
623
649
|
var judgeSession = sm.createSession();
|
|
624
|
-
judgeSession.loop = { active: true, iteration: loopState.iteration, role: "judge" };
|
|
625
650
|
var judgeName = (loopState.wizardData && loopState.wizardData.name) || "";
|
|
626
|
-
|
|
651
|
+
var judgeSource = loopRegistry.getById(loopState.loopId);
|
|
652
|
+
var judgeSourceTag = (judgeSource && judgeSource.source) || null;
|
|
653
|
+
var isRalphJudge = judgeSourceTag === "ralph";
|
|
654
|
+
judgeSession.loop = { active: true, iteration: loopState.iteration, role: "judge", loopId: loopState.loopId, name: judgeName, source: judgeSourceTag, startedAt: loopState.startedAt };
|
|
655
|
+
judgeSession.title = (isRalphJudge ? "Ralph" : "Task") + (judgeName ? " " + judgeName : "") + " Judge #" + loopState.iteration;
|
|
627
656
|
sm.saveSessionFile(judgeSession);
|
|
628
657
|
sm.broadcastSessionList();
|
|
629
658
|
loopState.judgeSessionId = judgeSession.localId;
|
|
@@ -841,7 +870,7 @@ function createProjectContext(opts) {
|
|
|
841
870
|
sendTo(ws, { type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
842
871
|
sendTo(ws, { type: "term_list", terminals: tm.list() });
|
|
843
872
|
sendTo(ws, { type: "notes_list", notes: nm.list() });
|
|
844
|
-
sendTo(ws, { type: "loop_registry_updated", records:
|
|
873
|
+
sendTo(ws, { type: "loop_registry_updated", records: getHubSchedules() });
|
|
845
874
|
|
|
846
875
|
// Ralph Loop availability
|
|
847
876
|
var hasLoopFiles = false;
|
|
@@ -885,7 +914,15 @@ function createProjectContext(opts) {
|
|
|
885
914
|
// Session list
|
|
886
915
|
sendTo(ws, {
|
|
887
916
|
type: "session_list",
|
|
888
|
-
sessions: [].concat(Array.from(sm.sessions.values())).map(function (s) {
|
|
917
|
+
sessions: [].concat(Array.from(sm.sessions.values())).filter(function (s) { return !s.hidden; }).map(function (s) {
|
|
918
|
+
var loop = s.loop ? Object.assign({}, s.loop) : null;
|
|
919
|
+
if (loop && loop.loopId && loopRegistry) {
|
|
920
|
+
var rec = loopRegistry.getById(loop.loopId);
|
|
921
|
+
if (rec) {
|
|
922
|
+
if (rec.name) loop.name = rec.name;
|
|
923
|
+
if (rec.source) loop.source = rec.source;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
889
926
|
return {
|
|
890
927
|
id: s.localId,
|
|
891
928
|
cliSessionId: s.cliSessionId || null,
|
|
@@ -893,6 +930,7 @@ function createProjectContext(opts) {
|
|
|
893
930
|
active: s.localId === sm.activeSessionId,
|
|
894
931
|
isProcessing: s.isProcessing,
|
|
895
932
|
lastActivity: s.lastActivity || s.createdAt || 0,
|
|
933
|
+
loop: loop,
|
|
896
934
|
};
|
|
897
935
|
}),
|
|
898
936
|
});
|
|
@@ -900,7 +938,7 @@ function createProjectContext(opts) {
|
|
|
900
938
|
// Restore active session for this client
|
|
901
939
|
var active = sm.getActiveSession();
|
|
902
940
|
if (active) {
|
|
903
|
-
sendTo(ws, { type: "session_switched", id: active.localId, cliSessionId: active.cliSessionId || null });
|
|
941
|
+
sendTo(ws, { type: "session_switched", id: active.localId, cliSessionId: active.cliSessionId || null, loop: active.loop || null });
|
|
904
942
|
|
|
905
943
|
var total = active.history.length;
|
|
906
944
|
var fromIndex = 0;
|
|
@@ -1545,6 +1583,18 @@ function createProjectContext(opts) {
|
|
|
1545
1583
|
return;
|
|
1546
1584
|
}
|
|
1547
1585
|
|
|
1586
|
+
// --- Pre-check: does the project have tasks/schedules? ---
|
|
1587
|
+
if (msg.type === "remove_project_check") {
|
|
1588
|
+
var checkSlug = msg.slug;
|
|
1589
|
+
if (!checkSlug) {
|
|
1590
|
+
sendTo(ws, { type: "remove_project_check_result", slug: checkSlug, name: msg.name || checkSlug, count: 0 });
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
var schedCount = getScheduleCount(checkSlug);
|
|
1594
|
+
sendTo(ws, { type: "remove_project_check_result", slug: checkSlug, name: msg.name || checkSlug, count: schedCount });
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1548
1598
|
// --- Remove project from web UI ---
|
|
1549
1599
|
if (msg.type === "remove_project") {
|
|
1550
1600
|
var removeSlug = msg.slug;
|
|
@@ -1552,6 +1602,10 @@ function createProjectContext(opts) {
|
|
|
1552
1602
|
sendTo(ws, { type: "remove_project_result", ok: false, error: "Missing slug" });
|
|
1553
1603
|
return;
|
|
1554
1604
|
}
|
|
1605
|
+
// If client chose to move tasks to another project before removing
|
|
1606
|
+
if (msg.moveTasksTo) {
|
|
1607
|
+
moveAllSchedulesToProject(removeSlug, msg.moveTasksTo);
|
|
1608
|
+
}
|
|
1555
1609
|
if (typeof opts.onRemoveProject === "function") {
|
|
1556
1610
|
var removeResult = opts.onRemoveProject(removeSlug);
|
|
1557
1611
|
sendTo(ws, { type: "remove_project_result", ok: removeResult.ok, slug: removeSlug, error: removeResult.error });
|
|
@@ -1561,6 +1615,17 @@ function createProjectContext(opts) {
|
|
|
1561
1615
|
return;
|
|
1562
1616
|
}
|
|
1563
1617
|
|
|
1618
|
+
// --- Move a single schedule to another project ---
|
|
1619
|
+
if (msg.type === "schedule_move") {
|
|
1620
|
+
var moveResult = moveScheduleToProject(msg.recordId, msg.fromSlug, msg.toSlug);
|
|
1621
|
+
if (moveResult.ok) {
|
|
1622
|
+
// Re-broadcast updated records to this project's clients
|
|
1623
|
+
send({ type: "loop_registry_updated", records: getHubSchedules() });
|
|
1624
|
+
}
|
|
1625
|
+
sendTo(ws, { type: "schedule_move_result", ok: moveResult.ok, error: moveResult.error });
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1564
1629
|
// --- Reorder projects ---
|
|
1565
1630
|
if (msg.type === "reorder_projects") {
|
|
1566
1631
|
var slugs = msg.slugs;
|
|
@@ -2110,7 +2175,7 @@ function createProjectContext(opts) {
|
|
|
2110
2175
|
|
|
2111
2176
|
if (msg.type === "ralph_wizard_complete") {
|
|
2112
2177
|
var wData = msg.data || {};
|
|
2113
|
-
var maxIter = wData.maxIterations ||
|
|
2178
|
+
var maxIter = wData.maxIterations || 3;
|
|
2114
2179
|
var wizardCron = wData.cron || null;
|
|
2115
2180
|
var newLoopId = generateLoopId();
|
|
2116
2181
|
loopState.loopId = newLoopId;
|
|
@@ -2125,6 +2190,7 @@ function createProjectContext(opts) {
|
|
|
2125
2190
|
saveLoopState();
|
|
2126
2191
|
|
|
2127
2192
|
// Register in loop registry
|
|
2193
|
+
var recordSource = wData.source === "task" ? null : "ralph";
|
|
2128
2194
|
loopRegistry.register({
|
|
2129
2195
|
id: newLoopId,
|
|
2130
2196
|
name: loopState.wizardData.name,
|
|
@@ -2132,6 +2198,7 @@ function createProjectContext(opts) {
|
|
|
2132
2198
|
cron: wizardCron,
|
|
2133
2199
|
enabled: wizardCron ? true : false,
|
|
2134
2200
|
maxIterations: maxIter,
|
|
2201
|
+
source: recordSource,
|
|
2135
2202
|
});
|
|
2136
2203
|
|
|
2137
2204
|
// Create loop directory and write LOOP.json
|
|
@@ -2153,9 +2220,10 @@ function createProjectContext(opts) {
|
|
|
2153
2220
|
// Create a new session for crafting
|
|
2154
2221
|
var craftingSession = sm.createSession();
|
|
2155
2222
|
var craftName = (loopState.wizardData && loopState.wizardData.name) || "";
|
|
2156
|
-
|
|
2223
|
+
var isRalphCraft = recordSource === "ralph";
|
|
2224
|
+
craftingSession.title = (isRalphCraft ? "Ralph" : "Task") + (craftName ? " " + craftName : "") + " Crafting";
|
|
2157
2225
|
craftingSession.ralphCraftingMode = true;
|
|
2158
|
-
craftingSession.
|
|
2226
|
+
craftingSession.loop = { active: true, iteration: 0, role: "crafting", loopId: newLoopId, name: craftName, source: recordSource };
|
|
2159
2227
|
sm.saveSessionFile(craftingSession);
|
|
2160
2228
|
sm.switchSession(craftingSession.localId);
|
|
2161
2229
|
loopState.craftingSessionId = craftingSession.localId;
|
|
@@ -2166,7 +2234,7 @@ function createProjectContext(opts) {
|
|
|
2166
2234
|
// Start .claude/ directory watcher
|
|
2167
2235
|
startClaudeDirWatch();
|
|
2168
2236
|
|
|
2169
|
-
//
|
|
2237
|
+
// Send crafting prompt and start the conversation with Claude.
|
|
2170
2238
|
craftingSession.history.push({ type: "user_message", text: craftingPrompt });
|
|
2171
2239
|
sm.appendToSessionFile(craftingSession, { type: "user_message", text: craftingPrompt });
|
|
2172
2240
|
send({ type: "user_message", text: craftingPrompt });
|
|
@@ -2176,7 +2244,7 @@ function createProjectContext(opts) {
|
|
|
2176
2244
|
send({ type: "status", status: "processing" });
|
|
2177
2245
|
sdk.startQuery(craftingSession, craftingPrompt);
|
|
2178
2246
|
|
|
2179
|
-
send({ type: "ralph_crafting_started", sessionId: craftingSession.localId, taskId: newLoopId });
|
|
2247
|
+
send({ type: "ralph_crafting_started", sessionId: craftingSession.localId, taskId: newLoopId, source: recordSource });
|
|
2180
2248
|
send({ type: "ralph_phase", phase: "crafting", wizardData: loopState.wizardData, craftingSessionId: craftingSession.localId });
|
|
2181
2249
|
return;
|
|
2182
2250
|
}
|
|
@@ -2244,9 +2312,36 @@ function createProjectContext(opts) {
|
|
|
2244
2312
|
return;
|
|
2245
2313
|
}
|
|
2246
2314
|
|
|
2315
|
+
// --- Schedule create (from calendar click) ---
|
|
2316
|
+
if (msg.type === "schedule_create") {
|
|
2317
|
+
var sData = msg.data || {};
|
|
2318
|
+
var newRec = loopRegistry.register({
|
|
2319
|
+
name: sData.name || "Untitled",
|
|
2320
|
+
task: sData.name || "",
|
|
2321
|
+
description: sData.description || "",
|
|
2322
|
+
date: sData.date || null,
|
|
2323
|
+
time: sData.time || null,
|
|
2324
|
+
allDay: sData.allDay !== undefined ? sData.allDay : true,
|
|
2325
|
+
linkedTaskId: sData.taskId || null,
|
|
2326
|
+
cron: sData.cron || null,
|
|
2327
|
+
enabled: sData.cron ? (sData.enabled !== false) : false,
|
|
2328
|
+
maxIterations: sData.maxIterations || 3,
|
|
2329
|
+
source: "schedule",
|
|
2330
|
+
color: sData.color || null,
|
|
2331
|
+
recurrenceEnd: sData.recurrenceEnd || null,
|
|
2332
|
+
});
|
|
2333
|
+
return;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
// --- Hub: cross-project schedule aggregation ---
|
|
2337
|
+
if (msg.type === "hub_schedules_list") {
|
|
2338
|
+
sendTo(ws, { type: "hub_schedules", schedules: getHubSchedules() });
|
|
2339
|
+
return;
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2247
2342
|
// --- Loop Registry messages ---
|
|
2248
2343
|
if (msg.type === "loop_registry_list") {
|
|
2249
|
-
sendTo(ws, { type: "loop_registry_updated", records:
|
|
2344
|
+
sendTo(ws, { type: "loop_registry_updated", records: getHubSchedules() });
|
|
2250
2345
|
return;
|
|
2251
2346
|
}
|
|
2252
2347
|
|
|
@@ -2258,6 +2353,14 @@ function createProjectContext(opts) {
|
|
|
2258
2353
|
return;
|
|
2259
2354
|
}
|
|
2260
2355
|
|
|
2356
|
+
if (msg.type === "loop_registry_rename") {
|
|
2357
|
+
if (msg.id && msg.name) {
|
|
2358
|
+
loopRegistry.updateRecord(msg.id, { name: String(msg.name).substring(0, 100) });
|
|
2359
|
+
sm.broadcastSessionList();
|
|
2360
|
+
}
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2261
2364
|
if (msg.type === "loop_registry_remove") {
|
|
2262
2365
|
var removedRec = loopRegistry.remove(msg.id);
|
|
2263
2366
|
if (!removedRec) {
|
|
@@ -2266,6 +2369,31 @@ function createProjectContext(opts) {
|
|
|
2266
2369
|
return;
|
|
2267
2370
|
}
|
|
2268
2371
|
|
|
2372
|
+
if (msg.type === "loop_registry_convert") {
|
|
2373
|
+
// Convert ralph source to regular task (remove source tag)
|
|
2374
|
+
if (msg.id) {
|
|
2375
|
+
loopRegistry.updateRecord(msg.id, { source: null });
|
|
2376
|
+
sm.broadcastSessionList();
|
|
2377
|
+
}
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
if (msg.type === "delete_loop_group") {
|
|
2382
|
+
// Delete all sessions belonging to this loopId, then remove registry record
|
|
2383
|
+
var loopIdToDel = msg.loopId;
|
|
2384
|
+
if (!loopIdToDel) return;
|
|
2385
|
+
var sessionIds = [];
|
|
2386
|
+
sm.sessions.forEach(function (s, lid) {
|
|
2387
|
+
if (s.loop && s.loop.loopId === loopIdToDel) sessionIds.push(lid);
|
|
2388
|
+
});
|
|
2389
|
+
for (var di = 0; di < sessionIds.length; di++) {
|
|
2390
|
+
sm.deleteSessionQuiet(sessionIds[di]);
|
|
2391
|
+
}
|
|
2392
|
+
loopRegistry.remove(loopIdToDel);
|
|
2393
|
+
sm.broadcastSessionList();
|
|
2394
|
+
return;
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2269
2397
|
if (msg.type === "loop_registry_toggle") {
|
|
2270
2398
|
var toggledRec = loopRegistry.toggleEnabled(msg.id);
|
|
2271
2399
|
if (!toggledRec) {
|
|
@@ -2730,9 +2858,37 @@ function createProjectContext(opts) {
|
|
|
2730
2858
|
handleDisconnection: handleDisconnection,
|
|
2731
2859
|
handleHTTP: handleHTTP,
|
|
2732
2860
|
getStatus: getStatus,
|
|
2861
|
+
getSchedules: function () { return loopRegistry.getAll(); },
|
|
2862
|
+
importSchedule: function (data) { return loopRegistry.register(data); },
|
|
2863
|
+
removeSchedule: function (id) { return loopRegistry.remove(id); },
|
|
2733
2864
|
setTitle: setTitle,
|
|
2734
2865
|
setIcon: setIcon,
|
|
2735
|
-
warmup: function () {
|
|
2866
|
+
warmup: function () {
|
|
2867
|
+
sdk.warmup();
|
|
2868
|
+
// Auto-install clay-ralph skill globally if not present
|
|
2869
|
+
var clayRalphDir = path.join(os.homedir(), ".claude", "skills", "clay-ralph", "SKILL.md");
|
|
2870
|
+
try {
|
|
2871
|
+
fs.accessSync(clayRalphDir, fs.constants.R_OK);
|
|
2872
|
+
} catch (e) {
|
|
2873
|
+
console.log("[project] Auto-installing clay-ralph skill...");
|
|
2874
|
+
var child = spawn("npx", ["skills", "add", "https://github.com/chadbyte/clay-ralph", "--skill", "clay-ralph"], {
|
|
2875
|
+
cwd: os.homedir(),
|
|
2876
|
+
stdio: "ignore",
|
|
2877
|
+
detached: false,
|
|
2878
|
+
});
|
|
2879
|
+
child.on("close", function (code) {
|
|
2880
|
+
if (code === 0) {
|
|
2881
|
+
console.log("[project] clay-ralph skill installed successfully");
|
|
2882
|
+
send({ type: "skill_installed", skill: "clay-ralph", scope: "global", success: true, error: null });
|
|
2883
|
+
} else {
|
|
2884
|
+
console.log("[project] clay-ralph skill install failed (code " + code + ")");
|
|
2885
|
+
}
|
|
2886
|
+
});
|
|
2887
|
+
child.on("error", function (err) {
|
|
2888
|
+
console.log("[project] clay-ralph skill install error: " + err.message);
|
|
2889
|
+
});
|
|
2890
|
+
}
|
|
2891
|
+
},
|
|
2736
2892
|
destroy: destroy,
|
|
2737
2893
|
};
|
|
2738
2894
|
}
|