@gitkraken/compose-tools 0.8.1 → 0.9.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +2 -0
  3. package/dist/context.d.ts +66 -0
  4. package/dist/context.js +1 -0
  5. package/dist/git/apply-target.js +1 -1
  6. package/dist/git/index.js +1 -1
  7. package/dist/index.d.ts +3 -1
  8. package/dist/index.js +1 -1
  9. package/dist/tasks/analyze/analyze.js +1 -1
  10. package/dist/tasks/analyze/prompt.d.ts +6 -2
  11. package/dist/tasks/analyze/prompt.js +5 -5
  12. package/dist/tasks/analyze/types.d.ts +11 -0
  13. package/dist/tasks/compose-group/compose-group.js +1 -1
  14. package/dist/tasks/compose-group/prompt.d.ts +6 -1
  15. package/dist/tasks/compose-group/prompt.js +31 -31
  16. package/dist/tasks/compose-group/types.d.ts +16 -0
  17. package/dist/tasks/group/group.js +1 -1
  18. package/dist/tasks/group/prompt.d.ts +7 -1
  19. package/dist/tasks/group/prompt.js +21 -21
  20. package/dist/tasks/group/types.d.ts +16 -0
  21. package/dist/tasks/quick-compose/prompt.d.ts +3 -1
  22. package/dist/tasks/quick-compose/prompt.js +11 -11
  23. package/dist/tasks/quick-compose/quick-compose.js +1 -1
  24. package/dist/tasks/quick-compose/types.d.ts +7 -0
  25. package/dist/tasks/utils/analysis-prompt.d.ts +14 -0
  26. package/dist/tasks/utils/analysis-prompt.js +5 -0
  27. package/dist/tasks/utils/authoring-context.d.ts +47 -0
  28. package/dist/tasks/utils/authoring-context.js +9 -0
  29. package/dist/tasks/utils/hunk-summaries.d.ts +19 -0
  30. package/dist/tasks/utils/hunk-summaries.js +5 -0
  31. package/dist/workflows/author.d.ts +101 -0
  32. package/dist/workflows/author.js +16 -0
  33. package/dist/workflows/compose.d.ts +89 -1
  34. package/dist/workflows/compose.js +3 -3
  35. package/dist/workflows/split.js +2 -2
  36. package/dist/workflows/types.d.ts +16 -2
  37. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/) and this
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.0] - 2026-10-05
10
+
11
+ ### Added
12
+
13
+ - Adds `AuthoringContext` — a structured, optional description of what a change's author knows that the diff cannot show: intent, issue (url/key/title/body), pull request, spec, notes, per-file edit rationales, ordering dependencies, and conventions. `ComposeContext` now extends it, so every compose entry point accepts the full shape; the flat `issueUrl`/`pullRequestUrl` fields are deprecated in favor of `issue.url`/`pullRequest.url` and remain honored (the structured field wins when both are set). Everything beyond the URLs is rendered into prompts as escaped, per-field-capped, tagged data behind an explicit data-not-instructions notice, so tracker text cannot act as a prompt injection and one verbose issue body cannot crowd the hunks out of the token budget; the URL fields themselves are emitted as plain context lines, forced to a single line and length-capped. Supplying nothing leaves every prompt byte-identical to before. Type exports: `AuthoringContext`, `AuthoringDependency`, `AuthoringEdit`, `AuthoringIssue`, `AuthoringNote`, `AuthoringPullRequest`.
14
+ - Adds `collectComposeHunks` and `buildAuthoredComposePlan`, the two halves of an authored-plan flow in which the caller writes the commit plan and no model is called at any point. `collectComposeHunks` runs the collection phase alone and returns the hunk index map, the safety snapshot, and the resolved source/target — the parts a caller cannot derive itself. `buildAuthoredComposePlan` validates a caller-written commit list against the collected indices (each index claimed exactly once; unknown, duplicate, and unassigned indices are rejected with the offending files named in the error) and shapes it into a `ComposePlan` for `applyComposePlan`. Built for consumers whose planner is the coding agent that made the changes: the agent already holds the full context of its own edits, so the grouping decision can come from it directly instead of from a model prompt carrying the diff. The produced plan is a single branch group — multi-branch splitting stays with the model-driven flow. Type exports: `CollectComposeHunksInput`, `CollectComposeHunksResult`, `AuthoredCommit`, `BuildAuthoredComposePlanInput`.
15
+ - Adds `analysis` and `condenseHunks` to `compose` and `composePlan` — inputs that let a caller who already knows the change inform the composing model instead of paying to have it inferred. `analysis` supplies the breakdown the `analyze` phase exists to derive from the diff; at `depth: 'deep'` that phase is skipped entirely (still reported, with zero attempts) and the grouping model is no longer asked to produce its own. `condenseHunks` mode sends hunk *metadata only* — index, file name, hunk header, line counts — omitting every patch body from the prompts, for the situation where the answering model already has the change in front of it (an agent composing edits it made itself). Supplying neither leaves every prompt byte-identical to before. `analysis` cannot be combined with a resumed `session` — continuation prompts never carry it — and the combination is rejected with `ComposeWorkflowInputError`.
16
+ - Adds `hunkSummaries` — one-line, caller-supplied summaries of what individual hunks do, keyed by hunk index — rendered into the analyze and grouping prompts as an escaped `<hunk-summaries>` block (at `depth: 'deep'` they enter at the analyze phase and the conversation carries them forward). Built for `condenseHunks` mode: with patch bodies absent, headers say where a change is but not what it does, and grouping suffers most where related hunks span files (an export in one file, its import in another). Summaries restore that signal at a fraction of the diff's size, carry no concern structure (so, unlike a supplied `analysis`, they cannot pre-bias the grouping), are capped at 200 characters each, and drop entries for indices that were not collected. Sparse maps are expected — summarize the hunks whose headers don't speak for themselves.
17
+ - Adds `condensed` to `collectComposeHunks` — the returned hunks come back with empty patch bodies (binary payloads dropped), index map only, while collection and the safety snapshot still compute from the real content. For consumers relaying the result to the agent that authored the changes: masking becomes the library's guarantee instead of something every consumer must remember, so the author never pays to read its own diff.
18
+ - Adds `buildAuthoredPlanInstructions` — the agent-facing rules for authoring a plan (the decision JSON shape, the every-index-exactly-once rules, ordering, the hunk-boundary caveat), shipped from the library so consumers embed a function call instead of maintaining prose that can drift from the validator.
19
+ - Adds `SESSION-FLOWS.md` — consumer documentation of the four session interaction flows (detached model informed/uninformed, calling agent as composer for its own or unfamiliar changes), which functions each flow uses, and what the consumer owns (wiring, routing, the collect-to-apply binding).
20
+
9
21
  ## [0.8.1] - 2026-08-05
10
22
 
11
23
  ### Changed
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  AI-powered git change organization. Takes a messy working directory or a branch full of interleaved commits and restructures them into clean, atomic commits on well-named branches — ready for code review.
4
4
 
5
+ Using compose from an MCP server or CLI that a coding agent drives? [SESSION-FLOWS.md](./SESSION-FLOWS.md) documents the four supported interaction flows and how to wire each one.
6
+
5
7
  ## What it does
6
8
 
7
9
  `compose()` collects your changes, sends the diffs through an AI pipeline that understands the logical structure of code changes, and produces clean commits grouped by concern. Depending on configuration, it can:
