@supaku/agentfactory-cli 0.7.6 → 0.7.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.
@@ -1 +1 @@
1
- {"version":3,"file":"linear-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/linear-runner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,CAAA;IACjD,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAA;CAChB;AAOD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAC/C,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,CAAA;IACjD,cAAc,EAAE,MAAM,EAAE,CAAA;CACzB,CA2CA;AA+cD,wBAAsB,SAAS,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAyLvF"}
1
+ {"version":3,"file":"linear-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/linear-runner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,CAAA;IACjD,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAA;CAChB;AAOD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAC/C,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,CAAA;IACjD,cAAc,EAAE,MAAM,EAAE,CAAA;CACzB,CA2CA;AAymBD,wBAAsB,SAAS,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA2MvF"}
@@ -393,6 +393,121 @@ async function checkDeployment(prNumber, format = 'json') {
393
393
  }
394
394
  return result;
395
395
  }
396
+ async function createBlocker(client, options) {
397
+ // 1. Fetch source issue to resolve team/project
398
+ const sourceIssue = await client.getIssue(options.sourceIssueId);
399
+ const sourceTeam = await sourceIssue.team;
400
+ const sourceProject = await sourceIssue.project;
401
+ const teamName = options.team ?? sourceTeam?.name;
402
+ if (!teamName) {
403
+ throw new Error('Could not resolve team from source issue. Provide --team explicitly.');
404
+ }
405
+ const team = await client.getTeam(teamName);
406
+ const projectName = options.project ?? sourceProject?.name;
407
+ // 2. Deduplicate: check for existing Icebox issues with same title + "Needs Human" label
408
+ if (projectName) {
409
+ const projects = await client.linearClient.projects({
410
+ filter: { name: { eqIgnoreCase: projectName } },
411
+ });
412
+ if (projects.nodes.length > 0) {
413
+ const existingIssues = await client.linearClient.issues({
414
+ filter: {
415
+ project: { id: { eq: projects.nodes[0].id } },
416
+ state: { name: { eqIgnoreCase: 'Icebox' } },
417
+ labels: { name: { eqIgnoreCase: 'Needs Human' } },
418
+ },
419
+ });
420
+ const duplicate = existingIssues.nodes.find((i) => i.title.toLowerCase() === options.title.toLowerCase());
421
+ if (duplicate) {
422
+ // Add a +1 comment to the existing issue
423
+ await client.createComment(duplicate.id, `+1 — Also needed by ${sourceIssue.identifier}`);
424
+ return {
425
+ id: duplicate.id,
426
+ identifier: duplicate.identifier,
427
+ title: duplicate.title,
428
+ url: duplicate.url,
429
+ sourceIssue: sourceIssue.identifier,
430
+ relation: 'blocks',
431
+ deduplicated: true,
432
+ };
433
+ }
434
+ }
435
+ }
436
+ // 3. Create the blocker issue
437
+ const createPayload = {
438
+ teamId: team.id,
439
+ title: options.title,
440
+ };
441
+ // Description with source reference
442
+ const descParts = [];
443
+ if (options.description) {
444
+ descParts.push(options.description);
445
+ }
446
+ descParts.push(`\n---\n*Source issue: ${sourceIssue.identifier}*`);
447
+ createPayload.description = descParts.join('\n\n');
448
+ // Set state to Icebox
449
+ const statuses = await client.getTeamStatuses(team.id);
450
+ const iceboxStateId = statuses['Icebox'];
451
+ if (iceboxStateId) {
452
+ createPayload.stateId = iceboxStateId;
453
+ }
454
+ // Set "Needs Human" label
455
+ const allLabels = await client.linearClient.issueLabels();
456
+ const needsHumanLabel = allLabels.nodes.find((l) => l.name.toLowerCase() === 'needs human');
457
+ if (needsHumanLabel) {
458
+ createPayload.labelIds = [needsHumanLabel.id];
459
+ }
460
+ // Set project
461
+ if (projectName) {
462
+ const projects = await client.linearClient.projects({
463
+ filter: { name: { eqIgnoreCase: projectName } },
464
+ });
465
+ if (projects.nodes.length > 0) {
466
+ createPayload.projectId = projects.nodes[0].id;
467
+ }
468
+ }
469
+ const payload = await client.linearClient.createIssue(createPayload);
470
+ if (!payload.success) {
471
+ throw new Error('Failed to create blocker issue');
472
+ }
473
+ const blockerIssue = await payload.issue;
474
+ if (!blockerIssue) {
475
+ throw new Error('Blocker issue created but not returned');
476
+ }
477
+ // 4. Create blocking relation: blocker blocks source
478
+ await client.createIssueRelation({
479
+ issueId: blockerIssue.id,
480
+ relatedIssueId: sourceIssue.id,
481
+ type: 'blocks',
482
+ });
483
+ // 5. Post comment on source issue
484
+ await client.createComment(sourceIssue.id, `\u{1F6A7} Human blocker created: [${blockerIssue.identifier}](${blockerIssue.url}) — ${options.title}`);
485
+ // 6. Optionally assign
486
+ if (options.assignee) {
487
+ const users = await client.linearClient.users({
488
+ filter: {
489
+ or: [
490
+ { name: { eqIgnoreCase: options.assignee } },
491
+ { email: { eq: options.assignee } },
492
+ ],
493
+ },
494
+ });
495
+ if (users.nodes.length > 0) {
496
+ await client.linearClient.updateIssue(blockerIssue.id, {
497
+ assigneeId: users.nodes[0].id,
498
+ });
499
+ }
500
+ }
501
+ return {
502
+ id: blockerIssue.id,
503
+ identifier: blockerIssue.identifier,
504
+ title: blockerIssue.title,
505
+ url: blockerIssue.url,
506
+ sourceIssue: sourceIssue.identifier,
507
+ relation: 'blocks',
508
+ deduplicated: false,
509
+ };
510
+ }
396
511
  // ── Commands that don't require LINEAR_API_KEY ─────────────────────
