pullfrog 0.0.201 → 0.0.203
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/agents/postRun.d.ts +66 -0
- package/dist/agents/reviewer.d.ts +32 -0
- package/dist/agents/sessionLabeler.d.ts +77 -0
- package/dist/agents/shared.d.ts +86 -3
- package/dist/cli.mjs +2803 -1025
- package/dist/external.d.ts +1 -1
- package/dist/index.js +2780 -1009
- package/dist/internal/index.d.ts +1 -1
- package/dist/internal.js +280 -68
- package/dist/lifecycle.d.ts +1 -1
- package/dist/mcp/checkout.d.ts +16 -2
- package/dist/mcp/comment.d.ts +1 -0
- package/dist/mcp/geminiSanitizer.d.ts +17 -0
- package/dist/mcp/git.d.ts +8 -2
- package/dist/mcp/review.d.ts +139 -0
- package/dist/mcp/server.d.ts +12 -0
- package/dist/mcp/shared.d.ts +1 -1
- package/dist/models.d.ts +17 -0
- package/dist/modes.d.ts +1 -1
- package/dist/skills/git-archaeology/SKILL.md +188 -0
- package/dist/utils/activity.d.ts +4 -0
- package/dist/utils/agent.d.ts +3 -1
- package/dist/utils/diffCoverage.d.ts +62 -0
- package/dist/utils/lifecycle.d.ts +14 -2
- package/dist/utils/log.d.ts +13 -2
- package/dist/utils/patchWorkflowRunFields.d.ts +27 -4
- package/dist/utils/runContext.d.ts +2 -0
- package/dist/utils/secrets.d.ts +9 -2
- package/dist/utils/setup.d.ts +13 -0
- package/dist/utils/skills.d.ts +10 -0
- package/dist/utils/subprocess.d.ts +7 -0
- package/dist/utils/time.d.ts +1 -0
- package/dist/utils/todoTracking.d.ts +3 -1
- package/package.json +3 -2
package/dist/mcp/checkout.d.ts
CHANGED
|
@@ -5,6 +5,9 @@ export type FormatFilesResult = {
|
|
|
5
5
|
content: string;
|
|
6
6
|
toc: string;
|
|
7
7
|
};
|
|
8
|
+
export type FetchAndFormatPrDiffResult = FormatFilesResult & {
|
|
9
|
+
files: PullFile[];
|
|
10
|
+
};
|
|
8
11
|
/**
|
|
9
12
|
* formats PR files with explicit line numbers for each code line.
|
|
10
13
|
* preserves all original diff info (file headers, hunk headers) and adds:
|
|
@@ -19,6 +22,7 @@ export type CheckoutPrResult = {
|
|
|
19
22
|
success: true;
|
|
20
23
|
number: number;
|
|
21
24
|
title: string;
|
|
25
|
+
body: string | null;
|
|
22
26
|
base: string;
|
|
23
27
|
localBranch: string;
|
|
24
28
|
remoteBranch: string;
|
|
@@ -29,13 +33,21 @@ export type CheckoutPrResult = {
|
|
|
29
33
|
diffPath: string;
|
|
30
34
|
incrementalDiffPath?: string | undefined;
|
|
31
35
|
toc: string;
|
|
36
|
+
commitCount: number;
|
|
37
|
+
commitLog: string;
|
|
38
|
+
/** true when commitLog was capped because the PR has more commits than we render */
|
|
39
|
+
commitLogTruncated: boolean;
|
|
40
|
+
/** true when commit metadata could not be computed (e.g. base ref unreachable after shallow fetch). commitCount/commitLog are zero/empty in that case, not "no commits". */
|
|
41
|
+
commitLogUnavailable: boolean;
|
|
42
|
+
/** non-fatal warning from the post-checkout lifecycle hook, if any */
|
|
43
|
+
hookWarning?: string | undefined;
|
|
32
44
|
instructions: string;
|
|
33
45
|
};
|
|
34
46
|
/**
|
|
35
47
|
* fetches PR files from GitHub and formats them with line numbers and TOC.
|
|
36
48
|
* this is the core diff formatting logic, extracted for testability.
|
|
37
49
|
*/
|
|
38
|
-
export declare function fetchAndFormatPrDiff(ctx: ToolContext, pullNumber: number): Promise<
|
|
50
|
+
export declare function fetchAndFormatPrDiff(ctx: ToolContext, pullNumber: number): Promise<FetchAndFormatPrDiffResult>;
|
|
39
51
|
import type { GitContext } from "../utils/setup.ts";
|
|
40
52
|
export type PrData = {
|
|
41
53
|
number: number;
|
|
@@ -54,7 +66,9 @@ type CheckoutPrBranchParams = GitContext & {
|
|
|
54
66
|
* Assumes origin remote is already configured with authentication.
|
|
55
67
|
* Updates toolState.issueNumber, toolState.checkoutSha, and toolState.pushUrl (for fork PRs).
|
|
56
68
|
*/
|
|
57
|
-
export declare function checkoutPrBranch(pr: PrData, params: CheckoutPrBranchParams): Promise<
|
|
69
|
+
export declare function checkoutPrBranch(pr: PrData, params: CheckoutPrBranchParams): Promise<{
|
|
70
|
+
hookWarning?: string | undefined;
|
|
71
|
+
}>;
|
|
58
72
|
export declare function CheckoutPrTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
|
|
59
73
|
pull_number: number;
|
|
60
74
|
}, {
|
package/dist/mcp/comment.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { ToolContext } from "./server.ts";
|
|
|
5
5
|
* and hasn't been updated with progress or error messages.
|
|
6
6
|
*/
|
|
7
7
|
export declare const LEAPING_INTO_ACTION_PREFIX = "Leaping into action";
|
|
8
|
+
export declare function isLeapingIntoActionCommentBody(body: string): boolean;
|
|
8
9
|
export declare function addFooter(ctx: ToolContext, body: string): string;
|
|
9
10
|
export declare const Comment: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
10
11
|
issueNumber: number;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import type { Tool } from "fastmcp";
|
|
3
|
+
import type { ToolContext } from "./server.ts";
|
|
4
|
+
/**
|
|
5
|
+
* Recursively transform a JSON schema to gemini's stricter subset.
|
|
6
|
+
* See module header for the exact transforms applied.
|
|
7
|
+
*/
|
|
8
|
+
export declare function sanitizeForGemini(schema: unknown): unknown;
|
|
9
|
+
export declare function wrapSchemaForGemini(schema: StandardSchemaV1<any>): StandardSchemaV1<any>;
|
|
10
|
+
export declare function sanitizeToolForGemini<T extends Tool<any, any>>(tool: T): T;
|
|
11
|
+
/**
|
|
12
|
+
* true when the effective upstream model is served by google's generative
|
|
13
|
+
* language API — directly (`google/*`), via opencode (`opencode/gemini-*`),
|
|
14
|
+
* or via openrouter (`openrouter/google/gemini-*`). slug-substring match
|
|
15
|
+
* works because every gemini route's model id contains "gemini".
|
|
16
|
+
*/
|
|
17
|
+
export declare function isGeminiRouted(ctx: ToolContext): boolean;
|
package/dist/mcp/git.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { ToolContext } from "./server.ts";
|
|
2
|
+
export declare function rejectIfLeadingDash(value: string, kind: string): void;
|
|
3
|
+
export declare function rejectSpecialRef(value: string, kind: string): void;
|
|
4
|
+
export declare function validateTagName(tag: string): void;
|
|
2
5
|
export declare const PushBranch: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
3
6
|
force: import("arktype/internal/attributes.ts").Default<boolean, false>;
|
|
4
7
|
branchName?: string;
|
|
@@ -10,11 +13,14 @@ export declare function PushBranchTool(ctx: ToolContext): import("fastmcp").Tool
|
|
|
10
13
|
branchName?: string;
|
|
11
14
|
force?: boolean;
|
|
12
15
|
}>>;
|
|
16
|
+
export declare const AUTH_REQUIRED_REDIRECT: Record<string, string>;
|
|
17
|
+
export declare const NOSHELL_BLOCKED_SUBCOMMANDS: Record<string, string>;
|
|
18
|
+
export declare const NOSHELL_BLOCKED_ARGS: string[];
|
|
13
19
|
export declare function GitTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
|
|
14
|
-
|
|
20
|
+
command: string;
|
|
15
21
|
args?: string[];
|
|
16
22
|
}, {
|
|
17
|
-
|
|
23
|
+
command: string;
|
|
18
24
|
args?: string[];
|
|
19
25
|
}>>;
|
|
20
26
|
export declare function GitFetchTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
|
package/dist/mcp/review.d.ts
CHANGED
|
@@ -1,4 +1,102 @@
|
|
|
1
|
+
import type { RestEndpointMethodTypes } from "@octokit/rest";
|
|
1
2
|
import type { ToolContext } from "./server.ts";
|
|
3
|
+
export type CommentableLines = {
|
|
4
|
+
RIGHT: Set<number>;
|
|
5
|
+
LEFT: Set<number>;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* parse a PR file's patch to determine which line numbers on each side are
|
|
9
|
+
* valid anchors for inline comments. GitHub only accepts comments on lines
|
|
10
|
+
* inside a diff hunk: added/context lines on RIGHT, removed/context lines
|
|
11
|
+
* on LEFT.
|
|
12
|
+
*/
|
|
13
|
+
export declare function commentableLinesForFile(patch: string | undefined): CommentableLines;
|
|
14
|
+
export declare function buildCommentableMap(ctx: ToolContext, pullNumber: number): Promise<Map<string, CommentableLines>>;
|
|
15
|
+
export type ReviewCommentInput = NonNullable<RestEndpointMethodTypes["pulls"]["createReview"]["parameters"]["comments"]>[number];
|
|
16
|
+
export interface DroppedComment {
|
|
17
|
+
path: string;
|
|
18
|
+
line: number;
|
|
19
|
+
startLine?: number | undefined;
|
|
20
|
+
side: "LEFT" | "RIGHT";
|
|
21
|
+
reason: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function validateInlineComments(comments: ReviewCommentInput[], map: Map<string, CommentableLines>): {
|
|
24
|
+
valid: ReviewCommentInput[];
|
|
25
|
+
dropped: DroppedComment[];
|
|
26
|
+
};
|
|
27
|
+
export declare const MAX_DROPPED_COMMENT_LINES = 50;
|
|
28
|
+
/**
|
|
29
|
+
* reason a create_pull_request_review call should be skipped without hitting
|
|
30
|
+
* GitHub. returned by reviewSkipDecision; null means submit normally.
|
|
31
|
+
*/
|
|
32
|
+
export type ReviewSkipDecision = {
|
|
33
|
+
kind: "no-issues";
|
|
34
|
+
reason: string;
|
|
35
|
+
} | {
|
|
36
|
+
kind: "empty-downgraded-approve";
|
|
37
|
+
reason: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* decision returned by duplicateReviewDecision when a session has already
|
|
41
|
+
* submitted a review and the current call would be a duplicate.
|
|
42
|
+
*/
|
|
43
|
+
export type DuplicateReviewDecision = {
|
|
44
|
+
kind: "already-submitted";
|
|
45
|
+
reviewId: number;
|
|
46
|
+
reason: string;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* decide whether a second create_pull_request_review call in the same session
|
|
50
|
+
* is a duplicate of an earlier submission.
|
|
51
|
+
*
|
|
52
|
+
* the agent is instructed to call create_pull_request_review exactly once per
|
|
53
|
+
* Review-mode session (see action/modes.ts), but in practice it sometimes
|
|
54
|
+
* submits twice — once with substantive feedback, then again with the
|
|
55
|
+
* canonical "Reviewed — no issues found." body when the prompt's branch
|
|
56
|
+
* logic re-classifies non-blocking observations. the second submission is
|
|
57
|
+
* always redundant: the first review is the record, and the duplicate just
|
|
58
|
+
* adds noise to the PR.
|
|
59
|
+
*
|
|
60
|
+
* legitimate follow-up reviews after new commits ARE allowed: the
|
|
61
|
+
* new-commits-mid-review path advances toolState.checkoutSha past the
|
|
62
|
+
* previously reviewed sha, and a subsequent checkout_pr advances it again.
|
|
63
|
+
* any call where checkoutSha has moved past the prior reviewedSha is a real
|
|
64
|
+
* follow-up and goes through. anything else — same sha, or no checkoutSha
|
|
65
|
+
* to compare against — is a duplicate.
|
|
66
|
+
*/
|
|
67
|
+
export declare function duplicateReviewDecision(params: {
|
|
68
|
+
existing: {
|
|
69
|
+
id: number;
|
|
70
|
+
reviewedSha: string | undefined;
|
|
71
|
+
} | undefined;
|
|
72
|
+
currentCheckoutSha: string | undefined;
|
|
73
|
+
}): DuplicateReviewDecision | null;
|
|
74
|
+
/**
|
|
75
|
+
* decide whether to skip a review submission before any network call.
|
|
76
|
+
*
|
|
77
|
+
* GitHub rejects `event: "COMMENT"` reviews with no body and no inline comments
|
|
78
|
+
* with HTTP 422 "Unprocessable Entity". two paths produce that shape:
|
|
79
|
+
*
|
|
80
|
+
* 1. `!approved` + empty body/comments: agent's "no issues found" result.
|
|
81
|
+
* skipping preserves the agent's intent (nothing to post is a fine
|
|
82
|
+
* outcome for a review run) without a spurious 422.
|
|
83
|
+
* 2. `approved` + `!prApproveEnabled` + empty body/comments: the runtime
|
|
84
|
+
* downgrades APPROVE to COMMENT when prApproveEnabled is off, and the
|
|
85
|
+
* resulting empty-COMMENT is exactly the shape GitHub 422s. skipping
|
|
86
|
+
* here surfaces the cause (downgrade + nothing to say) instead of an
|
|
87
|
+
* opaque 422 the agent can't recover from.
|
|
88
|
+
*
|
|
89
|
+
* legitimate bare approvals (`approved` + `prApproveEnabled`, no body/comments)
|
|
90
|
+
* are never skipped — GitHub accepts empty APPROVE reviews and the approval
|
|
91
|
+
* stamp itself is the review's content.
|
|
92
|
+
*/
|
|
93
|
+
export declare function reviewSkipDecision(params: {
|
|
94
|
+
approved: boolean;
|
|
95
|
+
body: string | null | undefined;
|
|
96
|
+
hasComments: boolean;
|
|
97
|
+
prApproveEnabled: boolean;
|
|
98
|
+
}): ReviewSkipDecision | null;
|
|
99
|
+
export declare function formatDroppedCommentsNote(dropped: DroppedComment[]): string;
|
|
2
100
|
export declare const CreatePullRequestReview: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
3
101
|
pull_number: number;
|
|
4
102
|
body?: string;
|
|
@@ -40,6 +138,47 @@ export declare function CreatePullRequestReviewTool(ctx: ToolContext): import("f
|
|
|
40
138
|
start_line?: number;
|
|
41
139
|
}[];
|
|
42
140
|
}>>;
|
|
141
|
+
/**
|
|
142
|
+
* clear a pending review draft stranded on the PR by a prior hard-killed run
|
|
143
|
+
* (workflow timeout, OOM) so the next createReview can succeed.
|
|
144
|
+
*
|
|
145
|
+
* GitHub enforces one-pending-review-per-user-per-PR. if the previous process
|
|
146
|
+
* died between createReview(PENDING) and submitReview, the draft remains and
|
|
147
|
+
* the next run's createReview 422s with "already has a pending review".
|
|
148
|
+
* listReviews only exposes PENDING reviews to their author, so filtering on
|
|
149
|
+
* state === "PENDING" is already scoped to the authed token's own draft.
|
|
150
|
+
*
|
|
151
|
+
* if `originalErr` is not a pending-review 422, or no leftover is found, this
|
|
152
|
+
* function rethrows `originalErr` so the caller surfaces the original failure.
|
|
153
|
+
* delete failures with 404 (draft already gone) or 422 (draft submitted by a
|
|
154
|
+
* concurrent caller) are swallowed — the caller's retry will succeed in both
|
|
155
|
+
* cases. any other delete error is rethrown unchanged.
|
|
156
|
+
*
|
|
157
|
+
* known limitation: if two runs on the SAME PR share the authed token and
|
|
158
|
+
* overlap in time, the loser's createReview 422s on the winner's still-active
|
|
159
|
+
* draft. recovery would then delete the winner's active draft and the
|
|
160
|
+
* winner's submitReview would 404. this is not distinguishable from a
|
|
161
|
+
* genuinely-stranded draft via the review object alone (PENDING reviews
|
|
162
|
+
* expose no created_at timestamp, and both reviews are authored by the same
|
|
163
|
+
* bot user). rely on workflow-level concurrency controls (e.g. a concurrency
|
|
164
|
+
* key keyed to the PR number) to prevent overlap.
|
|
165
|
+
*/
|
|
166
|
+
export declare function clearStrandedPendingReview(ctx: ToolContext, params: {
|
|
167
|
+
owner: string;
|
|
168
|
+
repo: string;
|
|
169
|
+
pull_number: number;
|
|
170
|
+
originalErr: unknown;
|
|
171
|
+
}): Promise<void>;
|
|
172
|
+
/**
|
|
173
|
+
* single-step createReview (event != PENDING) with stranded-draft recovery.
|
|
174
|
+
* the body path goes through createAndSubmitWithFooter which already recovers
|
|
175
|
+
* from a stranded PENDING draft at its own createReview call. the no-body path
|
|
176
|
+
* used to call createReview directly with no recovery — so a PR whose previous
|
|
177
|
+
* body-path run crashed between createReview(PENDING) and submitReview would
|
|
178
|
+
* permanently 422 any subsequent no-body review (approve-with-no-feedback or
|
|
179
|
+
* comments-only) until a body-path run happened to clear the draft.
|
|
180
|
+
*/
|
|
181
|
+
export declare function createReviewWithStrandedRecovery(ctx: ToolContext, params: RestEndpointMethodTypes["pulls"]["createReview"]["parameters"]): Promise<Awaited<ReturnType<typeof ctx.octokit.rest.pulls.createReview>>>;
|
|
43
182
|
/**
|
|
44
183
|
* report the review node ID so the WorkflowRun is marked as "review submitted".
|
|
45
184
|
* exported for use in main.ts post-agent cleanup.
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -3,10 +3,12 @@ import type { AgentUsage } from "../agents/index.ts";
|
|
|
3
3
|
import { type AgentId } from "../external.ts";
|
|
4
4
|
import type { Mode } from "../modes.ts";
|
|
5
5
|
import type { PrepResult } from "../prep/index.ts";
|
|
6
|
+
import type { DiffCoverageState } from "../utils/diffCoverage.ts";
|
|
6
7
|
import type { OctokitWithPlugins } from "../utils/github.ts";
|
|
7
8
|
import type { ResolvedPayload } from "../utils/payload.ts";
|
|
8
9
|
import type { RunContextData } from "../utils/runContextData.ts";
|
|
9
10
|
import type { TodoTracker } from "../utils/todoTracking.ts";
|
|
11
|
+
import type { CommentableLines } from "./review.ts";
|
|
10
12
|
export type BackgroundProcess = {
|
|
11
13
|
pid: number;
|
|
12
14
|
outputPath: string;
|
|
@@ -29,6 +31,9 @@ export interface ToolState {
|
|
|
29
31
|
pushDest?: StoredPushDest;
|
|
30
32
|
issueNumber?: number;
|
|
31
33
|
checkoutSha?: string;
|
|
34
|
+
commentableLinesByFile?: Map<string, CommentableLines>;
|
|
35
|
+
commentableLinesPullNumber?: number;
|
|
36
|
+
commentableLinesCheckoutSha?: string | undefined;
|
|
32
37
|
beforeSha?: string;
|
|
33
38
|
selectedMode?: string;
|
|
34
39
|
backgroundProcesses: Map<string, BackgroundProcess>;
|
|
@@ -55,6 +60,7 @@ export interface ToolState {
|
|
|
55
60
|
usageEntries: AgentUsage[];
|
|
56
61
|
model?: string | undefined;
|
|
57
62
|
todoTracker?: TodoTracker | undefined;
|
|
63
|
+
diffCoverage?: DiffCoverageState | undefined;
|
|
58
64
|
}
|
|
59
65
|
interface InitToolStateParams {
|
|
60
66
|
progressCommentId: string | undefined;
|
|
@@ -78,6 +84,7 @@ export interface ToolContext {
|
|
|
78
84
|
jobId: string | undefined;
|
|
79
85
|
mcpServerUrl: string;
|
|
80
86
|
tmpdir: string;
|
|
87
|
+
resolvedModel: string | undefined;
|
|
81
88
|
}
|
|
82
89
|
type JsonSchema = Record<string, unknown>;
|
|
83
90
|
type McpHttpServerOptions = {
|
|
@@ -85,6 +92,11 @@ type McpHttpServerOptions = {
|
|
|
85
92
|
};
|
|
86
93
|
/**
|
|
87
94
|
* Start the MCP HTTP server.
|
|
95
|
+
*
|
|
96
|
+
* The returned disposer is idempotent — safe to call multiple times.
|
|
97
|
+
* Callers (e.g. the inner activity-timeout handler in main.ts) may need to
|
|
98
|
+
* stop the server before the `await using` block exits; a subsequent
|
|
99
|
+
* automatic dispose is then a no-op.
|
|
88
100
|
*/
|
|
89
101
|
export declare function startMcpHttpServer(ctx: ToolContext, options?: McpHttpServerOptions): Promise<{
|
|
90
102
|
url: string;
|
package/dist/mcp/shared.d.ts
CHANGED
|
@@ -18,4 +18,4 @@ export declare const handleToolError: (error: unknown) => ToolResult;
|
|
|
18
18
|
* @param toolName - optional tool name for error logging
|
|
19
19
|
*/
|
|
20
20
|
export declare const execute: <T, R extends Record<string, any> | string>(fn: (params: T) => Promise<R>, toolName?: string) => (params: T) => Promise<ToolResult>;
|
|
21
|
-
export declare const addTools: (
|
|
21
|
+
export declare const addTools: (ctx: ToolContext, server: FastMCP<any>, tools: Tool<any, any>[]) => FastMCP<any>;
|
package/dist/models.d.ts
CHANGED
|
@@ -60,10 +60,27 @@ export declare function getModelEnvVars(slug: string): string[];
|
|
|
60
60
|
export declare const modelAliases: ModelAlias[];
|
|
61
61
|
/** resolve a model slug to its concrete models.dev specifier (e.g. "anthropic/claude-opus-4-6") */
|
|
62
62
|
export declare function resolveModelSlug(slug: string): string | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* walk the fallback chain to the terminal (non-deprecated) alias.
|
|
65
|
+
* returns undefined if the chain is broken, exhausted, or cyclic.
|
|
66
|
+
*
|
|
67
|
+
* use this in UI display sites (dropdown trigger labels, PR-comment footers,
|
|
68
|
+
* etc.) so a deprecated stored slug renders as the model the user actually
|
|
69
|
+
* runs against — not the historical name. selectable lists should still hide
|
|
70
|
+
* deprecated aliases by filtering on `!a.fallback`.
|
|
71
|
+
*/
|
|
72
|
+
export declare function resolveDisplayAlias(slug: string): ModelAlias | undefined;
|
|
63
73
|
/**
|
|
64
74
|
* resolve a model slug to the CLI-ready model string, following the fallback
|
|
65
75
|
* chain when a model is deprecated. returns the first non-deprecated resolve
|
|
66
76
|
* target, or undefined if the chain is exhausted or broken.
|
|
67
77
|
*/
|
|
68
78
|
export declare function resolveCliModel(slug: string): string | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* resolve a model slug to the OpenRouter-ready model string, following the
|
|
81
|
+
* fallback chain when a model is deprecated. returns undefined if the chain
|
|
82
|
+
* is exhausted/broken or the terminal alias has no openrouter equivalent
|
|
83
|
+
* (e.g. free opencode models).
|
|
84
|
+
*/
|
|
85
|
+
export declare function resolveOpenRouterModel(slug: string): string | undefined;
|
|
69
86
|
export {};
|
package/dist/modes.d.ts
CHANGED
|
@@ -4,6 +4,6 @@ export interface Mode {
|
|
|
4
4
|
description: string;
|
|
5
5
|
prompt?: string | undefined;
|
|
6
6
|
}
|
|
7
|
-
export declare const PR_SUMMARY_FORMAT = "### Default format\n\nFollow this structure exactly:\n\n<b>TL;DR</b> \u2014 1-3 sentences on what the PR does and why. Focus on intent, not mechanics.\nNOTE: use HTML bold <b>TL;DR</b>, NOT markdown bold **TL;DR**.\n\n### Key changes\n\n- **Short human-readable title** \u2014 1 sentence per change. Write a short prose phrase (title case or sentence case); when you name a file, type, or function, put that name in backticks (e.g. **Add `TodoTracker` for live checklists**). A reviewer should understand the full PR from this list alone.\n\n<sub><b>Summary</b> \uFF5C {file_count} files \uFF5C {commit_count} commits \uFF5C base: `{base}` \u2190 `{head}`</sub>\nNOTE: the metadata line goes AFTER the bullet list, not before it.\n\nThen for each key change, a ## section with a short descriptive title that reads like a documentation heading (e.g. ## Live todo checklist tracking).\n\n<br/>\n\n## Example readable section title\n\n> **Before:** [old behavior/state]<br/>**After:** [new behavior/state]\nIMPORTANT: Before and After MUST be on a SINGLE blockquote line with an inline <br/> between them. Two separate `>` lines creates a double line break.\n\n1-2 sentences of explanation. Break up text with tables, blockquotes, or lists \u2014 NEVER 3+ plain paragraphs in a row.\n\nIf a change warrants deeper explanation, use a blockquoted details/summary framed as a question:\n> <details><summary>How does X work?</summary>\n> Extended explanation here.\n> </details>\n\nEnd each section with a file links trail (3-4 key files max):\n[`file.ts`](https://github.com/{owner}/{repo}/pull/{number}/files#diff-{sha256hex_of_filepath}) \u00B7 ...\n\nSingle-feature PRs: skip the ## sections. Fold before/after and explanation into the header after key changes.\n\nCRITICAL \u2014 GitHub markdown rendering rule:\nGitHub's markdown parser requires a blank line between ALL block-level elements. This includes transitions between: HTML tags (<br/>, <sub>, <details>, <b>, etc.) and markdown syntax (headings, lists, blockquotes, paragraphs). Without a blank line, GitHub treats the following content as a continuation of the HTML block and renders markdown syntax as literal text. ALWAYS separate block-level elements with a blank line.\n\nRules:\n- `##` titles and key-change bullet lead-ins are plain-language summaries; backtick only actual code tokens (files, types, functions) where they appear in the title\n- ALL variable names, identifiers, and file names in body text must be in backticks\n- ALL file references MUST link to the PR Files Changed view.
|
|
7
|
+
export declare const PR_SUMMARY_FORMAT = "### Default format\n\nFollow this structure exactly:\n\n<b>TL;DR</b> \u2014 1-3 sentences on what the PR does and why. Focus on intent, not mechanics.\nNOTE: use HTML bold <b>TL;DR</b>, NOT markdown bold **TL;DR**.\n\n### Key changes\n\n- **Short human-readable title** \u2014 1 sentence per change. Write a short prose phrase (title case or sentence case); when you name a file, type, or function, put that name in backticks (e.g. **Add `TodoTracker` for live checklists**). A reviewer should understand the full PR from this list alone.\n\n<sub><b>Summary</b> \uFF5C {file_count} files \uFF5C {commit_count} commits \uFF5C base: `{base}` \u2190 `{head}`</sub>\nNOTE: the metadata line goes AFTER the bullet list, not before it.\n\nThen for each key change, a ## section with a short descriptive title that reads like a documentation heading (e.g. ## Live todo checklist tracking).\n\n<br/>\n\n## Example readable section title\n\n> **Before:** [old behavior/state]<br/>**After:** [new behavior/state]\nIMPORTANT: Before and After MUST be on a SINGLE blockquote line with an inline <br/> between them. Two separate `>` lines creates a double line break.\n\n1-2 sentences of explanation. Break up text with tables, blockquotes, or lists \u2014 NEVER 3+ plain paragraphs in a row.\n\nIf a change warrants deeper explanation, use a blockquoted details/summary framed as a question:\n> <details><summary>How does X work?</summary>\n> Extended explanation here.\n> </details>\n\nEnd each section with a file links trail (3-4 key files max):\n[`file.ts`](https://github.com/{owner}/{repo}/pull/{number}/files#diff-{sha256hex_of_filepath}) \u00B7 ...\n\nSingle-feature PRs: skip the ## sections. Fold before/after and explanation into the header after key changes.\n\nCRITICAL \u2014 GitHub markdown rendering rule:\nGitHub's markdown parser requires a blank line between ALL block-level elements. This includes transitions between: HTML tags (<br/>, <sub>, <details>, <b>, etc.) and markdown syntax (headings, lists, blockquotes, paragraphs). Without a blank line, GitHub treats the following content as a continuation of the HTML block and renders markdown syntax as literal text. ALWAYS separate block-level elements with a blank line.\n\nRules:\n- `##` titles and key-change bullet lead-ins are plain-language summaries; backtick only actual code tokens (files, types, functions) where they appear in the title\n- ALL variable names, identifiers, and file names in body text must be in backticks\n- ALL file references MUST link to the PR Files Changed view. Use the `diff-<hex>` anchor precomputed next to each filename in the `checkout_pr` TOC \u2014 do NOT run `sha256sum` or any other shell command to compute anchors. NEVER fabricate hex strings. If a file is not in the TOC, omit the `#diff-` anchor rather than guessing.\n- Add <br/> before each ## heading for visual spacing. Do NOT use horizontal rules (---)\n- Do NOT include raw diff stats like '+123 / -45' or line counts\n- Do NOT include code blocks or repeat diff contents\n- Do NOT include a changelog section \u2014 the key changes list serves this purpose\n- Focus on *intent*, not *what* \u2014 the diff already shows what changed\n- Get the file count and commit count from the checkout_pr metadata, not by counting manually";
|
|
8
8
|
export declare function computeModes(agentId: AgentId): Mode[];
|
|
9
9
|
export declare const modes: Mode[];
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: git-archaeology
|
|
3
|
+
description: Investigate how code reached its current state — when a line, function, import, or whole file was changed or deleted, who removed it, and what it looked like before. Use when `git blame` came up empty, when content has been refactored away, or when you need the full evolution of a function across commits.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Git history archaeology
|
|
7
|
+
|
|
8
|
+
`git blame` only sees what's still in the working tree. For anything that was
|
|
9
|
+
deleted, moved, or refactored away, you need the commands below. Most agents
|
|
10
|
+
under-use them and end up scrolling through `git log -p` instead.
|
|
11
|
+
|
|
12
|
+
## Output discipline (read first)
|
|
13
|
+
|
|
14
|
+
`git log -p` on a long-lived file can dump tens of thousands of lines and blow
|
|
15
|
+
the context window. Always:
|
|
16
|
+
|
|
17
|
+
1. **Start narrow.** Use `--oneline` or `--stat` to get a list of candidate
|
|
18
|
+
commits.
|
|
19
|
+
2. **Drill in.** Use `git show <sha> -- <path>` for the diff of one specific
|
|
20
|
+
commit.
|
|
21
|
+
3. **Scope the search.** Add `--since="3 months ago"`, `-n 20`, or a path
|
|
22
|
+
restriction (`-- <path>`) so output stays manageable.
|
|
23
|
+
4. **Avoid `git log -p` without a path filter** on any non-trivial repo.
|
|
24
|
+
|
|
25
|
+
## Decision tree (by agent intent)
|
|
26
|
+
|
|
27
|
+
### "When did this exact line, string, or import disappear?"
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git log -S'<exact-string>' --oneline -- <file>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The pickaxe. Returns commits that **changed the count** of that string in the
|
|
34
|
+
file. The most recent hit is typically the removal commit. Add `-p` only after
|
|
35
|
+
you've narrowed to a few candidates.
|
|
36
|
+
|
|
37
|
+
Notes:
|
|
38
|
+
- `-S` is exact-string by default. Add `--pickaxe-regex` to make it a regex.
|
|
39
|
+
- The argument is "cuddled" with `-S` (`-S'foo bar'`), no space.
|
|
40
|
+
- `-S` will not detect pure in-file moves (count unchanged). Use `-G` for that.
|
|
41
|
+
- `--pickaxe-all` shows the entire changeset of matching commits, useful when
|
|
42
|
+
a commit changes both a definition and its call sites in other files.
|
|
43
|
+
|
|
44
|
+
### "When did the diff stop matching this regex?"
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git log -G'<regex>' --oneline -- <file>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Like `-S` but matches any added or removed hunk line against the regex. Use
|
|
51
|
+
`-G` when:
|
|
52
|
+
- You don't know the exact string but know a pattern.
|
|
53
|
+
- You want to catch in-file moves (`-S` won't).
|
|
54
|
+
- You want to find any diff that touched a pattern, even if the count was
|
|
55
|
+
preserved (e.g., a refactor that changed call sites without removing the
|
|
56
|
+
function).
|
|
57
|
+
|
|
58
|
+
### "How did this function evolve over time?"
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git log -L :<function-name>:<file>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Every commit that touched the function, with diffs scoped to just the function
|
|
65
|
+
body. Works for languages git understands (most mainstream ones).
|
|
66
|
+
|
|
67
|
+
### "How did lines N–M evolve?"
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
git log -L <N>,<M>:<file>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### "What's the full history of this file, including across renames?"
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git log --follow --oneline -- <file> # overview
|
|
77
|
+
git log --follow -p -- <file> # with diffs (use sparingly)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`--follow` only works for a single file, not directories.
|
|
81
|
+
|
|
82
|
+
### "Where was a now-deleted line last present?"
|
|
83
|
+
|
|
84
|
+
Two-step pattern when you have an exact deleted string:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# 1. find a historical commit that contained the string
|
|
88
|
+
git log -S'<deleted-string>' --oneline --all -- <file>
|
|
89
|
+
|
|
90
|
+
# 2. reverse-blame from that commit to find the last commit it survived in
|
|
91
|
+
git blame --reverse <old-sha>..HEAD -- <file>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The reverse blame tells you, for each line, the last commit it survived in
|
|
95
|
+
before being modified or deleted. Pinpoints the exact deletion commit.
|
|
96
|
+
|
|
97
|
+
### "This file no longer exists — when was it deleted, and what was in it?"
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# find all commits that touched the path, even on other branches
|
|
101
|
+
git log --all --full-history --oneline -- <deleted-path>
|
|
102
|
+
|
|
103
|
+
# the most recent of those is usually the deletion. confirm:
|
|
104
|
+
git show <sha> --stat
|
|
105
|
+
|
|
106
|
+
# view the file's contents at any commit where it existed
|
|
107
|
+
git show <sha>^:<deleted-path>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
If you don't know the path, find it from filename alone:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# list all delete events with paths
|
|
114
|
+
git log --all --diff-filter=D --summary | grep -i '<filename>'
|
|
115
|
+
|
|
116
|
+
# or glob across all branches
|
|
117
|
+
git log --all --oneline -- '**/<filename>.*'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### "Who deleted it, in one shot?"
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
git rev-list -n 1 HEAD -- <deleted-path> # the deletion commit
|
|
124
|
+
git show $(git rev-list -n 1 HEAD -- <deleted-path>) -- <deleted-path>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### "Restore a deleted file (locally, no commit)"
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
git restore --source=<deletion-sha>^ -- <deleted-path>
|
|
131
|
+
# or, on older git:
|
|
132
|
+
git checkout <deletion-sha>^ -- <deleted-path>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The `^` is critical — at the deletion commit the file is already gone, so we
|
|
136
|
+
read from its parent.
|
|
137
|
+
|
|
138
|
+
### "Search commit messages, not content"
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
git log --all --grep='<text>' --oneline
|
|
142
|
+
git log --all --grep='<text>' -i --oneline # case-insensitive
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Orthogonal to `-S`/`-G`, which only see the diff.
|
|
146
|
+
|
|
147
|
+
## Standard workflow for "why does this code look like this"
|
|
148
|
+
|
|
149
|
+
1. `git log --follow --oneline -- <file>` — overview of commits touching it.
|
|
150
|
+
2. If a recent commit looks suspicious: `git show <sha> -- <file>`.
|
|
151
|
+
3. If you expected to find something and it's missing:
|
|
152
|
+
`git log -S'<expected-string>' --oneline -- <file>`.
|
|
153
|
+
4. For a specific function's full lifecycle:
|
|
154
|
+
`git log -L :<fn>:<file>`.
|
|
155
|
+
5. For the deletion point of a known string: pickaxe to find an old commit
|
|
156
|
+
that contained it, then `git blame --reverse <old-sha>..HEAD -- <file>`.
|
|
157
|
+
|
|
158
|
+
## Useful flags reference
|
|
159
|
+
|
|
160
|
+
| Flag | Effect |
|
|
161
|
+
|------|--------|
|
|
162
|
+
| `--all` | Search all refs, not just the current branch. Use when investigating something that may have lived only on a feature branch. |
|
|
163
|
+
| `--full-history` | Keeps commits that history-simplification would otherwise drop. Needed for accurate history across merges. |
|
|
164
|
+
| `--follow` | Track a single file across renames. Single-file only. |
|
|
165
|
+
| `-M` / `-C` | Detect renames (`-M`) and copies (`-C`) when reading diffs. |
|
|
166
|
+
| `--diff-filter=D` | Restrict to commits that **deleted** something. `A`=added, `M`=modified, `R`=renamed. |
|
|
167
|
+
| `--source` | When combined with `--all`, annotate each commit with the ref it was reached from. |
|
|
168
|
+
| `--pickaxe-all` | With `-S`/`-G`, show all files in the matching commit, not just the matching file. |
|
|
169
|
+
| `--pickaxe-regex` | Treat the `-S` argument as a regex. |
|
|
170
|
+
| `--since` / `--until` | Time-bound the search. Cheap perf win on big repos. |
|
|
171
|
+
| `-n <count>` | Cap result count. |
|
|
172
|
+
| `--stat` | Per-commit file stats instead of full patches. Good first pass. |
|
|
173
|
+
|
|
174
|
+
## Notes and pitfalls
|
|
175
|
+
|
|
176
|
+
- Always include `--` before paths to disambiguate from refs (e.g.
|
|
177
|
+
`git log -S'foo' -- src/auth.ts`).
|
|
178
|
+
- `-S` triggers on **count change**. A pure refactor that moves a line within
|
|
179
|
+
the same file will not match. Use `-G` for those.
|
|
180
|
+
- `-G` runs diff twice and greps; it's slower than `-S`. Scope with paths and
|
|
181
|
+
`--since` on big repos.
|
|
182
|
+
- Without `--all`, `git log -- <path>` shows nothing if the path never existed
|
|
183
|
+
on the current branch. When in doubt, add `--all`.
|
|
184
|
+
- `git log --full-history -- <path>` alone has had bugs in some git versions
|
|
185
|
+
for deleted files; pair with `--all` for reliability.
|
|
186
|
+
- For files that were renamed, `git log -- <new-path>` only shows post-rename
|
|
187
|
+
history. Use `--follow` (one file) or `git log --all -- <old-path>` when
|
|
188
|
+
hunting across rename events.
|
package/dist/utils/activity.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare const DEFAULT_ACTIVITY_TIMEOUT_MS = 300000;
|
|
2
2
|
export declare const DEFAULT_ACTIVITY_CHECK_INTERVAL_MS = 5000;
|
|
3
|
+
export declare const ACTIVITY_NOISE_PATTERNS: readonly RegExp[];
|
|
4
|
+
export declare function isActivityNoise(chunk: string | Uint8Array): boolean;
|
|
3
5
|
type ActivityTimeoutContext = {
|
|
4
6
|
timeoutMs: number;
|
|
5
7
|
checkIntervalMs: number;
|
|
@@ -7,6 +9,8 @@ type ActivityTimeoutContext = {
|
|
|
7
9
|
export type ActivityTimeout = {
|
|
8
10
|
promise: Promise<never>;
|
|
9
11
|
stop: () => void;
|
|
12
|
+
/** force the timeout to reject immediately with a custom reason */
|
|
13
|
+
forceReject: (reason: string) => void;
|
|
10
14
|
};
|
|
11
15
|
/**
|
|
12
16
|
* mark activity to reset the no-output timeout.
|
package/dist/utils/agent.d.ts
CHANGED
|
@@ -3,7 +3,9 @@ import type { Agent } from "../agents/index.ts";
|
|
|
3
3
|
* resolve the effective model for this run.
|
|
4
4
|
*
|
|
5
5
|
* priority:
|
|
6
|
-
* 1. PULLFROG_MODEL env var
|
|
6
|
+
* 1. PULLFROG_MODEL env var — resolved through the alias registry first,
|
|
7
|
+
* so values like "anthropic/claude-opus" become "anthropic/claude-opus-4-7".
|
|
8
|
+
* raw specifiers (e.g. "anthropic/claude-opus-4-6") pass through unchanged.
|
|
7
9
|
* 2. slug from repo config / payload → alias registry
|
|
8
10
|
* 3. undefined — agent will auto-select
|
|
9
11
|
*/
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export type DiffLineRange = {
|
|
2
|
+
startLine: number;
|
|
3
|
+
endLine: number;
|
|
4
|
+
};
|
|
5
|
+
export type DiffTocEntry = {
|
|
6
|
+
filename: string;
|
|
7
|
+
startLine: number;
|
|
8
|
+
endLine: number;
|
|
9
|
+
};
|
|
10
|
+
export type DiffCoverageFileBreakdown = {
|
|
11
|
+
filename: string;
|
|
12
|
+
startLine: number;
|
|
13
|
+
endLine: number;
|
|
14
|
+
totalLines: number;
|
|
15
|
+
coveredLines: number;
|
|
16
|
+
coveredRanges: DiffLineRange[];
|
|
17
|
+
unreadRanges: DiffLineRange[];
|
|
18
|
+
};
|
|
19
|
+
export type DiffCoverageBreakdown = {
|
|
20
|
+
totalLines: number;
|
|
21
|
+
coveredLines: number;
|
|
22
|
+
unreadLines: number;
|
|
23
|
+
coveragePercent: number;
|
|
24
|
+
coveredRanges: DiffLineRange[];
|
|
25
|
+
unreadRanges: DiffLineRange[];
|
|
26
|
+
files: DiffCoverageFileBreakdown[];
|
|
27
|
+
};
|
|
28
|
+
export type DiffCoverageState = {
|
|
29
|
+
diffPath: string;
|
|
30
|
+
totalLines: number;
|
|
31
|
+
tocEntries: DiffTocEntry[];
|
|
32
|
+
coveredRanges: DiffLineRange[];
|
|
33
|
+
coveragePreflightRan: boolean;
|
|
34
|
+
lastBreakdown?: string | undefined;
|
|
35
|
+
};
|
|
36
|
+
export declare function countLines(params: {
|
|
37
|
+
content: string;
|
|
38
|
+
}): number;
|
|
39
|
+
export declare function parseDiffTocEntries(params: {
|
|
40
|
+
toc: string;
|
|
41
|
+
}): DiffTocEntry[];
|
|
42
|
+
export declare function createDiffCoverageState(params: {
|
|
43
|
+
diffPath: string;
|
|
44
|
+
totalLines: number;
|
|
45
|
+
toc: string;
|
|
46
|
+
}): DiffCoverageState;
|
|
47
|
+
export declare function recordDiffReadFromToolUse(params: {
|
|
48
|
+
state: DiffCoverageState | undefined;
|
|
49
|
+
toolName: string;
|
|
50
|
+
input: unknown;
|
|
51
|
+
cwd: string;
|
|
52
|
+
}): boolean;
|
|
53
|
+
export declare function getDiffCoverageBreakdown(params: {
|
|
54
|
+
state: DiffCoverageState;
|
|
55
|
+
}): DiffCoverageBreakdown;
|
|
56
|
+
export declare function renderDiffCoverageBreakdown(params: {
|
|
57
|
+
diffPath: string;
|
|
58
|
+
breakdown: DiffCoverageBreakdown;
|
|
59
|
+
}): string;
|
|
60
|
+
export declare function countLinesInRanges(params: {
|
|
61
|
+
ranges: DiffLineRange[];
|
|
62
|
+
}): number;
|
|
@@ -2,8 +2,20 @@ export interface ExecuteLifecycleHookParams {
|
|
|
2
2
|
event: string;
|
|
3
3
|
script: string | null;
|
|
4
4
|
}
|
|
5
|
+
export interface LifecycleHookResult {
|
|
6
|
+
/**
|
|
7
|
+
* human-readable warning when the hook failed. includes retry guidance:
|
|
8
|
+
* transient spawn/exit errors are worth retrying, timeouts and
|
|
9
|
+
* persistent failures are not. absent when the hook succeeded or was
|
|
10
|
+
* skipped.
|
|
11
|
+
*/
|
|
12
|
+
warning?: string;
|
|
13
|
+
}
|
|
5
14
|
/**
|
|
6
15
|
* execute a lifecycle hook script if one is configured.
|
|
7
|
-
*
|
|
16
|
+
*
|
|
17
|
+
* soft-fails: instead of throwing on hook errors, returns a warning string
|
|
18
|
+
* so callers can choose whether to surface it (mcp tools) or upgrade it to
|
|
19
|
+
* a fatal error (setup/prepush). timeouts are flagged as non-retryable.
|
|
8
20
|
*/
|
|
9
|
-
export declare function executeLifecycleHook(params: ExecuteLifecycleHookParams): Promise<
|
|
21
|
+
export declare function executeLifecycleHook(params: ExecuteLifecycleHookParams): Promise<LifecycleHookResult>;
|