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,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { formatCliError, isDirectCliRun, parseJsonText } from "../_core-helpers.mjs";
3
3
  import { runChild as _runChild } from "../_cli-primitives.mjs";
4
+ import { parseArgs } from "node:util";
4
5
 
5
6
  const USAGE = `Usage: dev-loops project list --repo <owner/name> --project <number|id> [--column <name>] [--limit <n>]
6
7
 
@@ -24,69 +25,69 @@ Exit codes:
24
25
  3 — project, field, or column not found
25
26
  `.trim();
26
27
 
27
- const VALID_ARGS = new Set(["--repo", "--project", "--column", "--limit", "--help", "-h"]);
28
+ function parseCliArgs(argv) {
29
+ const parseError = (message) => Object.assign(new Error(message), { usage: USAGE });
30
+ const requireValue = (token, message) => {
31
+ const v = token.value;
32
+ if (typeof v !== "string" || v.length === 0 || v.startsWith("-")) {
33
+ throw parseError(message);
34
+ }
35
+ return v;
36
+ };
28
37
 
29
- function parseArgs(argv) {
30
38
  const args = {};
31
- for (let i = 0; i < argv.length; i++) {
32
- const arg = argv[i];
33
- if (!VALID_ARGS.has(arg) && arg.startsWith("-")) {
34
- throw Object.assign(
35
- new Error(`Unknown flag: ${arg}`),
36
- { code: "INVALID_ARGS", usage: USAGE },
37
- );
39
+ const { tokens } = parseArgs({
40
+ args: [...argv],
41
+ options: {
42
+ repo: { type: "string" },
43
+ project: { type: "string" },
44
+ column: { type: "string" },
45
+ limit: { type: "string" },
46
+ help: { type: "boolean", short: "h" },
47
+ },
48
+ allowPositionals: true,
49
+ strict: false,
50
+ tokens: true,
51
+ });
52
+
53
+ for (const token of tokens) {
54
+ if (token.kind === "positional") {
55
+ throw parseError(`Unexpected argument: ${token.value}`);
38
56
  }
39
- if (arg === "--repo") {
40
- if (i + 1 >= argv.length || argv[i + 1].startsWith("-")) {
41
- throw Object.assign(
42
- new Error("--repo requires a value (owner/name)"),
43
- { code: "INVALID_ARGS", usage: USAGE },
44
- );
45
- }
46
- args.repo = argv[++i];
47
- } else if (arg === "--project") {
48
- if (i + 1 >= argv.length || argv[i + 1].startsWith("-")) {
49
- throw Object.assign(
50
- new Error("--project requires a value (number or node ID)"),
51
- { code: "INVALID_ARGS", usage: USAGE },
52
- );
53
- }
54
- args.project = argv[++i];
55
- } else if (arg === "--column") {
56
- if (i + 1 >= argv.length || argv[i + 1].startsWith("-")) {
57
- throw Object.assign(
58
- new Error("--column requires a value"),
59
- { code: "INVALID_ARGS", usage: USAGE },
60
- );
61
- }
62
- args.column = argv[++i];
63
- } else if (arg === "--limit") {
64
- if (i + 1 >= argv.length || argv[i + 1].startsWith("-")) {
65
- throw Object.assign(
66
- new Error("--limit requires a positive integer"),
67
- { code: "INVALID_ARGS", usage: USAGE },
68
- );
69
- }
70
- const val = Number(argv[++i]);
71
- if (!Number.isInteger(val) || val < 1) {
72
- throw Object.assign(
73
- new Error(`--limit must be a positive integer, got "${argv[i]}"`),
74
- { code: "INVALID_ARGS", usage: USAGE },
75
- );
57
+ if (token.kind !== "option") {
58
+ continue;
59
+ }
60
+ switch (token.name) {
61
+ case "help":
62
+ if (token.value !== undefined) {
63
+ throw parseError(`Unknown flag: ${token.rawName}=${token.value}`);
64
+ }
65
+ args.help = true;
66
+ break;
67
+ case "repo":
68
+ args.repo = requireValue(token, "--repo requires a value (owner/name)");
69
+ break;
70
+ case "project":
71
+ args.project = requireValue(token, "--project requires a value (number or node ID)");
72
+ break;
73
+ case "column":
74
+ args.column = requireValue(token, "--column requires a value");
75
+ break;
76
+ case "limit": {
77
+ const raw = requireValue(token, "--limit requires a positive integer");
78
+ const val = Number(raw);
79
+ if (!Number.isInteger(val) || val < 1) {
80
+ throw parseError(`--limit must be a positive integer, got "${raw}"`);
81
+ }
82
+ args.limit = val;
83
+ break;
76
84
  }
77
- args.limit = val;
78
- } else if (arg === "--help" || arg === "-h") {
79
- args.help = true;
80
- } else {
81
- throw Object.assign(
82
- new Error(`Unexpected argument: ${arg}`),
83
- { code: "INVALID_ARGS", usage: USAGE },
84
- );
85
+ default:
86
+ throw parseError(`Unknown flag: ${token.rawName}`);
85
87
  }
86
88
  }
87
89
  return args;
88
90
  }
89
-
90
91
  // ── Validation ───────────────────────────────────────────────────────────
91
92
 
92
93
  const OWNER_RE = /^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/;
@@ -460,7 +461,7 @@ async function main(args, { env = process.env, runChild } = {}) {
460
461
  async function runCli(argv, { stdout = process.stdout, stderr = process.stderr, env = process.env } = {}) {
461
462
  let args;
462
463
  try {
463
- args = parseArgs(argv);
464
+ args = parseCliArgs(argv);
464
465
  } catch (err) {
465
466
  stderr.write(`${formatCliError(err)}\n`);
466
467
  process.exitCode = 1;
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { formatCliError, isDirectCliRun, parseJsonText } from "../_core-helpers.mjs";
3
3
  import { runChild as _runChild } from "../_cli-primitives.mjs";
4
+ import { parseArgs } from "node:util";
4
5
 
5
6
  const USAGE = `Usage: dev-loops project move --repo <owner/name> --project <number|id> --item <number|node-id> --to-column <name>
6
7
 
@@ -23,47 +24,63 @@ Exit codes:
23
24
  3 — project, field, column, or item not found
24
25
  `.trim();
25
26
 
26
- const VALID_ARGS = new Set(["--repo", "--project", "--item", "--to-column", "--help", "-h"]);
27
+ function parseCliArgs(argv) {
28
+ const parseError = (message) => Object.assign(new Error(message), { usage: USAGE });
29
+ const requireValue = (token, message) => {
30
+ const v = token.value;
31
+ if (typeof v !== "string" || v.length === 0 || v.startsWith("-")) {
32
+ throw parseError(message);
33
+ }
34
+ return v;
35
+ };
27
36
 
28
- function parseArgs(argv) {
29
37
  const args = {};
30
- for (let i = 0; i < argv.length; i++) {
31
- const arg = argv[i];
32
- if (!VALID_ARGS.has(arg) && arg.startsWith("-")) {
33
- throw Object.assign(
34
- new Error(`Unknown flag: ${arg}`),
35
- { code: "INVALID_ARGS", usage: USAGE },
36
- );
38
+ const { tokens } = parseArgs({
39
+ args: [...argv],
40
+ options: {
41
+ repo: { type: "string" },
42
+ project: { type: "string" },
43
+ item: { type: "string" },
44
+ "to-column": { type: "string" },
45
+ help: { type: "boolean", short: "h" },
46
+ },
47
+ allowPositionals: true,
48
+ strict: false,
49
+ tokens: true,
50
+ });
51
+
52
+ for (const token of tokens) {
53
+ if (token.kind === "positional") {
54
+ throw parseError(`Unexpected argument: ${token.value}`);
37
55
  }
38
- if (arg === "--repo") {
39
- if (i + 1 >= argv.length || argv[i + 1].startsWith("-")) {
40
- throw Object.assign(new Error("--repo requires a value (owner/name)"), { code: "INVALID_REPO" });
41
- }
42
- args.repo = argv[++i];
43
- } else if (arg === "--project") {
44
- if (i + 1 >= argv.length || argv[i + 1].startsWith("-")) {
45
- throw Object.assign(new Error("--project requires a value (number or node ID)"), { code: "INVALID_PROJECT" });
46
- }
47
- args.project = argv[++i];
48
- } else if (arg === "--item") {
49
- if (i + 1 >= argv.length || argv[i + 1].startsWith("-")) {
50
- throw Object.assign(new Error("--item requires a value (number or node ID)"), { code: "INVALID_ITEM" });
51
- }
52
- args.item = argv[++i];
53
- } else if (arg === "--to-column") {
54
- if (i + 1 >= argv.length || argv[i + 1].startsWith("-")) {
55
- throw Object.assign(new Error("--to-column requires a value"), { code: "INVALID_COLUMN" });
56
- }
57
- args.toColumn = argv[++i];
58
- } else if (arg === "--help" || arg === "-h") {
59
- args.help = true;
60
- } else {
61
- throw Object.assign(new Error(`Unexpected argument: ${arg}`), { code: "INVALID_ARGS", usage: USAGE });
56
+ if (token.kind !== "option") {
57
+ continue;
58
+ }
59
+ switch (token.name) {
60
+ case "help":
61
+ if (token.value !== undefined) {
62
+ throw parseError(`Unknown flag: ${token.rawName}=${token.value}`);
63
+ }
64
+ args.help = true;
65
+ break;
66
+ case "repo":
67
+ args.repo = requireValue(token, "--repo requires a value (owner/name)");
68
+ break;
69
+ case "project":
70
+ args.project = requireValue(token, "--project requires a value (number or node ID)");
71
+ break;
72
+ case "item":
73
+ args.item = requireValue(token, "--item requires a value (number or node ID)");
74
+ break;
75
+ case "to-column":
76
+ args.toColumn = requireValue(token, "--to-column requires a value");
77
+ break;
78
+ default:
79
+ throw parseError(`Unknown flag: ${token.rawName}`);
62
80
  }
63
81
  }
64
82
  return args;
65
83
  }
66
-
67
84
  // ── Validation ───────────────────────────────────────────────────────────
68
85
 
69
86
  const OWNER_RE = /^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/;
@@ -210,7 +227,7 @@ const GET_PROJECT_ITEMS_BY_CONTENT = [
210
227
  "query($projectId:ID!, $after:String) {",
211
228
  " node(id:$projectId) {",
212
229
  " ... on ProjectV2 {",
213
- " items(first:10, after:$after, orderBy:{field:POSITION, direction:ASC}) {",
230
+ " items(first:100, after:$after, orderBy:{field:POSITION, direction:ASC}) {",
214
231
  " pageInfo { hasNextPage endCursor }",
215
232
  " nodes {",
216
233
  " id",
@@ -223,8 +240,8 @@ const GET_PROJECT_ITEMS_BY_CONTENT = [
223
240
  " }",
224
241
  " }",
225
242
  " content {",
226
- " ... on Issue { number repository { nameWithOwner } }",
227
- " ... on PullRequest { number repository { nameWithOwner } }",
243
+ " ... on Issue { __typename number repository { nameWithOwner } }",
244
+ " ... on PullRequest { __typename number repository { nameWithOwner } }",
228
245
  " }",
229
246
  " }",
230
247
  " }",
@@ -233,30 +250,6 @@ const GET_PROJECT_ITEMS_BY_CONTENT = [
233
250
  "}"
234
251
  ].join("\n");
235
252
 
236
- const GET_PROJECT_ITEM = [
237
- "query($projectId:ID!, $itemId:ID!) {",
238
- " node(id:$projectId) {",
239
- " ... on ProjectV2 {",
240
- " item: item(id:$itemId) {",
241
- " id",
242
- " fieldValues(first:20) {",
243
- " nodes {",
244
- " ... on ProjectV2ItemFieldSingleSelectValue {",
245
- " field { ... on ProjectV2SingleSelectField { id name } }",
246
- " name",
247
- " }",
248
- " }",
249
- " }",
250
- " content {",
251
- " ... on Issue { number title url }",
252
- " ... on PullRequest { number title url }",
253
- " }",
254
- " }",
255
- " }",
256
- " }",
257
- "}"
258
- ].join("\n");
259
-
260
253
  const UPDATE_ITEM_FIELD = [
261
254
  "mutation($projectId:ID!, $itemId:ID!, $fieldId:ID!, $optionId:String!) {",
262
255
  " updateProjectV2ItemFieldValue(input:{projectId:$projectId, itemId:$itemId, fieldId:$fieldId, value:{singleSelectOptionId:$optionId}}) {",
@@ -337,6 +330,39 @@ async function listAllFields(projectId, env, runChild) {
337
330
  return fields;
338
331
  }
339
332
 
333
+ // ── Paginated item listing (position order) ──────────────────────────────
334
+
335
+ async function fetchAllItems(projectId, env, runChild) {
336
+ const items = [];
337
+ let after = null;
338
+ while (true) {
339
+ const vars = { projectId };
340
+ if (after) vars.after = after;
341
+ const payload = await ghGraphql(GET_PROJECT_ITEMS_BY_CONTENT, vars, env, runChild);
342
+ const connection = payload?.data?.node?.items;
343
+ const nodes = connection?.nodes ?? [];
344
+ items.push(...nodes);
345
+ const pageInfo = connection?.pageInfo ?? {};
346
+ if (!pageInfo.hasNextPage) break;
347
+ if (!pageInfo.endCursor) {
348
+ throw Object.assign(
349
+ new Error("Invalid items payload: hasNextPage is true but endCursor is missing"),
350
+ { code: "GH_API_ERROR" },
351
+ );
352
+ }
353
+ after = pageInfo.endCursor;
354
+ }
355
+ return items;
356
+ }
357
+
358
+ function statusOf(node) {
359
+ const fvs = node?.fieldValues?.nodes ?? [];
360
+ for (const fv of fvs) {
361
+ if (fv && fv.field && fv.field.name === "Status") return fv.name;
362
+ }
363
+ return null;
364
+ }
365
+
340
366
  // ── Exit code classification ────────────────────────────────────────────
341
367
 
342
368
  function classifyExitCode(err) {
@@ -397,80 +423,53 @@ async function main(args, { env = process.env, runChild } = {}) {
397
423
  );
398
424
  }
399
425
 
400
- // 4. Find the item
401
- let itemId;
402
- let previousColumn = null;
403
- let issueNumber = null;
404
- let prNumber = null;
405
-
426
+ // 4. Find the item.
427
+ //
428
+ // Fetch the full board item list ONCE (paginated, position order) and resolve
429
+ // BOTH ref kinds against it. This reuses the proven pattern from
430
+ // reorder-queue-item / list-queue-items: a node-id ref matches by item.id, a
431
+ // number ref matches by content.number. Both are scoped to the requested repo
432
+ // so a cross-project ref fails closed with ITEM_NOT_FOUND. (The previous code
433
+ // used `ProjectV2.item` — a field that does not exist — for the node-id path,
434
+ // and a single non-paginated `items(first:10)` page for the number path, so it
435
+ // could not find items beyond the first page.)
436
+ const allItems = await fetchAllItems(project.id, env, child);
437
+
438
+ let match;
406
439
  if (itemRef.kind === "id") {
407
- // Direct item node ID lookup
408
- const itemPayload = await ghGraphql(GET_PROJECT_ITEM, {
409
- projectId: project.id,
410
- itemId: itemRef.value,
411
- }, env, child);
412
- const item = itemPayload?.data?.node?.item;
413
- if (!item) {
440
+ match = allItems.find(
441
+ (it) => it.id === itemRef.value && it.content?.repository?.nameWithOwner === repo,
442
+ );
443
+ if (!match) {
414
444
  throw Object.assign(
415
- new Error(`Item "${itemRef.value}" not found in project "${project.title}"`),
445
+ new Error(`Item "${itemRef.value}" not found in project "${project.title}" for repo "${repo}"`),
416
446
  { code: "ITEM_NOT_FOUND" },
417
447
  );
418
448
  }
419
- itemId = item.id;
420
- const fvs = item.fieldValues?.nodes ?? [];
421
- for (const fv of fvs) {
422
- if (fv && fv.field && fv.field.name === "Status") {
423
- previousColumn = fv.name;
424
- break;
425
- }
426
- }
427
- if (item.content) {
428
- if (item.content.__typename === "Issue") {
429
- issueNumber = item.content.number;
430
- } else {
431
- prNumber = item.content.number;
432
- }
433
- }
434
449
  } else {
435
- // Look up by issue/PR number in the project
436
- const itemsPayload = await ghGraphql(GET_PROJECT_ITEMS_BY_CONTENT, {
437
- projectId: project.id,
438
- }, env, child);
439
- const items = itemsPayload?.data?.node?.items?.nodes ?? [];
440
-
441
- // Filter by matching repo exactly and item number (when known)
442
- const matchingItems = items.filter((it) => {
443
- if (!it.content) return false;
444
- const repoMatch = it.content.repository?.nameWithOwner === repo;
445
- if (itemRef.kind === "number") {
446
- return repoMatch && it.content.number === itemRef.value;
447
- }
448
- return repoMatch;
449
- });
450
-
451
- if (matchingItems.length === 0) {
450
+ match = allItems.find(
451
+ (it) =>
452
+ it.content &&
453
+ it.content.repository?.nameWithOwner === repo &&
454
+ it.content.number === itemRef.value,
455
+ );
456
+ if (!match) {
452
457
  throw Object.assign(
453
458
  new Error(`Item #${itemRef.value} not found in project "${project.title}" for repo "${repo}"`),
454
459
  { code: "ITEM_NOT_FOUND" },
455
460
  );
456
461
  }
462
+ }
457
463
 
458
- // Use the first match (by position order)
459
- const match = matchingItems[0];
460
- itemId = match.id;
461
- const fvs = match.fieldValues?.nodes ?? [];
462
- for (const fv of fvs) {
463
- if (fv && fv.field && fv.field.name === "Status") {
464
- previousColumn = fv.name;
465
- break;
466
- }
467
- }
468
- if (match.content) {
469
- if (match.content.__typename === "Issue") {
470
- issueNumber = match.content.number;
471
- } else {
472
- prNumber = match.content.number;
473
- }
464
+ const itemId = match.id;
465
+ const previousColumn = statusOf(match);
466
+ let issueNumber = null;
467
+ let prNumber = null;
468
+ if (match.content) {
469
+ if (match.content.__typename === "PullRequest") {
470
+ prNumber = match.content.number;
471
+ } else {
472
+ issueNumber = match.content.number;
474
473
  }
475
474
  }
476
475
 
@@ -520,7 +519,7 @@ async function main(args, { env = process.env, runChild } = {}) {
520
519
  async function runCli(argv, { stdout = process.stdout, stderr = process.stderr, env = process.env } = {}) {
521
520
  let args;
522
521
  try {
523
- args = parseArgs(argv);
522
+ args = parseCliArgs(argv);
524
523
  } catch (err) {
525
524
  stderr.write(`${formatCliError(err)}\n`);
526
525
  process.exitCode = 1;