dev-loops 0.2.6 → 0.2.7
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/.claude/.claude-plugin/plugin.json +1 -1
- package/.claude/agents/dev-loop.md +2 -1
- package/.claude/skills/dev-loop/SKILL.md +5 -5
- package/CHANGELOG.md +17 -0
- package/agents/dev-loop.agent.md +5 -1
- package/cli/index.mjs +3 -1
- package/package.json +2 -2
- package/scripts/_cli-primitives.mjs +2 -0
- package/scripts/claude/generate-claude-assets.mjs +12 -2
- package/scripts/docs/validate-links.mjs +20 -11
- package/scripts/github/capture-review-threads.mjs +32 -14
- package/scripts/github/detect-checkpoint-evidence.mjs +28 -11
- package/scripts/github/detect-linked-issue-pr.mjs +22 -10
- package/scripts/github/manage-sub-issues.mjs +37 -16
- package/scripts/github/probe-copilot-review.mjs +24 -12
- package/scripts/github/ready-for-review.mjs +17 -8
- package/scripts/github/reconcile-draft-gate.mjs +22 -10
- package/scripts/github/reply-resolve-review-threads.mjs +34 -15
- package/scripts/github/request-copilot-review.mjs +28 -11
- package/scripts/github/resolve-tracker-local-spec.mjs +29 -12
- package/scripts/github/stage-reviewer-draft.mjs +32 -14
- package/scripts/github/upsert-checkpoint-verdict.mjs +49 -26
- package/scripts/github/write-gate-findings-log.mjs +41 -20
- package/scripts/loop/build-handoff-envelope.mjs +32 -14
- package/scripts/loop/conductor-monitor.mjs +25 -9
- package/scripts/loop/conductor.mjs +31 -12
- package/scripts/loop/copilot-pr-handoff.mjs +31 -14
- package/scripts/loop/debt-remediate.mjs +28 -11
- package/scripts/loop/detect-copilot-loop-state.mjs +29 -12
- package/scripts/loop/detect-copilot-session-activity.mjs +29 -12
- package/scripts/loop/detect-initial-copilot-pr-state.mjs +26 -10
- package/scripts/loop/detect-internal-only-pr.mjs +31 -13
- package/scripts/loop/detect-issue-refinement-artifact.mjs +29 -12
- package/scripts/loop/detect-pr-gate-coordination-state.mjs +26 -10
- package/scripts/loop/detect-reviewer-loop-state.mjs +35 -16
- package/scripts/loop/detect-stale-runner.mjs +32 -14
- package/scripts/loop/detect-tracker-pr-state.mjs +23 -8
- package/scripts/loop/info.mjs +28 -10
- package/scripts/loop/inspect-run-viewer/cli.mjs +44 -21
- package/scripts/loop/inspect-run.mjs +35 -16
- package/scripts/loop/outer-loop.mjs +35 -16
- package/scripts/loop/pr-runner-coordination.mjs +31 -12
- package/scripts/loop/pre-commit-branch-guard.mjs +26 -9
- package/scripts/loop/pre-flight-gate.mjs +25 -9
- package/scripts/loop/pre-pr-ready-gate.mjs +24 -8
- package/scripts/loop/pre-push-main-guard.mjs +19 -5
- package/scripts/loop/pre-write-remote-freshness-guard.mjs +23 -7
- package/scripts/loop/resolve-dev-loop-startup.mjs +29 -12
- package/scripts/loop/run-conductor-cycle.mjs +23 -8
- package/scripts/loop/run-refinement-audit.mjs +44 -22
- package/scripts/loop/run-watch-cycle.mjs +28 -12
- package/scripts/loop/steer-loop.mjs +122 -62
- package/scripts/loop/watch-initial-copilot-pr.mjs +28 -12
- package/scripts/projects/archive-done-items.mjs +410 -0
- package/scripts/projects/reorder-queue-item.mjs +334 -76
- package/scripts/refine/_refine-helpers.mjs +21 -9
- package/scripts/refine/verify.mjs +31 -13
- package/skills/dev-loop/SKILL.md +9 -5
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { buildParseError, formatCliError, isDirectCliRun, parseJsonText } from "../_core-helpers.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { requireTokenValue, runChild } from "../_cli-primitives.mjs";
|
|
5
5
|
import { parseRepoSlug } from "@dev-loops/core/github/repo-slug";
|
|
6
|
+
import { parseArgs } from "node:util";
|
|
6
7
|
import {
|
|
7
8
|
detectIssueRefinementArtifact,
|
|
8
9
|
REFINEMENT_SOURCE,
|
|
@@ -33,36 +34,52 @@ Error output (stderr, JSON):
|
|
|
33
34
|
{ "ok": false, "error": "...", "usage": "..." }`.trim();
|
|
34
35
|
const parseError = buildParseError(USAGE);
|
|
35
36
|
export function parseDetectIssueRefinementArtifactCliArgs(argv) {
|
|
36
|
-
const args = [...argv];
|
|
37
37
|
const options = {
|
|
38
38
|
help: false,
|
|
39
39
|
repo: undefined,
|
|
40
40
|
issue: undefined,
|
|
41
41
|
input: undefined,
|
|
42
42
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
const { tokens } = parseArgs({
|
|
44
|
+
args: [...argv],
|
|
45
|
+
options: {
|
|
46
|
+
help: { type: "boolean", short: "h" },
|
|
47
|
+
repo: { type: "string" },
|
|
48
|
+
issue: { type: "string" },
|
|
49
|
+
input: { type: "string" },
|
|
50
|
+
},
|
|
51
|
+
allowPositionals: true,
|
|
52
|
+
strict: false,
|
|
53
|
+
tokens: true,
|
|
54
|
+
});
|
|
55
|
+
for (const token of tokens) {
|
|
56
|
+
if (token.kind === "positional") {
|
|
57
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
58
|
+
}
|
|
59
|
+
if (token.kind !== "option") {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (token.name === "help") {
|
|
46
63
|
options.help = true;
|
|
47
64
|
return options;
|
|
48
65
|
}
|
|
49
|
-
if (token === "
|
|
50
|
-
options.repo =
|
|
66
|
+
if (token.name === "repo") {
|
|
67
|
+
options.repo = requireTokenValue(token, parseError).trim();
|
|
51
68
|
continue;
|
|
52
69
|
}
|
|
53
|
-
if (token === "
|
|
54
|
-
const value =
|
|
70
|
+
if (token.name === "issue") {
|
|
71
|
+
const value = requireTokenValue(token, parseError);
|
|
55
72
|
if (!/^\d+$/.test(value) || Number(value) === 0) {
|
|
56
73
|
throw parseError("--issue must be a positive integer");
|
|
57
74
|
}
|
|
58
75
|
options.issue = Number(value);
|
|
59
76
|
continue;
|
|
60
77
|
}
|
|
61
|
-
if (token === "
|
|
62
|
-
options.input =
|
|
78
|
+
if (token.name === "input") {
|
|
79
|
+
options.input = requireTokenValue(token, parseError).trim();
|
|
63
80
|
continue;
|
|
64
81
|
}
|
|
65
|
-
throw parseError(`Unknown argument: ${token}`);
|
|
82
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
66
83
|
}
|
|
67
84
|
const hasInput = typeof options.input === "string" && options.input.length > 0;
|
|
68
85
|
const hasRemote = typeof options.repo === "string" && options.repo.length > 0 && Number.isInteger(options.issue);
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
parseReviewThreads,
|
|
12
12
|
summarizeCopilotReviews,
|
|
13
13
|
} from "../_core-helpers.mjs";
|
|
14
|
-
import { parsePrNumber,
|
|
14
|
+
import { parsePrNumber, requireTokenValue, runChild } from "../_cli-primitives.mjs";
|
|
15
15
|
import { loadDevLoopConfig, resolveGateConfig, resolveRefinementConfig, resolveWorkflowConfig } from "@dev-loops/core/config";
|
|
16
16
|
import { parseRepoSlug } from "@dev-loops/core/github/repo-slug";
|
|
17
17
|
import { buildSnapshotFromPrFacts, interpretLoopState, summarizeLoopInterpretation } from "@dev-loops/core/loop/copilot-loop-state";
|
|
@@ -19,6 +19,7 @@ import { evaluatePrGateCoordination, PR_CHECKPOINT, PR_CHECKPOINT_ACTION } from
|
|
|
19
19
|
import { shouldGuardCopilotReviewRequest } from "@dev-loops/core/loop/pr-gate-coordination";
|
|
20
20
|
import { fetchGithubReviewThreadsPayload } from "../github/capture-review-threads.mjs";
|
|
21
21
|
import { detectCheckpointEvidence } from "../github/detect-checkpoint-evidence.mjs";
|
|
22
|
+
import { parseArgs } from "node:util";
|
|
22
23
|
const UNMERGED_GIT_STATUS_CODES = new Set(["DD", "AU", "UD", "UA", "DU", "AA", "UU"]);
|
|
23
24
|
const USAGE = `Usage: detect-pr-gate-coordination-state.mjs --repo <owner/name> --pr <number>
|
|
24
25
|
Determine which PR gate/transition is legal next for a pull request.
|
|
@@ -70,27 +71,42 @@ Exit codes:
|
|
|
70
71
|
1 Argument error or gh/runtime failure`.trim();
|
|
71
72
|
const parseError = buildParseError(USAGE);
|
|
72
73
|
export function parseDetectPrGateCoordinationCliArgs(argv) {
|
|
73
|
-
const args = [...argv];
|
|
74
74
|
const options = {
|
|
75
75
|
help: false,
|
|
76
76
|
repo: undefined,
|
|
77
77
|
pr: undefined,
|
|
78
78
|
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
const { tokens } = parseArgs({
|
|
80
|
+
args: [...argv],
|
|
81
|
+
options: {
|
|
82
|
+
help: { type: "boolean", short: "h" },
|
|
83
|
+
repo: { type: "string" },
|
|
84
|
+
pr: { type: "string" },
|
|
85
|
+
},
|
|
86
|
+
allowPositionals: true,
|
|
87
|
+
strict: false,
|
|
88
|
+
tokens: true,
|
|
89
|
+
});
|
|
90
|
+
for (const token of tokens) {
|
|
91
|
+
if (token.kind === "positional") {
|
|
92
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
93
|
+
}
|
|
94
|
+
if (token.kind !== "option") {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (token.name === "help") {
|
|
82
98
|
options.help = true;
|
|
83
99
|
return options;
|
|
84
100
|
}
|
|
85
|
-
if (token === "
|
|
86
|
-
options.repo =
|
|
101
|
+
if (token.name === "repo") {
|
|
102
|
+
options.repo = requireTokenValue(token, parseError).trim();
|
|
87
103
|
continue;
|
|
88
104
|
}
|
|
89
|
-
if (token === "
|
|
90
|
-
options.pr = parsePrNumber(
|
|
105
|
+
if (token.name === "pr") {
|
|
106
|
+
options.pr = parsePrNumber(requireTokenValue(token, parseError), parseError);
|
|
91
107
|
continue;
|
|
92
108
|
}
|
|
93
|
-
throw parseError(`Unknown argument: ${token}`);
|
|
109
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
94
110
|
}
|
|
95
111
|
if (options.repo === undefined || options.pr === undefined) {
|
|
96
112
|
throw parseError("detect-pr-gate-coordination-state requires both --repo <owner/name> and --pr <number>");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
|
-
import {
|
|
3
|
+
import { parseArgs } from "node:util";
|
|
4
|
+
import { parsePrNumber, requireTokenValue, runChild } from "../_cli-primitives.mjs";
|
|
4
5
|
import { formatCliError, isDirectCliRun, parseJsonText } from "../_core-helpers.mjs";
|
|
5
6
|
import { parseRepoSlug } from "@dev-loops/core/github/repo-slug";
|
|
6
7
|
import {
|
|
@@ -26,7 +27,20 @@ function parseBool(value, flag) {
|
|
|
26
27
|
throw new Error(`${flag} must be true or false`);
|
|
27
28
|
}
|
|
28
29
|
export function parseDetectReviewerCliArgs(argv) {
|
|
29
|
-
const
|
|
30
|
+
const { tokens } = parseArgs({
|
|
31
|
+
args: [...argv],
|
|
32
|
+
options: {
|
|
33
|
+
help: { type: "boolean", short: "h" },
|
|
34
|
+
input: { type: "string" },
|
|
35
|
+
repo: { type: "string" },
|
|
36
|
+
pr: { type: "string" },
|
|
37
|
+
"review-requested": { type: "string" },
|
|
38
|
+
"local-state": { type: "string" },
|
|
39
|
+
},
|
|
40
|
+
allowPositionals: true,
|
|
41
|
+
strict: false,
|
|
42
|
+
tokens: true,
|
|
43
|
+
});
|
|
30
44
|
const options = {
|
|
31
45
|
inputPath: undefined,
|
|
32
46
|
repo: undefined,
|
|
@@ -35,36 +49,41 @@ export function parseDetectReviewerCliArgs(argv) {
|
|
|
35
49
|
localStatePath: undefined,
|
|
36
50
|
help: false,
|
|
37
51
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
52
|
+
for (const token of tokens) {
|
|
53
|
+
if (token.kind === "positional") {
|
|
54
|
+
throw new Error(`Unknown argument: ${token.value}`);
|
|
55
|
+
}
|
|
56
|
+
if (token.kind !== "option") {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (token.name === "help") {
|
|
41
60
|
options.help = true;
|
|
42
61
|
return options;
|
|
43
62
|
}
|
|
44
|
-
if (token === "
|
|
45
|
-
options.inputPath =
|
|
63
|
+
if (token.name === "input") {
|
|
64
|
+
options.inputPath = requireTokenValue(token);
|
|
46
65
|
continue;
|
|
47
66
|
}
|
|
48
|
-
if (token === "
|
|
49
|
-
options.repo =
|
|
67
|
+
if (token.name === "repo") {
|
|
68
|
+
options.repo = requireTokenValue(token).trim();
|
|
50
69
|
continue;
|
|
51
70
|
}
|
|
52
|
-
if (token === "
|
|
53
|
-
options.pr = parsePrNumber(
|
|
71
|
+
if (token.name === "pr") {
|
|
72
|
+
options.pr = parsePrNumber(requireTokenValue(token));
|
|
54
73
|
continue;
|
|
55
74
|
}
|
|
56
|
-
if (token === "
|
|
75
|
+
if (token.name === "review-requested") {
|
|
57
76
|
options.reviewRequestedOverride = parseBool(
|
|
58
|
-
|
|
77
|
+
requireTokenValue(token),
|
|
59
78
|
"--review-requested",
|
|
60
79
|
);
|
|
61
80
|
continue;
|
|
62
81
|
}
|
|
63
|
-
if (token === "
|
|
64
|
-
options.localStatePath =
|
|
82
|
+
if (token.name === "local-state") {
|
|
83
|
+
options.localStatePath = requireTokenValue(token);
|
|
65
84
|
continue;
|
|
66
85
|
}
|
|
67
|
-
throw new Error(`Unknown argument: ${token}`);
|
|
86
|
+
throw new Error(`Unknown argument: ${token.rawName}`);
|
|
68
87
|
}
|
|
69
88
|
if (options.inputPath !== undefined) {
|
|
70
89
|
if (options.repo !== undefined || options.pr !== undefined) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
import { buildParseError, formatCliError, isDirectCliRun } from "../_core-helpers.mjs";
|
|
4
|
-
import { parsePrNumber,
|
|
4
|
+
import { parsePrNumber, requireTokenValue } from "../_cli-primitives.mjs";
|
|
5
5
|
import { parseRepoSlug } from "@dev-loops/core/github/repo-slug";
|
|
6
6
|
import { resolveRunId as resolveEnvRunId } from "@dev-loops/core/loop/run-context";
|
|
7
|
+
import { parseArgs } from "node:util";
|
|
7
8
|
import {
|
|
8
9
|
detectStaleRunner,
|
|
9
10
|
STALE_RUNNER_ERROR,
|
|
@@ -50,7 +51,6 @@ Exit codes:
|
|
|
50
51
|
1 Argument error or stale/exit-signal condition detected`.trim();
|
|
51
52
|
const parseError = buildParseError(USAGE);
|
|
52
53
|
function parseCliArgs(argv) {
|
|
53
|
-
const args = [...argv];
|
|
54
54
|
const options = {
|
|
55
55
|
help: false,
|
|
56
56
|
repo: undefined,
|
|
@@ -58,22 +58,40 @@ function parseCliArgs(argv) {
|
|
|
58
58
|
staleRunnerMaxAgeMs: undefined,
|
|
59
59
|
runId: undefined,
|
|
60
60
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
const { tokens } = parseArgs({
|
|
62
|
+
args: [...argv],
|
|
63
|
+
options: {
|
|
64
|
+
help: { type: "boolean", short: "h" },
|
|
65
|
+
repo: { type: "string" },
|
|
66
|
+
pr: { type: "string" },
|
|
67
|
+
"stale-runner-max-age-ms": { type: "string" },
|
|
68
|
+
"run-id": { type: "string" },
|
|
69
|
+
},
|
|
70
|
+
allowPositionals: true,
|
|
71
|
+
strict: false,
|
|
72
|
+
tokens: true,
|
|
73
|
+
});
|
|
74
|
+
for (const token of tokens) {
|
|
75
|
+
if (token.kind === "positional") {
|
|
76
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
77
|
+
}
|
|
78
|
+
if (token.kind !== "option") {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (token.name === "help") {
|
|
64
82
|
options.help = true;
|
|
65
83
|
return options;
|
|
66
84
|
}
|
|
67
|
-
if (token === "
|
|
68
|
-
options.repo =
|
|
85
|
+
if (token.name === "repo") {
|
|
86
|
+
options.repo = requireTokenValue(token, parseError).trim();
|
|
69
87
|
continue;
|
|
70
88
|
}
|
|
71
|
-
if (token === "
|
|
72
|
-
options.pr = parsePrNumber(
|
|
89
|
+
if (token.name === "pr") {
|
|
90
|
+
options.pr = parsePrNumber(requireTokenValue(token, parseError), parseError);
|
|
73
91
|
continue;
|
|
74
92
|
}
|
|
75
|
-
if (token === "
|
|
76
|
-
const raw =
|
|
93
|
+
if (token.name === "stale-runner-max-age-ms") {
|
|
94
|
+
const raw = requireTokenValue(token, parseError).trim();
|
|
77
95
|
const parsed = Number(raw);
|
|
78
96
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
79
97
|
throw parseError(`--stale-runner-max-age-ms must be a positive integer (ms), got: ${raw}`);
|
|
@@ -81,11 +99,11 @@ function parseCliArgs(argv) {
|
|
|
81
99
|
options.staleRunnerMaxAgeMs = Math.floor(parsed);
|
|
82
100
|
continue;
|
|
83
101
|
}
|
|
84
|
-
if (token === "
|
|
85
|
-
options.runId =
|
|
102
|
+
if (token.name === "run-id") {
|
|
103
|
+
options.runId = requireTokenValue(token, parseError).trim();
|
|
86
104
|
continue;
|
|
87
105
|
}
|
|
88
|
-
throw parseError(`Unknown argument: ${token}`);
|
|
106
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
89
107
|
}
|
|
90
108
|
if (options.repo === undefined || options.pr === undefined) {
|
|
91
109
|
throw parseError("detect-stale-runner requires both --repo <owner/name> and --pr <number>");
|
|
@@ -3,7 +3,8 @@ import { readFile } from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { buildParseError, formatCliError, parseJsonText } from "../_core-helpers.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { requireTokenValue } from "../_cli-primitives.mjs";
|
|
7
|
+
import { parseArgs } from "node:util";
|
|
7
8
|
import {
|
|
8
9
|
interpretTrackerPrState,
|
|
9
10
|
normalizeTrackerPrSnapshot,
|
|
@@ -51,22 +52,36 @@ Exit codes:
|
|
|
51
52
|
1 Argument error or runtime failure`.trim();
|
|
52
53
|
const parseError = buildParseError(USAGE);
|
|
53
54
|
export function parseDetectTrackerPrCliArgs(argv) {
|
|
54
|
-
const args = [...argv];
|
|
55
55
|
const options = {
|
|
56
56
|
help: false,
|
|
57
57
|
inputPath: undefined,
|
|
58
58
|
};
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
const { tokens } = parseArgs({
|
|
60
|
+
args: [...argv],
|
|
61
|
+
options: {
|
|
62
|
+
help: { type: "boolean", short: "h" },
|
|
63
|
+
input: { type: "string" },
|
|
64
|
+
},
|
|
65
|
+
allowPositionals: true,
|
|
66
|
+
strict: false,
|
|
67
|
+
tokens: true,
|
|
68
|
+
});
|
|
69
|
+
for (const token of tokens) {
|
|
70
|
+
if (token.kind === "positional") {
|
|
71
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
72
|
+
}
|
|
73
|
+
if (token.kind !== "option") {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (token.name === "help") {
|
|
62
77
|
options.help = true;
|
|
63
78
|
return options;
|
|
64
79
|
}
|
|
65
|
-
if (token === "
|
|
66
|
-
options.inputPath =
|
|
80
|
+
if (token.name === "input") {
|
|
81
|
+
options.inputPath = requireTokenValue(token, parseError);
|
|
67
82
|
continue;
|
|
68
83
|
}
|
|
69
|
-
throw parseError(`Unknown argument: ${token}`);
|
|
84
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
70
85
|
}
|
|
71
86
|
if (options.inputPath === undefined) {
|
|
72
87
|
throw parseError("--input <path> is required");
|
package/scripts/loop/info.mjs
CHANGED
|
@@ -3,9 +3,10 @@ import { execFileSync } from "node:child_process";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { buildParseError, formatCliError, isDirectCliRun } from "../_core-helpers.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { requireTokenValue, parsePositiveInteger } from "../_cli-primitives.mjs";
|
|
7
7
|
import { detectRepoSlug, normalizeRepoSlug } from "@dev-loops/core/github/repo-slug";
|
|
8
8
|
import { runContextEnv } from "@dev-loops/core/loop/run-context";
|
|
9
|
+
import { parseArgs } from "node:util";
|
|
9
10
|
|
|
10
11
|
// REPO_ROOT resolves to the git repo root (scripts/loop/info.mjs → scripts/ → repo/)
|
|
11
12
|
const REPO_ROOT = path.resolve(fileURLToPath(new URL("..", import.meta.url)), "..");
|
|
@@ -27,16 +28,33 @@ Exit codes:
|
|
|
27
28
|
const parseError = buildParseError(USAGE);
|
|
28
29
|
|
|
29
30
|
function parseCliArgs(argv) {
|
|
30
|
-
const args = [...argv];
|
|
31
31
|
const opts = { help: false, issue: undefined, pr: undefined, json: false, repo: undefined };
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
const { tokens } = parseArgs({
|
|
33
|
+
args: [...argv],
|
|
34
|
+
options: {
|
|
35
|
+
help: { type: "boolean", short: "h" },
|
|
36
|
+
json: { type: "boolean" },
|
|
37
|
+
issue: { type: "string" },
|
|
38
|
+
pr: { type: "string" },
|
|
39
|
+
repo: { type: "string" },
|
|
40
|
+
},
|
|
41
|
+
allowPositionals: true,
|
|
42
|
+
strict: false,
|
|
43
|
+
tokens: true,
|
|
44
|
+
});
|
|
45
|
+
for (const token of tokens) {
|
|
46
|
+
if (token.kind === "positional") {
|
|
47
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
48
|
+
}
|
|
49
|
+
if (token.kind !== "option") {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (token.name === "help") { opts.help = true; return opts; }
|
|
53
|
+
if (token.name === "json") { opts.json = true; continue; }
|
|
54
|
+
if (token.name === "issue") { opts.issue = parsePositiveInteger(requireTokenValue(token, parseError), "--issue", parseError); continue; }
|
|
55
|
+
if (token.name === "pr") { opts.pr = parsePositiveInteger(requireTokenValue(token, parseError), "--pr", parseError); continue; }
|
|
56
|
+
if (token.name === "repo") { opts.repo = requireTokenValue(token, parseError); continue; }
|
|
57
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
40
58
|
}
|
|
41
59
|
const modes = [opts.issue, opts.pr].filter(v => v !== undefined).length;
|
|
42
60
|
if (modes > 1) throw parseError("--issue and --pr are mutually exclusive");
|
|
@@ -3,7 +3,8 @@ import {
|
|
|
3
3
|
DEFAULT_PORT,
|
|
4
4
|
USAGE,
|
|
5
5
|
} from "./constants.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { parseArgs } from "node:util";
|
|
7
|
+
import { requireTokenValue } from "../../_cli-primitives.mjs";
|
|
7
8
|
import { normalizeInspectionTarget } from "../_inspect-run-viewer-adapter.mjs";
|
|
8
9
|
|
|
9
10
|
export function parseInspectRunViewerCliError(message) {
|
|
@@ -47,7 +48,6 @@ export function normalizeCliRepoOption(rawRepo) {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
export function parseInspectRunViewerCliArgs(argv) {
|
|
50
|
-
const args = [...argv];
|
|
51
51
|
const options = {
|
|
52
52
|
help: false,
|
|
53
53
|
repo: undefined,
|
|
@@ -60,48 +60,71 @@ export function parseInspectRunViewerCliArgs(argv) {
|
|
|
60
60
|
restart: false,
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
const { tokens } = parseArgs({
|
|
64
|
+
args: [...argv],
|
|
65
|
+
options: {
|
|
66
|
+
help: { type: "boolean", short: "h" },
|
|
67
|
+
repo: { type: "string" },
|
|
68
|
+
pr: { type: "boolean" },
|
|
69
|
+
host: { type: "string" },
|
|
70
|
+
port: { type: "string" },
|
|
71
|
+
"allow-non-localhost": { type: "boolean" },
|
|
72
|
+
restart: { type: "boolean" },
|
|
73
|
+
"steering-state-file": { type: "string" },
|
|
74
|
+
"copilot-input": { type: "string" },
|
|
75
|
+
"reviewer-input": { type: "string" },
|
|
76
|
+
},
|
|
77
|
+
allowPositionals: true,
|
|
78
|
+
strict: false,
|
|
79
|
+
tokens: true,
|
|
80
|
+
});
|
|
81
|
+
for (const token of tokens) {
|
|
82
|
+
if (token.kind === "positional") {
|
|
83
|
+
throw parseInspectRunViewerCliError(`Unknown argument: ${token.value}`);
|
|
84
|
+
}
|
|
85
|
+
if (token.kind !== "option") {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (token.name === "help") {
|
|
66
89
|
options.help = true;
|
|
67
90
|
return options;
|
|
68
91
|
}
|
|
69
|
-
if (token === "
|
|
70
|
-
options.repo =
|
|
92
|
+
if (token.name === "repo") {
|
|
93
|
+
options.repo = requireTokenValue(token, parseInspectRunViewerCliError);
|
|
71
94
|
continue;
|
|
72
95
|
}
|
|
73
|
-
if (token === "
|
|
96
|
+
if (token.name === "pr") {
|
|
74
97
|
throw parseInspectRunViewerCliError("--pr is no longer supported on the CLI; choose a PR with ?pr=<number> in the viewer URL");
|
|
75
98
|
}
|
|
76
|
-
if (token === "
|
|
77
|
-
options.host = parseHost(
|
|
99
|
+
if (token.name === "host") {
|
|
100
|
+
options.host = parseHost(requireTokenValue(token, parseInspectRunViewerCliError));
|
|
78
101
|
continue;
|
|
79
102
|
}
|
|
80
|
-
if (token === "
|
|
81
|
-
options.port = parsePort(
|
|
103
|
+
if (token.name === "port") {
|
|
104
|
+
options.port = parsePort(requireTokenValue(token, parseInspectRunViewerCliError));
|
|
82
105
|
continue;
|
|
83
106
|
}
|
|
84
|
-
if (token === "
|
|
107
|
+
if (token.name === "allow-non-localhost") {
|
|
85
108
|
options.allowNonLocalhost = true;
|
|
86
109
|
continue;
|
|
87
110
|
}
|
|
88
|
-
if (token === "
|
|
111
|
+
if (token.name === "restart") {
|
|
89
112
|
options.restart = true;
|
|
90
113
|
continue;
|
|
91
114
|
}
|
|
92
|
-
if (token === "
|
|
93
|
-
options.steeringStateFile =
|
|
115
|
+
if (token.name === "steering-state-file") {
|
|
116
|
+
options.steeringStateFile = requireTokenValue(token, parseInspectRunViewerCliError);
|
|
94
117
|
continue;
|
|
95
118
|
}
|
|
96
|
-
if (token === "
|
|
97
|
-
options.copilotInputPath =
|
|
119
|
+
if (token.name === "copilot-input") {
|
|
120
|
+
options.copilotInputPath = requireTokenValue(token, parseInspectRunViewerCliError);
|
|
98
121
|
continue;
|
|
99
122
|
}
|
|
100
|
-
if (token === "
|
|
101
|
-
options.reviewerInputPath =
|
|
123
|
+
if (token.name === "reviewer-input") {
|
|
124
|
+
options.reviewerInputPath = requireTokenValue(token, parseInspectRunViewerCliError);
|
|
102
125
|
continue;
|
|
103
126
|
}
|
|
104
|
-
throw parseInspectRunViewerCliError(`Unknown argument: ${token}`);
|
|
127
|
+
throw parseInspectRunViewerCliError(`Unknown argument: ${token.rawName}`);
|
|
105
128
|
}
|
|
106
129
|
|
|
107
130
|
if (!options.help) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { parsePrNumber,
|
|
5
|
+
import { parsePrNumber, requireTokenValue, runChild } from "../_cli-primitives.mjs";
|
|
6
6
|
import { buildParseError, formatCliError, parseJsonText, parseReviewThreads } from "../_core-helpers.mjs";
|
|
7
7
|
import { fetchGithubReviewThreadsPayload } from "../github/capture-review-threads.mjs";
|
|
8
8
|
import { parseRepoSlug } from "@dev-loops/core/github/repo-slug";
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
STEERING_KIND,
|
|
23
23
|
} from "@dev-loops/core/loop/steering";
|
|
24
24
|
import { validateSteeringStateTarget } from "./_steering-state-file.mjs";
|
|
25
|
+
import { parseArgs } from "node:util";
|
|
25
26
|
const USAGE = `Usage: inspect-run.mjs --repo <owner/name> --pr <number>
|
|
26
27
|
Read-only run inspection for the Copilot PR outer-loop family.
|
|
27
28
|
Produces a single JSON snapshot describing the current state of one
|
|
@@ -72,7 +73,6 @@ Exit codes:
|
|
|
72
73
|
1 Argument error or unexpected runtime failure`.trim();
|
|
73
74
|
const parseError = buildParseError(USAGE);
|
|
74
75
|
export function parseInspectRunCliArgs(argv) {
|
|
75
|
-
const args = [...argv];
|
|
76
76
|
const options = {
|
|
77
77
|
help: false,
|
|
78
78
|
repo: undefined,
|
|
@@ -81,33 +81,52 @@ export function parseInspectRunCliArgs(argv) {
|
|
|
81
81
|
copilotInputPath: undefined,
|
|
82
82
|
reviewerInputPath: undefined,
|
|
83
83
|
};
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
const { tokens } = parseArgs({
|
|
85
|
+
args: [...argv],
|
|
86
|
+
options: {
|
|
87
|
+
help: { type: "boolean", short: "h" },
|
|
88
|
+
repo: { type: "string" },
|
|
89
|
+
pr: { type: "string" },
|
|
90
|
+
"steering-state-file": { type: "string" },
|
|
91
|
+
"copilot-input": { type: "string" },
|
|
92
|
+
"reviewer-input": { type: "string" },
|
|
93
|
+
},
|
|
94
|
+
allowPositionals: true,
|
|
95
|
+
strict: false,
|
|
96
|
+
tokens: true,
|
|
97
|
+
});
|
|
98
|
+
for (const token of tokens) {
|
|
99
|
+
if (token.kind === "positional") {
|
|
100
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
101
|
+
}
|
|
102
|
+
if (token.kind !== "option") {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (token.name === "help") {
|
|
87
106
|
options.help = true;
|
|
88
107
|
return options;
|
|
89
108
|
}
|
|
90
|
-
if (token === "
|
|
91
|
-
options.repo =
|
|
109
|
+
if (token.name === "repo") {
|
|
110
|
+
options.repo = requireTokenValue(token, parseError).trim();
|
|
92
111
|
continue;
|
|
93
112
|
}
|
|
94
|
-
if (token === "
|
|
95
|
-
options.pr = parsePrNumber(
|
|
113
|
+
if (token.name === "pr") {
|
|
114
|
+
options.pr = parsePrNumber(requireTokenValue(token, parseError), parseError);
|
|
96
115
|
continue;
|
|
97
116
|
}
|
|
98
|
-
if (token === "
|
|
99
|
-
options.steeringStateFile =
|
|
117
|
+
if (token.name === "steering-state-file") {
|
|
118
|
+
options.steeringStateFile = requireTokenValue(token, parseError);
|
|
100
119
|
continue;
|
|
101
120
|
}
|
|
102
|
-
if (token === "
|
|
103
|
-
options.copilotInputPath =
|
|
121
|
+
if (token.name === "copilot-input") {
|
|
122
|
+
options.copilotInputPath = requireTokenValue(token, parseError);
|
|
104
123
|
continue;
|
|
105
124
|
}
|
|
106
|
-
if (token === "
|
|
107
|
-
options.reviewerInputPath =
|
|
125
|
+
if (token.name === "reviewer-input") {
|
|
126
|
+
options.reviewerInputPath = requireTokenValue(token, parseError);
|
|
108
127
|
continue;
|
|
109
128
|
}
|
|
110
|
-
throw parseError(`Unknown argument: ${token}`);
|
|
129
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
111
130
|
}
|
|
112
131
|
if (!options.help) {
|
|
113
132
|
if (options.repo === undefined || options.pr === undefined) {
|