@zhijiewang/openharness 0.1.0 → 0.1.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 (50) hide show
  1. package/.github/workflows/ci.yml +31 -0
  2. package/README.md +5 -3
  3. package/dist/components/Markdown.d.ts +4 -0
  4. package/dist/components/Markdown.d.ts.map +1 -0
  5. package/dist/components/Markdown.js +47 -0
  6. package/dist/components/Markdown.js.map +1 -0
  7. package/dist/components/Messages.d.ts.map +1 -1
  8. package/dist/components/Messages.js +10 -9
  9. package/dist/components/Messages.js.map +1 -1
  10. package/dist/components/PermissionPrompt.d.ts +2 -2
  11. package/dist/components/PermissionPrompt.d.ts.map +1 -1
  12. package/dist/components/PermissionPrompt.js +7 -6
  13. package/dist/components/PermissionPrompt.js.map +1 -1
  14. package/dist/components/REPL.d.ts +2 -1
  15. package/dist/components/REPL.d.ts.map +1 -1
  16. package/dist/components/REPL.js +128 -82
  17. package/dist/components/REPL.js.map +1 -1
  18. package/dist/components/Spinner.d.ts +1 -2
  19. package/dist/components/Spinner.d.ts.map +1 -1
  20. package/dist/components/Spinner.js +11 -2
  21. package/dist/components/Spinner.js.map +1 -1
  22. package/dist/components/StatusBar.d.ts +7 -0
  23. package/dist/components/StatusBar.d.ts.map +1 -0
  24. package/dist/components/StatusBar.js +6 -0
  25. package/dist/components/StatusBar.js.map +1 -0
  26. package/dist/components/ToolCallDisplay.d.ts +5 -4
  27. package/dist/components/ToolCallDisplay.d.ts.map +1 -1
  28. package/dist/components/ToolCallDisplay.js +3 -5
  29. package/dist/components/ToolCallDisplay.js.map +1 -1
  30. package/dist/harness/cost.test.d.ts +2 -0
  31. package/dist/harness/cost.test.d.ts.map +1 -0
  32. package/dist/harness/cost.test.js +43 -0
  33. package/dist/harness/cost.test.js.map +1 -0
  34. package/dist/harness/rules.test.d.ts +2 -0
  35. package/dist/harness/rules.test.d.ts.map +1 -0
  36. package/dist/harness/rules.test.js +25 -0
  37. package/dist/harness/rules.test.js.map +1 -0
  38. package/dist/harness/session.test.d.ts +2 -0
  39. package/dist/harness/session.test.d.ts.map +1 -0
  40. package/dist/harness/session.test.js +37 -0
  41. package/dist/harness/session.test.js.map +1 -0
  42. package/dist/tools/file-edit.test.d.ts +2 -0
  43. package/dist/tools/file-edit.test.d.ts.map +1 -0
  44. package/dist/tools/file-edit.test.js +35 -0
  45. package/dist/tools/file-edit.test.js.map +1 -0
  46. package/dist/types/permissions.test.d.ts +2 -0
  47. package/dist/types/permissions.test.d.ts.map +1 -0
  48. package/dist/types/permissions.test.js +34 -0
  49. package/dist/types/permissions.test.js.map +1 -0
  50. package/package.json +3 -1
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ node-version: [18, 20, 22]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Use Node.js ${{ matrix.node-version }}
20
+ uses: actions/setup-node@v4
21
+ with:
22
+ node-version: ${{ matrix.node-version }}
23
+ cache: npm
24
+
25
+ - run: npm ci
26
+
27
+ - name: Type check
28
+ run: npx tsc --noEmit
29
+
30
+ - name: Run tests
31
+ run: npm test
package/README.md CHANGED
@@ -23,7 +23,7 @@ Open-source terminal coding agent. Build your own Claude Code with any LLM.
23
23
  ## Quick Start
24
24
 
25
25
  ```bash
26
- npm install -g openharness
26
+ npm install -g @zhijiewang/openharness
27
27
  oh
28
28
  ```
29
29
 
@@ -36,13 +36,15 @@ oh --model gpt-4o # use OpenAI (needs OPENAI_API_KEY)
36
36
  oh --trust # auto-approve all tool calls
37
37
  ```
38
38
 
39
+ <!-- ![Demo](assets/demo.gif) -->
40
+
39
41
  ## Install
40
42
 
41
43
  Requires **Node.js 18+**.
42
44
 
43
45
  ```bash
44
- # From npm (when published)
45
- npm install -g openharness
46
+ # From npm
47
+ npm install -g @zhijiewang/openharness
46
48
 
47
49
  # From source
48
50
  git clone https://github.com/zhijiewong/openharness.git
