opencode-sdlc-plugin 1.1.0 → 1.1.2

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/dist/cli/index.js CHANGED
@@ -291,7 +291,7 @@ var FileManager = class {
291
291
  return false;
292
292
  }
293
293
  const sdlcPlugins = [
294
- "opencode-sdlc",
294
+ "opencode-sdlc-plugin",
295
295
  "oh-my-opencode",
296
296
  "opencode-antigravity-auth",
297
297
  "opencode-openai-codex-auth"
@@ -1590,7 +1590,7 @@ async function generateOpencodeConfig(answers, configDir) {
1590
1590
  } catch {
1591
1591
  }
1592
1592
  }
1593
- const plugins = ["opencode-sdlc/plugin", "oh-my-opencode"];
1593
+ const plugins = ["opencode-sdlc-plugin/plugin", "oh-my-opencode"];
1594
1594
  if (answers.subscriptions.hasGoogle && answers.subscriptions.googleAuth === "antigravity") {
1595
1595
  plugins.push("opencode-antigravity-auth");
1596
1596
  }
@@ -1756,7 +1756,7 @@ async function generateOpencodeConfig(answers, configDir) {
1756
1756
  return config;
1757
1757
  }
1758
1758
  function getRequiredPlugins(answers) {
1759
- const plugins = ["opencode-sdlc", "oh-my-opencode"];
1759
+ const plugins = ["opencode-sdlc-plugin", "oh-my-opencode"];
1760
1760
  if (answers.subscriptions.hasGoogle && answers.subscriptions.googleAuth === "antigravity") {
1761
1761
  plugins.push("opencode-antigravity-auth");
1762
1762
  }
@@ -2304,15 +2304,28 @@ var GitHubClient = class {
2304
2304
  // Repositories
2305
2305
  // ==========================================================================
2306
2306
  /**
2307
- * Create a new repository
2307
+ * Create a new repository (personal or in an organization)
2308
2308
  */
2309
2309
  async createRepository(options) {
2310
- const { data } = await this.octokit.repos.createForAuthenticatedUser({
2311
- name: options.name,
2312
- description: options.description,
2313
- private: options.private ?? false,
2314
- auto_init: options.autoInit ?? false
2315
- });
2310
+ let data;
2311
+ if (options.org) {
2312
+ const response = await this.octokit.repos.createInOrg({
2313
+ org: options.org,
2314
+ name: options.name,
2315
+ description: options.description,
2316
+ private: options.private ?? false,
2317
+ auto_init: options.autoInit ?? false
2318
+ });
2319
+ data = response.data;
2320
+ } else {
2321
+ const response = await this.octokit.repos.createForAuthenticatedUser({
2322
+ name: options.name,
2323
+ description: options.description,
2324
+ private: options.private ?? false,
2325
+ auto_init: options.autoInit ?? false
2326
+ });
2327
+ data = response.data;
2328
+ }
2316
2329
  return {
2317
2330
  id: data.id,
2318
2331
  name: data.name,
@@ -2322,6 +2335,32 @@ var GitHubClient = class {
2322
2335
  defaultBranch: data.default_branch || "main"
2323
2336
  };
2324
2337
  }
2338
+ /**
2339
+ * List organizations the authenticated user belongs to
2340
+ */
2341
+ async listOrganizations() {
2342
+ const query = `
2343
+ query {
2344
+ viewer {
2345
+ organizations(first: 100) {
2346
+ nodes {
2347
+ id
2348
+ databaseId
2349
+ login
2350
+ name
2351
+ }
2352
+ }
2353
+ }
2354
+ }
2355
+ `;
2356
+ const result = await this.octokit.graphql(query);
2357
+ return result.viewer.organizations.nodes.map((org) => ({
2358
+ id: String(org.databaseId),
2359
+ nodeId: org.id,
2360
+ login: org.login,
2361
+ name: org.name
2362
+ }));
2363
+ }
2325
2364
  /**
2326
2365
  * Get repository details
2327
2366
  */
@@ -2403,6 +2442,163 @@ var GitHubClient = class {
2403
2442
  return null;
2404
2443
  }
2405
2444
  }
2445
+ /**
2446
+ * Get project by number for an organization
2447
+ */
2448
+ async getOrgProject(orgLogin, projectNumber) {
2449
+ const query = `
2450
+ query($login: String!, $number: Int!) {
2451
+ organization(login: $login) {
2452
+ projectV2(number: $number) {
2453
+ id
2454
+ number
2455
+ title
2456
+ url
2457
+ }
2458
+ }
2459
+ }
2460
+ `;
2461
+ try {
2462
+ const result = await this.octokit.graphql(query, { login: orgLogin, number: projectNumber });
2463
+ return result.organization.projectV2;
2464
+ } catch {
2465
+ return null;
2466
+ }
2467
+ }
2468
+ /**
2469
+ * List projects for an organization
2470
+ */
2471
+ async listOrgProjects(orgLogin, limit = 20) {
2472
+ const query = `
2473
+ query($login: String!, $first: Int!) {
2474
+ organization(login: $login) {
2475
+ projectsV2(first: $first) {
2476
+ nodes {
2477
+ id
2478
+ number
2479
+ title
2480
+ url
2481
+ }
2482
+ }
2483
+ }
2484
+ }
2485
+ `;
2486
+ try {
2487
+ const result = await this.octokit.graphql(query, { login: orgLogin, first: limit });
2488
+ return result.organization.projectsV2.nodes;
2489
+ } catch {
2490
+ return [];
2491
+ }
2492
+ }
2493
+ /**
2494
+ * Create a new project
2495
+ */
2496
+ async createProject(options) {
2497
+ let ownerId;
2498
+ if (options.isOrg) {
2499
+ const query = `
2500
+ query($login: String!) {
2501
+ organization(login: $login) {
2502
+ id
2503
+ }
2504
+ }
2505
+ `;
2506
+ const result2 = await this.octokit.graphql(query, { login: options.ownerLogin });
2507
+ ownerId = result2.organization.id;
2508
+ } else {
2509
+ const query = `
2510
+ query($login: String!) {
2511
+ user(login: $login) {
2512
+ id
2513
+ }
2514
+ }
2515
+ `;
2516
+ const result2 = await this.octokit.graphql(query, { login: options.ownerLogin });
2517
+ ownerId = result2.user.id;
2518
+ }
2519
+ const mutation = `
2520
+ mutation($ownerId: ID!, $title: String!) {
2521
+ createProjectV2(input: { ownerId: $ownerId, title: $title }) {
2522
+ projectV2 {
2523
+ id
2524
+ number
2525
+ title
2526
+ url
2527
+ }
2528
+ }
2529
+ }
2530
+ `;
2531
+ const result = await this.octokit.graphql(mutation, { ownerId, title: options.title });
2532
+ return result.createProjectV2.projectV2;
2533
+ }
2534
+ /**
2535
+ * Copy an existing project
2536
+ */
2537
+ async copyProject(options) {
2538
+ let sourceProject;
2539
+ if (options.sourceIsOrg) {
2540
+ sourceProject = await this.getOrgProject(
2541
+ options.sourceOwnerLogin,
2542
+ options.sourceProjectNumber
2543
+ );
2544
+ } else {
2545
+ sourceProject = await this.getUserProject(
2546
+ options.sourceOwnerLogin,
2547
+ options.sourceProjectNumber
2548
+ );
2549
+ }
2550
+ if (!sourceProject) {
2551
+ throw new Error(
2552
+ `Source project #${options.sourceProjectNumber} not found for ${options.sourceOwnerLogin}`
2553
+ );
2554
+ }
2555
+ let targetOwnerId;
2556
+ if (options.targetIsOrg) {
2557
+ const query = `
2558
+ query($login: String!) {
2559
+ organization(login: $login) {
2560
+ id
2561
+ }
2562
+ }
2563
+ `;
2564
+ const result2 = await this.octokit.graphql(query, { login: options.targetOwnerLogin });
2565
+ targetOwnerId = result2.organization.id;
2566
+ } else {
2567
+ const query = `
2568
+ query($login: String!) {
2569
+ user(login: $login) {
2570
+ id
2571
+ }
2572
+ }
2573
+ `;
2574
+ const result2 = await this.octokit.graphql(query, { login: options.targetOwnerLogin });
2575
+ targetOwnerId = result2.user.id;
2576
+ }
2577
+ const mutation = `
2578
+ mutation($projectId: ID!, $ownerId: ID!, $title: String!, $includeDraftIssues: Boolean) {
2579
+ copyProjectV2(input: {
2580
+ projectId: $projectId,
2581
+ ownerId: $ownerId,
2582
+ title: $title,
2583
+ includeDraftIssues: $includeDraftIssues
2584
+ }) {
2585
+ projectV2 {
2586
+ id
2587
+ number
2588
+ title
2589
+ url
2590
+ }
2591
+ }
2592
+ }
2593
+ `;
2594
+ const result = await this.octokit.graphql(mutation, {
2595
+ projectId: sourceProject.id,
2596
+ ownerId: targetOwnerId,
2597
+ title: options.title,
2598
+ includeDraftIssues: options.includeDraftIssues ?? false
2599
+ });
2600
+ return result.copyProjectV2.projectV2;
2601
+ }
2406
2602
  /**
2407
2603
  * Link a repository to a project
2408
2604
  * Note: This creates a linked repository in the project
@@ -2625,17 +2821,22 @@ async function setupSmartGitHub(cwd) {
2625
2821
  return manualGitHubConfig();
2626
2822
  }
2627
2823
  let userLogin;
2824
+ let orgs = [];
2628
2825
  try {
2629
2826
  const user = await client.getAuthenticatedUser();
2630
2827
  userLogin = user.login;
2631
2828
  console.log(chalk5.green(`
2632
- \u2713 Authenticated as ${userLogin}
2633
- `));
2829
+ \u2713 Authenticated as ${userLogin}`));
2830
+ orgs = await client.listOrganizations();
2831
+ if (orgs.length > 0) {
2832
+ console.log(chalk5.dim(` Organizations: ${orgs.map((o) => o.login).join(", ")}`));
2833
+ }
2834
+ console.log();
2634
2835
  } catch (error) {
2635
2836
  console.log(chalk5.yellow("\n\u26A0 Could not verify GitHub authentication.\n"));
2636
2837
  return manualGitHubConfig();
2637
2838
  }
2638
- const repoResult = await setupRepository(client, userLogin, cwd);
2839
+ const repoResult = await setupRepository(client, userLogin, orgs, cwd);
2639
2840
  if (repoResult.action === "skip") {
2640
2841
  return { enabled: false };
2641
2842
  }
@@ -2644,7 +2845,8 @@ async function setupSmartGitHub(cwd) {
2644
2845
  return { enabled: false };
2645
2846
  }
2646
2847
  client.setRepo(owner, repo);
2647
- const projectResult = await setupProjectBoard(client, userLogin);
2848
+ const ownerIsOrg = orgs.some((o) => o.login === owner);
2849
+ const projectResult = await setupProjectBoard(client, userLogin, owner, ownerIsOrg, orgs);
2648
2850
  let rulesetsCreated = false;
2649
2851
  const wantRulesets = await confirm({
2650
2852
  message: "Configure branch protection rules for main branch?",
@@ -2662,11 +2864,32 @@ async function setupSmartGitHub(cwd) {
2662
2864
  statuses: DEFAULT_STATUSES
2663
2865
  },
2664
2866
  repoCreated: repoResult.action === "create-public" || repoResult.action === "create-private",
2665
- projectCreated: projectResult.action === "create-blank",
2867
+ projectCreated: projectResult.action === "create-blank" || projectResult.action === "copy",
2666
2868
  rulesetsCreated
2667
2869
  };
2668
2870
  }
2669
- async function setupRepository(client, userLogin, cwd) {
2871
+ async function selectOwner(userLogin, orgs, prompt) {
2872
+ const choices = [
2873
+ {
2874
+ value: { login: userLogin, isOrg: false, name: userLogin },
2875
+ name: `${userLogin} (personal)`,
2876
+ description: "Your personal account"
2877
+ },
2878
+ ...orgs.map((org) => ({
2879
+ value: { login: org.login, isOrg: true, name: org.name || org.login },
2880
+ name: org.login,
2881
+ description: org.name || "Organization"
2882
+ }))
2883
+ ];
2884
+ if (choices.length === 1) {
2885
+ return choices[0].value;
2886
+ }
2887
+ return await select({
2888
+ message: prompt,
2889
+ choices
2890
+ });
2891
+ }
2892
+ async function setupRepository(client, userLogin, orgs, cwd) {
2670
2893
  const isGitRepo = isGitRepository(cwd);
2671
2894
  const existingRemote = isGitRepo ? detectGitHubRepo(cwd) : null;
2672
2895
  if (existingRemote) {
@@ -2714,17 +2937,34 @@ async function setupRepository(client, userLogin, cwd) {
2714
2937
  if (repoAction === "existing") {
2715
2938
  return await connectExistingRepo(client);
2716
2939
  }
2717
- return await createNewRepo(client, userLogin, repoAction === "create-private", cwd, isGitRepo);
2940
+ return await createNewRepo(
2941
+ client,
2942
+ userLogin,
2943
+ orgs,
2944
+ repoAction === "create-private",
2945
+ cwd,
2946
+ isGitRepo
2947
+ );
2718
2948
  }
2719
2949
  async function connectExistingRepo(client) {
2720
- const owner = await input({
2721
- message: "Repository owner (user or org):",
2722
- validate: (v) => v.trim() ? true : "Owner is required"
2723
- });
2724
- const repo = await input({
2725
- message: "Repository name:",
2726
- validate: (v) => v.trim() ? true : "Repository name is required"
2950
+ const ownerRepo = await input({
2951
+ message: "Repository (owner/repo or just repo for personal):",
2952
+ validate: (v) => v.trim() ? true : "Repository is required"
2727
2953
  });
2954
+ let owner;
2955
+ let repo;
2956
+ if (ownerRepo.includes("/")) {
2957
+ [owner, repo] = ownerRepo.split("/", 2);
2958
+ } else {
2959
+ try {
2960
+ const user = await client.getAuthenticatedUser();
2961
+ owner = user.login;
2962
+ repo = ownerRepo;
2963
+ } catch {
2964
+ console.log(chalk5.yellow("\n\u26A0 Could not determine owner. Please use owner/repo format.\n"));
2965
+ return { action: "skip" };
2966
+ }
2967
+ }
2728
2968
  try {
2729
2969
  const exists = await client.repoExists(owner, repo);
2730
2970
  if (!exists) {
@@ -2739,7 +2979,8 @@ async function connectExistingRepo(client) {
2739
2979
  }
2740
2980
  return { action: "existing", owner, repo };
2741
2981
  }
2742
- async function createNewRepo(client, userLogin, isPrivate, cwd, isGitRepo) {
2982
+ async function createNewRepo(client, userLogin, orgs, isPrivate, cwd, isGitRepo) {
2983
+ const ownerChoice = await selectOwner(userLogin, orgs, "Create repository under:");
2743
2984
  const repoName = await input({
2744
2985
  message: "New repository name:",
2745
2986
  validate: (v) => {
@@ -2753,35 +2994,39 @@ async function createNewRepo(client, userLogin, isPrivate, cwd, isGitRepo) {
2753
2994
  default: ""
2754
2995
  });
2755
2996
  try {
2756
- console.log(chalk5.dim(`
2757
- Creating ${isPrivate ? "private" : "public"} repository...`));
2997
+ console.log(
2998
+ chalk5.dim(
2999
+ `
3000
+ Creating ${isPrivate ? "private" : "public"} repository in ${ownerChoice.login}...`
3001
+ )
3002
+ );
2758
3003
  const newRepo = await client.createRepository({
2759
3004
  name: repoName,
2760
3005
  description: description || void 0,
2761
3006
  private: isPrivate,
2762
- autoInit: !isGitRepo
3007
+ autoInit: !isGitRepo,
2763
3008
  // Only auto-init if no local git repo
3009
+ org: ownerChoice.isOrg ? ownerChoice.login : void 0
2764
3010
  });
2765
3011
  console.log(chalk5.green(`\u2713 Created repository: ${newRepo.url}
2766
3012
  `));
3013
+ const remoteUrl = `git@github.com:${ownerChoice.login}/${repoName}.git`;
2767
3014
  if (isGitRepo) {
2768
- const remoteUrl = `git@github.com:${userLogin}/${repoName}.git`;
2769
3015
  const added = addGitRemote("origin", remoteUrl, cwd);
2770
3016
  if (added) {
2771
- console.log(chalk5.green(`\u2713 Added git remote 'origin'`));
3017
+ console.log(chalk5.green("\u2713 Added git remote 'origin'"));
2772
3018
  }
2773
3019
  } else {
2774
3020
  const initialized = initGitRepo(cwd);
2775
3021
  if (initialized) {
2776
3022
  console.log(chalk5.green("\u2713 Initialized git repository"));
2777
- const remoteUrl = `git@github.com:${userLogin}/${repoName}.git`;
2778
3023
  addGitRemote("origin", remoteUrl, cwd);
2779
- console.log(chalk5.green(`\u2713 Added git remote 'origin'`));
3024
+ console.log(chalk5.green("\u2713 Added git remote 'origin'"));
2780
3025
  }
2781
3026
  }
2782
3027
  return {
2783
3028
  action: isPrivate ? "create-private" : "create-public",
2784
- owner: userLogin,
3029
+ owner: ownerChoice.login,
2785
3030
  repo: repoName
2786
3031
  };
2787
3032
  } catch (error) {
@@ -2792,7 +3037,7 @@ async function createNewRepo(client, userLogin, isPrivate, cwd, isGitRepo) {
2792
3037
  return { action: "skip" };
2793
3038
  }
2794
3039
  }
2795
- async function setupProjectBoard(client, userLogin) {
3040
+ async function setupProjectBoard(client, userLogin, repoOwner, repoOwnerIsOrg, orgs) {
2796
3041
  const wantProject = await confirm({
2797
3042
  message: "Use a GitHub Project board for issue status tracking?",
2798
3043
  default: true
@@ -2806,12 +3051,17 @@ async function setupProjectBoard(client, userLogin) {
2806
3051
  {
2807
3052
  value: "existing",
2808
3053
  name: "Link existing project",
2809
- description: "Enter project number from URL"
3054
+ description: "Select from your projects or enter project number"
2810
3055
  },
2811
3056
  {
2812
3057
  value: "create-blank",
2813
3058
  name: "Create new blank project",
2814
- description: "Create a new project with default columns"
3059
+ description: "Create a new empty project"
3060
+ },
3061
+ {
3062
+ value: "copy",
3063
+ name: "Copy from existing project",
3064
+ description: "Copy structure from another project (yours or a template)"
2815
3065
  },
2816
3066
  {
2817
3067
  value: "skip",
@@ -2824,22 +3074,19 @@ async function setupProjectBoard(client, userLogin) {
2824
3074
  return { action: "skip" };
2825
3075
  }
2826
3076
  if (projectAction === "existing") {
2827
- return await linkExistingProject(client, userLogin);
3077
+ return await linkExistingProject(client, userLogin, repoOwner, repoOwnerIsOrg, orgs);
2828
3078
  }
2829
- return await createNewProject(client, userLogin);
2830
- }
2831
- async function linkExistingProject(client, userLogin) {
2832
- try {
2833
- const projects = await client.listUserProjects(10);
2834
- if (projects.length > 0) {
2835
- console.log(chalk5.dim("\n Your recent projects:"));
2836
- for (const p of projects) {
2837
- console.log(chalk5.dim(` #${p.number}: ${p.title}`));
2838
- }
2839
- console.log();
2840
- }
2841
- } catch {
3079
+ if (projectAction === "copy") {
3080
+ return await copyExistingProject(client, userLogin, repoOwner, repoOwnerIsOrg, orgs);
2842
3081
  }
3082
+ return await createNewProject(client, userLogin, repoOwner, repoOwnerIsOrg, orgs);
3083
+ }
3084
+ async function linkExistingProject(client, userLogin, repoOwner, repoOwnerIsOrg, orgs) {
3085
+ await showAvailableProjects(client, userLogin, orgs);
3086
+ const projectOwner = await input({
3087
+ message: "Project owner (user or org, leave empty for repo owner):",
3088
+ default: repoOwner
3089
+ });
2843
3090
  const projectNum = await number({
2844
3091
  message: "Project number (from project URL):",
2845
3092
  min: 1,
@@ -2848,35 +3095,137 @@ async function linkExistingProject(client, userLogin) {
2848
3095
  if (!projectNum) {
2849
3096
  return { action: "skip" };
2850
3097
  }
3098
+ const ownerIsOrg = projectOwner === repoOwner ? repoOwnerIsOrg : orgs.some((o) => o.login === projectOwner);
2851
3099
  try {
2852
- const project = await client.getUserProject(userLogin, projectNum);
3100
+ const project = ownerIsOrg ? await client.getOrgProject(projectOwner, projectNum) : await client.getUserProject(projectOwner, projectNum);
2853
3101
  if (project) {
2854
3102
  console.log(chalk5.green(`\u2713 Found project: ${project.title}`));
2855
3103
  return { action: "existing", projectNumber: projectNum };
2856
3104
  }
2857
- } catch {
3105
+ console.log(chalk5.yellow(`
3106
+ \u26A0 Project #${projectNum} not found for ${projectOwner}
3107
+ `));
3108
+ } catch (error) {
3109
+ const message = error instanceof Error ? error.message : "Unknown error";
3110
+ console.log(chalk5.yellow(`
3111
+ \u26A0 Could not verify project: ${message}
3112
+ `));
2858
3113
  }
2859
3114
  return { action: "existing", projectNumber: projectNum };
2860
3115
  }
2861
- async function createNewProject(client, userLogin) {
2862
- console.log(chalk5.yellow("\n\u26A0 Project creation requires manual setup via GitHub UI."));
2863
- console.log(` Visit: https://github.com/users/${userLogin}/projects?type=new
2864
- `);
2865
- const createdProject = await confirm({
2866
- message: "Did you create a project? Enter its number?",
2867
- default: false
3116
+ async function createNewProject(client, userLogin, repoOwner, repoOwnerIsOrg, orgs) {
3117
+ const ownerChoice = await selectOwner(userLogin, orgs, "Create project under:");
3118
+ const projectTitle = await input({
3119
+ message: "Project title:",
3120
+ default: "Project Board",
3121
+ validate: (v) => v.trim() ? true : "Title is required"
3122
+ });
3123
+ try {
3124
+ console.log(chalk5.dim(`
3125
+ Creating project "${projectTitle}" in ${ownerChoice.login}...`));
3126
+ const project = await client.createProject({
3127
+ title: projectTitle,
3128
+ ownerLogin: ownerChoice.login,
3129
+ isOrg: ownerChoice.isOrg
3130
+ });
3131
+ console.log(chalk5.green(`\u2713 Created project: ${project.title} (#${project.number})`));
3132
+ console.log(chalk5.dim(` URL: ${project.url}
3133
+ `));
3134
+ return {
3135
+ action: "create-blank",
3136
+ projectNumber: project.number,
3137
+ projectTitle: project.title
3138
+ };
3139
+ } catch (error) {
3140
+ const message = error instanceof Error ? error.message : "Unknown error";
3141
+ console.log(chalk5.red(`
3142
+ \u2717 Failed to create project: ${message}
3143
+ `));
3144
+ return { action: "skip" };
3145
+ }
3146
+ }
3147
+ async function copyExistingProject(client, userLogin, repoOwner, repoOwnerIsOrg, orgs) {
3148
+ console.log(chalk5.dim("\n Available projects to copy from:\n"));
3149
+ await showAvailableProjects(client, userLogin, orgs);
3150
+ const sourceOwner = await input({
3151
+ message: "Source project owner (user or org):",
3152
+ default: userLogin
3153
+ });
3154
+ const sourceProjectNum = await number({
3155
+ message: "Source project number to copy:",
3156
+ min: 1,
3157
+ validate: (v) => v && v >= 1 ? true : "Project number required"
2868
3158
  });
2869
- if (!createdProject) {
3159
+ if (!sourceProjectNum) {
2870
3160
  return { action: "skip" };
2871
3161
  }
2872
- const projectNum = await number({
2873
- message: "New project number:",
2874
- min: 1
3162
+ const targetOwnerChoice = await selectOwner(userLogin, orgs, "Create copied project under:");
3163
+ const projectTitle = await input({
3164
+ message: "Title for new project:",
3165
+ default: "Project Board (copy)",
3166
+ validate: (v) => v.trim() ? true : "Title is required"
2875
3167
  });
2876
- return {
2877
- action: "create-blank",
2878
- projectNumber: projectNum
2879
- };
3168
+ const includeDrafts = await confirm({
3169
+ message: "Include draft issues from source project?",
3170
+ default: false
3171
+ });
3172
+ const sourceIsOrg = sourceOwner !== userLogin && orgs.some((o) => o.login === sourceOwner);
3173
+ try {
3174
+ console.log(
3175
+ chalk5.dim(
3176
+ `
3177
+ Copying project #${sourceProjectNum} from ${sourceOwner} to ${targetOwnerChoice.login}...`
3178
+ )
3179
+ );
3180
+ const project = await client.copyProject({
3181
+ sourceProjectNumber: sourceProjectNum,
3182
+ sourceOwnerLogin: sourceOwner,
3183
+ sourceIsOrg,
3184
+ targetOwnerLogin: targetOwnerChoice.login,
3185
+ targetIsOrg: targetOwnerChoice.isOrg,
3186
+ title: projectTitle,
3187
+ includeDraftIssues: includeDrafts
3188
+ });
3189
+ console.log(chalk5.green(`\u2713 Copied project: ${project.title} (#${project.number})`));
3190
+ console.log(chalk5.dim(` URL: ${project.url}
3191
+ `));
3192
+ return {
3193
+ action: "copy",
3194
+ projectNumber: project.number,
3195
+ projectTitle: project.title
3196
+ };
3197
+ } catch (error) {
3198
+ const message = error instanceof Error ? error.message : "Unknown error";
3199
+ console.log(chalk5.red(`
3200
+ \u2717 Failed to copy project: ${message}
3201
+ `));
3202
+ return { action: "skip" };
3203
+ }
3204
+ }
3205
+ async function showAvailableProjects(client, userLogin, orgs) {
3206
+ try {
3207
+ const userProjects = await client.listUserProjects(10);
3208
+ if (userProjects.length > 0) {
3209
+ console.log(chalk5.dim(` ${userLogin} (personal):`));
3210
+ for (const p of userProjects) {
3211
+ console.log(chalk5.dim(` #${p.number}: ${p.title}`));
3212
+ }
3213
+ }
3214
+ } catch {
3215
+ }
3216
+ for (const org of orgs.slice(0, 3)) {
3217
+ try {
3218
+ const orgProjects = await client.listOrgProjects(org.login, 5);
3219
+ if (orgProjects.length > 0) {
3220
+ console.log(chalk5.dim(` ${org.login}:`));
3221
+ for (const p of orgProjects) {
3222
+ console.log(chalk5.dim(` #${p.number}: ${p.title}`));
3223
+ }
3224
+ }
3225
+ } catch {
3226
+ }
3227
+ }
3228
+ console.log();
2880
3229
  }
2881
3230
  async function setupBranchRulesets(client, owner, repo) {
2882
3231
  console.log(chalk5.dim("\n Configuring branch protection for 'main'...\n"));
@@ -2927,14 +3276,15 @@ async function setupBranchRulesets(client, owner, repo) {
2927
3276
  }
2928
3277
  }
2929
3278
  async function manualGitHubConfig() {
2930
- const owner = await input({
2931
- message: "GitHub repository owner:",
2932
- validate: (v) => v.trim() ? true : "Owner is required"
2933
- });
2934
- const repo = await input({
2935
- message: "GitHub repository name:",
2936
- validate: (v) => v.trim() ? true : "Repository name is required"
3279
+ const ownerRepo = await input({
3280
+ message: "GitHub repository (owner/repo):",
3281
+ validate: (v) => {
3282
+ if (!v.trim()) return "Repository is required";
3283
+ if (!v.includes("/")) return "Please use owner/repo format";
3284
+ return true;
3285
+ }
2937
3286
  });
3287
+ const [owner, repo] = ownerRepo.split("/", 2);
2938
3288
  const useProject = await confirm({
2939
3289
  message: "Use a GitHub Project board?",
2940
3290
  default: false
@@ -4242,11 +4592,11 @@ async function upgrade(options) {
4242
4592
  ];
4243
4593
  const updates = [];
4244
4594
  const sdlcChannel = detectReleaseChannel(VERSION);
4245
- const sdlcLatest = await getLatestVersion("opencode-sdlc", sdlcChannel);
4595
+ const sdlcLatest = await getLatestVersion("opencode-sdlc-plugin", sdlcChannel);
4246
4596
  if (sdlcLatest) {
4247
4597
  const sdlcHasUpdate = semver__default.valid(sdlcLatest) && semver__default.valid(VERSION) ? semver__default.gt(sdlcLatest, VERSION) : sdlcLatest !== VERSION;
4248
4598
  updates.push({
4249
- name: "opencode-sdlc",
4599
+ name: "opencode-sdlc-plugin",
4250
4600
  current: VERSION,
4251
4601
  latest: sdlcLatest,
4252
4602
  updateAvailable: sdlcHasUpdate
@@ -4424,34 +4774,34 @@ Current version: ${existingVersion}`));
4424
4774
  writeSpinner.succeed("Configuration files updated");
4425
4775
  logger.section("Updating Packages");
4426
4776
  const fileManager = new FileManager();
4427
- const sdlcUpdate = updatesAvailable.find((u) => u.name === "opencode-sdlc");
4777
+ const sdlcUpdate = updatesAvailable.find((u) => u.name === "opencode-sdlc-plugin");
4428
4778
  if (sdlcUpdate) {
4429
- const sdlcSpinner = ora5("Updating opencode-sdlc...").start();
4779
+ const sdlcSpinner = ora5("Updating opencode-sdlc-plugin...").start();
4430
4780
  try {
4431
4781
  const channel = detectReleaseChannel(VERSION);
4432
- await fileManager.installDependencies([`opencode-sdlc@${channel}`]);
4433
- sdlcSpinner.succeed(`opencode-sdlc updated to ${sdlcUpdate.latest}`);
4782
+ await fileManager.installDependencies([`opencode-sdlc-plugin@${channel}`]);
4783
+ sdlcSpinner.succeed(`opencode-sdlc-plugin updated to ${sdlcUpdate.latest}`);
4434
4784
  } catch (err) {
4435
- sdlcSpinner.fail("Failed to update opencode-sdlc");
4785
+ sdlcSpinner.fail("Failed to update opencode-sdlc-plugin");
4436
4786
  logger.error(err instanceof Error ? err.message : String(err));
4437
4787
  }
4438
4788
  } else {
4439
4789
  const generator = new ConfigGenerator(fullAnswers);
4440
4790
  const packages = generator.getRequiredPackages();
4441
- const sdlcPackage = packages.find((p) => p.startsWith("opencode-sdlc"));
4791
+ const sdlcPackage = packages.find((p) => p.startsWith("opencode-sdlc-plugin"));
4442
4792
  if (sdlcPackage) {
4443
- const sdlcSpinner = ora5("Installing opencode-sdlc...").start();
4793
+ const sdlcSpinner = ora5("Installing opencode-sdlc-plugin...").start();
4444
4794
  try {
4445
4795
  const channel = detectReleaseChannel(VERSION);
4446
- await fileManager.installDependencies([`opencode-sdlc@${channel}`]);
4447
- sdlcSpinner.succeed("opencode-sdlc installed");
4796
+ await fileManager.installDependencies([`opencode-sdlc-plugin@${channel}`]);
4797
+ sdlcSpinner.succeed("opencode-sdlc-plugin installed");
4448
4798
  } catch (err) {
4449
- sdlcSpinner.fail("Failed to install opencode-sdlc");
4799
+ sdlcSpinner.fail("Failed to install opencode-sdlc-plugin");
4450
4800
  logger.error(err instanceof Error ? err.message : String(err));
4451
4801
  }
4452
4802
  }
4453
4803
  }
4454
- const pluginUpdates = updatesAvailable.filter((u) => u.name !== "opencode-sdlc");
4804
+ const pluginUpdates = updatesAvailable.filter((u) => u.name !== "opencode-sdlc-plugin");
4455
4805
  if (pluginUpdates.length > 0) {
4456
4806
  const pluginSpinner = ora5("Updating plugins...").start();
4457
4807
  try {
@@ -4465,7 +4815,7 @@ Current version: ${existingVersion}`));
4465
4815
  } else {
4466
4816
  const generator = new ConfigGenerator(fullAnswers);
4467
4817
  const allPackages = generator.getRequiredPackages();
4468
- const pluginPackages = allPackages.filter((p) => !p.startsWith("opencode-sdlc"));
4818
+ const pluginPackages = allPackages.filter((p) => !p.startsWith("opencode-sdlc-plugin"));
4469
4819
  if (pluginPackages.length > 0) {
4470
4820
  const pluginSpinner = ora5(`Installing plugins: ${pluginPackages.join(", ")}...`).start();
4471
4821
  try {