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.
- package/dist/halfcop.d.ts +6 -0
- package/dist/halfcop.d.ts.map +1 -0
- package/dist/halfcop.js +1103 -0
- package/dist/halfcop.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +255 -0
- package/dist/index.js.map +1 -0
- package/dist/tui/app.d.ts +11 -0
- package/dist/tui/app.d.ts.map +1 -0
- package/dist/tui/app.js +105 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/components/ChatView.d.ts +12 -0
- package/dist/tui/components/ChatView.d.ts.map +1 -0
- package/dist/tui/components/ChatView.js +70 -0
- package/dist/tui/components/ChatView.js.map +1 -0
- package/dist/tui/components/InputField.d.ts +8 -0
- package/dist/tui/components/InputField.d.ts.map +1 -0
- package/dist/tui/components/InputField.js +24 -0
- package/dist/tui/components/InputField.js.map +1 -0
- package/dist/tui/components/StatusBar.d.ts +18 -0
- package/dist/tui/components/StatusBar.d.ts.map +1 -0
- package/dist/tui/components/StatusBar.js +57 -0
- package/dist/tui/components/StatusBar.js.map +1 -0
- package/dist/tui/components/ToolApproval.d.ts +11 -0
- package/dist/tui/components/ToolApproval.d.ts.map +1 -0
- package/dist/tui/components/ToolApproval.js +41 -0
- package/dist/tui/components/ToolApproval.js.map +1 -0
- package/dist/tui/components/index.d.ts +5 -0
- package/dist/tui/components/index.d.ts.map +1 -0
- package/dist/tui/components/index.js +5 -0
- package/dist/tui/components/index.js.map +1 -0
- package/dist/tui/index.d.ts +3 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +3 -0
- package/dist/tui/index.js.map +1 -0
- package/package.json +43 -0
- package/src/__tests__/cli.test.ts +73 -0
- package/src/halfcop.ts +1373 -0
- package/src/index.ts +348 -0
- package/src/tui/app.tsx +160 -0
- package/src/tui/components/ChatView.tsx +130 -0
- package/src/tui/components/InputField.tsx +41 -0
- package/src/tui/components/StatusBar.tsx +92 -0
- package/src/tui/components/ToolApproval.tsx +80 -0
- package/src/tui/components/index.ts +4 -0
- package/src/tui/index.ts +7 -0
- package/tsconfig.json +20 -0
- package/tsconfig.tsbuildinfo +1 -0
- 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":"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"}
|
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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
|
+
});
|