@tritard/waterbrother 0.9.5 → 0.9.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tritard/waterbrother",
3
- "version": "0.9.5",
3
+ "version": "0.9.7",
4
4
  "description": "Waterbrother: Grok-powered coding CLI with local tools, sessions, operator modes, and approval controls",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -6207,43 +6207,57 @@ async function promptLoop(agent, session, context) {
6207
6207
  }
6208
6208
 
6209
6209
  if (line === "/experiment" || line.startsWith("/experiment ")) {
6210
- const goalArg = line.replace("/experiment", "").trim();
6210
+ // Parse: /experiment <goal> --metric "command" --attempts N --time M
6211
+ const rawArgs = line.replace("/experiment", "").trim();
6212
+ if (!rawArgs) {
6213
+ console.log('Usage: /experiment <goal> --metric "npm test" --attempts 5 --time 30');
6214
+ console.log(' --metric command to measure (required)');
6215
+ console.log(' --attempts max attempts, 0=infinite (default: 0)');
6216
+ console.log(' --time time limit in minutes (default: 60)');
6217
+ continue;
6218
+ }
6219
+
6220
+ // Extract flags — parse --metric value (everything until next -- flag or end)
6221
+ const attemptsMatch = rawArgs.match(/--attempts\s+(\d+)/);
6222
+ const timeMatch = rawArgs.match(/--time\s+(\d+)/);
6223
+ // Remove --attempts and --time first so --metric can grab the rest
6224
+ let cleaned = rawArgs.replace(/--attempts\s+\d+/g, "").replace(/--time\s+\d+/g, "");
6225
+ let metricCmd = "";
6226
+ const metricIdx = cleaned.indexOf("--metric");
6227
+ if (metricIdx !== -1) {
6228
+ const afterMetric = cleaned.slice(metricIdx + 8).trim();
6229
+ // If quoted, take the quoted content; otherwise take everything until end
6230
+ if (afterMetric.startsWith('"')) {
6231
+ const endQuote = afterMetric.indexOf('"', 1);
6232
+ metricCmd = endQuote > 0 ? afterMetric.slice(1, endQuote) : afterMetric.slice(1);
6233
+ } else {
6234
+ metricCmd = afterMetric;
6235
+ }
6236
+ cleaned = cleaned.slice(0, metricIdx).trim();
6237
+ }
6238
+ const goalArg = cleaned.trim();
6239
+
6211
6240
  if (!goalArg) {
6212
- console.log("Usage: /experiment <goal> (e.g. /experiment speed up the test suite)");
6241
+ console.log("experiment needs a goal");
6213
6242
  continue;
6214
6243
  }
6215
6244
 
6216
- // Build charter
6217
6245
  const charter = parseCharterFromGoal(goalArg);
6218
6246
 
6219
- // Ask for metric command
6220
- if (!charter.metric.command) {
6221
- try {
6222
- const metricCmd = await promptLine("metric command (e.g. npm test, python bench.py): ", { input: process.stdin, output: process.stdout });
6223
- charter.metric.command = metricCmd.trim();
6224
- } catch {
6225
- console.log("experiment canceled");
6226
- continue;
6227
- }
6228
- if (!charter.metric.command) {
6229
- console.log("no metric command — experiment canceled");
6230
- continue;
6231
- }
6247
+ if (metricCmd) {
6248
+ charter.metric.command = metricCmd.trim();
6249
+ }
6250
+ if (attemptsMatch) {
6251
+ charter.budget.maxAttempts = parseInt(attemptsMatch[1], 10);
6252
+ }
6253
+ if (timeMatch) {
6254
+ charter.budget.maxMinutes = parseInt(timeMatch[1], 10);
6232
6255
  }
6233
6256
 
6234
- // Ask for attempt budget (0 = infinite, runs until Ctrl+C or time limit)
6235
- try {
6236
- const attemptsStr = await promptLine("max attempts (0 = run until interrupted) [0]: ", { input: process.stdin, output: process.stdout });
6237
- const parsed = parseInt(attemptsStr.trim(), 10);
6238
- if (parsed > 0) charter.budget.maxAttempts = parsed;
6239
- } catch {}
6240
-
6241
- // Ask for time budget
6242
- try {
6243
- const timeStr = await promptLine(`time limit in minutes [${charter.budget.maxMinutes}]: `, { input: process.stdin, output: process.stdout });
6244
- const parsed = parseInt(timeStr.trim(), 10);
6245
- if (parsed > 0) charter.budget.maxMinutes = parsed;
6246
- } catch {}
6257
+ if (!charter.metric.command) {
6258
+ console.log('experiment needs a metric: /experiment <goal> --metric "npm test"');
6259
+ continue;
6260
+ }
6247
6261
 
6248
6262
  const isInfinite = !charter.budget.maxAttempts || charter.budget.maxAttempts <= 0;
6249
6263
  console.log(`────────────────────────────────────────────────────────────`);
package/src/experiment.js CHANGED
@@ -63,12 +63,12 @@ export async function runMetric({ command, extract, cwd }) {
63
63
  let stdout, stderr;
64
64
 
65
65
  if (isWin) {
66
- const result = await execFileAsync(command, [], { ...execOpts, shell: true });
66
+ // Use PowerShell on Windows for access to proper commands
67
+ const result = await execFileAsync("powershell.exe", ["-NoProfile", "-Command", command], execOpts);
67
68
  stdout = String(result.stdout || "");
68
69
  stderr = String(result.stderr || "");
69
70
  } else {
70
- const parts = command.split(/\s+/);
71
- const result = await execFileAsync(parts[0], parts.slice(1), execOpts);
71
+ const result = await execFileAsync("/bin/sh", ["-c", command], execOpts);
72
72
  stdout = String(result.stdout || "");
73
73
  stderr = String(result.stderr || "");
74
74
  }