halfcopilot 0.0.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/dist/halfcop.d.ts +6 -0
  2. package/dist/halfcop.d.ts.map +1 -0
  3. package/dist/halfcop.js +1103 -0
  4. package/dist/halfcop.js.map +1 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +255 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/tui/app.d.ts +11 -0
  10. package/dist/tui/app.d.ts.map +1 -0
  11. package/dist/tui/app.js +105 -0
  12. package/dist/tui/app.js.map +1 -0
  13. package/dist/tui/components/ChatView.d.ts +12 -0
  14. package/dist/tui/components/ChatView.d.ts.map +1 -0
  15. package/dist/tui/components/ChatView.js +70 -0
  16. package/dist/tui/components/ChatView.js.map +1 -0
  17. package/dist/tui/components/InputField.d.ts +8 -0
  18. package/dist/tui/components/InputField.d.ts.map +1 -0
  19. package/dist/tui/components/InputField.js +24 -0
  20. package/dist/tui/components/InputField.js.map +1 -0
  21. package/dist/tui/components/StatusBar.d.ts +18 -0
  22. package/dist/tui/components/StatusBar.d.ts.map +1 -0
  23. package/dist/tui/components/StatusBar.js +57 -0
  24. package/dist/tui/components/StatusBar.js.map +1 -0
  25. package/dist/tui/components/ToolApproval.d.ts +11 -0
  26. package/dist/tui/components/ToolApproval.d.ts.map +1 -0
  27. package/dist/tui/components/ToolApproval.js +41 -0
  28. package/dist/tui/components/ToolApproval.js.map +1 -0
  29. package/dist/tui/components/index.d.ts +5 -0
  30. package/dist/tui/components/index.d.ts.map +1 -0
  31. package/dist/tui/components/index.js +5 -0
  32. package/dist/tui/components/index.js.map +1 -0
  33. package/dist/tui/index.d.ts +3 -0
  34. package/dist/tui/index.d.ts.map +1 -0
  35. package/dist/tui/index.js +3 -0
  36. package/dist/tui/index.js.map +1 -0
  37. package/package.json +43 -0
  38. package/src/__tests__/cli.test.ts +73 -0
  39. package/src/halfcop.ts +1373 -0
  40. package/src/index.ts +348 -0
  41. package/src/tui/app.tsx +160 -0
  42. package/src/tui/components/ChatView.tsx +130 -0
  43. package/src/tui/components/InputField.tsx +41 -0
  44. package/src/tui/components/StatusBar.tsx +92 -0
  45. package/src/tui/components/ToolApproval.tsx +80 -0
  46. package/src/tui/components/index.ts +4 -0
  47. package/src/tui/index.ts +7 -0
  48. package/tsconfig.json +20 -0
  49. package/tsconfig.tsbuildinfo +1 -0
  50. package/vitest.config.ts +7 -0
