@exla/rudder 0.0.0-stage → 3.4.1

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 (187) hide show
  1. package/LICENSE +100 -0
  2. package/README.md +733 -2
  3. package/assets/rudder.icns +0 -0
  4. package/assets/sounds/NOTICE.md +8 -0
  5. package/assets/sounds/ping.mp3 +0 -0
  6. package/dist/agent-attention.d.ts +5 -0
  7. package/dist/agent-attention.js +54 -0
  8. package/dist/agent-attention.js.map +1 -0
  9. package/dist/analytics.d.ts +36 -0
  10. package/dist/analytics.js +178 -0
  11. package/dist/analytics.js.map +1 -0
  12. package/dist/attach-session.d.ts +91 -0
  13. package/dist/attach-session.js +290 -0
  14. package/dist/attach-session.js.map +1 -0
  15. package/dist/auth.d.ts +36 -0
  16. package/dist/auth.js +397 -0
  17. package/dist/auth.js.map +1 -0
  18. package/dist/backends.d.ts +3 -0
  19. package/dist/backends.js +638 -0
  20. package/dist/backends.js.map +1 -0
  21. package/dist/board/board.css +1 -0
  22. package/dist/board/board.js +2 -0
  23. package/dist/board/daemon.d.ts +39 -0
  24. package/dist/board/daemon.js +1510 -0
  25. package/dist/board/daemon.js.map +1 -0
  26. package/dist/brain.d.ts +24 -0
  27. package/dist/brain.js +244 -0
  28. package/dist/brain.js.map +1 -0
  29. package/dist/bus.d.ts +9 -0
  30. package/dist/bus.js +23 -0
  31. package/dist/bus.js.map +1 -0
  32. package/dist/checkout-sync.d.ts +35 -0
  33. package/dist/checkout-sync.js +247 -0
  34. package/dist/checkout-sync.js.map +1 -0
  35. package/dist/cloud.d.ts +51 -0
  36. package/dist/cloud.js +4672 -0
  37. package/dist/cloud.js.map +1 -0
  38. package/dist/codex-binary.d.ts +12 -0
  39. package/dist/codex-binary.js +184 -0
  40. package/dist/codex-binary.js.map +1 -0
  41. package/dist/context-audit.d.ts +21 -0
  42. package/dist/context-audit.js +109 -0
  43. package/dist/context-audit.js.map +1 -0
  44. package/dist/daemon.d.ts +27 -0
  45. package/dist/daemon.js +203 -0
  46. package/dist/daemon.js.map +1 -0
  47. package/dist/effort.d.ts +9 -0
  48. package/dist/effort.js +72 -0
  49. package/dist/effort.js.map +1 -0
  50. package/dist/feedback.d.ts +46 -0
  51. package/dist/feedback.js +173 -0
  52. package/dist/feedback.js.map +1 -0
  53. package/dist/gc.d.ts +3 -0
  54. package/dist/gc.js +262 -0
  55. package/dist/gc.js.map +1 -0
  56. package/dist/git.d.ts +31 -0
  57. package/dist/git.js +455 -0
  58. package/dist/git.js.map +1 -0
  59. package/dist/goal.d.ts +33 -0
  60. package/dist/goal.js +95 -0
  61. package/dist/goal.js.map +1 -0
  62. package/dist/graph.d.ts +93 -0
  63. package/dist/graph.js +372 -0
  64. package/dist/graph.js.map +1 -0
  65. package/dist/handoff.d.ts +110 -0
  66. package/dist/handoff.js +565 -0
  67. package/dist/handoff.js.map +1 -0
  68. package/dist/improve/advisor.d.ts +10 -0
  69. package/dist/improve/advisor.js +115 -0
  70. package/dist/improve/advisor.js.map +1 -0
  71. package/dist/improve/collect.d.ts +49 -0
  72. package/dist/improve/collect.js +274 -0
  73. package/dist/improve/collect.js.map +1 -0
  74. package/dist/improve/gate.d.ts +17 -0
  75. package/dist/improve/gate.js +60 -0
  76. package/dist/improve/gate.js.map +1 -0
  77. package/dist/improve/index.d.ts +9 -0
  78. package/dist/improve/index.js +376 -0
  79. package/dist/improve/index.js.map +1 -0
  80. package/dist/improve/judge.d.ts +32 -0
  81. package/dist/improve/judge.js +114 -0
  82. package/dist/improve/judge.js.map +1 -0
  83. package/dist/improve/mine.d.ts +31 -0
  84. package/dist/improve/mine.js +192 -0
  85. package/dist/improve/mine.js.map +1 -0
  86. package/dist/improve/propose.d.ts +51 -0
  87. package/dist/improve/propose.js +150 -0
  88. package/dist/improve/propose.js.map +1 -0
  89. package/dist/improve/rank.d.ts +16 -0
  90. package/dist/improve/rank.js +49 -0
  91. package/dist/improve/rank.js.map +1 -0
  92. package/dist/improve/schedule.d.ts +1 -0
  93. package/dist/improve/schedule.js +95 -0
  94. package/dist/improve/schedule.js.map +1 -0
  95. package/dist/improve/ship.d.ts +25 -0
  96. package/dist/improve/ship.js +82 -0
  97. package/dist/improve/ship.js.map +1 -0
  98. package/dist/improve/state.d.ts +157 -0
  99. package/dist/improve/state.js +264 -0
  100. package/dist/improve/state.js.map +1 -0
  101. package/dist/index.d.ts +2 -0
  102. package/dist/index.js +88 -0
  103. package/dist/index.js.map +1 -0
  104. package/dist/jj-binary.d.ts +36 -0
  105. package/dist/jj-binary.js +143 -0
  106. package/dist/jj-binary.js.map +1 -0
  107. package/dist/jj.d.ts +163 -0
  108. package/dist/jj.js +926 -0
  109. package/dist/jj.js.map +1 -0
  110. package/dist/logger.d.ts +22 -0
  111. package/dist/logger.js +110 -0
  112. package/dist/logger.js.map +1 -0
  113. package/dist/main.d.ts +3 -0
  114. package/dist/main.js +1366 -0
  115. package/dist/main.js.map +1 -0
  116. package/dist/migration.d.ts +82 -0
  117. package/dist/migration.js +198 -0
  118. package/dist/migration.js.map +1 -0
  119. package/dist/models.d.ts +9 -0
  120. package/dist/models.js +623 -0
  121. package/dist/models.js.map +1 -0
  122. package/dist/move-home.d.ts +82 -0
  123. package/dist/move-home.js +472 -0
  124. package/dist/move-home.js.map +1 -0
  125. package/dist/native/darwin-arm64/rudder-native +0 -0
  126. package/dist/native/darwin-x64/rudder-native +0 -0
  127. package/dist/native/linux-x64/rudder-native +0 -0
  128. package/dist/native-agents.d.ts +8 -0
  129. package/dist/native-agents.js +178 -0
  130. package/dist/native-agents.js.map +1 -0
  131. package/dist/native-binary.d.ts +4 -0
  132. package/dist/native-binary.js +140 -0
  133. package/dist/native-binary.js.map +1 -0
  134. package/dist/onboarding.d.ts +11 -0
  135. package/dist/onboarding.js +111 -0
  136. package/dist/onboarding.js.map +1 -0
  137. package/dist/paste-bridge.d.ts +36 -0
  138. package/dist/paste-bridge.js +279 -0
  139. package/dist/paste-bridge.js.map +1 -0
  140. package/dist/plan-mode.d.ts +2 -0
  141. package/dist/plan-mode.js +22 -0
  142. package/dist/plan-mode.js.map +1 -0
  143. package/dist/planner.d.ts +27 -0
  144. package/dist/planner.js +587 -0
  145. package/dist/planner.js.map +1 -0
  146. package/dist/predict.d.ts +67 -0
  147. package/dist/predict.js +369 -0
  148. package/dist/predict.js.map +1 -0
  149. package/dist/qr.d.ts +13 -0
  150. package/dist/qr.js +377 -0
  151. package/dist/qr.js.map +1 -0
  152. package/dist/report.d.ts +74 -0
  153. package/dist/report.js +412 -0
  154. package/dist/report.js.map +1 -0
  155. package/dist/rudder-md.d.ts +33 -0
  156. package/dist/rudder-md.js +142 -0
  157. package/dist/rudder-md.js.map +1 -0
  158. package/dist/run-manager.d.ts +62 -0
  159. package/dist/run-manager.js +1400 -0
  160. package/dist/run-manager.js.map +1 -0
  161. package/dist/scheduler.d.ts +128 -0
  162. package/dist/scheduler.js +955 -0
  163. package/dist/scheduler.js.map +1 -0
  164. package/dist/state.d.ts +127 -0
  165. package/dist/state.js +704 -0
  166. package/dist/state.js.map +1 -0
  167. package/dist/surfaces.d.ts +91 -0
  168. package/dist/surfaces.js +460 -0
  169. package/dist/surfaces.js.map +1 -0
  170. package/dist/task-summary.d.ts +30 -0
  171. package/dist/task-summary.js +472 -0
  172. package/dist/task-summary.js.map +1 -0
  173. package/dist/types.d.ts +492 -0
  174. package/dist/types.js +2 -0
  175. package/dist/types.js.map +1 -0
  176. package/dist/usage.d.ts +198 -0
  177. package/dist/usage.js +830 -0
  178. package/dist/usage.js.map +1 -0
  179. package/dist/util.d.ts +73 -0
  180. package/dist/util.js +399 -0
  181. package/dist/util.js.map +1 -0
  182. package/dist/version-check.d.ts +16 -0
  183. package/dist/version-check.js +269 -0
  184. package/dist/version-check.js.map +1 -0
  185. package/package.json +68 -4
  186. package/scripts/copy-native.mjs +89 -0
  187. package/scripts/postinstall.mjs +34 -0