@@ -0,0 +1,4 @@
1
+ export default function Markdown({ children }: {
2
+ children: string;
3
+ }): import("react/jsx-runtime").JSX.Element | null;
4
+ //# sourceMappingURL=Markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Markdown.d.ts","sourceRoot":"","sources":["../../src/components/Markdown.tsx"],"names":[],"mappings":"AAMA,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,kDAoBlE"}
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from "ink";
3
+ import { marked } from "marked";
4
+ const MD_RE = /[#*`|[\]>\-_~]|\n\n/;
5
+ export default function Markdown({ children }) {
6
+ if (!children.trim())
7
+ return null;
8
+ if (!MD_RE.test(children)) {
9
+ return _jsx(Text, { children: children });
10
+ }
11
+ let tokens;
12
+ try {
13
+ tokens = marked.lexer(children);
14
+ }
15
+ catch {
16
+ return _jsx(Text, { children: children });
17
+ }
18
+ return (_jsx(Box, { flexDirection: "column", children: tokens.map((t, i) => (_jsx(TokenView, { token: t }, i))) }));
19
+ }
20
+ function TokenView({ token }) {
21
+ switch (token.type) {
22
+ case "heading":
23
+ return (_jsxs(Text, { bold: true, color: "cyan", children: ["#".repeat(token.depth ?? 1), " ", cleanInline(token.text ?? "")] }));
24
+ case "paragraph":
25
+ return _jsx(Text, { children: cleanInline(token.text ?? "") });
26
+ case "code":
27
+ return (_jsxs(Box, { flexDirection: "column", marginY: 0, children: [token.lang ? (_jsxs(Text, { dimColor: true, children: ["```", token.lang] })) : (_jsx(Text, { dimColor: true, children: "```" })), _jsx(Text, { dimColor: true, children: token.text ?? "" }), _jsx(Text, { dimColor: true, children: "```" })] }));
28
+ case "list":
29
+ return (_jsx(Box, { flexDirection: "column", children: (token.items ?? []).map((item, i) => (_jsxs(Text, { children: [" ", token.ordered ? `${i + 1}.` : "•", " ", cleanInline(item.text ?? "")] }, i))) }));
30
+ case "blockquote":
31
+ return _jsxs(Text, { dimColor: true, children: ["\u2502 ", cleanInline(token.text ?? "")] });
32
+ case "hr":
33
+ return _jsx(Text, { dimColor: true, children: "─".repeat(40) });
34
+ case "space":
35
+ return null;
36
+ default:
37
+ return token.text ? _jsx(Text, { children: cleanInline(token.text) }) : null;
38
+ }
39
+ }
40
+ function cleanInline(text) {
41
+ return text
42
+ .replace(/\*\*(.+?)\*\*/g, "$1")
43
+ .replace(/\*(.+?)\*/g, "$1")
44
+ .replace(/`(.+?)`/g, "$1")
45
+ .replace(/\[(.+?)\]\((.+?)\)/g, "$1 ($2)");
46
+ }
47
+ //# sourceMappingURL=Markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Markdown.js","sourceRoot":"","sources":["../../src/components/Markdown.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,MAAM,KAAK,GAAG,qBAAqB,CAAC;AAEpC,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAwB;IACjE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAC,IAAI,cAAE,QAAQ,GAAQ,CAAC;IACjC,CAAC;IAED,IAAI,MAAa,CAAC;IAClB,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAC,IAAI,cAAE,QAAQ,GAAQ,CAAC;IACjC,CAAC;IAED,OAAO,CACL,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,YACxB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,CAAS,EAAE,EAAE,CAAC,CACjC,KAAC,SAAS,IAAS,KAAK,EAAE,CAAC,IAAX,CAAC,CAAc,CAChC,CAAC,GACE,CACP,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,EAAE,KAAK,EAAkB;IAC1C,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,SAAS;YACZ,OAAO,CACL,MAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,MAAM,aACpB,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,OAAG,WAAW,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,IACxD,CACR,CAAC;QAEJ,KAAK,WAAW;YACd,OAAO,KAAC,IAAI,cAAE,WAAW,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,GAAQ,CAAC;QAEtD,KAAK,MAAM;YACT,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,OAAO,EAAE,CAAC,aACnC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CACZ,MAAC,IAAI,IAAC,QAAQ,mBAAE,KAAK,EAAE,KAAK,CAAC,IAAI,IAAQ,CAC1C,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,IAAC,QAAQ,kBAAE,KAAK,GAAQ,CAC9B,EACD,KAAC,IAAI,IAAC,QAAQ,kBAAE,KAAK,CAAC,IAAI,IAAI,EAAE,GAAQ,EACxC,KAAC,IAAI,IAAC,QAAQ,kBAAE,KAAK,GAAQ,IACzB,CACP,CAAC;QAEJ,KAAK,MAAM;YACT,OAAO,CACL,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,YACxB,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAS,EAAE,CAAS,EAAE,EAAE,CAAC,CACjD,MAAC,IAAI,eACF,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,OAAG,WAAW,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,KAD9D,CAAC,CAEL,CACR,CAAC,GACE,CACP,CAAC;QAEJ,KAAK,YAAY;YACf,OAAO,MAAC,IAAI,IAAC,QAAQ,8BAAI,WAAW,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,IAAQ,CAAC;QAEjE,KAAK,IAAI;YACP,OAAO,KAAC,IAAI,IAAC,QAAQ,kBAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAQ,CAAC;QAEhD,KAAK,OAAO;YACV,OAAO,IAAI,CAAC;QAEd;YACE,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAC,IAAI,cAAE,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,GAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IACtE,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI;SACR,OAAO,CAAC,gBAAgB,EAAE,IAAI,CAAC;SAC/B,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC;SACzB,OAAO,CAAC,qBAAqB,EAAE,SAAS,CAAC,CAAC;AAC/C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Messages.d.ts","sourceRoot":"","sources":["../../src/components/Messages.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D,KAAK,aAAa,GAAG;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,aAAa,2CAQtE"}
1
+ {"version":3,"file":"Messages.d.ts","sourceRoot":"","sources":["../../src/components/Messages.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D,KAAK,aAAa,GAAG;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,aAAa,2CActE"}
@@ -1,23 +1,24 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
2
3
  import { Box, Text } from "ink";
3
4
  import ToolCallDisplay from "./ToolCallDisplay.js";
5
+ import Markdown from "./Markdown.js";
4
6
  export default function Messages({ messages, toolCalls }) {
5
- return (_jsx(Box, { flexDirection: "column", children: messages.map((msg) => (_jsx(MessageRow, { message: msg, toolCalls: toolCalls }, msg.uuid))) }));
7
+ return (_jsx(Box, { flexDirection: "column", children: messages.map((msg, i) => {
8
+ const showDivider = msg.role === "user" && i > 0;
9
+ return (_jsxs(React.Fragment, { children: [showDivider && _jsx(Text, { dimColor: true, children: "─".repeat(50) }), _jsx(MessageRow, { message: msg, toolCalls: toolCalls })] }, msg.uuid));
10
+ }) }));
6
11
  }
7
12
  function MessageRow({ message, toolCalls, }) {
8
- const { role, content } = message;
9
- if (role === "user") {
10
- return (_jsxs(Box, { marginY: 0, children: [_jsx(Text, { color: "cyan", bold: true, children: "❯ " }), _jsx(Text, { bold: true, children: content })] }));
13
+ if (message.role === "user") {
14
+ return (_jsxs(Box, { marginY: 0, children: [_jsx(Text, { color: "cyan", bold: true, children: "❯ " }), _jsx(Text, { bold: true, children: message.content })] }));
11
15
  }
12
- if (role === "assistant") {
13
- return (_jsxs(Box, { flexDirection: "column", marginY: 0, children: [content ? (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", bold: true, children: "◆ " }), _jsx(Text, { children: content })] })) : null, message.toolCalls?.map((tc) => {
16
+ if (message.role === "assistant") {
17
+ return (_jsxs(Box, { flexDirection: "column", marginY: 0, children: [message.content ? (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", bold: true, children: "◆ " }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(Markdown, { children: message.content }) })] })) : null, message.toolCalls?.map((tc) => {
14
18
  const state = toolCalls.get(tc.id);
15
19
  return state ? _jsx(ToolCallDisplay, { toolCall: state }, tc.id) : null;
16
20
  })] }));
17
21
  }
18
- if (role === "tool") {
19
- return null; // Tool results shown inline via ToolCallDisplay
20
- }
21
22
  return null;
22
23
  }
23
24
  //# sourceMappingURL=Messages.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../src/components/Messages.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAGhC,OAAO,eAAe,MAAM,sBAAsB,CAAC;AAOnD,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAiB;IACrE,OAAO,CACL,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,YACxB,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACrB,KAAC,UAAU,IAAgB,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,IAA5C,GAAG,CAAC,IAAI,CAAwC,CAClE,CAAC,GACE,CACP,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,EAClB,OAAO,EACP,SAAS,GAIV;IACC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAElC,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,CACL,MAAC,GAAG,IAAC,OAAO,EAAE,CAAC,aACb,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,IAAI,kBAAE,IAAI,GAAQ,EACrC,KAAC,IAAI,IAAC,IAAI,kBAAE,OAAO,GAAQ,IACvB,CACP,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QACzB,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,OAAO,EAAE,CAAC,aACnC,OAAO,CAAC,CAAC,CAAC,CACT,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,KAAK,EAAC,SAAS,EAAC,IAAI,kBAAE,IAAI,GAAQ,EACxC,KAAC,IAAI,cAAE,OAAO,GAAQ,IAClB,CACP,CAAC,CAAC,CAAC,IAAI,EACP,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;oBAC7B,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;oBACnC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAC,eAAe,IAAa,QAAQ,EAAE,KAAK,IAAtB,EAAE,CAAC,EAAE,CAAqB,CAAC,CAAC,CAAC,IAAI,CAAC;gBACzE,CAAC,CAAC,IACE,CACP,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC,CAAC,gDAAgD;IAC/D,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../src/components/Messages.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAGhC,OAAO,eAAe,MAAM,sBAAsB,CAAC;AACnD,OAAO,QAAQ,MAAM,eAAe,CAAC;AAOrC,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAiB;IACrE,OAAO,CACL,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,YACxB,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;YACvB,MAAM,WAAW,GAAG,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;YACjD,OAAO,CACL,MAAC,KAAK,CAAC,QAAQ,eACZ,WAAW,IAAI,KAAC,IAAI,IAAC,QAAQ,kBAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAQ,EACtD,KAAC,UAAU,IAAC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,GAAI,KAF/B,GAAG,CAAC,IAAI,CAGZ,CAClB,CAAC;QACJ,CAAC,CAAC,GACE,CACP,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,EAClB,OAAO,EACP,SAAS,GAIV;IACC,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,CACL,MAAC,GAAG,IAAC,OAAO,EAAE,CAAC,aACb,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,IAAI,kBAAE,IAAI,GAAQ,EACrC,KAAC,IAAI,IAAC,IAAI,kBAAE,OAAO,CAAC,OAAO,GAAQ,IAC/B,CACP,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACjC,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,OAAO,EAAE,CAAC,aACnC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CACjB,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,KAAK,EAAC,SAAS,EAAC,IAAI,kBAAE,IAAI,GAAQ,EACxC,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC,YACrC,KAAC,QAAQ,cAAE,OAAO,CAAC,OAAO,GAAY,GAClC,IACF,CACP,CAAC,CAAC,CAAC,IAAI,EACP,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;oBAC7B,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;oBACnC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAC,eAAe,IAAa,QAAQ,EAAE,KAAK,IAAtB,EAAE,CAAC,EAAE,CAAqB,CAAC,CAAC,CAAC,IAAI,CAAC;gBACzE,CAAC,CAAC,IACE,CACP,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1,9 +1,9 @@
1
- type PermissionPromptProps = {
1
+ type Props = {
2
2
  toolName: string;
3
3
  description: string;
4
4
  riskLevel: string;
5
5
  onResolve: (allowed: boolean) => void;
6
6
  };
7
- export default function PermissionPrompt({ toolName, description, riskLevel, onResolve, }: PermissionPromptProps): import("react/jsx-runtime").JSX.Element;
7
+ export default function PermissionPrompt({ toolName, description, riskLevel, onResolve, }: Props): import("react/jsx-runtime").JSX.Element;
8
8
  export {};
9
9
  //# sourceMappingURL=PermissionPrompt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PermissionPrompt.d.ts","sourceRoot":"","sources":["../../src/components/PermissionPrompt.tsx"],"names":[],"mappings":"AAGA,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC,CAAC;AAQF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,QAAQ,EACR,WAAW,EACX,SAAS,EACT,SAAS,GACV,EAAE,qBAAqB,2CAwBvB"}
1
+ {"version":3,"file":"PermissionPrompt.d.ts","sourceRoot":"","sources":["../../src/components/PermissionPrompt.tsx"],"names":[],"mappings":"AAGA,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC,CAAC;AAQF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,QAAQ,EACR,WAAW,EACX,SAAS,EACT,SAAS,GACV,EAAE,KAAK,2CAqCP"}
@@ -1,18 +1,19 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text, useInput } from "ink";
3
- const riskColor = {
3
+ const RISK_COLORS = {
4
4
  low: "green",
5
5
  medium: "yellow",
6
6
  high: "red",
7
7
  };
8
8
  export default function PermissionPrompt({ toolName, description, riskLevel, onResolve, }) {
9
9
  useInput((input) => {
10
- if (input.toLowerCase() === "y")
10
+ const key = input.toLowerCase();
11
+ if (key === "y")
11
12
  onResolve(true);
12
- if (input.toLowerCase() === "n")
13
+ if (key === "n")
13
14
  onResolve(false);
14
15
  });
15
- const color = riskColor[riskLevel] ?? "white";
16
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: color, paddingX: 1, children: [_jsxs(Text, { bold: true, color: color, children: ["Permission Request [", riskLevel.toUpperCase(), "]"] }), _jsxs(Text, { children: ["Tool: ", _jsx(Text, { bold: true, children: toolName })] }), _jsx(Text, { dimColor: true, children: description }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: ["Allow? [", _jsx(Text, { color: "green", bold: true, children: "Y" }), "/", _jsx(Text, { color: "red", bold: true, children: "N" }), "]"] }) })] }));
16
+ const color = RISK_COLORS[riskLevel] ?? "white";
17
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: color, paddingX: 2, paddingY: 0, marginY: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: color, bold: true, children: " " }), _jsx(Text, { bold: true, children: toolName }), _jsxs(Text, { dimColor: true, children: [" ", riskLevel, " risk"] })] }), _jsx(Box, { marginLeft: 2, marginY: 0, children: _jsx(Text, { children: description.slice(0, 300) }) }), _jsx(Box, { marginTop: 0, children: _jsxs(Text, { children: ["Allow? [", _jsx(Text, { color: "green", bold: true, children: "Y" }), "/", _jsx(Text, { color: "red", bold: true, children: "N" }), "]", " "] }) })] }));
17
18
  }
