@viraatdas/rudder 2.10.6 → 2.10.8

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/README.md CHANGED
@@ -48,10 +48,9 @@ rudder
48
48
  ```
49
49
 
50
50
  With no arguments, `rudder` opens the dashboard. Type in the bottom input and
51
- press `Enter` to start a one-off agent in your main checkout. This is the default
52
- for questions, repo inspection, docs/API research, and small direct changes. Use
53
- `/plan <text>` when you want the orchestrator to plan a DAG and run isolated
54
- workers.
51
+ press `Enter` to hand the request to the orchestrator. It can answer, inspect the
52
+ repo, or plan a DAG and run isolated workers. Use `/ask <text>` when you want a
53
+ one-off agent in the main checkout with no DAG.
55
54
 
56
55
  If a task needs shared local context like API tokens, private URLs, account ids,
57
56
  or environment values, save it with `/share <text>` in the task input. Rudder
@@ -91,7 +90,7 @@ to `~/.rudder/config.json`.
91
90
  │ task list │ live Claude Code or Codex terminal │
92
91
  │ status/model │ DAG-over-orchestrator, scrollback, review │
93
92
  ├───────────────┴────────────────────────────────────────────┤
94
- │ task input: one-off agent by default, /plan for DAGs
93
+ │ task input: orchestrator by default, /ask for one-off
95
94
  └──────────────────────────────────────────────────────────────┘
96
95
  ```
97
96
 
@@ -191,13 +190,12 @@ reused next time. Rudder refreshes model metadata from
191
190
  ## One-Off and Planning
192
191
 
193
192
  Type a fresh request in the bottom task input. When no plan is active, Rudder
194
- starts one conversational agent in the main checkout and shows it in the
195
- `one-off` section. This keeps ordinary questions like "where is the app?" or "is
196
- it ready to use?" out of the DAG planner.
193
+ starts the orchestrator, which can inspect the repo, plan the work, and spawn
194
+ isolated workers when needed.
197
195
 
198
- Use `/plan <text>` for planned implementation work. This starts the orchestrator,
199
- which decomposes the work into isolated workers. `/ask <text>` is kept as an
200
- explicit alias for the default one-off path.
196
+ Use `/ask <text>` for a one-off conversational agent in the main checkout, with
197
+ no DAG. Use `/plan <text>` when you want to be explicit about the orchestrator /
198
+ DAG path; it is the same route as plain input.
201
199
 
202
200
  For planned work, Rudder runs a dedicated Claude Code orchestrator PTY with a DAG
203
201
  pane above it. The flow stays inside Rudder: the orchestrator researches
@@ -325,16 +323,6 @@ rudder sync <runId>
325
323
  rudder cleanup