397
512
  const NO_API_KEY_COMMANDS = new Set(['check-deployment']);
398
513
  // ── Main runner ────────────────────────────────────────────────────
@@ -544,6 +659,21 @@ export async function runLinear(config) {
544
659
  output = await checkDeployment(prNumber, format);
545
660
  break;
546
661
  }
662
+ case 'create-blocker': {
663
+ const sourceIssueId = requirePositional('source-issue-id');
664
+ if (!args.title) {
665
+ throw new Error('Usage: af-linear create-blocker <source-issue-id> --title "Title" [--description "..."] [--team "..."] [--project "..."] [--assignee "user@email.com"]');
666
+ }
667
+ output = await createBlocker(client(), {
668
+ title: args.title,
669
+ sourceIssueId,
670
+ description: args.description,
671
+ team: args.team,
672
+ project: args.project,
673
+ assignee: args.assignee,
674
+ });
675
+ break;
676
+ }
547
677
  default:
548
678
  throw new Error(`Unknown command: ${command}`);
549
679
  }
@@ -23,6 +23,7 @@
23
23
  * list-sub-issue-statuses <id> List sub-issue statuses (lightweight)
24
24
  * update-sub-issue <id> Update sub-issue status with comment
25
25
  * check-deployment <PR> Check Vercel deployment status for a PR
26
+ * create-blocker <source-id> Create a human-needed blocker issue
26
27
  *
27
28
  * Array Values:
28
29
  * --labels accepts comma-separated: --labels "Bug,Feature"
@@ -1 +1 @@
1
- {"version":3,"file":"linear.d.ts","sourceRoot":"","sources":["../../src/linear.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG"}
1
+ {"version":3,"file":"linear.d.ts","sourceRoot":"","sources":["../../src/linear.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG"}
@@ -23,6 +23,7 @@
23
23
  * list-sub-issue-statuses <id> List sub-issue statuses (lightweight)
24
24
  * update-sub-issue <id> Update sub-issue status with comment
25
25
  * check-deployment <PR> Check Vercel deployment status for a PR
26
+ * create-blocker <source-id> Create a human-needed blocker issue
26
27
  *
27
28
  * Array Values:
28
29
  * --labels accepts comma-separated: --labels "Bug,Feature"
@@ -60,6 +61,7 @@ Commands:
60
61
  list-sub-issue-statuses <id> List sub-issue statuses (lightweight)
61
62
  update-sub-issue <id> Update sub-issue status with comment
62
63
  check-deployment <PR> Check Vercel deployment status for a PR
64
+ create-blocker <source-id> Create a human-needed blocker issue
63
65
  help Show this help message
64
66
 
65
67
  Options:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supaku/agentfactory-cli",
3
- "version": "0.7.6",
3
+ "version": "0.7.7",
4
4
  "type": "module",
5
5
  "description": "CLI tools for AgentFactory — local orchestrator, remote worker, queue admin",
6
6
  "author": "Supaku (https://supaku.com)",
@@ -88,9 +88,9 @@
88
88
  ],
89
89
  "dependencies": {
90
90
  "dotenv": "^17.2.3",
91
- "@supaku/agentfactory": "0.7.6",
92
- "@supaku/agentfactory-linear": "0.7.6",
93
- "@supaku/agentfactory-server": "0.7.6"
91
+ "@supaku/agentfactory": "0.7.7",
92
+ "@supaku/agentfactory-server": "0.7.7",
93
+ "@supaku/agentfactory-linear": "0.7.7"
94
94
  },
95
95
  "devDependencies": {
96
96
  "@types/node": "^22.5.4",