18
19
  //# sourceMappingURL=PermissionPrompt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PermissionPrompt.js","sourceRoot":"","sources":["../../src/components/PermissionPrompt.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAS1C,MAAM,SAAS,GAA2B;IACxC,GAAG,EAAE,OAAO;IACZ,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,KAAK;CACZ,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,QAAQ,EACR,WAAW,EACX,SAAS,EACT,SAAS,GACa;IACtB,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE;QACjB,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,GAAG;YAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,GAAG;YAAE,SAAS,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC;IAE9C,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,WAAW,EAAC,OAAO,EAAC,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,aAC7E,MAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAE,KAAK,qCACA,SAAS,CAAC,WAAW,EAAE,SACvC,EACP,MAAC,IAAI,yBACG,KAAC,IAAI,IAAC,IAAI,kBAAE,QAAQ,GAAQ,IAC7B,EACP,KAAC,IAAI,IAAC,QAAQ,kBAAE,WAAW,GAAQ,EACnC,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,MAAC,IAAI,2BACK,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,wBAAS,OAAC,KAAC,IAAI,IAAC,KAAK,EAAC,KAAK,EAAC,IAAI,wBAAS,SACpE,GACH,IACF,CACP,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"PermissionPrompt.js","sourceRoot":"","sources":["../../src/components/PermissionPrompt.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAS1C,MAAM,WAAW,GAA2B;IAC1C,GAAG,EAAE,OAAO;IACZ,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,KAAK;CACZ,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,QAAQ,EACR,WAAW,EACX,SAAS,EACT,SAAS,GACH;IACN,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE;QACjB,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAChC,IAAI,GAAG,KAAK,GAAG;YAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,GAAG,KAAK,GAAG;YAAE,SAAS,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC;IAEhD,OAAO,CACL,MAAC,GAAG,IACF,aAAa,EAAC,QAAQ,EACtB,WAAW,EAAC,OAAO,EACnB,WAAW,EAAE,KAAK,EAClB,QAAQ,EAAE,CAAC,EACX,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,CAAC,aAEV,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,KAAK,EAAE,KAAK,EAAE,IAAI,kBACrB,IAAI,GACA,EACP,KAAC,IAAI,IAAC,IAAI,kBAAE,QAAQ,GAAQ,EAC5B,MAAC,IAAI,IAAC,QAAQ,mBAAE,GAAG,EAAE,SAAS,aAAa,IACvC,EAEN,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,YAC5B,KAAC,IAAI,cAAE,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAQ,GACpC,EAEN,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,MAAC,IAAI,2BACK,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,wBAAS,OAAC,KAAC,IAAI,IAAC,KAAK,EAAC,KAAK,EAAC,IAAI,wBAAS,OAAE,GAAG,IACzE,GACH,IACF,CACP,CAAC;AACJ,CAAC"}
