@ridit/lens 0.3.7 → 0.3.9
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/index.mjs +105368 -274002
- package/package.json +13 -19
- package/src/colors.ts +15 -15
- package/src/commands/chat.tsx +32 -23
- package/src/commands/provider.tsx +11 -238
- package/src/commands/repo.tsx +66 -120
- package/src/commands/timeline.tsx +11 -22
- package/src/components/ChatView.tsx +238 -0
- package/src/components/Message.tsx +46 -0
- package/src/components/ToolCall.tsx +67 -0
- package/src/components/chat/ChatView.tsx +550 -0
- package/src/components/chat/Message.tsx +152 -0
- package/src/components/chat/StatusBar.tsx +214 -0
- package/src/components/chat/TextArea.tsx +173 -176
- package/src/components/provider/ApiKeyStep.tsx +207 -199
- package/src/components/provider/ModelStep.tsx +90 -88
- package/src/components/provider/ProviderSetup.tsx +331 -0
- package/src/components/provider/ProviderTypeStep.tsx +53 -61
- package/src/components/repo/StepRow.tsx +68 -69
- package/src/components/timeline/TimelineView.tsx +840 -0
- package/src/components/toolcall-utils.ts +103 -0
- package/src/components/watch/RunView.tsx +497 -0
- package/src/hooks/useChatInput.ts +49 -0
- package/src/hooks/useCommandHandler.ts +117 -0
- package/src/index.tsx +386 -139
- package/src/utils/git.ts +149 -155
- package/src/utils/repo.ts +62 -69
- package/src/utils/thinking.tsx +64 -0
- package/src/utils/watch.ts +165 -307
- package/tests/message.test.ts +38 -0
- package/tests/toolcall-utils.test.ts +111 -0
- package/tsconfig.json +8 -24
- package/CLAUDE.md +0 -50
- package/LENS.md +0 -48
- package/LICENSE +0 -21
- package/README.md +0 -93
- package/addons/README.md +0 -55
- package/addons/clean-cache.js +0 -48
- package/addons/generate-readme.js +0 -67
- package/addons/git-stats.js +0 -29
- package/addons/run-tests.js +0 -127
- package/src/commands/commit.tsx +0 -668
- package/src/commands/review.tsx +0 -294
- package/src/commands/run.tsx +0 -56
- package/src/commands/task.tsx +0 -36
- package/src/components/chat/ChatMessage.tsx +0 -195
- package/src/components/chat/ChatOverlays.tsx +0 -399
- package/src/components/chat/ChatRunner.tsx +0 -517
- package/src/components/chat/hooks/useChat.ts +0 -631
- package/src/components/chat/hooks/useChatInput.ts +0 -79
- package/src/components/chat/hooks/useCommandHandlers.ts +0 -327
- package/src/components/provider/ProviderPicker.tsx +0 -76
- package/src/components/provider/RemoveProviderStep.tsx +0 -82
- package/src/components/repo/DiffViewer.tsx +0 -175
- package/src/components/repo/FileReviewer.tsx +0 -70
- package/src/components/repo/FileViewer.tsx +0 -60
- package/src/components/repo/IssueFixer.tsx +0 -666
- package/src/components/repo/LensFileMenu.tsx +0 -115
- package/src/components/repo/NoProviderPrompt.tsx +0 -28
- package/src/components/repo/PreviewRunner.tsx +0 -217
- package/src/components/repo/RepoAnalysis.tsx +0 -534
- package/src/components/task/TaskRunner.tsx +0 -396
- package/src/components/timeline/CommitDetail.tsx +0 -272
- package/src/components/timeline/CommitList.tsx +0 -162
- package/src/components/timeline/TimelineChat.tsx +0 -166
- package/src/components/timeline/TimelineRunner.tsx +0 -1285
- package/src/components/watch/RunRunner.tsx +0 -929
- package/src/prompts/fewshot.ts +0 -252
- package/src/prompts/index.ts +0 -2
- package/src/prompts/system.ts +0 -285
- package/src/tools/chart.ts +0 -202
- package/src/tools/convert-image.ts +0 -312
- package/src/tools/files.ts +0 -253
- package/src/tools/git.ts +0 -603
- package/src/tools/index.ts +0 -17
- package/src/tools/pdf.ts +0 -164
- package/src/tools/shell.ts +0 -96
- package/src/tools/view-image.ts +0 -335
- package/src/tools/web.ts +0 -212
- package/src/types/chat.ts +0 -86
- package/src/types/config.ts +0 -20
- package/src/types/repo.ts +0 -54
- package/src/utils/addons/loadAddons.ts +0 -34
- package/src/utils/ai.ts +0 -321
- package/src/utils/chat.ts +0 -326
- package/src/utils/chatHistory.ts +0 -121
- package/src/utils/config.ts +0 -61
- package/src/utils/files.ts +0 -105
- package/src/utils/intentClassifier.ts +0 -58
- package/src/utils/lensfile.ts +0 -142
- package/src/utils/llm.ts +0 -81
- package/src/utils/memory.ts +0 -209
- package/src/utils/preview.ts +0 -119
- package/src/utils/stats.ts +0 -174
- package/src/utils/tools/builtins.ts +0 -377
- package/src/utils/tools/registry.ts +0 -105
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ridit/lens",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.9",
|
|
4
|
+
"description": "Understand your codebase.",
|
|
5
5
|
"author": "Ridit Jangra <riditjangra09@gmail.com> (https://ridit.space)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -13,33 +13,27 @@
|
|
|
13
13
|
"lens": "./dist/index.mjs"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"
|
|
16
|
+
"dev": "bun src/index.tsx",
|
|
17
|
+
"build": "bun build src/index.tsx --target node --outfile dist/index.mjs --external react-devtools-core",
|
|
17
18
|
"postbuild": "node -e \"const fs=require('fs');const f='dist/index.mjs';fs.writeFileSync(f,'#!/usr/bin/env node\\n'+fs.readFileSync(f,'utf8'))\"",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
19
|
+
"prepublishOnly": "bun run build",
|
|
20
|
+
"test": "bun test"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
22
|
-
"@ridit/
|
|
23
|
-
"
|
|
24
|
-
"
|
|
23
|
+
"@ridit/ink-ui": "*",
|
|
24
|
+
"@ridit/lens-core": "*",
|
|
25
|
+
"chalk": "^5.6.2",
|
|
25
26
|
"commander": "^14.0.3",
|
|
26
27
|
"figures": "^6.1.0",
|
|
27
|
-
"ink": "
|
|
28
|
+
"ink": "4.4.1",
|
|
28
29
|
"ink-spinner": "^5.0.0",
|
|
29
30
|
"ink-text-input": "^6.0.0",
|
|
30
|
-
"
|
|
31
|
-
"pdfkit": "^0.18.0",
|
|
32
|
-
"react": "^19.2.4",
|
|
33
|
-
"react-devtools-core": "^7.0.1",
|
|
34
|
-
"sugar-high": "^0.9.5",
|
|
35
|
-
"terminal-image": "^4.2.0"
|
|
31
|
+
"react": "18.2.0"
|
|
36
32
|
},
|
|
37
33
|
"devDependencies": {
|
|
38
|
-
"@types/
|
|
34
|
+
"@types/react": "^18.0.0",
|
|
39
35
|
"@types/bun": "latest",
|
|
40
|
-
"@
|
|
41
|
-
"@types/pdfkit": "^0.17.5",
|
|
42
|
-
"@types/react": "^19.2.14"
|
|
36
|
+
"@ridit/typescript-config": "*"
|
|
43
37
|
},
|
|
44
38
|
"peerDependencies": {
|
|
45
39
|
"typescript": "^5"
|
package/src/colors.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
export const ACCENT = "#DA7758";
|
|
2
|
-
export const HEADING = "#FFFFFF";
|
|
3
|
-
export const TEXT = "#E8E8E8";
|
|
4
|
-
export const RED = "#E06C75";
|
|
5
|
-
export const GREEN = "#85C98A";
|
|
6
|
-
export const CYAN = "#79C5D4";
|
|
7
|
-
|
|
8
|
-
export const TOKEN_KEYWORD = "#B385C9";
|
|
9
|
-
export const TOKEN_STRING = "#85C98A";
|
|
10
|
-
export const TOKEN_NUMBER = "#E8C170";
|
|
11
|
-
export const TOKEN_PROPERTY = "#79C5D4";
|
|
12
|
-
export const TOKEN_ENTITY = "#7AABDB";
|
|
13
|
-
export const TOKEN_TEXT = "#E8E8E8";
|
|
14
|
-
export const TOKEN_MUTED = "#888888";
|
|
15
|
-
export const TOKEN_COMMENT = "#777777";
|
|
1
|
+
export const ACCENT = "#DA7758";
|
|
2
|
+
export const HEADING = "#FFFFFF";
|
|
3
|
+
export const TEXT = "#E8E8E8";
|
|
4
|
+
export const RED = "#E06C75";
|
|
5
|
+
export const GREEN = "#85C98A";
|
|
6
|
+
export const CYAN = "#79C5D4";
|
|
7
|
+
|
|
8
|
+
export const TOKEN_KEYWORD = "#B385C9";
|
|
9
|
+
export const TOKEN_STRING = "#85C98A";
|
|
10
|
+
export const TOKEN_NUMBER = "#E8C170";
|
|
11
|
+
export const TOKEN_PROPERTY = "#79C5D4";
|
|
12
|
+
export const TOKEN_ENTITY = "#7AABDB";
|
|
13
|
+
export const TOKEN_TEXT = "#E8E8E8";
|
|
14
|
+
export const TOKEN_MUTED = "#888888";
|
|
15
|
+
export const TOKEN_COMMENT = "#777777";
|
package/src/commands/chat.tsx
CHANGED
|
@@ -1,23 +1,32 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Box
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box } from "ink";
|
|
3
|
+
import { ChatRunner } from "../components/chat/ChatView";
|
|
4
|
+
|
|
5
|
+
export function ChatCommand({
|
|
6
|
+
path,
|
|
7
|
+
autoForce = false,
|
|
8
|
+
initialMessage,
|
|
9
|
+
dev = false,
|
|
10
|
+
single = false,
|
|
11
|
+
sessionId,
|
|
12
|
+
}: {
|
|
13
|
+
path: string;
|
|
14
|
+
autoForce?: boolean;
|
|
15
|
+
initialMessage?: string;
|
|
16
|
+
dev?: boolean;
|
|
17
|
+
single?: boolean;
|
|
18
|
+
sessionId?: string;
|
|
19
|
+
}) {
|
|
20
|
+
return (
|
|
21
|
+
<Box flexDirection="column">
|
|
22
|
+
<ChatRunner
|
|
23
|
+
repoPath={path}
|
|
24
|
+
autoForce={autoForce}
|
|
25
|
+
initialMessage={initialMessage}
|
|
26
|
+
dev={dev}
|
|
27
|
+
single={single}
|
|
28
|
+
sessionId={sessionId}
|
|
29
|
+
/>
|
|
30
|
+
</Box>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -1,238 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
type InitStage =
|
|
14
|
-
| { type: "menu" }
|
|
15
|
-
| { type: "provider-type" }
|
|
16
|
-
| { type: "api-key"; providerType: ProviderType }
|
|
17
|
-
| { type: "base-url"; providerType: ProviderType; apiKey: string }
|
|
18
|
-
| {
|
|
19
|
-
type: "model";
|
|
20
|
-
providerType: ProviderType;
|
|
21
|
-
apiKey: string;
|
|
22
|
-
baseUrl?: string;
|
|
23
|
-
}
|
|
24
|
-
| { type: "remove" }
|
|
25
|
-
| { type: "done"; provider: Provider };
|
|
26
|
-
|
|
27
|
-
const MENU_OPTIONS = [
|
|
28
|
-
{ label: "Add a provider", action: "provider-type" },
|
|
29
|
-
{ label: "Remove a provider", action: "remove" },
|
|
30
|
-
] as const;
|
|
31
|
-
|
|
32
|
-
export const InitCommand = () => {
|
|
33
|
-
const [stage, setStage] = useState<InitStage>({ type: "menu" });
|
|
34
|
-
const [completedSteps, setCompletedSteps] = useState<string[]>([]);
|
|
35
|
-
const [menuIndex, setMenuIndex] = useState(0);
|
|
36
|
-
|
|
37
|
-
const pushStep = (label: string) => setCompletedSteps((s) => [...s, label]);
|
|
38
|
-
const popStep = () => setCompletedSteps((s) => s.slice(0, -1));
|
|
39
|
-
const popSteps = (n: number) => setCompletedSteps((s) => s.slice(0, -n));
|
|
40
|
-
|
|
41
|
-
useInput((input, key) => {
|
|
42
|
-
if (stage.type !== "menu") return;
|
|
43
|
-
if (key.upArrow) setMenuIndex((i) => Math.max(0, i - 1));
|
|
44
|
-
if (key.downArrow)
|
|
45
|
-
setMenuIndex((i) => Math.min(MENU_OPTIONS.length - 1, i + 1));
|
|
46
|
-
if (key.return) {
|
|
47
|
-
const action = MENU_OPTIONS[menuIndex]?.action;
|
|
48
|
-
if (action === "provider-type") setStage({ type: "provider-type" });
|
|
49
|
-
if (action === "remove") setStage({ type: "remove" });
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
if (stage.type === "menu") {
|
|
54
|
-
const config = loadConfig();
|
|
55
|
-
return (
|
|
56
|
-
<Box flexDirection="column" gap={1}>
|
|
57
|
-
{completedSteps.map((s, i) => (
|
|
58
|
-
<Text key={i} color={GREEN}>
|
|
59
|
-
{figures.arrowRight} {s}
|
|
60
|
-
</Text>
|
|
61
|
-
))}
|
|
62
|
-
{config.providers.length > 0 && (
|
|
63
|
-
<Text color="gray">
|
|
64
|
-
{config.providers.length} provider(s) configured
|
|
65
|
-
</Text>
|
|
66
|
-
)}
|
|
67
|
-
{MENU_OPTIONS.map((opt, i) => (
|
|
68
|
-
<Box key={opt.action} marginLeft={1}>
|
|
69
|
-
<Text color={i === menuIndex ? ACCENT : "white"}>
|
|
70
|
-
{i === menuIndex ? figures.arrowRight : " "}
|
|
71
|
-
{" "}
|
|
72
|
-
{opt.label}
|
|
73
|
-
</Text>
|
|
74
|
-
</Box>
|
|
75
|
-
))}
|
|
76
|
-
<Text color="gray">↑↓ navigate · enter to select</Text>
|
|
77
|
-
</Box>
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (stage.type === "remove") {
|
|
82
|
-
return (
|
|
83
|
-
<Box flexDirection="column" gap={1}>
|
|
84
|
-
{completedSteps.map((s, i) => (
|
|
85
|
-
<Text key={i} color={GREEN}>
|
|
86
|
-
{figures.arrowRight} {s}
|
|
87
|
-
</Text>
|
|
88
|
-
))}
|
|
89
|
-
<RemoveProviderStep onDone={() => setStage({ type: "menu" })} />
|
|
90
|
-
</Box>
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (stage.type === "provider-type") {
|
|
95
|
-
return (
|
|
96
|
-
<Box flexDirection="column" gap={1}>
|
|
97
|
-
{completedSteps.map((s, i) => (
|
|
98
|
-
<Text key={i} color={GREEN}>
|
|
99
|
-
{figures.arrowRight} {s}
|
|
100
|
-
</Text>
|
|
101
|
-
))}
|
|
102
|
-
<ProviderTypeStep
|
|
103
|
-
onSelect={(providerType) => {
|
|
104
|
-
pushStep(`Provider: ${providerType}`);
|
|
105
|
-
setStage({ type: "api-key", providerType });
|
|
106
|
-
}}
|
|
107
|
-
onBack={() => setStage({ type: "menu" })}
|
|
108
|
-
/>
|
|
109
|
-
</Box>
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (stage.type === "api-key") {
|
|
114
|
-
return (
|
|
115
|
-
<Box flexDirection="column" gap={1}>
|
|
116
|
-
{completedSteps.map((s, i) => (
|
|
117
|
-
<Text key={i} color={GREEN}>
|
|
118
|
-
{figures.arrowRight} {s}
|
|
119
|
-
</Text>
|
|
120
|
-
))}
|
|
121
|
-
<ApiKeyStep
|
|
122
|
-
providerType={stage.providerType}
|
|
123
|
-
onSubmit={(value) => {
|
|
124
|
-
if (stage.providerType === "custom") {
|
|
125
|
-
const { apiKey, baseUrl } = value as {
|
|
126
|
-
apiKey: string;
|
|
127
|
-
baseUrl?: string;
|
|
128
|
-
};
|
|
129
|
-
pushStep("API key saved");
|
|
130
|
-
if (baseUrl) pushStep(`Base URL: ${baseUrl}`);
|
|
131
|
-
setStage({
|
|
132
|
-
type: "model",
|
|
133
|
-
providerType: stage.providerType,
|
|
134
|
-
apiKey,
|
|
135
|
-
baseUrl,
|
|
136
|
-
});
|
|
137
|
-
} else if (stage.providerType === "ollama") {
|
|
138
|
-
pushStep(`Base URL: ${value}`);
|
|
139
|
-
setStage({
|
|
140
|
-
type: "model",
|
|
141
|
-
providerType: stage.providerType,
|
|
142
|
-
apiKey: "",
|
|
143
|
-
baseUrl: value as string,
|
|
144
|
-
});
|
|
145
|
-
} else {
|
|
146
|
-
pushStep("API key saved");
|
|
147
|
-
setStage({
|
|
148
|
-
type: "model",
|
|
149
|
-
providerType: stage.providerType,
|
|
150
|
-
apiKey: value as string,
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
}}
|
|
154
|
-
onBack={() => {
|
|
155
|
-
popStep();
|
|
156
|
-
setStage({ type: "provider-type" });
|
|
157
|
-
}}
|
|
158
|
-
/>
|
|
159
|
-
</Box>
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (stage.type === "base-url") {
|
|
164
|
-
return (
|
|
165
|
-
<Box flexDirection="column" gap={1}>
|
|
166
|
-
{completedSteps.map((s, i) => (
|
|
167
|
-
<Text key={i} color={GREEN}>
|
|
168
|
-
{figures.arrowRight} {s}
|
|
169
|
-
</Text>
|
|
170
|
-
))}
|
|
171
|
-
<ApiKeyStep
|
|
172
|
-
providerType="ollama"
|
|
173
|
-
onSubmit={(baseUrl) => {
|
|
174
|
-
pushStep(`Base URL: ${baseUrl}`);
|
|
175
|
-
setStage({
|
|
176
|
-
type: "model",
|
|
177
|
-
providerType: stage.providerType,
|
|
178
|
-
apiKey: stage.apiKey,
|
|
179
|
-
baseUrl: baseUrl as string,
|
|
180
|
-
});
|
|
181
|
-
}}
|
|
182
|
-
onBack={() => {
|
|
183
|
-
popStep();
|
|
184
|
-
setStage({ type: "api-key", providerType: stage.providerType });
|
|
185
|
-
}}
|
|
186
|
-
/>
|
|
187
|
-
</Box>
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (stage.type === "model") {
|
|
192
|
-
return (
|
|
193
|
-
<Box flexDirection="column" gap={1}>
|
|
194
|
-
{completedSteps.map((s, i) => (
|
|
195
|
-
<Text key={i} color={GREEN}>
|
|
196
|
-
{figures.arrowRight} {s}
|
|
197
|
-
</Text>
|
|
198
|
-
))}
|
|
199
|
-
<ModelStep
|
|
200
|
-
providerType={stage.providerType}
|
|
201
|
-
onSelect={(model) => {
|
|
202
|
-
const provider: Provider = {
|
|
203
|
-
id: nanoid(8),
|
|
204
|
-
type: stage.providerType,
|
|
205
|
-
name: `${stage.providerType}-${model}`,
|
|
206
|
-
apiKey: stage.apiKey || undefined,
|
|
207
|
-
baseUrl: stage.baseUrl,
|
|
208
|
-
model,
|
|
209
|
-
};
|
|
210
|
-
addProvider(provider);
|
|
211
|
-
pushStep(`Model: ${model}`);
|
|
212
|
-
setStage({ type: "done", provider });
|
|
213
|
-
}}
|
|
214
|
-
onBack={() => {
|
|
215
|
-
popStep();
|
|
216
|
-
setStage({ type: "api-key", providerType: stage.providerType });
|
|
217
|
-
}}
|
|
218
|
-
/>
|
|
219
|
-
</Box>
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
return (
|
|
224
|
-
<Box flexDirection="column" gap={1}>
|
|
225
|
-
{completedSteps.map((s, i) => (
|
|
226
|
-
<Text key={i} color={GREEN}>
|
|
227
|
-
{figures.arrowRight} {s}
|
|
228
|
-
</Text>
|
|
229
|
-
))}
|
|
230
|
-
<Text color={GREEN}>
|
|
231
|
-
{figures.arrowRight} Provider configured successfully
|
|
232
|
-
</Text>
|
|
233
|
-
<Text color="gray">
|
|
234
|
-
Run <Text color={CYAN}>lens provider</Text> again to manage providers.
|
|
235
|
-
</Text>
|
|
236
|
-
</Box>
|
|
237
|
-
);
|
|
238
|
-
};
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box } from "ink";
|
|
3
|
+
import { ProviderSetup } from "../components/provider/ProviderSetup";
|
|
4
|
+
|
|
5
|
+
export function ProviderCommand() {
|
|
6
|
+
return (
|
|
7
|
+
<Box flexDirection="column" paddingX={1} paddingY={1}>
|
|
8
|
+
<ProviderSetup onDone={() => process.exit(0)} />
|
|
9
|
+
</Box>
|
|
10
|
+
);
|
|
11
|
+
}
|
package/src/commands/repo.tsx
CHANGED
|
@@ -1,120 +1,66 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
]);
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const last = steps[steps.length - 1];
|
|
68
|
-
if (last?.type !== "folder-exists") return;
|
|
69
|
-
const rPath = last.repoPath;
|
|
70
|
-
|
|
71
|
-
if (input === "y" || input === "Y") {
|
|
72
|
-
updateLastStep({ type: "cloning", status: "pending" });
|
|
73
|
-
startCloneRepo(url, { forceReclone: true }).then((result) => {
|
|
74
|
-
if (result.done) {
|
|
75
|
-
handleCloneSuccess(rPath);
|
|
76
|
-
} else if (!result.folderExists) {
|
|
77
|
-
updateLastStep({
|
|
78
|
-
type: "error",
|
|
79
|
-
message: result.error ?? "Unknown error",
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (input === "n" || input === "N") handleCloneSuccess(rPath);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const allDone =
|
|
89
|
-
steps[steps.length - 1]?.type === "reading-files" &&
|
|
90
|
-
(steps[steps.length - 1] as Extract<Step, { type: "reading-files" }>)
|
|
91
|
-
.status === "done";
|
|
92
|
-
|
|
93
|
-
return (
|
|
94
|
-
<Box flexDirection="column">
|
|
95
|
-
{steps.map((step, i) => (
|
|
96
|
-
<StepRow key={i} step={step} />
|
|
97
|
-
))}
|
|
98
|
-
|
|
99
|
-
{allDone && !reviewDone && importantFiles.length > 0 && (
|
|
100
|
-
<FileReviewer
|
|
101
|
-
files={importantFiles}
|
|
102
|
-
onDone={() => setReviewDone(true)}
|
|
103
|
-
/>
|
|
104
|
-
)}
|
|
105
|
-
|
|
106
|
-
{allDone && importantFiles.length === 0 && !reviewDone && (
|
|
107
|
-
<Text color="gray">{figures.info} No important files found</Text>
|
|
108
|
-
)}
|
|
109
|
-
|
|
110
|
-
{(reviewDone || (allDone && importantFiles.length === 0)) && (
|
|
111
|
-
<RepoAnalysis
|
|
112
|
-
repoUrl={url}
|
|
113
|
-
repoPath={repoPath}
|
|
114
|
-
fileTree={fileTree}
|
|
115
|
-
files={importantFiles}
|
|
116
|
-
/>
|
|
117
|
-
)}
|
|
118
|
-
</Box>
|
|
119
|
-
);
|
|
120
|
-
};
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
|
2
|
+
import { Box, useInput } from "ink";
|
|
3
|
+
import { StepRow, type Step } from "../components/repo/StepRow";
|
|
4
|
+
import { ChatRunner } from "../components/chat/ChatView";
|
|
5
|
+
import { startCloneRepo } from "../utils/repo";
|
|
6
|
+
|
|
7
|
+
export function RepoCommand({ url }: { url: string }) {
|
|
8
|
+
const [steps, setSteps] = useState<Step[]>([
|
|
9
|
+
{ type: "cloning", status: "pending" },
|
|
10
|
+
]);
|
|
11
|
+
const [repoPath, setRepoPath] = useState<string | null>(null);
|
|
12
|
+
|
|
13
|
+
const updateLastStep = (updated: Step) =>
|
|
14
|
+
setSteps((prev) => [...prev.slice(0, -1), updated]);
|
|
15
|
+
|
|
16
|
+
const pushStep = (step: Step) => setSteps((prev) => [...prev, step]);
|
|
17
|
+
|
|
18
|
+
const doClone = (forceReclone = false) => {
|
|
19
|
+
startCloneRepo(url, { forceReclone }).then((result) => {
|
|
20
|
+
if (result.done) {
|
|
21
|
+
updateLastStep({ type: "cloning", status: "done" });
|
|
22
|
+
setRepoPath(result.repoPath);
|
|
23
|
+
} else if (!result.done && result.folderExists) {
|
|
24
|
+
updateLastStep({
|
|
25
|
+
type: "folder-exists",
|
|
26
|
+
status: "pending",
|
|
27
|
+
repoPath: result.repoPath,
|
|
28
|
+
});
|
|
29
|
+
} else if (!result.done) {
|
|
30
|
+
updateLastStep({ type: "error", message: result.error ?? "Clone failed" });
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
doClone();
|
|
37
|
+
}, [url]);
|
|
38
|
+
|
|
39
|
+
useInput((input) => {
|
|
40
|
+
const last = steps[steps.length - 1];
|
|
41
|
+
if (last?.type !== "folder-exists") return;
|
|
42
|
+
if (input === "y" || input === "Y") {
|
|
43
|
+
updateLastStep({ type: "cloning", status: "pending" });
|
|
44
|
+
doClone(true);
|
|
45
|
+
}
|
|
46
|
+
if (input === "n" || input === "N") {
|
|
47
|
+
updateLastStep({ type: "cloning", status: "done" });
|
|
48
|
+
setRepoPath(last.repoPath);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Box flexDirection="column">
|
|
54
|
+
{steps.map((step, i) => (
|
|
55
|
+
<StepRow key={i} step={step} />
|
|
56
|
+
))}
|
|
57
|
+
|
|
58
|
+
{repoPath && (
|
|
59
|
+
<ChatRunner
|
|
60
|
+
repoPath={repoPath}
|
|
61
|
+
initialMessage={`I've cloned the repository from ${url}. Please analyze it — give me an overview of what it does, the tech stack, key architectural decisions, and anything interesting.`}
|
|
62
|
+
/>
|
|
63
|
+
)}
|
|
64
|
+
</Box>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -1,22 +1,11 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Box
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<Box marginTop={1}>
|
|
14
|
-
<Text color="red">
|
|
15
|
-
{figures.cross} Path not found: {resolvedPath}
|
|
16
|
-
</Text>
|
|
17
|
-
</Box>
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return <TimelineRunner repoPath={resolvedPath} />;
|
|
22
|
-
};
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box } from "ink";
|
|
3
|
+
import { TimelineRunner } from "../components/timeline/TimelineView";
|
|
4
|
+
|
|
5
|
+
export function TimelineCommand({ path }: { path: string }) {
|
|
6
|
+
return (
|
|
7
|
+
<Box flexDirection="column">
|
|
8
|
+
<TimelineRunner repoPath={path} />
|
|
9
|
+
</Box>
|
|
10
|
+
);
|
|
11
|
+
}
|