gh-manager-cli 1.43.0 → 1.45.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [1.45.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.44.0...v1.45.0) (2026-06-07)
2
+
3
+
4
+ ### Features
5
+
6
+ * add repository creation (Ctrl+N) and transfer (Shift+M) ([#60](https://github.com/wiiiimm/gh-manager-cli/issues/60)) ([2e9c271](https://github.com/wiiiimm/gh-manager-cli/commit/2e9c2711b985be9373a94483e0bd0b420dc48aec))
7
+
1
8
  # [1.43.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.42.0...v1.43.0) (2026-06-05)
2
9
 
3
10
 
package/README.md CHANGED
@@ -84,7 +84,9 @@ On first run, you'll be prompted to authenticate with GitHub (OAuth recommended)
84
84
  - View detailed info (`I`) - Shows repository metadata, language, size, and timestamps
85
85
  - Open in browser (Enter/`O`) — for forks a chooser lets you open this repo or the upstream
86
86
  - Jump to upstream (`P`) — moves cursor to the parent if loaded; otherwise fetches and shows it
87
+ - Create new repository (`Ctrl+N`) — prompts for a name (with the personal/organisation slug shown in front), choose visibility with `Tab`, and surfaces GitHub errors inline
87
88
  - Rename repository (`Ctrl+R`) with inline validation and automatic cache update
89
+ - Transfer repository to another owner (`Shift+M`) — prompts for the destination owner, requires a verification code, shows a final confirmation, and surfaces GitHub errors inline
88
90
  - Copy repository URL to clipboard (`C`) with SSH/HTTPS options
89
91
  - Delete repository (`Del` or `Backspace`) with secure two-step confirmation
90
92
  - Archive/unarchive repositories (`Ctrl+A`) with confirmation prompts
@@ -95,7 +97,7 @@ On first run, you'll be prompted to authenticate with GitHub (OAuth recommended)
95
97
  ### User Interface & Experience
96
98
  - **Keyboard Navigation**: Full keyboard control (arrow keys, PageUp/Down, `Ctrl+G`/`G`)
97
99
  - **Display Density**: Toggle between compact/cozy/comfy spacing (`T`)
98
- - **Colour Themes**: Four themes (Default, Ocean, Forest, Monochrome) cycled with `Shift+T`, persisted across restarts
100
+ - **Colour Themes**: Four themes (Default, Ocean, Forest, Monochrome) cycled with `Shift+T`, persisted across restarts; the selected-row highlight is theme-aware and tuned per theme for readable contrast
99
101
  - **Visual Indicators**: Fork status, private/internal/archived badges, language colors, visibility status
100
102
  - **Enterprise Support**: Full support for GitHub Enterprise with Internal repository visibility
101
103
  - **Organization Context**: Switch between personal and organization accounts with ENT badge for enterprise orgs
@@ -272,7 +274,7 @@ Launch the app, then use the keys below:
272
274
  - Stars: Number of stars
273
275
  - **Sort Direction**: `D` to open sort direction modal (ascending/descending)
274
276
  - **Display Density**: `T` to toggle compact/cozy/comfy
275
- - **Colour Theme**: `Shift+T` to cycle themes (Default → Ocean → Forest → Monochrome); selection persists across restarts
277
+ - **Colour Theme**: `Shift+T` to cycle themes (Default → Ocean → Forest → Monochrome); selection persists across restarts. Each theme defines its own selected-row highlight (a darker on-theme background) so the highlighted repository stays high-contrast
276
278
  - **Fork Status**: Always enabled — shows commits **ahead** and **behind** upstream once enrichment completes (see below)
277
279
  - **Visibility Filter**: `V` opens modal (All, Public, Private/Internal for enterprise)
278
280
  - **Archive Filter**: `A` toggles archive filter (All → Unarchived → Archived)
@@ -288,6 +290,10 @@ Launch the app, then use the keys below:
288
290
  - **Quit**: `Q`
289
291
 
290
292
  ### Repository Actions
293
+ - **Create repository**: `Ctrl+N` to create a new repository in the current context (personal or organisation)
294
+ - Prompts for a name with the owner slug (`owner/`) shown in front
295
+ - `Tab` cycles visibility (Private/Public, plus Internal for enterprise organisations)
296
+ - Enter to create; GitHub errors (e.g. name already exists) are shown inline
291
297
  - **Repository info**: `I` to view detailed metadata (size, language, timestamps)
292
298
  - **Cache info**: `K` to inspect Apollo cache status
293
299
  - **Archive/Unarchive**: `Ctrl+A` with confirmation prompt
@@ -298,6 +304,10 @@ Launch the app, then use the keys below:
298
304
  - **Star/Unstar**: `Ctrl+S` to toggle star status for any repository
299
305
  - **Sync fork**: `Ctrl+F` (for forks only, shows ahead/behind counts and handles conflicts)
300
306
  - **Rename repository**: `Ctrl+R` with inline validation
307
+ - **Transfer repository**: `Shift+M` (Move) to transfer ownership to another user or organisation
308
+ - Prompts for the destination owner (`new-owner/<repo>` preview)
309
+ - Requires typing a randomly generated verification code (like delete), then a final confirmation before transferring
310
+ - GitHub errors (e.g. insufficient permissions) are shown inline
301
311
  - **Copy URL**: `C` to copy repository URL to clipboard (SSH/HTTPS options)
302
312
 
303
313
  ### General
@@ -811,6 +811,85 @@ async function deleteRepositoryRest(token, owner, repo) {
811
811
  });
812
812
  throw new Error(msg);
813
813
  }
