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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { mkdir, writeFile } 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 } from "../_core-helpers.mjs";
|
|
7
7
|
import { parseRepoSlug } from "@dev-loops/core/github/repo-slug";
|
|
8
8
|
import {
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
resolveEffectiveAsyncStartMode,
|
|
26
26
|
} from "@dev-loops/core/loop/async-start-contract";
|
|
27
27
|
import { loadDevLoopConfig, resolveConductorModel, resolveAutonomyStopAt, resolveWorkflowConfig } from "@dev-loops/core/config";
|
|
28
|
+
import { parseArgs } from "node:util";
|
|
28
29
|
const USAGE = `Usage: outer-loop.mjs --repo <owner/name> --pr <number>
|
|
29
30
|
Thin outer-loop wrapper for the Copilot PR remediation loop.
|
|
30
31
|
Detects current PR state from both the Copilot inner loop and the reviewer
|
|
@@ -88,7 +89,6 @@ Exit codes:
|
|
|
88
89
|
1 Argument error, gh/git failure, or indeterminate state`.trim();
|
|
89
90
|
const parseError = buildParseError(USAGE);
|
|
90
91
|
export function parseOuterLoopCliArgs(argv) {
|
|
91
|
-
const args = [...argv];
|
|
92
92
|
const options = {
|
|
93
93
|
help: false,
|
|
94
94
|
repo: undefined,
|
|
@@ -97,33 +97,52 @@ export function parseOuterLoopCliArgs(argv) {
|
|
|
97
97
|
copilotInputPath: undefined,
|
|
98
98
|
reviewerInputPath: undefined,
|
|
99
99
|
};
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
const { tokens } = parseArgs({
|
|
101
|
+
args: [...argv],
|
|
102
|
+
options: {
|
|
103
|
+
help: { type: "boolean", short: "h" },
|
|
104
|
+
repo: { type: "string" },
|
|
105
|
+
pr: { type: "string" },
|
|
106
|
+
"checkpoint-dir": { type: "string" },
|
|
107
|
+
"copilot-input": { type: "string" },
|
|
108
|
+
"reviewer-input": { type: "string" },
|
|
109
|
+
},
|
|
110
|
+
allowPositionals: true,
|
|
111
|
+
strict: false,
|
|
112
|
+
tokens: true,
|
|
113
|
+
});
|
|
114
|
+
for (const token of tokens) {
|
|
115
|
+
if (token.kind === "positional") {
|
|
116
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
117
|
+
}
|
|
118
|
+
if (token.kind !== "option") {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (token.name === "help") {
|
|
103
122
|
options.help = true;
|
|
104
123
|
return options;
|
|
105
124
|
}
|
|
106
|
-
if (token === "
|
|
107
|
-
options.repo =
|
|
125
|
+
if (token.name === "repo") {
|
|
126
|
+
options.repo = requireTokenValue(token, parseError).trim();
|
|
108
127
|
continue;
|
|
109
128
|
}
|
|
110
|
-
if (token === "
|
|
111
|
-
options.pr = parsePrNumber(
|
|
129
|
+
if (token.name === "pr") {
|
|
130
|
+
options.pr = parsePrNumber(requireTokenValue(token, parseError), parseError);
|
|
112
131
|
continue;
|
|
113
132
|
}
|
|
114
|
-
if (token === "
|
|
115
|
-
options.checkpointDir =
|
|
133
|
+
if (token.name === "checkpoint-dir") {
|
|
134
|
+
options.checkpointDir = requireTokenValue(token, parseError);
|
|
116
135
|
continue;
|
|
117
136
|
}
|
|
118
|
-
if (token === "
|
|
119
|
-
options.copilotInputPath =
|
|
137
|
+
if (token.name === "copilot-input") {
|
|
138
|
+
options.copilotInputPath = requireTokenValue(token, parseError);
|
|
120
139
|
continue;
|
|
121
140
|
}
|
|
122
|
-
if (token === "
|
|
123
|
-
options.reviewerInputPath =
|
|
141
|
+
if (token.name === "reviewer-input") {
|
|
142
|
+
options.reviewerInputPath = requireTokenValue(token, parseError);
|
|
124
143
|
continue;
|
|
125
144
|
}
|
|
126
|
-
throw parseError(`Unknown argument: ${token}`);
|
|
145
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
127
146
|
}
|
|
128
147
|
if (!options.help) {
|
|
129
148
|
if (options.repo === undefined || options.pr === undefined) {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import process from "node:process";
|
|
3
|
+
import { parseArgs } from "node:util";
|
|
3
4
|
import { buildParseError, formatCliError, isDirectCliRun } from "../_core-helpers.mjs";
|
|
4
|
-
import { parsePrNumber,
|
|
5
|
+
import { parsePrNumber, requireTokenValue } from "../_cli-primitives.mjs";
|
|
5
6
|
import { parseRepoSlug } from "@dev-loops/core/github/repo-slug";
|
|
6
7
|
import { resolveRunId as resolveEnvRunId } from "@dev-loops/core/loop/run-context";
|
|
7
8
|
import {
|
|
@@ -42,29 +43,47 @@ function parseCliArgs(argv) {
|
|
|
42
43
|
return options;
|
|
43
44
|
}
|
|
44
45
|
options.command = command;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
const { tokens } = parseArgs({
|
|
47
|
+
args,
|
|
48
|
+
options: {
|
|
49
|
+
help: { type: "boolean", short: "h" },
|
|
50
|
+
repo: { type: "string" },
|
|
51
|
+
pr: { type: "string" },
|
|
52
|
+
"run-id": { type: "string" },
|
|
53
|
+
"require-existing": { type: "boolean" },
|
|
54
|
+
},
|
|
55
|
+
allowPositionals: true,
|
|
56
|
+
strict: false,
|
|
57
|
+
tokens: true,
|
|
58
|
+
});
|
|
59
|
+
for (const token of tokens) {
|
|
60
|
+
if (token.kind === "positional") {
|
|
61
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
62
|
+
}
|
|
63
|
+
if (token.kind !== "option") {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (token.name === "help") {
|
|
48
67
|
options.help = true;
|
|
49
68
|
return options;
|
|
50
69
|
}
|
|
51
|
-
if (token === "
|
|
52
|
-
options.repo =
|
|
70
|
+
if (token.name === "repo") {
|
|
71
|
+
options.repo = requireTokenValue(token, parseError).trim();
|
|
53
72
|
continue;
|
|
54
73
|
}
|
|
55
|
-
if (token === "
|
|
56
|
-
options.pr = parsePrNumber(
|
|
74
|
+
if (token.name === "pr") {
|
|
75
|
+
options.pr = parsePrNumber(requireTokenValue(token, parseError), parseError);
|
|
57
76
|
continue;
|
|
58
77
|
}
|
|
59
|
-
if (token === "
|
|
60
|
-
options.runId =
|
|
78
|
+
if (token.name === "run-id") {
|
|
79
|
+
options.runId = requireTokenValue(token, parseError).trim();
|
|
61
80
|
continue;
|
|
62
81
|
}
|
|
63
|
-
if (token === "
|
|
82
|
+
if (token.name === "require-existing") {
|
|
64
83
|
options.requireExisting = true;
|
|
65
84
|
continue;
|
|
66
85
|
}
|
|
67
|
-
throw parseError(`Unknown argument: ${token}`);
|
|
86
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
68
87
|
}
|
|
69
88
|
const validCommands = new Set(["status", "claim", "takeover", "assert", "release"]);
|
|
70
89
|
if (!validCommands.has(options.command)) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { buildParseError, formatCliError, isDirectCliRun } from "../_core-helpers.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { requireTokenValue, runCommand } from "../_cli-primitives.mjs";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
4
5
|
import {
|
|
5
6
|
isUnderWorktreePath, parseMainWorktreePath, isMainCheckout,
|
|
6
7
|
} from "@dev-loops/core/loop/worktree-guard";
|
|
@@ -13,15 +14,31 @@ Verify the current git branch identity and/or worktree isolation before local co
|
|
|
13
14
|
const parseError = buildParseError(USAGE);
|
|
14
15
|
|
|
15
16
|
export function parseBranchGuardCliArgs(argv) {
|
|
16
|
-
const args = [...argv];
|
|
17
17
|
const options = { help: false, expectedBranch: undefined, requireWorktree: false, blockMainCheckout: false };
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
const { tokens } = parseArgs({
|
|
19
|
+
args: [...argv],
|
|
20
|
+
options: {
|
|
21
|
+
help: { type: "boolean", short: "h" },
|
|
22
|
+
"expected-branch": { type: "string" },
|
|
23
|
+
"require-worktree": { type: "boolean" },
|
|
24
|
+
"block-main-checkout": { type: "boolean" },
|
|
25
|
+
},
|
|
26
|
+
allowPositionals: true,
|
|
27
|
+
strict: false,
|
|
28
|
+
tokens: true,
|
|
29
|
+
});
|
|
30
|
+
for (const token of tokens) {
|
|
31
|
+
if (token.kind === "positional") {
|
|
32
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
33
|
+
}
|
|
34
|
+
if (token.kind !== "option") {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (token.name === "help") { options.help = true; return options; }
|
|
38
|
+
if (token.name === "expected-branch") { options.expectedBranch = requireTokenValue(token, parseError, { flagPattern: /^-/u }); continue; }
|
|
39
|
+
if (token.name === "require-worktree") { options.requireWorktree = true; continue; }
|
|
40
|
+
if (token.name === "block-main-checkout") { options.blockMainCheckout = true; continue; }
|
|
41
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
25
42
|
}
|
|
26
43
|
if (options.expectedBranch === undefined) { throw parseError("--expected-branch <name> is required"); }
|
|
27
44
|
return options;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execFileSync } from "node:child_process";
|
|
3
3
|
import { buildParseError, formatCliError, isDirectCliRun } from "../_core-helpers.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { requireTokenValue } from "../_cli-primitives.mjs";
|
|
5
5
|
import { createPiAdapter } from "@dev-loops/core/harness";
|
|
6
|
+
import { parseArgs } from "node:util";
|
|
6
7
|
import {
|
|
7
8
|
isUnderWorktreePath,
|
|
8
9
|
parseMainWorktreePath,
|
|
@@ -30,27 +31,42 @@ Bypass:
|
|
|
30
31
|
PI_PREFLIGHT_BYPASS=1 Skip all checks (for development/testing only).`.trim();
|
|
31
32
|
const parseError = buildParseError(USAGE);
|
|
32
33
|
export function parsePreFlightGateCliArgs(argv) {
|
|
33
|
-
const args = [...argv];
|
|
34
34
|
const options = {
|
|
35
35
|
help: false,
|
|
36
36
|
expectedBranch: undefined,
|
|
37
37
|
checkSubagents: false,
|
|
38
38
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
const { tokens } = parseArgs({
|
|
40
|
+
args: [...argv],
|
|
41
|
+
options: {
|
|
42
|
+
help: { type: "boolean", short: "h" },
|
|
43
|
+
"expected-branch": { type: "string" },
|
|
44
|
+
"check-subagents": { type: "boolean" },
|
|
45
|
+
},
|
|
46
|
+
allowPositionals: true,
|
|
47
|
+
strict: false,
|
|
48
|
+
tokens: true,
|
|
49
|
+
});
|
|
50
|
+
for (const token of tokens) {
|
|
51
|
+
if (token.kind === "positional") {
|
|
52
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
53
|
+
}
|
|
54
|
+
if (token.kind !== "option") {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (token.name === "help") {
|
|
42
58
|
options.help = true;
|
|
43
59
|
return options;
|
|
44
60
|
}
|
|
45
|
-
if (token === "
|
|
46
|
-
options.expectedBranch =
|
|
61
|
+
if (token.name === "expected-branch") {
|
|
62
|
+
options.expectedBranch = requireTokenValue(token, parseError, { flagPattern: /^-/u });
|
|
47
63
|
continue;
|
|
48
64
|
}
|
|
49
|
-
if (token === "
|
|
65
|
+
if (token.name === "check-subagents") {
|
|
50
66
|
options.checkSubagents = true;
|
|
51
67
|
continue;
|
|
52
68
|
}
|
|
53
|
-
throw parseError(`Unknown argument: ${token}`);
|
|
69
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
54
70
|
}
|
|
55
71
|
return options;
|
|
56
72
|
}
|
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
summarizeGateReviewComments,
|
|
8
8
|
summarizeGateReviewCommentMarkers,
|
|
9
9
|
} from "../_core-helpers.mjs";
|
|
10
|
-
import { parsePrNumber,
|
|
10
|
+
import { parsePrNumber, requireTokenValue, runChild } from "../_cli-primitives.mjs";
|
|
11
11
|
import { parseRepoSlug } from "@dev-loops/core/github/repo-slug";
|
|
12
|
+
import { parseArgs } from "node:util";
|
|
12
13
|
|
|
13
14
|
const USAGE = `Usage:
|
|
14
15
|
pre-pr-ready-gate.mjs --repo <owner/name> --pr <number>
|
|
@@ -38,14 +39,29 @@ const parseError = buildParseError(USAGE);
|
|
|
38
39
|
const PR_VIEW_QUERY = `query($owner:String!, $name:String!, $number:Int!) { repository(owner:$owner, name:$name) { pullRequest(number:$number) { id, isDraft, headRefOid, state } } }`;
|
|
39
40
|
|
|
40
41
|
export function parsePrePrReadyGateCliArgs(argv) {
|
|
41
|
-
const args = [...argv];
|
|
42
42
|
const options = { help: false, repo: undefined, pr: undefined };
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
const { tokens } = parseArgs({
|
|
44
|
+
args: [...argv],
|
|
45
|
+
options: {
|
|
46
|
+
help: { type: "boolean", short: "h" },
|
|
47
|
+
repo: { type: "string" },
|
|
48
|
+
pr: { type: "string" },
|
|
49
|
+
},
|
|
50
|
+
allowPositionals: true,
|
|
51
|
+
strict: false,
|
|
52
|
+
tokens: true,
|
|
53
|
+
});
|
|
54
|
+
for (const token of tokens) {
|
|
55
|
+
if (token.kind === "positional") {
|
|
56
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
57
|
+
}
|
|
58
|
+
if (token.kind !== "option") {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (token.name === "help") { options.help = true; return options; }
|
|
62
|
+
if (token.name === "repo") { options.repo = requireTokenValue(token, parseError).trim(); continue; }
|
|
63
|
+
if (token.name === "pr") { options.pr = parsePrNumber(requireTokenValue(token, parseError), parseError); continue; }
|
|
64
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
49
65
|
}
|
|
50
66
|
if (options.repo === undefined || options.pr === undefined) {
|
|
51
67
|
throw parseError("pre-pr-ready-gate requires both --repo <owner/name> and --pr <number>");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createInterface } from "node:readline";
|
|
3
3
|
import { buildParseError, formatCliError, isDirectCliRun } from "../_core-helpers.mjs";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
4
5
|
|
|
5
6
|
const PI_PREPUSH_BYPASS_VAR = "PI_PREPUSH_BYPASS";
|
|
6
7
|
const BLOCKED_REFS = ["refs/heads/main"];
|
|
@@ -61,11 +62,24 @@ function findBlockedRef(refs) {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
export function parsePrePushGuardCliArgs(argv) {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
const { tokens } = parseArgs({
|
|
66
|
+
args: [...argv],
|
|
67
|
+
options: {
|
|
68
|
+
help: { type: "boolean", short: "h" },
|
|
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") return { help: true };
|
|
82
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
69
83
|
}
|
|
70
84
|
return { help: false };
|
|
71
85
|
}
|
|
@@ -1,17 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { buildParseError, formatCliError, isDirectCliRun } from "../_core-helpers.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { requireTokenValue, runCommand } from "../_cli-primitives.mjs";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
4
5
|
|
|
5
6
|
const USAGE = `Usage: pre-write-remote-freshness-guard.mjs --branch <name>\nRefresh remote branch state before starting local file writes.`;
|
|
6
7
|
const parseError = buildParseError(USAGE);
|
|
7
8
|
|
|
8
9
|
export function parseRemoteFreshnessGuardCliArgs(argv) {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
const options = { help: false, branch: undefined };
|
|
11
|
+
const { tokens } = parseArgs({
|
|
12
|
+
args: [...argv],
|
|
13
|
+
options: {
|
|
14
|
+
help: { type: "boolean", short: "h" },
|
|
15
|
+
branch: { type: "string" },
|
|
16
|
+
},
|
|
17
|
+
allowPositionals: true,
|
|
18
|
+
strict: false,
|
|
19
|
+
tokens: true,
|
|
20
|
+
});
|
|
21
|
+
for (const token of tokens) {
|
|
22
|
+
if (token.kind === "positional") {
|
|
23
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
24
|
+
}
|
|
25
|
+
if (token.kind !== "option") {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (token.name === "help") { options.help = true; return options; }
|
|
29
|
+
if (token.name === "branch") { options.branch = requireTokenValue(token, parseError, { flagPattern: /^-/u }); continue; }
|
|
30
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
15
31
|
}
|
|
16
32
|
if (options.branch === undefined) throw parseError("--branch <name> is required");
|
|
17
33
|
return options;
|
|
@@ -4,7 +4,7 @@ import { readFileSync } from "node:fs";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { resolveAuthoritativeStartupResumeBundle } from "@dev-loops/core/loop/public-dev-loop-routing";
|
|
6
6
|
import { buildParseError, formatCliError, isDirectCliRun, parseJsonText } from "../_core-helpers.mjs";
|
|
7
|
-
import {
|
|
7
|
+
import { requireTokenValue, parsePositiveInteger } from "../_cli-primitives.mjs";
|
|
8
8
|
import { execFileSync } from "node:child_process";
|
|
9
9
|
import {
|
|
10
10
|
isUnderWorktreePath,
|
|
@@ -23,6 +23,7 @@ import { detectRepoSlug } from "@dev-loops/core/github/repo-slug";
|
|
|
23
23
|
import { isCopilotLogin } from "@dev-loops/core/github/copilot-helpers";
|
|
24
24
|
import { loadDevLoopConfig, resolveWorkflowConfig } from "@dev-loops/core/config";
|
|
25
25
|
import { createPiAdapter } from "@dev-loops/core/harness";
|
|
26
|
+
import { parseArgs } from "node:util";
|
|
26
27
|
const USAGE = `Usage:
|
|
27
28
|
resolve-dev-loop-startup.mjs --issue <number>
|
|
28
29
|
resolve-dev-loop-startup.mjs --pr <number>
|
|
@@ -96,32 +97,48 @@ const STRATEGY_ASYNC_DISPATCH = {
|
|
|
96
97
|
};
|
|
97
98
|
const parseError = buildParseError(USAGE);
|
|
98
99
|
export function parseResolveDevLoopStartupCliArgs(argv) {
|
|
99
|
-
const args = [...argv];
|
|
100
100
|
const options = {
|
|
101
101
|
help: false,
|
|
102
102
|
inputPath: undefined,
|
|
103
103
|
issue: undefined,
|
|
104
104
|
pr: undefined,
|
|
105
105
|
};
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
const { tokens } = parseArgs({
|
|
107
|
+
args: [...argv],
|
|
108
|
+
options: {
|
|
109
|
+
help: { type: "boolean", short: "h" },
|
|
110
|
+
input: { type: "string" },
|
|
111
|
+
issue: { type: "string" },
|
|
112
|
+
pr: { type: "string" },
|
|
113
|
+
},
|
|
114
|
+
allowPositionals: true,
|
|
115
|
+
strict: false,
|
|
116
|
+
tokens: true,
|
|
117
|
+
});
|
|
118
|
+
for (const token of tokens) {
|
|
119
|
+
if (token.kind === "positional") {
|
|
120
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
121
|
+
}
|
|
122
|
+
if (token.kind !== "option") {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (token.name === "help") {
|
|
109
126
|
options.help = true;
|
|
110
127
|
return options;
|
|
111
128
|
}
|
|
112
|
-
if (token === "
|
|
113
|
-
options.inputPath =
|
|
129
|
+
if (token.name === "input") {
|
|
130
|
+
options.inputPath = requireTokenValue(token, parseError);
|
|
114
131
|
continue;
|
|
115
132
|
}
|
|
116
|
-
if (token === "
|
|
117
|
-
options.issue = parsePositiveInteger(
|
|
133
|
+
if (token.name === "issue") {
|
|
134
|
+
options.issue = parsePositiveInteger(requireTokenValue(token, parseError), "--issue", parseError);
|
|
118
135
|
continue;
|
|
119
136
|
}
|
|
120
|
-
if (token === "
|
|
121
|
-
options.pr = parsePositiveInteger(
|
|
137
|
+
if (token.name === "pr") {
|
|
138
|
+
options.pr = parsePositiveInteger(requireTokenValue(token, parseError), "--pr", parseError);
|
|
122
139
|
continue;
|
|
123
140
|
}
|
|
124
|
-
throw parseError(`Unknown argument: ${token}`);
|
|
141
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
125
142
|
}
|
|
126
143
|
const modeCount = [options.inputPath, options.issue, options.pr].filter(v => v !== undefined).length;
|
|
127
144
|
if (modeCount > 1) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { requireTokenValue } from "../_cli-primitives.mjs";
|
|
3
3
|
import {
|
|
4
4
|
buildParseError,
|
|
5
5
|
formatCliError,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
buildHandoffContractForConductorAction,
|
|
15
15
|
} from "./_handoff-contract.mjs";
|
|
16
16
|
import { listOpenPrs } from "./_loop-pr-aggregation.mjs";
|
|
17
|
+
import { parseArgs } from "node:util";
|
|
17
18
|
export { listOpenPrs };
|
|
18
19
|
const USAGE = `Usage: run-conductor-cycle.mjs --repo <owner/name>
|
|
19
20
|
Poll all open PRs, detect state, and output an ordered action queue.`.trim();
|
|
@@ -63,22 +64,36 @@ export function actionRequiresApproval(action, autonomyStopAt = ["merge"]) {
|
|
|
63
64
|
}
|
|
64
65
|
const parseError = buildParseError(USAGE);
|
|
65
66
|
export function parseCliArgs(argv) {
|
|
66
|
-
const args = [...argv];
|
|
67
67
|
const options = {
|
|
68
68
|
help: false,
|
|
69
69
|
repo: undefined,
|
|
70
70
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
const { tokens } = parseArgs({
|
|
72
|
+
args: [...argv],
|
|
73
|
+
options: {
|
|
74
|
+
help: { type: "boolean", short: "h" },
|
|
75
|
+
repo: { 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 parseError(`Unknown argument: ${token.value}`);
|
|
84
|
+
}
|
|
85
|
+
if (token.kind !== "option") {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (token.name === "help") {
|
|
74
89
|
options.help = true;
|
|
75
90
|
return options;
|
|
76
91
|
}
|
|
77
|
-
if (token === "
|
|
78
|
-
options.repo =
|
|
92
|
+
if (token.name === "repo") {
|
|
93
|
+
options.repo = requireTokenValue(token, parseError).trim();
|
|
79
94
|
continue;
|
|
80
95
|
}
|
|
81
|
-
throw parseError(`Unknown argument: ${token}`);
|
|
96
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
82
97
|
}
|
|
83
98
|
if (options.repo === undefined) {
|
|
84
99
|
throw parseError("run-conductor-cycle requires --repo <owner/name>");
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { buildParseError, formatCliError, isDirectCliRun } from "../_core-helpers.mjs";
|
|
5
|
-
import { parsePositiveInteger,
|
|
5
|
+
import { parsePositiveInteger, requireTokenValue, runCommand } from "../_cli-primitives.mjs";
|
|
6
|
+
import { parseArgs } from "node:util";
|
|
6
7
|
const DEFAULT_MAX_LINES = 1000;
|
|
7
8
|
const DEFAULT_DUPLICATE_WINDOW_LINES = 4;
|
|
8
9
|
const DEFAULT_BRANCH_THRESHOLD = 25;
|
|
@@ -43,7 +44,6 @@ const PRIORITY_ORDER = new Map([
|
|
|
43
44
|
["low", 2],
|
|
44
45
|
]);
|
|
45
46
|
export function parseRefinementAuditCliArgs(argv) {
|
|
46
|
-
const args = [...argv];
|
|
47
47
|
const options = {
|
|
48
48
|
help: false,
|
|
49
49
|
paths: undefined,
|
|
@@ -55,57 +55,79 @@ export function parseRefinementAuditCliArgs(argv) {
|
|
|
55
55
|
thinWrapperMaxLines: DEFAULT_THIN_WRAPPER_MAX_LINES,
|
|
56
56
|
output: undefined,
|
|
57
57
|
};
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
const { tokens } = parseArgs({
|
|
59
|
+
args: [...argv],
|
|
60
|
+
options: {
|
|
61
|
+
help: { type: "boolean", short: "h" },
|
|
62
|
+
paths: { type: "string" },
|
|
63
|
+
"paths-file": { type: "string" },
|
|
64
|
+
root: { type: "string" },
|
|
65
|
+
"max-lines": { type: "string" },
|
|
66
|
+
"duplicate-window-lines": { type: "string" },
|
|
67
|
+
"branch-threshold": { type: "string" },
|
|
68
|
+
"thin-wrapper-max-lines": { type: "string" },
|
|
69
|
+
output: { type: "string" },
|
|
70
|
+
},
|
|
71
|
+
allowPositionals: true,
|
|
72
|
+
strict: false,
|
|
73
|
+
tokens: true,
|
|
74
|
+
});
|
|
75
|
+
for (const token of tokens) {
|
|
76
|
+
if (token.kind === "positional") {
|
|
77
|
+
throw parseError(`Unknown argument: ${token.value}`);
|
|
78
|
+
}
|
|
79
|
+
if (token.kind !== "option") {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (token.name === "help") {
|
|
61
83
|
options.help = true;
|
|
62
84
|
return options;
|
|
63
85
|
}
|
|
64
|
-
if (token === "
|
|
65
|
-
options.paths =
|
|
86
|
+
if (token.name === "paths") {
|
|
87
|
+
options.paths = requireTokenValue(token, parseError, { flagPattern: /^-/u });
|
|
66
88
|
continue;
|
|
67
89
|
}
|
|
68
|
-
if (token === "
|
|
69
|
-
options.pathsFile =
|
|
90
|
+
if (token.name === "paths-file") {
|
|
91
|
+
options.pathsFile = requireTokenValue(token, parseError, { flagPattern: /^-/u });
|
|
70
92
|
continue;
|
|
71
93
|
}
|
|
72
|
-
if (token === "
|
|
73
|
-
options.root =
|
|
94
|
+
if (token.name === "root") {
|
|
95
|
+
options.root = requireTokenValue(token, parseError, { flagPattern: /^-/u });
|
|
74
96
|
continue;
|
|
75
97
|
}
|
|
76
|
-
if (token === "
|
|
77
|
-
options.maxLines = parsePositiveInteger(
|
|
98
|
+
if (token.name === "max-lines") {
|
|
99
|
+
options.maxLines = parsePositiveInteger(requireTokenValue(token, parseError), "--max-lines", parseError);
|
|
78
100
|
continue;
|
|
79
101
|
}
|
|
80
|
-
if (token === "
|
|
102
|
+
if (token.name === "duplicate-window-lines") {
|
|
81
103
|
options.duplicateWindowLines = parsePositiveInteger(
|
|
82
|
-
|
|
104
|
+
requireTokenValue(token, parseError),
|
|
83
105
|
"--duplicate-window-lines",
|
|
84
106
|
parseError,
|
|
85
107
|
);
|
|
86
108
|
continue;
|
|
87
109
|
}
|
|
88
|
-
if (token === "
|
|
110
|
+
if (token.name === "branch-threshold") {
|
|
89
111
|
options.branchThreshold = parsePositiveInteger(
|
|
90
|
-
|
|
112
|
+
requireTokenValue(token, parseError),
|
|
91
113
|
"--branch-threshold",
|
|
92
114
|
parseError,
|
|
93
115
|
);
|
|
94
116
|
continue;
|
|
95
117
|
}
|
|
96
|
-
if (token === "
|
|
118
|
+
if (token.name === "thin-wrapper-max-lines") {
|
|
97
119
|
options.thinWrapperMaxLines = parsePositiveInteger(
|
|
98
|
-
|
|
120
|
+
requireTokenValue(token, parseError),
|
|
99
121
|
"--thin-wrapper-max-lines",
|
|
100
122
|
parseError,
|
|
101
123
|
);
|
|
102
124
|
continue;
|
|
103
125
|
}
|
|
104
|
-
if (token === "
|
|
105
|
-
options.output =
|
|
126
|
+
if (token.name === "output") {
|
|
127
|
+
options.output = requireTokenValue(token, parseError, { flagPattern: /^-/u });
|
|
106
128
|
continue;
|
|
107
129
|
}
|
|
108
|
-
throw parseError(`Unknown argument: ${token}`);
|
|
130
|
+
throw parseError(`Unknown argument: ${token.rawName}`);
|
|
109
131
|
}
|
|
110
132
|
if (options.paths !== undefined && options.pathsFile !== undefined) {
|
|
111
133
|
throw parseError("Specify exactly one of --paths or --paths-file");
|