@@ -0,0 +1,66 @@
1
+ //#region src/context.d.ts
2
+ /**
3
+ * What the author of a change knows that the diff cannot show: the goal, the ticket, the reasoning
4
+ * behind individual edits, and the order things had to happen in.
5
+ *
6
+ * Every field is optional and every field is a hint. A consumer must still behave correctly when it
7
+ * is absent or empty, and must still validate whatever a model produces from it.
8
+ *
9
+ * The producer is deliberately unspecified. A coding agent writes it from its own session, a host
10
+ * application generates it from a task record, or a person writes it by hand — the consuming
11
+ * library cannot tell the difference and does not need to.
12
+ *
13
+ * **Treat every string here as untrusted.** Issue and pull-request bodies are third-party text on
14
+ * its way into a prompt, so a consumer rendering them must escape them and label them as data.
15
+ */
16
+ interface AuthoringContext {
17
+ /** Why this work exists, in the author's own words. */
18
+ intent?: string;
19
+ issue?: AuthoringIssue;
20
+ pullRequest?: AuthoringPullRequest;
21
+ /** The design or plan the author worked from, when there was a written one. */
22
+ spec?: string;
23
+ /** Decisions and findings recorded during the work. */
24
+ notes?: AuthoringNote[];
25
+ /** Per-file rationale: why this path was touched at all. */
26
+ edits?: AuthoringEdit[];
27
+ /** Ordering constraints the author already knows. */
28
+ dependencies?: AuthoringDependency[];
29
+ /** Repository rules the author was already following. */
30
+ conventions?: string;
31
+ }
32
+ interface AuthoringIssue {
33
+ url?: string;
34
+ /** Tracker-native identifier, e.g. `AGP-430` or `#1234`. */
35
+ key?: string;
36
+ title?: string;
37
+ body?: string;
38
+ }
39
+ interface AuthoringPullRequest {
40
+ url?: string;
41
+ title?: string;
42
+ body?: string;
43
+ }
44
+ interface AuthoringNote {
45
+ title?: string;
46
+ content: string;
47
+ }
48
+ interface AuthoringEdit {
49
+ path: string;
50
+ rationale: string;
51
+ }
52
+ /**
53
+ * `before` must land ahead of `after`. Each names either a repository path or a free-form concern
54
+ * label; a consumer resolves them against whatever it is grouping and ignores what it cannot match.
55
+ *
56
+ * Deliberately looser than the analyzer's `ConcernDependency`, which relates two concern ids inside
57
+ * one `AnalysisResult`. This shape has no analysis to reference — the author states orderings in
58
+ * its own words before any concern structure exists, and the ends here are prose, not identifiers.
59
+ */
60
+ interface AuthoringDependency {
61
+ before: string;
62
+ after: string;
63
+ why?: string;
64
+ }
65
+ //#endregion
66
+ export { AuthoringContext, AuthoringDependency, AuthoringEdit, AuthoringIssue, AuthoringNote, AuthoringPullRequest };
@@ -0,0 +1 @@
1
+ export{};
@@ -1 +1 @@
1
- import{GitError as e}from"../errors/git.js";import{applyPatchToWorkdir as t,checkout as n,cleanWorkdir as r,currentBranch as i,deleteBranch as a,discardWorkdirChanges as o,reset as s,revParse as c,revParseVerify as l,updateRef as u}from"./port-dispatch.js";import{applyBranchStructure as d}from"./apply-branches.js";import{resolveWorkdirLayers as f}from"./diff-collector.js";import{reparentDescendants as p}from"./reparent.js";import{applyCommitChain as m,applyRangeRewrite as h,buildCommitChain as g,moveRefsAtSha as _}from"./commit-chain.js";import{verifyMultiBranchDiff as v,verifyResultingDiff as y}from"./safety.js";async function b(t){let{git:n,source:r,target:i,branchPlans:a,allOrderedCommits:o,hunks:s,snapshot:l,collection:u,signing:p,authorAttribution:_,stashLabel:v,leftoverPatch:b}=t,C=r.type===`commit-range`&&r.includeWorkdir!=null?f(r.includeWorkdir):void 0;if(i.type===`rewrite-range`){if(!u.branchName||!u.branchSha)throw new e(`OPERATION_FAILED`,`rewrite-range target requires a source that records branchName and branchSha (branch or commit-range).`);let t;try{t=await c(n,`${i.rangeFrom}^`)}catch{throw new e(`OPERATION_FAILED`,`rewrite-range target's rangeFrom (${i.rangeFrom.slice(0,8)}) has no parent (root commit) — rewriting the root commit is not currently supported.`)}let{headSha:r,shaMap:a}=await g({git:n,baseSha:t,commits:o,hunks:s,signing:p,authorAttribution:_}),d=await y(n,t,r,l);if(!d.valid)throw new e(`SAFETY_CHECK_FAILED`,`Diff verification failed: ${d.error}`);let f=await h({git:n,branch:i.branch,rangeFrom:i.rangeFrom,rangeTo:i.rangeTo,newHeadSha:r,originalBranchSha:u.branchSha,stashLabel:v,leftoverPatch:b,includeWorkdir:C});return{commitShas:Object.fromEntries(a),mutations:D(f.refMovements),stashConflict:f.stashConflict,stashNotRestored:f.stashNotRestored}}if(u.branchName&&!u.mergeBaseSha)throw new e(`OPERATION_FAILED`,`Branch source is missing mergeBaseSha — cannot determine base commit`);let w=u.mergeBaseSha??await c(n,`HEAD`),E=k(r,i);if(i.type===`auto`&&a.length>1)return x(n,w,a,s,l,u,E,p,_);if(i.type===`auto`&&a.length===1&&a[0].partitioning)return S(n,w,a[0],s,l,u,E,p,_,v,C);let{headSha:j,shaMap:M}=await g({git:n,baseSha:w,commits:o,hunks:s,signing:p,authorAttribution:_}),N=Object.fromEntries(M),P=await y(n,w,j,l);if(!P.valid)throw new e(`SAFETY_CHECK_FAILED`,`Diff verification failed: ${P.error}`);if(i.type===`branch`)return A({git:n,source:r,target:i,newHeadSha:j,collection:u,stashLabel:v,leftoverPatch:b,commitShas:N});if(i.type===`auto`){let e=a[0]?.branchGroup.name||u.branchName;if(e){let t=!E&&u.branchName&&u.branchSha?{originalBranchName:u.branchName,originalBranchSha:u.branchSha}:{},r={branchName:e,title:a[0]?.branchGroup.title??``,description:a[0]?.branchGroup.description??``,commitIds:o.map(e=>e.id)};await d({git:n,branches:[{partitions:[r],tipSha:j}],...t});let i=O([r],N,t.originalBranchName,t.originalBranchSha);return{partitions:[r],commitShas:N,mutations:i}}let t=await m({git:n,newHeadSha:j,stashLabel:v,includeWorkdir:C});return{commitShas:N,mutations:T(),stashConflict:t.stashConflict,stashNotRestored:t.stashNotRestored}}let F=await m({git:n,newHeadSha:j,stashLabel:v,leftoverPatch:b,includeWorkdir:C});return{commitShas:N,mutations:T(),stashConflict:F.stashConflict,stashNotRestored:F.stashNotRestored}}async function x(t,n,i,a,s,c,l,u,f){let p=[],m=[],h=[],_=[],y={};for(let e of i){let r=C(e),{headSha:i,shaMap:o}=await g({git:t,baseSha:n,commits:r,hunks:a,signing:u,authorAttribution:f});h.push(i);for(let[e,t]of o)y[e]=t;if(e.partitioning&&e.partitioning.partitions.length>1){let t=w(e.partitioning.partitions,o);p.push(...t),m.push(t),_.push({partitions:t,tipSha:i})}else{let t={branchName:e.branchGroup.name,title:e.branchGroup.title,description:e.branchGroup.description,commitIds:r.map(e=>o.get(e.id)??e.id)};p.push(t),m.push([t]),_.push({partitions:[t],tipSha:i})}}let b=await v(t,n,h,s);if(!b.valid)throw new e(`SAFETY_CHECK_FAILED`,`Diff verification failed: ${b.error}`);let x=!l&&c.branchName&&c.branchSha?{originalBranchName:c.branchName,originalBranchSha:c.branchSha}:{};return await d({git:t,branches:_,...x}),!c.branchName&&!l&&(await o(t),c.untrackedIncluded&&await r(t)),{partitions:p,partitionGroups:m,commitShas:y,mutations:O(p,y,x.originalBranchName,x.originalBranchSha)}}async function S(t,n,i,a,s,c,l,u,f,p,h){let{headSha:_,shaMap:v}=await g({git:t,baseSha:n,commits:C(i),hunks:a,signing:u,authorAttribution:f}),b=await y(t,n,_,s);if(!b.valid)throw new e(`SAFETY_CHECK_FAILED`,`Diff verification failed: ${b.error}`);let x=w(i.partitioning.partitions,v),S=Object.fromEntries(v);if(x.length>1){let e=!l&&c.branchName&&c.branchSha?{originalBranchName:c.branchName,originalBranchSha:c.branchSha}:{};return await d({git:t,branches:[{partitions:x,tipSha:_}],...e}),!c.branchName&&!l&&(await o(t),c.untrackedIncluded&&await r(t,{force:!0,directories:!0})),{partitions:x,commitShas:S,mutations:O(x,S,e.originalBranchName,e.originalBranchSha)}}let E=await m({git:t,newHeadSha:_,stashLabel:p,includeWorkdir:h});return{partitions:x,commitShas:S,mutations:T(),stashConflict:E.stashConflict,stashNotRestored:E.stashNotRestored}}function C(t){return t.orderedCommitIds.map(n=>{let r=t.branchGroup.commits.find(e=>e.id===n);if(!r)throw new e(`OPERATION_FAILED`,`Commit ${n} not found in branch '${t.branchGroup.id}'`);return r})}function w(t,n){return t.map(t=>({...t,commitIds:t.commitIds.map(t=>{let r=n.get(t);if(!r)throw new e(`OPERATION_FAILED`,`No SHA found for commit ID '${t}'`);return r})}))}function T(){return{createdRefs:[],deletedRefs:[],movedRefs:[],composedBranchCount:0}}function E(t){if(t.startsWith(`refs/heads/`))return{kind:`branch`,name:t.slice(11)};if(t.startsWith(`refs/tags/`))return{kind:`tag`,name:t.slice(10)};throw new e(`OPERATION_FAILED`,`Unexpected refname '${t}' in apply mutations — only refs/heads/* and refs/tags/* are tracked.`)}function D(e){let t=[];for(let n of e){let{kind:e,name:r}=E(n.refname);t.push({kind:e,name:r,previousSha:n.previousSha,newSha:n.newSha})}return{createdRefs:[],deletedRefs:[],movedRefs:t,composedBranchCount:1}}function O(e,t,n,r){let i=e.map(e=>{let n=e.commitIds[e.commitIds.length-1];return{kind:`branch`,name:e.branchName,sha:t[n]??n}}),a=[];return n&&r&&a.push({kind:`branch`,name:n,sha:r}),{createdRefs:i,deletedRefs:a,movedRefs:[],composedBranchCount:i.length}}function k(e,t){return t.type!==`branch`&&t.type!==`auto`||e.type===`commit-range`?!0:t.preserveOriginal===!0}async function A(r){let{git:o,source:d,target:f,newHeadSha:m,collection:h,leftoverPatch:g,commitShas:v}=r,y=k(d,f),b=d.type===`branch`&&!y&&h.branchName!=null&&h.branchSha!=null;if(b&&f.name===h.branchName)throw new e(`OPERATION_FAILED`,`target 'branch' with name '${f.name}' conflicts with the source branch (preserveOriginal=false). Use target 'rewrite-range' to rewrite the branch in place under its existing name.`);let x=await l(o,`refs/heads/${f.name}`),S=x?{createdRefs:[],deletedRefs:[],movedRefs:[{kind:`branch`,name:f.name,previousSha:x,newSha:m}],composedBranchCount:1}:{createdRefs:[{kind:`branch`,name:f.name,sha:m}],deletedRefs:[],movedRefs:[],composedBranchCount:1},C=await i(o),w=await c(o,`HEAD`);if(await u(o,`refs/heads/${f.name}`,m),b){let e=h.branchSha,r=h.branchName,i=await p({git:o,oldToSha:e,newTopSha:m,sourceBranchName:void 0});for(let e of i.reparentedRefs){let{kind:t,name:n}=E(e.refname);S.movedRefs.push({kind:t,name:n,previousSha:e.oldSha,newSha:e.newSha})}let c=await _(o,e,m,`refs/heads/${r}`);for(let e of c){let{kind:t,name:n}=E(e.refname);S.movedRefs.push({kind:t,name:n,previousSha:e.previousSha,newSha:e.newSha})}C===r&&(await n(o,f.name),await s(o,m,`hard`),g&&await t(o,g)),await a(o,r,{force:!0}),S.deletedRefs.push({kind:`branch`,name:r,sha:e})}else d.type===`workdir`&&!y&&(await s(o,w,`hard`),g&&await t(o,g));return{commitShas:v,mutations:S}}export{b as applyToTarget};
1
+ import{GitError as e}from"../errors/git.js";import{applyPatchToWorkdir as t,checkout as n,cleanWorkdir as r,currentBranch as i,deleteBranch as a,discardWorkdirChanges as o,reset as s,revParse as c,revParseVerify as l,updateRef as u}from"./port-dispatch.js";import{resolveWorkdirLayers as d}from"./diff-collector.js";import{applyBranchStructure as f}from"./apply-branches.js";import{reparentDescendants as p}from"./reparent.js";import{applyCommitChain as m,applyRangeRewrite as h,buildCommitChain as g,moveRefsAtSha as _}from"./commit-chain.js";import{verifyMultiBranchDiff as v,verifyResultingDiff as y}from"./safety.js";async function b(t){let{git:n,source:r,target:i,branchPlans:a,allOrderedCommits:o,hunks:s,snapshot:l,collection:u,signing:p,authorAttribution:_,stashLabel:v,leftoverPatch:b}=t,C=r.type===`commit-range`&&r.includeWorkdir!=null?d(r.includeWorkdir):void 0;if(i.type===`rewrite-range`){if(!u.branchName||!u.branchSha)throw new e(`OPERATION_FAILED`,`rewrite-range target requires a source that records branchName and branchSha (branch or commit-range).`);let t;try{t=await c(n,`${i.rangeFrom}^`)}catch{throw new e(`OPERATION_FAILED`,`rewrite-range target's rangeFrom (${i.rangeFrom.slice(0,8)}) has no parent (root commit) — rewriting the root commit is not currently supported.`)}let{headSha:r,shaMap:a}=await g({git:n,baseSha:t,commits:o,hunks:s,signing:p,authorAttribution:_}),d=await y(n,t,r,l);if(!d.valid)throw new e(`SAFETY_CHECK_FAILED`,`Diff verification failed: ${d.error}`);let f=await h({git:n,branch:i.branch,rangeFrom:i.rangeFrom,rangeTo:i.rangeTo,newHeadSha:r,originalBranchSha:u.branchSha,stashLabel:v,leftoverPatch:b,includeWorkdir:C});return{commitShas:Object.fromEntries(a),mutations:D(f.refMovements),stashConflict:f.stashConflict,stashNotRestored:f.stashNotRestored}}if(u.branchName&&!u.mergeBaseSha)throw new e(`OPERATION_FAILED`,`Branch source is missing mergeBaseSha — cannot determine base commit`);let w=u.mergeBaseSha??await c(n,`HEAD`),E=k(r,i);if(i.type===`auto`&&a.length>1)return x(n,w,a,s,l,u,E,p,_);if(i.type===`auto`&&a.length===1&&a[0].partitioning)return S(n,w,a[0],s,l,u,E,p,_,v,C);let{headSha:j,shaMap:M}=await g({git:n,baseSha:w,commits:o,hunks:s,signing:p,authorAttribution:_}),N=Object.fromEntries(M),P=await y(n,w,j,l);if(!P.valid)throw new e(`SAFETY_CHECK_FAILED`,`Diff verification failed: ${P.error}`);if(i.type===`branch`)return A({git:n,source:r,target:i,newHeadSha:j,collection:u,stashLabel:v,leftoverPatch:b,commitShas:N});if(i.type===`auto`){let e=a[0]?.branchGroup.name||u.branchName;if(e){let t=!E&&u.branchName&&u.branchSha?{originalBranchName:u.branchName,originalBranchSha:u.branchSha}:{},r={branchName:e,title:a[0]?.branchGroup.title??``,description:a[0]?.branchGroup.description??``,commitIds:o.map(e=>e.id)};await f({git:n,branches:[{partitions:[r],tipSha:j}],...t});let i=O([r],N,t.originalBranchName,t.originalBranchSha);return{partitions:[r],commitShas:N,mutations:i}}let t=await m({git:n,newHeadSha:j,stashLabel:v,includeWorkdir:C});return{commitShas:N,mutations:T(),stashConflict:t.stashConflict,stashNotRestored:t.stashNotRestored}}let F=await m({git:n,newHeadSha:j,stashLabel:v,leftoverPatch:b,includeWorkdir:C});return{commitShas:N,mutations:T(),stashConflict:F.stashConflict,stashNotRestored:F.stashNotRestored}}async function x(t,n,i,a,s,c,l,u,d){let p=[],m=[],h=[],_=[],y={};for(let e of i){let r=C(e),{headSha:i,shaMap:o}=await g({git:t,baseSha:n,commits:r,hunks:a,signing:u,authorAttribution:d});h.push(i);for(let[e,t]of o)y[e]=t;if(e.partitioning&&e.partitioning.partitions.length>1){let t=w(e.partitioning.partitions,o);p.push(...t),m.push(t),_.push({partitions:t,tipSha:i})}else{let t={branchName:e.branchGroup.name,title:e.branchGroup.title,description:e.branchGroup.description,commitIds:r.map(e=>o.get(e.id)??e.id)};p.push(t),m.push([t]),_.push({partitions:[t],tipSha:i})}}let b=await v(t,n,h,s);if(!b.valid)throw new e(`SAFETY_CHECK_FAILED`,`Diff verification failed: ${b.error}`);let x=!l&&c.branchName&&c.branchSha?{originalBranchName:c.branchName,originalBranchSha:c.branchSha}:{};return await f({git:t,branches:_,...x}),!c.branchName&&!l&&(await o(t),c.untrackedIncluded&&await r(t)),{partitions:p,partitionGroups:m,commitShas:y,mutations:O(p,y,x.originalBranchName,x.originalBranchSha)}}async function S(t,n,i,a,s,c,l,u,d,p,h){let{headSha:_,shaMap:v}=await g({git:t,baseSha:n,commits:C(i),hunks:a,signing:u,authorAttribution:d}),b=await y(t,n,_,s);if(!b.valid)throw new e(`SAFETY_CHECK_FAILED`,`Diff verification failed: ${b.error}`);let x=w(i.partitioning.partitions,v),S=Object.fromEntries(v);if(x.length>1){let e=!l&&c.branchName&&c.branchSha?{originalBranchName:c.branchName,originalBranchSha:c.branchSha}:{};return await f({git:t,branches:[{partitions:x,tipSha:_}],...e}),!c.branchName&&!l&&(await o(t),c.untrackedIncluded&&await r(t,{force:!0,directories:!0})),{partitions:x,commitShas:S,mutations:O(x,S,e.originalBranchName,e.originalBranchSha)}}let E=await m({git:t,newHeadSha:_,stashLabel:p,includeWorkdir:h});return{partitions:x,commitShas:S,mutations:T(),stashConflict:E.stashConflict,stashNotRestored:E.stashNotRestored}}function C(t){return t.orderedCommitIds.map(n=>{let r=t.branchGroup.commits.find(e=>e.id===n);if(!r)throw new e(`OPERATION_FAILED`,`Commit ${n} not found in branch '${t.branchGroup.id}'`);return r})}function w(t,n){return t.map(t=>({...t,commitIds:t.commitIds.map(t=>{let r=n.get(t);if(!r)throw new e(`OPERATION_FAILED`,`No SHA found for commit ID '${t}'`);return r})}))}function T(){return{createdRefs:[],deletedRefs:[],movedRefs:[],composedBranchCount:0}}function E(t){if(t.startsWith(`refs/heads/`))return{kind:`branch`,name:t.slice(11)};if(t.startsWith(`refs/tags/`))return{kind:`tag`,name:t.slice(10)};throw new e(`OPERATION_FAILED`,`Unexpected refname '${t}' in apply mutations — only refs/heads/* and refs/tags/* are tracked.`)}function D(e){let t=[];for(let n of e){let{kind:e,name:r}=E(n.refname);t.push({kind:e,name:r,previousSha:n.previousSha,newSha:n.newSha})}return{createdRefs:[],deletedRefs:[],movedRefs:t,composedBranchCount:1}}function O(e,t,n,r){let i=e.map(e=>{let n=e.commitIds[e.commitIds.length-1];return{kind:`branch`,name:e.branchName,sha:t[n]??n}}),a=[];return n&&r&&a.push({kind:`branch`,name:n,sha:r}),{createdRefs:i,deletedRefs:a,movedRefs:[],composedBranchCount:i.length}}function k(e,t){return t.type!==`branch`&&t.type!==`auto`||e.type===`commit-range`?!0:t.preserveOriginal===!0}async function A(r){let{git:o,source:d,target:f,newHeadSha:m,collection:h,leftoverPatch:g,commitShas:v}=r,y=k(d,f),b=d.type===`branch`&&!y&&h.branchName!=null&&h.branchSha!=null;if(b&&f.name===h.branchName)throw new e(`OPERATION_FAILED`,`target 'branch' with name '${f.name}' conflicts with the source branch (preserveOriginal=false). Use target 'rewrite-range' to rewrite the branch in place under its existing name.`);let x=await l(o,`refs/heads/${f.name}`),S=x?{createdRefs:[],deletedRefs:[],movedRefs:[{kind:`branch`,name:f.name,previousSha:x,newSha:m}],composedBranchCount:1}:{createdRefs:[{kind:`branch`,name:f.name,sha:m}],deletedRefs:[],movedRefs:[],composedBranchCount:1},C=await i(o),w=await c(o,`HEAD`);if(await u(o,`refs/heads/${f.name}`,m),b){let e=h.branchSha,r=h.branchName,i=await p({git:o,oldToSha:e,newTopSha:m,sourceBranchName:void 0});for(let e of i.reparentedRefs){let{kind:t,name:n}=E(e.refname);S.movedRefs.push({kind:t,name:n,previousSha:e.oldSha,newSha:e.newSha})}let c=await _(o,e,m,`refs/heads/${r}`);for(let e of c){let{kind:t,name:n}=E(e.refname);S.movedRefs.push({kind:t,name:n,previousSha:e.previousSha,newSha:e.newSha})}C===r&&(await n(o,f.name),await s(o,m,`hard`),g&&await t(o,g)),await a(o,r,{force:!0}),S.deletedRefs.push({kind:`branch`,name:r,sha:e})}else d.type===`workdir`&&!y&&(await s(o,w,`hard`),g&&await t(o,g));return{commitShas:v,mutations:S}}export{b as applyToTarget};
package/dist/git/index.js CHANGED
@@ -1 +1 @@
1
- import{applyBranchStructure as e,createStackedBranches as t}from"./apply-branches.js";import{collectBranchDiffs as n,collectCommitsForSplit as r,collectFromSource as i,collectWorkingDiffs as a}from"./diff-collector.js";import{applyCommitChain as o,buildCommitChain as s}from"./commit-chain.js";import{verifyMultiBranchDiff as c,verifyResultingDiff as l,verifySnapshot as u}from"./safety.js";import{applyToTarget as d}from"./apply-target.js";export{e as applyBranchStructure,o as applyCommitChain,d as applyToTarget,s as buildCommitChain,n as collectBranchDiffs,r as collectCommitsForSplit,i as collectFromSource,a as collectWorkingDiffs,t as createStackedBranches,c as verifyMultiBranchDiff,l as verifyResultingDiff,u as verifySnapshot};
1
+ import{collectBranchDiffs as e,collectCommitsForSplit as t,collectFromSource as n,collectWorkingDiffs as r}from"./diff-collector.js";import{applyBranchStructure as i,createStackedBranches as a}from"./apply-branches.js";import{applyCommitChain as o,buildCommitChain as s}from"./commit-chain.js";import{verifyMultiBranchDiff as c,verifyResultingDiff as l,verifySnapshot as u}from"./safety.js";import{applyToTarget as d}from"./apply-target.js";export{i as applyBranchStructure,o as applyCommitChain,d as applyToTarget,s as buildCommitChain,e as collectBranchDiffs,t as collectCommitsForSplit,n as collectFromSource,r as collectWorkingDiffs,a as createStackedBranches,c as verifyMultiBranchDiff,l as verifyResultingDiff,u as verifySnapshot};
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { DuplicateHunksRepair, PhaseDetail, ProgressEvent, RepairScope, RepairedHunkClaim, TokenUsage, ValidationRepair } from "./ai/types.js";
2
+ import { AuthoringContext, AuthoringDependency, AuthoringEdit, AuthoringIssue, AuthoringNote, AuthoringPullRequest } from "./context.js";
2
3
  import { AIError, AIErrorCode } from "./errors/ai.js";
