dw-sdlc 0.10.0 → 0.12.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/agentic-config.d.ts +1 -1
- package/dist/agentic-config.js +1 -1
- package/dist/cli.js +9 -1
- package/dist/commands/doctor.d.ts +0 -5
- package/dist/commands/doctor.js +12 -142
- package/dist/commands/pr-ship.js +10 -27
- package/dist/commands/recap.d.ts +2 -0
- package/dist/commands/recap.js +348 -0
- package/dist/commands/worker-ls.d.ts +2 -0
- package/dist/commands/worker-ls.js +53 -0
- package/dist/commands/worker-report.d.ts +2 -0
- package/dist/commands/worker-report.js +117 -0
- package/dist/commands/worker-start.d.ts +2 -0
- package/dist/commands/worker-start.js +234 -0
- package/dist/doctor-plugins.d.ts +14 -0
- package/dist/doctor-plugins.js +182 -0
- package/dist/pr/decide.d.ts +7 -1
- package/dist/pr/decide.js +16 -1
- package/dist/pr/read.d.ts +12 -1
- package/dist/pr/read.js +72 -13
- package/dist/pr/state.d.ts +15 -3
- package/dist/pr/state.js +8 -2
- package/dist/worker/herdr.d.ts +48 -0
- package/dist/worker/herdr.js +115 -0
- package/dist/worker/tabs.d.ts +9 -0
- package/dist/worker/tabs.js +62 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,6 +86,7 @@ script posts that status itself, its description saying why, and goes on.
|
|
|
86
86
|
| `references setup` | the Router, its table and the ignore rules a repository needs to declare References |
|
|
87
87
|
| `pr ship [<n>]` | the Action one pull request needs next, performing the script's own |
|
|
88
88
|
| `pr reviewed <n>` | a clean review pinned to the head it read, as a commit status |
|
|
89
|
+
| `recap` | the facts a Recap rests on and whether the session can be closed |
|
|
89
90
|
|
|
90
91
|
`dw-sdlc <command> --help` gives each one's options.
|
|
91
92
|
|
package/dist/agentic-config.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ export type PrConfig = {
|
|
|
36
36
|
reviewCommand: string;
|
|
37
37
|
};
|
|
38
38
|
/** Claude Code's effort levels, lowest first: what `worker.effort` takes. */
|
|
39
|
-
declare const WORKER_EFFORTS: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
39
|
+
export declare const WORKER_EFFORTS: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
40
40
|
export type WorkerEffort = (typeof WORKER_EFFORTS)[number];
|
|
41
41
|
/** How a Worker's Claude Code session starts: the words that start it, its model and its effort. */
|
|
42
42
|
export type WorkerConfig = {
|
package/dist/agentic-config.js
CHANGED
|
@@ -14,7 +14,7 @@ export const COMMIT_CONVENTIONS = ["ticket-key", "conventional"];
|
|
|
14
14
|
/** The Review depths, strictest first: what the dev's `--review` names. */
|
|
15
15
|
export const REVIEW_DEPTHS = ["full", "light", "off"];
|
|
16
16
|
/** Claude Code's effort levels, lowest first: what `worker.effort` takes. */
|
|
17
|
-
const WORKER_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
17
|
+
export const WORKER_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
18
18
|
/** Strictest value first; the local override may only move towards the front. */
|
|
19
19
|
export const STRICTNESS = {
|
|
20
20
|
forcePush: ["deny", "ask", "lease"],
|
package/dist/cli.js
CHANGED
|
@@ -8,9 +8,13 @@ import * as gate from "./commands/gate.js";
|
|
|
8
8
|
import * as prReviewed from "./commands/pr-reviewed.js";
|
|
9
9
|
import * as prShip from "./commands/pr-ship.js";
|
|
10
10
|
import * as primitives from "./commands/primitives.js";
|
|
11
|
+
import * as recap from "./commands/recap.js";
|
|
11
12
|
import * as referencesSetup from "./commands/references-setup.js";
|
|
12
13
|
import * as referencesSync from "./commands/references-sync.js";
|
|
13
14
|
import * as setup from "./commands/setup.js";
|
|
15
|
+
import * as workerLs from "./commands/worker-ls.js";
|
|
16
|
+
import * as workerReport from "./commands/worker-report.js";
|
|
17
|
+
import * as workerStart from "./commands/worker-start.js";
|
|
14
18
|
const COMMANDS = new Map([
|
|
15
19
|
["gate", gate],
|
|
16
20
|
["check", check],
|
|
@@ -21,7 +25,11 @@ const COMMANDS = new Map([
|
|
|
21
25
|
["references sync", referencesSync],
|
|
22
26
|
["references setup", referencesSetup],
|
|
23
27
|
["pr ship", prShip],
|
|
24
|
-
["pr reviewed", prReviewed]
|
|
28
|
+
["pr reviewed", prReviewed],
|
|
29
|
+
["recap", recap],
|
|
30
|
+
["worker start", workerStart],
|
|
31
|
+
["worker ls", workerLs],
|
|
32
|
+
["worker report", workerReport]
|
|
25
33
|
]);
|
|
26
34
|
function usage() {
|
|
27
35
|
const width = Math.max(...[...COMMANDS.keys()].map((name) => name.length)) + 2;
|
|
@@ -40,11 +40,6 @@ export type Inputs = {
|
|
|
40
40
|
};
|
|
41
41
|
/** Claude Code's config dir: `$CLAUDE_CONFIG_DIR` when set, the way Claude Code reads it, else `~/.claude`. */
|
|
42
42
|
export declare function claudeConfigDir(env: Record<string, string | undefined>, home: string): string;
|
|
43
|
-
/** The marketplace Claude Code registers this repository under, the plugin in it, and the package the plugin links to. */
|
|
44
|
-
export declare const MARKETPLACE = "dw-sdlc";
|
|
45
|
-
export declare const PLUGIN = "sdlc";
|
|
46
|
-
export declare const PLUGIN_ID = "sdlc@dw-sdlc";
|
|
47
|
-
export declare const PACKAGE = "dw-sdlc";
|
|
48
43
|
/** rtk find passes -exec through verbatim from this release; before it, `rtk find … -exec` exits 1. */
|
|
49
44
|
export declare const RTK_FIND_EXEC_FIXED = "0.46.0";
|
|
50
45
|
export declare const PNPM_MINIMUM_RELEASE_AGE = 10080;
|
package/dist/commands/doctor.js
CHANGED
|
@@ -7,16 +7,12 @@ import { homedir } from "node:os";
|
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { parseArgs } from "node:util";
|
|
9
9
|
import { findRepository, resolveConfig } from "../agentic-config.js";
|
|
10
|
-
import {
|
|
10
|
+
import { checkPackage, checkPlugin, checkSessions, parseJson, runningPluginRoot, versionBefore } from "../doctor-plugins.js";
|
|
11
|
+
import { isObject } from "../json-text.js";
|
|
11
12
|
/** Claude Code's config dir: `$CLAUDE_CONFIG_DIR` when set, the way Claude Code reads it, else `~/.claude`. */
|
|
12
13
|
export function claudeConfigDir(env, home) {
|
|
13
14
|
return env.CLAUDE_CONFIG_DIR ?? join(home, ".claude");
|
|
14
15
|
}
|
|
15
|
-
/** The marketplace Claude Code registers this repository under, the plugin in it, and the package the plugin links to. */
|
|
16
|
-
export const MARKETPLACE = "dw-sdlc";
|
|
17
|
-
export const PLUGIN = "sdlc";
|
|
18
|
-
export const PLUGIN_ID = `${PLUGIN}@${MARKETPLACE}`;
|
|
19
|
-
export const PACKAGE = "dw-sdlc";
|
|
20
16
|
/** rtk find passes -exec through verbatim from this release; before it, `rtk find … -exec` exits 1. */
|
|
21
17
|
export const RTK_FIND_EXEC_FIXED = "0.46.0";
|
|
22
18
|
export const PNPM_MINIMUM_RELEASE_AGE = 10080;
|
|
@@ -34,20 +30,6 @@ export const realProbe = {
|
|
|
34
30
|
return (result.stdout ?? "").trim().split("\n")[0] ?? "";
|
|
35
31
|
}
|
|
36
32
|
};
|
|
37
|
-
function isObject(value) {
|
|
38
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
39
|
-
}
|
|
40
|
-
function parseJson(text) {
|
|
41
|
-
if (text === undefined) {
|
|
42
|
-
return undefined;
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
return JSON.parse(text);
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return undefined;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
33
|
function checkJq(probe) {
|
|
52
34
|
const { status, stdout } = probe.run("jq", ["--version"]);
|
|
53
35
|
if (status === 0) {
|
|
@@ -55,116 +37,6 @@ function checkJq(probe) {
|
|
|
55
37
|
}
|
|
56
38
|
return { name: "jq", level: "FAIL", detail: "missing: every guard is silent without it. brew install jq" };
|
|
57
39
|
}
|
|
58
|
-
/** Where the plugin's hooks run: live from the checkout for a marketplace added from a directory, a
|
|
59
|
-
* cached snapshot for one added from git. A marker placed in only one of the two settled it. */
|
|
60
|
-
/** The checkout a directory marketplace runs the plugin from, or undefined for a snapshot. */
|
|
61
|
-
function liveCheckout(probe, claude) {
|
|
62
|
-
const known = parseJson(probe.read(join(claude, "plugins", "known_marketplaces.json")));
|
|
63
|
-
const entry = isObject(known) && isObject(known[MARKETPLACE]) ? known[MARKETPLACE] : undefined;
|
|
64
|
-
const source = entry !== undefined && isObject(entry.source) ? entry.source : undefined;
|
|
65
|
-
return source?.source === "directory" && typeof source.path === "string" ? source.path : undefined;
|
|
66
|
-
}
|
|
67
|
-
function pluginOrigin(probe, claude, installPath) {
|
|
68
|
-
const checkout = liveCheckout(probe, claude);
|
|
69
|
-
if (checkout !== undefined) {
|
|
70
|
-
return `runs live from ${checkout}/plugins/${PLUGIN}; a change there is in the next session`;
|
|
71
|
-
}
|
|
72
|
-
return `installed at ${installPath}; a change reaches it through claude plugin update`;
|
|
73
|
-
}
|
|
74
|
-
/** The directory whose hooks run on this machine: the checkout of a directory marketplace, else the installed snapshot. */
|
|
75
|
-
function runningPluginRoot(probe, claude) {
|
|
76
|
-
const checkout = liveCheckout(probe, claude);
|
|
77
|
-
if (checkout !== undefined) {
|
|
78
|
-
return join(checkout, "plugins", PLUGIN);
|
|
79
|
-
}
|
|
80
|
-
const { status, stdout } = probe.run("claude", ["plugin", "list", "--json"]);
|
|
81
|
-
const list = status === 0 ? parseJson(stdout) : undefined;
|
|
82
|
-
if (!Array.isArray(list)) {
|
|
83
|
-
return undefined;
|
|
84
|
-
}
|
|
85
|
-
const entry = list.find((item) => isObject(item) && item.id === PLUGIN_ID);
|
|
86
|
-
return entry !== undefined && typeof entry.installPath === "string" ? entry.installPath : undefined;
|
|
87
|
-
}
|
|
88
|
-
function checkPlugin(probe, claude) {
|
|
89
|
-
const { status, stdout } = probe.run("claude", ["plugin", "list", "--json"]);
|
|
90
|
-
const list = status === 0 ? parseJson(stdout) : undefined;
|
|
91
|
-
if (!Array.isArray(list)) {
|
|
92
|
-
return { name: "plugin", level: "FAIL", detail: "claude plugin list did not answer; is Claude Code installed?" };
|
|
93
|
-
}
|
|
94
|
-
const entry = list.find((item) => isObject(item) && item.id === PLUGIN_ID);
|
|
95
|
-
if (entry === undefined) {
|
|
96
|
-
return {
|
|
97
|
-
name: "plugin",
|
|
98
|
-
level: "FAIL",
|
|
99
|
-
detail: `${PLUGIN_ID} is not installed. claude plugin marketplace add dominikwozniak/dw-sdlc --sparse .claude-plugin plugins packages && claude plugin install ${PLUGIN_ID} --scope user`
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
const version = typeof entry.version === "string" ? entry.version : "?";
|
|
103
|
-
const installPath = typeof entry.installPath === "string" ? entry.installPath : "?";
|
|
104
|
-
if (entry.enabled !== true) {
|
|
105
|
-
return {
|
|
106
|
-
name: "plugin",
|
|
107
|
-
level: "FAIL",
|
|
108
|
-
detail: `${version} installed but disabled, so no guard runs. claude plugin enable ${PLUGIN_ID}`
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
return {
|
|
112
|
-
name: "plugin",
|
|
113
|
-
level: "PASS",
|
|
114
|
-
detail: `${version} enabled; ${pluginOrigin(probe, claude, installPath)}`
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
/** The `version` of the package.json in `dir`, or undefined when there is none. */
|
|
118
|
-
function manifestVersion(probe, dir) {
|
|
119
|
-
const manifest = parseJson(probe.read(join(dir, "package.json")));
|
|
120
|
-
return isObject(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
|
|
121
|
-
}
|
|
122
|
-
/** Whether the plugin's hooks and skills run the dw-sdlc the repository pins, the one its Gate runs. */
|
|
123
|
-
function checkPackage(probe, claude, root) {
|
|
124
|
-
const name = "package";
|
|
125
|
-
const manifest = root === undefined ? undefined : probe.read(join(root, "package.json"));
|
|
126
|
-
if (root === undefined || manifest === undefined) {
|
|
127
|
-
return { name, level: "INFO", detail: `no package.json here to pin ${PACKAGE}` };
|
|
128
|
-
}
|
|
129
|
-
let spec;
|
|
130
|
-
try {
|
|
131
|
-
spec = parsePins(manifest, parseCatalog(probe.read(join(root, "pnpm-workspace.yaml")) ?? ""))[PACKAGE];
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
return { name, level: "INFO", detail: `${join(root, "package.json")} is not JSON` };
|
|
135
|
-
}
|
|
136
|
-
if (spec === undefined) {
|
|
137
|
-
return { name, level: "INFO", detail: `the repository does not pin ${PACKAGE}` };
|
|
138
|
-
}
|
|
139
|
-
// `workspace:` or a range names no one version: the copy installed under node_modules is the one that runs.
|
|
140
|
-
const pinned = EXACT_VERSION.test(spec) ? spec : manifestVersion(probe, join(root, "node_modules", PACKAGE));
|
|
141
|
-
if (pinned === undefined) {
|
|
142
|
-
return { name, level: "WARN", detail: `the repository pins ${PACKAGE} ${spec}, not installed: pnpm install` };
|
|
143
|
-
}
|
|
144
|
-
const plugin = runningPluginRoot(probe, claude);
|
|
145
|
-
if (plugin === undefined) {
|
|
146
|
-
return { name, level: "INFO", detail: `no ${PLUGIN_ID} runs here to compare with the pinned ${pinned}` };
|
|
147
|
-
}
|
|
148
|
-
const linked = manifestVersion(probe, join(plugin, PACKAGE));
|
|
149
|
-
if (linked === undefined) {
|
|
150
|
-
return {
|
|
151
|
-
name,
|
|
152
|
-
level: "WARN",
|
|
153
|
-
detail: `the plugin at ${plugin} carries no ${PACKAGE}: an install from GitHub needs packages in its --sparse set`
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
if (linked !== pinned) {
|
|
157
|
-
const remedy = versionBefore(linked, pinned)
|
|
158
|
-
? `claude plugin update ${PLUGIN_ID}`
|
|
159
|
-
: `pnpm add --save-dev --save-exact ${PACKAGE}@${linked}`;
|
|
160
|
-
return {
|
|
161
|
-
name,
|
|
162
|
-
level: "WARN",
|
|
163
|
-
detail: `drift: the plugin runs ${PACKAGE} ${linked} and the repository pins ${pinned}, so its hooks and skills run other code than the Gate. ${remedy}`
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
return { name, level: "PASS", detail: `${PACKAGE} ${linked} in the plugin and in the repository` };
|
|
167
|
-
}
|
|
168
40
|
/** Every hook command string in a settings file, whatever the event and matcher. */
|
|
169
41
|
function hookCommands(settings) {
|
|
170
42
|
if (!isObject(settings) || !isObject(settings.hooks)) {
|
|
@@ -203,18 +75,6 @@ export function rtkExcludes(toml) {
|
|
|
203
75
|
}
|
|
204
76
|
return [...(match[1] ?? "").matchAll(/"([^"]*)"/g)].map((entry) => entry[1] ?? "");
|
|
205
77
|
}
|
|
206
|
-
function versionBefore(version, threshold) {
|
|
207
|
-
const a = version.split(".").map((part) => Number.parseInt(part, 10));
|
|
208
|
-
const b = threshold.split(".").map((part) => Number.parseInt(part, 10));
|
|
209
|
-
for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
|
|
210
|
-
const x = a[i] ?? 0;
|
|
211
|
-
const y = b[i] ?? 0;
|
|
212
|
-
if (x !== y) {
|
|
213
|
-
return x < y;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
218
78
|
// The plugin's rtk-rewrite.sh is the one wiring of `rtk hook claude`; a second one in the user
|
|
219
79
|
// settings rewrites again, and in a linked worktree that copy is what the isolation check refuses.
|
|
220
80
|
function checkRtk(probe, home, claude) {
|
|
@@ -261,6 +121,14 @@ function checkRtk(probe, home, claude) {
|
|
|
261
121
|
detail: `rtk ${version} behind the plugin's rewrite hook; exclude_commands: ${excludes.join(", ") || "none"}`
|
|
262
122
|
};
|
|
263
123
|
}
|
|
124
|
+
/** herdr's `hc` in the agent's shell, which a Lead starts its Workers through; a machine may go without it. */
|
|
125
|
+
function checkHc(probe) {
|
|
126
|
+
const hc = probe.shell("command -v hc");
|
|
127
|
+
if (hc === "") {
|
|
128
|
+
return { name: "hc", level: "INFO", detail: "no hc on PATH, so no /sdlc-delegate: install herdr to run Workers" };
|
|
129
|
+
}
|
|
130
|
+
return { name: "hc", level: "PASS", detail: `${hc}, so a Lead can start Workers` };
|
|
131
|
+
}
|
|
264
132
|
function checkSafeChain(probe) {
|
|
265
133
|
const binary = probe.shell("command -v safe-chain");
|
|
266
134
|
if (binary === "") {
|
|
@@ -342,6 +210,8 @@ export function diagnose(inputs) {
|
|
|
342
210
|
checkJq(probe),
|
|
343
211
|
checkPlugin(probe, claude),
|
|
344
212
|
checkPackage(probe, claude, inputs.config === undefined ? undefined : inputs.root),
|
|
213
|
+
checkSessions(probe, claude),
|
|
214
|
+
checkHc(probe),
|
|
345
215
|
checkRtk(probe, home, claude),
|
|
346
216
|
checkSafeChain(probe),
|
|
347
217
|
checkPnpmReleaseAge(probe, home),
|
package/dist/commands/pr-ship.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// is not, as a line or one JSON document, with an exit code that names whose turn it is.
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
import { repositoryRoot, resolveConfig, REVIEW_DEPTHS } from "../agentic-config.js";
|
|
6
|
-
import { decide, devCommand, hostArgs, isScriptAction, readyToMerge, REVIEW, shellLine } from "../pr/decide.js";
|
|
7
|
-
import { CannotRun, carriesBase, gh, namedProblem, read, readAgain, said } from "../pr/read.js";
|
|
6
|
+
import { decide, declaredPolicy, devCommand, hostArgs, isScriptAction, lineOf, readyToMerge, REVIEW, shellLine } from "../pr/decide.js";
|
|
7
|
+
import { CannotRun, carriesBase, gh, namedProblem, now, read, readAgain, said, withBehind } from "../pr/read.js";
|
|
8
8
|
export const summary = "decide the Action one pull request needs next, as a line or JSON";
|
|
9
9
|
const USAGE = `Usage: dw-sdlc pr ship [<n>] [-R owner/name] [--merge] [--review full|light|off] [--dry-run] [--json] [--verbose]
|
|
10
10
|
|
|
@@ -13,7 +13,9 @@ is omitted, and answers the Action it needs next: the status, the action, whose
|
|
|
13
13
|
The script performs its own Actions, each at most once and then reads again: it updates a branch
|
|
14
14
|
behind its base, marks a draft ready once nothing else holds it back, and squash merges a pull
|
|
15
15
|
request ready to merge under pr.merge auto in .ai/agentic.config.json or with --merge for this call;
|
|
16
|
-
under ask, the default, it parks with the merge command. A
|
|
16
|
+
under ask, the default, it parks with the merge command. A branch is behind when the compare of
|
|
17
|
+
the base with the head shows the head lacks a commit of the base, or GitHub reports BEHIND, which
|
|
18
|
+
it does only under a rule that a branch be up to date. A head without a ${REVIEW} success
|
|
17
19
|
gets the Review depth --review names for this call, else pr.review: under full the agent reviews it
|
|
18
20
|
with the command pr.reviewCommand names, /code-review by default; under light one sdlc-reviewer
|
|
19
21
|
subagent reads the diff; under off the script posts the success itself, its description saying
|
|
@@ -38,7 +40,6 @@ Exit 0 done, 10 the agent's turn, 20 wait, 30 the dev's turn, 2 when it cannot r
|
|
|
38
40
|
/** The depths --review takes, strictest first, as a sentence lists them. */
|
|
39
41
|
const DEPTHS_SENTENCE = `${REVIEW_DEPTHS.slice(0, -1).join(", ")} or ${REVIEW_DEPTHS.at(-1)}`;
|
|
40
42
|
const EXIT = { done: 0, act: 10, wait: 20, park: 30 };
|
|
41
|
-
const REMEDIES = { clock: "unset it: it stands in for the clock in tests" };
|
|
42
43
|
/** A call that stopped after it ran Actions: what it ran stands in `performed`, `ok` null where no read followed. */
|
|
43
44
|
class Interrupted extends CannotRun {
|
|
44
45
|
performed;
|
|
@@ -92,18 +93,6 @@ function parseCli(argv) {
|
|
|
92
93
|
return { error: error instanceof Error ? error.message : String(error) };
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
|
-
/** The time the decision reads: DW_SDLC_NOW, an ISO 8601 time a test sets, else the system clock. */
|
|
96
|
-
function now() {
|
|
97
|
-
const given = process.env.DW_SDLC_NOW?.trim();
|
|
98
|
-
if (given === undefined || given === "") {
|
|
99
|
-
return Date.now();
|
|
100
|
-
}
|
|
101
|
-
const time = Date.parse(given);
|
|
102
|
-
if (Number.isNaN(time)) {
|
|
103
|
-
throw new CannotRun(`DW_SDLC_NOW is ${JSON.stringify(given)}, not an ISO 8601 time`, REMEDIES.clock);
|
|
104
|
-
}
|
|
105
|
-
return time;
|
|
106
|
-
}
|
|
107
96
|
/** How a reason names each script Action: what the script would do, the act, and the state it acts on. */
|
|
108
97
|
const NAMED = {
|
|
109
98
|
"update-branch": { would: "update the branch", act: "the update", still: "the branch still reads behind its base" },
|
|
@@ -156,7 +145,7 @@ function handedOver(pr, policy, answer, action, reason) {
|
|
|
156
145
|
function ship(cli, policy) {
|
|
157
146
|
// Only pr.review auto routes the depth from the changes, so only it reads them.
|
|
158
147
|
const sized = policy.review.depth === "auto";
|
|
159
|
-
let pr = read(cli, "pr ship", sized);
|
|
148
|
+
let pr = withBehind(read(cli, "pr ship", sized));
|
|
160
149
|
const performed = [];
|
|
161
150
|
// The head each Action ran on.
|
|
162
151
|
const postedOn = new Map();
|
|
@@ -190,7 +179,7 @@ function ship(cli, policy) {
|
|
|
190
179
|
const record = { action, command, exit: status, ...(message === "" ? {} : { stderr: message }) };
|
|
191
180
|
postedOn.set(action, before.head);
|
|
192
181
|
try {
|
|
193
|
-
pr = readAgain(pr, sized);
|
|
182
|
+
pr = withBehind(readAgain(pr, sized));
|
|
194
183
|
performed.push({ ...record, ok: TOOK[action](before, pr) });
|
|
195
184
|
}
|
|
196
185
|
catch (error) {
|
|
@@ -202,12 +191,6 @@ function ship(cli, policy) {
|
|
|
202
191
|
}
|
|
203
192
|
}
|
|
204
193
|
}
|
|
205
|
-
/** `<status> <action>: <reason>`, the action dropped when it repeats the status, then the recheck and the command. */
|
|
206
|
-
function lineOf({ status, action, reason, command, recheckAfter }) {
|
|
207
|
-
const label = status === action ? status : `${status} ${action}`;
|
|
208
|
-
const recheck = recheckAfter === undefined ? "" : `, recheck in ${recheckAfter} s`;
|
|
209
|
-
return `${label}: ${reason}${recheck}${command === undefined ? "" : ` — ${command}`}`;
|
|
210
|
-
}
|
|
211
194
|
function documentOf(pr, answer, performed, verbose) {
|
|
212
195
|
const facts = verbose ? { facts: pr } : {};
|
|
213
196
|
return { pr: pr.url, head: pr.head, base: pr.base, ...answer, performed, ...facts };
|
|
@@ -225,9 +208,9 @@ export function run(argv) {
|
|
|
225
208
|
// An invalid config layer resolves to ask and full, like every other strictest setting.
|
|
226
209
|
const declared = resolveConfig(repositoryRoot(process.cwd())).config.pr;
|
|
227
210
|
const policy = {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
211
|
+
...declaredPolicy(declared),
|
|
212
|
+
...(cli.merge ? { merge: "auto" } : {}),
|
|
213
|
+
...(cli.review === undefined ? {} : { review: { depth: cli.review, source: "flag" } })
|
|
231
214
|
};
|
|
232
215
|
let shipped;
|
|
233
216
|
try {
|