@popoverai/dotrequirements 0.34.1 → 0.35.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/dist/commands/solution/brief.d.ts +7 -0
- package/dist/commands/solution/index.js +37 -7
- package/dist/commands/solution/pull-request.d.ts +43 -2
- package/dist/commands/solution/pull-request.js +92 -3
- package/dist/commands/solution/status.js +16 -2
- package/dist/commands/solution/things.js +2 -2
- package/dist/convex.d.ts +3 -0
- package/dist/convex.js +3 -0
- package/package.json +4 -4
|
@@ -49,6 +49,13 @@ export type PanelOutcome = {
|
|
|
49
49
|
waitingFor?: string[];
|
|
50
50
|
/** Its drafter's work was set aside and its new draft isn't taken yet: a session at it owes nothing more (CHECKPOINT-6.3.2.3). */
|
|
51
51
|
setAside?: boolean;
|
|
52
|
+
/** The agents at it now, each drafting it or reviewing it; absent from a backend from before it said. */
|
|
53
|
+
atWork?: Array<{
|
|
54
|
+
agent: string;
|
|
55
|
+
part: "draft" | "review";
|
|
56
|
+
}>;
|
|
57
|
+
/** The reviewers waiting on its pull request, their sessions ended (PULL-5). */
|
|
58
|
+
waiting?: string[];
|
|
52
59
|
};
|
|
53
60
|
/** A checkpoint on the plan panel, as far as the CLI reads it. */
|
|
54
61
|
export type PanelCheckpoint = {
|
|
@@ -6,7 +6,7 @@ import { REQUEST_CONTRACT } from "./contract.js";
|
|
|
6
6
|
import { createCommand } from "./create.js";
|
|
7
7
|
import { thingsCommand } from "./look-up.js";
|
|
8
8
|
import { nextCommand } from "./next.js";
|
|
9
|
-
import { pullRequestCommand } from "./pull-request.js";
|
|
9
|
+
import { prStatusCommand, prWaitCommand, pullRequestCommand, } from "./pull-request.js";
|
|
10
10
|
import { rehearseCommand } from "./rehearse.js";
|
|
11
11
|
import { reviewCommand } from "./review.js";
|
|
12
12
|
import { doneCommand } from "./share.js";
|
|
@@ -41,7 +41,7 @@ Where what you made is, one or more:
|
|
|
41
41
|
mockup:<id> a mockup in dotrequirements
|
|
42
42
|
scores:<id> scores in dotrequirements, made with dotreq scores create
|
|
43
43
|
pr:<number> the solution's pull request, opened or found with
|
|
44
|
-
dotreq solution
|
|
44
|
+
dotreq solution pr open. An outcome kept on a pull
|
|
45
45
|
request is submitted with it, with the rest of its work
|
|
46
46
|
image:<path> an image, such as a screenshot: a PNG, JPEG, GIF, WebP or
|
|
47
47
|
AVIF. The app keeps a copy; don't commit it
|
|
@@ -91,11 +91,27 @@ Examples:
|
|
|
91
91
|
dotreq solution author file:src/checkout.ts@guest pr:12 image:checkout.png --comment "Guest path, behind the flag."
|
|
92
92
|
dotreq solution author "preview:8080/design/roll-count pnpm dev" file:src/RollCount.tsx --title "Roll count after three dice" --title "Roll count component" --comment "The roll count, as a dev-only page."
|
|
93
93
|
`;
|
|
94
|
-
/** What `
|
|
94
|
+
/** What `pr status` says, on its `--help` (PULL-4). */
|
|
95
|
+
const PR_STATUS_HELP = `
|
|
96
|
+
Reads the pull request of the outcome you are at, or of the outcome it awaits that
|
|
97
|
+
is kept on one, as GitHub reports it. The merge state is GitHub's word for whether
|
|
98
|
+
it can merge: clean means it can, with every required check passing and no
|
|
99
|
+
conflict. Failed checks come with a link to them; checks still running are named.
|
|
100
|
+
Reading it changes nothing.
|
|
101
|
+
`;
|
|
102
|
+
/** What `pr wait` does, on its `--help` (PULL-5, PULL-6). */
|
|
103
|
+
const PR_WAIT_HELP = `
|
|
104
|
+
For a reviewer, when the pull request has to change before you can give a result,
|
|
105
|
+
as while its checks run. Records where it stands now; then end your session
|
|
106
|
+
without a result. You are started again, in this session, when GitHub reports it
|
|
107
|
+
changed and nothing on it is still running, or once it is merged or closed. A new
|
|
108
|
+
submission ends the wait too, and you review that as you would any other.
|
|
109
|
+
`;
|
|
110
|
+
/** What `pr open` does, on its `--help`, where `author --help` sends the agent (PULL-2). */
|
|
95
111
|
const PULL_REQUEST_HELP = `
|
|
96
112
|
Pushes your commits to the solution's branch, then opens a pull request from it
|
|
97
|
-
into the branch it was made from,
|
|
98
|
-
Prints its reference and its address:
|
|
113
|
+
into the branch it was made from, as a draft, or finds the one already open. The
|
|
114
|
+
plan says when it becomes ready for review. Prints its reference and its address:
|
|
99
115
|
|
|
100
116
|
The solution's pull request is pr:12: https://github.com/<owner>/<repository>/pull/12
|
|
101
117
|
|
|
@@ -216,11 +232,25 @@ export function registerSolution(program) {
|
|
|
216
232
|
.option("--title <text>", "A reference's name on the outcome's tab, for the owner: one per reference, in the order the references are given, or none", (value, previous = []) => [...previous, value])
|
|
217
233
|
.addHelpText("after", REFERENCE_FORMS)
|
|
218
234
|
.action(wrap(authorCommand));
|
|
219
|
-
|
|
220
|
-
.command("
|
|
235
|
+
const pr = solution
|
|
236
|
+
.command("pr", HIDDEN)
|
|
237
|
+
.description("The solution's pull request: open it, read it, wait on it");
|
|
238
|
+
whereOptions(pr
|
|
239
|
+
.command("open")
|
|
221
240
|
.description("Push the solution's branch and open its pull request, or find the one open; prints its reference, pr:<number>, to submit"))
|
|
222
241
|
.addHelpText("after", PULL_REQUEST_HELP)
|
|
223
242
|
.action(wrap(pullRequestCommand));
|
|
243
|
+
whereOptions(pr
|
|
244
|
+
.command("status")
|
|
245
|
+
.description("Where the pull request of the outcome you are at stands, in GitHub's words: its merge state, conflicts, review decision and checks"))
|
|
246
|
+
.option("--json", "Print as JSON")
|
|
247
|
+
.addHelpText("after", PR_STATUS_HELP)
|
|
248
|
+
.action(wrap(prStatusCommand));
|
|
249
|
+
whereOptions(pr
|
|
250
|
+
.command("wait")
|
|
251
|
+
.description("Wait on the pull request with your session ended: you start again when it changes and nothing on it is still running"))
|
|
252
|
+
.addHelpText("after", PR_WAIT_HELP)
|
|
253
|
+
.action(wrap(prWaitCommand));
|
|
224
254
|
whereOptions(solution
|
|
225
255
|
.command("review <result> [video...]", HIDDEN)
|
|
226
256
|
.description("Record the review's result: approved, approved-with-revisions, needs-revision or escalate"))
|
|
@@ -5,9 +5,50 @@ export type PullRequest = {
|
|
|
5
5
|
number: number;
|
|
6
6
|
};
|
|
7
7
|
/** The command that opens the solution's pull request and prints its reference. */
|
|
8
|
-
export declare const OPEN_IT = "dotreq solution
|
|
8
|
+
export declare const OPEN_IT = "dotreq solution pr open";
|
|
9
|
+
/** Where a pull request stands, in GitHub's words (web: lib/runner/github.ts). */
|
|
10
|
+
export type PullRequestState = {
|
|
11
|
+
number: number;
|
|
12
|
+
url: string;
|
|
13
|
+
mergeState: string;
|
|
14
|
+
mergeable: string;
|
|
15
|
+
reviewDecision: string | null;
|
|
16
|
+
checks: string | null;
|
|
17
|
+
failed: Array<{
|
|
18
|
+
name: string;
|
|
19
|
+
result: string;
|
|
20
|
+
link: string | null;
|
|
21
|
+
}>;
|
|
22
|
+
running: Array<{
|
|
23
|
+
name: string;
|
|
24
|
+
link: string | null;
|
|
25
|
+
}>;
|
|
26
|
+
head: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Where the pull request stood, as a wait records it (PULL-6.4): what a
|
|
30
|
+
* reviewer judges by, so a later read that differs is a change.
|
|
31
|
+
*/
|
|
32
|
+
export declare function seenOf(state: PullRequestState): string;
|
|
33
|
+
/** Where the pull request stands, as a reviewer reads it (PULL-4). */
|
|
34
|
+
export declare function describeState(state: PullRequestState): string;
|
|
35
|
+
/**
|
|
36
|
+
* `dotreq solution pr status`: where the pull request of the outcome the
|
|
37
|
+
* agent is at stands, in GitHub's words (PULL-4). Changes nothing (4.6).
|
|
38
|
+
*/
|
|
39
|
+
export declare function prStatusCommand(options: WhereOptions & {
|
|
40
|
+
json?: boolean;
|
|
41
|
+
}): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* `dotreq solution pr wait`: the reviewer waits on the pull request with
|
|
44
|
+
* no session running (PULL-5): it records where the pull request stands,
|
|
45
|
+
* and its session can end without a result. It starts again, in the same
|
|
46
|
+
* session, when the pull request changes and nothing on it is still running
|
|
47
|
+
* (PULL-6).
|
|
48
|
+
*/
|
|
49
|
+
export declare function prWaitCommand(options: WhereOptions): Promise<void>;
|
|
9
50
|
/**
|
|
10
|
-
* `dotreq solution
|
|
51
|
+
* `dotreq solution pr open`: the solution's pull request, opened or
|
|
11
52
|
* found (PULL-2). Inside a runner the branch is pushed first, as a submission
|
|
12
53
|
* pushes it (2.0, SOLBRANCH-3); then the app opens a pull request from it
|
|
13
54
|
* into the branch it was made from, or finds the one already open (2.3,
|
|
@@ -1,13 +1,101 @@
|
|
|
1
1
|
import { webApiUrl } from "../../cloud.js";
|
|
2
|
+
import { api } from "../../convex.js";
|
|
2
3
|
import { getProjectCredentials } from "../../utils/project-settings.js";
|
|
3
4
|
import { solutionClient } from "./client.js";
|
|
4
5
|
import { pushWork } from "./pushing.js";
|
|
5
6
|
import { requireWhere } from "./run-file.js";
|
|
6
7
|
import { outcomeFor } from "./status.js";
|
|
7
8
|
/** The command that opens the solution's pull request and prints its reference. */
|
|
8
|
-
export const OPEN_IT = "dotreq solution
|
|
9
|
+
export const OPEN_IT = "dotreq solution pr open";
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* Where the pull request stood, as a wait records it (PULL-6.4): what a
|
|
12
|
+
* reviewer judges by, so a later read that differs is a change.
|
|
13
|
+
*/
|
|
14
|
+
export function seenOf(state) {
|
|
15
|
+
return [
|
|
16
|
+
state.head,
|
|
17
|
+
state.mergeState,
|
|
18
|
+
state.mergeable,
|
|
19
|
+
state.reviewDecision ?? "",
|
|
20
|
+
state.checks ?? "",
|
|
21
|
+
].join(" ");
|
|
22
|
+
}
|
|
23
|
+
/** The pull request the outcome the agent is at reads, as the app finds it (PULL-4.0, 4.0.0). */
|
|
24
|
+
async function stateFor(where) {
|
|
25
|
+
const said = await ask({
|
|
26
|
+
solutionId: where.solutionId,
|
|
27
|
+
agent: where.agent,
|
|
28
|
+
...(where.outcome ? { outcome: where.outcome } : {}),
|
|
29
|
+
op: "status",
|
|
30
|
+
});
|
|
31
|
+
if (said.noRepository) {
|
|
32
|
+
// PULL-4.5.
|
|
33
|
+
throw new Error("There's no pull request: a pull request needs a repository, and this project has none connected.");
|
|
34
|
+
}
|
|
35
|
+
if (said.noBranch || said.none) {
|
|
36
|
+
// PULL-4.4.
|
|
37
|
+
throw new Error(`There's no pull request open for it. Its drafter opens one with \`${OPEN_IT}\`.`);
|
|
38
|
+
}
|
|
39
|
+
if (said.refused) {
|
|
40
|
+
throw new Error(`Couldn't read the pull request from the repository: ${said.refused}`);
|
|
41
|
+
}
|
|
42
|
+
if (!said.state)
|
|
43
|
+
throw new Error("The app gave no pull request.");
|
|
44
|
+
return said.state;
|
|
45
|
+
}
|
|
46
|
+
/** Where the pull request stands, as a reviewer reads it (PULL-4). */
|
|
47
|
+
export function describeState(state) {
|
|
48
|
+
const lines = [
|
|
49
|
+
`pr:${state.number} (${state.url}), at ${state.head.slice(0, 7)}`,
|
|
50
|
+
`Merge state: ${state.mergeState}`,
|
|
51
|
+
`Conflicts with the branch it merges into: ${state.mergeable === "conflicting" ? "yes" : state.mergeable === "mergeable" ? "no" : "not worked out yet"}`,
|
|
52
|
+
`Review decision: ${state.reviewDecision ?? "none required"}`,
|
|
53
|
+
`Checks: ${state.checks ?? "none"}`,
|
|
54
|
+
];
|
|
55
|
+
for (const check of state.failed) {
|
|
56
|
+
lines.push(` failed: ${check.name} (${check.result})${check.link ? ` ${check.link}` : ""}`);
|
|
57
|
+
}
|
|
58
|
+
for (const check of state.running) {
|
|
59
|
+
lines.push(` still running: ${check.name}${check.link ? ` ${check.link}` : ""}`);
|
|
60
|
+
}
|
|
61
|
+
if (state.mergeable === "unknown") {
|
|
62
|
+
// PULL-4.3.
|
|
63
|
+
lines.push("GitHub hasn't yet worked out whether it can merge: read it again shortly, or wait on it.");
|
|
64
|
+
}
|
|
65
|
+
return lines.join("\n");
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* `dotreq solution pr status`: where the pull request of the outcome the
|
|
69
|
+
* agent is at stands, in GitHub's words (PULL-4). Changes nothing (4.6).
|
|
70
|
+
*/
|
|
71
|
+
export async function prStatusCommand(options) {
|
|
72
|
+
const state = await stateFor(requireWhere(options));
|
|
73
|
+
console.log(options.json ? JSON.stringify(state) : describeState(state));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* `dotreq solution pr wait`: the reviewer waits on the pull request with
|
|
77
|
+
* no session running (PULL-5): it records where the pull request stands,
|
|
78
|
+
* and its session can end without a result. It starts again, in the same
|
|
79
|
+
* session, when the pull request changes and nothing on it is still running
|
|
80
|
+
* (PULL-6).
|
|
81
|
+
*/
|
|
82
|
+
export async function prWaitCommand(options) {
|
|
83
|
+
const where = requireWhere(options);
|
|
84
|
+
const state = await stateFor(where);
|
|
85
|
+
await solutionClient().mutation(api.solutions.waits.waitOnPullRequest, {
|
|
86
|
+
solutionId: where.solutionId,
|
|
87
|
+
agent: where.agent,
|
|
88
|
+
...(where.outcome ? { outcome: where.outcome } : {}),
|
|
89
|
+
seen: seenOf(state),
|
|
90
|
+
});
|
|
91
|
+
console.log([
|
|
92
|
+
describeState(state),
|
|
93
|
+
"",
|
|
94
|
+
"Waiting on it. End your session now: you'll be started again, here, when it changes and nothing on it is still running.",
|
|
95
|
+
].join("\n"));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* `dotreq solution pr open`: the solution's pull request, opened or
|
|
11
99
|
* found (PULL-2). Inside a runner the branch is pushed first, as a submission
|
|
12
100
|
* pushes it (2.0, SOLBRANCH-3); then the app opens a pull request from it
|
|
13
101
|
* into the branch it was made from, or finds the one already open (2.3,
|
|
@@ -98,7 +186,8 @@ function pullRequestIn(said) {
|
|
|
98
186
|
/**
|
|
99
187
|
* Ask the app's pull-request route, on the project's credentials: with the
|
|
100
188
|
* outcome, to open the pull request it is kept on; with a number, to check
|
|
101
|
-
* that one is the solution's open pull request, which needs no outcome
|
|
189
|
+
* that one is the solution's open pull request, which needs no outcome;
|
|
190
|
+
* with `op: "status"`, for where the open one stands.
|
|
102
191
|
*/
|
|
103
192
|
async function ask(body) {
|
|
104
193
|
const { projectId, projectSecret } = getProjectCredentials();
|
|
@@ -23,6 +23,11 @@ export function agentAt(panel, agent, named) {
|
|
|
23
23
|
if (!drafting && !reviewing) {
|
|
24
24
|
throw new Error(`${agent} neither drafts nor reviews "${named}".`);
|
|
25
25
|
}
|
|
26
|
+
// At it now: the part it is at, as for an agent that both drafts and
|
|
27
|
+
// reviews the outcome, whatever word the outcome stands in.
|
|
28
|
+
const at = outcome.atWork?.find((each) => each.agent === agent);
|
|
29
|
+
if (at)
|
|
30
|
+
return { outcome, part: at.part };
|
|
26
31
|
return {
|
|
27
32
|
outcome,
|
|
28
33
|
part: reviewing && (!drafting || outcome.status === "reviewing")
|
|
@@ -129,10 +134,19 @@ export async function statusCommand(options) {
|
|
|
129
134
|
// CHECKPOINT-6.3.2.3: a drafter whose work was set aside owes nothing
|
|
130
135
|
// more there; the new draft is another session's.
|
|
131
136
|
const setAside = part === "draft" && outcome.setAside === true;
|
|
137
|
+
// STEP-REVIEW-9.8: an agent still at its part owes it, whatever word the
|
|
138
|
+
// outcome stands in because of its other agents, as stalled when the
|
|
139
|
+
// other reviewer stalled. Asking, stalling and recording all take the
|
|
140
|
+
// agent off it.
|
|
141
|
+
const atIt = outcome.atWork?.some((each) => each.agent === where.agent && each.part === part) === true;
|
|
142
|
+
// PULL-5.2: a reviewer waiting on the pull request owes nothing until it
|
|
143
|
+
// starts again.
|
|
144
|
+
const waiting = part === "review" && outcome.waiting?.includes(where.agent) === true;
|
|
132
145
|
const owes = part === "draft"
|
|
133
146
|
? !setAside &&
|
|
134
|
-
(outcome.status === "drafting" || outcome.status === "ready")
|
|
135
|
-
:
|
|
147
|
+
(atIt || outcome.status === "drafting" || outcome.status === "ready")
|
|
148
|
+
: !waiting &&
|
|
149
|
+
(atIt || outcome.status === "reviewing") &&
|
|
136
150
|
(await reviewerOwes(client, where.solutionId, outcome.name, where.agent));
|
|
137
151
|
if (options.json) {
|
|
138
152
|
process.stdout.write(`${JSON.stringify({ agent: where.agent, outcome: outcome.name, part, status: outcome.status, reason: outcome.reason ?? null, reviews: part === "review", ...(setAside ? { setAside } : {}), owes })}\n`);
|
|
@@ -22,10 +22,10 @@ export function parseThing(reference) {
|
|
|
22
22
|
return { kind, scoresId: rest };
|
|
23
23
|
case "pr": {
|
|
24
24
|
// PULL-3: the solution's pull request, by the reference
|
|
25
|
-
// `dotreq solution
|
|
25
|
+
// `dotreq solution pr open` printed.
|
|
26
26
|
const number = Number(rest.replace(/^#/, ""));
|
|
27
27
|
if (!Number.isInteger(number) || number <= 0) {
|
|
28
|
-
throw new Error(`"${reference}" isn't a pull request's reference. It is pr: and the pull request's number, as \`dotreq solution
|
|
28
|
+
throw new Error(`"${reference}" isn't a pull request's reference. It is pr: and the pull request's number, as \`dotreq solution pr open\` prints it, e.g. pr:42.`);
|
|
29
29
|
}
|
|
30
30
|
return { kind: "pull request", number };
|
|
31
31
|
}
|
package/dist/convex.d.ts
CHANGED
|
@@ -121,6 +121,9 @@ export declare const api: {
|
|
|
121
121
|
recordPush: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
122
122
|
removal: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
|
|
123
123
|
};
|
|
124
|
+
waits: {
|
|
125
|
+
waitOnPullRequest: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
126
|
+
};
|
|
124
127
|
};
|
|
125
128
|
lib: {
|
|
126
129
|
authActions: {
|
package/dist/convex.js
CHANGED
|
@@ -141,6 +141,9 @@ export const api = {
|
|
|
141
141
|
recordPush: mutation("solutions/branches:recordPush"),
|
|
142
142
|
removal: query("solutions/branches:removal"),
|
|
143
143
|
},
|
|
144
|
+
waits: {
|
|
145
|
+
waitOnPullRequest: mutation("solutions/waits:waitOnPullRequest"),
|
|
146
|
+
},
|
|
144
147
|
},
|
|
145
148
|
lib: {
|
|
146
149
|
authActions: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@popoverai/dotrequirements",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "Requirements as testable, human-readable data — CLI and test harness for spec-driven development with AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"test": "vitest",
|
|
27
27
|
"test:run": "vitest run",
|
|
28
28
|
"test:coverage": "vitest run --coverage",
|
|
29
|
-
"publish:patch": "echo 'ERROR: publish:patch is decommissioned. CLI npm publishes now happen via the cli-publish GitHub Action when a
|
|
30
|
-
"publish:minor": "echo 'ERROR: publish:minor is decommissioned. CLI npm publishes now happen via the cli-publish GitHub Action when a
|
|
31
|
-
"publish:major": "echo 'ERROR: publish:major is decommissioned. CLI npm publishes now happen via the cli-publish GitHub Action when a
|
|
29
|
+
"publish:patch": "echo 'ERROR: publish:patch is decommissioned. CLI npm publishes now happen via the cli-publish GitHub Action when a PR that bumps the CLI version merges to main. See docs/working/cli-publish-workflow.md for details.' && exit 1",
|
|
30
|
+
"publish:minor": "echo 'ERROR: publish:minor is decommissioned. CLI npm publishes now happen via the cli-publish GitHub Action when a PR that bumps the CLI version merges to main. See docs/working/cli-publish-workflow.md for details.' && exit 1",
|
|
31
|
+
"publish:major": "echo 'ERROR: publish:major is decommissioned. CLI npm publishes now happen via the cli-publish GitHub Action when a PR that bumps the CLI version merges to main. See docs/working/cli-publish-workflow.md for details.' && exit 1"
|
|
32
32
|
},
|
|
33
33
|
"keywords": [
|
|
34
34
|
"requirements",
|