octocode-cli 1.2.5 → 1.2.6

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/README.md CHANGED
@@ -41,19 +41,27 @@ npx octocode-cli
41
41
  ## 📦 Installation & Usage
42
42
 
43
43
  ### Interactive Mode
44
+
44
45
  The recommended way to use Octocode CLI. Access all features through a unified menu:
45
- - **Octocode Configuration**: Setup MCP servers and GitHub auth.
46
- - **Manage System Skills**: Install AI capabilities.
47
- - **Manage System MCP**: Sync configs and browse the marketplace.
46
+
47
+ | Menu Item | Description |
48
+ |-----------|-------------|
49
+ | **🐙 Octocode MCP** | Install and configure Octocode MCP for your IDEs |
50
+ | **🐙 Octocode Skills** | Install AI-powered research, planning & review skills |
51
+ | **🧠 Manage System Skills** | Browse skills marketplace and manage installed skills |
52
+ | **🔑 Manage Auth** | Sign in/out via Octocode OAuth or gh CLI |
53
+ | **⚡ Manage System MCP** | Sync configs, browse MCP marketplace, inspect settings |
48
54
 
49
55
  ```bash
50
56
  npx octocode-cli
51
57
  ```
52
58
 
53
59
  ### CLI Commands
60
+
54
61
  For automation and power users, Octocode CLI offers a comprehensive command-line interface.
55
62
 
56
63
  #### 1. Install Octocode MCP
64
+
57
65
  Install the GitHub MCP server for your preferred IDE.
58
66
 
59
67
  ```bash
@@ -64,56 +72,82 @@ octocode-cli install
64
72
  octocode-cli install --ide cursor --method npx
65
73
  octocode-cli install --ide claude-desktop --method direct
66
74
  octocode-cli install --ide windsurf
75
+ octocode-cli install --ide zed
67
76
  ```
68
77
 
78
+ **Supported IDEs**: `cursor`, `claude-desktop`, `claude-code`, `windsurf`, `zed`, `vscode-cline`, `vscode-roo`, `vscode-continue`, `opencode`, `trae`, `antigravity`
79
+
69
80
  #### 2. Manage Authentication
81
+
70
82
  Securely authenticate with GitHub. Credentials are encrypted (AES-256-GCM) and stored in `~/.octocode/`.
71
83
 
72
84
  ```bash
73
- # Interactive login
85
+ # Interactive login (OAuth device flow)
74
86
  octocode-cli login
75
87
 
76
- # Check status
88
+ # Check authentication status
77
89
  octocode-cli status
78
90
 
79
91
  # Enterprise Login
80
92
  octocode-cli login --hostname github.mycompany.com
93
+
94
+ # Sign out
95
+ octocode-cli logout
96
+
97
+ # Auth management menu
98
+ octocode-cli auth
81
99
  ```
82
100
 
83
- #### 3. Sync Configurations
101
+ #### 3. Get GitHub Token
102
+
103
+ Retrieve tokens for scripting or debugging.
104
+
105
+ ```bash
106
+ # Get token (auto: env → gh → octocode)
107
+ octocode-cli token
108
+
109
+ # Get token from specific source
110
+ octocode-cli token --type=octocode
111
+ octocode-cli token --type=gh
112
+
113
+ # Show token source and user info
114
+ octocode-cli token --source
115
+
116
+ # JSON output for scripting
117
+ octocode-cli token --json
118
+ ```
119
+
120
+ #### 4. Sync Configurations
121
+
84
122
  Keep your MCP settings consistent across different editors.
85
123
 
86
124
  ```bash
87
125
  # Sync all IDEs
88
126
  octocode-cli sync
89
127
 
90
- # Preview changes
128
+ # Preview changes (dry run)
91
129
  octocode-cli sync --dry-run
130
+
131
+ # Show sync status
132
+ octocode-cli sync --status
133
+
134
+ # Force sync (auto-resolve conflicts)
135
+ octocode-cli sync --force
92
136
  ```
93
137
 
94
- #### 4. Manage Skills
138
+ #### 5. Manage Skills
139
+
95
140
  Install AI skills for Claude Code.
96
141
 
