@oh-my-pi/pi-coding-agent 6.2.0 → 6.7.67
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/CHANGELOG.md +60 -0
- package/docs/sdk.md +1 -1
- package/package.json +5 -5
- package/scripts/generate-template.ts +6 -6
- package/src/cli/args.ts +3 -0
- package/src/core/agent-session.ts +39 -0
- package/src/core/bash-executor.ts +3 -3
- package/src/core/cursor/exec-bridge.ts +95 -88
- package/src/core/custom-commands/bundled/review/index.ts +142 -145
- package/src/core/custom-commands/bundled/wt/index.ts +68 -66
- package/src/core/custom-commands/loader.ts +4 -6
- package/src/core/custom-tools/index.ts +2 -2
- package/src/core/custom-tools/loader.ts +66 -61
- package/src/core/custom-tools/types.ts +4 -4
- package/src/core/custom-tools/wrapper.ts +61 -25
- package/src/core/event-bus.ts +19 -47
- package/src/core/extensions/index.ts +8 -4
- package/src/core/extensions/loader.ts +160 -120
- package/src/core/extensions/types.ts +4 -4
- package/src/core/extensions/wrapper.ts +149 -100
- package/src/core/hooks/index.ts +1 -1
- package/src/core/hooks/tool-wrapper.ts +96 -70
- package/src/core/hooks/types.ts +1 -2
- package/src/core/index.ts +1 -0
- package/src/core/mcp/index.ts +6 -2
- package/src/core/mcp/json-rpc.ts +88 -0
- package/src/core/mcp/loader.ts +22 -4
- package/src/core/mcp/manager.ts +202 -48
- package/src/core/mcp/tool-bridge.ts +143 -55
- package/src/core/mcp/tool-cache.ts +122 -0
- package/src/core/python-executor.ts +3 -9
- package/src/core/sdk.ts +33 -32
- package/src/core/session-manager.ts +30 -0
- package/src/core/settings-manager.ts +54 -1
- package/src/core/ssh/ssh-executor.ts +6 -84
- package/src/core/streaming-output.ts +107 -53
- package/src/core/tools/ask.ts +92 -93
- package/src/core/tools/bash.ts +103 -94
- package/src/core/tools/calculator.ts +41 -26
- package/src/core/tools/complete.ts +76 -66
- package/src/core/tools/context.ts +22 -24
- package/src/core/tools/exa/index.ts +1 -1
- package/src/core/tools/exa/mcp-client.ts +56 -101
- package/src/core/tools/find.ts +250 -253
- package/src/core/tools/git.ts +39 -33
- package/src/core/tools/grep.ts +440 -427
- package/src/core/tools/index.ts +63 -61
- package/src/core/tools/ls.ts +119 -114
- package/src/core/tools/lsp/clients/biome-client.ts +5 -7
- package/src/core/tools/lsp/clients/index.ts +4 -4
- package/src/core/tools/lsp/clients/lsp-linter-client.ts +5 -7
- package/src/core/tools/lsp/config.ts +2 -2
- package/src/core/tools/lsp/index.ts +604 -578
- package/src/core/tools/notebook.ts +121 -119
- package/src/core/tools/output.ts +163 -147
- package/src/core/tools/patch/applicator.ts +1100 -0
- package/src/core/tools/patch/diff.ts +362 -0
- package/src/core/tools/patch/fuzzy.ts +647 -0
- package/src/core/tools/patch/index.ts +430 -0
- package/src/core/tools/patch/normalize.ts +220 -0
- package/src/core/tools/patch/normative.ts +73 -0
- package/src/core/tools/patch/parser.ts +528 -0
- package/src/core/tools/patch/shared.ts +257 -0
- package/src/core/tools/patch/types.ts +244 -0
- package/src/core/tools/python.ts +139 -136
- package/src/core/tools/read.ts +239 -216
- package/src/core/tools/render-utils.ts +196 -77
- package/src/core/tools/renderers.ts +6 -2
- package/src/core/tools/ssh.ts +99 -80
- package/src/core/tools/task/executor.ts +11 -7
- package/src/core/tools/task/index.ts +352 -343
- package/src/core/tools/task/worker.ts +13 -23
- package/src/core/tools/todo-write.ts +74 -59
- package/src/core/tools/web-fetch.ts +54 -47
- package/src/core/tools/web-search/index.ts +27 -16
- package/src/core/tools/write.ts +108 -47
- package/src/core/ttsr.ts +106 -152
- package/src/core/voice.ts +49 -39
- package/src/index.ts +16 -12
- package/src/lib/worktree/index.ts +1 -9
- package/src/modes/interactive/components/diff.ts +15 -8
- package/src/modes/interactive/components/settings-defs.ts +42 -0
- package/src/modes/interactive/components/tool-execution.ts +46 -8
- package/src/modes/interactive/controllers/event-controller.ts +6 -19
- package/src/modes/interactive/controllers/input-controller.ts +1 -1
- package/src/modes/interactive/utils/ui-helpers.ts +5 -1
- package/src/modes/rpc/rpc-mode.ts +99 -81
- package/src/prompts/tools/patch.md +76 -0
- package/src/prompts/tools/read.md +1 -1
- package/src/prompts/tools/{edit.md → replace.md} +1 -0
- package/src/utils/shell.ts +0 -40
- package/src/core/tools/edit-diff.ts +0 -574
- package/src/core/tools/edit.ts +0 -345
package/src/core/ttsr.ts
CHANGED
|
@@ -21,44 +21,6 @@ interface InjectionRecord {
|
|
|
21
21
|
lastInjectedAt: number;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export interface TtsrManager {
|
|
25
|
-
/** Add a TTSR rule to be monitored */
|
|
26
|
-
addRule(rule: Rule): void;
|
|
27
|
-
|
|
28
|
-
/** Check if any uninjected TTSR matches the stream buffer. Returns matching rules. */
|
|
29
|
-
check(streamBuffer: string): Rule[];
|
|
30
|
-
|
|
31
|
-
/** Mark rules as injected (won't trigger again until conditions allow) */
|
|
32
|
-
markInjected(rules: Rule[]): void;
|
|
33
|
-
|
|
34
|
-
/** Get names of all injected rules (for persistence) */
|
|
35
|
-
getInjectedRuleNames(): string[];
|
|
36
|
-
|
|
37
|
-
/** Restore injected state from a list of rule names */
|
|
38
|
-
restoreInjected(ruleNames: string[]): void;
|
|
39
|
-
|
|
40
|
-
/** Reset stream buffer (called on new turn) */
|
|
41
|
-
resetBuffer(): void;
|
|
42
|
-
|
|
43
|
-
/** Get current stream buffer */
|
|
44
|
-
getBuffer(): string;
|
|
45
|
-
|
|
46
|
-
/** Append to stream buffer */
|
|
47
|
-
appendToBuffer(text: string): void;
|
|
48
|
-
|
|
49
|
-
/** Check if any TTSRs are registered */
|
|
50
|
-
hasRules(): boolean;
|
|
51
|
-
|
|
52
|
-
/** Increment message counter (call after each turn) */
|
|
53
|
-
incrementMessageCount(): void;
|
|
54
|
-
|
|
55
|
-
/** Get current message count */
|
|
56
|
-
getMessageCount(): number;
|
|
57
|
-
|
|
58
|
-
/** Get settings */
|
|
59
|
-
getSettings(): Required<TtsrSettings>;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
24
|
const DEFAULT_SETTINGS: Required<TtsrSettings> = {
|
|
63
25
|
enabled: true,
|
|
64
26
|
contextMode: "discard",
|
|
@@ -66,146 +28,138 @@ const DEFAULT_SETTINGS: Required<TtsrSettings> = {
|
|
|
66
28
|
repeatGap: 10,
|
|
67
29
|
};
|
|
68
30
|
|
|
69
|
-
export
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
/** Map of rule name -> { rule, compiled regex } */
|
|
77
|
-
const rules = new Map<string, TtsrEntry>();
|
|
78
|
-
|
|
79
|
-
/** Map of rule name -> injection record */
|
|
80
|
-
const injectionRecords = new Map<string, InjectionRecord>();
|
|
31
|
+
export class TtsrManager {
|
|
32
|
+
private readonly settings: Required<TtsrSettings>;
|
|
33
|
+
private readonly rules = new Map<string, TtsrEntry>();
|
|
34
|
+
private readonly injectionRecords = new Map<string, InjectionRecord>();
|
|
35
|
+
private buffer = "";
|
|
36
|
+
private messageCount = 0;
|
|
81
37
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
/** Message counter for tracking gap between injections */
|
|
86
|
-
let messageCount = 0;
|
|
38
|
+
constructor(settings?: TtsrSettings) {
|
|
39
|
+
this.settings = { ...DEFAULT_SETTINGS, ...settings };
|
|
40
|
+
}
|
|
87
41
|
|
|
88
42
|
/** Check if a rule can be triggered based on repeat settings */
|
|
89
|
-
|
|
90
|
-
const record = injectionRecords.get(ruleName);
|
|
43
|
+
private canTrigger(ruleName: string): boolean {
|
|
44
|
+
const record = this.injectionRecords.get(ruleName);
|
|
91
45
|
if (!record) {
|
|
92
|
-
// Never injected, can trigger
|
|
93
46
|
return true;
|
|
94
47
|
}
|
|
95
48
|
|
|
96
|
-
if (
|
|
97
|
-
// Once mode: never trigger again after first injection
|
|
49
|
+
if (this.settings.repeatMode === "once") {
|
|
98
50
|
return false;
|
|
99
51
|
}
|
|
100
52
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return gap >= resolvedSettings.repeatGap;
|
|
53
|
+
const gap = this.messageCount - record.lastInjectedAt;
|
|
54
|
+
return gap >= this.settings.repeatGap;
|
|
104
55
|
}
|
|
105
56
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
57
|
+
/** Add a TTSR rule to be monitored */
|
|
58
|
+
addRule(rule: Rule): void {
|
|
59
|
+
if (!rule.ttsrTrigger) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (this.rules.has(rule.name)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const regex = new RegExp(rule.ttsrTrigger);
|
|
69
|
+
this.rules.set(rule.name, { rule, regex });
|
|
70
|
+
logger.debug("TTSR rule registered", {
|
|
71
|
+
ruleName: rule.name,
|
|
72
|
+
pattern: rule.ttsrTrigger,
|
|
73
|
+
});
|
|
74
|
+
} catch (err) {
|
|
75
|
+
logger.warn("TTSR rule has invalid regex pattern, skipping", {
|
|
76
|
+
ruleName: rule.name,
|
|
77
|
+
pattern: rule.ttsrTrigger,
|
|
78
|
+
error: err instanceof Error ? err.message : String(err),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
112
82
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
83
|
+
/** Check if any uninjected TTSR matches the stream buffer. Returns matching rules. */
|
|
84
|
+
check(streamBuffer: string): Rule[] {
|
|
85
|
+
const matches: Rule[] = [];
|
|
86
|
+
|
|
87
|
+
for (const [name, entry] of this.rules) {
|
|
88
|
+
if (!this.canTrigger(name)) {
|
|
89
|
+
continue;
|
|
116
90
|
}
|
|
117
91
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
ruleName: rule.name,
|
|
124
|
-
pattern: rule.ttsrTrigger,
|
|
125
|
-
});
|
|
126
|
-
} catch (err) {
|
|
127
|
-
logger.warn("TTSR rule has invalid regex pattern, skipping", {
|
|
128
|
-
ruleName: rule.name,
|
|
129
|
-
pattern: rule.ttsrTrigger,
|
|
130
|
-
error: err instanceof Error ? err.message : String(err),
|
|
92
|
+
if (entry.regex.test(streamBuffer)) {
|
|
93
|
+
matches.push(entry.rule);
|
|
94
|
+
logger.debug("TTSR pattern matched", {
|
|
95
|
+
ruleName: name,
|
|
96
|
+
pattern: entry.rule.ttsrTrigger,
|
|
131
97
|
});
|
|
132
98
|
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
check(streamBuffer: string): Rule[] {
|
|
136
|
-
const matches: Rule[] = [];
|
|
137
|
-
|
|
138
|
-
for (const [name, entry] of rules) {
|
|
139
|
-
// Skip rules that can't trigger yet
|
|
140
|
-
if (!canTrigger(name)) {
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Test the buffer against the rule's pattern
|
|
145
|
-
if (entry.regex.test(streamBuffer)) {
|
|
146
|
-
matches.push(entry.rule);
|
|
147
|
-
logger.debug("TTSR pattern matched", {
|
|
148
|
-
ruleName: name,
|
|
149
|
-
pattern: entry.rule.ttsrTrigger,
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
}
|
|
99
|
+
}
|
|
153
100
|
|
|
154
|
-
|
|
155
|
-
|
|
101
|
+
return matches;
|
|
102
|
+
}
|
|
156
103
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
}
|
|
104
|
+
/** Mark rules as injected (won't trigger again until conditions allow) */
|
|
105
|
+
markInjected(rulesToMark: Rule[]): void {
|
|
106
|
+
for (const rule of rulesToMark) {
|
|
107
|
+
this.injectionRecords.set(rule.name, { lastInjectedAt: this.messageCount });
|
|
108
|
+
logger.debug("TTSR rule marked as injected", {
|
|
109
|
+
ruleName: rule.name,
|
|
110
|
+
messageCount: this.messageCount,
|
|
111
|
+
repeatMode: this.settings.repeatMode,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
167
115
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
116
|
+
/** Get names of all injected rules (for persistence) */
|
|
117
|
+
getInjectedRuleNames(): string[] {
|
|
118
|
+
return Array.from(this.injectionRecords.keys());
|
|
119
|
+
}
|
|
171
120
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
},
|
|
121
|
+
/** Restore injected state from a list of rule names */
|
|
122
|
+
restoreInjected(ruleNames: string[]): void {
|
|
123
|
+
for (const name of ruleNames) {
|
|
124
|
+
this.injectionRecords.set(name, { lastInjectedAt: 0 });
|
|
125
|
+
}
|
|
126
|
+
if (ruleNames.length > 0) {
|
|
127
|
+
logger.debug("TTSR injected state restored", { ruleNames });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
182
130
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
131
|
+
/** Reset stream buffer (called on new turn) */
|
|
132
|
+
resetBuffer(): void {
|
|
133
|
+
this.buffer = "";
|
|
134
|
+
}
|
|
186
135
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
136
|
+
/** Get current stream buffer */
|
|
137
|
+
getBuffer(): string {
|
|
138
|
+
return this.buffer;
|
|
139
|
+
}
|
|
190
140
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
141
|
+
/** Append to stream buffer */
|
|
142
|
+
appendToBuffer(text: string): void {
|
|
143
|
+
this.buffer += text;
|
|
144
|
+
}
|
|
194
145
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
146
|
+
/** Check if any TTSRs are registered */
|
|
147
|
+
hasRules(): boolean {
|
|
148
|
+
return this.rules.size > 0;
|
|
149
|
+
}
|
|
198
150
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
151
|
+
/** Increment message counter (call after each turn) */
|
|
152
|
+
incrementMessageCount(): void {
|
|
153
|
+
this.messageCount++;
|
|
154
|
+
}
|
|
202
155
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
156
|
+
/** Get current message count */
|
|
157
|
+
getMessageCount(): number {
|
|
158
|
+
return this.messageCount;
|
|
159
|
+
}
|
|
206
160
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
161
|
+
/** Get settings */
|
|
162
|
+
getSettings(): Required<TtsrSettings> {
|
|
163
|
+
return this.settings;
|
|
164
|
+
}
|
|
211
165
|
}
|
package/src/core/voice.ts
CHANGED
|
@@ -23,6 +23,50 @@ export interface VoiceRecordingHandle {
|
|
|
23
23
|
cleanup: () => void;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export class VoiceRecording implements VoiceRecordingHandle {
|
|
27
|
+
readonly filePath: string;
|
|
28
|
+
private proc: ReturnType<typeof Bun.spawn>;
|
|
29
|
+
|
|
30
|
+
constructor(_settings: VoiceSettings) {
|
|
31
|
+
const sampleRate = DEFAULT_SAMPLE_RATE;
|
|
32
|
+
const channels = DEFAULT_CHANNELS;
|
|
33
|
+
this.filePath = join(tmpdir(), `omp-voice-${nanoid()}.wav`);
|
|
34
|
+
const command = buildRecordingCommand(this.filePath, sampleRate, channels);
|
|
35
|
+
if (!command) {
|
|
36
|
+
throw new Error("No audio recorder found (install sox, arecord, or ffmpeg).");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
logger.debug("voice: starting recorder", { command });
|
|
40
|
+
this.proc = Bun.spawn(command, {
|
|
41
|
+
stdin: "ignore",
|
|
42
|
+
stdout: "ignore",
|
|
43
|
+
stderr: "pipe",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async stop(): Promise<void> {
|
|
48
|
+
try {
|
|
49
|
+
this.proc.kill();
|
|
50
|
+
} catch {
|
|
51
|
+
// ignore
|
|
52
|
+
}
|
|
53
|
+
await this.proc.exited;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
cleanup(): void {
|
|
57
|
+
try {
|
|
58
|
+
unlinkSync(this.filePath);
|
|
59
|
+
} catch {
|
|
60
|
+
// ignore cleanup errors
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async cancel(): Promise<void> {
|
|
65
|
+
await this.stop();
|
|
66
|
+
this.cleanup();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
26
70
|
export interface VoiceTranscriptionResult {
|
|
27
71
|
text: string;
|
|
28
72
|
}
|
|
@@ -99,45 +143,11 @@ function buildRecordingCommand(filePath: string, sampleRate: number, channels: n
|
|
|
99
143
|
return null;
|
|
100
144
|
}
|
|
101
145
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (!command) {
|
|
108
|
-
throw new Error("No audio recorder found (install sox, arecord, or ffmpeg).");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
logger.debug("voice: starting recorder", { command });
|
|
112
|
-
const proc = Bun.spawn(command, {
|
|
113
|
-
stdin: "ignore",
|
|
114
|
-
stdout: "ignore",
|
|
115
|
-
stderr: "pipe",
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
const stop = async (): Promise<void> => {
|
|
119
|
-
try {
|
|
120
|
-
proc.kill();
|
|
121
|
-
} catch {
|
|
122
|
-
// ignore
|
|
123
|
-
}
|
|
124
|
-
await proc.exited;
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const cleanup = (): void => {
|
|
128
|
-
try {
|
|
129
|
-
unlinkSync(filePath);
|
|
130
|
-
} catch {
|
|
131
|
-
// ignore cleanup errors
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
const cancel = async (): Promise<void> => {
|
|
136
|
-
await stop();
|
|
137
|
-
cleanup();
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
return { filePath, stop, cancel, cleanup };
|
|
146
|
+
/**
|
|
147
|
+
* @deprecated Use `new VoiceRecording(settings)` instead.
|
|
148
|
+
*/
|
|
149
|
+
export function startVoiceRecording(settings: VoiceSettings): VoiceRecordingHandle {
|
|
150
|
+
return new VoiceRecording(settings);
|
|
141
151
|
}
|
|
142
152
|
|
|
143
153
|
export async function transcribeAudio(
|
package/src/index.ts
CHANGED
|
@@ -63,7 +63,7 @@ export type {
|
|
|
63
63
|
LoadedCustomTool,
|
|
64
64
|
RenderResultOptions,
|
|
65
65
|
} from "./core/custom-tools/index";
|
|
66
|
-
export { discoverAndLoadCustomTools, loadCustomTools } from "./core/custom-tools/index";
|
|
66
|
+
export { CustomToolLoader, discoverAndLoadCustomTools, loadCustomTools } from "./core/custom-tools/index";
|
|
67
67
|
// Extension types and utilities
|
|
68
68
|
export type {
|
|
69
69
|
AppAction,
|
|
@@ -79,7 +79,6 @@ export type {
|
|
|
79
79
|
ExtensionFactory,
|
|
80
80
|
ExtensionFlag,
|
|
81
81
|
ExtensionHandler,
|
|
82
|
-
ExtensionRuntime,
|
|
83
82
|
ExtensionShortcut,
|
|
84
83
|
ExtensionUIContext,
|
|
85
84
|
ExtensionUIDialogOptions,
|
|
@@ -97,9 +96,9 @@ export type {
|
|
|
97
96
|
UserBashEventResult,
|
|
98
97
|
} from "./core/extensions/index";
|
|
99
98
|
export {
|
|
100
|
-
createExtensionRuntime,
|
|
101
99
|
discoverAndLoadExtensions,
|
|
102
100
|
ExtensionRunner,
|
|
101
|
+
ExtensionRuntime,
|
|
103
102
|
isBashToolResult,
|
|
104
103
|
isEditToolResult,
|
|
105
104
|
isFindToolResult,
|
|
@@ -119,22 +118,16 @@ export { ModelRegistry } from "./core/model-registry";
|
|
|
119
118
|
export type { PromptTemplate } from "./core/prompt-templates";
|
|
120
119
|
// SDK for programmatic usage
|
|
121
120
|
export {
|
|
121
|
+
// Factory
|
|
122
|
+
BashTool,
|
|
122
123
|
// Tool factories
|
|
123
124
|
BUILTIN_TOOLS,
|
|
124
125
|
type BuildSystemPromptOptions,
|
|
125
126
|
buildSystemPrompt,
|
|
126
127
|
type CreateAgentSessionOptions,
|
|
127
128
|
type CreateAgentSessionResult,
|
|
128
|
-
// Factory
|
|
129
129
|
createAgentSession,
|
|
130
|
-
createBashTool,
|
|
131
|
-
createEditTool,
|
|
132
|
-
createFindTool,
|
|
133
|
-
createGrepTool,
|
|
134
|
-
createLsTool,
|
|
135
|
-
createReadTool,
|
|
136
130
|
createTools,
|
|
137
|
-
createWriteTool,
|
|
138
131
|
// Discovery
|
|
139
132
|
discoverAuthStorage,
|
|
140
133
|
discoverContextFiles,
|
|
@@ -144,8 +137,16 @@ export {
|
|
|
144
137
|
discoverModels,
|
|
145
138
|
discoverPromptTemplates,
|
|
146
139
|
discoverSkills,
|
|
140
|
+
EditTool,
|
|
141
|
+
FindTool,
|
|
142
|
+
GrepTool,
|
|
143
|
+
LsTool,
|
|
147
144
|
loadSettings,
|
|
145
|
+
loadSshTool,
|
|
146
|
+
PythonTool,
|
|
147
|
+
ReadTool,
|
|
148
148
|
type ToolSession,
|
|
149
|
+
WriteTool,
|
|
149
150
|
} from "./core/sdk";
|
|
150
151
|
export {
|
|
151
152
|
type BranchSummaryEntry,
|
|
@@ -201,11 +202,11 @@ export {
|
|
|
201
202
|
type FindToolDetails,
|
|
202
203
|
type FindToolOptions,
|
|
203
204
|
formatSize,
|
|
205
|
+
GitTool,
|
|
204
206
|
type GitToolDetails,
|
|
205
207
|
type GrepOperations,
|
|
206
208
|
type GrepToolDetails,
|
|
207
209
|
type GrepToolOptions,
|
|
208
|
-
gitTool,
|
|
209
210
|
type LsOperations,
|
|
210
211
|
type LsToolDetails,
|
|
211
212
|
type LsToolOptions,
|
|
@@ -226,6 +227,9 @@ export {
|
|
|
226
227
|
InteractiveMode,
|
|
227
228
|
type InteractiveModeOptions,
|
|
228
229
|
type PrintModeOptions,
|
|
230
|
+
RpcClient,
|
|
231
|
+
type RpcClientOptions,
|
|
232
|
+
type RpcEventListener,
|
|
229
233
|
runPrintMode,
|
|
230
234
|
runRpcMode,
|
|
231
235
|
} from "./modes/index";
|
|
@@ -2,15 +2,7 @@ export { type CollapseOptions, type CollapseResult, type CollapseStrategy, colla
|
|
|
2
2
|
export { WORKTREE_BASE } from "./constants";
|
|
3
3
|
export { WorktreeError, WorktreeErrorCode } from "./errors";
|
|
4
4
|
export { getRepoName, getRepoRoot, git, gitWithStdin } from "./git";
|
|
5
|
-
export {
|
|
6
|
-
create,
|
|
7
|
-
find,
|
|
8
|
-
list,
|
|
9
|
-
prune,
|
|
10
|
-
remove,
|
|
11
|
-
type Worktree,
|
|
12
|
-
which,
|
|
13
|
-
} from "./operations";
|
|
5
|
+
export { create, find, list, prune, remove, type Worktree, which } from "./operations";
|
|
14
6
|
export {
|
|
15
7
|
cleanupSessions,
|
|
16
8
|
createSession,
|
|
@@ -6,9 +6,9 @@ import { theme } from "../theme/theme";
|
|
|
6
6
|
* Format: "+123 content" or "-123 content" or " 123 content" or " ..."
|
|
7
7
|
*/
|
|
8
8
|
function parseDiffLine(line: string): { prefix: string; lineNum: string; content: string } | null {
|
|
9
|
-
const match = line.match(/^([+-\s])(\s*\d
|
|
9
|
+
const match = line.match(/^([+-\s])(?:(\s*\d+)\s)?(.*)$/);
|
|
10
10
|
if (!match) return null;
|
|
11
|
-
return { prefix: match[1], lineNum: match[2], content: match[3] };
|
|
11
|
+
return { prefix: match[1], lineNum: match[2] ?? "", content: match[3] };
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -80,6 +80,13 @@ export function renderDiff(diffText: string, _options: RenderDiffOptions = {}):
|
|
|
80
80
|
const lines = diffText.split("\n");
|
|
81
81
|
const result: string[] = [];
|
|
82
82
|
|
|
83
|
+
const formatLine = (prefix: string, lineNum: string, content: string): string => {
|
|
84
|
+
if (lineNum.trim().length === 0) {
|
|
85
|
+
return `${prefix}${content}`;
|
|
86
|
+
}
|
|
87
|
+
return `${prefix}${lineNum} ${content}`;
|
|
88
|
+
};
|
|
89
|
+
|
|
83
90
|
let i = 0;
|
|
84
91
|
while (i < lines.length) {
|
|
85
92
|
const line = lines[i];
|
|
@@ -121,24 +128,24 @@ export function renderDiff(diffText: string, _options: RenderDiffOptions = {}):
|
|
|
121
128
|
replaceTabs(added.content),
|
|
122
129
|
);
|
|
123
130
|
|
|
124
|
-
result.push(theme.fg("toolDiffRemoved",
|
|
125
|
-
result.push(theme.fg("toolDiffAdded",
|
|
131
|
+
result.push(theme.fg("toolDiffRemoved", formatLine("-", removed.lineNum, removedLine)));
|
|
132
|
+
result.push(theme.fg("toolDiffAdded", formatLine("+", added.lineNum, addedLine)));
|
|
126
133
|
} else {
|
|
127
134
|
// Show all removed lines first, then all added lines
|
|
128
135
|
for (const removed of removedLines) {
|
|
129
|
-
result.push(theme.fg("toolDiffRemoved",
|
|
136
|
+
result.push(theme.fg("toolDiffRemoved", formatLine("-", removed.lineNum, replaceTabs(removed.content))));
|
|
130
137
|
}
|
|
131
138
|
for (const added of addedLines) {
|
|
132
|
-
result.push(theme.fg("toolDiffAdded",
|
|
139
|
+
result.push(theme.fg("toolDiffAdded", formatLine("+", added.lineNum, replaceTabs(added.content))));
|
|
133
140
|
}
|
|
134
141
|
}
|
|
135
142
|
} else if (parsed.prefix === "+") {
|
|
136
143
|
// Standalone added line
|
|
137
|
-
result.push(theme.fg("toolDiffAdded",
|
|
144
|
+
result.push(theme.fg("toolDiffAdded", formatLine("+", parsed.lineNum, replaceTabs(parsed.content))));
|
|
138
145
|
i++;
|
|
139
146
|
} else {
|
|
140
147
|
// Context line
|
|
141
|
-
result.push(theme.fg("toolDiffContext",
|
|
148
|
+
result.push(theme.fg("toolDiffContext", formatLine(" ", parsed.lineNum, replaceTabs(parsed.content))));
|
|
142
149
|
i++;
|
|
143
150
|
}
|
|
144
151
|
}
|
|
@@ -209,6 +209,15 @@ export const SETTINGS_DEFS: SettingDef[] = [
|
|
|
209
209
|
get: (sm) => sm.getCollapseChangelog(),
|
|
210
210
|
set: (sm, v) => sm.setCollapseChangelog(v),
|
|
211
211
|
},
|
|
212
|
+
{
|
|
213
|
+
id: "normativeRewrite",
|
|
214
|
+
tab: "behavior",
|
|
215
|
+
type: "boolean",
|
|
216
|
+
label: "Normative rewrite",
|
|
217
|
+
description: "Rewrite tool call arguments to normalized format in session history",
|
|
218
|
+
get: (sm) => sm.getNormativeRewrite(),
|
|
219
|
+
set: (sm, v) => sm.setNormativeRewrite(v),
|
|
220
|
+
},
|
|
212
221
|
{
|
|
213
222
|
id: "doubleEscapeAction",
|
|
214
223
|
tab: "behavior",
|
|
@@ -288,6 +297,39 @@ export const SETTINGS_DEFS: SettingDef[] = [
|
|
|
288
297
|
get: (sm) => sm.getEditFuzzyMatch(),
|
|
289
298
|
set: (sm, v) => sm.setEditFuzzyMatch(v),
|
|
290
299
|
},
|
|
300
|
+
{
|
|
301
|
+
id: "editFuzzyThreshold",
|
|
302
|
+
tab: "tools",
|
|
303
|
+
type: "submenu",
|
|
304
|
+
label: "Edit fuzzy threshold",
|
|
305
|
+
description: "Similarity threshold for fuzzy matches (higher = stricter)",
|
|
306
|
+
get: (sm) => sm.getEditFuzzyThreshold().toFixed(2),
|
|
307
|
+
set: (sm, v) => sm.setEditFuzzyThreshold(Number(v)),
|
|
308
|
+
getOptions: () => [
|
|
309
|
+
{ value: "0.85", label: "0.85", description: "Lenient" },
|
|
310
|
+
{ value: "0.90", label: "0.90", description: "Moderate" },
|
|
311
|
+
{ value: "0.95", label: "0.95", description: "Default" },
|
|
312
|
+
{ value: "0.98", label: "0.98", description: "Strict" },
|
|
313
|
+
],
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
id: "editPatchMode",
|
|
317
|
+
tab: "tools",
|
|
318
|
+
type: "boolean",
|
|
319
|
+
label: "Edit patch mode",
|
|
320
|
+
description: "Use codex-style apply-patch format instead of oldText/newText for edits",
|
|
321
|
+
get: (sm) => sm.getEditPatchMode(),
|
|
322
|
+
set: (sm, v) => sm.setEditPatchMode(v),
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
id: "readLineNumbers",
|
|
326
|
+
tab: "tools",
|
|
327
|
+
type: "boolean",
|
|
328
|
+
label: "Read line numbers",
|
|
329
|
+
description: "Prepend line numbers to read tool output by default",
|
|
330
|
+
get: (sm) => sm.getReadLineNumbers(),
|
|
331
|
+
set: (sm, v) => sm.setReadLineNumbers(v),
|
|
332
|
+
},
|
|
291
333
|
{
|
|
292
334
|
id: "mcpProjectConfig",
|
|
293
335
|
tab: "tools",
|