@@ -9,7 +9,8 @@ type REPLProps = {
9
9
  systemPrompt: string;
10
10
  model?: string;
11
11
  initialMessages?: Message[];
12
+ resumeSessionId?: string;
12
13
  };
13
- export default function REPL({ provider, tools, permissionMode, systemPrompt, model, initialMessages, }: REPLProps): import("react/jsx-runtime").JSX.Element;
14
+ export default function REPL({ provider, tools, permissionMode, systemPrompt, model, initialMessages, resumeSessionId, }: REPLProps): import("react/jsx-runtime").JSX.Element;
14
15
  export {};
15
16
  //# sourceMappingURL=REPL.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"REPL.d.ts","sourceRoot":"","sources":["../../src/components/REPL.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAS9D,KAAK,SAAS,GAAG;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;CAC7B,CAAC;AAkBF,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,EAC3B,QAAQ,EACR,KAAK,EACL,cAAc,EACd,YAAY,EACZ,KAAK,EACL,eAAe,GAChB,EAAE,SAAS,2CAiKX"}
1
+ {"version":3,"file":"REPL.d.ts","sourceRoot":"","sources":["../../src/components/REPL.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAW9D,KAAK,SAAS,GAAG;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAkBF,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,EAC3B,QAAQ,EACR,KAAK,EACL,cAAc,EACd,YAAY,EACZ,KAAK,EACL,eAAe,EACf,eAAe,GAChB,EAAE,SAAS,2CAoNX"}
@@ -1,8 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useCallback } from "react";
2
+ import { useState, useCallback, useRef, useEffect } from "react";
3
3
  import { Box, Text, useApp } from "ink";
4
4
  import { createAssistantMessage, createUserMessage } from "../types/message.js";
5
5
  import { query } from "../query.js";
6
+ import { createSession, saveSession, loadSession } from "../harness/session.js";
7
+ import { CostTracker, estimateCost } from "../harness/cost.js";
6
8
  import Messages from "./Messages.js";
7
9
  import Spinner from "./Spinner.js";
8
10
  import TextInput from "./TextInput.js";
@@ -15,100 +17,144 @@ const BANNER = ` ___
15
17
  ))(( \\___/|_| |___|_|\\_|_||_/_/ \\_\\_|_\\_|\\_|___|___/___/
16
18
  (( ))
17
19
  \`--\``;
