clay-server 4.1.0-beta.5 → 4.1.0-beta.7

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 (155) hide show
  1. package/LICENSE +661 -21
  2. package/LICENSES/MIT-legacy.txt +21 -0
  3. package/LICENSES/README.md +7 -0
  4. package/README.md +2 -2
  5. package/bin/cli.js +0 -2
  6. package/lib/autonomous-run-record.js +32 -0
  7. package/lib/autonomous-run-scheduler.js +92 -0
  8. package/lib/daemon.js +0 -2
  9. package/lib/default-ai-runtime.js +117 -0
  10. package/lib/durable-scheduler-record.js +61 -0
  11. package/lib/durable-scheduler-store.js +185 -0
  12. package/lib/durable-scheduler.js +495 -0
  13. package/lib/git-cli.js +1 -0
  14. package/lib/issue-work-session.js +10 -0
  15. package/lib/issues-comments.js +40 -0
  16. package/lib/issues-commit-verification.js +30 -0
  17. package/lib/issues-mcp-server.js +27 -0
  18. package/lib/issues-schema.js +119 -0
  19. package/lib/issues-service.js +339 -0
  20. package/lib/issues-store.js +210 -0
  21. package/lib/loop-cron.js +47 -0
  22. package/lib/loop-durable-schedule.js +159 -0
  23. package/lib/loop-guidance.js +35 -0
  24. package/lib/loop-registry-occurrence.js +76 -0
  25. package/lib/model-entry.js +15 -0
  26. package/lib/project-autonomous-run.js +498 -0
  27. package/lib/project-capsule-turn.js +1 -0
  28. package/lib/project-connection.js +6 -1
  29. package/lib/project-delegated-session.js +1 -0
  30. package/lib/project-filesystem.js +10 -10
  31. package/lib/project-full-access.js +86 -0
  32. package/lib/project-http.js +3 -1
  33. package/lib/project-issue-launch.js +154 -0
  34. package/lib/project-issues.js +58 -0
  35. package/lib/project-logs-mcp-server.js +8 -12
  36. package/lib/project-loop-interview.js +163 -0
  37. package/lib/project-loop.js +150 -64
  38. package/lib/project-message-delivery.js +4 -2
  39. package/lib/project-models.js +71 -3
  40. package/lib/project-pair-autonomous-stop.js +16 -0
  41. package/lib/project-pair-owned-stop.js +18 -0
  42. package/lib/project-pair-task-control.js +14 -1
  43. package/lib/project-pending-message-queue.js +264 -0
  44. package/lib/project-scheduled-messages.js +316 -0
  45. package/lib/project-scheduled-task-execution.js +236 -0
  46. package/lib/project-scheduled-tasks.js +481 -0
  47. package/lib/project-session-pair.js +16 -19
  48. package/lib/project-sessions.js +114 -31
  49. package/lib/project-tool-llm-runtime.js +48 -0
  50. package/lib/project-user-message.js +202 -60
  51. package/lib/project-workspace-query.js +25 -0
  52. package/lib/project.js +361 -69
  53. package/lib/public/app.js +85 -22
  54. package/lib/public/css/default-ai.css +33 -0
  55. package/lib/public/css/home-hub.css +15 -2
  56. package/lib/public/css/home-sidebar.css +50 -2
  57. package/lib/public/css/input.css +160 -5
  58. package/lib/public/css/issues.css +101 -0
  59. package/lib/public/css/messages.css +19 -0
  60. package/lib/public/css/pane.css +58 -7
  61. package/lib/public/css/pending-message-queue.css +187 -0
  62. package/lib/public/css/rewind.css +11 -2
  63. package/lib/public/css/scheduled-tasks.css +122 -0
  64. package/lib/public/index.html +42 -150
  65. package/lib/public/modules/app-connection.js +3 -0
  66. package/lib/public/modules/app-cursors.js +29 -12
  67. package/lib/public/modules/app-loop-ui.js +4 -8
  68. package/lib/public/modules/app-message-router.js +8 -0
  69. package/lib/public/modules/app-messages.js +34 -9
  70. package/lib/public/modules/app-projects.js +2 -0
  71. package/lib/public/modules/app-rate-limit.js +8 -5
  72. package/lib/public/modules/autonomous-run.js +285 -0
  73. package/lib/public/modules/clay-file-links.js +111 -0
  74. package/lib/public/modules/clay-issue-links.js +131 -0
  75. package/lib/public/modules/clay-log-links.js +2 -0
  76. package/lib/public/modules/cursor-sharing-preference.js +38 -0
  77. package/lib/public/modules/default-ai.js +377 -0
  78. package/lib/public/modules/filebrowser.js +43 -2
  79. package/lib/public/modules/home-sidebar.js +16 -8
  80. package/lib/public/modules/home-surface.js +5 -0
  81. package/lib/public/modules/input.js +11 -20
  82. package/lib/public/modules/issues-context.js +117 -0
  83. package/lib/public/modules/issues-render.js +29 -0
  84. package/lib/public/modules/issues.js +270 -0
  85. package/lib/public/modules/loop-interview.js +151 -0
  86. package/lib/public/modules/markdown.js +4 -0
  87. package/lib/public/modules/message-delivery.js +5 -3
  88. package/lib/public/modules/pending-message-image-editor.js +143 -0
  89. package/lib/public/modules/pending-message-queue-model.js +22 -0
  90. package/lib/public/modules/pending-message-queue.js +449 -0
  91. package/lib/public/modules/project-logs.js +2 -2
  92. package/lib/public/modules/scheduled-message-state.js +46 -0
  93. package/lib/public/modules/scheduled-task-edit-state.js +7 -0
  94. package/lib/public/modules/scheduled-task-engine.js +48 -0
  95. package/lib/public/modules/scheduled-task-form.js +122 -0
  96. package/lib/public/modules/scheduled-task-recurrence.js +46 -0
  97. package/lib/public/modules/scheduled-task-schedule-rule.js +115 -0
  98. package/lib/public/modules/scheduled-tasks.js +459 -0
  99. package/lib/public/modules/scheduler-config.js +6 -52
  100. package/lib/public/modules/scheduler.js +9 -21
  101. package/lib/public/modules/sidebar-mobile.js +3 -0
  102. package/lib/public/modules/sidebar.js +0 -11
  103. package/lib/public/modules/split-view.js +2 -0
  104. package/lib/public/modules/sticky-note-markdown.js +1 -0
  105. package/lib/public/modules/terminal.js +2 -0
  106. package/lib/public/modules/tool-palette-order.js +3 -3
  107. package/lib/public/modules/tools.js +5 -2
  108. package/lib/public/style.css +3 -0
  109. package/lib/record-revision.js +11 -0
  110. package/lib/schedule-rule.js +151 -0
  111. package/lib/schedule-validation.js +124 -0
  112. package/lib/scheduled-run-state.js +87 -0
  113. package/lib/scheduled-task-records.js +127 -0
  114. package/lib/scheduler.js +179 -196
  115. package/lib/sdk-bridge.js +157 -22
  116. package/lib/sdk-message-processor.js +3 -0
  117. package/lib/server-cursor-sharing.js +65 -0
  118. package/lib/server-default-ai.js +265 -0
  119. package/lib/server-durable-scheduler.js +26 -0
  120. package/lib/server-home-chat.js +29 -9
  121. package/lib/server-home-clay-entry.js +4 -1
  122. package/lib/server-home-debate-planning.js +32 -14
  123. package/lib/server-home-mate-creation.js +30 -11
  124. package/lib/server-home-models.js +14 -6
  125. package/lib/server-issue-links.js +18 -0
  126. package/lib/server.js +63 -0
  127. package/lib/session-notes-mcp-server.js +1 -1
  128. package/lib/session-pair-factory.js +42 -4
  129. package/lib/session-pair-query-start.js +22 -0
  130. package/lib/session-query-tool-bridge.js +52 -0
  131. package/lib/session-search.js +4 -1
  132. package/lib/session-split-groups.js +35 -1
  133. package/lib/sessions.js +51 -3
  134. package/lib/tool-llm.js +1 -0
  135. package/lib/users-default-ai-preferences.js +80 -0
  136. package/lib/users-home-surface-preferences.js +2 -1
  137. package/lib/users-preferences.js +35 -0
  138. package/lib/users-scheduled-task-preferences.js +44 -0
  139. package/lib/users.js +16 -0
  140. package/lib/workspace-query-projection.js +29 -0
  141. package/lib/workspace-query-service.js +19 -29
  142. package/lib/ws-schema.js +61 -1
  143. package/lib/yoke/adapters/antigravity.js +1 -0
  144. package/lib/yoke/adapters/claude-worker.js +11 -6
  145. package/lib/yoke/adapters/claude.js +35 -1
  146. package/lib/yoke/adapters/codex.js +3 -0
  147. package/lib/yoke/claude-user-input.js +2 -0
  148. package/lib/yoke/index.js +2 -0
  149. package/lib/yoke/mcp-bridge-server.js +7 -1
  150. package/lib/yoke/package.json +1 -1
  151. package/lib/yoke/session-tool-transport.js +53 -0
  152. package/lib/yoke/user-input.js +21 -7
  153. package/package.json +5 -6
  154. package/bin/claude-relay.js +0 -6
  155. package/lib/public/modules/app-loop-wizard.js +0 -439
