clay-server 2.7.1 → 2.7.2

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/bin/cli.js CHANGED
@@ -21,9 +21,8 @@ var { execSync, execFileSync, spawn } = require("child_process");
21
21
  var qrcode = require("qrcode-terminal");
22
22
  var net = require("net");
23
23
 
24
- // Detect dev mode — dev and prod use separate daemon files so they can run simultaneously
24
+ // Detect dev mode (no separate storage — dev and prod share ~/.clay)
25
25
  var _isDev = (process.argv[1] && path.basename(process.argv[1]) === "clay-dev") || process.argv.includes("--dev");
26
- if (_isDev) process.env.CLAY_DEV = "1";
27
26
 
28
27
  var { loadConfig, saveConfig, configPath, socketPath, logPath, ensureConfigDir, isDaemonAlive, isDaemonAliveAsync, generateSlug, clearStaleConfig, loadClayrc, saveClayrc, readCrashInfo } = require("../lib/config");
29
28
  var { sendIPCCommand } = require("../lib/ipc");
package/lib/config.js CHANGED
@@ -70,23 +70,20 @@ var CONFIG_DIR = CLAY_HOME;
70
70
  var CLAYRC_PATH = path.join(os.homedir(), ".clayrc");
71
71
  var CRASH_INFO_PATH = path.join(CONFIG_DIR, "crash.json");
72
72
 
73
- // Dev mode uses separate daemon files so dev and prod can run simultaneously
74
- var _devMode = !!process.env.CLAY_DEV;
75
-
76
73
  function configPath() {
77
- return path.join(CONFIG_DIR, _devMode ? "daemon-dev.json" : "daemon.json");
74
+ return path.join(CONFIG_DIR, "daemon.json");
78
75
  }
79
76
 
80
77
  function socketPath() {
81
78
  if (process.platform === "win32") {
82
- var pipeName = _devMode ? "clay-daemon-dev" : "clay-daemon";
79
+ var pipeName = "clay-daemon";
83
80
  return "\\\\.\\pipe\\" + pipeName;
84
81
  }
85
- return path.join(CONFIG_DIR, _devMode ? "daemon-dev.sock" : "daemon.sock");
82
+ return path.join(CONFIG_DIR, "daemon.sock");
86
83
  }
87
84
 
88
85
  function logPath() {
89
- return path.join(CONFIG_DIR, _devMode ? "daemon-dev.log" : "daemon.log");
86
+ return path.join(CONFIG_DIR, "daemon.log");
90
87
  }
91
88
 
92
89
  function ensureConfigDir() {
package/lib/project.js CHANGED
@@ -8,7 +8,6 @@ var { createTerminalManager } = require("./terminal-manager");
8
8
  var { createNotesManager } = require("./notes");
9
9
  var { fetchLatestVersion, isNewer } = require("./updater");
10
10
  var { execFileSync, spawn } = require("child_process");
11
- var { createLoopRegistry } = require("./scheduler");
12
11
 
13
12
  var MAX_UPLOAD_BYTES = 50 * 1024 * 1024; // 50 MB
14
13
 
@@ -396,71 +395,17 @@ function createProjectContext(opts) {
396
395
  judgeReady: hasJudge,
397
396
  loopJsonReady: hasLoopJson,
398
397
  bothReady: hasPrompt && hasJudge,
399
- taskId: loopState.loopId,
400
398
  });
401
399
  // Auto-transition to approval phase when both files appear
402
400
  if (hasPrompt && hasJudge && loopState.phase === "crafting") {
403
401
  loopState.phase = "approval";
404
402
  saveLoopState();
405
-
406
- // Parse recommended title from crafting session conversation
407
- if (loopState.craftingSessionId && loopState.loopId) {
408
- var craftSess = sm.sessions.get(loopState.craftingSessionId);
409
- if (craftSess && craftSess.history) {
410
- for (var hi = craftSess.history.length - 1; hi >= 0; hi--) {
411
- var entry = craftSess.history[hi];
412
- var entryText = entry.text || "";
413
- var titleMatch = entryText.match(/\[\[LOOP_TITLE:\s*(.+?)\]\]/);
414
- if (titleMatch) {
415
- var suggestedTitle = titleMatch[1].trim();
416
- if (suggestedTitle) {
417
- loopRegistry.updateRecord(loopState.loopId, { name: suggestedTitle });
418
- }
419
- break;
420
- }
421
- }
422
- }
423
- }
424
403
  }