814
+ async function parseGitHubRestError(res, defaultMessage) {
815
+ let msg = defaultMessage;
816
+ try {
817
+ const errBody = await res.json();
818
+ if (errBody?.message) msg = errBody.message;
819
+ if (Array.isArray(errBody?.errors) && errBody.errors.length > 0) {
820
+ const details = errBody.errors.map((e) => e.message || (e.field ? `${e.field}: ${e.code}` : e.code)).filter(Boolean).join("; ");
821
+ if (details) msg += ` (${details})`;
822
+ }
823
+ } catch {
824
+ }
825
+ return msg;
826
+ }
827
+ async function createRepositoryRest(token, options) {
828
+ const { name, visibility, description, org } = options;
829
+ const url = org ? `https://api.github.com/orgs/${org}/repos` : `https://api.github.com/user/repos`;
830
+ const body = { name };
831
+ if (visibility === "INTERNAL") {
832
+ body.visibility = "internal";
833
+ } else {
834
+ body.private = visibility === "PRIVATE";
835
+ }
836
+ if (description) body.description = description;
837
+ logger.info("Creating repository", { name, visibility, org: org ?? null, url });
838
+ let res;
839
+ try {
840
+ res = await fetch(url, {
841
+ method: "POST",
842
+ headers: {
843
+ "Authorization": `token ${token}`,
844
+ "Accept": "application/vnd.github+json",
845
+ "Content-Type": "application/json",
846
+ "User-Agent": "gh-manager-cli"
847
+ },
848
+ body: JSON.stringify(body)
849
+ });
850
+ } catch (networkError) {
851
+ logger.error("Network error during repository creation", { error: networkError.message, name, org: org ?? null });
852
+ throw new Error(`Network error whilst creating repository: ${networkError.message}`);
853
+ }
854
+ if (res.status === 201) {
855
+ const data = await res.json();
856
+ logger.info("Successfully created repository", {
857
+ nameWithOwner: data.full_name,
858
+ url: data.html_url
859
+ });
860
+ return { nameWithOwner: data.full_name, url: data.html_url };
861
+ }
862
+ const msg = await parseGitHubRestError(res, `Failed to create repository (status ${res.status})`);
863
+ logger.error("Failed to create repository", { status: res.status, error: msg, name, org: org ?? null });
864
+ throw new Error(msg);
865
+ }
866
+ async function transferRepositoryRest(token, owner, repo, newOwner) {
867
+ const url = `https://api.github.com/repos/${owner}/${repo}/transfer`;
868
+ logger.info("Transferring repository", { owner, repo, newOwner, url });
869
+ let res;
870
+ try {
871
+ res = await fetch(url, {
872
+ method: "POST",
873
+ headers: {
874
+ "Authorization": `token ${token}`,
875
+ "Accept": "application/vnd.github+json",
876
+ "Content-Type": "application/json",
877
+ "User-Agent": "gh-manager-cli"
878
+ },
879
+ body: JSON.stringify({ new_owner: newOwner })
880
+ });
881
+ } catch (networkError) {
882
+ logger.error("Network error during repository transfer", { error: networkError.message, owner, repo, newOwner });
883
+ throw new Error(`Network error whilst transferring repository: ${networkError.message}`);
884
+ }
885
+ if (res.status === 202 || res.status === 200) {
886
+ logger.info("Successfully initiated repository transfer", { owner, repo, newOwner, status: res.status });
887
+ return;
888
+ }
889
+ const msg = await parseGitHubRestError(res, `Failed to transfer repository (status ${res.status})`);
890
+ logger.error("Failed to transfer repository", { status: res.status, error: msg, owner, repo, newOwner });
891
+ throw new Error(msg);
892
+ }
814
893
  async function getStarredRepositories(client, first, after) {
815
894
  logger.info("Fetching starred repositories", {
816
895
  first,
@@ -1607,6 +1686,8 @@ export {
1607
1686
  fetchViewerReposPageUnified,
1608
1687
  searchRepositoriesUnified,
1609
1688
  deleteRepositoryRest,
1689
+ createRepositoryRest,
1690
+ transferRepositoryRest,
1610
1691
  getStarredRepositories,
1611
1692
  starRepository,
1612
1693
  unstarRepository,
@@ -3,6 +3,7 @@ import {
3
3
  archiveRepositoryById,
4
4
  changeRepositoryVisibility,
5
5
  checkOrganizationIsEnterprise,
6
+ createRepositoryRest,
6
7
  deleteRepositoryRest,
7
8
  enrichForksWithAheadBehind,
8
9
  fetchRepositoryById,
@@ -21,6 +22,7 @@ import {
21
22
  searchRepositoriesUnified,
22
23
  starRepository,
23
24
  syncForkWithUpstream,
25
+ transferRepositoryRest,
24
26
  unarchiveRepositoryById,
25
27
  unstarRepository,
26
28
  updateCacheAfterArchive,
@@ -28,11 +30,12 @@ import {
28
30
  updateCacheAfterRename,
29
31
  updateCacheAfterVisibilityChange,
30
32
  updateCacheWithRepository
31
- } from "./chunk-MKIUBPVD.js";
33
+ } from "./chunk-WHX6IOTW.js";
32
34
  export {
33
35
  archiveRepositoryById,
34
36
  changeRepositoryVisibility,
35
37
  checkOrganizationIsEnterprise,
38
+ createRepositoryRest,
36
39
  deleteRepositoryRest,
37
40
  enrichForksWithAheadBehind,
38
41
  fetchRepositoryById,
@@ -51,6 +54,7 @@ export {
51
54
  searchRepositoriesUnified,
52
55
  starRepository,
53
56
  syncForkWithUpstream,
57
+ transferRepositoryRest,
54
58
  unarchiveRepositoryById,
55
59
  unstarRepository,
56
60
  updateCacheAfterArchive,