@tritard/waterbrother 0.6.0 → 0.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tritard/waterbrother",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
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/prompt.js CHANGED
@@ -8,10 +8,15 @@ export async function promptLine(label, { input = process.stdin, output = proces
8
8
  let buf = "";
9
9
  let resolved = false;
10
10
  const shouldPauseOnCleanup = typeof input.pause === "function";
11
+ const isTTY = input.isTTY && typeof input.setRawMode === "function";
12
+
11
13
  function cleanup() {
12
14
  resolved = true;
13
15
  input.removeListener("data", onData);
14
16
  if (signal) signal.removeEventListener("abort", onAbort);
17
+ if (isTTY) {
18
+ try { input.setRawMode(false); } catch {}
19
+ }
15
20
  if (shouldPauseOnCleanup) {
16
21
  try {
17
22
  input.pause();
@@ -25,10 +30,27 @@ export async function promptLine(label, { input = process.stdin, output = proces
25
30
  if (ch === "\r") continue;
26
31
  if (ch === "\n") {
27
32
  cleanup();
33
+ output.write("\n");
28
34
  resolve(buf);
29
35
  return;
30
36
  }
37
+ if (ch === "\u0003") {
38
+ cleanup();
39
+ reject(new DOMException("The operation was aborted.", "AbortError"));
40
+ return;
41
+ }
42
+ // Backspace
43
+ if (ch === "\u007f" || ch === "\b") {
44
+ if (buf.length > 0) {
45
+ buf = buf.slice(0, -1);
46
+ output.write("\b \b");
47
+ }
48
+ continue;
49
+ }
50
+ // Skip non-printable control chars
51
+ if (ch.charCodeAt(0) < 32) continue;
31
52
  buf += ch;
53
+ output.write(ch);
32
54
  }
33
55
  }
34
56
  function onAbort() {
@@ -36,6 +58,15 @@ export async function promptLine(label, { input = process.stdin, output = proces
36
58
  cleanup();
37
59
  reject(signal.reason || new DOMException("The operation was aborted.", "AbortError"));
38
60
  }
61
+
62
+ // Ensure clean stdin state before listening
63
+ if (isTTY) {
64
+ try { input.setRawMode(false); } catch {}
65
+ }
66
+ input.pause();
67
+ if (isTTY) {
68
+ input.setRawMode(true);
69
+ }
39
70
  input.on("data", onData);
40
71
  input.resume();
41
72
  if (signal) signal.addEventListener("abort", onAbort, { once: true });
package/src/router.js CHANGED
@@ -22,14 +22,15 @@ function looksLikeWorkRequest(text) {
22
22
  function inferPassFromText(text) {
23
23
  const l = lower(text);
24
24
  if (!l) return null;
25
- if (/^(ship it|accept|merge it|looks good ship it)\b/.test(l)) return { intent: "accept", confidence: "high" };
26
- if (/^(fix these|fix that|address those|redo|rebuild|try again|patch it|build it|go ahead|go$|implement it)\b/.test(l)) return { intent: "build", confidence: "high" };
27
- if (/^(check it|what'?s wrong|what is wrong|review it|challenge( harder)?|find bugs|poke holes|stress test it)\b/.test(l)) return { intent: "challenge", confidence: "high" };
28
- if (/^(what am i not thinking of|what else|what if|surprise me|invent|give me alternatives|think wider|out of the box)\b/.test(l)) return { intent: "invent", confidence: "high" };
29
- if (/^(should we|which should|which option|what should we|think deeper|tell me more about|compare |tradeoff|jwt or |sessions or |oauth|oauth2)\b/.test(l)) return { intent: "decide", confidence: "medium" };
25
+ // Decision detail must be checked before generic "decide" "tell me more about 2" is detail, not decide
30
26
  if (/^[1-9]\d*$/.test(l)) return { intent: "choose", confidence: "high", optionIndex: Number.parseInt(l, 10) };
31
27
  const moreMatch = l.match(/^(tell me more about|more on|details on|expand)\s+(\d+)\b/);
32
28
  if (moreMatch) return { intent: "decision-detail", confidence: "high", optionIndex: Number.parseInt(moreMatch[2], 10) };
29
+ if (/^(ship it|accept|merge it|looks good ship it)\b/.test(l)) return { intent: "accept", confidence: "high" };
30
+ if (/^(rebuild|try again|patch it|build it|go ahead|implement it)\b/.test(l)) return { intent: "build", confidence: "high" };
31
+ if (/^(check it|what'?s wrong|what is wrong|review it|challenge( harder)?|find bugs|poke holes|stress test it)\b/.test(l)) return { intent: "challenge", confidence: "high" };
32
+ if (/^(what am i not thinking of|what else|what if|surprise me|invent|give me alternatives|think wider|out of the box)\b/.test(l)) return { intent: "invent", confidence: "high" };
33
+ if (/^(should we|which should|which option|what should we|think deeper|compare |tradeoff|jwt or |sessions or |oauth|oauth2)\b/.test(l)) return { intent: "decide", confidence: "medium" };
33
34
  if (/^ignore\b/.test(l)) return { intent: "ignore", confidence: "medium" };
34
35
  return null;
35
36
  }
@@ -62,8 +63,13 @@ export function routeNaturalInput(text, { task = null } = {}) {
62
63
  return { kind: "choose-recommended-and-build", confidence: "high", raw };
63
64
  }
64
65
  }
66
+ if (task.state === "build-ready") {
67
+ if (/^(go|go ahead|do it|build it)\b/i.test(raw)) {
68
+ return { kind: "build", confidence: "high", raw };
69
+ }
70
+ }
65
71
  if (task.state === "review-ready") {
66
- if (/^(fix these|fix them|address those|redo)\b/i.test(raw)) {
72
+ if (/^(fix these|fix them|fix that|address those|redo)\b/i.test(raw)) {
67
73
  return { kind: "fix-review-findings", confidence: "high", raw };
68
74
  }
69
75
  if (/^(think deeper|re-think|rethink|step back)\b/i.test(raw)) {