425
404
  }
426
405
 
427
406
  // Load persisted state on startup
428
407
  loadLoopState();
429
408
 
430
- // --- Loop Registry (unified one-off + scheduled) ---
431
- var activeRegistryId = null; // track which registry record triggered current loop
432
-
433
- var loopRegistry = createLoopRegistry({
434
- cwd: cwd,
435
- onTrigger: function (record) {
436
- // Only trigger if no loop is currently active
437
- if (loopState.active || loopState.phase === "executing") {
438
- console.log("[loop-registry] Skipping trigger — loop already active");
439
- return;
440
- }
441
- // Verify the loop directory and files exist
442
- var recDir = path.join(cwd, ".claude", "loops", record.id);
443
- try {
444
- fs.accessSync(path.join(recDir, "PROMPT.md"));
445
- fs.accessSync(path.join(recDir, "JUDGE.md"));
446
- } catch (e) {
447
- console.error("[loop-registry] Loop files missing for " + record.id);
448
- return;
449
- }
450
- // Set the loopId and start
451
- loopState.loopId = record.id;
452
- activeRegistryId = record.id;
453
- console.log("[loop-registry] Auto-starting loop: " + record.name);
454
- send({ type: "schedule_run_started", recordId: record.id });
455
- startLoop();
456
- },
457
- onChange: function (records) {
458
- send({ type: "loop_registry_updated", records: records });
459
- },
460
- });
461
- loopRegistry.load();
462
- loopRegistry.startTimer();
463
-
464
409
  function startLoop(opts) {
465
410
  var loopOpts = opts || {};
466
411
  var dir = loopDir();
@@ -709,19 +654,6 @@ function createProjectContext(opts) {
709
654
  results: loopState.results,
710
655
  });
711
656
 
712
- // Record result in loop registry
713
- if (loopState.loopId) {
714
- loopRegistry.recordRun(loopState.loopId, {
715
- reason: reason,
716
- startedAt: loopState.startedAt,
717
- iterations: loopState.iteration,
718
- });
719
- }
720
- if (activeRegistryId) {
721
- send({ type: "schedule_run_finished", recordId: activeRegistryId, reason: reason, iterations: loopState.iteration });
722
- activeRegistryId = null;
723
- }
724
-
725
657
  if (pushModule) {
726
658
  var body = reason === "pass"
727
659
  ? "Task completed after " + loopState.iteration + " iteration(s)"
@@ -841,7 +773,6 @@ function createProjectContext(opts) {
841
773
  sendTo(ws, { type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [] });
842
774
  sendTo(ws, { type: "term_list", terminals: tm.list() });
843
775
  sendTo(ws, { type: "notes_list", notes: nm.list() });
844
- sendTo(ws, { type: "loop_registry_updated", records: loopRegistry.getAll() });
845
776
 
846
777
  // Ralph Loop availability
847
778
  var hasLoopFiles = false;
@@ -878,7 +809,6 @@ function createProjectContext(opts) {
878
809
  promptReady: _hasPrompt,
879
810
  judgeReady: _hasJudge,
880
811
  bothReady: _hasPrompt && _hasJudge,
881
- taskId: loopState.loopId,
882
812
  });
883
813
  }
884
814
 
@@ -2089,16 +2019,6 @@ function createProjectContext(opts) {
2089
2019
  }
2090
2020
 
2091
2021
  if (msg.type === "loop_start") {
2092
- // If this loop has a cron schedule, don't run immediately — just confirm registration
2093
- if (loopState.wizardData && loopState.wizardData.cron) {
2094
- loopState.active = false;
2095
- loopState.phase = "done";
2096
- saveLoopState();
2097
- send({ type: "loop_finished", reason: "scheduled", iterations: 0, results: [] });
2098
- send({ type: "ralph_phase", phase: "idle", wizardData: null });
2099
- send({ type: "loop_scheduled", recordId: loopState.loopId, cron: loopState.wizardData.cron });
2100
- return;
2101
- }
2102
2022
  startLoop();
2103
2023
  return;
2104
2024
  }