18
- export default function REPL({ provider, tools, permissionMode, systemPrompt, model, initialMessages, }) {
20
+ export default function REPL({ provider, tools, permissionMode, systemPrompt, model, initialMessages, resumeSessionId, }) {
19
21
  const { exit } = useApp();
20
- const [messages, setMessages] = useState(initialMessages ?? []);
22
+ // Session and cost tracking
23
+ const sessionRef = useRef(resumeSessionId
24
+ ? (() => { try {
25
+ return loadSession(resumeSessionId);
26
+ }
27
+ catch {
28
+ return createSession("unknown", model ?? "");
29
+ } })()
30
+ : createSession("unknown", model ?? ""));
31
+ const costRef = useRef(new CostTracker());
32
+ const [totalCost, setTotalCost] = useState(0);
33
+ const [sessionId] = useState(sessionRef.current.id);
34
+ const [messages, setMessages] = useState(resumeSessionId ? sessionRef.current.messages : (initialMessages ?? []));
21
35
  const [loading, setLoading] = useState(false);
22
36
  const [streamingText, setStreamingText] = useState("");
23
37
  const [toolCalls, setToolCalls] = useState(new Map());
24
38
  const [pendingPermission, setPendingPermission] = useState(null);
25
39
  const [error, setError] = useState(null);
26
40
  const [currentModel, setCurrentModel] = useState(model ?? "");
27
- const handleSubmit = useCallback(async (input) => {
28
- const trimmed = input.trim();
29
- if (trimmed === "exit" || trimmed === "quit" || trimmed === "/exit" || trimmed === "/quit") {
30
- exit();
41
+ // Save session on exit
42
+ useEffect(() => {
43
+ return () => {
44
+ sessionRef.current.messages = messages;
45
+ sessionRef.current.totalCost = costRef.current.totalCost;
46
+ try {
47
+ saveSession(sessionRef.current);
48
+ }
49
+ catch { /* ignore */ }
50
+ };
51
+ }, [messages]);
52
+ // Queue prompt submissions — useEffect picks them up for async processing
53
+ const pendingPromptRef = useRef(null);
54
+ const [submitCount, setSubmitCount] = useState(0);
55
+ const messagesRef = useRef(messages);
56
+ messagesRef.current = messages;
57
+ useEffect(() => {
58
+ const prompt = pendingPromptRef.current;
59
+ if (!prompt || loading)
31
60
  return;
32
- }
33
- setLoading(true);
34
- setStreamingText("");
35
- setError(null);
36
- setToolCalls(new Map());
37
- const userMsg = createUserMessage(input);
38
- setMessages((prev) => [...prev, userMsg]);
39
- const askUser = (toolName, description) => {
40
- return new Promise((resolve) => {
41
- setPendingPermission({
42
- toolName,
43
- description,
44
- riskLevel: "medium",
45
- resolve: (allowed) => {
46
- setPendingPermission(null);
47
- resolve(allowed);
48
- },
61
+ pendingPromptRef.current = null;
62
+ const run = async () => {
63
+ setLoading(true);
64
+ setStreamingText("");
65
+ setError(null);
66
+ setToolCalls(new Map());
67
+ const askUser = (toolName, description) => {
68
+ return new Promise((resolve) => {
69
+ setPendingPermission({
70
+ toolName,
71
+ description,
72
+ riskLevel: "medium",
73
+ resolve: (allowed) => {
74
+ setPendingPermission(null);
75
+ resolve(allowed);
76
+ },
77
+ });
49
78
  });
50
- });
51
- };
52
- const config = {
53
- provider,
54
- tools,
55
- systemPrompt,
56
- permissionMode,
57
- askUser,
58
- };
59
- let accumulatedText = "";
60
- try {
61
- for await (const event of query(input, config, messages)) {
62
- switch (event.type) {
63
- case "text_delta":
64
- accumulatedText += event.content;
65
- setStreamingText(accumulatedText);
66
- break;
67
- case "tool_call_start":
68
- setToolCalls((prev) => {
69
- const next = new Map(prev);
70
- next.set(event.callId, {
71
- callId: event.callId,
72
- toolName: event.toolName,
73
- status: "running",
79
+ };
80
+ const config = {
81
+ provider,
82
+ tools,
83
+ systemPrompt,
84
+ permissionMode,
85
+ askUser,
86
+ };
87
+ let accumulated = "";
88
+ try {
89
+ for await (const event of query(prompt, config, messagesRef.current)) {
90
+ switch (event.type) {
91
+ case "text_delta":
92
+ accumulated += event.content;
93
+ setStreamingText(accumulated);
94
+ break;
95
+ case "tool_call_start":
96
+ setToolCalls((prev) => {
97
+ const next = new Map(prev);
98
+ next.set(event.callId, {
99
+ callId: event.callId,
100
+ toolName: event.toolName,
101
+ status: "running",
102
+ });
103
+ return next;
74
104
  });
75
- return next;
76
- });
77
- break;
78
- case "tool_call_end":
79
- setToolCalls((prev) => {
80
- const next = new Map(prev);
81
- next.set(event.callId, {
82
- callId: event.callId,
83
- toolName: next.get(event.callId)?.toolName ?? "unknown",
84
- status: event.isError ? "error" : "done",
85
- output: event.output,
105
+ break;
106
+ case "tool_call_complete":
107
+ break;
108
+ case "tool_call_end":
109
+ setToolCalls((prev) => {
110
+ const next = new Map(prev);
111
+ next.set(event.callId, {
112
+ callId: event.callId,
113
+ toolName: next.get(event.callId)?.toolName ?? "unknown",
114
+ status: event.isError ? "error" : "done",
115
+ output: event.output,
116
+ });
117
+ return next;
86
118
  });
87
- return next;
88
- });
89
- break;
90
- case "cost_update":
91
- setCurrentModel(event.model);
92
- break;
93
- case "error":
94
- setError(event.message);
95
- break;
96
- case "turn_complete":
97
- if (accumulatedText) {
98
- setMessages((prev) => [...prev, createAssistantMessage(accumulatedText)]);
99
- }
100
- break;
119
+ break;
120
+ case "cost_update":
121
+ setCurrentModel(event.model);
122
+ costRef.current.record("provider", event.model, event.inputTokens, event.outputTokens, event.cost || estimateCost(event.model, event.inputTokens, event.outputTokens));
123
+ setTotalCost(costRef.current.totalCost);
124
+ break;
125
+ case "error":
126
+ setError(event.message);
127
+ break;
128
+ case "turn_complete":
129
+ if (accumulated) {
130
+ setMessages((prev) => [...prev, createAssistantMessage(accumulated)]);
131
+ }
132
+ break;
133
+ }
101
134
  }
102
135
  }
136
+ catch (err) {
137
+ setError(err instanceof Error ? err.message : String(err));
138
+ }
139
+ finally {
140
+ setLoading(false);
141
+ setStreamingText("");
142
+ }
143
+ };
144
+ run();
145
+ }, [submitCount, loading, provider, tools, systemPrompt, permissionMode]);
146
+ const handleSubmit = useCallback((input) => {
147
+ const trimmed = input.trim();
148
+ if (trimmed === "exit" || trimmed === "quit" || trimmed === "/exit" || trimmed === "/quit") {
149
+ exit();
150
+ return;
103
151
  }
104
- catch (err) {
105
- setError(err instanceof Error ? err.message : String(err));
106
- }
107
- finally {
108
- setLoading(false);
109
- setStreamingText("");
110
- }
111
- }, [provider, tools, systemPrompt, permissionMode, messages, exit]);
112
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: "magenta", children: BANNER }), _jsxs(Box, { children: [_jsx(Text, { bold: true, color: "magenta", children: "OpenHarness" }), _jsx(Text, { dimColor: true, children: " v0.1.0" }), _jsx(Text, { color: "cyan", children: currentModel ? ` ${currentModel}` : "" }), _jsx(Text, { dimColor: true, children: ` (${permissionMode})` })] }), _jsx(Text, { dimColor: true, children: "─".repeat(60) })] }), _jsx(Messages, { messages: messages, toolCalls: toolCalls }), loading && streamingText && (_jsxs(Box, { marginY: 0, flexDirection: "column", children: [_jsx(Text, { color: "magenta", bold: true, children: "◆ " }), _jsx(Text, { children: streamingText })] })), loading && !streamingText && _jsx(Spinner, { model: currentModel }), error && (_jsx(Box, { marginY: 1, borderStyle: "round", borderColor: "red", paddingX: 1, children: _jsxs(Text, { color: "red", children: ["\u2717 ", error] }) })), pendingPermission && (_jsx(PermissionPrompt, { toolName: pendingPermission.toolName, description: pendingPermission.description, riskLevel: pendingPermission.riskLevel, onResolve: pendingPermission.resolve })), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { onSubmit: handleSubmit, disabled: loading }) })] }));
152
+ const userMsg = createUserMessage(input);
153
+ setMessages((prev) => [...prev, userMsg]);
154
+ pendingPromptRef.current = input;
155
+ // Increment counter to trigger useEffect (refs don't cause re-renders)
156
+ setSubmitCount((c) => c + 1);
157
+ }, [exit]);
158
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: "magenta", children: BANNER }), _jsxs(Box, { children: [_jsx(Text, { bold: true, color: "magenta", children: "OpenHarness" }), _jsx(Text, { dimColor: true, children: " v0.1.0" }), _jsx(Text, { color: "cyan", children: currentModel ? ` ${currentModel}` : "" }), _jsx(Text, { dimColor: true, children: ` (${permissionMode})` })] }), _jsxs(Text, { dimColor: true, children: ["session ", sessionId, totalCost > 0 ? ` | $${totalCost.toFixed(4)}` : ""] }), _jsx(Text, { dimColor: true, children: "─".repeat(60) })] }), _jsx(Messages, { messages: messages, toolCalls: toolCalls }), loading && streamingText && (_jsxs(Box, { marginY: 0, children: [_jsx(Text, { color: "magenta", bold: true, children: "◆ " }), _jsx(Text, { children: streamingText })] })), loading && !streamingText && _jsx(Spinner, { model: currentModel }), error && (_jsx(Box, { marginY: 1, borderStyle: "round", borderColor: "red", paddingX: 1, children: _jsxs(Text, { color: "red", children: ["✗ ", error] }) })), pendingPermission && (_jsx(PermissionPrompt, { toolName: pendingPermission.toolName, description: pendingPermission.description, riskLevel: pendingPermission.riskLevel, onResolve: pendingPermission.resolve })), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { onSubmit: handleSubmit, disabled: loading }) })] }));
113
159
  }
