@oh-my-pi/pi-coding-agent 6.1.0 → 6.7.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/CHANGELOG.md +56 -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 +34 -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 +25 -25
- 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 +62 -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 +824 -639
- 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 +49 -0
- package/src/core/tools/patch/parser.ts +528 -0
- package/src/core/tools/patch/shared.ts +228 -0
- package/src/core/tools/patch/types.ts +244 -0
- package/src/core/tools/python.ts +139 -136
- package/src/core/tools/read.ts +237 -216
- package/src/core/tools/render-utils.ts +196 -77
- package/src/core/tools/renderers.ts +1 -1
- 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 +89 -41
- 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 +24 -0
- package/src/modes/interactive/components/tool-execution.ts +34 -6
- 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 -326
|
@@ -224,154 +224,152 @@ function buildReviewPrompt(mode: string, stats: DiffStats, rawDiff: string): str
|
|
|
224
224
|
});
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
export
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
227
|
+
export class ReviewCommand implements CustomCommand {
|
|
228
|
+
name = "review";
|
|
229
|
+
description = "Launch interactive code review";
|
|
230
|
+
|
|
231
|
+
constructor(private api: CustomCommandAPI) {}
|
|
232
|
+
|
|
233
|
+
async execute(_args: string[], ctx: HookCommandContext): Promise<string | undefined> {
|
|
234
|
+
if (!ctx.hasUI) {
|
|
235
|
+
return "Use the Task tool to run the 'reviewer' agent to review recent code changes.";
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const mode = await ctx.ui.select("Review Mode", [
|
|
239
|
+
"1. Review against a base branch (PR Style)",
|
|
240
|
+
"2. Review uncommitted changes",
|
|
241
|
+
"3. Review a specific commit",
|
|
242
|
+
"4. Custom review instructions",
|
|
243
|
+
]);
|
|
244
|
+
|
|
245
|
+
if (!mode) return undefined;
|
|
246
|
+
|
|
247
|
+
const modeNum = parseInt(mode[0], 10);
|
|
248
|
+
|
|
249
|
+
switch (modeNum) {
|
|
250
|
+
case 1: {
|
|
251
|
+
// PR-style review against base branch
|
|
252
|
+
const branches = await getGitBranches(this.api);
|
|
253
|
+
if (branches.length === 0) {
|
|
254
|
+
ctx.ui.notify("No git branches found", "error");
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const baseBranch = await ctx.ui.select("Select base branch to compare against", branches);
|
|
259
|
+
if (!baseBranch) return undefined;
|
|
260
|
+
|
|
261
|
+
const currentBranch = await getCurrentBranch(this.api);
|
|
262
|
+
const diffResult = await this.api.exec("git", ["diff", `${baseBranch}...${currentBranch}`], {
|
|
263
|
+
timeout: 30000,
|
|
264
|
+
});
|
|
265
|
+
if (diffResult.code !== 0) {
|
|
266
|
+
ctx.ui.notify(`Failed to get diff: ${diffResult.stderr}`, "error");
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (!diffResult.stdout.trim()) {
|
|
271
|
+
ctx.ui.notify(`No changes between ${baseBranch} and ${currentBranch}`, "warning");
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const stats = parseDiff(diffResult.stdout);
|
|
276
|
+
if (stats.files.length === 0) {
|
|
277
|
+
ctx.ui.notify("No reviewable files (all changes filtered out)", "warning");
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return buildReviewPrompt(
|
|
282
|
+
`Reviewing changes between \`${baseBranch}\` and \`${currentBranch}\` (PR-style)`,
|
|
283
|
+
stats,
|
|
284
|
+
diffResult.stdout,
|
|
285
|
+
);
|
|
235
286
|
}
|
|
236
287
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
case 1: {
|
|
250
|
-
// PR-style review against base branch
|
|
251
|
-
const branches = await getGitBranches(api);
|
|
252
|
-
if (branches.length === 0) {
|
|
253
|
-
ctx.ui.notify("No git branches found", "error");
|
|
254
|
-
return undefined;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const baseBranch = await ctx.ui.select("Select base branch to compare against", branches);
|
|
258
|
-
if (!baseBranch) return undefined;
|
|
259
|
-
|
|
260
|
-
const currentBranch = await getCurrentBranch(api);
|
|
261
|
-
const diffResult = await api.exec("git", ["diff", `${baseBranch}...${currentBranch}`], {
|
|
262
|
-
timeout: 30000,
|
|
263
|
-
});
|
|
264
|
-
if (diffResult.code !== 0) {
|
|
265
|
-
ctx.ui.notify(`Failed to get diff: ${diffResult.stderr}`, "error");
|
|
266
|
-
return undefined;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (!diffResult.stdout.trim()) {
|
|
270
|
-
ctx.ui.notify(`No changes between ${baseBranch} and ${currentBranch}`, "warning");
|
|
271
|
-
return undefined;
|
|
272
|
-
}
|
|
288
|
+
case 2: {
|
|
289
|
+
// Uncommitted changes - combine staged and unstaged
|
|
290
|
+
const status = await getGitStatus(this.api);
|
|
291
|
+
if (!status.trim()) {
|
|
292
|
+
ctx.ui.notify("No uncommitted changes found", "warning");
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const [unstagedResult, stagedResult] = await Promise.all([
|
|
297
|
+
this.api.exec("git", ["diff"], { timeout: 30000 }),
|
|
298
|
+
this.api.exec("git", ["diff", "--cached"], { timeout: 30000 }),
|
|
299
|
+
]);
|
|
273
300
|
|
|
274
|
-
|
|
275
|
-
if (stats.files.length === 0) {
|
|
276
|
-
ctx.ui.notify("No reviewable files (all changes filtered out)", "warning");
|
|
277
|
-
return undefined;
|
|
278
|
-
}
|
|
301
|
+
const combinedDiff = [unstagedResult.stdout, stagedResult.stdout].filter(Boolean).join("\n");
|
|
279
302
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
diffResult.stdout,
|
|
284
|
-
);
|
|
303
|
+
if (!combinedDiff.trim()) {
|
|
304
|
+
ctx.ui.notify("No diff content found", "warning");
|
|
305
|
+
return undefined;
|
|
285
306
|
}
|
|
286
307
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
ctx.ui.notify("No uncommitted changes found", "warning");
|
|
292
|
-
return undefined;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const [unstagedResult, stagedResult] = await Promise.all([
|
|
296
|
-
api.exec("git", ["diff"], { timeout: 30000 }),
|
|
297
|
-
api.exec("git", ["diff", "--cached"], { timeout: 30000 }),
|
|
298
|
-
]);
|
|
299
|
-
|
|
300
|
-
const combinedDiff = [unstagedResult.stdout, stagedResult.stdout].filter(Boolean).join("\n");
|
|
301
|
-
|
|
302
|
-
if (!combinedDiff.trim()) {
|
|
303
|
-
ctx.ui.notify("No diff content found", "warning");
|
|
304
|
-
return undefined;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const stats = parseDiff(combinedDiff);
|
|
308
|
-
if (stats.files.length === 0) {
|
|
309
|
-
ctx.ui.notify("No reviewable files (all changes filtered out)", "warning");
|
|
310
|
-
return undefined;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return buildReviewPrompt("Reviewing uncommitted changes (staged + unstaged)", stats, combinedDiff);
|
|
308
|
+
const stats = parseDiff(combinedDiff);
|
|
309
|
+
if (stats.files.length === 0) {
|
|
310
|
+
ctx.ui.notify("No reviewable files (all changes filtered out)", "warning");
|
|
311
|
+
return undefined;
|
|
314
312
|
}
|
|
315
313
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
return
|
|
314
|
+
return buildReviewPrompt("Reviewing uncommitted changes (staged + unstaged)", stats, combinedDiff);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
case 3: {
|
|
318
|
+
// Specific commit
|
|
319
|
+
const commits = await getRecentCommits(this.api, 20);
|
|
320
|
+
if (commits.length === 0) {
|
|
321
|
+
ctx.ui.notify("No commits found", "error");
|
|
322
|
+
return undefined;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const selected = await ctx.ui.select("Select commit to review", commits);
|
|
326
|
+
if (!selected) return undefined;
|
|
327
|
+
|
|
328
|
+
// Extract commit hash from selection (format: "abc1234 message")
|
|
329
|
+
const hash = selected.split(" ")[0];
|
|
330
|
+
|
|
331
|
+
// Get the commit diff (with timeout)
|
|
332
|
+
const showResult = await this.api.exec("git", ["show", "--format=", hash], { timeout: 30000 });
|
|
333
|
+
if (showResult.code !== 0) {
|
|
334
|
+
ctx.ui.notify(`Failed to get commit: ${showResult.stderr}`, "error");
|
|
335
|
+
return undefined;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (!showResult.stdout.trim()) {
|
|
339
|
+
ctx.ui.notify("Commit has no diff content", "warning");
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const stats = parseDiff(showResult.stdout);
|
|
344
|
+
if (stats.files.length === 0) {
|
|
345
|
+
ctx.ui.notify("No reviewable files in commit (all changes filtered out)", "warning");
|
|
346
|
+
return undefined;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return buildReviewPrompt(`Reviewing commit \`${hash}\``, stats, showResult.stdout);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
case 4: {
|
|
353
|
+
// Custom instructions - still uses the old approach since user provides context
|
|
354
|
+
const instructions = await ctx.ui.editor("Enter custom review instructions", "Review the following:\n\n");
|
|
355
|
+
if (!instructions?.trim()) return undefined;
|
|
356
|
+
|
|
357
|
+
// For custom, we still try to get current diff for context
|
|
358
|
+
const diffResult = await this.api.exec("git", ["diff", "HEAD"], { timeout: 30000 });
|
|
359
|
+
const hasDiff = diffResult.code === 0 && diffResult.stdout.trim();
|
|
360
|
+
|
|
361
|
+
if (hasDiff) {
|
|
362
|
+
const stats = parseDiff(diffResult.stdout);
|
|
363
|
+
// Even if all files filtered, include the custom instructions
|
|
364
|
+
return `${buildReviewPrompt(
|
|
365
|
+
`Custom review: ${instructions.split("\n")[0].slice(0, 60)}...`,
|
|
366
|
+
stats,
|
|
367
|
+
diffResult.stdout,
|
|
368
|
+
)}\n\n### Additional Instructions\n\n${instructions}`;
|
|
349
369
|
}
|
|
350
370
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
const instructions = await ctx.ui.editor(
|
|
354
|
-
"Enter custom review instructions",
|
|
355
|
-
"Review the following:\n\n",
|
|
356
|
-
);
|
|
357
|
-
if (!instructions?.trim()) return undefined;
|
|
358
|
-
|
|
359
|
-
// For custom, we still try to get current diff for context
|
|
360
|
-
const diffResult = await api.exec("git", ["diff", "HEAD"], { timeout: 30000 });
|
|
361
|
-
const hasDiff = diffResult.code === 0 && diffResult.stdout.trim();
|
|
362
|
-
|
|
363
|
-
if (hasDiff) {
|
|
364
|
-
const stats = parseDiff(diffResult.stdout);
|
|
365
|
-
// Even if all files filtered, include the custom instructions
|
|
366
|
-
return `${buildReviewPrompt(
|
|
367
|
-
`Custom review: ${instructions.split("\n")[0].slice(0, 60)}...`,
|
|
368
|
-
stats,
|
|
369
|
-
diffResult.stdout,
|
|
370
|
-
)}\n\n### Additional Instructions\n\n${instructions}`;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// No diff available, just pass instructions
|
|
374
|
-
return `## Code Review Request
|
|
371
|
+
// No diff available, just pass instructions
|
|
372
|
+
return `## Code Review Request
|
|
375
373
|
|
|
376
374
|
### Mode
|
|
377
375
|
Custom review instructions
|
|
@@ -381,13 +379,12 @@ Custom review instructions
|
|
|
381
379
|
${instructions}
|
|
382
380
|
|
|
383
381
|
Use the Task tool with \`agent: "reviewer"\` to execute this review.`;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
default:
|
|
387
|
-
return undefined;
|
|
388
382
|
}
|
|
389
|
-
|
|
390
|
-
|
|
383
|
+
|
|
384
|
+
default:
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
391
388
|
}
|
|
392
389
|
|
|
393
390
|
async function getGitBranches(api: CustomCommandAPI): Promise<string[]> {
|
|
@@ -434,4 +431,4 @@ async function getRecentCommits(api: CustomCommandAPI, count: number): Promise<s
|
|
|
434
431
|
}
|
|
435
432
|
}
|
|
436
433
|
|
|
437
|
-
export default
|
|
434
|
+
export default ReviewCommand;
|
|
@@ -358,76 +358,78 @@ async function handleParallel(args: ParallelTask[], ctx: HookCommandContext): Pr
|
|
|
358
358
|
return [`Parallel execution complete (${args.length} agents)`, "", "Results:", ...mergeResults].join("\n");
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
-
export
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
case "list":
|
|
384
|
-
return await handleList(ctx);
|
|
385
|
-
case "merge": {
|
|
386
|
-
const parsed = parseFlags(rest);
|
|
387
|
-
const source = parsed.positionals[0];
|
|
388
|
-
const target = parsed.positionals[1];
|
|
389
|
-
if (!source) return formatUsage();
|
|
390
|
-
const strategyRaw = getFlagValue(parsed.flags, "strategy");
|
|
391
|
-
if (parsed.flags.get("strategy") === true) {
|
|
392
|
-
return "Missing value for --strategy";
|
|
393
|
-
}
|
|
394
|
-
const strategy = strategyRaw as CollapseStrategy | undefined;
|
|
395
|
-
const keep = getFlagBoolean(parsed.flags, "keep");
|
|
396
|
-
return await handleMerge({ source, target, strategy, keep });
|
|
397
|
-
}
|
|
398
|
-
case "rm": {
|
|
399
|
-
const parsed = parseFlags(rest);
|
|
400
|
-
const name = parsed.positionals[0];
|
|
401
|
-
if (!name) return formatUsage();
|
|
402
|
-
const force = getFlagBoolean(parsed.flags, "force");
|
|
403
|
-
return await handleRm({ name, force });
|
|
361
|
+
export class WorktreeCommand implements CustomCommand {
|
|
362
|
+
name = "wt";
|
|
363
|
+
description = "Git worktree management";
|
|
364
|
+
|
|
365
|
+
// biome-ignore lint/complexity/noUselessConstructor: interface conformance - loader passes API to all commands
|
|
366
|
+
constructor(_api: CustomCommandAPI) {}
|
|
367
|
+
|
|
368
|
+
async execute(args: string[], ctx: HookCommandContext): Promise<string | undefined> {
|
|
369
|
+
if (args.length === 0) return formatUsage();
|
|
370
|
+
|
|
371
|
+
const subcommand = args[0];
|
|
372
|
+
const rest = args.slice(1);
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
switch (subcommand) {
|
|
376
|
+
case "new": {
|
|
377
|
+
const parsed = parseFlags(rest);
|
|
378
|
+
const branch = parsed.positionals[0];
|
|
379
|
+
if (!branch) return formatUsage();
|
|
380
|
+
const base = getFlagValue(parsed.flags, "base");
|
|
381
|
+
if (parsed.flags.get("base") === true) {
|
|
382
|
+
return "Missing value for --base";
|
|
404
383
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
return
|
|
384
|
+
return await handleNew({ branch, base });
|
|
385
|
+
}
|
|
386
|
+
case "list":
|
|
387
|
+
return await handleList(ctx);
|
|
388
|
+
case "merge": {
|
|
389
|
+
const parsed = parseFlags(rest);
|
|
390
|
+
const source = parsed.positionals[0];
|
|
391
|
+
const target = parsed.positionals[1];
|
|
392
|
+
if (!source) return formatUsage();
|
|
393
|
+
const strategyRaw = getFlagValue(parsed.flags, "strategy");
|
|
394
|
+
if (parsed.flags.get("strategy") === true) {
|
|
395
|
+
return "Missing value for --strategy";
|
|
417
396
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
397
|
+
const strategy = strategyRaw as CollapseStrategy | undefined;
|
|
398
|
+
const keep = getFlagBoolean(parsed.flags, "keep");
|
|
399
|
+
return await handleMerge({ source, target, strategy, keep });
|
|
400
|
+
}
|
|
401
|
+
case "rm": {
|
|
402
|
+
const parsed = parseFlags(rest);
|
|
403
|
+
const name = parsed.positionals[0];
|
|
404
|
+
if (!name) return formatUsage();
|
|
405
|
+
const force = getFlagBoolean(parsed.flags, "force");
|
|
406
|
+
return await handleRm({ name, force });
|
|
407
|
+
}
|
|
408
|
+
case "status":
|
|
409
|
+
return await handleStatus();
|
|
410
|
+
case "spawn": {
|
|
411
|
+
const parsed = parseFlags(rest);
|
|
412
|
+
const task = parsed.positionals[0];
|
|
413
|
+
if (!task) return formatUsage();
|
|
414
|
+
const scope = getFlagValue(parsed.flags, "scope");
|
|
415
|
+
if (parsed.flags.get("scope") === true) {
|
|
416
|
+
return "Missing value for --scope";
|
|
422
417
|
}
|
|
423
|
-
|
|
424
|
-
|
|
418
|
+
const name = getFlagValue(parsed.flags, "name");
|
|
419
|
+
return await handleSpawn({ task, scope, name }, ctx);
|
|
420
|
+
}
|
|
421
|
+
case "parallel": {
|
|
422
|
+
const tasks = parseParallelTasks(rest);
|
|
423
|
+
if (tasks.length === 0) return formatUsage();
|
|
424
|
+
return await handleParallel(tasks, ctx);
|
|
425
425
|
}
|
|
426
|
-
|
|
427
|
-
|
|
426
|
+
default:
|
|
427
|
+
return formatUsage();
|
|
428
428
|
}
|
|
429
|
-
}
|
|
430
|
-
|
|
429
|
+
} catch (err) {
|
|
430
|
+
return formatError(err);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
431
433
|
}
|
|
432
434
|
|
|
433
|
-
export default
|
|
435
|
+
export default WorktreeCommand;
|
|
@@ -12,8 +12,8 @@ import { getAgentDir, getConfigDirs } from "../../config";
|
|
|
12
12
|
import * as piCodingAgent from "../../index";
|
|
13
13
|
import { execCommand } from "../exec";
|
|
14
14
|
import { logger } from "../logger";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import { ReviewCommand } from "./bundled/review";
|
|
16
|
+
import { WorktreeCommand } from "./bundled/wt";
|
|
17
17
|
import type {
|
|
18
18
|
CustomCommand,
|
|
19
19
|
CustomCommandAPI,
|
|
@@ -146,19 +146,17 @@ function loadBundledCommands(sharedApi: CustomCommandAPI): LoadedCustomCommand[]
|
|
|
146
146
|
const bundled: LoadedCustomCommand[] = [];
|
|
147
147
|
|
|
148
148
|
// Add bundled commands here
|
|
149
|
-
const reviewCommand = createReviewCommand(sharedApi);
|
|
150
149
|
bundled.push({
|
|
151
150
|
path: "bundled:review",
|
|
152
151
|
resolvedPath: "bundled:review",
|
|
153
|
-
command:
|
|
152
|
+
command: new ReviewCommand(sharedApi),
|
|
154
153
|
source: "bundled",
|
|
155
154
|
});
|
|
156
155
|
|
|
157
|
-
const worktreeCommand = createWorktreeCommand(sharedApi);
|
|
158
156
|
bundled.push({
|
|
159
157
|
path: "bundled:wt",
|
|
160
158
|
resolvedPath: "bundled:wt",
|
|
161
|
-
command:
|
|
159
|
+
command: new WorktreeCommand(sharedApi),
|
|
162
160
|
source: "bundled",
|
|
163
161
|
});
|
|
164
162
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Custom tools module.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export { discoverAndLoadCustomTools, loadCustomTools } from "./loader";
|
|
5
|
+
export { CustomToolLoader, discoverAndLoadCustomTools, loadCustomTools } from "./loader";
|
|
6
6
|
export type {
|
|
7
7
|
AgentToolResult,
|
|
8
8
|
AgentToolUpdateCallback,
|
|
@@ -19,4 +19,4 @@ export type {
|
|
|
19
19
|
RenderResultOptions,
|
|
20
20
|
ToolLoadError,
|
|
21
21
|
} from "./types";
|
|
22
|
-
export {
|
|
22
|
+
export { CustomToolAdapter } from "./wrapper";
|