@@ -2111,29 +2031,17 @@ function createProjectContext(opts) {
2111
2031
  if (msg.type === "ralph_wizard_complete") {
2112
2032
  var wData = msg.data || {};
2113
2033
  var maxIter = wData.maxIterations || 25;
2114
- var wizardCron = wData.cron || null;
2115
2034
  var newLoopId = generateLoopId();
2116
2035
  loopState.loopId = newLoopId;
2117
2036
  loopState.wizardData = {
2118
- name: wData.name || wData.task || "Untitled",
2037
+ name: (wData.name || "").replace(/[^a-zA-Z0-9_-]/g, "") || "ralph",
2119
2038
  task: wData.task || "",
2120
2039
  maxIterations: maxIter,
2121
- cron: wizardCron,
2122
2040
  };
2123
2041
  loopState.phase = "crafting";
2124
2042
  loopState.startedAt = Date.now();
2125
2043
  saveLoopState();
2126
2044
 
2127
- // Register in loop registry
2128
- loopRegistry.register({
2129
- id: newLoopId,
2130
- name: loopState.wizardData.name,
2131
- task: wData.task || "",
2132
- cron: wizardCron,
2133
- enabled: wizardCron ? true : false,
2134
- maxIterations: maxIter,
2135
- });
2136
-
2137
2045
  // Create loop directory and write LOOP.json
2138
2046
  var lDir = loopDir();
2139
2047
  try { fs.mkdirSync(lDir, { recursive: true }); } catch (e) {}
@@ -2143,26 +2051,19 @@ function createProjectContext(opts) {
2143
2051
  fs.renameSync(tmpLoopJson, loopJsonPath);
2144
2052
 
2145
2053
  // Assemble prompt for clay-ralph skill (include loop dir path so skill knows where to write)
2146
- var craftingPrompt = "Use the /clay-ralph skill to design a Ralph Loop for the following task. " +
2147
- "You MUST invoke the clay-ralph skill — do NOT execute the task yourself. " +
2148
- "Your job is to interview me, then create PROMPT.md and JUDGE.md files " +
2149
- "that a future autonomous session will execute.\n\n" +
2150
- "## Task\n" + (wData.task || "") +
2151
- "\n\n## Loop Directory\n" + lDir;
2054
+ var craftingPrompt = "/clay-ralph\n## Task\n" + (wData.task || "") +
2055
+ "\n## Loop Directory\n" + lDir;
2152
2056
 
2153
2057
  // Create a new session for crafting
2154
2058
  var craftingSession = sm.createSession();
2155
2059
  var craftName = (loopState.wizardData && loopState.wizardData.name) || "";
2156
2060
  craftingSession.title = "Ralph" + (craftName ? " " + craftName : "") + " Crafting";
2157
2061
  craftingSession.ralphCraftingMode = true;
2158
- craftingSession.hidden = true;
2159
2062
  sm.saveSessionFile(craftingSession);
2160
2063
  sm.switchSession(craftingSession.localId);
2064
+ sm.broadcastSessionList();
2161
2065
  loopState.craftingSessionId = craftingSession.localId;
2162
2066
 
2163
- // Store crafting session ID in the registry record
2164
- loopRegistry.updateRecord(newLoopId, { craftingSessionId: craftingSession.localId });
2165
-
2166
2067
  // Start .claude/ directory watcher
2167
2068
  startClaudeDirWatch();
2168
2069
 
@@ -2176,27 +2077,11 @@ function createProjectContext(opts) {
2176
2077
  send({ type: "status", status: "processing" });
2177
2078
  sdk.startQuery(craftingSession, craftingPrompt);
2178
2079
 
2179
- send({ type: "ralph_crafting_started", sessionId: craftingSession.localId, taskId: newLoopId });
2080
+ send({ type: "ralph_crafting_started", sessionId: craftingSession.localId });
2180
2081
  send({ type: "ralph_phase", phase: "crafting", wizardData: loopState.wizardData, craftingSessionId: craftingSession.localId });
2181
2082
  return;
2182
2083
  }
2183
2084
 
2184
- if (msg.type === "loop_registry_files") {
2185
- var recId = msg.id;
2186
- var lDir = path.join(cwd, ".claude", "loops", recId);
2187
- var promptContent = "";
2188
- var judgeContent = "";
2189
- try { promptContent = fs.readFileSync(path.join(lDir, "PROMPT.md"), "utf8"); } catch (e) {}
2190
- try { judgeContent = fs.readFileSync(path.join(lDir, "JUDGE.md"), "utf8"); } catch (e) {}
2191
- send({
2192
- type: "loop_registry_files_content",
2193
- id: recId,
2194
- prompt: promptContent,
2195
- judge: judgeContent,
2196
- });
2197
- return;
2198
- }
2199
-
2200
2085
  if (msg.type === "ralph_preview_files") {
2201
2086
  var promptContent = "";
2202
2087
  var judgeContent = "";
@@ -2244,62 +2129,6 @@ function createProjectContext(opts) {
2244
2129
  return;
2245
2130
  }
2246
2131
 
2247
- // --- Loop Registry messages ---
2248
- if (msg.type === "loop_registry_list") {
2249
- sendTo(ws, { type: "loop_registry_updated", records: loopRegistry.getAll() });
2250
- return;
2251
- }
2252
-
2253
- if (msg.type === "loop_registry_update") {
2254
- var updatedRec = loopRegistry.update(msg.id, msg.data || {});
2255
- if (!updatedRec) {
2256
- sendTo(ws, { type: "loop_registry_error", text: "Record not found" });
2257
- }
2258
- return;
2259
- }
2260
-
2261
- if (msg.type === "loop_registry_remove") {
2262
- var removedRec = loopRegistry.remove(msg.id);
2263
- if (!removedRec) {
2264
- sendTo(ws, { type: "loop_registry_error", text: "Record not found" });
2265
- }
2266
- return;
2267
- }
2268
-
2269
- if (msg.type === "loop_registry_toggle") {
2270
- var toggledRec = loopRegistry.toggleEnabled(msg.id);
2271
- if (!toggledRec) {
2272
- sendTo(ws, { type: "loop_registry_error", text: "Record not found or not scheduled" });
2273
- }
2274
- return;
2275
- }
2276
-
2277
- if (msg.type === "loop_registry_rerun") {
2278
- // Re-run an existing job (one-off from library)
2279
- if (loopState.active || loopState.phase === "executing") {
2280
- sendTo(ws, { type: "loop_registry_error", text: "A loop is already running" });
2281
- return;
2282
- }
2283
- var rerunRec = loopRegistry.getById(msg.id);
2284
- if (!rerunRec) {
2285
- sendTo(ws, { type: "loop_registry_error", text: "Record not found" });
2286
- return;
2287
- }
2288
- var rerunDir = path.join(cwd, ".claude", "loops", rerunRec.id);
2289
- try {
2290
- fs.accessSync(path.join(rerunDir, "PROMPT.md"));
2291
- fs.accessSync(path.join(rerunDir, "JUDGE.md"));
2292
- } catch (e) {
2293
- sendTo(ws, { type: "loop_registry_error", text: "Loop files missing for " + rerunRec.id });
2294
- return;
2295
- }
2296
- loopState.loopId = rerunRec.id;
2297
- activeRegistryId = null; // not a scheduled trigger
2298
- send({ type: "loop_rerun_started", recordId: rerunRec.id });
2299
- startLoop();
2300
- return;
2301
- }
2302
-
2303
2132
  if (msg.type !== "message") return;
2304
2133
  if (!msg.text && (!msg.images || msg.images.length === 0) && (!msg.pastes || msg.pastes.length === 0)) return;
2305
2134
 
@@ -2662,7 +2491,6 @@ function createProjectContext(opts) {
2662
2491
 
2663
2492
  // --- Destroy ---
2664
2493
  function destroy() {
2665
- loopRegistry.stopTimer();
2666
2494
  stopFileWatch();
2667
2495
  stopAllDirWatches();
2668
2496
  // Abort all active sessions