@@ -0,0 +1,24 @@
1
+ import React, { useState } from "react";
2
+ import { Box, Text, useInput } from "ink";
3
+ export const InputField = ({ onSubmit, isLoading, }) => {
4
+ const [value, setValue] = useState("");
5
+ useInput((input, key) => {
6
+ if (key.return) {
7
+ if (value.trim()) {
8
+ onSubmit(value.trim());
9
+ setValue("");
10
+ }
11
+ }
12
+ else if (key.backspace || key.delete) {
13
+ setValue((prev) => prev.slice(0, -1));
14
+ }
15
+ else if (input && !key.ctrl && !key.meta) {
16
+ setValue((prev) => prev + input);
17
+ }
18
+ });
19
+ return (React.createElement(Box, { borderStyle: "single", borderColor: "green", paddingX: 1 },
20
+ React.createElement(Text, { color: "green", bold: true }, "❯ "),
21
+ React.createElement(Text, { color: "white" }, value),
22
+ isLoading ? (React.createElement(Text, { color: "yellow" }, " \u23F3")) : (React.createElement(Text, { color: "gray" }, "\u2588"))));
23
+ };
24
+ //# sourceMappingURL=InputField.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputField.js","sourceRoot":"","sources":["../../../src/tui/components/InputField.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAO1C,MAAM,CAAC,MAAM,UAAU,GAA8B,CAAC,EACpD,QAAQ,EACR,SAAS,GACV,EAAE,EAAE;IACH,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEvC,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtB,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YACf,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;gBACjB,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;gBACvB,QAAQ,CAAC,EAAE,CAAC,CAAC;YACf,CAAC;QACH,CAAC;aAAM,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YACvC,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,CAAC;aAAM,IAAI,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAC3C,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;QACnC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,CACL,oBAAC,GAAG,IAAC,WAAW,EAAC,QAAQ,EAAC,WAAW,EAAC,OAAO,EAAC,QAAQ,EAAE,CAAC;QACvD,oBAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,UACrB,IAAI,CACA;QACP,oBAAC,IAAI,IAAC,KAAK,EAAC,OAAO,IAAE,KAAK,CAAQ;QACjC,SAAS,CAAC,CAAC,CAAC,CACX,oBAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,cAAU,CAC/B,CAAC,CAAC,CAAC,CACF,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM,aAAS,CAC5B,CACG,CACP,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ interface StatusBarProps {
3
+ provider: string;
4
+ model: string;
5
+ mode: string;
6
+ tokenUsage?: {
7
+ inputTokens: number;
8
+ outputTokens: number;
9
+ };
10
+ turnInfo?: {
11
+ current: number;
12
+ max: number;
13
+ };
14
+ responseTime?: number;
15
+ }
16
+ export declare const StatusBar: React.FC<StatusBarProps>;
17
+ export {};
18
+ //# sourceMappingURL=StatusBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../../src/tui/components/StatusBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAG3D,UAAU,cAAc;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AASD,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAkE9C,CAAC"}
@@ -0,0 +1,57 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { Box, Text } from "ink";
3
+ const modeColors = {
4
+ plan: "blue",
5
+ act: "yellow",
6
+ auto: "green",
7
+ review: "magenta",
8
+ };
9
+ export const StatusBar = ({ provider, model, mode, tokenUsage, turnInfo, responseTime, }) => {
10
+ const [elapsed, setElapsed] = useState("0s");
11
+ useEffect(() => {
12
+ const start = Date.now();
13
+ const timer = setInterval(() => {
14
+ const s = Math.floor((Date.now() - start) / 1000);
15
+ if (s < 60)
16
+ setElapsed(`${s}s`);
17
+ else
18
+ setElapsed(`${Math.floor(s / 60)}m${s % 60}s`);
19
+ }, 1000);
20
+ return () => clearInterval(timer);
21
+ }, []);
22
+ return (React.createElement(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, paddingY: 0 },
23
+ React.createElement(Box, { marginRight: 2 },
24
+ React.createElement(Text, { color: "cyan" }, "P:"),
25
+ React.createElement(Text, { color: "white", bold: true },
26
+ " ",
27
+ provider)),
28
+ React.createElement(Box, { marginRight: 2 },
29
+ React.createElement(Text, { color: "cyan" }, "M:"),
30
+ React.createElement(Text, { color: "white", bold: true },
31
+ " ",
32
+ model)),
33
+ React.createElement(Box, { marginRight: 1 },
34
+ React.createElement(Text, { color: modeColors[mode] || "cyan", bold: true },
35
+ "[",
36
+ mode.toUpperCase(),
37
+ "]")),
38
+ tokenUsage &&
39
+ (tokenUsage.inputTokens > 0 || tokenUsage.outputTokens > 0) && (React.createElement(Box, { marginRight: 2 },
40
+ React.createElement(Text, { color: "green" },
41
+ tokenUsage.inputTokens,
42
+ "\u2193",
43
+ tokenUsage.outputTokens,
44
+ "\u2191"))),
45
+ turnInfo && turnInfo.max > 0 && (React.createElement(Box, { marginRight: 2 },
46
+ React.createElement(Text, { color: "gray" },
47
+ turnInfo.current,
48
+ "/",
49
+ turnInfo.max))),
50
+ responseTime !== undefined && responseTime > 0 && (React.createElement(Box, { marginRight: 2 },
51
+ React.createElement(Text, { color: "gray" },
52
+ (responseTime / 1000).toFixed(1),
53
+ "s"))),
54
+ React.createElement(Box, null,
55
+ React.createElement(Text, { color: "gray" }, elapsed))));
56
+ };
57
+ //# sourceMappingURL=StatusBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusBar.js","sourceRoot":"","sources":["../../../src/tui/components/StatusBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAU,MAAM,OAAO,CAAC;AAC3D,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAiBhC,MAAM,UAAU,GAA2B;IACzC,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,QAAQ;IACb,IAAI,EAAE,OAAO;IACb,MAAM,EAAE,SAAS;CAClB,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAA6B,CAAC,EAClD,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,YAAY,GACb,EAAE,EAAE;IACH,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE7C,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;YAClD,IAAI,CAAC,GAAG,EAAE;gBAAE,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;;gBAC3B,UAAU,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,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,oBAAC,GAAG,IAAC,WAAW,EAAC,QAAQ,EAAC,WAAW,EAAC,MAAM,EAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC;QACnE,oBAAC,GAAG,IAAC,WAAW,EAAE,CAAC;YACjB,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM,SAAU;YAC5B,oBAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI;gBACrB,GAAG;gBACH,QAAQ,CACJ,CACH;QACN,oBAAC,GAAG,IAAC,WAAW,EAAE,CAAC;YACjB,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM,SAAU;YAC5B,oBAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI;gBACrB,GAAG;gBACH,KAAK,CACD,CACH;QACN,oBAAC,GAAG,IAAC,WAAW,EAAE,CAAC;YACjB,oBAAC,IAAI,IAAC,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE,IAAI;;gBACzC,IAAI,CAAC,WAAW,EAAE;oBACf,CACH;QACL,UAAU;YACT,CAAC,UAAU,CAAC,WAAW,GAAG,CAAC,IAAI,UAAU,CAAC,YAAY,GAAG,CAAC,CAAC,IAAI,CAC7D,oBAAC,GAAG,IAAC,WAAW,EAAE,CAAC;YACjB,oBAAC,IAAI,IAAC,KAAK,EAAC,OAAO;gBAChB,UAAU,CAAC,WAAW;;gBAAG,UAAU,CAAC,YAAY;yBAC5C,CACH,CACP;QACF,QAAQ,IAAI,QAAQ,CAAC,GAAG,GAAG,CAAC,IAAI,CAC/B,oBAAC,GAAG,IAAC,WAAW,EAAE,CAAC;YACjB,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM;gBACf,QAAQ,CAAC,OAAO;;gBAAG,QAAQ,CAAC,GAAG,CAC3B,CACH,CACP;QACA,YAAY,KAAK,SAAS,IAAI,YAAY,GAAG,CAAC,IAAI,CACjD,oBAAC,GAAG,IAAC,WAAW,EAAE,CAAC;YACjB,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM;gBAAE,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;oBAAS,CACzD,CACP;QACD,oBAAC,GAAG;YACF,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM,IAAE,OAAO,CAAQ,CAC/B,CACF,CACP,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ interface ToolApprovalProps {
3
+ toolName: string;
4
+ toolInput: Record<string, unknown>;
5
+ permissionLevel?: "SAFE" | "WARN" | "UNSAFE";
6
+ onApprove: () => void;
7
+ onReject: () => void;
8
+ }
9
+ export declare const ToolApproval: React.FC<ToolApprovalProps>;
10
+ export {};
11
+ //# sourceMappingURL=ToolApproval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolApproval.d.ts","sourceRoot":"","sources":["../../../src/tui/components/ToolApproval.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,UAAU,iBAAiB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC7C,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAWD,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA2DpD,CAAC"}
@@ -0,0 +1,41 @@
1
+ import React from "react";
2
+ import { Box, Text, useInput } from "ink";
3
+ const PERMISSION_INDICATORS = {
4
+ SAFE: { emoji: "🟢", color: "green", label: "SAFE" },
5
+ WARN: { emoji: "🟡", color: "yellow", label: "WARN" },
6
+ UNSAFE: { emoji: "🔴", color: "red", label: "UNSAFE" },
7
+ };
8
+ export const ToolApproval = ({ toolName, toolInput, permissionLevel = "WARN", onApprove, onReject, }) => {
9
+ useInput((input, key) => {
10
+ if (input === "y" || input === "Y") {
11
+ onApprove();
12
+ }
13
+ else if (input === "n" || input === "N") {
14
+ onReject();
15
+ }
16
+ });
17
+ const indicator = PERMISSION_INDICATORS[permissionLevel];
18
+ return (React.createElement(Box, { flexDirection: "column", borderStyle: "double", borderColor: indicator.color, padding: 1 },
19
+ React.createElement(Box, null,
20
+ React.createElement(Text, { color: "yellow", bold: true }, "\u26A0\uFE0F Tool Execution Request"),
21
+ React.createElement(Text, null, " "),
22
+ React.createElement(Text, { color: indicator.color },
23
+ indicator.emoji,
24
+ " ",
25
+ indicator.label)),
26
+ React.createElement(Box, { marginTop: 1 },
27
+ React.createElement(Text, { color: "cyan" }, "Tool: "),
28
+ React.createElement(Text, { color: "white", bold: true }, toolName)),
29
+ React.createElement(Box, { marginTop: 1 },
30
+ React.createElement(Text, { color: "cyan" }, "Input: ")),
31
+ React.createElement(Box, { marginTop: 0, paddingLeft: 2 }, Object.entries(toolInput).map(([key, value]) => (React.createElement(Box, { key: key },
32
+ React.createElement(Text, { color: "gray" },
33
+ key,
34
+ ": "),
35
+ React.createElement(Text, { color: "white" }, typeof value === "string" ? value : JSON.stringify(value)))))),
36
+ React.createElement(Box, { marginTop: 1 },
37
+ React.createElement(Text, { color: "green" }, "[Y] Approve"),
38
+ React.createElement(Text, { color: "gray" }, " "),
39
+ React.createElement(Text, { color: "red" }, "[N] Reject"))));
40
+ };
41
+ //# sourceMappingURL=ToolApproval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolApproval.js","sourceRoot":"","sources":["../../../src/tui/components/ToolApproval.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAU1C,MAAM,qBAAqB,GAGvB;IACF,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE;IACpD,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE;IACrD,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE;CACvD,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAgC,CAAC,EACxD,QAAQ,EACR,SAAS,EACT,eAAe,GAAG,MAAM,EACxB,SAAS,EACT,QAAQ,GACT,EAAE,EAAE;IACH,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtB,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YACnC,SAAS,EAAE,CAAC;QACd,CAAC;aAAM,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YAC1C,QAAQ,EAAE,CAAC;QACb,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,qBAAqB,CAAC,eAAe,CAAC,CAAC;IAEzD,OAAO,CACL,oBAAC,GAAG,IACF,aAAa,EAAC,QAAQ,EACtB,WAAW,EAAC,QAAQ,EACpB,WAAW,EAAE,SAAS,CAAC,KAAY,EACnC,OAAO,EAAE,CAAC;QAEV,oBAAC,GAAG;YACF,oBAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,EAAC,IAAI,gDAElB;YACP,oBAAC,IAAI,YAAS;YACd,oBAAC,IAAI,IAAC,KAAK,EAAE,SAAS,CAAC,KAAY;gBAChC,SAAS,CAAC,KAAK;;gBAAG,SAAS,CAAC,KAAK,CAC7B,CACH;QACN,oBAAC,GAAG,IAAC,SAAS,EAAE,CAAC;YACf,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM,aAAc;YAChC,oBAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,UACrB,QAAQ,CACJ,CACH;QACN,oBAAC,GAAG,IAAC,SAAS,EAAE,CAAC;YACf,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM,cAAe,CAC7B;QACN,oBAAC,GAAG,IAAC,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,IAC9B,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAC/C,oBAAC,GAAG,IAAC,GAAG,EAAE,GAAG;YACX,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM;gBAAE,GAAG;qBAAU;YACjC,oBAAC,IAAI,IAAC,KAAK,EAAC,OAAO,IAChB,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CACrD,CACH,CACP,CAAC,CACE;QACN,oBAAC,GAAG,IAAC,SAAS,EAAE,CAAC;YACf,oBAAC,IAAI,IAAC,KAAK,EAAC,OAAO,kBAAmB;YACtC,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM,QAAS;YAC3B,oBAAC,IAAI,IAAC,KAAK,EAAC,KAAK,iBAAkB,CAC/B,CACF,CACP,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { StatusBar } from "./StatusBar.js";
2
+ export { ChatView } from "./ChatView.js";
3
+ export { InputField } from "./InputField.js";
4
+ export { ToolApproval } from "./ToolApproval.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tui/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { StatusBar } from "./StatusBar.js";
2
+ export { ChatView } from "./ChatView.js";
3
+ export { InputField } from "./InputField.js";
4
+ export { ToolApproval } from "./ToolApproval.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tui/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { App } from "./app.js";
2
+ export { StatusBar, ChatView, InputField, ToolApproval, } from "./components/index.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tui/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EACL,SAAS,EACT,QAAQ,EACR,UAAU,EACV,YAAY,GACb,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { App } from "./app.js";
2
+ export { StatusBar, ChatView, InputField, ToolApproval, } from "./components/index.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tui/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EACL,SAAS,EACT,QAAQ,EACR,UAAU,EACV,YAAY,GACb,MAAM,uBAAuB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "halfcopilot",
3
+ "version": "0.0.1",
4
+ "description": "HalfCopilot — Multi-model Agent Framework CLI with Hybrid Mode",
5
+ "type": "module",
6
+ "bin": {
7
+ "halfcop": "./dist/index.js",
8
+ "halfcopilot": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc -b",
12
+ "dev": "tsc --watch",
13
+ "test": "vitest run",
14
+ "clean": "rm -rf dist *.tsbuildinfo"
15
+ },
16
+ "dependencies": {
17
+ "@halfcopilot/config": "file:../config",
18
+ "@halfcopilot/core": "file:../core",
19
+ "@halfcopilot/mcp": "file:../mcp",
20
+ "@halfcopilot/memory": "file:../memory",
21
+ "@halfcopilot/provider": "file:../provider",
22
+ "@halfcopilot/shared": "file:../shared",
23
+ "@halfcopilot/skills": "file:../skills",
24
+ "@halfcopilot/tools": "file:../tools",
25
+ "commander": "^12.0.0",
26
+ "ink": "^4.4.0",
27
+ "react": "^18.3.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/react": "^18.3.0",
31
+ "typescript": "^5.7.0",
32
+ "vitest": "^2.1.0"
33
+ },
34
+ "keywords": [
35
+ "agent",
36
+ "cli",
37
+ "ai",
38
+ "multi-model",
39
+ "copilot"
40
+ ],
41
+ "author": "HalfCopilot Team",
42
+ "license": "MIT"
43
+ }
@@ -0,0 +1,73 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { Command } from "commander";
3
+
4
+ describe("CLI program structure", () => {
5
+ it("should create a program with expected commands", () => {
6
+ const program = new Command();
7
+ program
8
+ .name("halfcopilot")
9
+ .description(
10
+ "HalfCopilot — Multi-model Agent Framework CLI with Hybrid Mode",
11
+ )
12
+ .version("0.0.1");
13
+
14
+ program
15
+ .command("chat")
16
+ .description("Start interactive chat mode")
17
+ .option("-m, --model <model>", "Model to use");
18
+
19
+ program
20
+ .command("run <prompt>")
21
+ .description("Run a single prompt and exit")
22
+ .option("--mode <mode>", "Agent mode");
23
+
24
+ program.command("config").description("Show configuration");
25
+
26
+ program.command("providers").description("List available providers");
27
+
28
+ program.command("skills").description("List available skills");
29
+
30
+ program
31
+ .command("doctor")
32
+ .description("Check configuration and environment");
33
+
34
+ const commands = program.commands.map((c) => c.name());
35
+ expect(commands).toContain("chat");
36
+ expect(commands).toContain("run");
37
+ expect(commands).toContain("config");
38
+ expect(commands).toContain("providers");
39
+ expect(commands).toContain("skills");
40
+ expect(commands).toContain("doctor");
41
+ expect(commands).toHaveLength(6);
42
+ });
43
+
44
+ it("should have correct program metadata", () => {
45
+ const program = new Command();
46
+ program.name("halfcopilot").version("0.0.1");
47
+ expect(program.name()).toBe("halfcopilot");
48
+ expect(program.version()).toBe("0.0.1");
49
+ });
50
+
51
+ it("should parse chat command options", () => {
52
+ const program = new Command();
53
+ program
54
+ .command("chat")
55
+ .option("-m, --model <model>", "Model to use")
56
+ .option("-p, --provider <provider>", "Provider to use")
57
+ .option("--mode <mode>", "Agent mode", "auto")
58
+ .option("--hybrid", "Enable hybrid mode");
59
+
60
+ const cmd = program.commands.find((c) => c.name() === "chat")!;
61
+ expect(cmd.options).toHaveLength(4);
62
+ });
63
+
64
+ it("should parse run command with required prompt argument", () => {
65
+ const program = new Command();
66
+ program.command("run <prompt>").description("Run a single prompt and exit");
67
+
68
+ const cmd = program.commands.find((c) => c.name() === "run")!;
69
+ expect(cmd.registeredArguments).toHaveLength(1);
70
+ expect(cmd.registeredArguments[0].name()).toBe("prompt");
71
+ expect(cmd.registeredArguments[0].required).toBe(true);
72
+ });
73
+ });