@blogic-cz/agent-tools 1.12.0 → 1.13.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/README.md +1 -0
- package/dist/gh-tool/pr/checkbox-markdown.d.ts +22 -0
- package/dist/gh-tool/pr/checkbox-markdown.d.ts.map +1 -0
- package/dist/gh-tool/pr/checkbox.d.ts +21 -0
- package/dist/gh-tool/pr/checkbox.d.ts.map +1 -0
- package/dist/gh-tool/pr/commands.d.ts +9 -0
- package/dist/gh-tool/pr/commands.d.ts.map +1 -1
- package/dist/gh-tool/pr/index.d.ts +1 -1
- package/dist/gh-tool/pr/index.d.ts.map +1 -1
- package/dist/gh-tool/read/pulls.d.ts +1 -0
- package/dist/gh-tool/read/pulls.d.ts.map +1 -1
- package/dist/gh-tool/read/reads.d.ts +1 -0
- package/dist/gh-tool/read/reads.d.ts.map +1 -1
- package/dist/gh-tool/read/transport.d.ts +9 -0
- package/dist/gh-tool/read/transport.d.ts.map +1 -1
- package/dist/gh-tool/read/writes.d.ts +2 -1
- package/dist/gh-tool/read/writes.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/gh-tool/main.ts +2 -0
- package/src/gh-tool/pr/checkbox-markdown.ts +317 -0
- package/src/gh-tool/pr/checkbox.ts +113 -0
- package/src/gh-tool/pr/commands.ts +46 -0
- package/src/gh-tool/pr/index.ts +1 -0
- package/src/gh-tool/read/pulls.ts +19 -0
- package/src/gh-tool/read/reads.ts +2 -1
- package/src/gh-tool/read/transport.ts +85 -1
- package/src/gh-tool/read/writes.ts +19 -2
package/README.md
CHANGED
|
@@ -385,6 +385,7 @@ bun gh-tool pr rerun-checks --pr 123 --failed-only --watch --timeout 600
|
|
|
385
385
|
bun gh-tool pr trigger-checks --pr 123 --workflow dotnet-pull-request.yml # only when zero checks reported
|
|
386
386
|
bun gh-tool pr watch --prs 123,124 --format jsonl --timeout 600
|
|
387
387
|
bun gh-tool pr edit-comment --comment-id 456 --body-file correction.md # Edit your own review comment
|
|
388
|
+
bun gh-tool pr checkbox --pr 123 --match "Add regression coverage" --check # Toggle one PR task item
|
|
388
389
|
bun gh-tool pr edit-comment --comment-id 789 --kind issue --pr 123 --body "Corrected detail"
|
|
389
390
|
bun gh-tool pr reply-and-resolve --comment-id 456 --body "Done" # infers PR and thread
|
|
390
391
|
bun gh-tool pr request-review --repo be --pr 123 --reviewers alice,bob
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type CheckboxOutlineEntry = {
|
|
2
|
+
readonly type: "task";
|
|
3
|
+
readonly text: string;
|
|
4
|
+
readonly checked: boolean;
|
|
5
|
+
} | {
|
|
6
|
+
readonly type: "heading";
|
|
7
|
+
readonly text: string;
|
|
8
|
+
readonly level: number;
|
|
9
|
+
};
|
|
10
|
+
export type CheckboxOutlineParser = (body: string) => readonly CheckboxOutlineEntry[] | null;
|
|
11
|
+
type CheckboxLine = {
|
|
12
|
+
readonly before: string;
|
|
13
|
+
readonly offset: number;
|
|
14
|
+
readonly boxIndex: number;
|
|
15
|
+
readonly checked: boolean;
|
|
16
|
+
};
|
|
17
|
+
export declare const bunMarkdownOutline: CheckboxOutlineParser;
|
|
18
|
+
export declare const validateCheckboxEdit: (body: string, offset: number, replacement: " " | "x", parseOutline?: CheckboxOutlineParser) => void;
|
|
19
|
+
export declare const findCheckboxLines: (body: string, match: string, section: string | null, parseOutline?: CheckboxOutlineParser) => CheckboxLine[];
|
|
20
|
+
export declare const scanCheckboxOutline: (body: string) => readonly CheckboxOutlineEntry[];
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=checkbox-markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkbox-markdown.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/checkbox-markdown.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC3E;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhF,MAAM,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,oBAAoB,EAAE,GAAG,IAAI,CAAC;AAE7F,KAAK,YAAY,GAAG;IAClB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B,CAAC;AAmJF,eAAO,MAAM,kBAAkB,EAAE,qBA6ChC,CAAC;AAwBF,eAAO,MAAM,oBAAoB,SACzB,MAAM,UACJ,MAAM,eACD,GAAG,GAAG,GAAG,iBACR,qBAAqB,SAqCpC,CAAC;AAEF,eAAO,MAAM,iBAAiB,SACtB,MAAM,SACL,MAAM,WACJ,MAAM,GAAG,IAAI,iBACR,qBAAqB,mBAmCpC,CAAC;AAEF,eAAO,MAAM,mBAAmB,SAAU,MAAM,KAAG,SAAS,oBAAoB,EAK7E,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { type CheckboxOutlineParser } from "#gh/pr/checkbox-markdown";
|
|
3
|
+
type CheckboxState = "checked" | "unchecked";
|
|
4
|
+
export declare const togglePRCheckbox: (opts: {
|
|
5
|
+
pr: number;
|
|
6
|
+
match: string;
|
|
7
|
+
section: string | null;
|
|
8
|
+
state: CheckboxState;
|
|
9
|
+
}, parseOutline?: CheckboxOutlineParser | undefined) => Effect.Effect<{
|
|
10
|
+
pr: number;
|
|
11
|
+
state: "unchanged";
|
|
12
|
+
before: string;
|
|
13
|
+
after: string;
|
|
14
|
+
} | {
|
|
15
|
+
pr: number;
|
|
16
|
+
state: "updated";
|
|
17
|
+
before: string;
|
|
18
|
+
after: string;
|
|
19
|
+
}, import("../read/transport").GhError, import("../read/transport").GitHubService>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=checkbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/checkbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,EAIL,KAAK,qBAAqB,EAC3B,MAAM,0BAA0B,CAAC;AAMlC,KAAK,aAAa,GAAG,SAAS,GAAG,WAAW,CAAC;AAiB7C,eAAO,MAAM,gBAAgB;QAErB,MAAM;WACH,MAAM;aACJ,MAAM,GAAG,IAAI;WACf,aAAa;;;WAuCX,WAAW;;;;;WAiCb,SAAS;;;kFAIlB,CAAC"}
|
|
@@ -217,6 +217,15 @@ export declare const prEditCommand: Command.Command<"edit", {
|
|
|
217
217
|
readonly repo: Option.Option<string>;
|
|
218
218
|
readonly title: Option.Option<string>;
|
|
219
219
|
}, {}, import("#gh/read/transport").GhError, GitHubService>;
|
|
220
|
+
export declare const prCheckboxCommand: Command.Command<"checkbox", {
|
|
221
|
+
readonly check: boolean;
|
|
222
|
+
readonly format: "json" | "toon";
|
|
223
|
+
readonly match: string;
|
|
224
|
+
readonly pr: number;
|
|
225
|
+
readonly repo: Option.Option<string>;
|
|
226
|
+
readonly section: Option.Option<string>;
|
|
227
|
+
readonly uncheck: boolean;
|
|
228
|
+
}, {}, import("#gh/read/transport").GhError, GitHubService>;
|
|
220
229
|
export declare const prCloseCommand: Command.Command<"close", {
|
|
221
230
|
readonly comment: Option.Option<string>;
|
|
222
231
|
readonly commentFile: Option.Option<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/commands.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAe,MAAM,YAAY,CAAC;AAClD,OAAO,EAAW,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEjD,OAAO,KAAK,EAAE,WAAW,EAAkB,MAAM,WAAW,CAAC;AAG7D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { prChecksCommand, prChecksFailedCommand, prCloseCommand, prCommentCommand, prEditCommentCommand, prCommentsCommand, prCreateCommand, prDiscussionSummaryCommand, prDraftCommand, prEditCommand, prFeedbackCommand, prIssueCommentsCommand, prIssueCommentsLatestCommand, prLastHumanReviewerCommand, prListCommand, prMergeCommand, prReadyCommand, prStackCommand, prRequestReviewCommand, prReplyCommand, prRerunChecksCommand, prReplyAndResolveCommand, prResolveCommand, prReviewCommand, prReviewsCommand, prStatusCommand, prSubmitReviewCommand, prThreadsCommand, prTriggerChecksCommand, prWaitMergeableCommand, prReviewTriageCommand, prReviewTriageBatchCommand, prViewCommand, prWatchCommand, } from "./commands";
|
|
1
|
+
export { prChecksCommand, prChecksFailedCommand, prCloseCommand, prCommentCommand, prEditCommentCommand, prCommentsCommand, prCreateCommand, prDiscussionSummaryCommand, prDraftCommand, prEditCommand, prCheckboxCommand, prFeedbackCommand, prIssueCommentsCommand, prIssueCommentsLatestCommand, prLastHumanReviewerCommand, prListCommand, prMergeCommand, prReadyCommand, prStackCommand, prRequestReviewCommand, prReplyCommand, prRerunChecksCommand, prReplyAndResolveCommand, prResolveCommand, prReviewCommand, prReviewsCommand, prStatusCommand, prSubmitReviewCommand, prThreadsCommand, prTriggerChecksCommand, prWaitMergeableCommand, prReviewTriageCommand, prReviewTriageBatchCommand, prViewCommand, prWatchCommand, } from "./commands";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,EACf,0BAA0B,EAC1B,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,4BAA4B,EAC5B,0BAA0B,EAC1B,aAAa,EACb,cAAc,EACd,cAAc,EACd,cAAc,EACd,sBAAsB,EACtB,cAAc,EACd,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,0BAA0B,EAC1B,aAAa,EACb,cAAc,GACf,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,EACf,0BAA0B,EAC1B,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,4BAA4B,EAC5B,0BAA0B,EAC1B,aAAa,EACb,cAAc,EACd,cAAc,EACd,cAAc,EACd,sBAAsB,EACtB,cAAc,EACd,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,0BAA0B,EAC1B,aAAa,EACb,cAAc,GACf,MAAM,YAAY,CAAC"}
|
|
@@ -78,6 +78,7 @@ export declare const prHead: (repo: RepoRef, pr: number, consistency?: "cached"
|
|
|
78
78
|
baseSha: string;
|
|
79
79
|
signature: string;
|
|
80
80
|
}, import("#gh/read/transport").GhError, GitHubService>;
|
|
81
|
+
export declare const prBody: (repo: RepoRef, pr: number) => Effect.Effect<string, import("#gh/read/transport").GhError, GitHubService>;
|
|
81
82
|
export declare const checkSignal: (repo: RepoRef, sha: string) => Effect.Effect<string, import("#gh/read/transport").GhError, GitHubService>;
|
|
82
83
|
export declare const prView: (repo: RepoRef | null, selector: PrSelector, consistency?: "cached" | "origin" | undefined) => Effect.Effect<{
|
|
83
84
|
readonly number: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pulls.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/read/pulls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAO7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGtC,eAAO,MAAM,cAAc,oKACwI,CAAC;AACpK,eAAO,MAAM,cAAc,yHAC6F,CAAC;AACzH,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AACF,MAAM,MAAM,UAAU,GAAG;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,eAAe,CAAA;CAAE,CAAC;AACpF,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;EAeR,CAAC;AAEH,QAAA,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqBZ,CAAC;AACH,MAAM,MAAM,MAAM,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;AAC1C,MAAM,MAAM,UAAU,GAAG,OAAO,IAAI,CAAC,IAAI,CAAC;AAQ1C,eAAO,MAAM,MAAM;;;;uDA2BjB,CAAC;AAEH,eAAO,MAAM,WAAW,4GAkBtB,CAAC;AAEH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uDAsCjB,CAAC;AAEH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;yDAmCjB,CAAC;AAEH,eAAO,MAAM,QAAQ,iJA2DnB,CAAC"}
|
|
1
|
+
{"version":3,"file":"pulls.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/read/pulls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAO7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGtC,eAAO,MAAM,cAAc,oKACwI,CAAC;AACpK,eAAO,MAAM,cAAc,yHAC6F,CAAC;AACzH,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AACF,MAAM,MAAM,UAAU,GAAG;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,eAAe,CAAA;CAAE,CAAC;AACpF,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;EAeR,CAAC;AAEH,QAAA,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqBZ,CAAC;AACH,MAAM,MAAM,MAAM,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;AAC1C,MAAM,MAAM,UAAU,GAAG,OAAO,IAAI,CAAC,IAAI,CAAC;AAQ1C,eAAO,MAAM,MAAM;;;;uDA2BjB,CAAC;AAIH,eAAO,MAAM,MAAM,2GAejB,CAAC;AAEH,eAAO,MAAM,WAAW,4GAkBtB,CAAC;AAEH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uDAsCjB,CAAC;AAEH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;yDAmCjB,CAAC;AAEH,eAAO,MAAM,QAAQ,iJA2DnB,CAAC"}
|
|
@@ -133,6 +133,7 @@ export declare const reads: {
|
|
|
133
133
|
baseSha: string;
|
|
134
134
|
signature: string;
|
|
135
135
|
}, import("#gh/read/transport").GhError, GitHubService>;
|
|
136
|
+
prBody: (repo: RepoRef, pr: number) => Effect.Effect<string, import("#gh/read/transport").GhError, GitHubService>;
|
|
136
137
|
checkSignal: (repo: RepoRef, sha: string) => Effect.Effect<string, import("#gh/read/transport").GhError, GitHubService>;
|
|
137
138
|
runList: (repo: RepoRef, filter: RunFilter, consistency?: "cached" | "origin" | undefined) => Effect.Effect<readonly {
|
|
138
139
|
readonly databaseId: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reads.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/read/reads.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAkB,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAShD,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAUjD,MAAM,MAAM,MAAM,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AActE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;AAC3E,MAAM,MAAM,iBAAiB,GACzB,QAAQ,CAAC;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,CAAC,GACjE,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEhC,eAAO,MAAM,YAAY;;uDAwCvB,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;wBAY/B,CAAC;AA+CH,eAAO,MAAM,WAAW,sGAatB,CAAC;AAgHH,eAAO,MAAM,SAAS,6HAWpB,CAAC;AA+EH,eAAO,MAAM,KAAK
|
|
1
|
+
{"version":3,"file":"reads.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/read/reads.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAkB,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAShD,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAUjD,MAAM,MAAM,MAAM,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AActE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;AAC3E,MAAM,MAAM,iBAAiB,GACzB,QAAQ,CAAC;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,CAAC,GACjE,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEhC,eAAO,MAAM,YAAY;;uDAwCvB,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;wBAY/B,CAAC;AA+CH,eAAO,MAAM,WAAW,sGAatB,CAAC;AAgHH,eAAO,MAAM,SAAS,6HAWpB,CAAC;AA+EH,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyBjB,CAAC"}
|
|
@@ -12,8 +12,17 @@ export type GhResult = {
|
|
|
12
12
|
exitCode: number;
|
|
13
13
|
};
|
|
14
14
|
export type GhError = GitHubCommandError | GitHubAuthError | GitHubNotFoundError;
|
|
15
|
+
export type PRCheckboxWrite = {
|
|
16
|
+
readonly repo: RepoRef;
|
|
17
|
+
readonly pr: number;
|
|
18
|
+
readonly expectedBody: string;
|
|
19
|
+
readonly boxOffset: number;
|
|
20
|
+
readonly replacement: " " | "x";
|
|
21
|
+
readonly selectionText: readonly string[];
|
|
22
|
+
};
|
|
15
23
|
declare const GitHubService_base: Context.ServiceClass<GitHubService, "@agent-tools/GitHubService", {
|
|
16
24
|
readonly runGh: (args: string[]) => Effect.Effect<GhResult, GhError>;
|
|
25
|
+
readonly writePRCheckbox?: (opts: PRCheckboxWrite) => Effect.Effect<GhResult, GhError>;
|
|
17
26
|
readonly runGhJson: <T>(args: string[]) => Effect.Effect<T, GhError>;
|
|
18
27
|
readonly runGraphQL: (query: string, variables: Record<string, string | number | null>) => Effect.Effect<unknown, GhError>;
|
|
19
28
|
readonly apiRequest: <T>(opts: GitHubApiRequest) => Effect.Effect<GitHubApiResponse<T>, GhError>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/read/transport.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/read/transport.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAkB,MAAM,QAAQ,CAAC;AAEhE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAStC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,aAAa,EAA4C,MAAM,SAAS,CAAC;AA6DlF,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,kBAAkB,GAAG,eAAe,GAAG,mBAAmB,CAAC;AACjF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,GAAG,GAAG,GAAG,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C,CAAC;;oBAKkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;+BACzC,CAAC,IAAI,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;wBAClE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC;yBAC/C,CACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,KAC9C,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;yBACf,CAAC,CAAC,EACrB,IAAI,EAAE,gBAAgB,KACnB,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;4BACzB,MAAM,MAAM,CAAC,MAAM,CAAC,gBAAgB,GAAG,SAAS,EAAE,KAAK,CAAC;0BAC1D,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;yBACvC,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;6BACjC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,kBAAkB,EAAE,CAAC,CAAC;;AAnBpD,qBAAa,aAAc,SAAQ,kBAqBF;IAC/B,MAAM,CAAC,QAAQ,CAAC,KAAK,6FAkZnB;CACH"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { GitHubService } from "#gh/read/transport";
|
|
3
3
|
import type { Tag } from "./keys";
|
|
4
|
-
import type { GhError } from "#gh/read/transport";
|
|
4
|
+
import type { GhError, PRCheckboxWrite } from "#gh/read/transport";
|
|
5
5
|
import type { GitHubApiRequest } from "#gh/read/api";
|
|
6
6
|
declare const json: <A>(argv: string[], touched: readonly Tag[]) => Effect.Effect<A, GhError, GitHubService>;
|
|
7
7
|
declare const graphql: (query: string, variables: Record<string, string | number | null>, touched: readonly Tag[]) => Effect.Effect<unknown, GhError, GitHubService>;
|
|
8
8
|
declare const api: <A>(request: GitHubApiRequest, touched: readonly Tag[]) => Effect.Effect<import("#gh/read/api").GitHubApiResponse<A>, GhError, GitHubService>;
|
|
9
9
|
export declare const writes: {
|
|
10
10
|
run: (argv: string[], touched: readonly (`checks:${string}` | `issue-comments:${string}` | `issue:${string}` | `issues:${string}` | `pr:${string}` | `pulls:${string}` | `repo:${string}` | `review-comments:${string}` | `reviews:${string}` | `run:${string}` | `runs:${string}` | `threads:${string}`)[]) => Effect.Effect<import("#gh/read/transport").GhResult, GhError, GitHubService>;
|
|
11
|
+
prCheckbox: (opts: PRCheckboxWrite, touched: readonly (`checks:${string}` | `issue-comments:${string}` | `issue:${string}` | `issues:${string}` | `pr:${string}` | `pulls:${string}` | `repo:${string}` | `review-comments:${string}` | `reviews:${string}` | `run:${string}` | `runs:${string}` | `threads:${string}`)[]) => Effect.Effect<import("#gh/read/transport").GhResult, GhError, GitHubService>;
|
|
11
12
|
json: typeof json;
|
|
12
13
|
graphql: typeof graphql;
|
|
13
14
|
api: typeof api;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writes.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/read/writes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"writes.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/read/writes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AA4BrD,QAAA,MAAM,IAAI,GAAI,CAAC,QAAQ,MAAM,EAAE,WAAW,SAAS,GAAG,EAAE,6CAIpD,CAAC;AACL,QAAA,MAAM,OAAO,UACJ,MAAM,aACF,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,WACxC,SAAS,GAAG,EAAE,mDAKrB,CAAC;AACL,QAAA,MAAM,GAAG,GAAI,CAAC,WAAW,gBAAgB,WAAW,SAAS,GAAG,EAAE,uFAI9D,CAAC;AACL,eAAO,MAAM,MAAM;;;;;;CAA0C,CAAC"}
|
package/package.json
CHANGED
package/src/gh-tool/main.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
prCreateCommand,
|
|
28
28
|
prCloseCommand,
|
|
29
29
|
prEditCommand,
|
|
30
|
+
prCheckboxCommand,
|
|
30
31
|
prMergeCommand,
|
|
31
32
|
prReadyCommand,
|
|
32
33
|
prDraftCommand,
|
|
@@ -106,6 +107,7 @@ const prCommand = Command.make("pr", {}).pipe(
|
|
|
106
107
|
prCreateCommand,
|
|
107
108
|
prCloseCommand,
|
|
108
109
|
prEditCommand,
|
|
110
|
+
prCheckboxCommand,
|
|
109
111
|
prMergeCommand,
|
|
110
112
|
prReadyCommand,
|
|
111
113
|
prDraftCommand,
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
export type CheckboxOutlineEntry =
|
|
2
|
+
| { readonly type: "task"; readonly text: string; readonly checked: boolean }
|
|
3
|
+
| { readonly type: "heading"; readonly text: string; readonly level: number };
|
|
4
|
+
|
|
5
|
+
export type CheckboxOutlineParser = (body: string) => readonly CheckboxOutlineEntry[] | null;
|
|
6
|
+
|
|
7
|
+
type CheckboxLine = {
|
|
8
|
+
readonly before: string;
|
|
9
|
+
readonly offset: number;
|
|
10
|
+
readonly boxIndex: number;
|
|
11
|
+
readonly checked: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type ScannedEntry = CheckboxOutlineEntry & {
|
|
15
|
+
readonly offset?: number;
|
|
16
|
+
readonly boxIndex?: number;
|
|
17
|
+
readonly before?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type Fence = {
|
|
21
|
+
readonly marker: "`" | "~";
|
|
22
|
+
readonly length: number;
|
|
23
|
+
readonly listIndent: number | null;
|
|
24
|
+
};
|
|
25
|
+
type ListItemIndent = { readonly indent: number; readonly contentIndent: number };
|
|
26
|
+
|
|
27
|
+
const taskSuffix = /^[ \t]*\[([ xX])\][ \t]*(.*)$/;
|
|
28
|
+
const listMarker = /^( {0,3})(?:[-*+]|[0-9]+[.)])([ \t]+)/;
|
|
29
|
+
const quoteMarker = /^ {0,3}>[ \t]?/;
|
|
30
|
+
const plainSourceText = (text: string) =>
|
|
31
|
+
text
|
|
32
|
+
.replace(/(\*\*|__|~~|\*|_)(\S(?:.*?\S)?)\1/g, "$2")
|
|
33
|
+
.replace(/&/g, "&")
|
|
34
|
+
.replace(/</g, "<")
|
|
35
|
+
.replace(/>/g, ">")
|
|
36
|
+
.replace(/"/g, '"')
|
|
37
|
+
.replace(/'|'/g, "'")
|
|
38
|
+
.trim();
|
|
39
|
+
|
|
40
|
+
const removeContainers = (line: string) => {
|
|
41
|
+
let cursor = 0;
|
|
42
|
+
for (;;) {
|
|
43
|
+
const rest = line.slice(cursor);
|
|
44
|
+
const quote = quoteMarker.exec(rest);
|
|
45
|
+
if (quote !== null) {
|
|
46
|
+
cursor += quote[0].length;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const list = listMarker.exec(rest);
|
|
50
|
+
if (list !== null) {
|
|
51
|
+
cursor += list[0].length;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
return { text: line.slice(cursor), cursor };
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const openingFence = (line: string, listIndent: number | null): Fence | null => {
|
|
59
|
+
const content = removeContainers(line).text;
|
|
60
|
+
const match = /^ {0,3}(`{3,}|~{3,})([^\r\n]*)\r?$/.exec(content);
|
|
61
|
+
if (match === null) return null;
|
|
62
|
+
const marker = match[1]?.[0];
|
|
63
|
+
const info = match[2] ?? "";
|
|
64
|
+
if ((marker !== "`" && marker !== "~") || (marker === "`" && info.includes("`"))) return null;
|
|
65
|
+
return { marker, length: match[1]?.length ?? 0, listIndent };
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const closesFence = (line: string, fence: Fence) => {
|
|
69
|
+
const content = removeContainers(line).text;
|
|
70
|
+
const match = /^ {0,3}(`+|~+)[ \t]*\r?$/.exec(content);
|
|
71
|
+
const markers = match?.[1] ?? "";
|
|
72
|
+
return markers.length >= fence.length && markers[0] === fence.marker;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const headingFromLine = (line: string) => {
|
|
76
|
+
const { text } = removeContainers(line);
|
|
77
|
+
const match = /^ {0,3}(#{1,6})[ \t]+(.*?)\r?$/.exec(text);
|
|
78
|
+
if (match === null) return null;
|
|
79
|
+
return {
|
|
80
|
+
level: match[1]?.length ?? 0,
|
|
81
|
+
text: plainSourceText((match[2] ?? "").replace(/[ \t]+#+[ \t]*$/, "")),
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const scanOutline = (body: string): ScannedEntry[] => {
|
|
86
|
+
const entries: ScannedEntry[] = [];
|
|
87
|
+
const lines = body.match(/[^\n]*(?:\n|$)/g) ?? [];
|
|
88
|
+
let offset = 0;
|
|
89
|
+
let fence: Fence | null = null;
|
|
90
|
+
const listItems: ListItemIndent[] = [];
|
|
91
|
+
|
|
92
|
+
for (const raw of lines) {
|
|
93
|
+
if (raw.length === 0) continue;
|
|
94
|
+
const line = (raw.endsWith("\n") ? raw.slice(0, -1) : raw).replace(/\r$/, "");
|
|
95
|
+
const lineStart = offset;
|
|
96
|
+
offset += raw.length;
|
|
97
|
+
|
|
98
|
+
const marker = listMarker.exec(line);
|
|
99
|
+
if (marker !== null) {
|
|
100
|
+
const indent = marker[1]?.length ?? 0;
|
|
101
|
+
while ((listItems.at(-1)?.indent ?? -1) >= indent) {
|
|
102
|
+
listItems.pop();
|
|
103
|
+
}
|
|
104
|
+
listItems.push({ indent, contentIndent: indent + marker[0].length });
|
|
105
|
+
} else if (line.trim() !== "") {
|
|
106
|
+
const leading = /^ */.exec(line)?.[0].length ?? 0;
|
|
107
|
+
while (listItems.length > 0 && leading < (listItems.at(-1)?.contentIndent ?? 0)) {
|
|
108
|
+
listItems.pop();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (fence !== null) {
|
|
113
|
+
if (fence.listIndent !== null && line.trim() !== "") {
|
|
114
|
+
const leading = /^ */.exec(line)?.[0].length ?? 0;
|
|
115
|
+
if (leading < fence.listIndent) fence = null;
|
|
116
|
+
}
|
|
117
|
+
if (fence !== null) {
|
|
118
|
+
if (closesFence(line, fence)) fence = null;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const context = listItems.at(-1);
|
|
124
|
+
const opening = openingFence(line, context?.contentIndent ?? null);
|
|
125
|
+
if (opening !== null) {
|
|
126
|
+
fence = opening;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const heading = headingFromLine(line);
|
|
131
|
+
if (heading !== null) {
|
|
132
|
+
entries.push({ type: "heading", ...heading });
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const { text, cursor } = removeContainers(line);
|
|
137
|
+
const task = taskSuffix.exec(text);
|
|
138
|
+
if (task === null) continue;
|
|
139
|
+
const checked = task[1] !== " ";
|
|
140
|
+
const boxIndex = cursor + (text.indexOf("[") + 1);
|
|
141
|
+
const content = plainSourceText(task[2] ?? "");
|
|
142
|
+
entries.push({
|
|
143
|
+
type: "task",
|
|
144
|
+
text: content,
|
|
145
|
+
checked,
|
|
146
|
+
offset: lineStart + boxIndex,
|
|
147
|
+
boxIndex,
|
|
148
|
+
before: line.replace(/\r$/, ""),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return entries;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const tokenPrefix = "\uE000checkbox:";
|
|
155
|
+
const tokenSuffix = ":\uE001";
|
|
156
|
+
const makeToken = (id: number) => `${tokenPrefix}${id}${tokenSuffix}`;
|
|
157
|
+
const tokenPattern = /\uE000checkbox:(\d+):\uE001/g;
|
|
158
|
+
|
|
159
|
+
export const bunMarkdownOutline: CheckboxOutlineParser = (body) => {
|
|
160
|
+
const markdown = (globalThis as typeof globalThis & { Bun?: typeof Bun }).Bun?.markdown;
|
|
161
|
+
if (markdown?.render === undefined) return null;
|
|
162
|
+
|
|
163
|
+
const nodes: Array<{ readonly entry: CheckboxOutlineEntry | null; readonly children: string }> =
|
|
164
|
+
[];
|
|
165
|
+
const token = (entry: CheckboxOutlineEntry | null, children: string) => {
|
|
166
|
+
const id = nodes.push({ entry, children }) - 1;
|
|
167
|
+
return makeToken(id);
|
|
168
|
+
};
|
|
169
|
+
const plain = (value: string) => value.replace(tokenPattern, "").trim();
|
|
170
|
+
|
|
171
|
+
const rendered = markdown.render(body, {
|
|
172
|
+
text: (children) => children,
|
|
173
|
+
strong: (children) => children,
|
|
174
|
+
emphasis: (children) => children,
|
|
175
|
+
strikethrough: (children) => children,
|
|
176
|
+
link: (children) => children,
|
|
177
|
+
image: (children) => children,
|
|
178
|
+
codespan: (children) => children,
|
|
179
|
+
heading: (children, meta) =>
|
|
180
|
+
token({ type: "heading", level: meta.level, text: plain(children) }, children),
|
|
181
|
+
listItem: (children, meta) =>
|
|
182
|
+
token(
|
|
183
|
+
typeof meta.checked === "boolean"
|
|
184
|
+
? { type: "task", checked: meta.checked, text: plain(children) }
|
|
185
|
+
: null,
|
|
186
|
+
children,
|
|
187
|
+
),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const outline: CheckboxOutlineEntry[] = [];
|
|
191
|
+
const expand = (value: string) => {
|
|
192
|
+
for (const match of value.matchAll(tokenPattern)) {
|
|
193
|
+
const idText = match[1];
|
|
194
|
+
const id = idText === undefined ? -1 : Number(idText);
|
|
195
|
+
const node = nodes[id];
|
|
196
|
+
if (node === undefined || match.index === undefined)
|
|
197
|
+
throw new Error("Invalid Bun Markdown callback outline");
|
|
198
|
+
if (node.entry !== null) outline.push(node.entry);
|
|
199
|
+
expand(node.children);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
expand(rendered);
|
|
203
|
+
return outline;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const sameOutline = (scanned: readonly ScannedEntry[], parsed: readonly CheckboxOutlineEntry[]) => {
|
|
207
|
+
return (
|
|
208
|
+
scanned.length === parsed.length &&
|
|
209
|
+
scanned.every((entry, index) => {
|
|
210
|
+
const actual = parsed[index];
|
|
211
|
+
return (
|
|
212
|
+
actual !== undefined &&
|
|
213
|
+
entry.type === actual.type &&
|
|
214
|
+
entry.text === actual.text &&
|
|
215
|
+
(entry.type !== "heading" || (actual.type === "heading" && entry.level === actual.level)) &&
|
|
216
|
+
(entry.type !== "task" || (actual.type === "task" && entry.checked === actual.checked))
|
|
217
|
+
);
|
|
218
|
+
})
|
|
219
|
+
);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const sameEntry = (left: CheckboxOutlineEntry, right: CheckboxOutlineEntry) =>
|
|
223
|
+
left.type === right.type &&
|
|
224
|
+
left.text === right.text &&
|
|
225
|
+
(left.type !== "heading" || (right.type === "heading" && left.level === right.level)) &&
|
|
226
|
+
(left.type !== "task" || (right.type === "task" && left.checked === right.checked));
|
|
227
|
+
|
|
228
|
+
export const validateCheckboxEdit = (
|
|
229
|
+
body: string,
|
|
230
|
+
offset: number,
|
|
231
|
+
replacement: " " | "x",
|
|
232
|
+
parseOutline: CheckboxOutlineParser = bunMarkdownOutline,
|
|
233
|
+
) => {
|
|
234
|
+
const scanned = scanOutline(body);
|
|
235
|
+
const original = parseOutline(body);
|
|
236
|
+
const selectedIndex = scanned.findIndex(
|
|
237
|
+
(entry) => entry.type === "task" && entry.offset === offset,
|
|
238
|
+
);
|
|
239
|
+
if (
|
|
240
|
+
original === null ||
|
|
241
|
+
!sameOutline(scanned, original) ||
|
|
242
|
+
selectedIndex < 0 ||
|
|
243
|
+
original[selectedIndex]?.type !== "task" ||
|
|
244
|
+
body[offset] === undefined ||
|
|
245
|
+
(replacement !== " " && replacement !== "x")
|
|
246
|
+
) {
|
|
247
|
+
throw new Error("PR body structure is ambiguous; tick this item in the GitHub UI");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const editedBody = `${body.slice(0, offset)}${replacement}${body.slice(offset + 1)}`;
|
|
251
|
+
const edited = parseOutline(editedBody);
|
|
252
|
+
if (edited === null || edited.length !== original.length) {
|
|
253
|
+
throw new Error("PR body structure is ambiguous; tick this item in the GitHub UI");
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const expected = [...original];
|
|
257
|
+
const selected = expected[selectedIndex];
|
|
258
|
+
if (selected?.type === "task") {
|
|
259
|
+
expected[selectedIndex] = { ...selected, checked: replacement !== " " };
|
|
260
|
+
}
|
|
261
|
+
if (
|
|
262
|
+
!edited.every((entry, index) => {
|
|
263
|
+
const expectedEntry = expected[index];
|
|
264
|
+
return expectedEntry !== undefined && sameEntry(expectedEntry, entry);
|
|
265
|
+
})
|
|
266
|
+
) {
|
|
267
|
+
throw new Error("PR body structure is ambiguous; tick this item in the GitHub UI");
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
export const findCheckboxLines = (
|
|
272
|
+
body: string,
|
|
273
|
+
match: string,
|
|
274
|
+
section: string | null,
|
|
275
|
+
parseOutline: CheckboxOutlineParser = bunMarkdownOutline,
|
|
276
|
+
) => {
|
|
277
|
+
const scanned = scanOutline(body);
|
|
278
|
+
const parsed = parseOutline(body);
|
|
279
|
+
if (parsed === null || !sameOutline(scanned, parsed)) {
|
|
280
|
+
throw new Error("PR body structure is ambiguous; tick this item in the GitHub UI");
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const matches: CheckboxLine[] = [];
|
|
284
|
+
let sectionLevel: number | null = null;
|
|
285
|
+
let inSection = section === null;
|
|
286
|
+
|
|
287
|
+
for (const entry of scanned) {
|
|
288
|
+
if (entry.type === "heading") {
|
|
289
|
+
if (sectionLevel !== null && entry.level <= sectionLevel) {
|
|
290
|
+
inSection = false;
|
|
291
|
+
sectionLevel = null;
|
|
292
|
+
}
|
|
293
|
+
if (section !== null && entry.text === section) {
|
|
294
|
+
inSection = true;
|
|
295
|
+
sectionLevel = entry.level;
|
|
296
|
+
}
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (!inSection || !entry.text.includes(match)) continue;
|
|
300
|
+
if (entry.offset === undefined || entry.boxIndex === undefined || entry.before === undefined)
|
|
301
|
+
continue;
|
|
302
|
+
matches.push({
|
|
303
|
+
before: entry.before,
|
|
304
|
+
offset: entry.offset,
|
|
305
|
+
boxIndex: entry.boxIndex,
|
|
306
|
+
checked: entry.checked,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
return matches;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
export const scanCheckboxOutline = (body: string): readonly CheckboxOutlineEntry[] =>
|
|
313
|
+
scanOutline(body).map((entry) =>
|
|
314
|
+
entry.type === "task"
|
|
315
|
+
? { type: entry.type, text: entry.text, checked: entry.checked }
|
|
316
|
+
: { type: entry.type, text: entry.text, level: entry.level },
|
|
317
|
+
);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
import { GitHubCommandError } from "#gh/errors";
|
|
4
|
+
import {
|
|
5
|
+
bunMarkdownOutline,
|
|
6
|
+
findCheckboxLines,
|
|
7
|
+
validateCheckboxEdit,
|
|
8
|
+
type CheckboxOutlineParser,
|
|
9
|
+
} from "#gh/pr/checkbox-markdown";
|
|
10
|
+
import { repoSlug } from "#gh/read/keys";
|
|
11
|
+
import { reads, resolveRepo } from "#gh/read/reads";
|
|
12
|
+
import { writes } from "#gh/read/writes";
|
|
13
|
+
import { validateOutboundText } from "#gh/text-input";
|
|
14
|
+
|
|
15
|
+
type CheckboxState = "checked" | "unchecked";
|
|
16
|
+
|
|
17
|
+
const readPRBody = Effect.fn("pr.checkbox.readPRBody")(function* (pr: number) {
|
|
18
|
+
const repo = yield* resolveRepo(null);
|
|
19
|
+
const body = yield* reads.prBody(repo, pr);
|
|
20
|
+
return { repo, body };
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const checkboxMatchError = (pr: number, count: number) =>
|
|
24
|
+
new GitHubCommandError({
|
|
25
|
+
command: "gh-tool pr checkbox",
|
|
26
|
+
exitCode: 1,
|
|
27
|
+
stderr: `Found ${count} matching task items in PR #${pr}`,
|
|
28
|
+
message: `Expected one matching task item in PR #${pr}, found ${count}`,
|
|
29
|
+
hint: "Use --section to narrow the search or pass a longer --match.",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const togglePRCheckbox = Effect.fn("pr.togglePRCheckbox")(function* (
|
|
33
|
+
opts: {
|
|
34
|
+
pr: number;
|
|
35
|
+
match: string;
|
|
36
|
+
section: string | null;
|
|
37
|
+
state: CheckboxState;
|
|
38
|
+
},
|
|
39
|
+
parseOutline: CheckboxOutlineParser = bunMarkdownOutline,
|
|
40
|
+
) {
|
|
41
|
+
yield* validateOutboundText(opts.match, "gh-tool pr checkbox");
|
|
42
|
+
if (opts.section !== null) {
|
|
43
|
+
yield* validateOutboundText(opts.section, "gh-tool pr checkbox");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (opts.pr <= 0 || opts.match.length === 0 || opts.section === "") {
|
|
47
|
+
return yield* new GitHubCommandError({
|
|
48
|
+
command: "gh-tool pr checkbox",
|
|
49
|
+
exitCode: 1,
|
|
50
|
+
stderr: "--pr must be positive, --match must be non-empty, and --section must be non-empty",
|
|
51
|
+
message: "Invalid PR checkbox selection",
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const first = yield* readPRBody(opts.pr);
|
|
56
|
+
const matches = yield* Effect.try({
|
|
57
|
+
try: () => findCheckboxLines(first.body, opts.match, opts.section, parseOutline),
|
|
58
|
+
catch: (cause) =>
|
|
59
|
+
new GitHubCommandError({
|
|
60
|
+
command: "gh-tool pr checkbox",
|
|
61
|
+
exitCode: 1,
|
|
62
|
+
stderr: String(cause),
|
|
63
|
+
message: "Cannot locate PR checkbox safely",
|
|
64
|
+
hint: "PR body structure is ambiguous; tick this item in the GitHub UI.",
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
if (matches.length !== 1) return yield* checkboxMatchError(opts.pr, matches.length);
|
|
68
|
+
|
|
69
|
+
const selected = matches[0];
|
|
70
|
+
if (selected === undefined) return yield* checkboxMatchError(opts.pr, 0);
|
|
71
|
+
|
|
72
|
+
const alreadyRequested = opts.state === "checked" ? selected.checked : !selected.checked;
|
|
73
|
+
if (alreadyRequested) {
|
|
74
|
+
return {
|
|
75
|
+
pr: opts.pr,
|
|
76
|
+
state: "unchanged" as const,
|
|
77
|
+
before: selected.before,
|
|
78
|
+
after: selected.before,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const box = opts.state === "checked" ? "x" : " ";
|
|
83
|
+
yield* Effect.try({
|
|
84
|
+
try: () => validateCheckboxEdit(first.body, selected.offset, box, parseOutline),
|
|
85
|
+
catch: (cause) =>
|
|
86
|
+
new GitHubCommandError({
|
|
87
|
+
command: "gh-tool pr checkbox",
|
|
88
|
+
exitCode: 1,
|
|
89
|
+
stderr: String(cause),
|
|
90
|
+
message: "Cannot locate PR checkbox safely",
|
|
91
|
+
hint: "PR body structure is ambiguous; tick this item in the GitHub UI.",
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
const after = `${selected.before.slice(0, selected.boxIndex)}${box}${selected.before.slice(selected.boxIndex + 1)}`;
|
|
95
|
+
yield* writes.prCheckbox(
|
|
96
|
+
{
|
|
97
|
+
repo: first.repo,
|
|
98
|
+
pr: opts.pr,
|
|
99
|
+
expectedBody: first.body,
|
|
100
|
+
boxOffset: selected.offset,
|
|
101
|
+
replacement: box,
|
|
102
|
+
selectionText: [opts.match, ...(opts.section === null ? [] : [opts.section])],
|
|
103
|
+
},
|
|
104
|
+
[`pr:${repoSlug(first.repo)}#${opts.pr}`, `pulls:${repoSlug(first.repo)}`],
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
pr: opts.pr,
|
|
109
|
+
state: "updated" as const,
|
|
110
|
+
before: selected.before,
|
|
111
|
+
after,
|
|
112
|
+
};
|
|
113
|
+
});
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
} from "#gh/config";
|
|
34
34
|
|
|
35
35
|
import { mergeStack, readStack, unstackStack } from "./stack";
|
|
36
|
+
import { togglePRCheckbox } from "./checkbox";
|
|
36
37
|
import {
|
|
37
38
|
closePR,
|
|
38
39
|
collectWithStableState,
|
|
@@ -721,6 +722,51 @@ export const prEditCommand = Command.make(
|
|
|
721
722
|
),
|
|
722
723
|
).pipe(Command.withDescription("Edit an existing PR's title, body, or other metadata"));
|
|
723
724
|
|
|
725
|
+
export const prCheckboxCommand = Command.make(
|
|
726
|
+
"checkbox",
|
|
727
|
+
{
|
|
728
|
+
check: Flag.Boolean("check").pipe(
|
|
729
|
+
Flag.withDescription("Mark the matching task item as checked"),
|
|
730
|
+
Flag.withDefault(false),
|
|
731
|
+
),
|
|
732
|
+
format: formatOption,
|
|
733
|
+
match: Flag.String("match").pipe(
|
|
734
|
+
Flag.withDescription("Plain text that identifies one task item"),
|
|
735
|
+
),
|
|
736
|
+
pr: Flag.Int("pr").pipe(Flag.withDescription("PR number to update")),
|
|
737
|
+
repo: repoOption,
|
|
738
|
+
section: Flag.String("section").pipe(
|
|
739
|
+
Flag.withDescription("Limit the search to a Markdown heading"),
|
|
740
|
+
Flag.optional,
|
|
741
|
+
),
|
|
742
|
+
uncheck: Flag.Boolean("uncheck").pipe(
|
|
743
|
+
Flag.withDescription("Mark the matching task item as unchecked"),
|
|
744
|
+
Flag.withDefault(false),
|
|
745
|
+
),
|
|
746
|
+
},
|
|
747
|
+
({ check, format, match, pr, repo, section, uncheck }) =>
|
|
748
|
+
withRepo(
|
|
749
|
+
repo,
|
|
750
|
+
Effect.gen(function* () {
|
|
751
|
+
if (check === uncheck) {
|
|
752
|
+
return yield* new GitHubCommandError({
|
|
753
|
+
command: "gh-tool pr checkbox",
|
|
754
|
+
exitCode: 1,
|
|
755
|
+
stderr: "Provide exactly one of --check or --uncheck",
|
|
756
|
+
message: "Provide exactly one of --check or --uncheck",
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
const result = yield* togglePRCheckbox({
|
|
760
|
+
pr,
|
|
761
|
+
match,
|
|
762
|
+
section: Option.getOrNull(section),
|
|
763
|
+
state: check ? "checked" : "unchecked",
|
|
764
|
+
});
|
|
765
|
+
yield* logFormatted(result, format);
|
|
766
|
+
}),
|
|
767
|
+
),
|
|
768
|
+
).pipe(Command.withDescription("Check or uncheck one task item in a PR body"));
|
|
769
|
+
|
|
724
770
|
export const prCloseCommand = Command.make(
|
|
725
771
|
"close",
|
|
726
772
|
{
|
package/src/gh-tool/pr/index.ts
CHANGED
|
@@ -101,6 +101,25 @@ export const prHead = Effect.fn("reads.prHead")(function* (
|
|
|
101
101
|
return { headSha: head.head.sha, baseSha: head.base.sha, signature: snapshotDigest(snapshot) };
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
+
const PRBody = Schema.Struct({ body: Schema.NullOr(Schema.String) });
|
|
105
|
+
|
|
106
|
+
export const prBody = Effect.fn("reads.prBody")(function* (repo: RepoRef, pr: number) {
|
|
107
|
+
const gh = yield* GitHubService;
|
|
108
|
+
const path = `repos/${repoSlug(repo)}/pulls/${pr}`;
|
|
109
|
+
const response = yield* gh.apiRequest<unknown>({ path });
|
|
110
|
+
const parsed = yield* Effect.try({
|
|
111
|
+
try: () => Schema.decodeUnknownSync(PRBody)(response.body),
|
|
112
|
+
catch: (error) =>
|
|
113
|
+
new GitHubCommandError({
|
|
114
|
+
command: `GET ${path}`,
|
|
115
|
+
exitCode: 0,
|
|
116
|
+
message: `GitHub returned an invalid PR body: ${String(error)}`,
|
|
117
|
+
stderr: `GitHub returned an invalid PR body: ${String(error)}`,
|
|
118
|
+
}),
|
|
119
|
+
});
|
|
120
|
+
return parsed.body ?? "";
|
|
121
|
+
});
|
|
122
|
+
|
|
104
123
|
export const checkSignal = Effect.fn("reads.checkSignal")(function* (repo: RepoRef, sha: string) {
|
|
105
124
|
const snapshot = yield* restSnapshot({ op: "checkSignal", repo, sha }, [
|
|
106
125
|
{
|
|
@@ -6,7 +6,7 @@ import { cachedRead, ReadCache } from "./cache";
|
|
|
6
6
|
import { OPS } from "./classes";
|
|
7
7
|
import { repoSlug } from "./keys";
|
|
8
8
|
import { issueView, issueList, issueComments } from "./issues";
|
|
9
|
-
import { prView, prList, prChecks, prHead, checkSignal } from "./pulls";
|
|
9
|
+
import { prView, prList, prChecks, prHead, prBody, checkSignal } from "./pulls";
|
|
10
10
|
import { prComments, prReviews, reviewThreads, lastHumanReviewer } from "./reviews";
|
|
11
11
|
import { reviewGuards } from "./review-guards";
|
|
12
12
|
import type { RepoRef, RunFilter } from "./keys";
|
|
@@ -376,6 +376,7 @@ export const reads = {
|
|
|
376
376
|
prList,
|
|
377
377
|
prChecks,
|
|
378
378
|
prHead,
|
|
379
|
+
prBody,
|
|
379
380
|
checkSignal,
|
|
380
381
|
runList,
|
|
381
382
|
runView,
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { ghSubprocessEvent, recordRequest } from "./telemetry";
|
|
2
2
|
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
3
|
-
import { Context, Effect, Layer, Stream } from "effect";
|
|
3
|
+
import { Context, Effect, Layer, Schema, Stream } from "effect";
|
|
4
4
|
|
|
5
5
|
import type { GitHubRepoConfig } from "#config";
|
|
6
6
|
import type { RepoInfo } from "#gh/types";
|
|
7
7
|
import type { RepoRef } from "./keys";
|
|
8
8
|
|
|
9
|
+
import {
|
|
10
|
+
bunMarkdownOutline,
|
|
11
|
+
findCheckboxLines,
|
|
12
|
+
validateCheckboxEdit,
|
|
13
|
+
} from "#gh/pr/checkbox-markdown";
|
|
9
14
|
import { GH_BINARY } from "#gh/config";
|
|
10
15
|
import { ghEnvironment } from "#gh/environment";
|
|
11
16
|
import { GitHubAuthError, GitHubCommandError, GitHubNotFoundError } from "#gh/errors";
|
|
@@ -80,11 +85,20 @@ export type GhResult = {
|
|
|
80
85
|
};
|
|
81
86
|
|
|
82
87
|
export type GhError = GitHubCommandError | GitHubAuthError | GitHubNotFoundError;
|
|
88
|
+
export type PRCheckboxWrite = {
|
|
89
|
+
readonly repo: RepoRef;
|
|
90
|
+
readonly pr: number;
|
|
91
|
+
readonly expectedBody: string;
|
|
92
|
+
readonly boxOffset: number;
|
|
93
|
+
readonly replacement: " " | "x";
|
|
94
|
+
readonly selectionText: readonly string[];
|
|
95
|
+
};
|
|
83
96
|
|
|
84
97
|
export class GitHubService extends Context.Service<
|
|
85
98
|
GitHubService,
|
|
86
99
|
{
|
|
87
100
|
readonly runGh: (args: string[]) => Effect.Effect<GhResult, GhError>;
|
|
101
|
+
readonly writePRCheckbox?: (opts: PRCheckboxWrite) => Effect.Effect<GhResult, GhError>;
|
|
88
102
|
readonly runGhJson: <T>(args: string[]) => Effect.Effect<T, GhError>;
|
|
89
103
|
readonly runGraphQL: (
|
|
90
104
|
query: string,
|
|
@@ -325,6 +339,75 @@ export class GitHubService extends Context.Service<
|
|
|
325
339
|
});
|
|
326
340
|
};
|
|
327
341
|
|
|
342
|
+
const writePRCheckbox = Effect.fn("GitHubService.writePRCheckbox")(function* (
|
|
343
|
+
opts: PRCheckboxWrite,
|
|
344
|
+
) {
|
|
345
|
+
const path = `repos/${opts.repo.owner}/${opts.repo.name}/pulls/${opts.pr}`;
|
|
346
|
+
const response = yield* githubApi<unknown>({ path });
|
|
347
|
+
const parsed = yield* Effect.try({
|
|
348
|
+
try: () =>
|
|
349
|
+
Schema.decodeUnknownSync(Schema.Struct({ body: Schema.NullOr(Schema.String) }))(
|
|
350
|
+
response.body,
|
|
351
|
+
),
|
|
352
|
+
catch: (error) =>
|
|
353
|
+
new GitHubCommandError({
|
|
354
|
+
command: `GET ${path}`,
|
|
355
|
+
exitCode: 0,
|
|
356
|
+
message: `GitHub returned an invalid PR body: ${String(error)}`,
|
|
357
|
+
stderr: `GitHub returned an invalid PR body: ${String(error)}`,
|
|
358
|
+
}),
|
|
359
|
+
});
|
|
360
|
+
const body = parsed.body ?? "";
|
|
361
|
+
if (body !== opts.expectedBody) {
|
|
362
|
+
return yield* new GitHubCommandError({
|
|
363
|
+
command: "gh-tool pr checkbox",
|
|
364
|
+
exitCode: 1,
|
|
365
|
+
stderr: `PR #${opts.pr} body changed before the checkbox update`,
|
|
366
|
+
message: `Refusing to update PR #${opts.pr}: its body changed after the first read`,
|
|
367
|
+
hint: "Re-read the pull request and retry with the current task text.",
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const checkboxes = findCheckboxLines(
|
|
372
|
+
body,
|
|
373
|
+
opts.selectionText[0] ?? "",
|
|
374
|
+
null,
|
|
375
|
+
bunMarkdownOutline,
|
|
376
|
+
);
|
|
377
|
+
if (
|
|
378
|
+
!checkboxes.some((checkbox) => checkbox.offset === opts.boxOffset) ||
|
|
379
|
+
body[opts.boxOffset] === opts.replacement
|
|
380
|
+
) {
|
|
381
|
+
return yield* new GitHubCommandError({
|
|
382
|
+
command: "gh-tool pr checkbox",
|
|
383
|
+
exitCode: 1,
|
|
384
|
+
stderr: "Refusing an invalid PR checkbox position",
|
|
385
|
+
message: "Refusing to update a character that is not a task checkbox",
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
yield* Effect.try({
|
|
390
|
+
try: () => validateCheckboxEdit(body, opts.boxOffset, opts.replacement),
|
|
391
|
+
catch: () =>
|
|
392
|
+
new GitHubCommandError({
|
|
393
|
+
command: "gh-tool pr checkbox",
|
|
394
|
+
exitCode: 1,
|
|
395
|
+
stderr: "PR body structure is ambiguous; tick this item in the GitHub UI",
|
|
396
|
+
message: "Refusing to update the PR checkbox because its source is ambiguous",
|
|
397
|
+
hint: "PR body structure is ambiguous; tick this item in the GitHub UI.",
|
|
398
|
+
}),
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
const updatedBody =
|
|
402
|
+
body.slice(0, opts.boxOffset) + opts.replacement + body.slice(opts.boxOffset + 1);
|
|
403
|
+
const args = ["api", "--method", "PATCH", path, "-f", `body=${updatedBody}`];
|
|
404
|
+
const outboundText = [...args];
|
|
405
|
+
outboundText[5] = "body=[GitHub PR body]";
|
|
406
|
+
outboundText.push(...opts.selectionText);
|
|
407
|
+
yield* validateOutboundText(outboundText.join("\n"), "gh-tool pr checkbox");
|
|
408
|
+
return yield* runGhAttempt(args);
|
|
409
|
+
});
|
|
410
|
+
|
|
328
411
|
const runGhJson = <T>(args: string[]) =>
|
|
329
412
|
Effect.gen(function* () {
|
|
330
413
|
const result = yield* runGh(args);
|
|
@@ -415,6 +498,7 @@ export class GitHubService extends Context.Service<
|
|
|
415
498
|
|
|
416
499
|
return {
|
|
417
500
|
runGh,
|
|
501
|
+
writePRCheckbox,
|
|
418
502
|
runGhJson,
|
|
419
503
|
runGraphQL,
|
|
420
504
|
apiRequest: githubApi,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
+
import { GitHubCommandError } from "#gh/errors";
|
|
2
3
|
import { GitHubService } from "#gh/read/transport";
|
|
3
4
|
import { invalidate } from "./cache";
|
|
4
5
|
import type { Tag } from "./keys";
|
|
5
|
-
import type { GhError } from "#gh/read/transport";
|
|
6
|
+
import type { GhError, PRCheckboxWrite } from "#gh/read/transport";
|
|
6
7
|
import type { GitHubApiRequest } from "#gh/read/api";
|
|
7
8
|
|
|
8
9
|
const mutate = <A, R>(effect: Effect.Effect<A, GhError, R>, touched: readonly Tag[]) =>
|
|
@@ -15,6 +16,22 @@ const run = Effect.fn("writes.run")(function* (argv: string[], touched: readonly
|
|
|
15
16
|
const gh = yield* GitHubService;
|
|
16
17
|
return yield* mutate(gh.runGh(argv), touched);
|
|
17
18
|
});
|
|
19
|
+
const prCheckbox = Effect.fn("writes.prCheckbox")(function* (
|
|
20
|
+
opts: PRCheckboxWrite,
|
|
21
|
+
touched: readonly Tag[],
|
|
22
|
+
) {
|
|
23
|
+
const gh = yield* GitHubService;
|
|
24
|
+
if (gh.writePRCheckbox === undefined) {
|
|
25
|
+
return yield* new GitHubCommandError({
|
|
26
|
+
command: "gh-tool pr checkbox",
|
|
27
|
+
exitCode: 1,
|
|
28
|
+
stderr: "PR checkbox writes are unavailable",
|
|
29
|
+
message:
|
|
30
|
+
"Refusing to update the PR checkbox because the GitHub service cannot verify the body",
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return yield* mutate(gh.writePRCheckbox(opts), touched);
|
|
34
|
+
});
|
|
18
35
|
const json = <A>(argv: string[], touched: readonly Tag[]) =>
|
|
19
36
|
Effect.gen(function* () {
|
|
20
37
|
const gh = yield* GitHubService;
|
|
@@ -34,4 +51,4 @@ const api = <A>(request: GitHubApiRequest, touched: readonly Tag[]) =>
|
|
|
34
51
|
const gh = yield* GitHubService;
|
|
35
52
|
return yield* mutate(gh.apiRequest<A>(request), touched);
|
|
36
53
|
});
|
|
37
|
-
export const writes = { run, json, graphql, api };
|
|
54
|
+
export const writes = { run, prCheckbox, json, graphql, api };
|