@gitkraken/compose-tools 0.2.0 → 0.3.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 +40 -0
- package/README.md +38 -17
- package/dist/git/apply-target.d.ts +5 -9
- package/dist/git/apply-target.js +1 -1
- package/dist/git/commit-chain.d.ts +36 -42
- package/dist/git/commit-chain.js +7 -7
- package/dist/git/diff-collector.d.ts +40 -17
- package/dist/git/diff-collector.js +6 -7
- package/dist/git/index.d.ts +2 -2
- package/dist/git/index.js +1 -1
- package/dist/git/port-dispatch.d.ts +4 -2
- package/dist/git/port-dispatch.js +4 -3
- package/dist/git/reparent.d.ts +39 -0
- package/dist/git/reparent.js +5 -0
- package/dist/git/safety-range.d.ts +33 -0
- package/dist/git/safety-range.js +5 -0
- package/dist/git/types.d.ts +10 -14
- package/dist/git/utils/authorship.d.ts +22 -0
- package/dist/git/utils/authorship.js +1 -0
- package/dist/git/utils/hunk-parser.d.ts +1 -1
- package/dist/git/utils/hunk-parser.js +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/ports/git.d.ts +22 -1
- package/dist/tasks/analyze/analyze.js +1 -1
- package/dist/tasks/analyze/prompt.d.ts +2 -0
- package/dist/tasks/analyze/prompt.js +5 -5
- package/dist/tasks/analyze/types.d.ts +3 -0
- package/dist/tasks/compose-group/compose-group.js +1 -1
- package/dist/tasks/compose-group/prompt.d.ts +2 -0
- package/dist/tasks/compose-group/prompt.js +29 -29
- package/dist/tasks/compose-group/types.d.ts +3 -0
- package/dist/tasks/group/group.js +1 -1
- package/dist/tasks/group/prompt.d.ts +2 -0
- package/dist/tasks/group/prompt.js +22 -22
- package/dist/tasks/group/types.d.ts +3 -0
- package/dist/tasks/quick-compose/prompt.d.ts +2 -0
- package/dist/tasks/quick-compose/prompt.js +11 -11
- package/dist/tasks/quick-compose/quick-compose.js +1 -1
- package/dist/tasks/quick-compose/types.d.ts +3 -0
- package/dist/tasks/types.d.ts +10 -9
- package/dist/tasks/utils/redact.d.ts +14 -0
- package/dist/tasks/utils/redact.js +1 -0
- package/dist/workflows/compose.d.ts +25 -14
- package/dist/workflows/compose.js +3 -2
- package/dist/workflows/split.d.ts +3 -0
- package/dist/workflows/split.js +2 -2
- package/dist/workflows/types.d.ts +41 -18
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{hunkContentForPrompt as e}from"../utils/redact.js";import{groupingResultSchema as t}from"../group/schema.js";import{z as n}from"zod";const r=JSON.stringify(n.toJSONSchema(t),null,2),i=`
|
|
2
2
|
In addition to the \`branches\` field, include an \`analysis\` field at the top level:
|
|
3
3
|
|
|
4
4
|
\`\`\`json
|
|
@@ -10,15 +10,15 @@ In addition to the \`branches\` field, include an \`analysis\` field at the top
|
|
|
10
10
|
"branches": [ ... ]
|
|
11
11
|
}
|
|
12
12
|
\`\`\`
|
|
13
|
-
`,
|
|
13
|
+
`,a=`5. Write a full commit message for each commit containing both a summary and a description:
|
|
14
14
|
- **Summary** (first line): concise, 50 chars max, third-person singular present tense ("Fixes", "Adds", "Improves"). This line stands alone as the commit subject.
|
|
15
15
|
- **Blank line**: mandatory separator between summary and description.
|
|
16
16
|
- **Description** (body): one or more lines explaining *why* this change was made, not *what* it does. Focus on motivation, trade-offs, or context a reviewer needs. For non-trivial changes, always include a description — don't leave it at just the summary.
|
|
17
|
-
- Informal yet professional tone.`;function
|
|
17
|
+
- Informal yet professional tone.`;function o(e,t=`branches`,n,r,i){return i?t===`commits`?d(e,n):f(e,n):t===`commits`?s(e,n,r):c(e,n,r)}function s(e,t,n){let o=e?.promptFragment?`5. Write a commit message for each commit following the repository's conventions (see below).
|
|
18
18
|
6. Write a detailed explanation for each commit as if explaining to a reviewer.
|
|
19
19
|
|
|
20
|
-
${e.promptFragment}`:`${
|
|
21
|
-
6. Write a detailed explanation for each commit as if explaining to a reviewer`,s=
|
|
20
|
+
${e.promptFragment}`:`${a}
|
|
21
|
+
6. Write a detailed explanation for each commit as if explaining to a reviewer`,s=u(t?.commitGranularity);return`You are an advanced AI programming assistant tasked with analyzing and organizing code changes into commits.
|
|
22
22
|
|
|
23
23
|
## Critical constraints (output is rejected if violated)
|
|
24
24
|
|
|
@@ -29,14 +29,14 @@ ${e.promptFragment}`:`${i}
|
|
|
29
29
|
|
|
30
30
|
Before producing the \`<output>\` tag, do a self-check: count the total number of hunk indices across every commit's \`hunks\` array. That count MUST equal the input hunk count. If it does not match, you have either duplicated or skipped a hunk — find and fix it before outputting.
|
|
31
31
|
|
|
32
|
-
${
|
|
32
|
+
${n?`## Step 1: Analysis`:`## Step 1: Internal Analysis`}
|
|
33
33
|
|
|
34
34
|
Before grouping, analyze the changes to understand:
|
|
35
35
|
- **Concerns** — what distinct themes, features, or purposes exist across the hunks
|
|
36
36
|
- **Dependencies** — which hunks provide things (define functions, types, files) that other hunks consume (import, call, reference)
|
|
37
37
|
- **Ordering constraints** — if hunk A defines something hunk B uses, they should be in the same commit or A's commit should come before B's
|
|
38
38
|
|
|
39
|
-
${
|
|
39
|
+
${n?"Output this analysis as a JSON field named `analysis` alongside your `branches` field. The `analysis` field should contain:\n- `concerns`: array of strings, each describing a distinct theme or feature in the changes\n- `key_dependencies`: array of strings, each describing an ordering dependency between concerns":`Do NOT output this analysis. Use it internally to inform your grouping decisions.`}
|
|
40
40
|
|
|
41
41
|
## Step 2: Group hunks into commits
|
|
42
42
|
|
|
@@ -50,16 +50,16 @@ ${s}
|
|
|
50
50
|
Output your organization as JSON inside a <output> tag matching this schema:
|
|
51
51
|
|
|
52
52
|
\`\`\`json
|
|
53
|
-
${
|
|
53
|
+
${r}
|
|
54
54
|
\`\`\`
|
|
55
|
-
${
|
|
55
|
+
${n?i:``}
|
|
56
56
|
Wrap all commits in a single branch object with id "default", name "", title "", and description "".
|
|
57
57
|
|
|
58
|
-
Return only the <output> tag and no other text.`}function
|
|
58
|
+
Return only the <output> tag and no other text.`}function c(e,t,n){let o=e?.promptFragment?`5. Write a commit message for each commit following the repository's conventions (see below).
|
|
59
59
|
6. Write a detailed explanation for each commit as if explaining to a reviewer.
|
|
60
60
|
|
|
61
|
-
${e.promptFragment}`:`${
|
|
62
|
-
6. Write a detailed explanation for each commit as if explaining to a reviewer`,s=
|
|
61
|
+
${e.promptFragment}`:`${a}
|
|
62
|
+
6. Write a detailed explanation for each commit as if explaining to a reviewer`,s=l(t?.branchSplitting,e),c=u(t?.commitGranularity);return`You are an advanced AI programming assistant tasked with analyzing and organizing code changes into commits and branches.
|
|
63
63
|
|
|
64
64
|
## Critical constraints (output is rejected if violated)
|
|
65
65
|
|
|
@@ -70,7 +70,7 @@ ${e.promptFragment}`:`${i}
|
|
|
70
70
|
|
|
71
71
|
Before producing the \`<output>\` tag, do a self-check: count the total number of hunk indices across every commit's \`hunks\` array (across every branch). That count MUST equal the input hunk count. If it does not match, you have either duplicated or skipped a hunk — find and fix it before outputting.
|
|
72
72
|
|
|
73
|
-
${
|
|
73
|
+
${n?`## Step 1: Analysis`:`## Step 1: Internal Analysis`}
|
|
74
74
|
|
|
75
75
|
Before grouping, analyze the changes to understand:
|
|
76
76
|
- **Concerns** — what distinct themes, features, or purposes exist across the hunks
|
|
@@ -78,7 +78,7 @@ Before grouping, analyze the changes to understand:
|
|
|
78
78
|
- **Ordering constraints** — if hunk A defines something hunk B uses, they should be in the same commit or A's commit should come before B's
|
|
79
79
|
- **Merge-order preference** — even if two concerns compile independently, would merging one without the other be confusing or incomplete?
|
|
80
80
|
|
|
81
|
-
${
|
|
81
|
+
${n?"Output this analysis as a JSON field named `analysis` alongside your `branches` field. The `analysis` field should contain:\n- `concerns`: array of strings, each describing a distinct theme or feature in the changes\n- `key_dependencies`: array of strings, each describing an ordering dependency between concerns":`Do NOT output this analysis. Use it internally to inform your grouping decisions.`}
|
|
82
82
|
|
|
83
83
|
## Step 2: Organize hunks into commits and branches
|
|
84
84
|
|
|
@@ -92,7 +92,7 @@ Your job has two levels:
|
|
|
92
92
|
1. Group hunks into logical units that make sense together and can be applied atomically
|
|
93
93
|
2. Ensure each commit is self-contained and atomic
|
|
94
94
|
${o}
|
|
95
|
-
${
|
|
95
|
+
${c}
|
|
96
96
|
|
|
97
97
|
### Branch guidelines
|
|
98
98
|
|
|
@@ -103,14 +103,14 @@ ${s}
|
|
|
103
103
|
Output your organization as JSON inside a <output> tag matching this schema:
|
|
104
104
|
|
|
105
105
|
\`\`\`json
|
|
106
|
-
${
|
|
106
|
+
${r}
|
|
107
107
|
\`\`\`
|
|
108
|
-
${
|
|
109
|
-
Return only the <output> tag and no other text.`}function
|
|
108
|
+
${n?i:``}
|
|
109
|
+
Return only the <output> tag and no other text.`}function l(e,t){let n=`Each branch must have a descriptive name, a short PR-style title (under 72 characters, imperative mood), and at least one commit.`,r=`- Each commit belongs to exactly one branch.
|
|
110
110
|
${t?.promptFragment?`- ${n} Follow the repository's branch naming conventions (see above).`:`- ${n}`}
|
|
111
|
-
- Changes addressing the same pattern across different files belong in ONE branch.`,i=`- Consider merge-order preference, not just code dependencies. Even if two concerns compile independently, they may need to be in the same branch if merging one without the other would be confusing or incomplete.`;return e===`conservative`?`${r}\n- Default to a single branch. Only create multiple branches when concerns are completely unrelated.\n${i}\n- When in doubt, use fewer branches.`:e===`aggressive`?`${r}\n- Create separate branches for each distinct concern.\n${i}\n- Prefer more branches over fewer.`:`${r}\n- Create separate branches when concerns are genuinely independent — meaning a reviewer would gain nothing from seeing them together.\n- Keep changes in the same branch when they form a coherent story.\n${i}\n- When in doubt: "Would a reviewer want to see these together?"`}function
|
|
111
|
+
- Changes addressing the same pattern across different files belong in ONE branch.`,i=`- Consider merge-order preference, not just code dependencies. Even if two concerns compile independently, they may need to be in the same branch if merging one without the other would be confusing or incomplete.`;return e===`conservative`?`${r}\n- Default to a single branch. Only create multiple branches when concerns are completely unrelated.\n${i}\n- When in doubt, use fewer branches.`:e===`aggressive`?`${r}\n- Create separate branches for each distinct concern.\n${i}\n- Prefer more branches over fewer.`:`${r}\n- Create separate branches when concerns are genuinely independent — meaning a reviewer would gain nothing from seeing them together.\n- Keep changes in the same branch when they form a coherent story.\n${i}\n- When in doubt: "Would a reviewer want to see these together?"`}function u(e){return e===`consolidated`?`
|
|
112
112
|
Prefer fewer, larger commits. Combine related changes into a single commit when they serve the same purpose.`:e===`granular`?`
|
|
113
|
-
Prefer more, smaller commits. Each distinct change should be its own commit.`:``}function
|
|
113
|
+
Prefer more, smaller commits. Each distinct change should be its own commit.`:``}function d(e,t){return`Organize code hunks into clean, atomic commits.
|
|
114
114
|
|
|
115
115
|
## Rules
|
|
116
116
|
|
|
@@ -121,7 +121,7 @@ Prefer more, smaller commits. Each distinct change should be its own commit.`:``
|
|
|
121
121
|
5. ${e?.promptFragment?`Write commit messages following the repository's conventions (see below). Write a one-sentence explanation per commit.
|
|
122
122
|
|
|
123
123
|
${e.promptFragment}`:`Write commit messages: first line under 50 chars, imperative mood ("Add", "Fix", "Update"). Write a one-sentence explanation per commit.`}
|
|
124
|
-
${
|
|
124
|
+
${u(t?.commitGranularity)}
|
|
125
125
|
|
|
126
126
|
## Output
|
|
127
127
|
|
|
@@ -131,9 +131,9 @@ JSON inside <output> tags. Use one branch with id "default", name "", title "",
|
|
|
131
131
|
{"branches":[{"id":"default","name":"","title":"","description":"","commits":[{"id":"commit-1","message":"Add feature X","explanation":"Brief reason.","hunks":[0,1,2]}]}]}
|
|
132
132
|
\`\`\`
|
|
133
133
|
|
|
134
|
-
CRITICAL: Every hunk index must appear exactly once across all commits. Every commit must contain at least one hunk. Missing hunks, duplicate hunks, or empty commits will fail validation. Return only the <output> tag.`}function
|
|
134
|
+
CRITICAL: Every hunk index must appear exactly once across all commits. Every commit must contain at least one hunk. Missing hunks, duplicate hunks, or empty commits will fail validation. Return only the <output> tag.`}function f(e,t){let n=e?.promptFragment?`Write commit messages following the repository's conventions (see below). Write a one-sentence explanation per commit.
|
|
135
135
|
|
|
136
|
-
${e.promptFragment}`:`Write commit messages: first line under 50 chars, third-person singular present tense ("Fixes", "Adds", "Improves"). Write a one-sentence explanation per commit.`,r=
|
|
136
|
+
${e.promptFragment}`:`Write commit messages: first line under 50 chars, third-person singular present tense ("Fixes", "Adds", "Improves"). Write a one-sentence explanation per commit.`,r=l(t?.branchSplitting,e);return`Organize code hunks into clean, atomic commits and branches.
|
|
137
137
|
|
|
138
138
|
## Commit rules
|
|
139
139
|
|
|
@@ -142,7 +142,7 @@ ${e.promptFragment}`:`Write commit messages: first line under 50 chars, third-pe
|
|
|
142
142
|
3. Each commit needs a unique ID (e.g., "commit-1", "commit-2").
|
|
143
143
|
4. Every commit must have at least one hunk. No empty commits.
|
|
144
144
|
5. ${n}
|
|
145
|
-
${
|
|
145
|
+
${u(t?.commitGranularity)}
|
|
146
146
|
|
|
147
147
|
## Branch guidelines
|
|
148
148
|
|
|
@@ -158,11 +158,11 @@ JSON inside <output> tags. Each branch needs a unique ID and non-empty name.
|
|
|
158
158
|
|
|
159
159
|
CRITICAL: Every hunk index must appear exactly once across all commits in all branches. Every commit must contain at least one hunk. Missing hunks, duplicate hunks, or empty commits will fail validation. Each branch must have a unique ID and non-empty name.
|
|
160
160
|
|
|
161
|
-
Return only the <output> tag and no other text.`}function
|
|
161
|
+
Return only the <output> tag and no other text.`}function p(t){let{hunks:n,commitMessages:r,commitOrder:i,branchName:a,baseBranch:o,issueUrl:s,pullRequestUrl:c,instructions:l,condensed:u,redactHunkContent:d}=t,f=JSON.stringify(u?n.map(e=>({index:e.index,fileName:e.fileName,hunkHeader:e.hunkHeader,additions:e.additions,deletions:e.deletions,...e.isBinary?{isBinary:!0}:{},...e.isRename?{isRename:!0}:{},...e.originalFileName?{originalFileName:e.originalFileName}:{}})):n.map(t=>({index:t.index,fileName:t.fileName,hunkHeader:t.hunkHeader,content:e(t,d),...t.isBinary?{isBinary:!0}:{},...t.isRename?{isRename:!0}:{},...t.originalFileName?{originalFileName:t.originalFileName}:{}}))),p=`Organize these ${n.length} code hunks (indices 0 to ${n.length-1}) into commits. Every index must appear exactly once.
|
|
162
162
|
|
|
163
163
|
<hunks>
|
|
164
|
-
${
|
|
165
|
-
</hunks>`,
|
|
166
|
-
`)}`),
|
|
164
|
+
${f}
|
|
165
|
+
</hunks>`,m=[];return a&&m.push(`Branch: ${a}`),o&&m.push(`Base: ${o}`),s&&m.push(`Issue: ${s}`),c&&m.push(`PR: ${c}`),m.length>0&&(p+=`\n\nContext:\n${m.join(`
|
|
166
|
+
`)}`),r?.length&&(p+=`\n\nOriginal commit messages (match their style):\n<commit_messages>\n${JSON.stringify(r)}\n</commit_messages>`),i?.length&&(p+=`\n\nOriginal commit order:\n${JSON.stringify(i)}`),l&&(p+=`\n\nAdditional instructions:\n${l}`),p+=`
|
|
167
167
|
|
|
168
|
-
Return only the <output> tag and no other text.`,
|
|
168
|
+
Return only the <output> tag and no other text.`,p}export{o as buildComposeGroupSystemPrompt,p as buildComposeGroupUserPrompt};
|
|
@@ -2,6 +2,7 @@ import { ProgressEvent, TokenUsage } from "../../ai/types.js";
|
|
|
2
2
|
import { ComposeHunk, PromptOverride } from "../types.js";
|
|
3
3
|
import { ConventionDescriptor } from "../../git/utils/conventions.js";
|
|
4
4
|
import { OnAfterModelCall, OnBeforeModelCall, OnBeforePrompt } from "../ai-loop.js";
|
|
5
|
+
import { RedactHunkContent } from "../utils/redact.js";
|
|
5
6
|
import { GroupingResult, GroupingStrategy } from "../group/types.js";
|
|
6
7
|
import { ComposeSession } from "../session.js";
|
|
7
8
|
import { AiModelPort } from "@gitkraken/shared-tools";
|
|
@@ -37,6 +38,8 @@ interface ComposeGroupInput {
|
|
|
37
38
|
lightweight?: boolean;
|
|
38
39
|
/** When true, sends only hunk metadata (fileName, hunkHeader, counts) without diff content. */
|
|
39
40
|
condensed?: boolean;
|
|
41
|
+
/** No-op when `condensed: true`. See {@link RedactHunkContent}. */
|
|
42
|
+
redactHunkContent?: RedactHunkContent;
|
|
40
43
|
model: AiModelPort;
|
|
41
44
|
/** Prior session to resume from. When `completedSteps.composeGroup` is present, skip the AI call. */
|
|
42
45
|
session?: ComposeSession;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{TaskInputError as e}from"../../errors/task.js";import{runAiLoop as t}from"../ai-loop.js";import{applyUserPromptOverride as n}from"../types.js";import{validateGroupResponse as r}from"./validator.js";import{buildGroupContinuationPrompt as i,buildGroupSystemPrompt as a,buildGroupUserPrompt as o}from"./prompt.js";async function s(s){let c=s.hunks,l=s.mode??`branches`;if((!c||c.length===0)&&!s.session)throw new e(`No hunks provided: supply 'hunks' or run analyze() first with a session`);if(s.session?.completedSteps.group)return{result:s.session.completedSteps.group,session:s.session,attempts:0,durationMs:0,callIds:[]};let u=n(s.session?i(s,l):o({hunks:c,commitMessages:s.commitMessages,instructions:s.instructions,mode:l}),s.promptOverride),d=c?.length??s.session?.completedSteps.analyze?.hunkAnnotations.length??0;if(d===0)throw new e(`Cannot validate grouping: no hunk count available from input or session`);let f=await t({model:s.model,systemPrompt:s.session?void 0:s.promptOverride?.system??a(s.conventions,l,s.strategy),userPrompt:u,session:s.session,maxRetries:s.maxRetries,maxTokens:s.maxTokens,onProgress:s.onProgress,cancellation:s.cancellation,onBeforeModelCall:s.onBeforeModelCall,onAfterModelCall:s.onAfterModelCall,onBeforePrompt:s.onBeforePrompt,validate:e=>r(e,d,l,s.existingBranchNames),taskName:`group`}),p=s.session?.completedSteps??{},m={...f.session,completedSteps:{...p,group:f.result}};return{result:f.result,session:m,usage:f.usage,attempts:f.attempts,durationMs:f.durationMs,callIds:f.callIds}}export{s as group};
|
|
1
|
+
import{TaskInputError as e}from"../../errors/task.js";import{runAiLoop as t}from"../ai-loop.js";import{applyUserPromptOverride as n}from"../types.js";import{validateGroupResponse as r}from"./validator.js";import{buildGroupContinuationPrompt as i,buildGroupSystemPrompt as a,buildGroupUserPrompt as o}from"./prompt.js";async function s(s){let c=s.hunks,l=s.mode??`branches`;if((!c||c.length===0)&&!s.session)throw new e(`No hunks provided: supply 'hunks' or run analyze() first with a session`);if(s.session?.completedSteps.group)return{result:s.session.completedSteps.group,session:s.session,attempts:0,durationMs:0,callIds:[]};let u=n(s.session?i(s,l):o({hunks:c,commitMessages:s.commitMessages,instructions:s.instructions,mode:l,redactHunkContent:s.redactHunkContent}),s.promptOverride),d=c?.length??s.session?.completedSteps.analyze?.hunkAnnotations.length??0;if(d===0)throw new e(`Cannot validate grouping: no hunk count available from input or session`);let f=await t({model:s.model,systemPrompt:s.session?void 0:s.promptOverride?.system??a(s.conventions,l,s.strategy),userPrompt:u,session:s.session,maxRetries:s.maxRetries,maxTokens:s.maxTokens,onProgress:s.onProgress,cancellation:s.cancellation,onBeforeModelCall:s.onBeforeModelCall,onAfterModelCall:s.onAfterModelCall,onBeforePrompt:s.onBeforePrompt,validate:e=>r(e,d,l,s.existingBranchNames),taskName:`group`}),p=s.session?.completedSteps??{},m={...f.session,completedSteps:{...p,group:f.result}};return{result:f.result,session:m,usage:f.usage,attempts:f.attempts,durationMs:f.durationMs,callIds:f.callIds}}export{s as group};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ComposeHunk } from "../types.js";
|
|
2
2
|
import { ConventionDescriptor } from "../../git/utils/conventions.js";
|
|
3
|
+
import { RedactHunkContent } from "../utils/redact.js";
|
|
3
4
|
import { GroupInput, GroupingStrategy } from "./types.js";
|
|
4
5
|
|
|
5
6
|
//#region src/tasks/group/prompt.d.ts
|
|
@@ -9,6 +10,7 @@ declare function buildGroupUserPrompt(params: {
|
|
|
9
10
|
commitMessages?: string[];
|
|
10
11
|
instructions?: string;
|
|
11
12
|
mode?: 'commits' | 'branches';
|
|
13
|
+
redactHunkContent?: RedactHunkContent;
|
|
12
14
|
}): string;
|
|
13
15
|
declare function buildGroupContinuationPrompt(input: GroupInput, mode: 'commits' | 'branches'): string;
|
|
14
16
|
//#endregion
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{hunkContentForPrompt as e}from"../utils/redact.js";import{groupingResultSchema as t}from"./schema.js";import{z as n}from"zod";const r=JSON.stringify(n.toJSONSchema(t),null,2),i=`5. Write a full commit message for each commit containing both a summary and a description:
|
|
2
2
|
- **Summary** (first line): concise, 50 chars max, third-person singular present tense ("Fixes", "Adds", "Improves"). This line stands alone as the commit subject.
|
|
3
3
|
- **Blank line**: mandatory separator between summary and description.
|
|
4
4
|
- **Description** (body): one or more lines explaining *why* this change was made, not *what* it does. Focus on motivation, trade-offs, or context a reviewer needs. For non-trivial changes, always include a description — don't leave it at just the summary.
|
|
5
|
-
- Informal yet professional tone.`,
|
|
5
|
+
- Informal yet professional tone.`,a=`Each branch must have a descriptive name, a short PR-style title (under 72 characters, imperative mood), and at least one commit.`;function o(e,t=`branches`,n){return t===`commits`?s(e,n):c(e,n)}function s(e,t){return`You are an advanced AI programming assistant tasked with organizing code changes into commits. You will be working with individual code hunks.
|
|
6
6
|
|
|
7
7
|
## Critical constraints (output is rejected if violated)
|
|
8
8
|
|
|
@@ -20,25 +20,25 @@ Before producing the \`<output>\` tag, do a self-check: count the total number o
|
|
|
20
20
|
${e?.promptFragment?`5. Write a commit message for each commit following the repository's conventions (see below).
|
|
21
21
|
6. Write a detailed explanation for each commit as if explaining to a reviewer.
|
|
22
22
|
|
|
23
|
-
${e.promptFragment}`:`${
|
|
23
|
+
${e.promptFragment}`:`${i}
|
|
24
24
|
6. Write a detailed explanation for each commit as if explaining to a reviewer`}
|
|
25
|
-
${
|
|
25
|
+
${u(t?.commitGranularity)}
|
|
26
26
|
|
|
27
27
|
## Required Output Format
|
|
28
28
|
|
|
29
29
|
Output your organization as JSON inside a <output> tag matching this schema:
|
|
30
30
|
|
|
31
31
|
\`\`\`json
|
|
32
|
-
${
|
|
32
|
+
${r}
|
|
33
33
|
\`\`\`
|
|
34
34
|
|
|
35
35
|
Wrap all commits in a single branch object with id "default", name "", title "", and description "".
|
|
36
36
|
|
|
37
|
-
Return only the <output> tag and no other text.`}function
|
|
37
|
+
Return only the <output> tag and no other text.`}function c(e,t){let n=e?.promptFragment?`5. Write a commit message for each commit following the repository's conventions (see below).
|
|
38
38
|
6. Write a detailed explanation for each commit as if explaining to a reviewer.
|
|
39
39
|
|
|
40
|
-
${e.promptFragment}`:`${
|
|
41
|
-
6. Write a detailed explanation for each commit as if explaining to a reviewer`,a=
|
|
40
|
+
${e.promptFragment}`:`${i}
|
|
41
|
+
6. Write a detailed explanation for each commit as if explaining to a reviewer`,a=l(t?.branchSplitting,e);return`You are an advanced AI programming assistant tasked with organizing code changes into commits and branches. You will be working with individual code hunks.
|
|
42
42
|
|
|
43
43
|
Your job has two levels:
|
|
44
44
|
|
|
@@ -58,8 +58,8 @@ Before producing the \`<output>\` tag, do a self-check: count the total number o
|
|
|
58
58
|
|
|
59
59
|
1. Group hunks into logical units that make sense together and can be applied atomically
|
|
60
60
|
2. Ensure each commit is self-contained and atomic
|
|
61
|
-
${
|
|
62
|
-
${
|
|
61
|
+
${n}
|
|
62
|
+
${u(t?.commitGranularity)}
|
|
63
63
|
|
|
64
64
|
## Branch guidelines
|
|
65
65
|
|
|
@@ -70,11 +70,11 @@ ${a}
|
|
|
70
70
|
Output your organization as JSON inside a <output> tag matching this schema:
|
|
71
71
|
|
|
72
72
|
\`\`\`json
|
|
73
|
-
${
|
|
73
|
+
${r}
|
|
74
74
|
\`\`\`
|
|
75
75
|
|
|
76
|
-
Return only the <output> tag and no other text.`}function
|
|
77
|
-
${t?.promptFragment?`- ${
|
|
76
|
+
Return only the <output> tag and no other text.`}function l(e,t){let n=`- Each commit belongs to exactly one branch.
|
|
77
|
+
${t?.promptFragment?`- ${a} Follow the repository's branch naming conventions (see above).`:`- ${a}`}
|
|
78
78
|
- Changes addressing the same pattern across different files belong in ONE branch. For example, applying the same bug fix to multiple functions, or updating similar config across modules — these are the same concern even if the files are different.`,r=`- Consider merge-order preference, not just code dependencies. Even if two concerns compile independently, they may need to be in the same branch or stacked branches if merging one without the other would be confusing or incomplete (e.g., documentation that describes utilities introduced in other commits should be in the same branch or stacked after those utilities).`;return e===`conservative`?`${n}
|
|
79
79
|
- Default to a single branch. Only create multiple branches when concerns are completely unrelated — different domains, different purposes, and no merge-order preference between them.
|
|
80
80
|
${r}
|
|
@@ -85,22 +85,22 @@ ${r}
|
|
|
85
85
|
- Create separate branches when concerns are genuinely independent — meaning they address different problems or features, and a reviewer would gain nothing from seeing them together in one PR. Two commits that each add a separate utility function unrelated to each other are good candidates for independent branches.
|
|
86
86
|
- Keep changes in the same branch when they form a coherent story that benefits from being reviewed together, even if they could technically stand alone.
|
|
87
87
|
${r}
|
|
88
|
-
- When in doubt, consider: "Would a reviewer want to see these changes together, or would separating them make review easier?" If separating helps, split.`}function
|
|
88
|
+
- When in doubt, consider: "Would a reviewer want to see these changes together, or would separating them make review easier?" If separating helps, split.`}function u(e){return e===`consolidated`?`
|
|
89
89
|
Prefer fewer, larger commits. Combine related changes into a single commit when they serve the same purpose. A commit can touch many files if they are all part of the same logical change.`:e===`granular`?`
|
|
90
|
-
Prefer more, smaller commits. Each distinct change should be its own commit, even if multiple changes serve a similar purpose. Aim for commits that touch as few files as possible.`:``}function
|
|
90
|
+
Prefer more, smaller commits. Each distinct change should be its own commit, even if multiple changes serve a similar purpose. Aim for commits that touch as few files as possible.`:``}function d(t){let{hunks:n,commitMessages:r,instructions:i,mode:a=`branches`,redactHunkContent:o}=t,s=JSON.stringify(n.map(t=>({index:t.index,fileName:t.fileName,hunkHeader:t.hunkHeader,content:e(t,o),...t.isBinary?{isBinary:!0}:{},...t.isRename?{isRename:!0}:{},...t.originalFileName?{originalFileName:t.originalFileName}:{}}))),c=`${a===`commits`?`Examine the following ${n.length} code hunks (indices 0 to ${n.length-1}) and organize them into commits. Every index must appear exactly once.`:`Examine the following ${n.length} code hunks (indices 0 to ${n.length-1}) and organize them into commits and branches. Every index must appear exactly once.`}
|
|
91
91
|
|
|
92
92
|
<hunks>
|
|
93
|
-
${
|
|
94
|
-
</hunks>`;return
|
|
93
|
+
${s}
|
|
94
|
+
</hunks>`;return r?.length&&(c+=`
|
|
95
95
|
|
|
96
96
|
The following commit messages are from the original commits. Use them to understand intent and match their style:
|
|
97
97
|
|
|
98
98
|
<commit_messages>
|
|
99
|
-
${JSON.stringify(
|
|
100
|
-
</commit_messages>`),
|
|
99
|
+
${JSON.stringify(r)}
|
|
100
|
+
</commit_messages>`),i&&(c+=`
|
|
101
101
|
|
|
102
102
|
Carefully follow these additional instructions, but do not deviate from the output structure:
|
|
103
|
-
${
|
|
103
|
+
${i}`),c+=`\n\n${a===`commits`?`Now organize the hunks into commits.`:`Now organize the hunks into commits and branches.`} Return only the <output> tag and no other text.`,c}function f(e,t){let n;return n=t===`commits`?`Based on your analysis of the changes, now organize the hunks into commits. Each hunk must be assigned to exactly one commit.`:p(e.strategy),e.conventions?.promptFragment&&(n+=`\n\n${e.conventions.promptFragment}`),e.commitMessages?.length&&(n+=`\n\nMatch the style of these original commit messages:\n${JSON.stringify(e.commitMessages)}`),e.instructions&&(n+=`\n\nAdditional instructions:\n${e.instructions}`),t===`commits`?n+=`
|
|
104
104
|
|
|
105
105
|
Return your grouping as JSON inside <output> tags. Wrap all commits in a single branch with id "default", name "", title "", description "":
|
|
106
106
|
<output>
|
|
@@ -148,7 +148,7 @@ Return your grouping as JSON inside <output> tags:
|
|
|
148
148
|
}
|
|
149
149
|
</output>
|
|
150
150
|
|
|
151
|
-
Return only the <output> tag and no other text.`,n}function
|
|
151
|
+
Return only the <output> tag and no other text.`,n}function p(e){let t=e?.branchSplitting??`balanced`,n=`Based on your analysis of the changes, now organize the hunks into commits and branches. Each hunk must be assigned to exactly one commit, and each commit belongs to exactly one branch.`,r=`Consider merge-order preference: even if two concerns compile independently, they should be in the same branch or stacked if merging one without the other would be incomplete or confusing (e.g., documentation for utilities should be with or after those utilities).`;return t===`conservative`?`${n}
|
|
152
152
|
|
|
153
153
|
Default to a single branch. Only create multiple branches when concerns are completely unrelated — different domains, different purposes, no merge-order preference.
|
|
154
154
|
${r}
|
|
@@ -161,4 +161,4 @@ Create separate branches when concerns are genuinely independent — different p
|
|
|
161
161
|
|
|
162
162
|
Keep changes in the same branch when they form a coherent story that benefits from joint review, or when there is a merge-order preference between them.
|
|
163
163
|
${r}
|
|
164
|
-
When in doubt, consider: "Would a reviewer want to see these together, or would separating them make review easier?"`}export{
|
|
164
|
+
When in doubt, consider: "Would a reviewer want to see these together, or would separating them make review easier?"`}export{f as buildGroupContinuationPrompt,o as buildGroupSystemPrompt,d as buildGroupUserPrompt};
|
|
@@ -2,6 +2,7 @@ import { ProgressEvent, TokenUsage } from "../../ai/types.js";
|
|
|
2
2
|
import { BranchGroup, ComposeHunk, PromptOverride } from "../types.js";
|
|
3
3
|
import { ConventionDescriptor } from "../../git/utils/conventions.js";
|
|
4
4
|
import { OnAfterModelCall, OnBeforeModelCall, OnBeforePrompt } from "../ai-loop.js";
|
|
5
|
+
import { RedactHunkContent } from "../utils/redact.js";
|
|
5
6
|
import { AnalysisResult } from "../analyze/types.js";
|
|
6
7
|
import { ComposeSession } from "../session.js";
|
|
7
8
|
import { AiModelPort } from "@gitkraken/shared-tools";
|
|
@@ -35,6 +36,8 @@ interface GroupInput {
|
|
|
35
36
|
strategy?: GroupingStrategy;
|
|
36
37
|
/** Branch names that already exist in the repo — used to detect collisions during validation. */
|
|
37
38
|
existingBranchNames?: Set<string>;
|
|
39
|
+
/** See {@link RedactHunkContent}. */
|
|
40
|
+
redactHunkContent?: RedactHunkContent;
|
|
38
41
|
model: AiModelPort;
|
|
39
42
|
session?: ComposeSession;
|
|
40
43
|
maxRetries?: number;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ComposeHunk } from "../types.js";
|
|
2
2
|
import { ConventionDescriptor } from "../../git/utils/conventions.js";
|
|
3
|
+
import { RedactHunkContent } from "../utils/redact.js";
|
|
3
4
|
import { GroupingStrategy } from "../group/types.js";
|
|
4
5
|
|
|
5
6
|
//#region src/tasks/quick-compose/prompt.d.ts
|
|
@@ -13,6 +14,7 @@ declare function buildQuickComposeUserPrompt(params: {
|
|
|
13
14
|
issueUrl?: string;
|
|
14
15
|
pullRequestUrl?: string;
|
|
15
16
|
instructions?: string;
|
|
17
|
+
redactHunkContent?: RedactHunkContent;
|
|
16
18
|
}): string;
|
|
17
19
|
//#endregion
|
|
18
20
|
export { buildQuickComposeSystemPrompt, buildQuickComposeUserPrompt };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{hunkContentForPrompt as e}from"../utils/redact.js";import{quickComposeResultSchema as t}from"./schema.js";import{z as n}from"zod";const r=JSON.stringify(n.toJSONSchema(t),null,2);function i(e,t=`branches`,n,i=!1){let a=e?.promptFragment?`Write commit messages following the repository's conventions:\n\n${e.promptFragment}`:`Write commit messages:
|
|
2
2
|
Each commit message must contain both a summary and a description, separated by a blank line:
|
|
3
3
|
- **Summary** (first line): concise, 50 chars max, third-person singular present tense ("Fixes", "Adds", "Improves"). Stands alone as the commit subject.
|
|
4
4
|
- **Blank line**: mandatory separator.
|
|
5
|
-
- **Description** (body): one or more lines explaining *why* the change was made, not *what* it does. Focus on motivation, trade-offs, or context a reviewer needs. For non-trivial changes, always include a description.`,l=
|
|
5
|
+
- **Description** (body): one or more lines explaining *why* the change was made, not *what* it does. Focus on motivation, trade-offs, or context a reviewer needs. For non-trivial changes, always include a description.`,l=o(n?.commitGranularity),u=t===`branches`?s(n?.branchSplitting,e):``,d=i?c():``;return`You are a fast AI code organizer. Given a set of code diff hunks, organize them into well-structured commits${t===`branches`?` and branches`:``} in a single pass.
|
|
6
6
|
|
|
7
7
|
## Your task
|
|
8
8
|
|
|
@@ -13,7 +13,7 @@ ${t===`branches`?`4. Assign commits to branches — each branch is a cohesive un
|
|
|
13
13
|
${i?`5. If a branch should be split into stacked sub-branches for review, provide partitions.`:``}
|
|
14
14
|
|
|
15
15
|
## Commit style
|
|
16
|
-
${
|
|
16
|
+
${a}
|
|
17
17
|
${l}
|
|
18
18
|
${u}
|
|
19
19
|
${d}
|
|
@@ -36,21 +36,21 @@ Before producing the \`<output>\` tag, do a self-check: count the total number o
|
|
|
36
36
|
Output as JSON inside a <output> tag matching this schema:
|
|
37
37
|
|
|
38
38
|
\`\`\`json
|
|
39
|
-
${
|
|
39
|
+
${r}
|
|
40
40
|
\`\`\`
|
|
41
41
|
|
|
42
|
-
Return only the <output> tag and no other text.`}function
|
|
42
|
+
Return only the <output> tag and no other text.`}function a(t){let{hunks:n,commitMessages:r,commitOrder:i,branchName:a,baseBranch:o,issueUrl:s,pullRequestUrl:c,instructions:l,redactHunkContent:u}=t,d=`Organize these code hunks into commits:
|
|
43
43
|
|
|
44
44
|
<hunks>
|
|
45
|
-
${JSON.stringify(
|
|
46
|
-
</hunks>`,
|
|
47
|
-
`)}`),
|
|
45
|
+
${JSON.stringify(n.map(t=>({index:t.index,fileName:t.fileName,hunkHeader:t.hunkHeader,content:e(t,u),...t.isBinary?{isBinary:!0}:{},...t.isRename?{isRename:!0}:{},...t.originalFileName?{originalFileName:t.originalFileName}:{}})))}
|
|
46
|
+
</hunks>`,f=[];return a&&f.push(`Branch: ${a}`),o&&f.push(`Base: ${o}`),s&&f.push(`Issue: ${s}`),c&&f.push(`PR: ${c}`),f.length>0&&(d+=`\n\nContext:\n${f.join(`
|
|
47
|
+
`)}`),r?.length&&(d+=`\n\nOriginal commit messages (match their style):\n<commit_messages>\n${JSON.stringify(r)}\n</commit_messages>`),i?.length&&(d+=`\n\nOriginal commit order:\n${JSON.stringify(i)}`),l&&(d+=`\n\nAdditional instructions:\n${l}`),d+=`
|
|
48
48
|
|
|
49
|
-
Return only the <output> tag and no other text.`,
|
|
49
|
+
Return only the <output> tag and no other text.`,d}function o(e){return e===`consolidated`?`
|
|
50
50
|
Prefer fewer, larger commits. Combine related changes into a single commit.`:e===`granular`?`
|
|
51
|
-
Prefer more, smaller commits. Each distinct change gets its own commit.`:``}function
|
|
51
|
+
Prefer more, smaller commits. Each distinct change gets its own commit.`:``}function s(e,t){let n=`\n## Branch guidelines\n- Each branch has a descriptive name, a short PR title (under 72 chars), and at least one commit.\n- ${t?.promptFragment?`Follow the repository's branch naming conventions.`:`Give each branch a descriptive name.`}`;return e===`conservative`?`${n}\n- Default to a single branch. Only split when concerns are completely unrelated.`:e===`aggressive`?`${n}\n- Create separate branches for each distinct concern. Maximize focus per PR.`:`${n}\n- Split when concerns are genuinely independent. Keep together when they form a coherent story.`}function c(){return`
|
|
52
52
|
## Partition guidelines (stacked sub-branches)
|
|
53
53
|
- If a branch has multiple distinct concerns, split it into partitions for stacked review.
|
|
54
54
|
- Each partition is a contiguous range of commits from orderedCommitIds.
|
|
55
55
|
- Each partition gets a unique, valid git branch name.
|
|
56
|
-
- Single partition (no split) is a valid outcome.`}export{
|
|
56
|
+
- Single partition (no split) is a valid outcome.`}export{i as buildQuickComposeSystemPrompt,a as buildQuickComposeUserPrompt};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{TaskInputError as e}from"../../errors/task.js";import{runAiLoop as t}from"../ai-loop.js";import{applyUserPromptOverride as n}from"../types.js";import{buildQuickComposeSystemPrompt as r,buildQuickComposeUserPrompt as i}from"./prompt.js";import{validateQuickComposeResponse as a}from"./validator.js";async function o(o){let s=o.hunks,c=o.mode??`branches`,l=o.includePartitions??!1;if(!s||s.length===0)throw new e(`No hunks provided for quick compose`);if(o.session?.completedSteps.quickCompose)return{result:o.session.completedSteps.quickCompose,session:o.session,attempts:0,durationMs:0,callIds:[]};let u=o.promptOverride?.system??r(o.conventions,c,o.strategy,l),d=n(i({hunks:s,commitMessages:o.commitMessages,commitOrder:o.commitOrder,branchName:o.branchName,baseBranch:o.baseBranch,issueUrl:o.issueUrl,pullRequestUrl:o.pullRequestUrl,instructions:o.instructions}),o.promptOverride),f=await t({model:o.model,systemPrompt:u,userPrompt:d,maxRetries:o.maxRetries,maxTokens:o.maxTokens,onProgress:o.onProgress,cancellation:o.cancellation,onBeforeModelCall:o.onBeforeModelCall,onAfterModelCall:o.onAfterModelCall,onBeforePrompt:o.onBeforePrompt,validate:e=>a(e,s.length,c,l,o.existingBranchNames),taskName:`quick-compose`}),p=o.session?.completedSteps??{},m={...f.session,completedSteps:{...p,quickCompose:f.result}};return{result:f.result,session:m,usage:f.usage,attempts:f.attempts,durationMs:f.durationMs,callIds:f.callIds}}export{o as quickCompose};
|
|
1
|
+
import{TaskInputError as e}from"../../errors/task.js";import{runAiLoop as t}from"../ai-loop.js";import{applyUserPromptOverride as n}from"../types.js";import{buildQuickComposeSystemPrompt as r,buildQuickComposeUserPrompt as i}from"./prompt.js";import{validateQuickComposeResponse as a}from"./validator.js";async function o(o){let s=o.hunks,c=o.mode??`branches`,l=o.includePartitions??!1;if(!s||s.length===0)throw new e(`No hunks provided for quick compose`);if(o.session?.completedSteps.quickCompose)return{result:o.session.completedSteps.quickCompose,session:o.session,attempts:0,durationMs:0,callIds:[]};let u=o.promptOverride?.system??r(o.conventions,c,o.strategy,l),d=n(i({hunks:s,commitMessages:o.commitMessages,commitOrder:o.commitOrder,branchName:o.branchName,baseBranch:o.baseBranch,issueUrl:o.issueUrl,pullRequestUrl:o.pullRequestUrl,instructions:o.instructions,redactHunkContent:o.redactHunkContent}),o.promptOverride),f=await t({model:o.model,systemPrompt:u,userPrompt:d,maxRetries:o.maxRetries,maxTokens:o.maxTokens,onProgress:o.onProgress,cancellation:o.cancellation,onBeforeModelCall:o.onBeforeModelCall,onAfterModelCall:o.onAfterModelCall,onBeforePrompt:o.onBeforePrompt,validate:e=>a(e,s.length,c,l,o.existingBranchNames),taskName:`quick-compose`}),p=o.session?.completedSteps??{},m={...f.session,completedSteps:{...p,quickCompose:f.result}};return{result:f.result,session:m,usage:f.usage,attempts:f.attempts,durationMs:f.durationMs,callIds:f.callIds}}export{o as quickCompose};
|
|
@@ -2,6 +2,7 @@ import { ProgressEvent, TokenUsage } from "../../ai/types.js";
|
|
|
2
2
|
import { BranchPartition, CommitSuggestion, ComposeHunk, PromptOverride } from "../types.js";
|
|
3
3
|
import { ConventionDescriptor } from "../../git/utils/conventions.js";
|
|
4
4
|
import { OnAfterModelCall, OnBeforeModelCall, OnBeforePrompt } from "../ai-loop.js";
|
|
5
|
+
import { RedactHunkContent } from "../utils/redact.js";
|
|
5
6
|
import { GroupingStrategy } from "../group/types.js";
|
|
6
7
|
import { ComposeSession } from "../session.js";
|
|
7
8
|
import { AiModelPort } from "@gitkraken/shared-tools";
|
|
@@ -25,6 +26,8 @@ interface QuickComposeInput {
|
|
|
25
26
|
strategy?: GroupingStrategy;
|
|
26
27
|
/** Branch names that already exist — used to detect collisions during validation. */
|
|
27
28
|
existingBranchNames?: Set<string>;
|
|
29
|
+
/** See {@link RedactHunkContent}. */
|
|
30
|
+
redactHunkContent?: RedactHunkContent;
|
|
28
31
|
model: AiModelPort;
|
|
29
32
|
/** Prior session to resume from. When `completedSteps.quickCompose` is present, skip the AI call. */
|
|
30
33
|
session?: ComposeSession;
|
package/dist/tasks/types.d.ts
CHANGED
|
@@ -7,18 +7,18 @@ interface ComposeHunk {
|
|
|
7
7
|
content: string;
|
|
8
8
|
additions: number;
|
|
9
9
|
deletions: number;
|
|
10
|
-
source: 'staged' | 'unstaged' | 'untracked' | 'branch';
|
|
11
|
-
sourceCommitSha?: string;
|
|
12
10
|
isRename?: boolean;
|
|
13
11
|
originalFileName?: string;
|
|
14
12
|
isBinary?: boolean;
|
|
15
13
|
/** Full binary patch block (base85-encoded). Excluded from AI context, used only by patch builder. */
|
|
16
14
|
binaryPatchData?: string;
|
|
17
15
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
16
|
+
* Likely author of this hunk. For `commit-range` and `branch` sources the
|
|
17
|
+
* library populates this via a line-range-overlap heuristic against the
|
|
18
|
+
* per-commit hunks of the source range; the author with the highest
|
|
19
|
+
* cumulative overlap wins, and other authors land in `coAuthors`. The apply
|
|
20
|
+
* phase aggregates `author` across the hunks of each new commit to pick the
|
|
21
|
+
* commit's primary author and `Co-authored-by` trailers.
|
|
22
22
|
*
|
|
23
23
|
* `date` is the commit's author date when known — used by consumers for UI
|
|
24
24
|
* display and, for the commit created by the apply path, as `GIT_AUTHOR_DATE`.
|
|
@@ -29,9 +29,10 @@ interface ComposeHunk {
|
|
|
29
29
|
date?: Date;
|
|
30
30
|
};
|
|
31
31
|
/**
|
|
32
|
-
* Additional authors
|
|
33
|
-
*
|
|
34
|
-
* produce the final Co-authored-by
|
|
32
|
+
* Additional likely authors of this hunk (those whose per-commit overlap
|
|
33
|
+
* was non-zero but didn't win the primary slot). Combined with `author`
|
|
34
|
+
* from all hunks in a new commit to produce the final `Co-authored-by`
|
|
35
|
+
* trailer list.
|
|
35
36
|
*/
|
|
36
37
|
coAuthors?: {
|
|
37
38
|
name: string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ComposeHunk } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/tasks/utils/redact.d.ts
|
|
4
|
+
/** Placeholder substituted for hunk content when {@link RedactHunkContent} returns true. */
|
|
5
|
+
declare const REDACTED_HUNK_CONTENT = "[content redacted because this file is excluded from AI processing \u2014 still assign this hunk to a commit based on its filename]";
|
|
6
|
+
/**
|
|
7
|
+
* Returns `true` for hunks whose content should be redacted from AI prompts. The library
|
|
8
|
+
* keeps the hunk's `fileName`, `hunkHeader`, `index`, line counts, and rename metadata in
|
|
9
|
+
* the prompt; only the patch body is replaced.
|
|
10
|
+
*/
|
|
11
|
+
type RedactHunkContent = (hunk: ComposeHunk) => boolean;
|
|
12
|
+
declare function hunkContentForPrompt(hunk: ComposeHunk, redact: RedactHunkContent | undefined): string;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { REDACTED_HUNK_CONTENT, RedactHunkContent, hunkContentForPrompt };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=`[content redacted because this file is excluded from AI processing — still assign this hunk to a commit based on its filename]`;function t(t,n){return n?.(t)?e:t.content}export{e as REDACTED_HUNK_CONTENT,t as hunkContentForPrompt};
|
|
@@ -3,6 +3,7 @@ import { ComposeGitPort, SigningConfig } from "../ports/git.js";
|
|
|
3
3
|
import { BranchPartition, CommitSuggestion, ComposeHunk, ComposeTaskName, PromptOverride } from "../tasks/types.js";
|
|
4
4
|
import { ConventionDescriptor } from "../git/utils/conventions.js";
|
|
5
5
|
import { OnAfterModelCall, OnBeforeModelCall, OnBeforePrompt } from "../tasks/ai-loop.js";
|
|
6
|
+
import { RedactHunkContent } from "../tasks/utils/redact.js";
|
|
6
7
|
import { AnalysisResult } from "../tasks/analyze/types.js";
|
|
7
8
|
import { GroupingResult, GroupingStrategy } from "../tasks/group/types.js";
|
|
8
9
|
import { OrderingResult } from "../tasks/order/types.js";
|
|
@@ -40,6 +41,8 @@ interface ComposePlanInput {
|
|
|
40
41
|
onBeforePrompt?: OnBeforePrompt;
|
|
41
42
|
/** See {@link ComposeInput.hunkFilter}. */
|
|
42
43
|
hunkFilter?: ComposeInput['hunkFilter'];
|
|
44
|
+
/** See {@link ComposeInput.redactHunkContent}. */
|
|
45
|
+
redactHunkContent?: ComposeInput['redactHunkContent'];
|
|
43
46
|
/** See {@link ComposeInput.session}. */
|
|
44
47
|
session?: ComposeSession;
|
|
45
48
|
/** See {@link ComposeInput.promptOverrides}. */
|
|
@@ -73,8 +76,8 @@ interface ApplyComposePlanInput {
|
|
|
73
76
|
signing?: SigningConfig;
|
|
74
77
|
/** How to pick the primary author for each commit. See {@link ComposeInput.authorAttribution}. */
|
|
75
78
|
authorAttribution?: ComposeInput['authorAttribution'];
|
|
76
|
-
/** See {@link ComposeInput.
|
|
77
|
-
|
|
79
|
+
/** See {@link ComposeInput.applyCommitIds}. */
|
|
80
|
+
applyCommitIds?: string[];
|
|
78
81
|
/** See {@link ComposeInput.stashLabel}. */
|
|
79
82
|
stashLabel?: string;
|
|
80
83
|
/**
|
|
@@ -189,6 +192,16 @@ interface ComposeInput {
|
|
|
189
192
|
* values so the plan still references the authoritative collection.
|
|
190
193
|
*/
|
|
191
194
|
hunkFilter?: (hunks: ComposeHunk[]) => Promise<ComposeHunk[]> | ComposeHunk[];
|
|
195
|
+
/**
|
|
196
|
+
* Predicate that returns true for hunks whose patch body should be replaced with a
|
|
197
|
+
* placeholder in AI prompts. Matching hunks keep their filename, hunk header, index,
|
|
198
|
+
* line counts, and rename metadata; only the body is masked. The apply path still uses
|
|
199
|
+
* the real content.
|
|
200
|
+
*
|
|
201
|
+
* Intended for `.aiexclude`/`.aiignore`/`.cursorignore`-style features: the AI assigns
|
|
202
|
+
* matching hunks to commits by filename alone, never seeing the diff body.
|
|
203
|
+
*/
|
|
204
|
+
redactHunkContent?: RedactHunkContent;
|
|
192
205
|
/**
|
|
193
206
|
* Per-task prompt overrides, keyed by {@link ComposeTaskName}. Consumers with
|
|
194
207
|
* existing prompt template services (server-driven templates, provider-specific
|
|
@@ -242,20 +255,18 @@ interface ComposeInput {
|
|
|
242
255
|
name: string;
|
|
243
256
|
email: string;
|
|
244
257
|
} | undefined);
|
|
245
|
-
/**
|
|
246
|
-
|
|
258
|
+
/**
|
|
259
|
+
* Apply only the listed commit ids from `plan.allOrderedCommits`. Hunks belonging to omitted
|
|
260
|
+
* commits (plus unassigned hunks, e.g. those dropped by `hunkFilter`) become unstaged WIP on
|
|
261
|
+
* top of the rewritten branch. Undefined applies every commit.
|
|
262
|
+
*
|
|
263
|
+
* On destructive targets (`rewrite-range`, or `branch`/`auto` with `preserveOriginal: false`)
|
|
264
|
+
* with `branch`/`commit-range` sources, HEAD must be non-detached and at the source's tip.
|
|
265
|
+
*/
|
|
266
|
+
applyCommitIds?: string[];
|
|
247
267
|
/** Stash message used by the apply path when stashing pending workdir state. Defaults to `'compose backup'`. */
|
|
248
268
|
stashLabel?: string;
|
|
249
269
|
}
|
|
250
|
-
type ApplyUpTo = {
|
|
251
|
-
kind: 'all';
|
|
252
|
-
} | {
|
|
253
|
-
kind: 'commitId';
|
|
254
|
-
id: string;
|
|
255
|
-
} | {
|
|
256
|
-
kind: 'count';
|
|
257
|
-
count: number;
|
|
258
|
-
};
|
|
259
270
|
interface ComposePlan {
|
|
260
271
|
analysis?: AnalysisResult;
|
|
261
272
|
grouping: GroupingResult;
|
|
@@ -436,4 +447,4 @@ declare function composePlan(input: ComposePlanInput): Promise<ComposePlanResult
|
|
|
436
447
|
*/
|
|
437
448
|
declare function applyComposePlan(input: ApplyComposePlanInput): Promise<ApplyComposePlanResult>;
|
|
438
449
|
//#endregion
|
|
439
|
-
export { ApplyComposePlanInput, ApplyComposePlanResult,
|
|
450
|
+
export { ApplyComposePlanInput, ApplyComposePlanResult, ComposeApplyPlan, type ComposeBranchPlan, type ComposeContext, ComposeDepth, ComposeInput, ComposePlan, ComposePlanInput, ComposePlanResult, ComposeProgressEvent, ComposeResult, type ComposeSource, ComposeStrategy, type ComposeTarget, type StashConflict, applyComposePlan, compose, composePlan, defaultPhaseMessages };
|