fraude-code 0.1.0
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/README.md +68 -0
- package/dist/index.js +179297 -0
- package/package.json +88 -0
- package/src/agent/agent.ts +475 -0
- package/src/agent/contextManager.ts +141 -0
- package/src/agent/index.ts +14 -0
- package/src/agent/pendingChanges.ts +270 -0
- package/src/agent/prompts/AskPrompt.txt +10 -0
- package/src/agent/prompts/FastPrompt.txt +40 -0
- package/src/agent/prompts/PlannerPrompt.txt +51 -0
- package/src/agent/prompts/ReviewerPrompt.txt +57 -0
- package/src/agent/prompts/WorkerPrompt.txt +33 -0
- package/src/agent/subagents/askAgent.ts +37 -0
- package/src/agent/subagents/extractionAgent.ts +123 -0
- package/src/agent/subagents/fastAgent.ts +45 -0
- package/src/agent/subagents/managerAgent.ts +36 -0
- package/src/agent/subagents/relationAgent.ts +76 -0
- package/src/agent/subagents/researchSubAgent.ts +79 -0
- package/src/agent/subagents/reviewerSubAgent.ts +42 -0
- package/src/agent/subagents/workerSubAgent.ts +42 -0
- package/src/agent/tools/bashTool.ts +94 -0
- package/src/agent/tools/descriptions/bash.txt +47 -0
- package/src/agent/tools/descriptions/edit.txt +7 -0
- package/src/agent/tools/descriptions/glob.txt +4 -0
- package/src/agent/tools/descriptions/grep.txt +8 -0
- package/src/agent/tools/descriptions/lsp.txt +20 -0
- package/src/agent/tools/descriptions/plan.txt +3 -0
- package/src/agent/tools/descriptions/read.txt +9 -0
- package/src/agent/tools/descriptions/todo.txt +12 -0
- package/src/agent/tools/descriptions/write.txt +8 -0
- package/src/agent/tools/editTool.ts +44 -0
- package/src/agent/tools/globTool.ts +59 -0
- package/src/agent/tools/grepTool.ts +343 -0
- package/src/agent/tools/lspTool.ts +429 -0
- package/src/agent/tools/planTool.ts +118 -0
- package/src/agent/tools/readTool.ts +78 -0
- package/src/agent/tools/rememberTool.ts +91 -0
- package/src/agent/tools/testRunnerTool.ts +77 -0
- package/src/agent/tools/testTool.ts +44 -0
- package/src/agent/tools/todoTool.ts +224 -0
- package/src/agent/tools/writeTool.ts +33 -0
- package/src/commands/COMMANDS.ts +38 -0
- package/src/commands/cerebras/auth.ts +27 -0
- package/src/commands/cerebras/index.ts +31 -0
- package/src/commands/forget.ts +29 -0
- package/src/commands/google/auth.ts +24 -0
- package/src/commands/google/index.ts +31 -0
- package/src/commands/groq/add_model.ts +60 -0
- package/src/commands/groq/auth.ts +24 -0
- package/src/commands/groq/index.ts +33 -0
- package/src/commands/index.ts +65 -0
- package/src/commands/knowledge.ts +92 -0
- package/src/commands/log.ts +32 -0
- package/src/commands/mistral/auth.ts +27 -0
- package/src/commands/mistral/index.ts +31 -0
- package/src/commands/model/index.ts +145 -0
- package/src/commands/models/index.ts +16 -0
- package/src/commands/ollama/index.ts +29 -0
- package/src/commands/openrouter/add_model.ts +64 -0
- package/src/commands/openrouter/auth.ts +24 -0
- package/src/commands/openrouter/index.ts +33 -0
- package/src/commands/remember.ts +48 -0
- package/src/commands/serve.ts +31 -0
- package/src/commands/session/index.ts +21 -0
- package/src/commands/usage.ts +15 -0
- package/src/commands/visualize.ts +773 -0
- package/src/components/App.tsx +55 -0
- package/src/components/IntroComponent.tsx +70 -0
- package/src/components/LoaderComponent.tsx +68 -0
- package/src/components/OutputRenderer.tsx +88 -0
- package/src/components/SettingsRenderer.tsx +23 -0
- package/src/components/input/CommandSuggestions.tsx +41 -0
- package/src/components/input/FileSuggestions.tsx +61 -0
- package/src/components/input/InputBox.tsx +371 -0
- package/src/components/output/CheckpointView.tsx +13 -0
- package/src/components/output/CommandView.tsx +13 -0
- package/src/components/output/CommentView.tsx +12 -0
- package/src/components/output/ConfirmationView.tsx +179 -0
- package/src/components/output/ContextUsage.tsx +62 -0
- package/src/components/output/DiffView.tsx +202 -0
- package/src/components/output/ErrorView.tsx +14 -0
- package/src/components/output/InteractiveServerView.tsx +69 -0
- package/src/components/output/KnowledgeView.tsx +220 -0
- package/src/components/output/MarkdownView.tsx +15 -0
- package/src/components/output/ModelSelectView.tsx +71 -0
- package/src/components/output/ReasoningView.tsx +21 -0
- package/src/components/output/ToolCallView.tsx +45 -0
- package/src/components/settings/ModelList.tsx +250 -0
- package/src/components/settings/TokenUsage.tsx +274 -0
- package/src/config/schema.ts +19 -0
- package/src/config/settings.ts +229 -0
- package/src/index.tsx +100 -0
- package/src/parsers/tree-sitter-python.wasm +0 -0
- package/src/providers/providers.ts +71 -0
- package/src/services/PluginLoader.ts +123 -0
- package/src/services/cerebras.ts +69 -0
- package/src/services/embeddingService.ts +229 -0
- package/src/services/google.ts +65 -0
- package/src/services/graphSerializer.ts +248 -0
- package/src/services/groq.ts +23 -0
- package/src/services/knowledgeOrchestrator.ts +286 -0
- package/src/services/mistral.ts +79 -0
- package/src/services/ollama.ts +109 -0
- package/src/services/openrouter.ts +23 -0
- package/src/services/symbolExtractor.ts +277 -0
- package/src/store/useFraudeStore.ts +123 -0
- package/src/store/useSettingsStore.ts +38 -0
- package/src/theme.ts +26 -0
- package/src/types/Agent.ts +147 -0
- package/src/types/CommandDefinition.ts +8 -0
- package/src/types/Model.ts +94 -0
- package/src/types/OutputItem.ts +24 -0
- package/src/types/PluginContext.ts +55 -0
- package/src/types/TokenUsage.ts +5 -0
- package/src/types/assets.d.ts +4 -0
- package/src/utils/agentCognition.ts +1152 -0
- package/src/utils/fileSuggestions.ts +111 -0
- package/src/utils/index.ts +17 -0
- package/src/utils/initFraude.ts +8 -0
- package/src/utils/logger.ts +24 -0
- package/src/utils/lspClient.ts +1415 -0
- package/src/utils/paths.ts +24 -0
- package/src/utils/queryHandler.ts +227 -0
- package/src/utils/router.ts +278 -0
- package/src/utils/streamHandler.ts +132 -0
- package/src/utils/treeSitterQueries.ts +125 -0
- package/tsconfig.json +33 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { Box, Text } from "ink";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { type DiffPatch } from "@/agent/pendingChanges";
|
|
4
|
+
import { THEME } from "@/theme";
|
|
5
|
+
|
|
6
|
+
interface DiffViewProps {
|
|
7
|
+
diff?: string;
|
|
8
|
+
patches?: DiffPatch[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function DiffView({ diff, patches }: DiffViewProps) {
|
|
12
|
+
if (patches && patches.length > 0) {
|
|
13
|
+
return (
|
|
14
|
+
<Box flexDirection="column" paddingY={1}>
|
|
15
|
+
{patches.map((patch, pIndex) => (
|
|
16
|
+
<Box key={pIndex} flexDirection="column" marginBottom={1}>
|
|
17
|
+
{patch.hunks.map((hunk, hIndex) => {
|
|
18
|
+
let oldLn = hunk.oldStart;
|
|
19
|
+
let newLn = hunk.newStart;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Box key={hIndex} flexDirection="column">
|
|
23
|
+
{/* Visual separator for hunks/chunks */}
|
|
24
|
+
{(pIndex > 0 || hIndex > 0) && (
|
|
25
|
+
<Box
|
|
26
|
+
borderStyle="single"
|
|
27
|
+
borderTop={false}
|
|
28
|
+
borderLeft={false}
|
|
29
|
+
borderRight={false}
|
|
30
|
+
borderColor={THEME.border}
|
|
31
|
+
marginY={0}
|
|
32
|
+
/>
|
|
33
|
+
)}
|
|
34
|
+
{/* Hunk Header */}
|
|
35
|
+
<Box>
|
|
36
|
+
<Text color={THEME.primary}>
|
|
37
|
+
┌─ {hunk.oldStart},{hunk.oldLines} → {hunk.newStart},
|
|
38
|
+
{hunk.newLines}
|
|
39
|
+
</Text>
|
|
40
|
+
</Box>
|
|
41
|
+
|
|
42
|
+
{hunk.lines.map((line, lIndex) => {
|
|
43
|
+
let type: "add" | "remove" | "context" = "context";
|
|
44
|
+
if (line.startsWith("-")) type = "remove";
|
|
45
|
+
else if (line.startsWith("+")) type = "add";
|
|
46
|
+
|
|
47
|
+
const content = line.substring(1);
|
|
48
|
+
let displayNum = "";
|
|
49
|
+
let color = THEME.text;
|
|
50
|
+
|
|
51
|
+
if (type === "remove") {
|
|
52
|
+
displayNum = oldLn.toString();
|
|
53
|
+
oldLn++;
|
|
54
|
+
color = THEME.error;
|
|
55
|
+
} else if (type === "add") {
|
|
56
|
+
displayNum = newLn.toString();
|
|
57
|
+
newLn++;
|
|
58
|
+
color = THEME.success;
|
|
59
|
+
} else {
|
|
60
|
+
displayNum = newLn.toString();
|
|
61
|
+
oldLn++;
|
|
62
|
+
newLn++;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<Box key={lIndex}>
|
|
67
|
+
<Box
|
|
68
|
+
width={4}
|
|
69
|
+
marginRight={1}
|
|
70
|
+
justifyContent="flex-end"
|
|
71
|
+
>
|
|
72
|
+
<Text color={THEME.dim}>{displayNum}</Text>
|
|
73
|
+
</Box>
|
|
74
|
+
<Box>
|
|
75
|
+
<Text bold={type !== "context"} color={color}>
|
|
76
|
+
{content}
|
|
77
|
+
</Text>
|
|
78
|
+
</Box>
|
|
79
|
+
</Box>
|
|
80
|
+
);
|
|
81
|
+
})}
|
|
82
|
+
</Box>
|
|
83
|
+
);
|
|
84
|
+
})}
|
|
85
|
+
</Box>
|
|
86
|
+
))}
|
|
87
|
+
</Box>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (diff) {
|
|
92
|
+
/*
|
|
93
|
+
* Filter out the header lines:
|
|
94
|
+
* --- @/sample/utils.py Original
|
|
95
|
+
* +++ @/sample/utils.py Modified
|
|
96
|
+
*/
|
|
97
|
+
const rawLines = diff
|
|
98
|
+
.split("\n")
|
|
99
|
+
.filter((line) => !line.startsWith("--- ") && !line.startsWith("+++ "));
|
|
100
|
+
|
|
101
|
+
let oldLn = 0;
|
|
102
|
+
let newLn = 0;
|
|
103
|
+
|
|
104
|
+
const parsedLines = rawLines.reduce<
|
|
105
|
+
{
|
|
106
|
+
type: "add" | "remove" | "context" | "chunk" | "other";
|
|
107
|
+
content: string;
|
|
108
|
+
lineNumber?: number; // focusing on new line number for simplicity or context
|
|
109
|
+
oldLineNumber?: number;
|
|
110
|
+
}[]
|
|
111
|
+
>((acc, line) => {
|
|
112
|
+
if (line.startsWith("@@")) {
|
|
113
|
+
const match = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
|
|
114
|
+
if (match) {
|
|
115
|
+
oldLn = parseInt(match[1] || "0", 10);
|
|
116
|
+
newLn = parseInt(match[2] || "0", 10);
|
|
117
|
+
}
|
|
118
|
+
if (acc.length > 0) {
|
|
119
|
+
acc.push({ type: "chunk", content: "..." });
|
|
120
|
+
}
|
|
121
|
+
return acc;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (line.startsWith("-")) {
|
|
125
|
+
acc.push({
|
|
126
|
+
type: "remove",
|
|
127
|
+
content: line.substring(1),
|
|
128
|
+
oldLineNumber: oldLn,
|
|
129
|
+
});
|
|
130
|
+
oldLn++;
|
|
131
|
+
} else if (line.startsWith("+")) {
|
|
132
|
+
acc.push({
|
|
133
|
+
type: "add",
|
|
134
|
+
content: line.substring(1),
|
|
135
|
+
lineNumber: newLn,
|
|
136
|
+
});
|
|
137
|
+
newLn++;
|
|
138
|
+
} else if (line.startsWith(" ")) {
|
|
139
|
+
acc.push({
|
|
140
|
+
type: "context",
|
|
141
|
+
content: line.substring(1),
|
|
142
|
+
oldLineNumber: oldLn,
|
|
143
|
+
lineNumber: newLn,
|
|
144
|
+
});
|
|
145
|
+
oldLn++;
|
|
146
|
+
newLn++;
|
|
147
|
+
} else {
|
|
148
|
+
acc.push({ type: "other", content: line });
|
|
149
|
+
}
|
|
150
|
+
return acc;
|
|
151
|
+
}, []);
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<Box flexDirection="column" paddingY={1}>
|
|
155
|
+
{parsedLines.map((item, index) => {
|
|
156
|
+
if (item.type === "chunk") {
|
|
157
|
+
return (
|
|
158
|
+
<Box
|
|
159
|
+
key={index}
|
|
160
|
+
borderStyle="single"
|
|
161
|
+
borderTop={false}
|
|
162
|
+
borderLeft={false}
|
|
163
|
+
borderRight={false}
|
|
164
|
+
borderColor={THEME.border}
|
|
165
|
+
marginY={0}
|
|
166
|
+
>
|
|
167
|
+
{/* Visual separator for chunks */}
|
|
168
|
+
</Box>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const lineNum = item.lineNumber ? item.lineNumber.toString() : "";
|
|
173
|
+
const oldLineNum = item.oldLineNumber
|
|
174
|
+
? item.oldLineNumber.toString()
|
|
175
|
+
: "";
|
|
176
|
+
|
|
177
|
+
// Determine color
|
|
178
|
+
let color = THEME.text;
|
|
179
|
+
if (item.type === "add") color = THEME.success;
|
|
180
|
+
if (item.type === "remove") color = THEME.error;
|
|
181
|
+
|
|
182
|
+
const displayNum = item.type === "remove" ? oldLineNum : lineNum;
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<Box key={index}>
|
|
186
|
+
<Box width={4} marginRight={1} justifyContent="flex-end">
|
|
187
|
+
<Text color={THEME.dim}>{displayNum}</Text>
|
|
188
|
+
</Box>
|
|
189
|
+
<Box>
|
|
190
|
+
<Text bold={item.type !== "context"} color={color}>
|
|
191
|
+
{item.content}
|
|
192
|
+
</Text>
|
|
193
|
+
</Box>
|
|
194
|
+
</Box>
|
|
195
|
+
);
|
|
196
|
+
})}
|
|
197
|
+
</Box>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Box, Text } from "ink";
|
|
2
|
+
import { THEME } from "@/theme";
|
|
3
|
+
|
|
4
|
+
const ErrorView = ({ error }: { error: string }) => {
|
|
5
|
+
return (
|
|
6
|
+
<Box marginY={0}>
|
|
7
|
+
<Text color={THEME.error} bold>
|
|
8
|
+
✘ {error}
|
|
9
|
+
</Text>
|
|
10
|
+
</Box>
|
|
11
|
+
);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default ErrorView;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Box, Text, useInput } from "ink";
|
|
2
|
+
import { THEME } from "../../theme";
|
|
3
|
+
import { BunApiRouter } from "../../utils/router";
|
|
4
|
+
import { useState, useEffect } from "react";
|
|
5
|
+
|
|
6
|
+
interface InteractiveServerViewProps {
|
|
7
|
+
routerId: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function InteractiveServerView({
|
|
11
|
+
routerId,
|
|
12
|
+
}: InteractiveServerViewProps) {
|
|
13
|
+
const [stopped, setStopped] = useState(false);
|
|
14
|
+
|
|
15
|
+
// Retrieve the router instance to get details (like port)
|
|
16
|
+
const router = BunApiRouter.getRouter(routerId);
|
|
17
|
+
const port = router?.port || 3000;
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (router) {
|
|
21
|
+
router.onStop = () => {
|
|
22
|
+
setStopped(true);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}, [router]);
|
|
26
|
+
|
|
27
|
+
useInput((input, key) => {
|
|
28
|
+
if (stopped) return;
|
|
29
|
+
|
|
30
|
+
if (input === "q") {
|
|
31
|
+
BunApiRouter.stopRouter(routerId);
|
|
32
|
+
setStopped(true);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (stopped) {
|
|
37
|
+
return (
|
|
38
|
+
<Box borderStyle="round" borderColor={THEME.dim} paddingX={1}>
|
|
39
|
+
<Text color={THEME.dim}>● </Text>
|
|
40
|
+
<Text color={THEME.dim}>Server stopped</Text>
|
|
41
|
+
</Box>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Box
|
|
47
|
+
flexDirection="column"
|
|
48
|
+
borderStyle="round"
|
|
49
|
+
borderColor="white"
|
|
50
|
+
paddingX={1}
|
|
51
|
+
>
|
|
52
|
+
<Box>
|
|
53
|
+
<Text color={THEME.success}>● </Text>
|
|
54
|
+
<Text bold color={THEME.text}>
|
|
55
|
+
API Server Running{" "}
|
|
56
|
+
</Text>
|
|
57
|
+
<Text color={THEME.dim}>on </Text>
|
|
58
|
+
<Text color={THEME.primary} underline>
|
|
59
|
+
http://localhost:{port}
|
|
60
|
+
</Text>
|
|
61
|
+
</Box>
|
|
62
|
+
<Box marginTop={1}>
|
|
63
|
+
<Text color={THEME.dim} italic>
|
|
64
|
+
Press 'q' to stop server
|
|
65
|
+
</Text>
|
|
66
|
+
</Box>
|
|
67
|
+
</Box>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Box, Text } from "ink";
|
|
2
|
+
import { Badge, UnorderedList } from "@inkjs/ui";
|
|
3
|
+
import { THEME } from "@/theme";
|
|
4
|
+
|
|
5
|
+
// Knowledge type configuration - using simple ASCII symbols
|
|
6
|
+
const TYPE_CONFIG: Record<
|
|
7
|
+
string,
|
|
8
|
+
{ icon: string; label: string; color: string }
|
|
9
|
+
> = {
|
|
10
|
+
decision: { icon: "*", label: "DEC", color: THEME.success },
|
|
11
|
+
fact: { icon: "-", label: "FCT", color: THEME.info },
|
|
12
|
+
concept: { icon: "+", label: "CON", color: "#cba6f7" }, // Mauve
|
|
13
|
+
reference: { icon: ">", label: "REF", color: "#fab387" }, // Peach
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
interface KnowledgeItem {
|
|
17
|
+
content: string;
|
|
18
|
+
type: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface KnowledgeSummaryData {
|
|
22
|
+
decisions: KnowledgeItem[];
|
|
23
|
+
facts: KnowledgeItem[];
|
|
24
|
+
concepts: KnowledgeItem[];
|
|
25
|
+
references: KnowledgeItem[];
|
|
26
|
+
// summaries: KnowledgeItem[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface KnowledgeSearchData {
|
|
30
|
+
query: string;
|
|
31
|
+
results: KnowledgeItem[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type KnowledgeViewData =
|
|
35
|
+
| { mode: "summary"; data: KnowledgeSummaryData }
|
|
36
|
+
| { mode: "search"; data: KnowledgeSearchData }
|
|
37
|
+
| { mode: "empty"; message?: string };
|
|
38
|
+
|
|
39
|
+
interface KnowledgeViewProps {
|
|
40
|
+
data: KnowledgeViewData;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Helper to truncate text
|
|
44
|
+
const truncate = (str: string, len: number): string => {
|
|
45
|
+
const cleaned = str.replace(/\n/g, " ").trim();
|
|
46
|
+
return cleaned.length > len ? `${cleaned.slice(0, len)}...` : cleaned;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Section renderer for summary view
|
|
50
|
+
function KnowledgeSection({
|
|
51
|
+
title,
|
|
52
|
+
type,
|
|
53
|
+
items,
|
|
54
|
+
limit,
|
|
55
|
+
}: {
|
|
56
|
+
title: string;
|
|
57
|
+
type: string;
|
|
58
|
+
items: KnowledgeItem[];
|
|
59
|
+
limit: number;
|
|
60
|
+
}) {
|
|
61
|
+
if (items.length === 0) return null;
|
|
62
|
+
|
|
63
|
+
const config = TYPE_CONFIG[type] || {
|
|
64
|
+
icon: "-",
|
|
65
|
+
label: type.slice(0, 3).toUpperCase(),
|
|
66
|
+
color: THEME.dim,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<Box flexDirection="column" marginBottom={1}>
|
|
71
|
+
<Box gap={1}>
|
|
72
|
+
<Text color={config.color}>{config.icon}</Text>
|
|
73
|
+
<Text bold color={THEME.text}>
|
|
74
|
+
{title}
|
|
75
|
+
</Text>
|
|
76
|
+
<Text color={THEME.dim}>-</Text>
|
|
77
|
+
<Text color={THEME.dim}>{items.length}</Text>
|
|
78
|
+
</Box>
|
|
79
|
+
<Box flexDirection="column" paddingLeft={2}>
|
|
80
|
+
<UnorderedList>
|
|
81
|
+
{items.slice(0, limit).map((item, i) => (
|
|
82
|
+
<UnorderedList.Item key={i}>
|
|
83
|
+
<Text color={THEME.text}>{truncate(item.content, 58)}</Text>
|
|
84
|
+
</UnorderedList.Item>
|
|
85
|
+
))}
|
|
86
|
+
</UnorderedList>
|
|
87
|
+
{items.length > limit && (
|
|
88
|
+
<Text color={THEME.dim} italic>
|
|
89
|
+
{" "}... {items.length - limit} more
|
|
90
|
+
</Text>
|
|
91
|
+
)}
|
|
92
|
+
</Box>
|
|
93
|
+
</Box>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Search result item
|
|
98
|
+
function SearchResultItem({ item }: { item: KnowledgeItem }) {
|
|
99
|
+
const config = TYPE_CONFIG[item.type] || {
|
|
100
|
+
icon: "-",
|
|
101
|
+
label: item.type.slice(0, 3).toUpperCase(),
|
|
102
|
+
color: THEME.dim,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<Box gap={1}>
|
|
107
|
+
<Badge color={config.color}>{config.label}</Badge>
|
|
108
|
+
<Text color={THEME.text}>{truncate(item.content, 52)}</Text>
|
|
109
|
+
</Box>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export default function KnowledgeView({ data }: KnowledgeViewProps) {
|
|
114
|
+
if (data.mode === "empty") {
|
|
115
|
+
return (
|
|
116
|
+
<Box flexDirection="column">
|
|
117
|
+
<Text bold color={THEME.primary}>
|
|
118
|
+
Project Knowledge
|
|
119
|
+
</Text>
|
|
120
|
+
<Box paddingY={1}>
|
|
121
|
+
<Text color={THEME.dim}>
|
|
122
|
+
{data.message ||
|
|
123
|
+
"No knowledge stored yet. Start a conversation to build context."}
|
|
124
|
+
</Text>
|
|
125
|
+
</Box>
|
|
126
|
+
<Box>
|
|
127
|
+
<Text color={THEME.dim}>Tip: Use </Text>
|
|
128
|
+
<Text color={THEME.primary}>/knowledge {"<query>"}</Text>
|
|
129
|
+
<Text color={THEME.dim}> to search</Text>
|
|
130
|
+
</Box>
|
|
131
|
+
</Box>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (data.mode === "search") {
|
|
136
|
+
const { query, results } = data.data;
|
|
137
|
+
|
|
138
|
+
if (results.length === 0) {
|
|
139
|
+
return (
|
|
140
|
+
<Box flexDirection="column">
|
|
141
|
+
<Box gap={1}>
|
|
142
|
+
<Text bold color={THEME.primary}>
|
|
143
|
+
Search:
|
|
144
|
+
</Text>
|
|
145
|
+
<Text color={THEME.dim}>"{truncate(query, 40)}"</Text>
|
|
146
|
+
</Box>
|
|
147
|
+
<Box paddingY={1}>
|
|
148
|
+
<Text color={THEME.dim}>No results found</Text>
|
|
149
|
+
</Box>
|
|
150
|
+
<Box>
|
|
151
|
+
<Text color={THEME.dim}>Try a different query or use </Text>
|
|
152
|
+
<Text color={THEME.primary}>/knowledge</Text>
|
|
153
|
+
<Text color={THEME.dim}> to see all items</Text>
|
|
154
|
+
</Box>
|
|
155
|
+
</Box>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<Box flexDirection="column">
|
|
161
|
+
<Box gap={1} marginBottom={1}>
|
|
162
|
+
<Text bold color={THEME.primary}>
|
|
163
|
+
Search:
|
|
164
|
+
</Text>
|
|
165
|
+
<Text color={THEME.dim}>
|
|
166
|
+
"{truncate(query, 30)}" - {results.length} found
|
|
167
|
+
</Text>
|
|
168
|
+
</Box>
|
|
169
|
+
<Box flexDirection="column" gap={1}>
|
|
170
|
+
{results.map((item, i) => (
|
|
171
|
+
<SearchResultItem key={i} item={item} />
|
|
172
|
+
))}
|
|
173
|
+
</Box>
|
|
174
|
+
</Box>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Summary view
|
|
179
|
+
const { decisions, facts, concepts, references } = data.data;
|
|
180
|
+
const total =
|
|
181
|
+
decisions.length + facts.length + concepts.length + references.length;
|
|
182
|
+
// + summaries.length;
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<Box flexDirection="column">
|
|
186
|
+
<Box gap={1} marginBottom={1}>
|
|
187
|
+
<Text bold color={THEME.primary}>
|
|
188
|
+
Project Knowledge
|
|
189
|
+
</Text>
|
|
190
|
+
<Text color={THEME.dim}>- {total} items</Text>
|
|
191
|
+
</Box>
|
|
192
|
+
<Box flexDirection="column">
|
|
193
|
+
<KnowledgeSection
|
|
194
|
+
title="Decisions"
|
|
195
|
+
type="decision"
|
|
196
|
+
items={decisions}
|
|
197
|
+
limit={4}
|
|
198
|
+
/>
|
|
199
|
+
<KnowledgeSection title="Facts" type="fact" items={facts} limit={4} />
|
|
200
|
+
<KnowledgeSection
|
|
201
|
+
title="Concepts"
|
|
202
|
+
type="concept"
|
|
203
|
+
items={concepts}
|
|
204
|
+
limit={3}
|
|
205
|
+
/>
|
|
206
|
+
<KnowledgeSection
|
|
207
|
+
title="References"
|
|
208
|
+
type="reference"
|
|
209
|
+
items={references}
|
|
210
|
+
limit={3}
|
|
211
|
+
/>
|
|
212
|
+
</Box>
|
|
213
|
+
<Box marginTop={1}>
|
|
214
|
+
<Text color={THEME.dim}>Tip: Use </Text>
|
|
215
|
+
<Text color={THEME.primary}>/knowledge {"<query>"}</Text>
|
|
216
|
+
<Text color={THEME.dim}> to search</Text>
|
|
217
|
+
</Box>
|
|
218
|
+
</Box>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Markdown from "@inkkit/ink-markdown";
|
|
2
|
+
import Chalk from "chalk";
|
|
3
|
+
import { THEME } from "@/theme";
|
|
4
|
+
|
|
5
|
+
export default function MarkdownView({ markdown }: { markdown: string }) {
|
|
6
|
+
return (
|
|
7
|
+
<Markdown
|
|
8
|
+
code={Chalk.white}
|
|
9
|
+
codespan={Chalk.white}
|
|
10
|
+
heading={Chalk.hex(THEME.primary)}
|
|
11
|
+
>
|
|
12
|
+
{markdown}
|
|
13
|
+
</Markdown>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Box, Text } from "ink";
|
|
2
|
+
import { Select } from "@inkjs/ui";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import useSettingsStore from "@/store/useSettingsStore";
|
|
5
|
+
import useFraudeStore from "@/store/useFraudeStore";
|
|
6
|
+
import { THEME } from "@/theme";
|
|
7
|
+
|
|
8
|
+
export default function ModelSelectView() {
|
|
9
|
+
const { models } = useSettingsStore();
|
|
10
|
+
const pendingSelection = useFraudeStore(
|
|
11
|
+
(state) => state.pendingModelSelection,
|
|
12
|
+
);
|
|
13
|
+
const resolveModelSelection = useFraudeStore(
|
|
14
|
+
(state) => state.resolveModelSelection,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
if (!pendingSelection) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { originalModel, errorMessage } = pendingSelection;
|
|
22
|
+
|
|
23
|
+
// Filter out the model that failed and get available alternatives
|
|
24
|
+
const availableModels = models.filter((m) => m.name !== originalModel);
|
|
25
|
+
|
|
26
|
+
const options = [
|
|
27
|
+
{ label: "✗ Cancel (abort the request)", value: "__cancel__" },
|
|
28
|
+
...availableModels.map((model) => ({
|
|
29
|
+
label: `→ ${model.name} (${model.type})`,
|
|
30
|
+
value: model.name,
|
|
31
|
+
})),
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const handleSelect = (value: string) => {
|
|
35
|
+
if (value === "__cancel__") {
|
|
36
|
+
resolveModelSelection(null);
|
|
37
|
+
} else {
|
|
38
|
+
resolveModelSelection(value);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Box
|
|
44
|
+
flexDirection="column"
|
|
45
|
+
borderStyle="single"
|
|
46
|
+
borderColor={THEME.warning}
|
|
47
|
+
paddingX={1}
|
|
48
|
+
>
|
|
49
|
+
<Text color={THEME.warning}>⚠ Rate Limit Exceeded</Text>
|
|
50
|
+
<Box marginY={1} flexDirection="column">
|
|
51
|
+
<Text>
|
|
52
|
+
Model{" "}
|
|
53
|
+
<Text bold color={THEME.error}>
|
|
54
|
+
{originalModel}
|
|
55
|
+
</Text>{" "}
|
|
56
|
+
hit its rate limit.
|
|
57
|
+
</Text>
|
|
58
|
+
</Box>
|
|
59
|
+
<Box marginBottom={1}>
|
|
60
|
+
<Text dimColor wrap="wrap">
|
|
61
|
+
{errorMessage.slice(0, 150)}
|
|
62
|
+
{errorMessage.length > 150 ? "..." : ""}
|
|
63
|
+
</Text>
|
|
64
|
+
</Box>
|
|
65
|
+
<Text>Select an alternative model:</Text>
|
|
66
|
+
<Box marginTop={0}>
|
|
67
|
+
<Select options={options} onChange={handleSelect} />
|
|
68
|
+
</Box>
|
|
69
|
+
</Box>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Box, Text } from "ink";
|
|
2
|
+
import { THEME } from "@/theme";
|
|
3
|
+
|
|
4
|
+
interface ReasoningViewProps {
|
|
5
|
+
content: string;
|
|
6
|
+
duration?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function ReasoningView({
|
|
10
|
+
content,
|
|
11
|
+
duration,
|
|
12
|
+
}: ReasoningViewProps) {
|
|
13
|
+
return (
|
|
14
|
+
<Box flexDirection="column" marginY={0}>
|
|
15
|
+
<Text color={THEME.dim}>
|
|
16
|
+
[thinking] {content}
|
|
17
|
+
{duration ? ` (${duration.toFixed(1)}s)` : ""}
|
|
18
|
+
</Text>
|
|
19
|
+
</Box>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Box, Text } from "ink";
|
|
2
|
+
import { THEME } from "@/theme";
|
|
3
|
+
|
|
4
|
+
interface ToolCallViewProps {
|
|
5
|
+
action: string;
|
|
6
|
+
details?: string;
|
|
7
|
+
result?: string;
|
|
8
|
+
duration?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function ToolCallView({
|
|
12
|
+
action,
|
|
13
|
+
details,
|
|
14
|
+
result,
|
|
15
|
+
duration,
|
|
16
|
+
}: ToolCallViewProps) {
|
|
17
|
+
// Truncate result preview to 80 chars
|
|
18
|
+
const resultPreview = result
|
|
19
|
+
? result.length > 80
|
|
20
|
+
? result.slice(0, 77) + "..."
|
|
21
|
+
: result
|
|
22
|
+
: null;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Box flexDirection="column" marginY={0}>
|
|
26
|
+
<Box>
|
|
27
|
+
<Text color={THEME.primary}>◇</Text>
|
|
28
|
+
<Text> </Text>
|
|
29
|
+
<Text color={THEME.text}>{action}</Text>
|
|
30
|
+
{details && (
|
|
31
|
+
<Text color={THEME.dim}>
|
|
32
|
+
{" "}
|
|
33
|
+
{details.length > 50 ? details.slice(0, 47) + "..." : details}
|
|
34
|
+
</Text>
|
|
35
|
+
)}
|
|
36
|
+
{duration && <Text color={THEME.dim}> {duration}</Text>}
|
|
37
|
+
</Box>
|
|
38
|
+
{resultPreview && (
|
|
39
|
+
<Box paddingLeft={2}>
|
|
40
|
+
<Text color={THEME.dim}>↳ {resultPreview}</Text>
|
|
41
|
+
</Box>
|
|
42
|
+
)}
|
|
43
|
+
</Box>
|
|
44
|
+
);
|
|
45
|
+
}
|