dev-loops 0.2.6 → 0.3.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.
Files changed (92) hide show
  1. package/.claude/.claude-plugin/plugin.json +1 -1
  2. package/.claude/agents/dev-loop.md +2 -1
  3. package/.claude/agents/developer.md +1 -0
  4. package/.claude/agents/fixer.md +1 -0
  5. package/.claude/agents/review.md +30 -0
  6. package/.claude/skills/copilot-pr-followup/SKILL.md +57 -3
  7. package/.claude/skills/dev-loop/SKILL.md +5 -5
  8. package/.claude/skills/docs/anti-patterns.md +2 -0
  9. package/.claude/skills/docs/copilot-loop-operations.md +2 -2
  10. package/.claude/skills/local-implementation/SKILL.md +17 -3
  11. package/AGENTS.md +1 -1
  12. package/CHANGELOG.md +60 -0
  13. package/agents/dev-loop.agent.md +5 -1
  14. package/agents/developer.agent.md +1 -0
  15. package/agents/fixer.agent.md +1 -0
  16. package/agents/review.agent.md +30 -0
  17. package/cli/index.mjs +42 -7
  18. package/package.json +2 -2
  19. package/scripts/README.md +6 -5
  20. package/scripts/_cli-primitives.mjs +2 -0
  21. package/scripts/_core-helpers.mjs +1 -0
  22. package/scripts/claude/generate-claude-assets.mjs +12 -2
  23. package/scripts/claude/headless-dev-loop.mjs +53 -13
  24. package/scripts/claude/headless-info-smoke.mjs +45 -11
  25. package/scripts/docs/validate-links.mjs +20 -11
  26. package/scripts/github/build-adjacent-bundle.mjs +448 -0
  27. package/scripts/github/capture-review-threads.mjs +32 -14
  28. package/scripts/github/{create-draft-pr.mjs → create-pr.mjs} +28 -12
  29. package/scripts/github/detect-checkpoint-evidence.mjs +123 -15
  30. package/scripts/github/detect-linked-issue-pr.mjs +22 -10
  31. package/scripts/github/manage-sub-issues.mjs +37 -16
  32. package/scripts/github/post-gate-findings.mjs +392 -0
  33. package/scripts/github/probe-copilot-review.mjs +24 -12
  34. package/scripts/github/ready-for-review.mjs +17 -8
  35. package/scripts/github/reconcile-draft-gate.mjs +24 -12
  36. package/scripts/github/reply-resolve-review-threads.mjs +34 -15
  37. package/scripts/github/request-copilot-review.mjs +97 -19
  38. package/scripts/github/resolve-tracker-local-spec.mjs +29 -12
  39. package/scripts/github/stage-reviewer-draft.mjs +32 -14
  40. package/scripts/github/upsert-checkpoint-verdict.mjs +646 -41
  41. package/scripts/github/verify-fresh-review-context.mjs +12 -1
  42. package/scripts/github/write-gate-context.mjs +634 -0
  43. package/scripts/github/write-gate-findings-log.mjs +42 -21
  44. package/scripts/loop/build-handoff-envelope.mjs +32 -14
  45. package/scripts/loop/conductor-monitor.mjs +25 -9
  46. package/scripts/loop/conductor.mjs +31 -12
  47. package/scripts/loop/copilot-pr-handoff.mjs +31 -14
  48. package/scripts/loop/debt-remediate.mjs +28 -11
  49. package/scripts/loop/detect-change-scope.mjs +36 -11
  50. package/scripts/loop/detect-copilot-loop-state.mjs +29 -12
  51. package/scripts/loop/detect-copilot-session-activity.mjs +29 -12
  52. package/scripts/loop/detect-initial-copilot-pr-state.mjs +26 -10
  53. package/scripts/loop/detect-internal-only-pr.mjs +31 -13
  54. package/scripts/loop/detect-issue-refinement-artifact.mjs +29 -12
  55. package/scripts/loop/detect-pr-gate-coordination-state.mjs +56 -28
  56. package/scripts/loop/detect-reviewer-loop-state.mjs +35 -16
  57. package/scripts/loop/detect-stale-runner.mjs +32 -14
  58. package/scripts/loop/detect-tracker-first-loop-state.mjs +38 -11
  59. package/scripts/loop/detect-tracker-pr-state.mjs +23 -8
  60. package/scripts/loop/info.mjs +28 -10
  61. package/scripts/loop/inspect-run-viewer/cli.mjs +44 -21
  62. package/scripts/loop/inspect-run.mjs +35 -16
  63. package/scripts/loop/outer-loop.mjs +35 -16
  64. package/scripts/loop/pr-runner-coordination.mjs +31 -12
  65. package/scripts/loop/pre-commit-branch-guard.mjs +26 -9
  66. package/scripts/loop/pre-flight-gate.mjs +25 -9
  67. package/scripts/loop/pre-pr-ready-gate.mjs +24 -8
  68. package/scripts/loop/pre-push-main-guard.mjs +19 -5
  69. package/scripts/loop/pre-write-remote-freshness-guard.mjs +23 -7
  70. package/scripts/loop/resolve-dev-loop-startup.mjs +29 -12
  71. package/scripts/loop/run-conductor-cycle.mjs +23 -8
  72. package/scripts/loop/run-queue.mjs +87 -15
  73. package/scripts/loop/run-refinement-audit.mjs +44 -22
  74. package/scripts/loop/run-watch-cycle.mjs +28 -12
  75. package/scripts/loop/steer-loop.mjs +122 -62
  76. package/scripts/loop/watch-initial-copilot-pr.mjs +28 -12
  77. package/scripts/projects/add-queue-item.mjs +60 -43
  78. package/scripts/projects/archive-done-items.mjs +506 -0
  79. package/scripts/projects/ensure-queue-board.mjs +65 -64
  80. package/scripts/projects/list-queue-items.mjs +57 -56
  81. package/scripts/projects/move-queue-item.mjs +123 -124
  82. package/scripts/projects/reorder-queue-item.mjs +384 -108
  83. package/scripts/projects/sync-item-status.mjs +198 -0
  84. package/scripts/refine/_refine-helpers.mjs +21 -9
  85. package/scripts/refine/verify.mjs +31 -13
  86. package/skills/copilot-pr-followup/SKILL.md +57 -3
  87. package/skills/dev-loop/SKILL.md +9 -5
  88. package/skills/dev-loop/scripts/log-bash-exit-1.mjs +2 -2
  89. package/skills/dev-loop/scripts/phase-files.mjs +2 -2
  90. package/skills/docs/anti-patterns.md +2 -0
  91. package/skills/docs/copilot-loop-operations.md +2 -2
  92. package/skills/local-implementation/SKILL.md +17 -3
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { parseArgs } from "node:util";
2
3
  import {
3
4
  buildParseError,
4
5
  formatCliError,
@@ -8,9 +9,13 @@ import {
8
9
  summarizeGateReviewCommentMarkers,
9
10
  summarizeGateReviewComments,
10
11
  } from "../_core-helpers.mjs";
11
- import { parsePrNumber, requireOptionValue, runChild } from "../_cli-primitives.mjs";
12
+ import { access } from "node:fs/promises";
13
+ import path from "node:path";
14
+ import { parsePrNumber, requireTokenValue, runChild } from "../_cli-primitives.mjs";
12
15
  import { fetchGithubReviewThreadsPayload } from "./capture-review-threads.mjs";
13
16
  import { parseRepoSlug } from "@dev-loops/core/github/repo-slug";
17
+ import { loadDevLoopConfig, resolveGateConfig, resolveRequireFanoutEvidence } from "@dev-loops/core/config";
18
+ import { buildLogPath } from "./write-gate-findings-log.mjs";
14
19
  import { ensureAsyncRunnerOwnership } from "../loop/_pr-runner-coordination.mjs";
15
20
  import { detectStaleRunner } from "../loop/_stale-runner-detection.mjs";
16
21
  const USAGE = `Usage: detect-checkpoint-evidence.mjs --repo <owner/name> --pr <number>
@@ -84,30 +89,46 @@ Exit codes:
84
89
  1 Argument error, gh failure, malformed gh JSON, or missing required pre-merge gate evidence.`.trim();
85
90
  const parseError = buildParseError(USAGE);
86
91
  export function parseDetectCheckpointEvidenceCliArgs(argv) {
87
- const args = [...argv];
92
+ const { tokens } = parseArgs({
93
+ args: [...argv],
94
+ options: {
95
+ help: { type: "boolean", short: "h" },
96
+ repo: { type: "string" },
97
+ pr: { type: "string" },
98
+ "require-before-merge": { type: "boolean" },
99
+ },
100
+ allowPositionals: true,
101
+ strict: false,
102
+ tokens: true,
103
+ });
88
104
  const options = {
89
105
  help: false,
90
106
  repo: undefined,
91
107
  pr: undefined,
92
108
  };
93
- while (args.length > 0) {
94
- const token = args.shift();
95
- if (token === "--help" || token === "-h") {
109
+ for (const token of tokens) {
110
+ if (token.kind === "positional") {
111
+ throw parseError(`Unknown argument: ${token.value}`);
112
+ }
113
+ if (token.kind !== "option") {
114
+ continue;
115
+ }
116
+ if (token.name === "help") {
96
117
  options.help = true;
97
118
  return options;
98
119
  }
99
- if (token === "--repo") {
100
- options.repo = requireOptionValue(args, "--repo", parseError).trim();
120
+ if (token.name === "repo") {
121
+ options.repo = requireTokenValue(token, parseError).trim();
101
122
  continue;
102
123
  }
103
- if (token === "--pr") {
104
- options.pr = parsePrNumber(requireOptionValue(args, "--pr", parseError), parseError);
124
+ if (token.name === "pr") {
125
+ options.pr = parsePrNumber(requireTokenValue(token, parseError), parseError);
105
126
  continue;
106
127
  }
107
- if (token === "--require-before-merge") {
128
+ if (token.name === "require-before-merge") {
108
129
  throw parseError(`--require-before-merge has been removed: gate evidence enforcement is now always-on by default. Omit the flag.`);
109
130
  }
110
- throw parseError(`Unknown argument: ${token}`);
131
+ throw parseError(`Unknown argument: ${token.rawName}`);
111
132
  }
112
133
  if (options.repo === undefined || options.pr === undefined) {
113
134
  throw parseError("detect-checkpoint-evidence requires both --repo <owner/name> and --pr <number>");
@@ -183,6 +204,8 @@ function emptyGateMarkerSummary() {
183
204
  verdict: null,
184
205
  findingsSummary: null,
185
206
  nextAction: null,
207
+ executionMode: null,
208
+ inlineReason: null,
186
209
  contractComplete: false,
187
210
  commentId: null,
188
211
  commentUrl: null,
@@ -199,13 +222,15 @@ function normalizeGateMarkerSummary(summary) {
199
222
  verdict: summary.verdict,
200
223
  findingsSummary: summary.findingsSummary,
201
224
  nextAction: summary.nextAction,
225
+ executionMode: summary.executionMode ?? null,
226
+ inlineReason: summary.inlineReason ?? null,
202
227
  contractComplete: summary.contractComplete === true,
203
228
  commentId: summary.commentId,
204
229
  commentUrl: summary.commentUrl,
205
230
  updatedAt: summary.updatedAt,
206
231
  };
207
232
  }
208
- export function buildPreMergeGateCheck(evidence, unresolvedThreadCount = null, staleRunnerCheck = null) {
233
+ export function buildPreMergeGateCheck(evidence, unresolvedThreadCount = null, staleRunnerCheck = null, fanoutEnforcement = null) {
209
234
  const failures = [];
210
235
  if (!(evidence.draftGate.visible && evidence.draftGate.verdict === "clean")) {
211
236
  failures.push("missing visible clean draft_gate comment");
@@ -219,6 +244,24 @@ export function buildPreMergeGateCheck(evidence, unresolvedThreadCount = null, s
219
244
  )) {
220
245
  failures.push("missing visible clean current-head pre_approval_gate comment");
221
246
  }
247
+ // Fail-closed fan-out evidence enforcement (gates.requireFanoutEvidence, ON by
248
+ // default / opt-out). When disabled or config-unavailable, fanoutEnforcement is
249
+ // { required: false, gates: [] } so the `.required` guard skips this block.
250
+ if (fanoutEnforcement && fanoutEnforcement.required) {
251
+ for (const gate of fanoutEnforcement.gates) {
252
+ if (gate.executionMode !== "fanout_fanin") {
253
+ failures.push(
254
+ `${gate.name}: requireFanoutEvidence is enabled but executionMode is "${gate.executionMode ?? "unset"}" (expected "fanout_fanin"); inline gate verdicts are not accepted`,
255
+ );
256
+ continue;
257
+ }
258
+ if (!gate.ledgerExists) {
259
+ failures.push(
260
+ `${gate.name}: requireFanoutEvidence is enabled but no findings-log ledger exists for the reviewed head (${gate.ledgerPath})`,
261
+ );
262
+ }
263
+ }
264
+ }
222
265
  if (typeof unresolvedThreadCount === "number" && unresolvedThreadCount !== 0) {
223
266
  if (unresolvedThreadCount === -1) {
224
267
  failures.push("could not fetch review thread state from GitHub API; re-run gate evidence check when API connectivity is restored");
@@ -236,6 +279,52 @@ export function buildPreMergeGateCheck(evidence, unresolvedThreadCount = null, s
236
279
  failures,
237
280
  };
238
281
  }
282
+ async function ledgerExists(fullPath) {
283
+ try {
284
+ await access(fullPath);
285
+ return true;
286
+ } catch {
287
+ return false;
288
+ }
289
+ }
290
+ /**
291
+ * Build the fan-out evidence enforcement descriptor.
292
+ *
293
+ * Enforcement is ON by default (opt-out via gates.requireFanoutEvidence: false).
294
+ * Returns { required: false } when enforcement is disabled OR when config is
295
+ * unavailable (config == null — null or undefined — after a failed load) — config-unavailable must
296
+ * fail open and never enable enforcement. When enabled, records per-required-gate
297
+ * executionMode and whether the deterministic findings-log ledger exists for the
298
+ * reviewed head SHA so the pre-merge check can fail closed on inline verdicts or
299
+ * missing ledgers.
300
+ */
301
+ async function buildFanoutEnforcement({ repo, pr, currentHeadSha, draftGateMarker, preApprovalGateMarker, config, cwd }) {
302
+ // Fail open when config could not be loaded/validated. `== null` covers both
303
+ // null and undefined; the loader only ever yields null on failure, but the
304
+ // loose check defensively treats an absent config as unavailable.
305
+ if (config == null || !resolveRequireFanoutEvidence(config)) {
306
+ return { required: false, gates: [] };
307
+ }
308
+ const draftRequired = resolveGateConfig(config, "draft").required;
309
+ const preApprovalRequired = resolveGateConfig(config, "preApproval").required;
310
+ const gateSpecs = [
311
+ { name: "draft_gate", marker: draftGateMarker, required: draftRequired },
312
+ { name: "pre_approval_gate", marker: preApprovalGateMarker, required: preApprovalRequired },
313
+ ].filter((spec) => spec.required && spec.marker.visible);
314
+ const gates = [];
315
+ for (const spec of gateSpecs) {
316
+ const headSha = spec.marker.headSha ?? currentHeadSha;
317
+ const ledgerPath = buildLogPath({ repo, pr, gate: spec.name, headSha, tmpRoot: "tmp" });
318
+ const fullPath = path.resolve(cwd, ledgerPath);
319
+ gates.push({
320
+ name: spec.name,
321
+ executionMode: spec.marker.executionMode ?? null,
322
+ ledgerPath,
323
+ ledgerExists: await ledgerExists(fullPath),
324
+ });
325
+ }
326
+ return { required: true, gates };
327
+ }
239
328
  export async function detectCheckpointEvidence(options, { env = process.env, ghCommand = "gh", cwd = process.cwd() } = {}) {
240
329
  const runnerOwnership = await ensureAsyncRunnerOwnership({
241
330
  repo: options.repo,
@@ -282,6 +371,24 @@ export async function detectCheckpointEvidence(options, { env = process.env, ghC
282
371
  const allComments = [...commentsPayload, ...prReviews];
283
372
  const commentSummary = summarizeGateReviewComments(allComments);
284
373
  const markerSummary = summarizeGateReviewCommentMarkers(allComments, { headSha: currentHeadSha });
374
+ const draftGateMarker = normalizeGateMarkerSummary(markerSummary.draft_gate);
375
+ const preApprovalGateMarker = normalizeGateMarkerSummary(markerSummary.pre_approval_gate);
376
+ // loadDevLoopConfig never throws: it returns { config, warnings, errors }.
377
+ // A non-empty errors array means the config could not be loaded/validated, so
378
+ // treat it as config-unavailable and leave fan-out enforcement disabled
379
+ // (preserves default behavior). Other gate checks remain unaffected.
380
+ let config = null;
381
+ const { config: loadedConfig, errors: configErrors } = await loadDevLoopConfig({ repoRoot: cwd });
382
+ config = Array.isArray(configErrors) && configErrors.length > 0 ? null : loadedConfig;
383
+ const fanoutEnforcement = await buildFanoutEnforcement({
384
+ repo: options.repo,
385
+ pr: options.pr,
386
+ currentHeadSha,
387
+ draftGateMarker,
388
+ preApprovalGateMarker,
389
+ config,
390
+ cwd,
391
+ });
285
392
  return {
286
393
  ok: true,
287
394
  repo: options.repo,
@@ -289,9 +396,10 @@ export async function detectCheckpointEvidence(options, { env = process.env, ghC
289
396
  currentHeadSha,
290
397
  draftGate: normalizeGateSummary(commentSummary.draft_gate),
291
398
  preApprovalGate: normalizeGateSummary(commentSummary.pre_approval_gate),
292
- draftGateMarker: normalizeGateMarkerSummary(markerSummary.draft_gate),
293
- preApprovalGateMarker: normalizeGateMarkerSummary(markerSummary.pre_approval_gate),
399
+ draftGateMarker,
400
+ preApprovalGateMarker,
294
401
  draftGateSatisfied: commentSummary.draft_gate?.verdict === "clean" && typeof commentSummary.draft_gate?.headSha === "string",
402
+ fanoutEnforcement,
295
403
  ...(runnerOwnership.status !== "skipped_no_async_run_id" ? { runnerOwnership } : {}),
296
404
  staleRunner: {
297
405
  status: staleRunnerDetection.status,
@@ -334,7 +442,7 @@ async function main() {
334
442
  ? [`exit signal recorded for run ${result.staleRunner.activeRun?.runId}: refuse to merge`]
335
443
  : [],
336
444
  };
337
- const preMergeGateCheck = buildPreMergeGateCheck(result, unresolvedThreadCount, staleRunnerCheck);
445
+ const preMergeGateCheck = buildPreMergeGateCheck(result, unresolvedThreadCount, staleRunnerCheck, result.fanoutEnforcement);
338
446
  const output = { ...result, preMergeGateCheck, staleRunnerCheck };
339
447
  if (!preMergeGateCheck.ok) {
340
448
  process.stderr.write(`${JSON.stringify({
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import { parseArgs } from "node:util";
2
3
  import { buildParseError, formatCliError, isDirectCliRun, parseJsonText } from "../_core-helpers.mjs";
3
- import { parseIssueNumber, requireOptionValue, runChild } from "../_cli-primitives.mjs";
4
+ import { parseIssueNumber, requireTokenValue, runChild } from "../_cli-primitives.mjs";
4
5
  import { parseRepoSlug } from "@dev-loops/core/github/repo-slug";
5
6
  export const LINKED_ISSUE_PR_QUERY = [
6
7
  "query($owner:String!, $name:String!, $issue:Int!, $after:String) {",
@@ -75,27 +76,38 @@ Error output (stderr, JSON):
75
76
  { "ok": false, "error": "..." }`.trim();
76
77
  const parseError = buildParseError(USAGE);
77
78
  export function parseDetectLinkedIssuePrCliArgs(argv) {
78
- const args = [...argv];
79
+ const { tokens } = parseArgs({
80
+ args: [...argv],
81
+ options: { help: { type: "boolean", short: "h" }, repo: { type: "string" }, issue: { type: "string" } },
82
+ allowPositionals: true,
83
+ strict: false,
84
+ tokens: true,
85
+ });
79
86
  const options = {
80
87
  help: false,
81
88
  repo: undefined,
82
89
  issue: undefined,
83
90
  };
84
- while (args.length > 0) {
85
- const token = args.shift();
86
- if (token === "--help" || token === "-h") {
91
+ for (const token of tokens) {
92
+ if (token.kind === "positional") {
93
+ throw parseError(`Unknown argument: ${token.value}`);
94
+ }
95
+ if (token.kind !== "option") {
96
+ continue;
97
+ }
98
+ if (token.name === "help") {
87
99
  options.help = true;
88
100
  return options;
89
101
  }
90
- if (token === "--repo") {
91
- options.repo = requireOptionValue(args, "--repo", parseError).trim();
102
+ if (token.name === "repo") {
103
+ options.repo = requireTokenValue(token, parseError).trim();
92
104
  continue;
93
105
  }
94
- if (token === "--issue") {
95
- options.issue = parseIssueNumber(requireOptionValue(args, "--issue", parseError), parseError);
106
+ if (token.name === "issue") {
107
+ options.issue = parseIssueNumber(requireTokenValue(token, parseError), parseError);
96
108
  continue;
97
109
  }
98
- throw parseError(`Unknown argument: ${token}`);
110
+ throw parseError(`Unknown argument: ${token.rawName}`);
99
111
  }
100
112
  if (options.repo === undefined || options.issue === undefined) {
101
113
  throw parseError("Linked PR detection requires both --repo <owner/name> and --issue <number>");
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import { parseArgs } from "node:util";
2
3
  import { buildParseError, formatCliError, isDirectCliRun, parseJsonText } from "../_core-helpers.mjs";
3
- import { parsePositiveInteger, requireOptionValue, runChild } from "../_cli-primitives.mjs";
4
+ import { parsePositiveInteger, requireTokenValue, runChild } from "../_cli-primitives.mjs";
4
5
  import { parseRepoSlug } from "@dev-loops/core/github/repo-slug";
5
6
  const USAGE = `Usage: manage-sub-issues.mjs <command> --repo <owner/name> --issue <number> [options]
6
7
  Deterministic helper for reading, linking, ordering, and verifying GitHub sub-issue trees.
@@ -76,37 +77,57 @@ export function parseManageSubIssuesCliArgs(argv) {
76
77
  expected: undefined,
77
78
  ordered: false,
78
79
  };
79
- while (args.length > 0) {
80
- const token = args.shift();
81
- if (token === "--help" || token === "-h") {
80
+ const { tokens } = parseArgs({
81
+ args,
82
+ options: {
83
+ help: { type: "boolean", short: "h" },
84
+ repo: { type: "string" },
85
+ issue: { type: "string" },
86
+ child: { type: "string" },
87
+ order: { type: "string" },
88
+ expected: { type: "string" },
89
+ ordered: { type: "boolean" },
90
+ },
91
+ allowPositionals: true,
92
+ strict: false,
93
+ tokens: true,
94
+ });
95
+ for (const token of tokens) {
96
+ if (token.kind === "positional") {
97
+ throw parseError(`Unknown argument: ${token.value}`);
98
+ }
99
+ if (token.kind !== "option") {
100
+ continue;
101
+ }
102
+ if (token.name === "help") {
82
103
  options.help = true;
83
104
  return options;
84
105
  }
85
- if (token === "--repo") {
86
- options.repo = requireOptionValue(args, "--repo", parseError).trim();
106
+ if (token.name === "repo") {
107
+ options.repo = requireTokenValue(token, parseError).trim();
87
108
  continue;
88
109
  }
89
- if (token === "--issue") {
90
- options.issue = parsePositiveInteger(requireOptionValue(args, "--issue", parseError), "Issue number", parseError);
110
+ if (token.name === "issue") {
111
+ options.issue = parsePositiveInteger(requireTokenValue(token, parseError), "Issue number", parseError);
91
112
  continue;
92
113
  }
93
- if (token === "--child") {
94
- options.child = parsePositiveInteger(requireOptionValue(args, "--child", parseError), "Issue number", parseError);
114
+ if (token.name === "child") {
115
+ options.child = parsePositiveInteger(requireTokenValue(token, parseError), "Issue number", parseError);
95
116
  continue;
96
117
  }
97
- if (token === "--order") {
98
- options.order = parseIssueList(requireOptionValue(args, "--order", parseError));
118
+ if (token.name === "order") {
119
+ options.order = parseIssueList(requireTokenValue(token, parseError));
99
120
  continue;
100
121
  }
101
- if (token === "--expected") {
102
- options.expected = parseIssueList(requireOptionValue(args, "--expected", parseError));
122
+ if (token.name === "expected") {
123
+ options.expected = parseIssueList(requireTokenValue(token, parseError));
103
124
  continue;
104
125
  }
105
- if (token === "--ordered") {
126
+ if (token.name === "ordered") {
106
127
  options.ordered = true;
107
128
  continue;
108
129
  }
109
- throw parseError(`Unknown argument: ${token}`);
130
+ throw parseError(`Unknown argument: ${token.rawName}`);
110
131
  }
111
132
  if (options.repo === undefined || options.issue === undefined) {
112
133
  throw parseError("Both --repo <owner/name> and --issue <number> are required");