@@ -0,0 +1,53 @@
1
+ // Shared production transport facts for query-bound Clay tools.
2
+
3
+ var vendorRegistry = require("./vendor-registry");
4
+ var CODEX_CATALOG_VERSION = 5;
5
+ var SCHEDULE_TOOLS = ["begin_scheduled_task_interview", "propose_scheduled_task", "list_scheduled_tasks", "read_scheduled_task", "update_scheduled_task", "report_scheduled_task_outcome"];
6
+
7
+ function capability(vendor) {
8
+ if (vendor === "claude") return { supported: true, kind: "sdk-mcp" };
9
+ if (vendor === "codex") return { supported: true, kind: "dynamic" };
10
+ var info = vendorRegistry.getVendorInfo(vendor);
11
+ if (info && info.sessionBoundTools === true) return { supported: true, kind: "stdio-mcp" };
12
+ return { supported: false, kind: "none" };
13
+ }
14
+
15
+ function availableForSession(session) {
16
+ var vendor = session && (session.vendor || "claude");
17
+ var transport = capability(vendor);
18
+ if (!transport.supported) return { available: false, reason: "This provider's current Clay adapter does not deliver custom session tools." };
19
+ if (vendor === "codex" && session.cliSessionId && Number(session.codexDynamicToolCatalogVersion || 0) < CODEX_CATALOG_VERSION) {
20
+ return { available: false, reason: "This Codex conversation predates Clay's scheduled-task tool catalog. Start a new conversation to use the assisted interview; manual task creation remains available here." };
21
+ }
22
+ return { available: true, reason: null };
23
+ }
24
+
25
+ function scheduleToolName(vendor, name) {
26
+ var kind = capability(vendor).kind;
27
+ if (kind === "sdk-mcp") return "mcp__clay-scheduled-tasks__" + name;
28
+ if (kind === "stdio-mcp") return "clay-scheduled-tasks__" + name;
29
+ return name;
30
+ }
31
+
32
+ function questionToolName(vendor) {
33
+ var kind = capability(vendor).kind;
34
+ if (kind === "sdk-mcp") return "mcp__yoke-user-input__ask_user_questions";
35
+ if (kind === "stdio-mcp") return "yoke-user-input__ask_user_questions";
36
+ return "ask_user_questions";
37
+ }
38
+
39
+ function bridgeServerForTool(name) {
40
+ if (name === "ask_user_questions") return "yoke-user-input";
41
+ if (SCHEDULE_TOOLS.indexOf(name) !== -1) return "clay-scheduled-tasks";
42
+ return null;
43
+ }
44
+
45
+ module.exports = {
46
+ capability: capability,
47
+ availableForSession: availableForSession,
48
+ CODEX_CATALOG_VERSION: CODEX_CATALOG_VERSION,
49
+ scheduleToolName: scheduleToolName,
50
+ questionToolName: questionToolName,
51
+ bridgeServerForTool: bridgeServerForTool,
52
+ SCHEDULE_TOOLS: SCHEDULE_TOOLS,
53
+ };
@@ -195,24 +195,38 @@ function elicitationResponse(result) {
195
195
  return { action: "accept", content: content };
196
196
  }
