wave-code 0.0.6 → 0.0.8
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 +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/components/App.d.ts +1 -0
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +4 -4
- package/dist/components/BashHistorySelector.d.ts.map +1 -1
- package/dist/components/BashHistorySelector.js +17 -3
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +4 -2
- package/dist/components/Confirmation.d.ts +11 -0
- package/dist/components/Confirmation.d.ts.map +1 -0
- package/dist/components/Confirmation.js +148 -0
- package/dist/components/DiffDisplay.d.ts +8 -0
- package/dist/components/DiffDisplay.d.ts.map +1 -0
- package/dist/components/DiffDisplay.js +168 -0
- package/dist/components/FileSelector.d.ts +2 -4
- package/dist/components/FileSelector.d.ts.map +1 -1
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +10 -1
- package/dist/components/MemoryDisplay.js +1 -1
- package/dist/components/MessageItem.d.ts +1 -2
- package/dist/components/MessageItem.d.ts.map +1 -1
- package/dist/components/MessageItem.js +3 -3
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +2 -2
- package/dist/components/ReasoningDisplay.d.ts +8 -0
- package/dist/components/ReasoningDisplay.d.ts.map +1 -0
- package/dist/components/ReasoningDisplay.js +10 -0
- package/dist/components/ToolResultDisplay.d.ts.map +1 -1
- package/dist/components/ToolResultDisplay.js +2 -1
- package/dist/contexts/useChat.d.ts +13 -1
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +117 -15
- package/dist/hooks/useInputManager.d.ts +3 -0
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -4
- package/dist/managers/InputManager.d.ts +8 -0
- package/dist/managers/InputManager.d.ts.map +1 -1
- package/dist/managers/InputManager.js +33 -2
- package/dist/print-cli.d.ts +1 -0
- package/dist/print-cli.d.ts.map +1 -1
- package/dist/print-cli.js +36 -3
- package/dist/utils/toolParameterTransforms.d.ts +23 -0
- package/dist/utils/toolParameterTransforms.d.ts.map +1 -0
- package/dist/utils/toolParameterTransforms.js +77 -0
- package/package.json +6 -5
- package/src/cli.tsx +3 -1
- package/src/components/App.tsx +7 -3
- package/src/components/BashHistorySelector.tsx +26 -3
- package/src/components/ChatInterface.tsx +29 -15
- package/src/components/Confirmation.tsx +253 -0
- package/src/components/DiffDisplay.tsx +300 -0
- package/src/components/FileSelector.tsx +2 -4
- package/src/components/InputBox.tsx +37 -14
- package/src/components/MemoryDisplay.tsx +1 -1
- package/src/components/MessageItem.tsx +4 -12
- package/src/components/MessageList.tsx +0 -2
- package/src/components/ReasoningDisplay.tsx +33 -0
- package/src/components/ToolResultDisplay.tsx +4 -0
- package/src/contexts/useChat.tsx +178 -14
- package/src/hooks/useInputManager.ts +19 -0
- package/src/index.ts +34 -4
- package/src/managers/InputManager.ts +46 -2
- package/src/print-cli.ts +42 -2
- package/src/utils/toolParameterTransforms.ts +104 -0
- package/dist/components/DiffViewer.d.ts +0 -9
- package/dist/components/DiffViewer.d.ts.map +0 -1
- package/dist/components/DiffViewer.js +0 -221
- package/dist/utils/fileSearch.d.ts +0 -20
- package/dist/utils/fileSearch.d.ts.map +0 -1
- package/dist/utils/fileSearch.js +0 -102
- package/src/components/DiffViewer.tsx +0 -323
- package/src/utils/fileSearch.ts +0 -133
package/src/print-cli.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface PrintCliOptions {
|
|
|
6
6
|
continueLastSession?: boolean;
|
|
7
7
|
message?: string;
|
|
8
8
|
showStats?: boolean;
|
|
9
|
+
bypassPermissions?: boolean;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
function displayTimingInfo(startTime: Date, showStats: boolean): void {
|
|
@@ -29,6 +30,7 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
29
30
|
continueLastSession,
|
|
30
31
|
message,
|
|
31
32
|
showStats = false,
|
|
33
|
+
bypassPermissions,
|
|
32
34
|
} = options;
|
|
33
35
|
|
|
34
36
|
if (
|
|
@@ -43,14 +45,31 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
let agent: Agent;
|
|
48
|
+
let isReasoning = false;
|
|
49
|
+
let isContent = false;
|
|
50
|
+
const subagentReasoningStates = new Map<string, boolean>();
|
|
51
|
+
const subagentContentStates = new Map<string, boolean>();
|
|
46
52
|
|
|
47
53
|
// Setup callbacks for agent
|
|
48
54
|
const callbacks: AgentCallbacks = {
|
|
49
55
|
onAssistantMessageAdded: () => {
|
|
56
|
+
isReasoning = false;
|
|
57
|
+
isContent = false;
|
|
50
58
|
// Assistant message started - no content to output yet
|
|
51
59
|
process.stdout.write("\n");
|
|
52
60
|
},
|
|
61
|
+
onAssistantReasoningUpdated: (chunk: string) => {
|
|
62
|
+
if (!isReasoning) {
|
|
63
|
+
process.stdout.write("💭 Reasoning:\n");
|
|
64
|
+
isReasoning = true;
|
|
65
|
+
}
|
|
66
|
+
process.stdout.write(chunk);
|
|
67
|
+
},
|
|
53
68
|
onAssistantContentUpdated: (chunk: string) => {
|
|
69
|
+
if (isReasoning && !isContent) {
|
|
70
|
+
process.stdout.write("\n\n📝 Response:\n");
|
|
71
|
+
isContent = true;
|
|
72
|
+
}
|
|
54
73
|
// FR-001: Stream content updates for real-time display - output only the new chunk
|
|
55
74
|
process.stdout.write(chunk);
|
|
56
75
|
},
|
|
@@ -87,11 +106,30 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
87
106
|
process.stdout.write(` ${statusIcon} Subagent status: ${status}\n`);
|
|
88
107
|
},
|
|
89
108
|
// Subagent message callbacks
|
|
90
|
-
onSubagentAssistantMessageAdded: () => {
|
|
109
|
+
onSubagentAssistantMessageAdded: (subagentId: string) => {
|
|
110
|
+
subagentReasoningStates.set(subagentId, false);
|
|
111
|
+
subagentContentStates.set(subagentId, false);
|
|
91
112
|
// Subagent assistant message started - add indentation
|
|
92
113
|
process.stdout.write("\n ");
|
|
93
114
|
},
|
|
94
|
-
|
|
115
|
+
onSubagentAssistantReasoningUpdated: (
|
|
116
|
+
subagentId: string,
|
|
117
|
+
chunk: string,
|
|
118
|
+
) => {
|
|
119
|
+
if (!subagentReasoningStates.get(subagentId)) {
|
|
120
|
+
process.stdout.write("💭 Reasoning: ");
|
|
121
|
+
subagentReasoningStates.set(subagentId, true);
|
|
122
|
+
}
|
|
123
|
+
process.stdout.write(chunk);
|
|
124
|
+
},
|
|
125
|
+
onSubagentAssistantContentUpdated: (subagentId: string, chunk: string) => {
|
|
126
|
+
if (
|
|
127
|
+
subagentReasoningStates.get(subagentId) &&
|
|
128
|
+
!subagentContentStates.get(subagentId)
|
|
129
|
+
) {
|
|
130
|
+
process.stdout.write("\n 📝 Response: ");
|
|
131
|
+
subagentContentStates.set(subagentId, true);
|
|
132
|
+
}
|
|
95
133
|
// Stream subagent content with indentation - output only the new chunk
|
|
96
134
|
process.stdout.write(chunk);
|
|
97
135
|
},
|
|
@@ -112,6 +150,8 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
112
150
|
callbacks,
|
|
113
151
|
restoreSessionId,
|
|
114
152
|
continueLastSession,
|
|
153
|
+
permissionMode: bypassPermissions ? "bypassPermissions" : undefined,
|
|
154
|
+
// 保持流式模式以获得更好的命令行用户体验
|
|
115
155
|
});
|
|
116
156
|
|
|
117
157
|
// Send message if provided and not empty
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool parameter transformation utilities for UI rendering
|
|
3
|
+
* Forces type judgment based on tool name using type assertions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
type Change,
|
|
8
|
+
type WriteToolParameters,
|
|
9
|
+
type EditToolParameters,
|
|
10
|
+
type MultiEditToolParameters,
|
|
11
|
+
} from "wave-agent-sdk";
|
|
12
|
+
import { logger } from "./logger.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parse tool block parameters
|
|
16
|
+
*/
|
|
17
|
+
function parseToolParameters(parameters: string): unknown {
|
|
18
|
+
if (!parameters) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(parameters);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
logger.warn("Failed to parse tool parameters:", error);
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Transform Write tool parameters to changes
|
|
32
|
+
*/
|
|
33
|
+
export function transformWriteParameters(
|
|
34
|
+
parameters: WriteToolParameters,
|
|
35
|
+
): Change[] {
|
|
36
|
+
return [
|
|
37
|
+
{
|
|
38
|
+
oldContent: "", // No previous content for write operations
|
|
39
|
+
newContent: parameters.content,
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Transform Edit tool parameters to changes
|
|
46
|
+
*/
|
|
47
|
+
export function transformEditParameters(
|
|
48
|
+
parameters: EditToolParameters,
|
|
49
|
+
): Change[] {
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
oldContent: parameters.old_string,
|
|
53
|
+
newContent: parameters.new_string,
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Transform MultiEdit tool parameters to changes
|
|
60
|
+
*/
|
|
61
|
+
export function transformMultiEditParameters(
|
|
62
|
+
parameters: MultiEditToolParameters,
|
|
63
|
+
): Change[] {
|
|
64
|
+
return parameters.edits.map((edit) => ({
|
|
65
|
+
oldContent: edit.old_string,
|
|
66
|
+
newContent: edit.new_string,
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Transform tool block parameters into standardized Change[] array for diff display
|
|
72
|
+
* Forces type judgment based on tool name using type assertions
|
|
73
|
+
*/
|
|
74
|
+
export function transformToolBlockToChanges(
|
|
75
|
+
toolName: string,
|
|
76
|
+
parameters: string,
|
|
77
|
+
): Change[] {
|
|
78
|
+
try {
|
|
79
|
+
if (!toolName) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const parsedParams = parseToolParameters(parameters);
|
|
84
|
+
|
|
85
|
+
switch (toolName) {
|
|
86
|
+
case "Write":
|
|
87
|
+
return transformWriteParameters(parsedParams as WriteToolParameters);
|
|
88
|
+
|
|
89
|
+
case "Edit":
|
|
90
|
+
return transformEditParameters(parsedParams as EditToolParameters);
|
|
91
|
+
|
|
92
|
+
case "MultiEdit":
|
|
93
|
+
return transformMultiEditParameters(
|
|
94
|
+
parsedParams as MultiEditToolParameters,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
default:
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
logger.warn("Failed to transform tool block to changes:", error);
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { DiffBlock } from "wave-agent-sdk";
|
|
3
|
-
interface DiffViewerProps {
|
|
4
|
-
block: DiffBlock;
|
|
5
|
-
isStatic?: boolean;
|
|
6
|
-
}
|
|
7
|
-
export declare const DiffViewer: React.FC<DiffViewerProps>;
|
|
8
|
-
export {};
|
|
9
|
-
//# sourceMappingURL=DiffViewer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DiffViewer.d.ts","sourceRoot":"","sources":["../../src/components/DiffViewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,UAAU,eAAe;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAwCD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAkRhD,CAAC"}
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo } from "react";
|
|
3
|
-
import { Text, Box } from "ink";
|
|
4
|
-
import { diffWords } from "diff";
|
|
5
|
-
// Render word-level diff
|
|
6
|
-
const renderWordLevelDiff = (removedLine, addedLine) => {
|
|
7
|
-
const changes = diffWords(removedLine, addedLine);
|
|
8
|
-
const removedParts = [];
|
|
9
|
-
const addedParts = [];
|
|
10
|
-
changes.forEach((part, index) => {
|
|
11
|
-
if (part.removed) {
|
|
12
|
-
removedParts.push(_jsx(Text, { color: "black", backgroundColor: "red", children: part.value }, `removed-${index}`));
|
|
13
|
-
}
|
|
14
|
-
else if (part.added) {
|
|
15
|
-
addedParts.push(_jsx(Text, { color: "black", backgroundColor: "green", children: part.value }, `added-${index}`));
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
// Unchanged parts, need to display on both sides
|
|
19
|
-
removedParts.push(_jsx(Text, { color: "red", children: part.value }, `removed-unchanged-${index}`));
|
|
20
|
-
addedParts.push(_jsx(Text, { color: "green", children: part.value }, `added-unchanged-${index}`));
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
return { removedParts, addedParts };
|
|
24
|
-
};
|
|
25
|
-
export const DiffViewer = ({ block, isStatic = true, }) => {
|
|
26
|
-
const { diffResult } = block;
|
|
27
|
-
const diffLines = useMemo(() => {
|
|
28
|
-
if (!diffResult)
|
|
29
|
-
return [];
|
|
30
|
-
const lines = [];
|
|
31
|
-
let originalLineNum = 1;
|
|
32
|
-
let modifiedLineNum = 1;
|
|
33
|
-
const maxContext = 3; // Show at most 3 lines of context
|
|
34
|
-
// Buffer for storing context
|
|
35
|
-
let contextBuffer = [];
|
|
36
|
-
let hasAnyChanges = false;
|
|
37
|
-
let afterChangeContext = 0;
|
|
38
|
-
// Temporarily store adjacent deleted and added lines for word-level comparison
|
|
39
|
-
let pendingRemovedLines = [];
|
|
40
|
-
const flushPendingLines = () => {
|
|
41
|
-
pendingRemovedLines.forEach((line) => {
|
|
42
|
-
lines.push({
|
|
43
|
-
content: line.content,
|
|
44
|
-
type: "removed",
|
|
45
|
-
lineNumber: line.lineNumber,
|
|
46
|
-
rawContent: line.rawContent,
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
pendingRemovedLines = [];
|
|
50
|
-
};
|
|
51
|
-
diffResult.forEach((part) => {
|
|
52
|
-
const partLines = part.value.split("\n");
|
|
53
|
-
// Remove the last empty line (produced by split)
|
|
54
|
-
if (partLines[partLines.length - 1] === "") {
|
|
55
|
-
partLines.pop();
|
|
56
|
-
}
|
|
57
|
-
if (part.removed) {
|
|
58
|
-
// If this is the first change encountered, add preceding context
|
|
59
|
-
if (!hasAnyChanges) {
|
|
60
|
-
// Take the last few lines from the buffer as preceding context
|
|
61
|
-
const preContext = contextBuffer.slice(-maxContext);
|
|
62
|
-
if (contextBuffer.length > maxContext) {
|
|
63
|
-
lines.push({
|
|
64
|
-
content: "...",
|
|
65
|
-
type: "separator",
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
lines.push(...preContext);
|
|
69
|
-
}
|
|
70
|
-
else if (afterChangeContext > maxContext) {
|
|
71
|
-
// If there's too much context after the previous change, add a separator
|
|
72
|
-
lines.push({
|
|
73
|
-
content: "...",
|
|
74
|
-
type: "separator",
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
// Temporarily store deleted lines, waiting for possible added lines for word-level comparison
|
|
78
|
-
partLines.forEach((line) => {
|
|
79
|
-
pendingRemovedLines.push({
|
|
80
|
-
content: `- ${line}`,
|
|
81
|
-
rawContent: line,
|
|
82
|
-
lineNumber: originalLineNum++,
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
hasAnyChanges = true;
|
|
86
|
-
afterChangeContext = 0;
|
|
87
|
-
contextBuffer = []; // Clear buffer
|
|
88
|
-
}
|
|
89
|
-
else if (part.added) {
|
|
90
|
-
// If this is the first change encountered, add preceding context
|
|
91
|
-
if (!hasAnyChanges) {
|
|
92
|
-
const preContext = contextBuffer.slice(-maxContext);
|
|
93
|
-
if (contextBuffer.length > maxContext) {
|
|
94
|
-
lines.push({
|
|
95
|
-
content: "...",
|
|
96
|
-
type: "separator",
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
lines.push(...preContext);
|
|
100
|
-
}
|
|
101
|
-
else if (afterChangeContext > maxContext) {
|
|
102
|
-
lines.push({
|
|
103
|
-
content: "...",
|
|
104
|
-
type: "separator",
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
// Process added lines, try to do word-level comparison with pending deleted lines
|
|
108
|
-
partLines.forEach((line, index) => {
|
|
109
|
-
if (index < pendingRemovedLines.length) {
|
|
110
|
-
// Has corresponding deleted line, perform word-level comparison
|
|
111
|
-
const removedLine = pendingRemovedLines[index];
|
|
112
|
-
const wordDiff = renderWordLevelDiff(removedLine.rawContent, line);
|
|
113
|
-
// Add deleted line (with word-level highlighting)
|
|
114
|
-
lines.push({
|
|
115
|
-
content: `- ${removedLine.rawContent}`,
|
|
116
|
-
type: "removed",
|
|
117
|
-
lineNumber: removedLine.lineNumber,
|
|
118
|
-
rawContent: removedLine.rawContent,
|
|
119
|
-
wordDiff: {
|
|
120
|
-
removedParts: wordDiff.removedParts,
|
|
121
|
-
addedParts: [],
|
|
122
|
-
},
|
|
123
|
-
});
|
|
124
|
-
// Add added line (with word-level highlighting)
|
|
125
|
-
lines.push({
|
|
126
|
-
content: `+ ${line}`,
|
|
127
|
-
type: "added",
|
|
128
|
-
lineNumber: modifiedLineNum++,
|
|
129
|
-
rawContent: line,
|
|
130
|
-
wordDiff: { removedParts: [], addedParts: wordDiff.addedParts },
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
// No corresponding deleted line, directly add the added line
|
|
135
|
-
lines.push({
|
|
136
|
-
content: `+ ${line}`,
|
|
137
|
-
type: "added",
|
|
138
|
-
lineNumber: modifiedLineNum++,
|
|
139
|
-
rawContent: line,
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
// If there are more deleted lines than added lines, add remaining deleted lines
|
|
144
|
-
for (let i = partLines.length; i < pendingRemovedLines.length; i++) {
|
|
145
|
-
const removedLine = pendingRemovedLines[i];
|
|
146
|
-
lines.push({
|
|
147
|
-
content: removedLine.content,
|
|
148
|
-
type: "removed",
|
|
149
|
-
lineNumber: removedLine.lineNumber,
|
|
150
|
-
rawContent: removedLine.rawContent,
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
pendingRemovedLines = []; // Clear pending deleted lines
|
|
154
|
-
hasAnyChanges = true;
|
|
155
|
-
afterChangeContext = 0;
|
|
156
|
-
contextBuffer = [];
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
// Before processing unchanged lines, first clear pending deleted lines
|
|
160
|
-
flushPendingLines();
|
|
161
|
-
// Process unchanged lines
|
|
162
|
-
partLines.forEach((line) => {
|
|
163
|
-
const contextLine = {
|
|
164
|
-
content: ` ${line}`,
|
|
165
|
-
type: "unchanged",
|
|
166
|
-
lineNumber: originalLineNum,
|
|
167
|
-
};
|
|
168
|
-
if (hasAnyChanges) {
|
|
169
|
-
// If there are already changes, these are post-change context
|
|
170
|
-
if (afterChangeContext < maxContext) {
|
|
171
|
-
lines.push(contextLine);
|
|
172
|
-
afterChangeContext++;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
// If no changes yet, add to buffer
|
|
177
|
-
contextBuffer.push(contextLine);
|
|
178
|
-
}
|
|
179
|
-
originalLineNum++;
|
|
180
|
-
modifiedLineNum++;
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
// Handle remaining deleted lines at the end
|
|
185
|
-
flushPendingLines();
|
|
186
|
-
return lines;
|
|
187
|
-
}, [diffResult]);
|
|
188
|
-
// Truncate to last 10 lines for non-static items
|
|
189
|
-
const displayLines = useMemo(() => {
|
|
190
|
-
if (isStatic) {
|
|
191
|
-
return diffLines;
|
|
192
|
-
}
|
|
193
|
-
const MAX_LINES = 10;
|
|
194
|
-
if (diffLines.length <= MAX_LINES) {
|
|
195
|
-
return diffLines;
|
|
196
|
-
}
|
|
197
|
-
return diffLines.slice(-MAX_LINES);
|
|
198
|
-
}, [diffLines, isStatic]);
|
|
199
|
-
if (!diffResult || diffResult.length === 0) {
|
|
200
|
-
return (_jsx(Box, { flexDirection: "column", children: _jsx(Text, { color: "gray", children: "No changes detected" }) }));
|
|
201
|
-
}
|
|
202
|
-
// Show traditional diff view
|
|
203
|
-
return (_jsx(Box, { flexDirection: "column", children: _jsx(Box, { flexDirection: "column", children: _jsx(Box, { flexDirection: "column", children: displayLines.map((line, index) => {
|
|
204
|
-
// If has word-level diff, render special effects
|
|
205
|
-
if (line.wordDiff) {
|
|
206
|
-
const prefix = line.type === "removed" ? "- " : "+ ";
|
|
207
|
-
const parts = line.type === "removed"
|
|
208
|
-
? line.wordDiff.removedParts
|
|
209
|
-
: line.wordDiff.addedParts;
|
|
210
|
-
return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: line.type === "removed" ? "red" : "green", children: prefix }), _jsx(Box, { flexDirection: "row", flexWrap: "wrap", children: parts })] }, index));
|
|
211
|
-
}
|
|
212
|
-
// Normal rendering
|
|
213
|
-
return (_jsx(Text, { color: line.type === "added"
|
|
214
|
-
? "green"
|
|
215
|
-
: line.type === "removed"
|
|
216
|
-
? "red"
|
|
217
|
-
: line.type === "separator"
|
|
218
|
-
? "gray"
|
|
219
|
-
: "white", dimColor: line.type === "separator", children: line.content }, index));
|
|
220
|
-
}) }) }) }));
|
|
221
|
-
};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export interface FileItem {
|
|
2
|
-
path: string;
|
|
3
|
-
type: "file" | "directory";
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* Check if path is a directory
|
|
7
|
-
*/
|
|
8
|
-
export declare const isDirectory: (filePath: string) => boolean;
|
|
9
|
-
/**
|
|
10
|
-
* Convert string paths to FileItem objects
|
|
11
|
-
*/
|
|
12
|
-
export declare const convertToFileItems: (paths: string[]) => FileItem[];
|
|
13
|
-
/**
|
|
14
|
-
* Search files and directories using glob patterns
|
|
15
|
-
*/
|
|
16
|
-
export declare const searchFiles: (query: string, options?: {
|
|
17
|
-
maxResults?: number;
|
|
18
|
-
workingDirectory?: string;
|
|
19
|
-
}) => Promise<FileItem[]>;
|
|
20
|
-
//# sourceMappingURL=fileSearch.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fileSearch.d.ts","sourceRoot":"","sources":["../../src/utils/fileSearch.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;CAC5B;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,UAAU,MAAM,KAAG,OAS9C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,EAAE,KAAG,QAAQ,EAK5D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,GACtB,OAAO,MAAM,EACb,UAAU;IACR,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,KACA,OAAO,CAAC,QAAQ,EAAE,CAyFpB,CAAC"}
|
package/dist/utils/fileSearch.js
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { glob } from "glob";
|
|
2
|
-
import { getGlobIgnorePatterns } from "wave-agent-sdk";
|
|
3
|
-
import * as fs from "fs";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
/**
|
|
6
|
-
* Check if path is a directory
|
|
7
|
-
*/
|
|
8
|
-
export const isDirectory = (filePath) => {
|
|
9
|
-
try {
|
|
10
|
-
const fullPath = path.isAbsolute(filePath)
|
|
11
|
-
? filePath
|
|
12
|
-
: path.join(process.cwd(), filePath);
|
|
13
|
-
return fs.statSync(fullPath).isDirectory();
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
/**
|
|
20
|
-
* Convert string paths to FileItem objects
|
|
21
|
-
*/
|
|
22
|
-
export const convertToFileItems = (paths) => {
|
|
23
|
-
return paths.map((filePath) => ({
|
|
24
|
-
path: filePath,
|
|
25
|
-
type: isDirectory(filePath) ? "directory" : "file",
|
|
26
|
-
}));
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* Search files and directories using glob patterns
|
|
30
|
-
*/
|
|
31
|
-
export const searchFiles = async (query, options) => {
|
|
32
|
-
const { maxResults = 10, workingDirectory = process.cwd() } = options || {};
|
|
33
|
-
try {
|
|
34
|
-
let files = [];
|
|
35
|
-
let directories = [];
|
|
36
|
-
const globOptions = {
|
|
37
|
-
ignore: getGlobIgnorePatterns(workingDirectory),
|
|
38
|
-
maxDepth: 10,
|
|
39
|
-
nocase: true, // Case insensitive
|
|
40
|
-
dot: true, // Include hidden files and directories
|
|
41
|
-
cwd: workingDirectory, // Specify search root directory
|
|
42
|
-
};
|
|
43
|
-
if (!query.trim()) {
|
|
44
|
-
// When query is empty, show some common file types and directories
|
|
45
|
-
const commonPatterns = [
|
|
46
|
-
"**/*.ts",
|
|
47
|
-
"**/*.tsx",
|
|
48
|
-
"**/*.js",
|
|
49
|
-
"**/*.jsx",
|
|
50
|
-
"**/*.json",
|
|
51
|
-
];
|
|
52
|
-
// Search files
|
|
53
|
-
const filePromises = commonPatterns.map((pattern) => glob(pattern, { ...globOptions, nodir: true }));
|
|
54
|
-
// Search directories (only search first level to avoid too many results)
|
|
55
|
-
const dirPromises = [glob("*/", { ...globOptions, maxDepth: 1 })];
|
|
56
|
-
const fileResults = await Promise.all(filePromises);
|
|
57
|
-
const dirResults = await Promise.all(dirPromises);
|
|
58
|
-
files = fileResults.flat();
|
|
59
|
-
directories = dirResults.flat().map((dir) => {
|
|
60
|
-
// glob returns string type paths, remove trailing slash
|
|
61
|
-
return String(dir).replace(/\/$/, "");
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
// Build multiple glob patterns to support more flexible search
|
|
66
|
-
const filePatterns = [
|
|
67
|
-
// Match files with filenames containing query
|
|
68
|
-
`**/*${query}*`,
|
|
69
|
-
// Match files with query in path (match directory names)
|
|
70
|
-
`**/${query}*/**/*`,
|
|
71
|
-
];
|
|
72
|
-
const dirPatterns = [
|
|
73
|
-
// Match directory names containing query
|
|
74
|
-
`**/*${query}*/`,
|
|
75
|
-
// Match directories containing query in path
|
|
76
|
-
`**/${query}*/`,
|
|
77
|
-
];
|
|
78
|
-
// Search files
|
|
79
|
-
const filePromises = filePatterns.map((pattern) => glob(pattern, { ...globOptions, nodir: true }));
|
|
80
|
-
// Search directories
|
|
81
|
-
const dirPromises = dirPatterns.map((pattern) => glob(pattern, { ...globOptions, nodir: false }));
|
|
82
|
-
const fileResults = await Promise.all(filePromises);
|
|
83
|
-
const dirResults = await Promise.all(dirPromises);
|
|
84
|
-
files = fileResults.flat();
|
|
85
|
-
directories = dirResults.flat().map((dir) => {
|
|
86
|
-
// glob returns string type paths, remove trailing slash
|
|
87
|
-
return String(dir).replace(/\/$/, "");
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
// Deduplicate and merge files and directories
|
|
91
|
-
const uniqueFiles = Array.from(new Set(files));
|
|
92
|
-
const uniqueDirectories = Array.from(new Set(directories));
|
|
93
|
-
const allPaths = [...uniqueDirectories, ...uniqueFiles]; // Directories first
|
|
94
|
-
// Limit to maximum results and convert to FileItem
|
|
95
|
-
const fileItems = convertToFileItems(allPaths.slice(0, maxResults));
|
|
96
|
-
return fileItems;
|
|
97
|
-
}
|
|
98
|
-
catch (error) {
|
|
99
|
-
console.error("Glob search error:", error);
|
|
100
|
-
return [];
|
|
101
|
-
}
|
|
102
|
-
};
|