97
142
  ```bash
98
143
  # List available skills
99
144
  octocode-cli skills list
100
145
 
101
- # Install all standard skills
146
+ # Install all skills
102
147
  octocode-cli skills install
103
- ```
104
148
 
105
- #### 5. Additional Commands
106
- Other useful commands for power users.
107
-
108
- ```bash
109
- # Logout from GitHub
110
- octocode-cli logout
111
-
112
- # Get GitHub token (for scripting)
113
- octocode-cli token
114
-
115
- # Auth management menu
116
- octocode-cli auth
149
+ # Force reinstall (overwrite existing)
150
+ octocode-cli skills install --force
117
151
  ```
118
152
 
119
153
  ---
@@ -79,7 +79,12 @@ class TimeoutError extends Error {
79
79
  async function withTimeout(promise, ms) {
80
80
  return Promise.race([
81
81
  promise,
82
- new Promise((_, reject) => setTimeout(() => reject(new TimeoutError(`Operation timed out after ${ms}ms`)), ms))
82
+ new Promise(
83
+ (_, reject) => setTimeout(
84
+ () => reject(new TimeoutError(`Operation timed out after ${ms}ms`)),
85
+ ms
86
+ )
87
+ )
83
88
  ]);
84
89
  }
85
90
  let _keychainAvailable = null;
@@ -99,8 +104,7 @@ const credentialsCache = /* @__PURE__ */ new Map();
99
104
  const CACHE_TTL_MS$1 = 5 * 60 * 1e3;
100
105
  function isCacheValid$1(hostname) {
101
106
  const cached = credentialsCache.get(hostname);
102
- if (!cached)
103
- return false;
107
+ if (!cached) return false;
104
108
  const age = Date.now() - cached.cachedAt;
105
109
  return age < CACHE_TTL_MS$1;
106
110
  }
@@ -156,20 +160,17 @@ async function keychainStore(hostname, credentials) {
156
160
  await setPassword(KEYCHAIN_SERVICE, hostname, data);
157
161
  }
158
162
  async function keychainGet(hostname) {
159
- if (!checkKeychainAvailable())
160
- return null;
163
+ if (!checkKeychainAvailable()) return null;
161
164
  try {
162
165
  const data = await getPassword(KEYCHAIN_SERVICE, hostname);
163
- if (!data)
164
- return null;
166
+ if (!data) return null;
165
167
  return JSON.parse(data);
166
168
  } catch {
167
169
  return null;
168
170
  }
169
171
  }
170
172
  async function keychainDelete(hostname) {
171
- if (!checkKeychainAvailable())
172
- return false;
173
+ if (!checkKeychainAvailable()) return false;
173
174
  try {
174
175
  return await deletePassword(KEYCHAIN_SERVICE, hostname);
175
176
  } catch {
@@ -223,7 +224,9 @@ function readCredentialsStore() {
223
224
  const decrypted = decrypt(encryptedContent);
224
225
  return JSON.parse(decrypted);
225
226
  } catch (error) {
226
- console.error("\n ⚠ Warning: Could not read credentials file. You may need to login again.");
227
+ console.error(
228
+ "\n ⚠ Warning: Could not read credentials file. You may need to login again."
229
+ );
227
230
  console.error(` File: ${CREDENTIALS_FILE}`);
228
231
  if (error instanceof Error && error.message) {
229
232
  console.error(` Reason: ${error.message}
@@ -251,7 +254,9 @@ function removeFromFileStorage(hostname) {
251
254
  }
252
255
  return false;
253
256
  } catch (err) {
254
- console.warn(`[token-storage] Failed to remove from file storage: ${err instanceof Error ? err.message : String(err)}`);
257
+ console.warn(
258
+ `[token-storage] Failed to remove from file storage: ${err instanceof Error ? err.message : String(err)}`
259
+ );
255
260
  return false;
256
261
  }
257
262
  }
@@ -285,12 +290,17 @@ async function storeCredentials(credentials) {
285
290
  };
286
291
  if (isSecureStorageAvailable()) {
287
292
  try {
288
- await withTimeout(keychainStore(hostname, normalizedCredentials), KEYRING_TIMEOUT_MS);
293
+ await withTimeout(
294
+ keychainStore(hostname, normalizedCredentials),
295
+ KEYRING_TIMEOUT_MS
296
+ );
289
297
  removeFromFileStorage(hostname);
290
298
  invalidateCredentialsCache(hostname);
291
299
  return { success: true, insecureStorageUsed: false };
292
300
  } catch (err) {
293
- console.warn(`[token-storage] Keyring storage failed, using file fallback: ${err instanceof Error ? err.message : String(err)}`);
301
+ console.warn(
302
+ `[token-storage] Keyring storage failed, using file fallback: ${err instanceof Error ? err.message : String(err)}`
303
+ );
294
304
  }
295
305
  }
296
306
  try {
@@ -301,7 +311,9 @@ async function storeCredentials(credentials) {
301
311
  return { success: true, insecureStorageUsed: true };
302
312
  } catch (fileError) {
303
313
  console.error(`[token-storage] CRITICAL: All storage methods failed!`);
304
- console.error(` Error: ${fileError instanceof Error ? fileError.message : String(fileError)}`);
314
+ console.error(
315
+ ` Error: ${fileError instanceof Error ? fileError.message : String(fileError)}`
316
+ );
305
317
  throw new Error("Failed to store credentials");
306
318
  }
307
319
  }
@@ -324,12 +336,16 @@ async function getCredentials(hostname = "github.com", options) {
324
336
  async function fetchCredentialsFromStorage(normalizedHostname) {
325
337
  if (isSecureStorageAvailable()) {
326
338
  try {
327
- const creds = await withTimeout(keychainGet(normalizedHostname), KEYRING_TIMEOUT_MS);
328
- if (creds)
329
- return creds;
339
+ const creds = await withTimeout(
340
+ keychainGet(normalizedHostname),
341
+ KEYRING_TIMEOUT_MS
342
+ );
343
+ if (creds) return creds;
330
344
  } catch (err) {
331
345
  if (!(err instanceof TimeoutError)) {
332
- console.warn(`[token-storage] Keyring read failed: ${err instanceof Error ? err.message : String(err)}`);
346
+ console.warn(
347
+ `[token-storage] Keyring read failed: ${err instanceof Error ? err.message : String(err)}`
348
+ );
333
349
  }
334
350
  }
335
351
  }
@@ -355,9 +371,14 @@ async function deleteCredentials(hostname = "github.com") {
355
371
  let deletedFromFile = false;
356
372
  if (isSecureStorageAvailable()) {
357
373
  try {
358
- deletedFromKeyring = await withTimeout(keychainDelete(normalizedHostname), KEYRING_TIMEOUT_MS);
374
+ deletedFromKeyring = await withTimeout(
375
+ keychainDelete(normalizedHostname),
376
+ KEYRING_TIMEOUT_MS
377
+ );
359
378
  } catch (err) {
360
- console.warn(`[token-storage] Keyring delete failed: ${err instanceof Error ? err.message : String(err)}`);
379
+ console.warn(
380
+ `[token-storage] Keyring delete failed: ${err instanceof Error ? err.message : String(err)}`
381
+ );
361
382
  }
362
383
  }
363
384
  const store = readCredentialsStore();
@@ -7843,18 +7864,18 @@ defineLazyProperty(apps, "safari", () => detectPlatformBinary({
7843
7864
  darwin: "Safari"
7844
7865
  }));
7845
7866
  const SKILLS_MARKETPLACES = [
7846
- // === OFFICIAL BUNDLED SKILLS ===
7867
+ // === OCTOCODE SKILLS ===
7847
7868
  {
7848
- id: "octocode-official",
7849
- name: "🐙 Octocode Official",
7850
- type: "local",
7851
- owner: "ArekSrorth",
7869
+ id: "octocode-skills",
7870
+ name: "Octocode Skills",
7871
+ type: "github",
7872
+ owner: "bgauryy",
7852
7873
  repo: "octocode-mcp",
7853
7874
  branch: "main",
7854
- skillsPath: "packages/octocode-cli/skills",
7875
+ skillsPath: "skills",
7855
7876
  skillPattern: "skill-folders",
7856
- description: "Official research, planning, review & roast skills",
7857
- url: "https://github.com/ArekSrorth/octocode-mcp"
7877
+ description: "Research, planning, implementation & PR review skills",
7878
+ url: "https://github.com/bgauryy/octocode-mcp/tree/main/skills"
7858
7879
  },
7859
7880
  // === COMMUNITY MARKETPLACES ===
7860
7881
  {
@@ -8472,7 +8493,7 @@ async function installSkill(skill) {
8472
8493
  }
8473
8494
  async function showOfficialFlowMenu(totalSkills, notInstalledCount) {
8474
8495
  console.log();
8475
- console.log(` ${bold("Octocode Official Skills")}`);
8496
+ console.log(` ${bold("🐙 Octocode Skills")}`);
8476
8497
  console.log(` ${dim(`${totalSkills} skills available`)}`);
8477
8498
  console.log();
8478
8499
  const choices = [];
@@ -8634,69 +8655,17 @@ async function runMarketplaceFlow() {
8634
8655
  }
8635
8656
  }
8636
8657
  }
8637
- async function installAllOctocodeSkills() {
8638
- const source = SKILLS_MARKETPLACES.find((s) => s.id === "octocode-official");
8639
- if (!source) {
8640
- return {
8641
- installed: 0,
8642
- alreadyInstalled: 0,
8643
- failed: 0,
8644
- allInstalled: false
8645
- };
8646
- }
8647
- let skills;
8648
- try {
8649
- skills = await fetchMarketplaceSkills(source);
8650
- } catch {
8651
- return {
8652
- installed: 0,
8653
- alreadyInstalled: 0,
8654
- failed: 0,
8655
- allInstalled: false
8656
- };
8657
- }
8658
- if (skills.length === 0) {
8659
- return { installed: 0, alreadyInstalled: 0, failed: 0, allInstalled: true };
8660
- }
8661
- const destDir = getSkillsDestDir();
8662
- const skillsToInstall = skills.filter((skill) => !isSkillInstalled(skill.name));
8663
- const alreadyInstalled = skills.length - skillsToInstall.length;
8664
- if (skillsToInstall.length === 0) {
8665
- return {
8666
- installed: 0,
8667
- alreadyInstalled,
8668
- failed: 0,
8669
- allInstalled: true
8670
- };
8671
- }
8672
- let installed = 0;
8673
- let failed = 0;
8674
- for (const skill of skillsToInstall) {
8675
- const result = await installMarketplaceSkill(skill, destDir);
8676
- if (result.success) {
8677
- installed++;
8678
- } else {
8679
- failed++;
8680
- }
8681
- }
8682
- return {
8683
- installed,
8684
- alreadyInstalled,
8685
- failed,
8686
- allInstalled: failed === 0
8687
- };
8688
- }
8689
- async function runOctocodeOfficialFlow() {
8690
- const source = SKILLS_MARKETPLACES.find((s) => s.id === "octocode-official");
8658
+ async function runOctocodeSkillsFlow() {
8659
+ const source = SKILLS_MARKETPLACES.find((s) => s.id === "octocode-skills");
8691
8660
  if (!source) {
8692
8661
  console.log();
8693
- console.log(` ${c("red", "✗")} Octocode Official source not found`);
8662
+ console.log(` ${c("red", "✗")} Octocode Skills source not found`);
8694
8663
  console.log();
8695
8664
  await pressEnterToContinue$2();
8696
8665
  return;
8697
8666
  }
8698
8667
  console.log();
8699
- const spinner = new Spinner(`Loading ${source.name}...`).start();
8668
+ const spinner = new Spinner(`Loading Octocode Skills...`).start();
8700
8669
  let skills;
8701
8670
  try {
8702
8671
  skills = await fetchMarketplaceSkills(source);
@@ -8721,8 +8690,8 @@ async function runOctocodeOfficialFlow() {
8721
8690
  const notInstalledCount = skills.filter(
8722
8691
  (s) => !isSkillInstalled(s.name)
8723
8692
  ).length;
8724
- let inOfficialFlow = true;
8725
- while (inOfficialFlow) {
8693
+ let inFlow = true;
8694
+ while (inFlow) {
8726
8695
  const menuChoice = await showOfficialFlowMenu(
8727
8696
  skills.length,
8728
8697
  notInstalledCount
@@ -8730,7 +8699,7 @@ async function runOctocodeOfficialFlow() {
8730
8699
  switch (menuChoice) {
8731
8700
  case "install-all":
8732
8701
  await installAllSkills(skills);
8733
- inOfficialFlow = false;
8702
+ inFlow = false;
8734
8703
  break;
8735
8704
  case "browse": {
8736
8705
  let inSkillsBrowser = true;
@@ -8749,7 +8718,7 @@ async function runOctocodeOfficialFlow() {
8749
8718
  }
8750
8719
  case "back":
8751
8720
  default:
8752
- inOfficialFlow = false;
8721
+ inFlow = false;
8753
8722
  break;
8754
8723
  }
8755
8724
  }
@@ -8861,11 +8830,6 @@ async function showSkillsMenu(installedCount) {
8861
8830
  description: "View, remove, or inspect individual skills"
8862
8831
  });
8863
8832
  }
8864
- choices.push({
8865
- name: "🐙 Octocode Official 📦",
8866
- value: "octocode-official",
8867
- description: "Research, PR review, local search & more"
8868
- });
8869
8833
  choices.push({
8870
8834
  name: "🌐 Browse Marketplace",
8871
8835
  value: "marketplace",
@@ -9145,9 +9109,6 @@ async function runSkillsMenu() {
9145
9109
  case "manage":
9146
9110
  await manageInstalledSkills();
9147
9111
  break;
9148
- case "octocode-official":
9149
- await runOctocodeOfficialFlow();
9150
- break;
9151
9112
  case "marketplace":
9152
9113
  await runMarketplaceFlow();
9153
9114
  break;
@@ -9802,6 +9763,23 @@ function buildSkillsMenuItem(skills) {
9802
9763
  description: `${c("cyan", "→")} Install skills for AI-powered coding workflows`
9803
9764
  };
9804
9765
  }
9766
+ function buildOctocodeSkillsMenuItem(skills) {
9767
+ const octocodeSkillsInstalled = skills.skills.filter(
9768
+ (s) => s.name.startsWith("octocode-") && s.installed
9769
+ ).length;
9770
+ if (octocodeSkillsInstalled > 0) {
9771
+ return {
9772
+ name: `🐙 Octocode Skills ${c("green", "✓")}`,
9773
+ value: "octocode-skills",
9774
+ description: `${octocodeSkillsInstalled} installed • Research, planning & review`
9775
+ };
9776
+ }
9777
+ return {
9778
+ name: "🐙 Octocode Skills",
9779
+ value: "octocode-skills",
9780
+ description: "Install AI-powered research, planning & review skills"
9781
+ };
9782
+ }
9805
9783
  function getAuthSourceDisplay(auth2) {
9806
9784
  switch (auth2.tokenSource) {
9807
9785
  case "gh-cli":
@@ -9893,10 +9871,7 @@ function printContextualHints(state) {
9893
9871
  c("yellow", ` ▸ Prompts: Use /research, /plan, /implement in chat`)
9894
9872
  );
9895
9873
  console.log(
9896
- c(
9897
- "yellow",
9898
- ` ▸ Skills: Add all via Manage System Skills → Octocode Official`
9899
- )
9874
+ c("yellow", ` ▸ Skills: Add via 🐙 Octocode Skills in main menu`)
9900
9875
  );
9901
9876
  console.log(
9902
9877
  c(
@@ -9923,6 +9898,7 @@ async function showMainMenu(state) {
9923
9898
  printContextualHints(state);
9924
9899
  const choices = [];
9925
9900
  choices.push(buildOctocodeMenuItem(state));
9901
+ choices.push(buildOctocodeSkillsMenuItem(state.skills));
9926
9902
  choices.push(buildSkillsMenuItem(state.skills));
9927
9903
  choices.push(buildAuthMenuItem(state.githubAuth));
9928
9904
  choices.push({
@@ -9978,14 +9954,6 @@ async function showOctocodeMenu(state) {
9978
9954
  description: "Server options & preferences"
9979
9955
  });
9980
9956
  }
9981
- if (!state.skills.allInstalled && state.skills.hasSkills) {
9982
- const notInstalled = state.skills.skills.filter((s) => !s.installed).length;
9983
- choices.push({
9984
- name: `🧠 Install All Skills ${c("cyan", `(${notInstalled} available)`)}`,
9985
- value: "install-skills",
9986
- description: "One-click install of all Octocode skills"
9987
- });
9988
- }
9989
9957
  choices.push(
9990
9958
  new Separator()
9991
9959
  );
@@ -10032,45 +10000,6 @@ async function runOctocodeFlow() {
10032
10000
  await runConfigOptionsFlow();
10033
10001
  console.log();
10034
10002
  break;
10035
- case "install-skills": {
10036
- console.log();
10037
- const skillsSpinner = new Spinner(
10038
- "Installing all Octocode skills..."
10039
- ).start();
10040
- const result = await installAllOctocodeSkills();
10041
- if (result.installed > 0) {
10042
- skillsSpinner.succeed(
10043
- `Installed ${result.installed} skill${result.installed !== 1 ? "s" : ""}!`
10044
- );
10045
- console.log();
10046
- console.log(
10047
- ` ${c("green", "✓")} ${result.installed} skill${result.installed !== 1 ? "s" : ""} installed successfully`
10048
- );
10049
- if (result.alreadyInstalled > 0) {
10050
- console.log(
10051
- ` ${dim(`(${result.alreadyInstalled} already installed)`)}`
10052
- );
10053
- }
10054
- if (result.failed > 0) {
10055
- console.log(
10056
- ` ${c("yellow", "⚠")} ${result.failed} skill${result.failed !== 1 ? "s" : ""} failed to install`
10057
- );
10058
- }
10059
- console.log();
10060
- console.log(` ${bold("Skills are now available in Claude Code!")}`);
10061
- } else if (result.allInstalled) {
10062
- skillsSpinner.succeed("All skills already installed!");
10063
- console.log();
10064
- console.log(` ${c("green", "✓")} All Octocode skills are installed`);
10065
- } else {
10066
- skillsSpinner.fail("Failed to install skills");
10067
- console.log();
10068
- console.log(` ${c("red", "✗")} Could not install skills`);
10069
- }
10070
- console.log();
10071
- await pressEnterToContinue();
10072
- break;
10073
- }
10074
10003
  case "back":
10075
10004
  default:
10076
10005
  inMenu = false;
@@ -10505,6 +10434,9 @@ async function handleMenuChoice(choice) {
10505
10434
  case "octocode":
10506
10435
  await runOctocodeFlow();
10507
10436
  return true;
10437
+ case "octocode-skills":
10438
+ await runOctocodeSkillsFlow();
10439
+ return true;
10508
10440
  case "skills":
10509
10441
  await runSkillsMenu();
10510
10442
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "octocode-cli",
3
- "version": "1.2.5",
3
+ "version": "1.2.6",
4
4
  "description": "Interactive CLI installer for octocode-mcp - Configure MCP servers for Cursor, Claude Desktop, and more",
5
5
  "keywords": [
6
6
  "octocode",
@@ -33,13 +33,13 @@
33
33
  "@octokit/auth-oauth-device": "^8.0.3",
34
34
  "@octokit/oauth-methods": "^6.0.2",
35
35
  "@octokit/request": "^10.0.7",
36
+ "octocode-shared": "1.0.0",
36
37
  "open": "^11.0.0"
37
38
  },
38
39
  "devDependencies": {
39
40
  "@types/node": "^22.15.29",
40
41
  "@vitest/coverage-v8": "^4.0.16",
41
42
  "eslint": "^9.18.0",
42
- "octocode-shared": "workspace:^",
43
43
  "tsx": "^4.21.0",
44
44
  "typescript": "^5.9.3",
45
45
  "vite": "^7.3.0",
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: octocode-implement-with-contect
2
+ name: octocode-implement
3
3
  description: Implement features from spec documents (context/doc required)
4
4
  ---
5
5