3
4
  import { GitError, GitErrorCode } from "./errors/git.js";
4
5
  import { DiffTooLargeError, TaskInputError } from "./errors/task.js";
@@ -29,8 +30,9 @@ import { quickCompose } from "./tasks/quick-compose/quick-compose.js";
29
30
  import { DEFAULT_INPUT_TOKEN_BUDGET } from "./tasks/utils/budget.js";
30
31
  import { isNoiseHunk, withDefaultNoiseRedaction } from "./tasks/utils/noise.js";
31
32
  import { ApplyComposePlanInput, ApplyComposePlanResult, ComposeApplyPlan, ComposeDepth, ComposeDiffStats, ComposeInput, ComposePlan, ComposePlanInput, ComposePlanResult, ComposeProgressEvent, ComposeResult, ComposeStrategy, applyComposePlan, compose, composePlan, defaultPhaseMessages } from "./workflows/compose.js";
33
+ import { AuthoredCommit, BuildAuthoredComposePlanInput, CollectComposeHunksInput, CollectComposeHunksResult, buildAuthoredComposePlan, buildAuthoredPlanInstructions, collectComposeHunks } from "./workflows/author.js";
32
34
  import { RefinePlanInput, RefinePlanResult, RefineProgressEvent, refinePlan } from "./workflows/refine.js";