114
160
  //# sourceMappingURL=REPL.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"REPL.js","sourceRoot":"","sources":["../../src/components/REPL.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"REPL.js","sourceRoot":"","sources":["../../src/components/REPL.tsx"],"names":[],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
  type SpinnerProps = {
2
- label?: string;
3
2
  model?: string;
4
3
  };
5
- export default function Spinner({ label, model }: SpinnerProps): import("react/jsx-runtime").JSX.Element;
4
+ export default function Spinner({ model }: SpinnerProps): import("react/jsx-runtime").JSX.Element;
6
5
  export {};
7
6
  //# sourceMappingURL=Spinner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Spinner.d.ts","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":"AAIA,KAAK,YAAY,GAAG;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,YAAY,2CAc7D"}
1
+ {"version":3,"file":"Spinner.d.ts","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":"AAIA,KAAK,YAAY,GAAG;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,YAAY,2CAsBtD"}
@@ -1,7 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
2
3
  import { Box, Text } from "ink";
3
4
  import InkSpinner from "ink-spinner";
4
- export default function Spinner({ label, model }) {
5
- return (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", children: _jsx(InkSpinner, { type: "dots" }) }), _jsxs(Text, { dimColor: true, children: [" ", label ?? "Thinking", model ? ` (${model})` : "", "..."] })] }));
5
+ export default function Spinner({ model }) {
6
+ const [elapsed, setElapsed] = useState(0);
7
+ useEffect(() => {
8
+ const start = Date.now();
9
+ const timer = setInterval(() => {
10
+ setElapsed(Math.floor((Date.now() - start) / 1000));
11
+ }, 1000);
12
+ return () => clearInterval(timer);
13
+ }, []);
14
+ return (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", children: _jsx(InkSpinner, { type: "dots" }) }), _jsxs(Text, { dimColor: true, children: [" ", "Thinking", model ? ` (${model})` : "", elapsed > 0 ? ` ${elapsed}s` : "", "..."] })] }));
6
15
  }
7
16
  //# sourceMappingURL=Spinner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Spinner.js","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,UAAU,MAAM,aAAa,CAAC;AAOrC,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,EAAgB;IAC5D,OAAO,CACL,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,KAAK,EAAC,SAAS,YACnB,KAAC,UAAU,IAAC,IAAI,EAAC,MAAM,GAAG,GACrB,EACP,MAAC,IAAI,IAAC,QAAQ,mBACX,GAAG,EACH,KAAK,IAAI,UAAU,EACnB,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,WAEtB,IACH,CACP,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"Spinner.js","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,UAAU,MAAM,aAAa,CAAC;AAMrC,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,EAAE,KAAK,EAAgB;IACrD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAE1C,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC7B,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACtD,CAAC,EAAE,IAAI,CAAC,CAAC;QACT,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CACL,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,KAAK,EAAC,SAAS,YACnB,KAAC,UAAU,IAAC,IAAI,EAAC,MAAM,GAAG,GACrB,EACP,MAAC,IAAI,IAAC,QAAQ,mBACX,GAAG,cAAU,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,EACvC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,WAC7B,IACH,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ type Props = {
2
+ model?: string;
3
+ permissionMode: string;
4
+ };
5
+ export default function StatusBar({ model, permissionMode }: Props): import("react/jsx-runtime").JSX.Element;
6
+ export {};
7
+ //# sourceMappingURL=StatusBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../src/components/StatusBar.tsx"],"names":[],"mappings":"AAGA,KAAK,KAAK,GAAG;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,KAAK,2CAQjE"}
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box, Text } from "ink";
3
+ export default function StatusBar({ model, permissionMode }) {
4
+ return (_jsx(Box, { marginTop: 0, children: _jsx(Text, { dimColor: true, children: "─".repeat(60) }) }));
5
+ }
6
+ //# sourceMappingURL=StatusBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusBar.js","sourceRoot":"","sources":["../../src/components/StatusBar.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAOhC,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAAE,KAAK,EAAE,cAAc,EAAS;IAChE,OAAO,CACL,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,KAAC,IAAI,IAAC,QAAQ,kBACX,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GACV,GACH,CACP,CAAC;AACJ,CAAC"}
@@ -1,12 +1,13 @@
1
- type ToolCallState = {
1
+ export type ToolCallState = {
2
2
  callId: string;
3
3
  toolName: string;
4
4
  status: "running" | "done" | "error";
5
5
  output?: string;
6
+ args?: string;
6
7
  };
7
- type ToolCallDisplayProps = {
8
+ type Props = {
8
9
  toolCall: ToolCallState;
9
10
  };
10
- export default function ToolCallDisplay({ toolCall }: ToolCallDisplayProps): import("react/jsx-runtime").JSX.Element;
11
- export type { ToolCallState };
11
+ export default function ToolCallDisplay({ toolCall }: Props): import("react/jsx-runtime").JSX.Element;
12
+ export {};
12
13
  //# sourceMappingURL=ToolCallDisplay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToolCallDisplay.d.ts","sourceRoot":"","sources":["../../src/components/ToolCallDisplay.tsx"],"names":[],"mappings":"AAIA,KAAK,aAAa,GAAG;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,QAAQ,EAAE,aAAa,CAAC;CACzB,CAAC;AAIF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,QAAQ,EAAE,EAAE,oBAAoB,2CA2BzE;AAQD,YAAY,EAAE,aAAa,EAAE,CAAC"}
1
+ {"version":3,"file":"ToolCallDisplay.d.ts","sourceRoot":"","sources":["../../src/components/ToolCallDisplay.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,aAAa,CAAC;CACzB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,2CA4B1D"}
@@ -1,13 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
3
  import InkSpinner from "ink-spinner";
4
- const MAX_OUTPUT_LINES = 8;
5
4
  export default function ToolCallDisplay({ toolCall }) {
6
- const { toolName, status, output } = toolCall;
7
- const icon = status === "running" ? (_jsxs(Text, { color: "yellow", children: [_jsx(InkSpinner, { type: "dots" }), " "] })) : status === "error" ? (_jsx(Text, { color: "red", children: "✗ " })) : (_jsx(Text, { color: "green", children: "✓ " }));
8
- return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginY: 0, children: [_jsxs(Box, { children: [icon, _jsx(Text, { color: "yellow", bold: true, children: toolName }), status === "running" && _jsx(Text, { dimColor: true, children: " ..." })] }), output != null && status !== "running" && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { color: status === "error" ? "red" : "gray", dimColor: true, children: truncateOutput(output, MAX_OUTPUT_LINES) }) }))] }));
5
+ const { toolName, status, output, args } = toolCall;
6
+ return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginY: 0, children: [_jsxs(Box, { children: [status === "running" ? (_jsxs(Text, { color: "yellow", children: [_jsx(InkSpinner, { type: "dots" }), " "] })) : status === "error" ? (_jsx(Text, { color: "red", children: "✗ " })) : (_jsx(Text, { color: "green", children: "✓ " })), _jsx(Text, { color: "yellow", bold: true, children: toolName }), status === "running" && args && (_jsxs(Text, { dimColor: true, children: [" ", args.slice(0, 60), args.length > 60 ? "..." : ""] }))] }), output != null && status !== "running" && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { color: status === "error" ? "red" : "gray", dimColor: true, children: truncate(output, 3) }) }))] }));
9
7
  }