197
197
 
198
- function buildQuestionShape() {
198
+ function buildQuestionShape(maxQuestions) {
199
199
  if (!z) return {};
200
200
  var option = z.object({ label: z.string().min(1).max(60), description: z.string().max(160).optional() }).passthrough();
201
201
  var question = z.object({
202
202
  id: z.string().max(128).optional(), header: z.string().max(40).optional(), question: z.string().min(1),
203
203
  multiSelect: z.boolean().optional(), options: z.array(option).max(6).refine(function (items) { return items.length === 0 || items.length >= 2; }),
204
204
  }).passthrough();
205
- return { questions: z.array(question).min(1).max(3) };
205
+ return { questions: z.array(question).min(1).max(maxQuestions || 3) };
206
206
  }
207
207
 
208
- function fallbackToolDefs(handler) {
208
+ function questionLimit(options) {
209
+ var value = options && options.maxQuestions;
210
+ if (typeof value === "function") value = value();
211
+ return value === 1 ? 1 : null;
212
+ }
213
+
214
+ function fallbackToolDefs(handler, options) {
215
+ options = options || {};
216
+ var initialLimit = questionLimit(options);
209
217
  return [{
210
218
  name: TOOL_NAME,
211
- description: "Ask the user 1-3 structured questions. Use zero options for freeform or 2-6 options for choices; never one option.",
212
- inputSchema: buildQuestionShape(),
219
+ queryBound: true,
220
+ description: initialLimit === 1
221
+ ? "Ask the user exactly one structured question, wait for the answer, then call again if another question is needed. Use zero options for freeform or 2-6 options for choices; never one option."
222
+ : "Ask the user 1-3 structured questions. Use zero options for freeform or 2-6 options for choices; never one option.",
223
+ inputSchema: buildQuestionShape(initialLimit),
213
224
  handler: function (input) {
225
+ if (questionLimit(options) === 1 && input && Array.isArray(input.questions) && input.questions.length !== 1) {
226
+ return Promise.resolve({ content: [{ type: "text", text: "Ask exactly one question at a time, wait for the answer, then ask the next question." }], isError: true });
227
+ }
214
228
  return dispatchUserInput(handler, input, { source: "mcp_fallback", native: false }).then(function (result) {
215
- if (result.status !== "submitted") return { content: [{ type: "text", text: "The user cancelled this question." }], isError: true };
229
+ if (result.status !== "submitted") return { content: [{ type: "text", text: result.reason || "The user cancelled this question." }], isError: true };
216
230
  return { content: [{ type: "text", text: JSON.stringify(result.answers) }] };
217
231
  }).catch(function (error) { return { content: [{ type: "text", text: "Error: " + error.message }], isError: true }; });
218
232
  },
@@ -235,7 +249,7 @@ function createFallbackServer(adapter, handler) {
235
249
  var opts = arguments.length > 2 && arguments[2] ? arguments[2] : {};
236
250
  if (!adapter || typeof adapter.createToolServer !== "function") return null;
237
251
  if (capability(adapter).mode === "native" && opts.force !== true) return null;
238
- return adapter.createToolServer({ name: "yoke-user-input", version: "1.0.0", tools: fallbackToolDefs(handler) });
252
+ return adapter.createToolServer({ name: "yoke-user-input", version: "1.0.0", tools: fallbackToolDefs(handler, opts) });
239
253
  }
240
254
 
241
255
  module.exports = {
package/package.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "4.1.0-beta.5",
3
+ "version": "4.1.0-beta.7",
4
4
  "description": "Self-hosted team workspace for Claude Code and Codex. Multi-user, browser-based, with persistent AI mates.",
5
5
  "bin": {
6
- "clay-server": "./bin/cli.js",
7
- "claude-relay": "./bin/claude-relay.js"
6
+ "clay-server": "./bin/cli.js"
8
7
  },
9
8
  "scripts": {
10
9
  "dev": "node bin/cli.js --dev",
@@ -15,7 +14,8 @@
15
14
  },
16
15
  "files": [
17
16
  "bin/",
18
- "lib/"
17
+ "lib/",
18
+ "LICENSES/"
19
19
  ],
20
20
  "keywords": [
21
21
  "claude",
@@ -59,13 +59,12 @@
59
59
  "web-push": "^3.6.7",
60
60
  "ws": "^8.18.0"
61
61
  },
62
- "license": "MIT",
62
+ "license": "AGPL-3.0-only",
63
63
  "engines": {
64
64
  "node": ">=20.0.0"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@semantic-release/changelog": "^6.0.3",
68
- "@semantic-release/exec": "^7.1.0",
69
68
  "@semantic-release/git": "^10.0.1",
70
69
  "semantic-release": "^25.0.3"
71
70
  }
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- console.warn("\x1b[33m⚠ 'claude-relay' is deprecated and will be removed in a future version.\x1b[0m");
4
- console.warn("\x1b[33m Use \x1b[1mnpx clay-server\x1b[22m instead.\x1b[0m\n");
5
-
6
- require("./cli.js");
@@ -1,439 +0,0 @@
1
- // app-loop-wizard.js - Ralph Loop wizard: step navigation, data collection, repeat picker
2
- // Extracted from app-loop-ui.js
3
-
4
- import { refreshIcons } from './icons.js';
5
- import { store } from './store.js';
6
- import { getWs } from './ws-ref.js';
7
- import { requireSkills } from './app-skills-install.js';
8
-
9
- // --- Module-owned state (not in store) ---
10
- var wizardStep = 1;
11
- var wizardSource = "ralph"; // "ralph" or "task"
12
- var wizardMode = "draft"; // "draft" or "own"
13
- var loopModeChoice = "judge"; // "simple" | "judge"
14
- var promptAuthor = "clay"; // "clay" | "me"
15
- var judgeAuthor = "clay"; // "clay" | "me"
16
-
17
- // --- Module-local state accessors ---
18
- export function getWizardSource() { return wizardSource; }
19
-
20
- // --- DOM refs for repeat picker (captured in init) ---
21
- var repeatSelect = null;
22
- var repeatTimeRow = null;
23
- var repeatCustom = null;
24
- var repeatUnitSelect = null;
25
- var repeatDowRow = null;
26
- var cronPreview = null;
27
-
28
- // ========================================================
29
- // Init
30
- // ========================================================
31
- export function initLoopWizard() {
32
- // Repeat picker DOM refs
33
- repeatSelect = document.getElementById("ralph-repeat");
34
- repeatTimeRow = document.getElementById("ralph-time-row");
35
- repeatCustom = document.getElementById("ralph-custom-repeat");
36
- repeatUnitSelect = document.getElementById("ralph-repeat-unit");
37
- repeatDowRow = document.getElementById("ralph-custom-dow-row");
38
- cronPreview = document.getElementById("ralph-cron-preview");
39
-
40
- // --- Wizard button listeners ---
41
- var wizardCloseBtn = document.getElementById("ralph-wizard-close");
42
- var wizardBackdrop = document.querySelector(".ralph-wizard-backdrop");
43
- var wizardBackBtn = document.getElementById("ralph-wizard-back");
44
- var wizardSkipBtn = document.getElementById("ralph-wizard-skip");
45
- var wizardNextBtn = document.getElementById("ralph-wizard-next");
46
-
47
- if (wizardCloseBtn) wizardCloseBtn.addEventListener("click", closeRalphWizard);
48
- if (wizardBackdrop) wizardBackdrop.addEventListener("click", closeRalphWizard);
49
- if (wizardBackBtn) wizardBackBtn.addEventListener("click", wizardBack);
50
- if (wizardSkipBtn) wizardSkipBtn.addEventListener("click", wizardSkip);
51
- if (wizardNextBtn) wizardNextBtn.addEventListener("click", wizardNext);
52
-
53
- // --- Mode cards (step 1: loop mode) ---
54
- var modeCards = document.querySelectorAll(".ralph-mode-card");
55
- for (var mc = 0; mc < modeCards.length; mc++) {
56
- modeCards[mc].addEventListener("click", function () {
57
- loopModeChoice = this.getAttribute("data-loop-mode");
58
- var all = document.querySelectorAll(".ralph-mode-card");
59
- for (var i = 0; i < all.length; i++) all[i].classList.remove("active");
60
- this.classList.add("active");
61
- updateModePreview();
62
- });
63
- }
64
-
65
- // --- Authorship toggles (step 3) ---
66
- var authorBtns = document.querySelectorAll(".ralph-authorship-toggle .config-segment-btn");
67
- for (var ab = 0; ab < authorBtns.length; ab++) {
68
- authorBtns[ab].addEventListener("click", function () {
69
- var toggle = this.parentElement;
70
- var file = toggle.getAttribute("data-file");
71
- var author = this.getAttribute("data-author");
72
- var siblings = toggle.querySelectorAll(".config-segment-btn");
73
- for (var s = 0; s < siblings.length; s++) siblings[s].classList.remove("active");
74
- this.classList.add("active");
75
- if (file === "prompt") promptAuthor = author;
76
- if (file === "judge") judgeAuthor = author;
77
- });
78
- }
79
-
80
- // --- Repeat picker handlers ---
81
- if (repeatSelect) {
82
- repeatSelect.addEventListener("change", updateRepeatUI);
83
- }
84
- if (repeatUnitSelect) {
85
- repeatUnitSelect.addEventListener("change", function () {
86
- if (repeatDowRow) repeatDowRow.style.display = this.value === "week" ? "" : "none";
87
- updateRepeatUI();
88
- });
89
- }
90
-
91
- var timeInput = document.getElementById("ralph-time");
92
- if (timeInput) timeInput.addEventListener("change", updateRepeatUI);
93
-
94
- // DOW buttons in custom repeat
95
- var customDowBtns = document.querySelectorAll("#ralph-custom-repeat .sched-dow-btn");
96
- for (var di = 0; di < customDowBtns.length; di++) {
97
- customDowBtns[di].addEventListener("click", function () {
98
- this.classList.toggle("active");
99
- updateRepeatUI();
100
- });
101
- }
102
- }
103
-
104
- // ========================================================
105
- // Mode preview toggle
106
- // ========================================================
107
-
108
- function updateModePreview() {
109
- var judgePreview = document.querySelector(".ralph-mode-preview-judge");
110
- var simplePreview = document.querySelector(".ralph-mode-preview-simple");
111
- if (judgePreview) judgePreview.style.display = loopModeChoice === "judge" ? "" : "none";
112
- if (simplePreview) simplePreview.style.display = loopModeChoice === "simple" ? "" : "none";
113
- }
114
-
115
- // ========================================================
116
- // requireClayRalph
117
- // ========================================================
118
-
119
- function requireClayRalph(cb) {
120
- requireSkills({
121
- title: "Skill Installation Required",
122
- reason: "This feature requires the following skill to be installed.",
123
- skills: [{ name: "clay-ralph", url: "https://github.com/chadbyte/clay-ralph", scope: "global" }]
124
- }, cb);
125
- }
126
-
127
- // ========================================================
128
- // Ralph Wizard (exported: openRalphWizard, closeRalphWizard)
129
- // ========================================================
130
-
131
- export function openRalphWizard(source) {
132
- requireClayRalph(function () {
133
- wizardSource = source || "ralph";
134
- store.set({ wizardData: { name: "", task: "", maxIterations: null } });
135
- loopModeChoice = "judge";
136
- promptAuthor = "clay";
137
- judgeAuthor = "clay";
138
-
139
- var el = document.getElementById("ralph-wizard");
140
- if (!el) return;
141
-
142
- // Reset inputs
143
- var taskEl = document.getElementById("ralph-task");
144
- if (taskEl) taskEl.value = "";
145
- var promptInput = document.getElementById("ralph-prompt-input");
146
- if (promptInput) promptInput.value = "";
147
- var judgeInput = document.getElementById("ralph-judge-input");
148
- if (judgeInput) judgeInput.value = "";
149
-
150
- // Reset mode cards
151
- var modeCards = document.querySelectorAll(".ralph-mode-card");
152
- for (var mc = 0; mc < modeCards.length; mc++) {
153
- if (modeCards[mc].getAttribute("data-loop-mode") === "judge") {
154
- modeCards[mc].classList.add("active");
155
- } else {
156
- modeCards[mc].classList.remove("active");
157
- }
158
- }
159
-
160
- // Reset authorship toggles
161
- var authorToggles = document.querySelectorAll(".ralph-authorship-toggle");
162
- for (var at = 0; at < authorToggles.length; at++) {
163
- var btns = authorToggles[at].querySelectorAll(".config-segment-btn");
164
- for (var b = 0; b < btns.length; b++) {
165
- if (btns[b].getAttribute("data-author") === "clay") {
166
- btns[b].classList.add("active");
167
- } else {
168
- btns[b].classList.remove("active");
169
- }
170
- }
171
- }
172
-
173
- // Update text based on source
174
- var isTask = wizardSource === "task";
175
- var headerSpan = el.querySelector(".ralph-wizard-header > span");
176
- if (headerSpan) headerSpan.textContent = isTask ? "New Task" : "New Loop";
177
-
178
- if (wizardSource === "task") {
179
- // Tasks skip step 1 (loop mode), go to step 2
180
- wizardStep = 2;
181
- } else {
182
- wizardStep = 1;
183
- }
184
- el.classList.remove("hidden");
185
- var statusEl = document.getElementById("ralph-install-status");
186
- if (statusEl) { statusEl.classList.add("hidden"); statusEl.innerHTML = ""; }
187
-
188
- // Reset exec modal flag
189
- store.set({ execModalShown: false });
190
-
191
- updateModePreview();
192
- updateWizardStep();
193
- });
194
- }
195
-
196
- export function closeRalphWizard() {
197
- var el = document.getElementById("ralph-wizard");
198
- if (el) el.classList.add("hidden");
199
- }
200
-
201
- // --- Internal wizard helpers ---
202
-
203
- function updateWizardStep() {
204
- var steps = document.querySelectorAll(".ralph-step");
205
- for (var i = 0; i < steps.length; i++) {
206
- var stepNum = parseInt(steps[i].getAttribute("data-step"), 10);
207
- if (stepNum === wizardStep) {
208
- steps[i].classList.add("active");
209
- } else {
210
- steps[i].classList.remove("active");
211
- }
212
- }
213
- var dots = document.querySelectorAll(".ralph-dot");
214
- for (var j = 0; j < dots.length; j++) {
215
- var dotStep = parseInt(dots[j].getAttribute("data-step"), 10);
216
- dots[j].classList.remove("active", "done");
217
- if (dotStep === wizardStep) dots[j].classList.add("active");
218
- else if (dotStep < wizardStep) dots[j].classList.add("done");
219
- }
220
-
221
- // Show/hide JUDGE authorship row based on loop mode
222
- var judgeRow = document.getElementById("ralph-judge-authorship-row");
223
- if (judgeRow) judgeRow.style.display = loopModeChoice === "simple" ? "none" : "";
224
-
225
- // Update step 3 input visibility
226
- if (wizardStep === 3) updateInputVisibility();
227
-
228
- var backBtn = document.getElementById("ralph-wizard-back");
229
- var skipBtn = document.getElementById("ralph-wizard-skip");
230
- var nextBtn = document.getElementById("ralph-wizard-next");
231
- if (backBtn) {
232
- var firstStep = (wizardSource === "task") ? 2 : 1;
233
- backBtn.style.visibility = (wizardStep === firstStep) ? "hidden" : "visible";
234
- backBtn.textContent = (wizardSource === "task" && wizardStep <= 2) ? "Cancel" : "Back";
235
- }
236
- if (skipBtn) skipBtn.style.display = "none";
237
- if (nextBtn) {
238
- if (wizardStep === 3) nextBtn.textContent = "Launch";
239
- else nextBtn.textContent = "Next";
240
- }
241
- }
242
-
243
- function updateInputVisibility() {
244
- var taskSection = document.getElementById("ralph-input-task-section");
245
- var promptSection = document.getElementById("ralph-input-prompt-section");
246
- var judgeSection = document.getElementById("ralph-input-judge-section");
247
-
248
- var needsTask = (promptAuthor === "clay");
249
- var needsPrompt = (promptAuthor === "me");
250
- var needsJudge = (loopModeChoice === "judge" && judgeAuthor === "me");
251
-
252
- if (taskSection) {
253
- if (needsTask) taskSection.classList.remove("hidden");
254
- else taskSection.classList.add("hidden");
255
- }
256
- if (promptSection) {
257
- if (needsPrompt) promptSection.classList.remove("hidden");
258
- else promptSection.classList.add("hidden");
259
- }
260
- if (judgeSection) {
261
- if (needsJudge) judgeSection.classList.remove("hidden");
262
- else judgeSection.classList.add("hidden");
263
- }
264
-
265
- var heading = document.querySelector('.ralph-step[data-step="3"] h3');
266
- if (heading) {
267
- if (needsTask && !needsJudge) heading.textContent = "Describe your task";
268
- else if (needsPrompt && !needsJudge) heading.textContent = "Provide your prompt";
269
- else if (needsPrompt && needsJudge) heading.textContent = "Provide your files";
270
- else if (needsTask && needsJudge) heading.textContent = "Provide details";
271
- else heading.textContent = "Provide details";
272
- }
273
- }
274
-
275
- function collectWizardData() {
276
- var wd = {
277
- name: "",
278
- maxIterations: null,
279
- cron: buildWizardCron(),
280
- loopMode: loopModeChoice,
281
- promptAuthor: promptAuthor,
282
- judgeAuthor: (loopModeChoice === "judge") ? judgeAuthor : null,
283
- mode: (promptAuthor === "me") ? "own" : "draft"
284
- };
285
-
286
- if (promptAuthor === "clay") {
287
- var taskEl = document.getElementById("ralph-task");
288
- wd.task = taskEl ? taskEl.value.trim() : "";
289
- wd.promptText = null;
290
- } else {
291
- var promptInput = document.getElementById("ralph-prompt-input");
292
- wd.task = "";
293
- wd.promptText = promptInput ? promptInput.value.trim() : "";
294
- }
295
-
296
- if (loopModeChoice === "judge" && judgeAuthor === "me") {
297
- var judgeInput = document.getElementById("ralph-judge-input");
298
- wd.judgeText = judgeInput ? judgeInput.value.trim() : "";
299
- } else {
300
- wd.judgeText = null;
301
- }
302
-
303
- store.set({ wizardData: wd });
304
- }
305
-
306
- function buildWizardCron() {
307
- if (!repeatSelect) return null;
308
- var preset = repeatSelect.value;
309
- if (preset === "none") return null;
310
-
311
- var timeEl = document.getElementById("ralph-time");
312
- var timeVal = timeEl ? timeEl.value : "09:00";
313
- var timeParts = timeVal.split(":");
314
- var hour = parseInt(timeParts[0], 10) || 9;
315
- var minute = parseInt(timeParts[1], 10) || 0;
316
-
317
- if (preset === "daily") return minute + " " + hour + " * * *";
318
- if (preset === "weekdays") return minute + " " + hour + " * * 1-5";
319
- if (preset === "weekly") return minute + " " + hour + " * * " + new Date().getDay();
320
- if (preset === "monthly") return minute + " " + hour + " " + new Date().getDate() + " * *";
321
-
322
- if (preset === "custom") {
323
- var unitEl = document.getElementById("ralph-repeat-unit");
324
- var unit = unitEl ? unitEl.value : "day";
325
- if (unit === "day") return minute + " " + hour + " * * *";
326
- if (unit === "month") return minute + " " + hour + " " + new Date().getDate() + " * *";
327
- // week: collect selected days
328
- var dowBtns = document.querySelectorAll("#ralph-custom-repeat .sched-dow-btn.active");
329
- var days = [];
330
- for (var i = 0; i < dowBtns.length; i++) {
331
- days.push(dowBtns[i].dataset.dow);
332
- }
333
- if (days.length === 0) days.push(String(new Date().getDay()));
334
- return minute + " " + hour + " * * " + days.join(",");
335
- }
336
- return null;
337
- }
338
-
339
- function cronToHumanText(cron) {
340
- if (!cron) return "";
341
- var parts = cron.trim().split(/\s+/);
342
- if (parts.length !== 5) return cron;
343
- var m = parts[0], h = parts[1], dom = parts[2], dow = parts[4];
344
- var pad = function(n) { return (parseInt(n,10) < 10 ? "0" : "") + parseInt(n,10); };
345
- var t = pad(h) + ":" + pad(m);
346
- var dayNames = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
347
- if (dow === "*" && dom === "*") return "Every day at " + t;
348
- if (dow === "1-5" && dom === "*") return "Weekdays at " + t;
349
- if (dom !== "*" && dow === "*") return "Monthly on day " + dom + " at " + t;
350
- if (dow !== "*" && dom === "*") {
351
- var ds = dow.split(",").map(function(d) { return dayNames[parseInt(d,10)] || d; });
352
- return "Every " + ds.join(", ") + " at " + t;
353
- }
354
- return cron;
355
- }
356
-
357
- var LAST_STEP = 3;
358
-
359
- function validateLastStep() {
360
- var wd = store.get('wizardData');
361
- if (promptAuthor === "clay") {
362
- var taskEl = document.getElementById("ralph-task");
363
- if (!wd.task) {
364
- if (taskEl) { taskEl.focus(); taskEl.style.borderColor = "#e74c3c"; setTimeout(function() { taskEl.style.borderColor = ""; }, 2000); }
365
- return false;
366
- }
367
- } else {
368
- var promptInput = document.getElementById("ralph-prompt-input");
369
- if (!wd.promptText) {
370
- if (promptInput) { promptInput.focus(); promptInput.style.borderColor = "#e74c3c"; setTimeout(function() { promptInput.style.borderColor = ""; }, 2000); }
371
- return false;
372
- }
373
- }
374
- if (loopModeChoice === "judge" && judgeAuthor === "me") {
375
- var judgeInput = document.getElementById("ralph-judge-input");
376
- if (!wd.judgeText) {
377
- if (judgeInput) { judgeInput.focus(); judgeInput.style.borderColor = "#e74c3c"; setTimeout(function() { judgeInput.style.borderColor = ""; }, 2000); }
378
- return false;
379
- }
380
- }
381
- return true;
382
- }
383
-
384
- function wizardNext() {
385
- collectWizardData();
386
- if (wizardStep < LAST_STEP) {
387
- wizardStep++;
388
- updateWizardStep();
389
- return;
390
- }
391
- if (validateLastStep()) wizardSubmit();
392
- }
393
-
394
- function wizardBack() {
395
- if (wizardSource === "task" && wizardStep <= 2) {
396
- closeRalphWizard();
397
- return;
398
- }
399
- if (wizardStep > 1) {
400
- collectWizardData();
401
- wizardStep--;
402
- updateWizardStep();
403
- }
404
- }
405
-
406
- function wizardSkip() {
407
- if (wizardStep < 3) {
408
- wizardStep++;
409
- updateWizardStep();
410
- }
411
- }
412
-
413
- function wizardSubmit() {
414
- collectWizardData();
415
- var wd = Object.assign({}, store.get('wizardData'));
416
- wd.source = wizardSource === "task" ? "task" : undefined;
417
- store.set({ wizardData: wd });
418
- closeRalphWizard();
419
- var ws = getWs();
420
- if (ws && ws.readyState === 1) {
421
- ws.send(JSON.stringify({ type: "ralph_wizard_complete", data: wd }));
422
- }
423
- }
424
-
425
- function updateRepeatUI() {
426
- if (!repeatSelect) return;
427
- var val = repeatSelect.value;
428
- var isScheduled = val !== "none";
429
- if (repeatTimeRow) repeatTimeRow.style.display = isScheduled ? "" : "none";
430
- if (repeatCustom) repeatCustom.style.display = val === "custom" ? "" : "none";
431
- if (cronPreview) cronPreview.style.display = isScheduled ? "" : "none";
432
- if (isScheduled) {
433
- var cron = buildWizardCron();
434
- var humanEl = document.getElementById("ralph-cron-human");
435
- var cronEl = document.getElementById("ralph-cron-expr");
436
- if (humanEl) humanEl.textContent = cronToHumanText(cron);
437
- if (cronEl) cronEl.textContent = cron || "";
438
- }
439
- }