33
35
  import { SplitInput, SplitPlan, SplitProgressEvent, SplitResult, split } from "./workflows/split.js";
34
36
  import { UndoComposeInput, ValidateUndoComposeInput, listUndoIds, undoCompose, validateUndoCompose } from "./workflows/undo.js";
35
37
  import { UndoForceOptions } from "@gitkraken/shared-tools";
36
- export { AIError, type AIErrorCode, type AfterModelCallInfo, type AnalysisResult, type AnalyzeInput, type AnalyzeOutput, type ApplyComposePlanInput, type ApplyComposePlanResult, type ApplyPatchOptions, type BeforeModelCallInfo, type BeforePromptInfo, type BranchGroup, type BranchOrdering, type BranchPartition, type CheckoutOptions, type CleanOptions, type CommitDetail, type CommitSuggestion, type CommitTreeOptions, type ComposeApplyPlan, type ComposeBranchPlan, type ComposeContext, type ComposeDepth, type ComposeDiffStats, type ComposeGitOps, type ComposeGitPort, ComposeGitPortMissingOpError, type ComposeGroupInput, type ComposeGroupOutput, type ComposeHunk, type ComposeInput, type ComposePlan, type ComposePlanInput, type ComposePlanResult, type ComposeProgressEvent, type ComposeResult, type ComposeSession, type ComposeSource, type ComposeStrategy, type ComposeTarget, type ComposeTaskName, ComposeWorkflowError, type ComposeWorkflowErrorCode, ComposeWorkflowInputError, type Concern, type ConcernDependency, type ConventionDescriptor, DEFAULT_INPUT_TOKEN_BUDGET, type DeleteBranchOptions, DiffTooLargeError, type DiffTreeOptions, type DuplicateHunksRepair, type ForEachRefOptions, type GitCommitIdentity, GitError, type GitErrorCode, type GroupInput, type GroupOutput, type GroupingResult, type GroupingStrategy, type HunkAnnotation, type HunkOrderingConstraint, type IndexScopedOptions, type InteriorRefViolation, type LogOptions, type OnAfterModelCall, type OnBeforeModelCall, type OnBeforePrompt, type OpOptions, type OrderInput, type OrderOutput, type OrderingResult, type OverrideSafetyFailures, type PartitionInput, type PartitionOutput, type PartitionResult, type PartitionStrategy, type PhaseDetail, type ProgressEvent, type PromptOverride, type QuickComposeInput, type QuickComposeOutput, type QuickComposeResult, REDACTED_HUNK_CONTENT, type RedactHunkContent, type RefinePlanInput, type RefinePlanResult, type RefineProgressEvent, type RepairScope, type RepairedHunkClaim, type SigningConfig, type SplitInput, type SplitPlan, type SplitProgressEvent, type SplitResult, type StageAllOptions, type StashConflict, type StashNotRestored, type StashPushOptions, TaskInputError, type TokenUsage, type UndoComposeInput, type UndoForceOptions, type UpdateRefOptions, type ValidateUndoComposeInput, type ValidationRepair, analyze, applyComposePlan, compose, composeGroup, composePlan, defaultPhaseMessages, detectInteriorRefs, estimatedCharactersPerToken, group, isNoiseHunk, listUndoIds, order, parseUnifiedDiff, partition, quickCompose, refinePlan, split, undoCompose, validateUndoCompose, withDefaultNoiseRedaction };
38
+ export { AIError, type AIErrorCode, type AfterModelCallInfo, type AnalysisResult, type AnalyzeInput, type AnalyzeOutput, type ApplyComposePlanInput, type ApplyComposePlanResult, type ApplyPatchOptions, type AuthoredCommit, type AuthoringContext, type AuthoringDependency, type AuthoringEdit, type AuthoringIssue, type AuthoringNote, type AuthoringPullRequest, type BeforeModelCallInfo, type BeforePromptInfo, type BranchGroup, type BranchOrdering, type BranchPartition, type BuildAuthoredComposePlanInput, type CheckoutOptions, type CleanOptions, type CollectComposeHunksInput, type CollectComposeHunksResult, type CommitDetail, type CommitSuggestion, type CommitTreeOptions, type ComposeApplyPlan, type ComposeBranchPlan, type ComposeContext, type ComposeDepth, type ComposeDiffStats, type ComposeGitOps, type ComposeGitPort, ComposeGitPortMissingOpError, type ComposeGroupInput, type ComposeGroupOutput, type ComposeHunk, type ComposeInput, type ComposePlan, type ComposePlanInput, type ComposePlanResult, type ComposeProgressEvent, type ComposeResult, type ComposeSession, type ComposeSource, type ComposeStrategy, type ComposeTarget, type ComposeTaskName, ComposeWorkflowError, type ComposeWorkflowErrorCode, ComposeWorkflowInputError, type Concern, type ConcernDependency, type ConventionDescriptor, DEFAULT_INPUT_TOKEN_BUDGET, type DeleteBranchOptions, DiffTooLargeError, type DiffTreeOptions, type DuplicateHunksRepair, type ForEachRefOptions, type GitCommitIdentity, GitError, type GitErrorCode, type GroupInput, type GroupOutput, type GroupingResult, type GroupingStrategy, type HunkAnnotation, type HunkOrderingConstraint, type IndexScopedOptions, type InteriorRefViolation, type LogOptions, type OnAfterModelCall, type OnBeforeModelCall, type OnBeforePrompt, type OpOptions, type OrderInput, type OrderOutput, type OrderingResult, type OverrideSafetyFailures, type PartitionInput, type PartitionOutput, type PartitionResult, type PartitionStrategy, type PhaseDetail, type ProgressEvent, type PromptOverride, type QuickComposeInput, type QuickComposeOutput, type QuickComposeResult, REDACTED_HUNK_CONTENT, type RedactHunkContent, type RefinePlanInput, type RefinePlanResult, type RefineProgressEvent, type RepairScope, type RepairedHunkClaim, type SigningConfig, type SplitInput, type SplitPlan, type SplitProgressEvent, type SplitResult, type StageAllOptions, type StashConflict, type StashNotRestored, type StashPushOptions, TaskInputError, type TokenUsage, type UndoComposeInput, type UndoForceOptions, type UpdateRefOptions, type ValidateUndoComposeInput, type ValidationRepair, analyze, applyComposePlan, buildAuthoredComposePlan, buildAuthoredPlanInstructions, collectComposeHunks, compose, composeGroup, composePlan, defaultPhaseMessages, detectInteriorRefs, estimatedCharactersPerToken, group, isNoiseHunk, listUndoIds, order, parseUnifiedDiff, partition, quickCompose, refinePlan, split, undoCompose, validateUndoCompose, withDefaultNoiseRedaction };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{AIError as e}from"./errors/ai.js";import{GitError as t}from"./errors/git.js";import{DiffTooLargeError as n,TaskInputError as r}from"./errors/task.js";import{ComposeWorkflowError as i,ComposeWorkflowInputError as a}from"./errors/workflow.js";import{ComposeGitPortMissingOpError as o}from"./ports/git.js";import{detectInteriorRefs as s}from"./git/safety-range.js";import{parseUnifiedDiff as c}from"./git/utils/hunk-parser.js";import{estimatedCharactersPerToken as l}from"./tasks/ai-loop.js";import{REDACTED_HUNK_CONTENT as u}from"./tasks/utils/redact.js";import{DEFAULT_INPUT_TOKEN_BUDGET as d}from"./tasks/utils/budget.js";import{analyze as f}from"./tasks/analyze/analyze.js";import{composeGroup as p}from"./tasks/compose-group/compose-group.js";import{group as m}from"./tasks/group/group.js";import{order as h}from"./tasks/order/order.js";import{partition as g}from"./tasks/partition/partition.js";import{quickCompose as _}from"./tasks/quick-compose/quick-compose.js";import{isNoiseHunk as v,withDefaultNoiseRedaction as y}from"./tasks/utils/noise.js";import{applyComposePlan as b,compose as x,composePlan as S,defaultPhaseMessages as C}from"./workflows/compose.js";import{refinePlan as w}from"./workflows/refine.js";import{split as T}from"./workflows/split.js";import{listUndoIds as E,undoCompose as D,validateUndoCompose as O}from"./workflows/undo.js";export{e as AIError,o as ComposeGitPortMissingOpError,i as ComposeWorkflowError,a as ComposeWorkflowInputError,d as DEFAULT_INPUT_TOKEN_BUDGET,n as DiffTooLargeError,t as GitError,u as REDACTED_HUNK_CONTENT,r as TaskInputError,f as analyze,b as applyComposePlan,x as compose,p as composeGroup,S as composePlan,C as defaultPhaseMessages,s as detectInteriorRefs,l as estimatedCharactersPerToken,m as group,v as isNoiseHunk,E as listUndoIds,h as order,c as parseUnifiedDiff,g as partition,_ as quickCompose,w as refinePlan,T as split,D as undoCompose,O as validateUndoCompose,y as withDefaultNoiseRedaction};
1
+ import{AIError as e}from"./errors/ai.js";import{GitError as t}from"./errors/git.js";import{DiffTooLargeError as n,TaskInputError as r}from"./errors/task.js";import{ComposeWorkflowError as i,ComposeWorkflowInputError as a}from"./errors/workflow.js";import{ComposeGitPortMissingOpError as o}from"./ports/git.js";import{detectInteriorRefs as s}from"./git/safety-range.js";import{parseUnifiedDiff as c}from"./git/utils/hunk-parser.js";import{estimatedCharactersPerToken as l}from"./tasks/ai-loop.js";import{REDACTED_HUNK_CONTENT as u}from"./tasks/utils/redact.js";import{DEFAULT_INPUT_TOKEN_BUDGET as d}from"./tasks/utils/budget.js";import{analyze as f}from"./tasks/analyze/analyze.js";import{composeGroup as p}from"./tasks/compose-group/compose-group.js";import{group as m}from"./tasks/group/group.js";import{order as h}from"./tasks/order/order.js";import{partition as g}from"./tasks/partition/partition.js";import{quickCompose as _}from"./tasks/quick-compose/quick-compose.js";import{isNoiseHunk as v,withDefaultNoiseRedaction as y}from"./tasks/utils/noise.js";import{applyComposePlan as b,compose as x,composePlan as S,defaultPhaseMessages as C}from"./workflows/compose.js";import{buildAuthoredComposePlan as w,buildAuthoredPlanInstructions as T,collectComposeHunks as E}from"./workflows/author.js";import{refinePlan as D}from"./workflows/refine.js";import{split as O}from"./workflows/split.js";import{listUndoIds as k,undoCompose as A,validateUndoCompose as j}from"./workflows/undo.js";export{e as AIError,o as ComposeGitPortMissingOpError,i as ComposeWorkflowError,a as ComposeWorkflowInputError,d as DEFAULT_INPUT_TOKEN_BUDGET,n as DiffTooLargeError,t as GitError,u as REDACTED_HUNK_CONTENT,r as TaskInputError,f as analyze,b as applyComposePlan,w as buildAuthoredComposePlan,T as buildAuthoredPlanInstructions,E as collectComposeHunks,x as compose,p as composeGroup,S as composePlan,C as defaultPhaseMessages,s as detectInteriorRefs,l as estimatedCharactersPerToken,m as group,v as isNoiseHunk,k as listUndoIds,h as order,c as parseUnifiedDiff,g as partition,_ as quickCompose,D as refinePlan,O as split,A as undoCompose,j as validateUndoCompose,y as withDefaultNoiseRedaction};
@@ -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{buildAnalyzeSystemPrompt as r,buildAnalyzeUserPrompt as i}from"./prompt.js";import{validateAnalyzeResponse as a}from"./validator.js";async function o(o){let s=o.hunks;if(!s||s.length===0)throw new e(`No hunks provided: supply 'hunks'`);if(o.session?.completedSteps.analyze)return{result:o.session.completedSteps.analyze,session:o.session,attempts:0,durationMs:0,callIds:[]};let c=o.session?void 0:o.promptOverride?.system??r(),l,u=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,inputTokenBudget:o.inputTokenBudget,systemPromptChars:c?.length,onBudgetReduction:e=>{l=e}}),o.promptOverride),d=new Set(s.map(e=>e.index)),f=await t({model:o.model,systemPrompt:c,userPrompt:u,session:o.session,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,d),taskName:`analyze`}),p=o.session?.completedSteps??{},m={...f.session,completedSteps:{...p,analyze:f.result}};return{result:f.result,session:m,usage:f.usage,attempts:f.attempts,durationMs:f.durationMs,callIds:f.callIds,hunksCondensedCount:l}}export{o as analyze};
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{buildAnalyzeSystemPrompt as r,buildAnalyzeUserPrompt as i}from"./prompt.js";import{validateAnalyzeResponse as a}from"./validator.js";async function o(o){let s=o.hunks;if(!s||s.length===0)throw new e(`No hunks provided: supply 'hunks'`);if(o.session?.completedSteps.analyze)return{result:o.session.completedSteps.analyze,session:o.session,attempts:0,durationMs:0,callIds:[]};let c=o.session?void 0:o.promptOverride?.system??r(),l,u=n(i({hunks:s,commitMessages:o.commitMessages,commitOrder:o.commitOrder,branchName:o.branchName,baseBranch:o.baseBranch,issueUrl:o.issueUrl,pullRequestUrl:o.pullRequestUrl,authoring:o.authoring,hunkSummaries:o.hunkSummaries,condensed:o.condensed,instructions:o.instructions,redactHunkContent:o.redactHunkContent,inputTokenBudget:o.inputTokenBudget,systemPromptChars:c?.length,onBudgetReduction:e=>{l=e}}),o.promptOverride),d=new Set(s.map(e=>e.index)),f=await t({model:o.model,systemPrompt:c,userPrompt:u,session:o.session,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,d),taskName:`analyze`}),p=o.session?.completedSteps??{},m={...f.session,completedSteps:{...p,analyze:f.result}};return{result:f.result,session:m,usage:f.usage,attempts:f.attempts,durationMs:f.durationMs,callIds:f.callIds,hunksCondensedCount:l}}export{o as analyze};
@@ -1,3 +1,4 @@
1
+ import { AuthoringContext } from "../../context.js";
1
2
  import { ComposeHunk } from "../types.js";