326
324
  ```
327
325
 
328
- ## Other interfaces
329
-
330
- The native dashboard is the default. Older interfaces remain available:
331
-
332
- ```bash
333
- rudder tmux # legacy tmux dashboard
334
- rudder tui # Ink-based interactive TUI
335
- rudder --no-native # skip the native binary
336
- ```
337
-
338
326
  ## Troubleshooting
339
327
 
340
328
  - Stale behavior after an upgrade: restart any already-running Rudder dashboards
@@ -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,6 @@
1
+ import type { HookConfig, HookInvocation, HookResult, RudderEvent } from "./types.js";
2
+ export declare function hooksConfigPath(repoRoot: string): string;
3
+ export declare function loadHookConfig(repoRoot: string): Promise<HookConfig>;
4
+ export declare function runHooks(invocation: HookInvocation, emit?: (event: RudderEvent) => Promise<void> | void): Promise<HookResult[]>;
5
+ export declare function firstBlockingHook(results: HookResult[]): HookResult | undefined;
6
+ export declare function initHookTemplates(repoRoot: string): Promise<string>;
package/dist/hooks.js ADDED
@@ -0,0 +1,203 @@
1
+ import { spawn } from "node:child_process";
2
+ import path from "node:path";
3
+ import { projectStateDir, loadConfig } from "./state.js";
4
+ import { nowIso, readJson, writeJson } from "./util.js";
5
+ export function hooksConfigPath(repoRoot) {
6
+ return path.join(projectStateDir(repoRoot), "hooks.json");
7
+ }
8
+ export async function loadHookConfig(repoRoot) {
9
+ const config = await loadConfig();
10
+ if (config.hooks?.enabled === false) {
11
+ return { version: 1, hooks: [] };
12
+ }
13
+ const configured = config.hooks?.configPath
14
+ ? path.resolve(repoRoot, config.hooks.configPath)
15
+ : hooksConfigPath(repoRoot);
16
+ const raw = await readJson(configured);
17
+ if (!raw || raw.version !== 1 || !Array.isArray(raw.hooks)) {
18
+ return { version: 1, hooks: [] };
19
+ }
20
+ return {
21
+ version: 1,
22
+ hooks: raw.hooks.filter(isHookDefinition),
23
+ };
24
+ }
25
+ export async function runHooks(invocation, emit) {
26
+ const config = await loadHookConfig(invocation.repoRoot);
27
+ const hooks = config.hooks.filter((hook) => hook.event === invocation.event && hookMatches(hook, invocation));
28
+ const results = [];
29
+ for (const hook of hooks) {
30
+ await emit?.({
31
+ ts: nowIso(),
32
+ runId: invocation.runId ?? "hooks",
33
+ nodeId: invocation.nodeId,
34
+ type: "hook.started",
35
+ message: `hook ${hook.name ?? hook.command} on ${invocation.event}`,
36
+ data: hookData(hook),
37
+ });
38
+ const result = await runHookCommand(hook, invocation);
39
+ results.push(result);
40
+ if (result.status === "blocked") {
41
+ await emit?.({
42
+ ts: nowIso(),
43
+ runId: invocation.runId ?? "hooks",
44
+ nodeId: invocation.nodeId,
45
+ type: "hook.blocked",
46
+ message: result.reason ?? result.stderr ?? `hook blocked ${invocation.event}`,
47
+ data: hookData(hook),
48
+ });
49
+ }
50
+ else if (result.status === "failed" || result.status === "timeout") {
51
+ await emit?.({
52
+ ts: nowIso(),
53
+ runId: invocation.runId ?? "hooks",
54
+ nodeId: invocation.nodeId,
55
+ type: "hook.failed",
56
+ message: result.reason ?? result.stderr ?? `hook failed ${invocation.event}`,
57
+ data: hookData(hook),
58
+ });
59
+ }
60
+ if (isBlockingEvent(invocation.event) && result.status === "blocked") {
61
+ break;
62
+ }
63
+ }
64
+ return results;
65
+ }
66
+ export function firstBlockingHook(results) {
67
+ return results.find((result) => result.status === "blocked");
68
+ }
69
+ export async function initHookTemplates(repoRoot) {
70
+ const target = hooksConfigPath(repoRoot);
71
+ const initial = {
72
+ version: 1,
73
+ hooks: [
74
+ {
75
+ event: "PromptSubmit",
76
+ name: "secret-prompt-warning",
77
+ command: "node -e \"let s='';process.stdin.on('data',d=>s+=d);process.stdin.on('end',()=>{const j=JSON.parse(s); if(/(sk-|ghp_|github_pat_|api[_-]?key=)/i.test(j.message||'')){console.error('prompt appears to contain a secret; use rudder share instead'); process.exit(2)}})\"",
78
+ timeoutMs: 5000,
79
+ },
80
+ ],
81
+ };
82
+ await writeJson(target, initial, { mode: 0o600 });
83
+ return target;
84
+ }
85
+ function runHookCommand(hook, invocation) {
86
+ const timeoutMs = hook.timeoutMs ?? defaultTimeout(invocation.event);
87
+ const input = JSON.stringify(invocation);
88
+ return new Promise((resolve) => {
89
+ const child = spawn(hook.command, {
90
+ cwd: invocation.repoRoot,
91
+ shell: true,
92
+ stdio: ["pipe", "pipe", "pipe"],
93
+ env: {
94
+ ...process.env,
95
+ RUDDER_REPO: invocation.repoRoot,
96
+ RUDDER_EVENT: invocation.event,
97
+ ...(invocation.runId ? { RUDDER_RUN_ID: invocation.runId } : {}),
98
+ ...(invocation.nodeId ? { RUDDER_NODE_ID: invocation.nodeId } : {}),
99
+ },
100
+ });
101
+ let stdout = "";
102
+ let stderr = "";
103
+ let settled = false;
104
+ const timer = setTimeout(() => {
105
+ if (settled) {
106
+ return;
107
+ }
108
+ settled = true;
109
+ child.kill("SIGTERM");
110
+ resolve({ hook, status: "timeout", stdout, stderr, reason: `hook timed out after ${timeoutMs}ms` });
111
+ }, timeoutMs);
112
+ timer.unref?.();
113
+ child.stdout?.on("data", (chunk) => {
114
+ stdout += String(chunk);
115
+ });
116
+ child.stderr?.on("data", (chunk) => {
117
+ stderr += String(chunk);
118
+ });
119
+ child.on("error", (error) => {
120
+ if (settled) {
121
+ return;
122
+ }
123
+ settled = true;
124
+ clearTimeout(timer);
125
+ resolve({ hook, status: "failed", stdout, stderr, reason: error.message });
126
+ });
127
+ child.on("exit", (code) => {
128
+ if (settled) {
129
+ return;
130
+ }
131
+ settled = true;
132
+ clearTimeout(timer);
133
+ const parsed = parseHookJson(stdout);
134
+ const blocked = code === 2 || parsed?.decision === "block";
135
+ resolve({
136
+ hook,
137
+ status: blocked ? "blocked" : code === 0 ? "allowed" : "failed",
138
+ stdout,
139
+ stderr,
140
+ ...(blocked ? { reason: parsed?.reason ?? firstLine(stderr) ?? "blocked by hook" } : {}),
141
+ ...(code !== 0 && !blocked ? { reason: firstLine(stderr) ?? `hook exited ${code}` } : {}),
142
+ });
143
+ });
144
+ child.stdin?.end(input);
145
+ });
146
+ }
147
+ function hookMatches(hook, invocation) {
148
+ if (!hook.matcher) {
149
+ return true;
150
+ }
151
+ const haystack = `${invocation.message ?? ""}\n${JSON.stringify(invocation.data ?? {})}`;
152
+ try {
153
+ return new RegExp(hook.matcher).test(haystack);
154
+ }
155
+ catch {
156
+ return haystack.includes(hook.matcher);
157
+ }
158
+ }
159
+ function isHookDefinition(value) {
160
+ const hook = value;
161
+ return Boolean(hook && isHookEvent(hook.event) && typeof hook.command === "string" && hook.command.trim());
162
+ }
163
+ function isHookEvent(value) {
164
+ return (value === "PromptSubmit" ||
165
+ value === "RunStart" ||
166
+ value === "RunComplete" ||
167
+ value === "NodeReview" ||
168
+ value === "NodeMerged" ||
169
+ value === "MergeConflict" ||
170
+ value === "BudgetExceeded" ||
171
+ value === "WorktreeCreate" ||
172
+ value === "SessionEnd");
173
+ }
174
+ function isBlockingEvent(event) {
175
+ return event === "PromptSubmit" || event === "RunStart" || event === "WorktreeCreate";
176
+ }
177
+ function defaultTimeout(event) {
178
+ return event === "PromptSubmit" ? 30_000 : 600_000;
179
+ }
180
+ function parseHookJson(stdout) {
181
+ const trimmed = stdout.trim();
182
+ if (!trimmed.startsWith("{")) {
183
+ return null;
184
+ }
185
+ try {
186
+ return JSON.parse(trimmed);
187
+ }
188
+ catch {
189
+ return null;
190
+ }
191
+ }
192
+ function firstLine(value) {
193
+ return value.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
194
+ }
195
+ function hookData(hook) {
196
+ return {
197
+ event: hook.event,
198
+ command: hook.command,
199
+ ...(hook.name ? { name: hook.name } : {}),
200
+ ...(hook.matcher ? { matcher: hook.matcher } : {}),
201
+ };
202
+ }
203
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}