10
- function truncateOutput(text, maxLines) {
8
+ function truncate(text, maxLines) {
11
9
  const lines = text.split("\n");
12
10
  if (lines.length <= maxLines)
13
11
  return text;
@@ -1 +1 @@
1
- {"version":3,"file":"ToolCallDisplay.js","sourceRoot":"","sources":["../../src/components/ToolCallDisplay.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,UAAU,MAAM,aAAa,CAAC;AAarC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,QAAQ,EAAwB;IACxE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC;IAE9C,MAAM,IAAI,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAClC,MAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,aAAC,KAAC,UAAU,IAAC,IAAI,EAAC,MAAM,GAAG,EAAC,GAAG,IAAQ,CAC5D,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CACvB,KAAC,IAAI,IAAC,KAAK,EAAC,KAAK,YAAE,IAAI,GAAQ,CAChC,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,YAAE,IAAI,GAAQ,CAClC,CAAC;IAEF,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,aACnD,MAAC,GAAG,eACD,IAAI,EACL,KAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,EAAC,IAAI,kBAAE,QAAQ,GAAQ,EAC1C,MAAM,KAAK,SAAS,IAAI,KAAC,IAAI,IAAC,QAAQ,kBAAE,MAAM,GAAQ,IACnD,EACL,MAAM,IAAI,IAAI,IAAI,MAAM,KAAK,SAAS,IAAI,CACzC,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,YAChB,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,kBACvD,cAAc,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACpC,GACH,CACP,IACG,CACP,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,IAAY,EAAE,QAAgB;IACpD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,UAAU,KAAK,CAAC,MAAM,SAAS,CAAC;AAC/E,CAAC"}
1
+ {"version":3,"file":"ToolCallDisplay.js","sourceRoot":"","sources":["../../src/components/ToolCallDisplay.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,UAAU,MAAM,aAAa,CAAC;AAcrC,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,QAAQ,EAAS;IACzD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC;IAEpD,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,aACnD,MAAC,GAAG,eACD,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CACtB,MAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,aAAC,KAAC,UAAU,IAAC,IAAI,EAAC,MAAM,GAAG,EAAC,GAAG,IAAQ,CAC5D,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CACvB,KAAC,IAAI,IAAC,KAAK,EAAC,KAAK,YAAE,IAAI,GAAQ,CAChC,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,YAAE,IAAI,GAAQ,CAClC,EACD,KAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,EAAC,IAAI,kBAAE,QAAQ,GAAQ,EAC1C,MAAM,KAAK,SAAS,IAAI,IAAI,IAAI,CAC/B,MAAC,IAAI,IAAC,QAAQ,mBAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAQ,CAC9E,IACG,EAEL,MAAM,IAAI,IAAI,IAAI,MAAM,KAAK,SAAS,IAAI,CACzC,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,YAChB,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,kBACvD,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,GACf,GACH,CACP,IACG,CACP,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,QAAgB;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,UAAU,KAAK,CAAC,MAAM,SAAS,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cost.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost.test.d.ts","sourceRoot":"","sources":["../../src/harness/cost.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,43 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { CostTracker, estimateCost } from "./cost.js";
4
+ test("record() updates totals", () => {
5
+ const t = new CostTracker();
6
+ t.record("openai", "gpt-4o", 100, 50, 0.01);
7
+ assert.equal(t.events.length, 1);
8
+ assert.equal(t.modelUsage.get("gpt-4o").requests, 1);
9
+ });
10
+ test("totalCost, totalInputTokens, totalOutputTokens", () => {
11
+ const t = new CostTracker();
12
+ t.record("openai", "gpt-4o", 100, 50, 0.01);
13
+ t.record("openai", "gpt-4o", 200, 100, 0.02);
14
+ assert.equal(t.totalCost, 0.03);
15
+ assert.equal(t.totalInputTokens, 300);
16
+ assert.equal(t.totalOutputTokens, 150);
17
+ });
18
+ test("isOverBudget() with budget set", () => {
19
+ const t = new CostTracker(0.01);
20
+ t.record("openai", "gpt-4o", 100, 50, 0.02);
21
+ assert.equal(t.isOverBudget(), true);
22
+ });
23
+ test("isOverBudget() returns false with no budget", () => {
24
+ const t = new CostTracker();
25
+ t.record("openai", "gpt-4o", 100, 50, 999);
26
+ assert.equal(t.isOverBudget(), false);
27
+ });
28
+ test("formatSummary() returns a string", () => {
29
+ const t = new CostTracker(1.0);
30
+ t.record("openai", "gpt-4o", 1000, 500, 0.05);
31
+ const s = t.formatSummary();
32
+ assert.equal(typeof s, "string");
33
+ assert.ok(s.includes("Total cost"));
34
+ assert.ok(s.includes("gpt-4o"));
35
+ });
36
+ test("estimateCost() with known model returns > 0", () => {
37
+ const c = estimateCost("gpt-4o", 1_000_000, 1_000_000);
38
+ assert.ok(c > 0);
39
+ });
40
+ test("estimateCost() with unknown model returns 0", () => {
41
+ assert.equal(estimateCost("unknown-model", 1000, 1000), 0);
42
+ });
43
+ //# sourceMappingURL=cost.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost.test.js","sourceRoot":"","sources":["../../src/harness/cost.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rules.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.test.d.ts","sourceRoot":"","sources":["../../src/harness/rules.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtempSync, existsSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { loadRules, createRulesFile } from "./rules.js";
7
+ test("loadRules() returns empty array when no .oh dir exists", () => {
8
+ const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
9
+ const rules = loadRules(tmp);
10
+ assert.deepEqual(rules, []);
11
+ });
12
+ test("createRulesFile() creates .oh/RULES.md", () => {
13
+ const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
14
+ const path = createRulesFile(tmp);
15
+ assert.ok(existsSync(path));
16
+ assert.ok(path.endsWith("RULES.md"));
17
+ });
18
+ test("loadRules() finds created file", () => {
19
+ const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
20
+ createRulesFile(tmp);
21
+ const rules = loadRules(tmp);
22
+ assert.equal(rules.length, 1);
23
+ assert.ok(rules[0].includes("Project Rules"));
24
+ });
25
+ //# sourceMappingURL=rules.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.test.js","sourceRoot":"","sources":["../../src/harness/rules.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAExD,IAAI,CAAC,wDAAwD,EAAE,GAAG,EAAE;IAClE,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC9B,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;IAClD,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5B,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;AACvC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC1C,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;IACpD,eAAe,CAAC,GAAG,CAAC,CAAC;IACrB,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=session.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.test.d.ts","sourceRoot":"","sources":["../../src/harness/session.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtempSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { createSession, saveSession, loadSession, listSessions } from "./session.js";
7
+ test("createSession() creates with id and empty messages", () => {
8
+ const s = createSession("openai", "gpt-4o");
9
+ assert.ok(s.id.length > 0);
10
+ assert.deepEqual(s.messages, []);
11
+ assert.equal(s.provider, "openai");
12
+ assert.equal(s.model, "gpt-4o");
13
+ });
14
+ test("saveSession() + loadSession() roundtrip", () => {
15
+ const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
16
+ const s = createSession("anthropic", "claude-sonnet-4-6");
17
+ saveSession(s, tmp);
18
+ const loaded = loadSession(s.id, tmp);
19
+ assert.equal(loaded.id, s.id);
20
+ assert.equal(loaded.provider, "anthropic");
21
+ assert.equal(loaded.model, "claude-sonnet-4-6");
22
+ });
23
+ test("listSessions() returns saved sessions sorted by updatedAt", async () => {
24
+ const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
25
+ const s1 = createSession("openai", "gpt-4o");
26
+ const s2 = createSession("openai", "gpt-4o-mini");
27
+ saveSession(s1, tmp);
28
+ // Small delay so updatedAt differs
29
+ await new Promise((r) => setTimeout(r, 20));
30
+ saveSession(s2, tmp);
31
+ const list = listSessions(tmp);
32
+ assert.equal(list.length, 2);
33
+ // Most recent first
34
+ assert.equal(list[0].id, s2.id);
35
+ assert.equal(list[1].id, s1.id);
36
+ });
37
+ //# sourceMappingURL=session.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.test.js","sourceRoot":"","sources":["../../src/harness/session.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAErF,IAAI,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAC9D,MAAM,CAAC,GAAG,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC5C,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3B,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACnC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACnD,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,aAAa,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;IAC1D,WAAW,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpB,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACtC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC3C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;IAC3E,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;IACpD,MAAM,EAAE,GAAG,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC7C,MAAM,EAAE,GAAG,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IAClD,WAAW,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACrB,mCAAmC;IACnC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC5C,WAAW,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAErB,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC7B,oBAAoB;IACpB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=file-edit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-edit.test.d.ts","sourceRoot":"","sources":["../../src/tools/file-edit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,35 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtempSync, writeFileSync, readFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { FileEditTool } from "./FileEditTool/index.js";
7
+ const ctx = { workingDir: process.cwd() };
8
+ test("replace string in file", async () => {
9
+ const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
10
+ const f = join(tmp, "test.txt");
11
+ writeFileSync(f, "hello world");
12
+ const r = await FileEditTool.call({ file_path: f, old_string: "hello", new_string: "goodbye" }, ctx);
13
+ assert.equal(r.isError, false);
14
+ assert.equal(readFileSync(f, "utf-8"), "goodbye world");
15
+ });
16
+ test("returns error when old_string not found", async () => {
17
+ const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
18
+ const f = join(tmp, "test.txt");
19
+ writeFileSync(f, "hello world");
20
+ const r = await FileEditTool.call({ file_path: f, old_string: "missing", new_string: "x" }, ctx);
21
+ assert.equal(r.isError, true);
22
+ assert.ok(r.output.includes("not found"));
23
+ });
24
+ test("returns error when old_string not unique unless replace_all", async () => {
25
+ const tmp = mkdtempSync(join(tmpdir(), "oh-test-"));
26
+ const f = join(tmp, "test.txt");
27
+ writeFileSync(f, "aaa bbb aaa");
28
+ const r1 = await FileEditTool.call({ file_path: f, old_string: "aaa", new_string: "ccc" }, ctx);
29
+ assert.equal(r1.isError, true);
30
+ assert.ok(r1.output.includes("not unique"));
31
+ const r2 = await FileEditTool.call({ file_path: f, old_string: "aaa", new_string: "ccc", replace_all: true }, ctx);
32
+ assert.equal(r2.isError, false);
33
+ assert.equal(readFileSync(f, "utf-8"), "ccc bbb ccc");
34
+ });
35
+ //# sourceMappingURL=file-edit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-edit.test.js","sourceRoot":"","sources":["../../src/tools/file-edit.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=permissions.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.test.d.ts","sourceRoot":"","sources":["../../src/types/permissions.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,34 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { checkPermission } from "./permissions.js";
4
+ test("trust mode allows everything", () => {
5
+ const r = checkPermission("trust", "high", false);
6
+ assert.equal(r.allowed, true);
7
+ assert.equal(r.reason, "trust-mode");
8
+ });
9
+ test("deny mode blocks non-low", () => {
10
+ const r = checkPermission("deny", "medium", false);
11
+ assert.equal(r.allowed, false);
12
+ assert.equal(r.reason, "deny-mode");
13
+ });
14
+ test("deny mode allows low+readonly", () => {
15
+ const r = checkPermission("deny", "low", true);
16
+ assert.equal(r.allowed, true);
17
+ assert.equal(r.reason, "auto-approved");
18
+ });
19
+ test("ask mode: low+readonly auto-approved", () => {
20
+ const r = checkPermission("ask", "low", true);
21
+ assert.equal(r.allowed, true);
22
+ assert.equal(r.reason, "auto-approved");
23
+ });
24
+ test("ask mode: high risk returns needs-approval", () => {
25
+ const r = checkPermission("ask", "high", false);
26
+ assert.equal(r.allowed, false);
27
+ assert.equal(r.reason, "needs-approval");
28
+ });
29
+ test("ask mode: medium risk returns needs-approval", () => {
30
+ const r = checkPermission("ask", "medium", false);
31
+ assert.equal(r.allowed, false);
32
+ assert.equal(r.reason, "needs-approval");
33
+ });
34
+ //# sourceMappingURL=permissions.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.test.js","sourceRoot":"","sources":["../../src/types/permissions.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,IAAI,CAAC,8BAA8B,EAAE,GAAG,EAAE;IACxC,MAAM,CAAC,GAAG,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AACvC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACpC,MAAM,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IACnD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+BAA+B,EAAE,GAAG,EAAE;IACzC,MAAM,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sCAAsC,EAAE,GAAG,EAAE;IAChD,MAAM,CAAC,GAAG,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAC9C,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4CAA4C,EAAE,GAAG,EAAE;IACtD,MAAM,CAAC,GAAG,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAChD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAC3C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;IACxD,MAAM,CAAC,GAAG,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAC3C,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhijiewang/openharness",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Open-source terminal coding agent. Build your own Claude Code with any LLM.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,11 +17,13 @@
17
17
  "start": "node dist/main.js"
18
18
  },
19
19
  "dependencies": {
20
+ "@types/marked": "^5.0.2",
20
21
  "chalk": "^5.4.1",
21
22
  "commander": "^13.0.0",
22
23
  "ink": "^5.2.0",
23
24
  "ink-spinner": "^5.0.0",
24
25
  "ink-text-input": "^6.0.0",
26
+ "marked": "^17.0.5",
25
27
  "react": "^18.3.1",
26
28
  "yaml": "^2.7.0",
27
29
  "zod": "^3.24.0"