2
3
  import { RedactHunkContent } from "../utils/redact.js";
3
4
 
@@ -10,8 +11,11 @@ declare function buildAnalyzeUserPrompt(params: {
10
11
  branchName?: string;
11
12
  baseBranch?: string;
12
13
  issueUrl?: string;
13
- pullRequestUrl?: string;
14
- instructions?: string;
14
+ pullRequestUrl?: string; /** What the author knows that the hunks cannot show. Rendered as escaped, tagged data. */
15
+ authoring?: AuthoringContext; /** One-line summaries of what individual hunks do, keyed by index. See `ComposeInput.hunkSummaries`. */
16
+ hunkSummaries?: Record<number, string>;
17
+ instructions?: string; /** Send hunk metadata only, omitting every patch body. See `ComposeInput.condenseHunks`. */
18
+ condensed?: boolean;
15
19
  redactHunkContent?: RedactHunkContent; /** Input-token ceiling that triggers progressive hunk reduction. Defaults to the shared budget. */
16
20
  inputTokenBudget?: number; /** Char count of the system prompt, folded into the budget estimate. Defaults to 0. */
17
21
  systemPromptChars?: number; /** Reports how many hunks were condensed for budget (full to condensed), for consumer telemetry. */
@@ -1,4 +1,4 @@
1
- import{assembleHunkPrompt as e}from"../utils/budget.js";import{analysisResultJsonSchema as t}from"./schema.js";const n=JSON.stringify(t,null,2);function r(){return`You are an advanced AI code analysis assistant. Your task is to deeply analyze a set of code changes (provided as diff hunks) and produce a structured understanding of what the changes accomplish, what concerns they address, and how hunks relate to each other.
1
+ import{authoringContextBlock as e,resolveAuthoringUrls as t}from"../utils/authoring-context.js";import{assembleHunkPrompt as n}from"../utils/budget.js";import{renderHunkSummaries as r}from"../utils/hunk-summaries.js";import{analysisResultJsonSchema as i}from"./schema.js";const a=JSON.stringify(i,null,2);function o(){return`You are an advanced AI code analysis assistant. Your task is to deeply analyze a set of code changes (provided as diff hunks) and produce a structured understanding of what the changes accomplish, what concerns they address, and how hunks relate to each other.
2
2
 
3
3
  Your analysis must produce:
4
4
  1. **Title** — a short, PR-style title for the changes (under 72 characters, imperative mood, e.g. "Add user authentication and session management"). This should capture the essence of what the changes accomplish.
@@ -26,10 +26,10 @@ Rules:
26
26
  Output your analysis as JSON inside a <output> tag matching this schema:
27
27
 
28
28
  \`\`\`json
29
- ${n}
29
+ ${a}
30
30
  \`\`\`
31
31
 
32
- Return only the <output> tag and no other text.`}function i(t){let{hunks:n,commitMessages:r,commitOrder:i,branchName:a,baseBranch:o,issueUrl:s,pullRequestUrl:c,instructions:l,redactHunkContent:u,inputTokenBudget:d,systemPromptChars:f,onBudgetReduction:p}=t,{prompt:m,hunksCondensedForBudget:h}=e({hunks:n,assemble:e=>{let t=`Analyze the following code changes:\n\n<hunks>\n${e}\n</hunks>`,n=[];if(a&&n.push(`Branch: ${a}`),o&&n.push(`Base branch: ${o}`),s&&n.push(`Issue: ${s}`),c&&n.push(`Pull request: ${c}`),n.length>0&&(t+=`\n\n<context>\n${n.join(`
33
- `)}\n</context>`),r?.length){let e=r.map((e,t)=>({index:t,sha:i?.[t],message:e}));t+=`\n\n<commit_messages>\n${JSON.stringify(e)}\n</commit_messages>`}return l&&(t+=`\n\nCarefully follow these additional instructions, but do not deviate from the output structure:\n${l}`),t+=`
32
+ Return only the <output> tag and no other text.`}function s(i){let{hunks:a,commitMessages:o,commitOrder:s,branchName:c,baseBranch:l,issueUrl:u,pullRequestUrl:d,authoring:f,hunkSummaries:p,instructions:m,condensed:h,redactHunkContent:g,inputTokenBudget:_,systemPromptChars:v,onBudgetReduction:y}=i,b=t(f,{issueUrl:u,pullRequestUrl:d}),x=e(f),S=r(p,a),{prompt:C,hunksCondensedForBudget:w}=n({hunks:a,assemble:e=>{let t=`Analyze the following code changes:\n\n<hunks>\n${e}\n</hunks>`,n=[];if(c&&n.push(`Branch: ${c}`),l&&n.push(`Base branch: ${l}`),b.issueUrl&&n.push(`Issue: ${b.issueUrl}`),b.pullRequestUrl&&n.push(`Pull request: ${b.pullRequestUrl}`),n.length>0&&(t+=`\n\n<context>\n${n.join(`
33
+ `)}\n</context>`),S&&(t+=`\n\n${S}`),x&&(t+=`\n\n${x}`),o?.length){let e=o.map((e,t)=>({index:t,sha:s?.[t],message:e}));t+=`\n\n<commit_messages>\n${JSON.stringify(e)}\n</commit_messages>`}return m&&(t+=`\n\nCarefully follow these additional instructions, but do not deviate from the output structure:\n${m}`),t+=`
34
34
 
35
- Now analyze the changes and return your structured analysis. Return only the <output> tag and no other text.`,t},redactHunkContent:u,inputTokenBudget:d,systemPromptChars:f});return p?.(h),m}export{r as buildAnalyzeSystemPrompt,i as buildAnalyzeUserPrompt};
35
+ Now analyze the changes and return your structured analysis. Return only the <output> tag and no other text.`,t},condensed:h,redactHunkContent:g,inputTokenBudget:_,systemPromptChars:v});return y?.(w),C}export{o as buildAnalyzeSystemPrompt,s as buildAnalyzeUserPrompt};
@@ -1,4 +1,5 @@
1
1
  import { ProgressEvent, TokenUsage } from "../../ai/types.js";
2
+ import { AuthoringContext } from "../../context.js";
2
3
  import { ComposeHunk, PromptOverride } from "../types.js";
3
4
  import { OnAfterModelCall, OnBeforeModelCall, OnBeforePrompt } from "../ai-loop.js";
4
5
  import { RedactHunkContent } from "../utils/redact.js";
@@ -14,7 +15,17 @@ interface AnalyzeInput {
14
15
  baseBranch?: string;
15
16
  issueUrl?: string;
16
17
  pullRequestUrl?: string;
18
+ /**
19
+ * What the author knows that the hunks cannot show — intent, issue text, per-file rationale,
20
+ * ordering constraints. Rendered into the prompt as escaped, tagged data, never as instructions;
21
+ * for guidance the model should act on, use {@link instructions}.
22
+ */
23
+ authoring?: AuthoringContext;
24
+ /** One-line summaries of what individual hunks do, keyed by index. See `ComposeInput.hunkSummaries`. */
25
+ hunkSummaries?: Record<number, string>;
17
26
  instructions?: string;
27
+ /** Send hunk metadata only, omitting every patch body. See `ComposeInput.condenseHunks`. */
28
+ condensed?: boolean;
18
29
  /** See {@link RedactHunkContent}. */
19
30
  redactHunkContent?: RedactHunkContent;
20
31
  /**
@@ -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"../group/validator.js";import{buildComposeGroupSystemPrompt as i,buildComposeGroupUserPrompt as a}from"./prompt.js";async function o(o){let s=o.hunks,c=o.mode??`branches`;if(!s||s.length===0)throw new e(`No hunks provided for compose-group`);let l=new Set(s.map(e=>e.index));if(o.session?.completedSteps.composeGroup)return{result:o.session.completedSteps.composeGroup,session:o.session,attempts:0,durationMs:0,callIds:[]};let u=o.promptOverride?.system??i(o.conventions,c,o.strategy,o.includeAnalysis,o.lightweight),d,f=n(a({hunks:s,commitMessages:o.commitMessages,commitOrder:o.commitOrder,branchName:o.branchName,baseBranch:o.baseBranch,issueUrl:o.issueUrl,pullRequestUrl:o.pullRequestUrl,instructions:o.instructions,condensed:o.condensed,redactHunkContent:o.redactHunkContent,inputTokenBudget:o.inputTokenBudget,systemPromptChars:u.length,onBudgetReduction:e=>{d=e}}),o.promptOverride),p=await t({model:o.model,systemPrompt:u,userPrompt:f,maxRetries:o.maxRetries,maxTokens:o.maxTokens,onProgress:o.onProgress,cancellation:o.cancellation,onBeforeModelCall:o.onBeforeModelCall,onAfterModelCall:o.onAfterModelCall,onBeforePrompt:o.onBeforePrompt,validate:e=>r(e,l,c,o.existingBranchNames),taskName:`compose-group`}),m=o.session?.completedSteps??{},h={...p.session,completedSteps:{...m,composeGroup:p.result}};return{result:p.result,session:h,usage:p.usage,attempts:p.attempts,durationMs:p.durationMs,callIds:p.callIds,hunksCondensedCount:d,repairs:p.repairs}}export{o as composeGroup};
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"../group/validator.js";import{buildComposeGroupSystemPrompt as i,buildComposeGroupUserPrompt as a}from"./prompt.js";async function o(o){let s=o.hunks,c=o.mode??`branches`;if(!s||s.length===0)throw new e(`No hunks provided for compose-group`);let l=new Set(s.map(e=>e.index));if(o.session?.completedSteps.composeGroup)return{result:o.session.completedSteps.composeGroup,session:o.session,attempts:0,durationMs:0,callIds:[]};let u=o.promptOverride?.system??i(o.conventions,c,o.strategy,o.includeAnalysis,o.lightweight),d,f=n(a({hunks:s,commitMessages:o.commitMessages,commitOrder:o.commitOrder,branchName:o.branchName,baseBranch:o.baseBranch,issueUrl:o.issueUrl,pullRequestUrl:o.pullRequestUrl,authoring:o.authoring,analysis:o.analysis,hunkSummaries:o.hunkSummaries,instructions:o.instructions,condensed:o.condensed,redactHunkContent:o.redactHunkContent,inputTokenBudget:o.inputTokenBudget,systemPromptChars:u.length,onBudgetReduction:e=>{d=e}}),o.promptOverride),p=await t({model:o.model,systemPrompt:u,userPrompt:f,maxRetries:o.maxRetries,maxTokens:o.maxTokens,onProgress:o.onProgress,cancellation:o.cancellation,onBeforeModelCall:o.onBeforeModelCall,onAfterModelCall:o.onAfterModelCall,onBeforePrompt:o.onBeforePrompt,validate:e=>r(e,l,c,o.existingBranchNames),taskName:`compose-group`}),m=o.session?.completedSteps??{},h={...p.session,completedSteps:{...m,composeGroup:p.result}};return{result:p.result,session:h,usage:p.usage,attempts:p.attempts,durationMs:p.durationMs,callIds:p.callIds,hunksCondensedCount:d,repairs:p.repairs}}export{o as composeGroup};
@@ -1,6 +1,8 @@
1
+ import { AuthoringContext } from "../../context.js";
1
2
  import { ComposeHunk } from "../types.js";
2
3
  import { ConventionDescriptor } from "../../git/utils/conventions.js";
3
4
  import { RedactHunkContent } from "../utils/redact.js";
5
+ import { AnalysisResult } from "../analyze/types.js";
4
6
  import { GroupingStrategy } from "../group/types.js";
5
7
 
6
8
  //#region src/tasks/compose-group/prompt.d.ts
@@ -19,7 +21,10 @@ declare function buildComposeGroupUserPrompt(params: {
19
21
  branchName?: string;
20
22
  baseBranch?: string;
21
23
  issueUrl?: string;
22
- pullRequestUrl?: string;
24
+ pullRequestUrl?: string; /** What the author knows that the hunks cannot show. Rendered as escaped, tagged data. */
25
+ authoring?: AuthoringContext; /** Caller-supplied analysis, used in place of asking the model to derive one. */
26
+ analysis?: AnalysisResult; /** One-line summaries of what individual hunks do, keyed by index. See `ComposeInput.hunkSummaries`. */
27
+ hunkSummaries?: Record<number, string>;
23
28
  instructions?: string;
24
29
  condensed?: boolean;
25
30
  redactHunkContent?: RedactHunkContent; /** Input-token ceiling that triggers progressive hunk reduction. Defaults to the shared budget. */
@@ -1,4 +1,4 @@
1
- import{assembleHunkPrompt as e}from"../utils/budget.js";import{groupingResultJsonSchema as t}from"../group/schema.js";const n=JSON.stringify(t,null,2),r=`
1
+ import{renderAuthoringContext as e}from"../utils/authoring-context.js";import{assembleHunkPrompt as t}from"../utils/budget.js";import{renderHunkSummaries as n}from"../utils/hunk-summaries.js";import{groupingResultJsonSchema as r}from"../group/schema.js";import{renderSuppliedAnalysis as i}from"../utils/analysis-prompt.js";const a=JSON.stringify(r,null,2),o=`
2
2
  In addition to the \`branches\` field, include an \`analysis\` field at the top level:
3
3
 
4
4
  \`\`\`json
@@ -10,12 +10,12 @@ In addition to the \`branches\` field, include an \`analysis\` field at the top
10
10
  "branches": [ ... ]
11
11
  }
12
12
  \`\`\`
13
- `,i=`5. Write a full commit message for each commit containing both a summary and a description:
13
+ `,s=`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.`,a=`5. Write a concise single-line commit message for each commit — a subject only, no body: 50 chars max, third-person singular present tense ("Fixes", "Adds", "Improves"). This repository's history overwhelmingly uses one-line commit messages, so do not add a description.`;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,o){let s=`${e?.commits.bodyStyle===`minimal`?a:i}
18
- 6. Write a detailed explanation for each commit as if explaining to a reviewer.${e?.promptFragment?`\n\n${e.promptFragment}`:``}`,c=u(t?.commitGranularity);return`You are an advanced AI programming assistant tasked with analyzing and organizing code changes into commits.
17
+ - Informal yet professional tone.`,c=`5. Write a concise single-line commit message for each commit — a subject only, no body: 50 chars max, third-person singular present tense ("Fixes", "Adds", "Improves"). This repository's history overwhelmingly uses one-line commit messages, so do not add a description.`;function l(e,t=`branches`,n,r,i){return i?t===`commits`?m(e,n):h(e,n):t===`commits`?u(e,n,r):d(e,n,r)}function u(e,t,n){let r=`${e?.commits.bodyStyle===`minimal`?c:s}
18
+ 6. Write a detailed explanation for each commit as if explaining to a reviewer.${e?.promptFragment?`\n\n${e.promptFragment}`:``}`,i=p(t?.commitGranularity);return`You are an advanced AI programming assistant tasked with analyzing and organizing code changes into commits.
19
19
 
20
20
  ## Critical constraints (output is rejected if violated)
21
21
 
@@ -26,34 +26,34 @@ In addition to the \`branches\` field, include an \`analysis\` field at the top
26
26
 
27
27
  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.
28
28
 
29
- ${o?`## Step 1: Analysis`:`## Step 1: Internal Analysis`}
29
+ ${n?`## Step 1: Analysis`:`## Step 1: Internal Analysis`}
30
30
 
31
31
  Before grouping, analyze the changes to understand:
32
32
  - **Concerns** — what distinct themes, features, or purposes exist across the hunks
33
33
  - **Dependencies** — which hunks provide things (define functions, types, files) that other hunks consume (import, call, reference)
34
34
  - **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
35
35
 
36
- ${o?"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.`}
36
+ ${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.`}
37
37
 
38
38
  ## Step 2: Group hunks into commits
39
39
 
40
40
  1. Group hunks into logical units that make sense together and can be applied atomically
41
41
  2. Ensure each commit is self-contained and atomic
42
- ${s}
43
- ${c}
42
+ ${r}
43
+ ${i}
44
44
 
45
45
  ## Required Output Format
46
46
 
47
47
  Output your organization as JSON inside a <output> tag matching this schema:
48
48
 
49
49
  \`\`\`json
50
- ${n}
50
+ ${a}
51
51
  \`\`\`
52
- ${o?r:``}
52
+ ${n?o:``}
53
53
  Wrap all commits in a single branch object with id "default", name "", title "", and description "".
54
54
 
55
- Return only the <output> tag and no other text.`}function c(e,t,o){let s=`${e?.commits.bodyStyle===`minimal`?a:i}
56
- 6. Write a detailed explanation for each commit as if explaining to a reviewer.${e?.promptFragment?`\n\n${e.promptFragment}`:``}`,c=l(t?.branchSplitting,e),d=u(t?.commitGranularity);return`You are an advanced AI programming assistant tasked with analyzing and organizing code changes into commits and branches.
55
+ Return only the <output> tag and no other text.`}function d(e,t,n){let r=`${e?.commits.bodyStyle===`minimal`?c:s}
56
+ 6. Write a detailed explanation for each commit as if explaining to a reviewer.${e?.promptFragment?`\n\n${e.promptFragment}`:``}`,i=f(t?.branchSplitting,e),l=p(t?.commitGranularity);return`You are an advanced AI programming assistant tasked with analyzing and organizing code changes into commits and branches.
57
57
 
58
58
  ## Critical constraints (output is rejected if violated)
59
59
 
@@ -64,7 +64,7 @@ Return only the <output> tag and no other text.`}function c(e,t,o){let s=`${e?.c
64
64
 
65
65
  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.
66
66
 
67
- ${o?`## Step 1: Analysis`:`## Step 1: Internal Analysis`}
67
+ ${n?`## Step 1: Analysis`:`## Step 1: Internal Analysis`}
68
68
 
69
69
  Before grouping, analyze the changes to understand:
70
70
  - **Concerns** — what distinct themes, features, or purposes exist across the hunks
@@ -72,7 +72,7 @@ Before grouping, analyze the changes to understand:
72
72
  - **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
73
73
  - **Merge-order preference** — even if two concerns compile independently, would merging one without the other be confusing or incomplete?
74
74
 
75
- ${o?"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.`}
75
+ ${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.`}
76
76
 
77
77
  ## Step 2: Organize hunks into commits and branches
78
78
 
@@ -85,26 +85,26 @@ Your job has two levels:
85
85
 
86
86
  1. Group hunks into logical units that make sense together and can be applied atomically
87
87
  2. Ensure each commit is self-contained and atomic
88
- ${s}
89
- ${d}
88
+ ${r}
89
+ ${l}
90
90
 
91
91
  ### Branch guidelines
92
92
 
93
- ${c}
93
+ ${i}
94
94
 
95
95
  ## Required Output Format
96
96
 
97
97
  Output your organization as JSON inside a <output> tag matching this schema:
98
98
 
99
99
  \`\`\`json
100
- ${n}
100
+ ${a}
101
101
  \`\`\`
102
- ${o?r:``}
103
- 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.
102
+ ${n?o:``}
103
+ Return only the <output> tag and no other text.`}function f(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.
104
104
  ${t?.promptFragment?`- ${n} Follow the repository's branch naming conventions (see above).`:`- ${n}`}
105
- - 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`?`
105
+ - 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 p(e){return e===`consolidated`?`
106
106
  Prefer fewer, larger commits. Combine related changes into a single commit when they serve the same purpose.`:e===`granular`?`
107
- Prefer more, smaller commits. Each distinct change should be its own commit.`:``}function d(e,t){return`Organize code hunks into clean, atomic commits.
107
+ Prefer more, smaller commits. Each distinct change should be its own commit.`:``}function m(e,t){return`Organize code hunks into clean, atomic commits.
108
108
 
109
109
  ## Rules
110
110
 
@@ -115,7 +115,7 @@ Prefer more, smaller commits. Each distinct change should be its own commit.`:``
115
115
  5. ${e?.promptFragment?`Write commit messages following the repository's conventions (see below). Write a one-sentence explanation per commit.
116
116
 
117
117
  ${e.promptFragment}`:`Write commit messages: first line under 50 chars, imperative mood ("Add", "Fix", "Update"). Write a one-sentence explanation per commit.`}
118
- ${u(t?.commitGranularity)}
118
+ ${p(t?.commitGranularity)}
119
119
 
120
120
  ## Output
121
121
 
@@ -125,9 +125,9 @@ JSON inside <output> tags. Use one branch with id "default", name "", title "",
125
125
  {"branches":[{"id":"default","name":"","title":"","description":"","commits":[{"id":"commit-1","message":"Add feature X","explanation":"Brief reason.","hunks":[0,1,2]}]}]}
126
126
  \`\`\`
127
127
 
128
- 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.
128
+ 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 h(e,t){let n=e?.promptFragment?`Write commit messages following the repository's conventions (see below). Write a one-sentence explanation per commit.
129
129
 
130
- ${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.
130
+ ${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=f(t?.branchSplitting,e);return`Organize code hunks into clean, atomic commits and branches.
131
131
 
132
132
  ## Commit rules
133
133
 
@@ -136,7 +136,7 @@ ${e.promptFragment}`:`Write commit messages: first line under 50 chars, third-pe
136
136
  3. Each commit needs a unique ID (e.g., "commit-1", "commit-2").
137
137
  4. Every commit must have at least one hunk. No empty commits.
138
138
  5. ${n}
139
- ${u(t?.commitGranularity)}
139
+ ${p(t?.commitGranularity)}
140
140
 
141
141
  ## Branch guidelines
142
142
 
@@ -152,11 +152,11 @@ JSON inside <output> tags. Each branch needs a unique ID and non-empty name.
152
152
 
153
153
  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.
154
154
 
155
- 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,inputTokenBudget:f,systemPromptChars:p,onBudgetReduction:m}=t,h=n.map(e=>e.index).join(`, `),{prompt:g,hunksCondensedForBudget:_}=e({hunks:n,assemble:e=>{let t=`Organize these ${n.length} code hunks (indices: ${h}) into commits. Every listed index must appear exactly once.
155
+ Return only the <output> tag and no other text.`}function g(r){let{hunks:a,commitMessages:o,commitOrder:s,branchName:c,baseBranch:l,issueUrl:u,pullRequestUrl:d,authoring:f,analysis:p,hunkSummaries:m,instructions:h,condensed:g,redactHunkContent:_,inputTokenBudget:v,systemPromptChars:y,onBudgetReduction:b}=r,x=i(p),S=n(m,a),C=a.map(e=>e.index).join(`, `),{prompt:w,hunksCondensedForBudget:T}=t({hunks:a,assemble:t=>{let n=`Organize these ${a.length} code hunks (indices: ${C}) into commits. Every listed index must appear exactly once.
156
156
 
157
157
  <hunks>
158
- ${e}
159
- </hunks>`,u=[];return a&&u.push(`Branch: ${a}`),o&&u.push(`Base: ${o}`),s&&u.push(`Issue: ${s}`),c&&u.push(`PR: ${c}`),u.length>0&&(t+=`\n\nContext:\n${u.join(`
160
- `)}`),r?.length&&(t+=`\n\nOriginal commit messages (match their style):\n<commit_messages>\n${JSON.stringify(r)}\n</commit_messages>`),i?.length&&(t+=`\n\nOriginal commit order:\n${JSON.stringify(i)}`),l&&(t+=`\n\nAdditional instructions:\n${l}`),t+=`
158
+ ${t}
159
+ </hunks>`,r=e(f,{issueUrl:u,pullRequestUrl:d}),i=[];return c&&i.push(`Branch: ${c}`),l&&i.push(`Base: ${l}`),i.push(...r.lines),i.length>0&&(n+=`\n\nContext:\n${i.join(`
160
+ `)}`),S&&(n+=`\n\n${S}`),x&&(n+=`\n\n${x}`),r.block&&(n+=`\n\n${r.block}`),o?.length&&(n+=`\n\nOriginal commit messages (match their style):\n<commit_messages>\n${JSON.stringify(o)}\n</commit_messages>`),s?.length&&(n+=`\n\nOriginal commit order:\n${JSON.stringify(s)}`),h&&(n+=`\n\nAdditional instructions:\n${h}`),n+=`
161
161
 
162
- Return only the <output> tag and no other text.`,t},condensed:u,redactHunkContent:d,inputTokenBudget:f,systemPromptChars:p});return m?.(_),g}export{o as buildComposeGroupSystemPrompt,p as buildComposeGroupUserPrompt};
162
+ Return only the <output> tag and no other text.`,n},condensed:g,redactHunkContent:_,inputTokenBudget:v,systemPromptChars:y});return b?.(T),w}export{l as buildComposeGroupSystemPrompt,g as buildComposeGroupUserPrompt};
@@ -1,8 +1,10 @@
1
1
  import { ProgressEvent } from "../../ai/types.js";
2
+ import { AuthoringContext } from "../../context.js";
2
3
  import { ComposeHunk, PromptOverride } from "../types.js";
3
4
  import { ConventionDescriptor } from "../../git/utils/conventions.js";
4
5
  import { OnAfterModelCall, OnBeforeModelCall, OnBeforePrompt } from "../ai-loop.js";
5
6
  import { RedactHunkContent } from "../utils/redact.js";
7
+ import { AnalysisResult } from "../analyze/types.js";
6
8
  import { GroupOutput, GroupingStrategy } from "../group/types.js";
7
9
  import { ComposeSession } from "../session.js";
8
10
  import { AiModelPort } from "@gitkraken/shared-tools";
@@ -24,6 +26,20 @@ interface ComposeGroupInput {
24
26
  baseBranch?: string;
25
27
  issueUrl?: string;
26
28
  pullRequestUrl?: string;
29
+ /**
30
+ * What the author knows that the hunks cannot show — intent, issue text, per-file rationale,
31
+ * ordering constraints. Rendered into the prompt as escaped, tagged data, never as instructions;
32
+ * for guidance the model should act on, use {@link instructions}.
33
+ */
34
+ authoring?: AuthoringContext;
35
+ /**
36
+ * Analysis supplied by the caller. Rendered into the prompt as the authoritative breakdown of the
37
+ * change, so the model reuses it instead of deriving one. Makes {@link includeAnalysis}
38
+ * unnecessary — there is nothing left for the model to produce.
39
+ */
40
+ analysis?: AnalysisResult;
41
+ /** One-line summaries of what individual hunks do, keyed by index. See `ComposeInput.hunkSummaries`. */
42
+ hunkSummaries?: Record<number, string>;
27
43
  instructions?: string;
28
44
  conventions?: ConventionDescriptor;
29
45
  /** "commits" = flat commit list. "branches" = AI decides branches (default). */
@@ -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=s.session?void 0:s.promptOverride?.system??a(s.conventions,l,s.strategy),d,f=n(s.session?i(s,l):o({hunks:c,commitMessages:s.commitMessages,instructions:s.instructions,mode:l,redactHunkContent:s.redactHunkContent,inputTokenBudget:s.inputTokenBudget,systemPromptChars:u?.length,onBudgetReduction:e=>{d=e}}),s.promptOverride),p=c?.length?new Set(c.map(e=>e.index)):new Set(s.session?.completedSteps.analyze?.hunkAnnotations.map(e=>e.hunkIndex)??[]);if(p.size===0)throw new e(`Cannot validate grouping: no hunk indices available from input or session`);let m=await t({model:s.model,systemPrompt:u,userPrompt:f,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,p,l,s.existingBranchNames),taskName:`group`}),h=s.session?.completedSteps??{},g={...m.session,completedSteps:{...h,group:m.result}};return{result:m.result,session:g,usage:m.usage,attempts:m.attempts,durationMs:m.durationMs,callIds:m.callIds,hunksCondensedCount:d,repairs:m.repairs}}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=s.session?void 0:s.promptOverride?.system??a(s.conventions,l,s.strategy),d,f=n(s.session?i(s,l):o({hunks:c,commitMessages:s.commitMessages,instructions:s.instructions,authoring:s.authoring,analysis:s.analysis,hunkSummaries:s.hunkSummaries,condensed:s.condensed,mode:l,redactHunkContent:s.redactHunkContent,inputTokenBudget:s.inputTokenBudget,systemPromptChars:u?.length,onBudgetReduction:e=>{d=e}}),s.promptOverride),p=c?.length?new Set(c.map(e=>e.index)):new Set(s.session?.completedSteps.analyze?.hunkAnnotations.map(e=>e.hunkIndex)??[]);if(p.size===0)throw new e(`Cannot validate grouping: no hunk indices available from input or session`);let m=await t({model:s.model,systemPrompt:u,userPrompt:f,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,p,l,s.existingBranchNames),taskName:`group`}),h=s.session?.completedSteps??{},g={...m.session,completedSteps:{...h,group:m.result}};return{result:m.result,session:g,usage:m.usage,attempts:m.attempts,durationMs:m.durationMs,callIds:m.callIds,hunksCondensedCount:d,repairs:m.repairs}}export{s as group};
@@ -1,6 +1,8 @@
1
+ import { AuthoringContext } from "../../context.js";
1
2
  import { ComposeHunk } from "../types.js";
2
3
  import { ConventionDescriptor } from "../../git/utils/conventions.js";
3
4
  import { RedactHunkContent } from "../utils/redact.js";
5
+ import { AnalysisResult } from "../analyze/types.js";
4
6
  import { GroupInput, GroupingStrategy } from "./types.js";
5
7
 
6
8
  //#region src/tasks/group/prompt.d.ts
@@ -8,7 +10,11 @@ declare function buildGroupSystemPrompt(conventions?: ConventionDescriptor, mode
8
10
  declare function buildGroupUserPrompt(params: {
9
11
  hunks: ComposeHunk[];
10
12
  commitMessages?: string[];
11
- instructions?: string;
13
+ instructions?: string; /** What the author knows that the hunks cannot show. Rendered as escaped, tagged data. */
14
+ authoring?: AuthoringContext; /** Caller-supplied analysis, used in place of one derived by a prior `analyze` call. */
15
+ analysis?: AnalysisResult; /** One-line summaries of what individual hunks do, keyed by index. See `ComposeInput.hunkSummaries`. */
16
+ hunkSummaries?: Record<number, string>; /** Send hunk metadata only, omitting every patch body. See `ComposeInput.condenseHunks`. */
17
+ condensed?: boolean;
12
18
  mode?: 'commits' | 'branches';
13
19
  redactHunkContent?: RedactHunkContent; /** Input-token ceiling that triggers progressive hunk reduction. Defaults to the shared budget. */
14
20
  inputTokenBudget?: number; /** Char count of the system prompt, folded into the budget estimate. Defaults to 0. */