@@ -0,0 +1,21 @@
1
+ export type ContextAuditSeverity = "low" | "medium" | "high";
2
+ export type ContextAuditFinding = {
3
+ severity: ContextAuditSeverity;
4
+ file: string;
5
+ message: string;
6
+ };
7
+ export type ContextAuditReport = {
8
+ repoRoot: string;
9
+ generatedAt: string;
10
+ findings: ContextAuditFinding[];
11
+ files: Array<{
12
+ path: string;
13
+ bytes: number;
14
+ lines: number;
15
+ }>;
16
+ };
17
+ export declare function auditContext(repoRoot?: string): Promise<ContextAuditReport>;
18
+ export declare function printContextAudit(opts?: {
19
+ json?: boolean;
20
+ repoRoot?: string;
21
+ }): Promise<void>;
@@ -0,0 +1,109 @@
1
+ import fsp from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { findRepoRoot } from "./git.js";
4
+ import { pathExists } from "./util.js";
5
+ const CANDIDATE_FILES = [
6
+ "AGENTS.md",
7
+ "CLAUDE.md",
8
+ ".claude/CLAUDE.md",
9
+ ".claude/settings.json",
10
+ ".claude/settings.local.json",
11
+ "RUDDER.md",
12
+ "DECISIONS.md",
13
+ "RUDDER_SHARED.md",
14
+ ];
15
+ export async function auditContext(repoRoot = findRepoRoot()) {
16
+ const files = await collectContextFiles(repoRoot);
17
+ const findings = [];
18
+ const seenLines = new Map();
19
+ const summaries = [];
20
+ for (const rel of files) {
21
+ const abs = path.join(repoRoot, rel);
22
+ const text = await fsp.readFile(abs, "utf8").catch(() => "");
23
+ const stat = await fsp.stat(abs).catch(() => null);
24
+ const lines = text.split(/\r?\n/);
25
+ summaries.push({ path: rel, bytes: stat?.size ?? Buffer.byteLength(text), lines: lines.length });
26
+ if (lines.length > 500) {
27
+ findings.push({ severity: "medium", file: rel, message: "context file is over 500 lines; consider splitting or pruning" });
28
+ }
29
+ if (Buffer.byteLength(text) > 64_000) {
30
+ findings.push({ severity: "medium", file: rel, message: "context file is over 64KB; always-on context may be noisy" });
31
+ }
32
+ if (secretPattern().test(text)) {
33
+ findings.push({ severity: rel === "RUDDER_SHARED.md" ? "low" : "high", file: rel, message: "secret-like token found in a context file" });
34
+ }
35
+ if (promptInjectionPattern().test(text)) {
36
+ findings.push({ severity: "high", file: rel, message: "prompt-injection phrase found in a context file" });
37
+ }
38
+ for (const line of lines) {
39
+ const normalized = line.trim().replace(/\s+/g, " ").toLowerCase();
40
+ if (normalized.length < 48 || normalized.startsWith("#")) {
41
+ continue;
42
+ }
43
+ const previous = seenLines.get(normalized);
44
+ if (previous && previous !== rel) {
45
+ findings.push({ severity: "low", file: rel, message: `duplicates a rule also present in ${previous}` });
46
+ break;
47
+ }
48
+ seenLines.set(normalized, rel);
49
+ }
50
+ }
51
+ return {
52
+ repoRoot,
53
+ generatedAt: new Date().toISOString(),
54
+ findings,
55
+ files: summaries.sort((a, b) => a.path.localeCompare(b.path)),
56
+ };
57
+ }
58
+ export async function printContextAudit(opts = {}) {
59
+ const report = await auditContext(opts.repoRoot);
60
+ if (opts.json) {
61
+ console.log(JSON.stringify(report, null, 2));
62
+ return;
63
+ }
64
+ console.log(`Context audit for ${report.repoRoot}`);
65
+ if (!report.findings.length) {
66
+ console.log("No context issues found.");
67
+ }
68
+ else {
69
+ for (const finding of report.findings) {
70
+ console.log(`${finding.severity.toUpperCase()} ${finding.file}: ${finding.message}`);
71
+ }
72
+ }
73
+ console.log("");
74
+ console.log("Files:");
75
+ for (const file of report.files) {
76
+ console.log(` ${file.path} (${file.lines} lines, ${file.bytes} bytes)`);
77
+ }
78
+ }
79
+ async function collectContextFiles(repoRoot) {
80
+ const files = new Set();
81
+ for (const rel of CANDIDATE_FILES) {
82
+ if (await pathExists(path.join(repoRoot, rel))) {
83
+ files.add(rel);
84
+ }
85
+ }
86
+ for (const dir of [".claude/skills", ".claude/commands", ".claude/agents"]) {
87
+ const abs = path.join(repoRoot, dir);
88
+ const entries = await fsp.readdir(abs, { withFileTypes: true }).catch(() => []);
89
+ for (const entry of entries) {
90
+ if (entry.isFile() && entry.name.endsWith(".md")) {
91
+ files.add(path.join(dir, entry.name));
92
+ }
93
+ if (entry.isDirectory()) {
94
+ const nested = path.join(abs, entry.name, "SKILL.md");
95
+ if (await pathExists(nested)) {
96
+ files.add(path.join(dir, entry.name, "SKILL.md"));
97
+ }
98
+ }
99
+ }
100
+ }
101
+ return [...files].sort();
102
+ }
103
+ function secretPattern() {
104
+ return /(sk-[A-Za-z0-9_-]{16,}|ghp_[A-Za-z0-9_]{16,}|github_pat_[A-Za-z0-9_]{16,}|xox[baprs]-[A-Za-z0-9-]{16,}|api[_-]?key\s*=\s*[A-Za-z0-9_.-]{16,})/i;
105
+ }
106
+ function promptInjectionPattern() {
107
+ return /(ignore (all )?(previous|prior) instructions|you are now|developer mode|<\/system>|<<SYS>>|\[INST\])/i;
108
+ }
109
+ //# sourceMappingURL=context-audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-audit.js","sourceRoot":"","sources":["../src/context-audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { BoardControlMode, BoardDaemonHandle } from "./board/daemon.js";
2
+ export type DaemonPidFile = {
3
+ pid: number;
4
+ port: number;
5
+ startedAt: string;
6
+ controlMode?: BoardControlMode;
7
+ };
8
+ export declare function daemonPidPath(repoRoot: string): string;
9
+ /**
10
+ * Phase 2 lifecycle: a single-instance guard. If a live daemon already holds
11
+ * the pid file, returns its port without starting a second server. Otherwise
12
+ * starts the board daemon in-process and writes the pid file.
13
+ */
14
+ export declare function ensureBoardRunning(repoRoot: string, opts?: {
15
+ port?: number;
16
+ open?: boolean;
17
+ scheduler?: boolean;
18
+ }): Promise<{
19
+ port: number;
20
+ url: string;
21
+ slug: string;
22
+ /** Deep link to THIS repo's board (`${url}/rudder/${slug}`), or the index when the
23
+ * slug is unknown. The TUI opens this so `o` lands on the current project. */
24
+ projectUrl: string;
25
+ started: boolean;
26
+ handle?: BoardDaemonHandle;
27
+ }>;
package/dist/daemon.js ADDED
@@ -0,0 +1,203 @@
1
+ import fs from "node:fs";
2
+ import fsp from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { processAlive } from "./git.js";
5
+ import { loadConfig, projectStateDir, registerProject, runsDir } from "./state.js";
6
+ import { DEFAULT_BOARD_PORT } from "./types.js";
7
+ import { ensureDir, readJson, writeJson } from "./util.js";
8
+ import { shortenHome } from "./util.js";
9
+ import { startBoardDaemon } from "./board/daemon.js";
10
+ import { RudderBus } from "./bus.js";
11
+ import { createLogger } from "./logger.js";
12
+ import { onRunTransition, scheduleTick } from "./scheduler.js";
13
+ import { renderLiveRudderMd } from "./surfaces.js";
14
+ const SCHEDULE_TICK_MS = 1000;
15
+ const logger = createLogger("daemon", { fileName: "daemon.log" });
16
+ export function daemonPidPath(repoRoot) {
17
+ return path.join(projectStateDir(repoRoot), "daemon.pid");
18
+ }
19
+ async function readPidFile(repoRoot) {
20
+ const data = await readJson(daemonPidPath(repoRoot));
21
+ if (data && typeof data.pid === "number" && typeof data.port === "number") {
22
+ return data;
23
+ }
24
+ return null;
25
+ }
26
+ /**
27
+ * Phase 2 lifecycle: a single-instance guard. If a live daemon already holds
28
+ * the pid file, returns its port without starting a second server. Otherwise
29
+ * starts the board daemon in-process and writes the pid file.
30
+ */
31
+ export async function ensureBoardRunning(repoRoot, opts) {
32
+ // registerProject is idempotent and returns this repo's stable slug, which we use
33
+ // to deep-link the browser straight to this project's board.
34
+ const project = await registerProject(repoRoot).catch(() => null);
35
+ const slug = project?.slug ?? "";
36
+ const projectPath = slug ? `/rudder/${slug}` : "/rudder";
37
+ const runScheduler = opts?.scheduler ?? true;
38
+ const requestedMode = runScheduler ? "scheduler" : "projector";
39
+ const existing = await readPidFile(repoRoot);
40
+ if (existing && existing.pid !== process.pid && processAlive(existing.pid)) {
41
+ if (!existing.controlMode) {
42
+ throw new Error(`An older board process already owns this repository on port ${existing.port}; stop it once so Rudder can record its control mode safely.`);
43
+ }
44
+ if (existing.controlMode !== requestedMode) {
45
+ throw new Error(`A ${existing.controlMode} board already owns this repository on port ${existing.port}; stop it before starting ${requestedMode} mode.`);
46
+ }
47
+ const url = `http://127.0.0.1:${existing.port}`;
48
+ return { port: existing.port, url, slug, projectUrl: `${url}${projectPath}`, started: false };
49
+ }
50
+ const config = await loadConfig();
51
+ const port = opts?.port ?? config.board?.port ?? DEFAULT_BOARD_PORT;
52
+ // One bus shared between the scheduler (producer) and the board SSE
53
+ // broadcaster (consumer). The daemon is the single authoritative scheduler.
54
+ const bus = new RudderBus();
55
+ const handle = await startBoardDaemon({
56
+ port,
57
+ repoRoot,
58
+ open: opts?.open,
59
+ bus,
60
+ controlMode: requestedMode,
61
+ });
62
+ // Projector-only mode: when scheduler === false the board still serves
63
+ // HTTP + SSE + fs.watch (so it reflects whatever is written to graph.json),
64
+ // but the LAUNCHING scheduler tick is NOT started. The TUI starts the daemon
65
+ // this way so the TUI remains the sole scheduler and there is no double-launch
66
+ // (the TUI mirrors its plan into graph.json; the daemon only projects it). The
67
+ // standalone `rudder board`/`serve` paths keep the full scheduler (default).
68
+ const scheduler = runScheduler ? startScheduler(repoRoot, bus) : { stop: () => { } };
69
+ await ensureDir(projectStateDir(repoRoot));
70
+ await writeJson(daemonPidPath(repoRoot), {
71
+ pid: process.pid,
72
+ port: handle.port,
73
+ startedAt: new Date().toISOString(),
74
+ controlMode: requestedMode,
75
+ });
76
+ const cleanup = async () => {
77
+ scheduler.stop();
78
+ await handle.close().catch(() => undefined);
79
+ await fsp.unlink(daemonPidPath(repoRoot)).catch(() => undefined);
80
+ };
81
+ for (const signal of ["SIGINT", "SIGTERM"]) {
82
+ process.once(signal, () => {
83
+ void cleanup().finally(() => process.exit(signal === "SIGINT" ? 130 : 143));
84
+ });
85
+ }
86
+ process.once("exit", () => {
87
+ // Best-effort synchronous-ish unlink on normal exit.
88
+ void fsp.unlink(daemonPidPath(repoRoot)).catch(() => undefined);
89
+ });
90
+ console.log(`rudder board listening on ${handle.url}`);
91
+ console.log(` repo: ${shortenHome(repoRoot)}`);
92
+ return {
93
+ port: handle.port,
94
+ url: handle.url,
95
+ slug,
96
+ projectUrl: `${handle.url}${projectPath}`,
97
+ started: true,
98
+ handle,
99
+ };
100
+ }
101
+ /**
102
+ * Run the dependency-aware scheduler inside the daemon. The daemon is the single
103
+ * authoritative scheduler: a coarse 1s tick covers steady-state launches, and a
104
+ * fs.watch on .rudder/runs edge-triggers onRunTransition the moment a run.json
105
+ * changes (completion/failure/merge). Each run id is debounced so a burst of
106
+ * writes coalesces into one transition. Returns a stop() to tear everything down.
107
+ */
108
+ function startScheduler(repoRoot, bus) {
109
+ let stopped = false;
110
+ let tickInFlight = false;
111
+ // Coalesce interval ticks. A tick can spend several seconds creating jj
112
+ // workspaces; blindly enqueueing another locked tick every second starves the
113
+ // completion transitions that need the same lock to move nodes through Review.
114
+ const tick = () => {
115
+ if (stopped || tickInFlight)
116
+ return;
117
+ tickInFlight = true;
118
+ const started = Date.now();
119
+ logger.debug("scheduler tick started", { repoRoot });
120
+ void scheduleTick(repoRoot, bus)
121
+ .catch((error) => {
122
+ logger.warn("scheduler tick failed", {
123
+ repoRoot,
124
+ error: error instanceof Error ? error.message : String(error),
125
+ });
126
+ })
127
+ .finally(() => {
128
+ logger.debug("scheduler tick finished", {
129
+ repoRoot,
130
+ durationMs: Date.now() - started,
131
+ });
132
+ tickInFlight = false;
133
+ });
134
+ };
135
+ // Render the live RUDDER.md once on startup and tick immediately so a daemon
136
+ // restart catches up missed transitions from disk.
137
+ void renderLiveRudderMd(repoRoot).catch(() => undefined);
138
+ tick();
139
+ const interval = setInterval(() => {
140
+ tick();
141
+ }, SCHEDULE_TICK_MS);
142
+ interval.unref?.();
143
+ // Watch .rudder/runs for run.json changes; map the path back to a run id and
144
+ // edge-trigger a transition. Per-run debounce so a write burst is one call.
145
+ const runs = runsDir(repoRoot);
146
+ try {
147
+ fs.mkdirSync(runs, { recursive: true });
148
+ }
149
+ catch {
150
+ // ignore
151
+ }
152
+ const runTimers = new Map();
153
+ const trigger = (runId) => {
154
+ const existing = runTimers.get(runId);
155
+ if (existing) {
156
+ clearTimeout(existing);
157
+ }
158
+ const timer = setTimeout(() => {
159
+ runTimers.delete(runId);
160
+ if (!stopped) {
161
+ void onRunTransition(repoRoot, runId, bus).catch((error) => {
162
+ logger.warn("scheduler transition failed", {
163
+ repoRoot,
164
+ runId,
165
+ error: error instanceof Error ? error.message : String(error),
166
+ });
167
+ });
168
+ }
169
+ }, 100);
170
+ timer.unref?.();
171
+ runTimers.set(runId, timer);
172
+ };
173
+ let watcher;
174
+ try {
175
+ watcher = fs.watch(runs, { recursive: true }, (_event, filename) => {
176
+ if (!filename) {
177
+ return;
178
+ }
179
+ const normalized = String(filename).replace(/\\/g, "/");
180
+ // Match runs/<id>/run.json or <id>/run.json.
181
+ const match = normalized.match(/(?:^|\/)([^/]+)\/run\.json$/);
182
+ if (match?.[1]) {
183
+ trigger(match[1]);
184
+ }
185
+ });
186
+ watcher.on("error", () => undefined);
187
+ }
188
+ catch {
189
+ watcher = undefined;
190
+ }
191
+ return {
192
+ stop: () => {
193
+ stopped = true;
194
+ clearInterval(interval);
195
+ for (const [, timer] of runTimers) {
196
+ clearTimeout(timer);
197
+ }
198
+ runTimers.clear();
199
+ watcher?.close();
200
+ },
201
+ };
202
+ }
203
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { BackendId, EffortLevel } from "./types.js";
2
+ export type EffortOption = {
3
+ label: string;
4
+ value?: EffortLevel;
5
+ detail: string;
6
+ };
7
+ export declare function discoverEffortOptions(backend: BackendId): Promise<EffortOption[]>;
8
+ export declare function fallbackEffortOptions(backend: BackendId): EffortOption[];
9
+ export declare function normalizeEffortForBackend(backend: BackendId, effort: EffortLevel | undefined): EffortLevel | undefined;
package/dist/effort.js ADDED
@@ -0,0 +1,72 @@
1
+ import { ensureRudderCodexBinary } from "./codex-binary.js";
2
+ import { runCommand } from "./util.js";
3
+ const FALLBACK_EFFORTS = ["low", "medium", "high", "xhigh"];
4
+ export async function discoverEffortOptions(backend) {
5
+ const values = backend === "claude"
6
+ ? await discoverClaudeEfforts()
7
+ : await discoverCodexEfforts();
8
+ return [
9
+ { label: "auto", value: undefined, detail: "let the selected model choose" },
10
+ ...values.map((value) => ({
11
+ label: value,
12
+ value,
13
+ detail: effortDetail(value),
14
+ })),
15
+ ];
16
+ }
17
+ export function fallbackEffortOptions(backend) {
18
+ return [
19
+ { label: "auto", value: undefined, detail: "let the selected model choose" },
20
+ ...(backend === "claude" ? [...FALLBACK_EFFORTS, "max"] : FALLBACK_EFFORTS)
21
+ .map((value) => ({ label: value, value, detail: effortDetail(value) })),
22
+ ];
23
+ }
24
+ export function normalizeEffortForBackend(backend, effort) {
25
+ if (!effort) {
26
+ return undefined;
27
+ }
28
+ // opencode exposes no reasoning-effort flag at all, so a stored effort is
29
+ // dropped rather than passed to a CLI that would reject it.
30
+ if (backend === "opencode") {
31
+ return undefined;
32
+ }
33
+ if (backend === "codex" && effort === "max") {
34
+ return "xhigh";
35
+ }
36
+ return effort;
37
+ }
38
+ async function discoverClaudeEfforts() {
39
+ const help = await runCommand("claude", ["--help"], { allowFailure: true });
40
+ const values = parseEfforts(help.stdout || help.stderr);
41
+ return values.length ? values : [...FALLBACK_EFFORTS, "max"];
42
+ }
43
+ async function discoverCodexEfforts() {
44
+ const codex = await ensureRudderCodexBinary();
45
+ const help = await runCommand(codex, ["exec", "--help"], { allowFailure: true });
46
+ const values = parseEfforts(help.stdout || help.stderr).filter((value) => value !== "max");
47
+ return values.length ? values : FALLBACK_EFFORTS;
48
+ }
49
+ function parseEfforts(text) {
50
+ const found = new Set();
51
+ const effortLine = text
52
+ .split(/\r?\n/)
53
+ .find((line) => /effort|reasoning/i.test(line)) ?? "";
54
+ for (const value of ["low", "medium", "high", "xhigh", "max"]) {
55
+ if (new RegExp(`\\b${value}\\b`, "i").test(effortLine)) {
56
+ found.add(value);
57
+ }
58
+ }
59
+ return [...found];
60
+ }
61
+ function effortDetail(value) {
62
+ if (value === "low")
63
+ return "fastest explicit override";
64
+ if (value === "medium")
65
+ return "balanced explicit override";
66
+ if (value === "high")
67
+ return "deeper explicit override";
68
+ if (value === "xhigh")
69
+ return "extra explicit override";
70
+ return "maximum explicit override";
71
+ }
72
+ //# sourceMappingURL=effort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effort.js","sourceRoot":"","sources":["../src/effort.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ export interface FeedbackContext {
2
+ /** Backend/model/effort the dashboard would use for a new agent. */
3
+ backend?: string;
4
+ model?: string;
5
+ effort?: string;
6
+ agents?: number;
7
+ agentsRunning?: number;
8
+ /** The most recent notice lines, oldest first. Redacted before sending. */
9
+ notices?: string[];
10
+ lastError?: string;
11
+ /** Which pane was focused, and what the user was looking at. */
12
+ focus?: string;
13
+ view?: string;
14
+ planActive?: boolean;
15
+ }
16
+ export interface FeedbackReport {
17
+ localPath: string;
18
+ posthog: boolean;
19
+ issueUrl?: string;
20
+ /** Why a destination was skipped, for an honest confirmation message. */
21
+ skipped: string[];
22
+ }
23
+ /**
24
+ * Strip anything that identifies a machine or a person from free text: home
25
+ * directories, absolute paths, and token-shaped strings. A GitHub issue is
26
+ * public, so this runs before every send — not only before PostHog.
27
+ */
28
+ export declare function redactText(text: string, home?: string): string;
29
+ export declare function feedbackDir(repoRoot: string): string;
30
+ /**
31
+ * Where issues get filed. A fork should collect its OWN feedback, so this reads
32
+ * the checkout's GitHub origin and only falls back to Rudder upstream when there
33
+ * is no GitHub remote to infer.
34
+ */
35
+ export declare function feedbackRepo(repoRoot: string): string;
36
+ /** `owner/repo` from any GitHub remote form (https, ssh, with or without .git). */
37
+ export declare function parseGithubSlug(remote: string): string | undefined;
38
+ export declare function submitFeedback(options: {
39
+ text: string;
40
+ context?: FeedbackContext;
41
+ repoRoot?: string;
42
+ /** Skip the GitHub issue (used by tests and by --no-issue). */
43
+ issue?: boolean;
44
+ }): Promise<FeedbackReport>;
45
+ export declare function issueTitle(text: string): string;
46
+ export declare function issueBody(text: string, context: FeedbackContext): string;