octocode-cli 1.2.0 → 1.2.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/README.md +18 -1
- package/out/octocode-cli.js +573 -354
- package/package.json +1 -1
- package/skills/README.md +17 -12
- package/skills/octocode-implement/SKILL.md +65 -60
- package/skills/octocode-local-search/SKILL.md +72 -41
- package/skills/octocode-plan/SKILL.md +431 -0
- package/skills/octocode-pr-review/SKILL.md +43 -1
- package/skills/octocode-research/SKILL.md +43 -101
- package/skills/octocode-roast/SKILL.md +39 -0
- package/skills/octocode-generate/SKILL.md +0 -172
package/out/octocode-cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import fs, { existsSync, readFileSync, mkdirSync, writeFileSync, statSync } from "node:fs";
|
|
3
3
|
import path, { join, dirname, resolve } from "node:path";
|
|
4
4
|
import childProcess, { spawnSync, exec, execSync, execFile as execFile$2 } from "node:child_process";
|
|
5
|
-
import { isMac, isWindows, getAppDataPath, HOME,
|
|
5
|
+
import { isMac, isWindows, getAppDataPath, HOME, getTokenWithRefresh, hasEnvToken, getEnvTokenSource, getCredentialsSync, isTokenExpired, getCredentials, deleteCredentials, storeCredentials, getCredentialsFilePath, resolveTokenFull, isUsingSecureStorage as isUsingSecureStorage$1 } from "octocode-shared";
|
|
6
6
|
import os, { homedir } from "node:os";
|
|
7
7
|
import { promisify } from "node:util";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
@@ -278,10 +278,21 @@ function printWelcome() {
|
|
|
278
278
|
}
|
|
279
279
|
function printGoodbye() {
|
|
280
280
|
console.log();
|
|
281
|
-
console.log(
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
console.log(
|
|
281
|
+
console.log(
|
|
282
|
+
` ${c("cyan", "💡")} ${bold("Quick tips for better AI coding with Octocode:")}`
|
|
283
|
+
);
|
|
284
|
+
console.log();
|
|
285
|
+
console.log(
|
|
286
|
+
` ${c("green", "▸")} ${dim("Prompts:")} Use ${c("cyan", "/research")}, ${c("cyan", "/plan")}, ${c("cyan", "/implement")} in chat`
|
|
287
|
+
);
|
|
288
|
+
console.log(
|
|
289
|
+
` ${c("green", "▸")} ${dim("Skills:")} Add all via ${c("cyan", "Manage System Skills")} → ${c("cyan", "Octocode Official")}`
|
|
290
|
+
);
|
|
291
|
+
console.log(
|
|
292
|
+
` ${c("green", "▸")} ${dim("Context:")} Add ${c("cyan", "AGENTS.md")} to your project ${dim("(you can ask octocode)")}`
|
|
293
|
+
);
|
|
294
|
+
console.log();
|
|
295
|
+
console.log(` 🔍🐙 ${c("underscore", "https://octocode.ai")}`);
|
|
285
296
|
console.log();
|
|
286
297
|
}
|
|
287
298
|
const activeSpinners = /* @__PURE__ */ new Set();
|
|
@@ -367,6 +378,11 @@ class Spinner {
|
|
|
367
378
|
if (text) this.text = text;
|
|
368
379
|
return this.stop("⚠", "yellow");
|
|
369
380
|
}
|
|
381
|
+
/** Update the spinner text while running */
|
|
382
|
+
update(text) {
|
|
383
|
+
this.text = text;
|
|
384
|
+
return this;
|
|
385
|
+
}
|
|
370
386
|
}
|
|
371
387
|
function dirExists(dirPath) {
|
|
372
388
|
try {
|
|
@@ -7305,7 +7321,7 @@ const SKILLS_MARKETPLACES = [
|
|
|
7305
7321
|
branch: "main",
|
|
7306
7322
|
skillsPath: "packages/octocode-cli/skills",
|
|
7307
7323
|
skillPattern: "skill-folders",
|
|
7308
|
-
description: "Official research, planning, review
|
|
7324
|
+
description: "Official research, planning, review & roast skills",
|
|
7309
7325
|
url: "https://github.com/ArekSrorth/octocode-mcp"
|
|
7310
7326
|
},
|
|
7311
7327
|
// === COMMUNITY MARKETPLACES ===
|
|
@@ -7922,6 +7938,110 @@ async function installSkill(skill) {
|
|
|
7922
7938
|
await pressEnterToContinue$2();
|
|
7923
7939
|
return result.success;
|
|
7924
7940
|
}
|
|
7941
|
+
async function showOfficialFlowMenu(totalSkills, notInstalledCount) {
|
|
7942
|
+
console.log();
|
|
7943
|
+
console.log(` ${bold("Octocode Official Skills")}`);
|
|
7944
|
+
console.log(` ${dim(`${totalSkills} skills available`)}`);
|
|
7945
|
+
console.log();
|
|
7946
|
+
const choices = [];
|
|
7947
|
+
if (notInstalledCount > 0) {
|
|
7948
|
+
choices.push({
|
|
7949
|
+
name: `${c("green", "⚡")} Install All Skills (${notInstalledCount} to install)`,
|
|
7950
|
+
value: "install-all",
|
|
7951
|
+
description: dim("One-click install of all Octocode skills")
|
|
7952
|
+
});
|
|
7953
|
+
} else {
|
|
7954
|
+
choices.push({
|
|
7955
|
+
name: `${c("green", "✓")} All skills installed!`,
|
|
7956
|
+
value: "browse",
|
|
7957
|
+
description: dim("Browse to reinstall or view details")
|
|
7958
|
+
});
|
|
7959
|
+
}
|
|
7960
|
+
choices.push({
|
|
7961
|
+
name: `${c("cyan", "📋")} Browse Skills Individually`,
|
|
7962
|
+
value: "browse",
|
|
7963
|
+
description: dim("View details and install one by one")
|
|
7964
|
+
});
|
|
7965
|
+
choices.push(
|
|
7966
|
+
new Separator()
|
|
7967
|
+
);
|
|
7968
|
+
choices.push({
|
|
7969
|
+
name: `${c("dim", "← Back")}`,
|
|
7970
|
+
value: "back"
|
|
7971
|
+
});
|
|
7972
|
+
const choice = await select({
|
|
7973
|
+
message: "",
|
|
7974
|
+
choices,
|
|
7975
|
+
loop: false,
|
|
7976
|
+
theme: {
|
|
7977
|
+
prefix: " ",
|
|
7978
|
+
style: {
|
|
7979
|
+
highlight: (text) => c("magenta", text)
|
|
7980
|
+
}
|
|
7981
|
+
}
|
|
7982
|
+
});
|
|
7983
|
+
return choice;
|
|
7984
|
+
}
|
|
7985
|
+
async function installAllSkills(skills) {
|
|
7986
|
+
const destDir = getSkillsDestDir();
|
|
7987
|
+
const skillsToInstall = skills.filter((skill) => !isSkillInstalled(skill.name));
|
|
7988
|
+
if (skillsToInstall.length === 0) {
|
|
7989
|
+
console.log();
|
|
7990
|
+
console.log(` ${c("green", "✓")} All skills are already installed!`);
|
|
7991
|
+
console.log();
|
|
7992
|
+
await pressEnterToContinue$2();
|
|
7993
|
+
return;
|
|
7994
|
+
}
|
|
7995
|
+
console.log();
|
|
7996
|
+
console.log(
|
|
7997
|
+
` ${bold("Installing")} ${skillsToInstall.length} ${bold("skills...")}`
|
|
7998
|
+
);
|
|
7999
|
+
console.log();
|
|
8000
|
+
const spinner = new Spinner(
|
|
8001
|
+
`Installing ${skillsToInstall.length} skills...`
|
|
8002
|
+
).start();
|
|
8003
|
+
let installed = 0;
|
|
8004
|
+
let failed = 0;
|
|
8005
|
+
const errors = [];
|
|
8006
|
+
for (const skill of skillsToInstall) {
|
|
8007
|
+
spinner.update(
|
|
8008
|
+
`Installing ${skill.displayName}... (${installed + failed + 1}/${skillsToInstall.length})`
|
|
8009
|
+
);
|
|
8010
|
+
const result = await installMarketplaceSkill(skill, destDir);
|
|
8011
|
+
if (result.success) {
|
|
8012
|
+
installed++;
|
|
8013
|
+
} else {
|
|
8014
|
+
failed++;
|
|
8015
|
+
errors.push({
|
|
8016
|
+
skill: skill.displayName,
|
|
8017
|
+
error: result.error || "Unknown error"
|
|
8018
|
+
});
|
|
8019
|
+
}
|
|
8020
|
+
}
|
|
8021
|
+
if (failed === 0) {
|
|
8022
|
+
spinner.succeed(`All ${installed} skills installed successfully!`);
|
|
8023
|
+
} else {
|
|
8024
|
+
spinner.warn(`Installed ${installed} skills, ${failed} failed`);
|
|
8025
|
+
}
|
|
8026
|
+
console.log();
|
|
8027
|
+
if (installed > 0) {
|
|
8028
|
+
console.log(
|
|
8029
|
+
` ${c("green", "✓")} Successfully installed ${installed} skill(s)`
|
|
8030
|
+
);
|
|
8031
|
+
console.log(` ${dim("Location:")} ${c("cyan", destDir)}`);
|
|
8032
|
+
}
|
|
8033
|
+
if (errors.length > 0) {
|
|
8034
|
+
console.log();
|
|
8035
|
+
console.log(` ${c("red", "✗")} Failed to install:`);
|
|
8036
|
+
for (const { skill, error } of errors) {
|
|
8037
|
+
console.log(` ${c("red", "•")} ${skill}: ${dim(error)}`);
|
|
8038
|
+
}
|
|
8039
|
+
}
|
|
8040
|
+
console.log();
|
|
8041
|
+
console.log(` ${bold("Skills are now available in Claude Code!")}`);
|
|
8042
|
+
console.log();
|
|
8043
|
+
await pressEnterToContinue$2();
|
|
8044
|
+
}
|
|
7925
8045
|
async function runMarketplaceFlow() {
|
|
7926
8046
|
console.log();
|
|
7927
8047
|
console.log(
|
|
@@ -7982,6 +8102,58 @@ async function runMarketplaceFlow() {
|
|
|
7982
8102
|
}
|
|
7983
8103
|
}
|
|
7984
8104
|
}
|
|
8105
|
+
async function installAllOctocodeSkills() {
|
|
8106
|
+
const source = SKILLS_MARKETPLACES.find((s) => s.id === "octocode-official");
|
|
8107
|
+
if (!source) {
|
|
8108
|
+
return {
|
|
8109
|
+
installed: 0,
|
|
8110
|
+
alreadyInstalled: 0,
|
|
8111
|
+
failed: 0,
|
|
8112
|
+
allInstalled: false
|
|
8113
|
+
};
|
|
8114
|
+
}
|
|
8115
|
+
let skills;
|
|
8116
|
+
try {
|
|
8117
|
+
skills = await fetchMarketplaceSkills(source);
|
|
8118
|
+
} catch {
|
|
8119
|
+
return {
|
|
8120
|
+
installed: 0,
|
|
8121
|
+
alreadyInstalled: 0,
|
|
8122
|
+
failed: 0,
|
|
8123
|
+
allInstalled: false
|
|
8124
|
+
};
|
|
8125
|
+
}
|
|
8126
|
+
if (skills.length === 0) {
|
|
8127
|
+
return { installed: 0, alreadyInstalled: 0, failed: 0, allInstalled: true };
|
|
8128
|
+
}
|
|
8129
|
+
const destDir = getSkillsDestDir();
|
|
8130
|
+
const skillsToInstall = skills.filter((skill) => !isSkillInstalled(skill.name));
|
|
8131
|
+
const alreadyInstalled = skills.length - skillsToInstall.length;
|
|
8132
|
+
if (skillsToInstall.length === 0) {
|
|
8133
|
+
return {
|
|
8134
|
+
installed: 0,
|
|
8135
|
+
alreadyInstalled,
|
|
8136
|
+
failed: 0,
|
|
8137
|
+
allInstalled: true
|
|
8138
|
+
};
|
|
8139
|
+
}
|
|
8140
|
+
let installed = 0;
|
|
8141
|
+
let failed = 0;
|
|
8142
|
+
for (const skill of skillsToInstall) {
|
|
8143
|
+
const result = await installMarketplaceSkill(skill, destDir);
|
|
8144
|
+
if (result.success) {
|
|
8145
|
+
installed++;
|
|
8146
|
+
} else {
|
|
8147
|
+
failed++;
|
|
8148
|
+
}
|
|
8149
|
+
}
|
|
8150
|
+
return {
|
|
8151
|
+
installed,
|
|
8152
|
+
alreadyInstalled,
|
|
8153
|
+
failed,
|
|
8154
|
+
allInstalled: failed === 0
|
|
8155
|
+
};
|
|
8156
|
+
}
|
|
7985
8157
|
async function runOctocodeOfficialFlow() {
|
|
7986
8158
|
const source = SKILLS_MARKETPLACES.find((s) => s.id === "octocode-official");
|
|
7987
8159
|
if (!source) {
|
|
@@ -8014,16 +8186,39 @@ async function runOctocodeOfficialFlow() {
|
|
|
8014
8186
|
await pressEnterToContinue$2();
|
|
8015
8187
|
return;
|
|
8016
8188
|
}
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
|
|
8026
|
-
|
|
8189
|
+
const notInstalledCount = skills.filter(
|
|
8190
|
+
(s) => !isSkillInstalled(s.name)
|
|
8191
|
+
).length;
|
|
8192
|
+
let inOfficialFlow = true;
|
|
8193
|
+
while (inOfficialFlow) {
|
|
8194
|
+
const menuChoice = await showOfficialFlowMenu(
|
|
8195
|
+
skills.length,
|
|
8196
|
+
notInstalledCount
|
|
8197
|
+
);
|
|
8198
|
+
switch (menuChoice) {
|
|
8199
|
+
case "install-all":
|
|
8200
|
+
await installAllSkills(skills);
|
|
8201
|
+
inOfficialFlow = false;
|
|
8202
|
+
break;
|
|
8203
|
+
case "browse": {
|
|
8204
|
+
let inSkillsBrowser = true;
|
|
8205
|
+
while (inSkillsBrowser) {
|
|
8206
|
+
const skillChoice = await browseSkills(source, skills);
|
|
8207
|
+
if (skillChoice === "back") {
|
|
8208
|
+
inSkillsBrowser = false;
|
|
8209
|
+
continue;
|
|
8210
|
+
}
|
|
8211
|
+
const detailChoice = await showSkillDetails(skillChoice);
|
|
8212
|
+
if (detailChoice === "install") {
|
|
8213
|
+
await installSkill(skillChoice);
|
|
8214
|
+
}
|
|
8215
|
+
}
|
|
8216
|
+
break;
|
|
8217
|
+
}
|
|
8218
|
+
case "back":
|
|
8219
|
+
default:
|
|
8220
|
+
inOfficialFlow = false;
|
|
8221
|
+
break;
|
|
8027
8222
|
}
|
|
8028
8223
|
}
|
|
8029
8224
|
}
|
|
@@ -9239,36 +9434,6 @@ async function exchangeDeviceCode(options) {
|
|
|
9239
9434
|
function toTimestamp2(apiTimeInMs, expirationInSeconds) {
|
|
9240
9435
|
return new Date(apiTimeInMs + expirationInSeconds * 1e3).toISOString();
|
|
9241
9436
|
}
|
|
9242
|
-
async function refreshToken(options) {
|
|
9243
|
-
const request$1 = options.request || request;
|
|
9244
|
-
const response = await oauthRequest(
|
|
9245
|
-
request$1,
|
|
9246
|
-
"POST /login/oauth/access_token",
|
|
9247
|
-
{
|
|
9248
|
-
client_id: options.clientId,
|
|
9249
|
-
client_secret: options.clientSecret,
|
|
9250
|
-
grant_type: "refresh_token",
|
|
9251
|
-
refresh_token: options.refreshToken
|
|
9252
|
-
}
|
|
9253
|
-
);
|
|
9254
|
-
const apiTimeInMs = new Date(response.headers.date).getTime();
|
|
9255
|
-
const authentication = {
|
|
9256
|
-
clientType: "github-app",
|
|
9257
|
-
clientId: options.clientId,
|
|
9258
|
-
clientSecret: options.clientSecret,
|
|
9259
|
-
token: response.data.access_token,
|
|
9260
|
-
refreshToken: response.data.refresh_token,
|
|
9261
|
-
expiresAt: toTimestamp3(apiTimeInMs, response.data.expires_in),
|
|
9262
|
-
refreshTokenExpiresAt: toTimestamp3(
|
|
9263
|
-
apiTimeInMs,
|
|
9264
|
-
response.data.refresh_token_expires_in
|
|
9265
|
-
)
|
|
9266
|
-
};
|
|
9267
|
-
return { ...response, authentication };
|
|
9268
|
-
}
|
|
9269
|
-
function toTimestamp3(apiTimeInMs, expirationInSeconds) {
|
|
9270
|
-
return new Date(apiTimeInMs + expirationInSeconds * 1e3).toISOString();
|
|
9271
|
-
}
|
|
9272
9437
|
async function getOAuthAccessToken(state, options) {
|
|
9273
9438
|
const cachedAuthentication = getCachedAuthentication(state, options.auth);
|
|
9274
9439
|
if (cachedAuthentication) return cachedAuthentication;
|
|
@@ -9543,60 +9708,57 @@ async function logout(hostname = DEFAULT_HOSTNAME, options) {
|
|
|
9543
9708
|
await deleteCredentials(hostname);
|
|
9544
9709
|
return { success: true };
|
|
9545
9710
|
}
|
|
9546
|
-
|
|
9547
|
-
|
|
9548
|
-
|
|
9711
|
+
function getAuthStatus(hostname = DEFAULT_HOSTNAME) {
|
|
9712
|
+
if (hasEnvToken()) {
|
|
9713
|
+
const envSource = getEnvTokenSource();
|
|
9549
9714
|
return {
|
|
9550
|
-
|
|
9551
|
-
|
|
9715
|
+
authenticated: true,
|
|
9716
|
+
hostname,
|
|
9717
|
+
username: void 0,
|
|
9718
|
+
// Can't determine username from env token
|
|
9719
|
+
tokenSource: "env",
|
|
9720
|
+
// Store the specific env var for display (e.g., 'env:GH_TOKEN')
|
|
9721
|
+
envTokenSource: envSource ?? void 0
|
|
9552
9722
|
};
|
|
9553
9723
|
}
|
|
9554
|
-
|
|
9724
|
+
const credentials = getCredentialsSync(hostname);
|
|
9725
|
+
if (credentials) {
|
|
9726
|
+
const tokenExpired = isTokenExpired(credentials);
|
|
9555
9727
|
return {
|
|
9556
|
-
|
|
9557
|
-
|
|
9728
|
+
authenticated: !tokenExpired,
|
|
9729
|
+
hostname: credentials.hostname,
|
|
9730
|
+
username: credentials.username,
|
|
9731
|
+
tokenExpired,
|
|
9732
|
+
tokenSource: "octocode"
|
|
9558
9733
|
};
|
|
9559
9734
|
}
|
|
9560
|
-
|
|
9735
|
+
const ghAuth = checkGitHubAuth();
|
|
9736
|
+
if (ghAuth.authenticated) {
|
|
9561
9737
|
return {
|
|
9562
|
-
|
|
9563
|
-
|
|
9738
|
+
authenticated: true,
|
|
9739
|
+
hostname,
|
|
9740
|
+
username: ghAuth.username,
|
|
9741
|
+
tokenSource: "gh-cli"
|
|
9564
9742
|
};
|
|
9565
9743
|
}
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
request: request.defaults({
|
|
9575
|
-
baseUrl: getApiBaseUrl(hostname)
|
|
9576
|
-
})
|
|
9577
|
-
});
|
|
9578
|
-
const newToken = {
|
|
9579
|
-
token: response.authentication.token,
|
|
9580
|
-
tokenType: "oauth",
|
|
9581
|
-
refreshToken: response.authentication.refreshToken,
|
|
9582
|
-
expiresAt: response.authentication.expiresAt,
|
|
9583
|
-
refreshTokenExpiresAt: response.authentication.refreshTokenExpiresAt
|
|
9584
|
-
};
|
|
9585
|
-
await updateToken(hostname, newToken);
|
|
9586
|
-
return {
|
|
9587
|
-
success: true,
|
|
9588
|
-
username: credentials.username,
|
|
9589
|
-
hostname
|
|
9590
|
-
};
|
|
9591
|
-
} catch (error) {
|
|
9744
|
+
return {
|
|
9745
|
+
authenticated: false,
|
|
9746
|
+
tokenSource: "none"
|
|
9747
|
+
};
|
|
9748
|
+
}
|
|
9749
|
+
async function getAuthStatusAsync(hostname = DEFAULT_HOSTNAME) {
|
|
9750
|
+
if (hasEnvToken()) {
|
|
9751
|
+
const envSource = getEnvTokenSource();
|
|
9592
9752
|
return {
|
|
9593
|
-
|
|
9594
|
-
|
|
9753
|
+
authenticated: true,
|
|
9754
|
+
hostname,
|
|
9755
|
+
username: void 0,
|
|
9756
|
+
// Can't determine username from env token
|
|
9757
|
+
tokenSource: "env",
|
|
9758
|
+
envTokenSource: envSource ?? void 0
|
|
9595
9759
|
};
|
|
9596
9760
|
}
|
|
9597
|
-
|
|
9598
|
-
function getAuthStatus(hostname = DEFAULT_HOSTNAME) {
|
|
9599
|
-
const credentials = getCredentialsSync(hostname);
|
|
9761
|
+
const credentials = await getCredentials(hostname);
|
|
9600
9762
|
if (credentials) {
|
|
9601
9763
|
const tokenExpired = isTokenExpired(credentials);
|
|
9602
9764
|
return {
|
|
@@ -9622,31 +9784,12 @@ function getAuthStatus(hostname = DEFAULT_HOSTNAME) {
|
|
|
9622
9784
|
};
|
|
9623
9785
|
}
|
|
9624
9786
|
async function getOctocodeToken(hostname = DEFAULT_HOSTNAME) {
|
|
9625
|
-
const
|
|
9626
|
-
if (
|
|
9627
|
-
if (isTokenExpired(credentials)) {
|
|
9628
|
-
if (credentials.token.refreshToken) {
|
|
9629
|
-
const result = await refreshAuthToken(hostname);
|
|
9630
|
-
if (result.success) {
|
|
9631
|
-
const updated = await getCredentials(hostname);
|
|
9632
|
-
if (updated?.token.token) {
|
|
9633
|
-
return {
|
|
9634
|
-
token: updated.token.token,
|
|
9635
|
-
source: "octocode",
|
|
9636
|
-
username: updated.username
|
|
9637
|
-
};
|
|
9638
|
-
}
|
|
9639
|
-
}
|
|
9640
|
-
}
|
|
9641
|
-
return {
|
|
9642
|
-
token: null,
|
|
9643
|
-
source: "none"
|
|
9644
|
-
};
|
|
9645
|
-
}
|
|
9787
|
+
const result = await getTokenWithRefresh(hostname, DEFAULT_CLIENT_ID);
|
|
9788
|
+
if (result.token) {
|
|
9646
9789
|
return {
|
|
9647
|
-
token:
|
|
9790
|
+
token: result.token,
|
|
9648
9791
|
source: "octocode",
|
|
9649
|
-
username:
|
|
9792
|
+
username: result.username
|
|
9650
9793
|
};
|
|
9651
9794
|
}
|
|
9652
9795
|
return {
|
|
@@ -9676,22 +9819,23 @@ async function getToken(hostname = DEFAULT_HOSTNAME, preferredSource = "auto") {
|
|
|
9676
9819
|
if (preferredSource === "gh") {
|
|
9677
9820
|
return getGhCliToken(hostname);
|
|
9678
9821
|
}
|
|
9679
|
-
const
|
|
9680
|
-
|
|
9681
|
-
|
|
9822
|
+
const result = await resolveTokenFull({
|
|
9823
|
+
hostname,
|
|
9824
|
+
getGhCliToken: getGitHubCLIToken
|
|
9825
|
+
});
|
|
9826
|
+
if (result?.token) {
|
|
9827
|
+
const source = result.source === "gh-cli" ? "gh-cli" : result.source?.startsWith("env:") ? "env" : "octocode";
|
|
9682
9828
|
return {
|
|
9683
|
-
token:
|
|
9684
|
-
source
|
|
9685
|
-
username:
|
|
9686
|
-
|
|
9687
|
-
envSource: source ?? void 0
|
|
9829
|
+
token: result.token,
|
|
9830
|
+
source,
|
|
9831
|
+
username: result.username,
|
|
9832
|
+
envSource: result.source?.startsWith("env:") ? result.source : void 0
|
|
9688
9833
|
};
|
|
9689
9834
|
}
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
}
|
|
9694
|
-
return getOctocodeToken(hostname);
|
|
9835
|
+
return {
|
|
9836
|
+
token: null,
|
|
9837
|
+
source: "none"
|
|
9838
|
+
};
|
|
9695
9839
|
}
|
|
9696
9840
|
function getStoragePath() {
|
|
9697
9841
|
return getCredentialsFilePath();
|
|
@@ -9699,6 +9843,19 @@ function getStoragePath() {
|
|
|
9699
9843
|
function isUsingSecureStorage() {
|
|
9700
9844
|
return isUsingSecureStorage$1();
|
|
9701
9845
|
}
|
|
9846
|
+
function getTokenType(source, envSource) {
|
|
9847
|
+
switch (source) {
|
|
9848
|
+
case "env":
|
|
9849
|
+
return envSource ?? "env:GITHUB_TOKEN";
|
|
9850
|
+
case "gh-cli":
|
|
9851
|
+
return "gh-cli";
|
|
9852
|
+
case "octocode":
|
|
9853
|
+
return "octocode-storage";
|
|
9854
|
+
case "none":
|
|
9855
|
+
default:
|
|
9856
|
+
return "none";
|
|
9857
|
+
}
|
|
9858
|
+
}
|
|
9702
9859
|
function getOctocodeState() {
|
|
9703
9860
|
const allClients = getAllClientInstallStatus();
|
|
9704
9861
|
const installedClients = allClients.filter((c2) => c2.octocodeInstalled);
|
|
@@ -9752,12 +9909,12 @@ function getSkillsState() {
|
|
|
9752
9909
|
hasSkills: skills.length > 0
|
|
9753
9910
|
};
|
|
9754
9911
|
}
|
|
9755
|
-
function getAppState() {
|
|
9912
|
+
async function getAppState() {
|
|
9756
9913
|
return {
|
|
9757
9914
|
octocode: getOctocodeState(),
|
|
9758
9915
|
skills: getSkillsState(),
|
|
9759
9916
|
currentClient: detectCurrentClient(),
|
|
9760
|
-
githubAuth:
|
|
9917
|
+
githubAuth: await getAuthStatusAsync()
|
|
9761
9918
|
};
|
|
9762
9919
|
}
|
|
9763
9920
|
async function pressEnterToContinue() {
|
|
@@ -9795,35 +9952,54 @@ function buildSkillsMenuItem(skills) {
|
|
|
9795
9952
|
return {
|
|
9796
9953
|
name: `🧠 Manage System Skills ${c("green", "✓")}`,
|
|
9797
9954
|
value: "skills",
|
|
9798
|
-
description: `${skills.totalInstalledCount} installed •
|
|
9955
|
+
description: `${skills.totalInstalledCount} installed • Research, PR Review & more`
|
|
9799
9956
|
};
|
|
9800
9957
|
}
|
|
9801
9958
|
if (skills.totalInstalledCount > 0) {
|
|
9802
9959
|
return {
|
|
9803
9960
|
name: "🧠 Manage System Skills",
|
|
9804
9961
|
value: "skills",
|
|
9805
|
-
description: `${skills.totalInstalledCount} installed •
|
|
9962
|
+
description: `${skills.totalInstalledCount}/${skills.skills.length} installed • Get more skills!`
|
|
9806
9963
|
};
|
|
9807
9964
|
}
|
|
9808
9965
|
return {
|
|
9809
|
-
name: "
|
|
9966
|
+
name: `🧠 ${bold("Manage System Skills")} ${c("cyan", "★")}`,
|
|
9810
9967
|
value: "skills",
|
|
9811
|
-
description:
|
|
9968
|
+
description: `${c("cyan", "→")} Install skills for AI-powered coding workflows`
|
|
9812
9969
|
};
|
|
9813
9970
|
}
|
|
9971
|
+
function getAuthSourceDisplay(auth2) {
|
|
9972
|
+
switch (auth2.tokenSource) {
|
|
9973
|
+
case "gh-cli":
|
|
9974
|
+
return "gh CLI";
|
|
9975
|
+
case "env": {
|
|
9976
|
+
if (auth2.envTokenSource) {
|
|
9977
|
+
const varName = auth2.envTokenSource.replace("env:", "");
|
|
9978
|
+
return `env (${varName})`;
|
|
9979
|
+
}
|
|
9980
|
+
return "env var";
|
|
9981
|
+
}
|
|
9982
|
+
case "octocode":
|
|
9983
|
+
return "Octocode";
|
|
9984
|
+
default:
|
|
9985
|
+
return "unknown";
|
|
9986
|
+
}
|
|
9987
|
+
}
|
|
9814
9988
|
function buildAuthMenuItem(auth2) {
|
|
9815
9989
|
if (auth2.authenticated) {
|
|
9816
|
-
const source = auth2
|
|
9990
|
+
const source = getAuthSourceDisplay(auth2);
|
|
9991
|
+
const user = auth2.username ? `@${auth2.username}` : "";
|
|
9992
|
+
const userPart = user ? `${user} ` : "";
|
|
9817
9993
|
return {
|
|
9818
9994
|
name: `🔑 Manage Auth ${c("green", "✓")}`,
|
|
9819
9995
|
value: "auth",
|
|
9820
|
-
description:
|
|
9996
|
+
description: `${userPart}via ${source}`
|
|
9821
9997
|
};
|
|
9822
9998
|
}
|
|
9823
9999
|
return {
|
|
9824
|
-
name: `🔑 Manage Auth ${c("red", "✗")}`,
|
|
10000
|
+
name: `🔑 ${bold("Manage Auth")} ${c("red", "✗ Required!")}`,
|
|
9825
10001
|
value: "auth",
|
|
9826
|
-
description: "
|
|
10002
|
+
description: `${c("yellow", "→")} Sign in to access GitHub`
|
|
9827
10003
|
};
|
|
9828
10004
|
}
|
|
9829
10005
|
function buildStatusLine(state) {
|
|
@@ -9865,9 +10041,52 @@ function buildOctocodeMenuItem(state) {
|
|
|
9865
10041
|
description: "Configure Octocode MCP - 0 IDEs configured"
|
|
9866
10042
|
};
|
|
9867
10043
|
}
|
|
10044
|
+
function printContextualHints(state) {
|
|
10045
|
+
if (!state.githubAuth.authenticated) {
|
|
10046
|
+
console.log();
|
|
10047
|
+
console.log(
|
|
10048
|
+
` ${c("yellow", "⚠")} ${bold("Auth required!")} Run ${c("cyan", "🔑 Manage Auth")} to access GitHub repos`
|
|
10049
|
+
);
|
|
10050
|
+
} else if (state.octocode.isInstalled && state.skills.totalInstalledCount === 0) {
|
|
10051
|
+
console.log();
|
|
10052
|
+
console.log(
|
|
10053
|
+
` ${c("cyan", "💡")} ${dim("Boost your AI coding:")} Install ${c("magenta", "Skills")} for research, PR review & more!`
|
|
10054
|
+
);
|
|
10055
|
+
}
|
|
10056
|
+
console.log();
|
|
10057
|
+
console.log(` ${c("yellow", "Hints:")}`);
|
|
10058
|
+
console.log(
|
|
10059
|
+
c("yellow", ` ▸ Prompts: Use /research, /plan, /implement in chat`)
|
|
10060
|
+
);
|
|
10061
|
+
console.log(
|
|
10062
|
+
c(
|
|
10063
|
+
"yellow",
|
|
10064
|
+
` ▸ Skills: Add all via Manage System Skills → Octocode Official`
|
|
10065
|
+
)
|
|
10066
|
+
);
|
|
10067
|
+
console.log(
|
|
10068
|
+
c(
|
|
10069
|
+
"yellow",
|
|
10070
|
+
` ▸ Context: Add AGENTS.md to your project (you can ask octocode)`
|
|
10071
|
+
)
|
|
10072
|
+
);
|
|
10073
|
+
console.log(
|
|
10074
|
+
c(
|
|
10075
|
+
"yellow",
|
|
10076
|
+
` ▸ Auth: Supports Octocode OAuth and gh CLI (if installed)`
|
|
10077
|
+
)
|
|
10078
|
+
);
|
|
10079
|
+
console.log(
|
|
10080
|
+
c(
|
|
10081
|
+
"yellow",
|
|
10082
|
+
` ▸ MCP: Manage all system MCP servers via Manage System MCP`
|
|
10083
|
+
)
|
|
10084
|
+
);
|
|
10085
|
+
}
|
|
9868
10086
|
async function showMainMenu(state) {
|
|
9869
10087
|
console.log();
|
|
9870
10088
|
console.log(` ${dim("Status:")} ${buildStatusLine(state)}`);
|
|
10089
|
+
printContextualHints(state);
|
|
9871
10090
|
const choices = [];
|
|
9872
10091
|
choices.push(buildOctocodeMenuItem(state));
|
|
9873
10092
|
choices.push(buildSkillsMenuItem(state.skills));
|
|
@@ -9925,6 +10144,14 @@ async function showOctocodeMenu(state) {
|
|
|
9925
10144
|
description: "Server options & preferences"
|
|
9926
10145
|
});
|
|
9927
10146
|
}
|
|
10147
|
+
if (!state.skills.allInstalled && state.skills.hasSkills) {
|
|
10148
|
+
const notInstalled = state.skills.skills.filter((s) => !s.installed).length;
|
|
10149
|
+
choices.push({
|
|
10150
|
+
name: `🧠 Install All Skills ${c("cyan", `(${notInstalled} available)`)}`,
|
|
10151
|
+
value: "install-skills",
|
|
10152
|
+
description: "One-click install of all Octocode skills"
|
|
10153
|
+
});
|
|
10154
|
+
}
|
|
9928
10155
|
choices.push(
|
|
9929
10156
|
new Separator()
|
|
9930
10157
|
);
|
|
@@ -9948,7 +10175,7 @@ async function showOctocodeMenu(state) {
|
|
|
9948
10175
|
}
|
|
9949
10176
|
async function runOctocodeFlow() {
|
|
9950
10177
|
await loadInquirer();
|
|
9951
|
-
let state = getAppState();
|
|
10178
|
+
let state = await getAppState();
|
|
9952
10179
|
console.log();
|
|
9953
10180
|
printInstalledIDEs(state.octocode.installedClients);
|
|
9954
10181
|
let inMenu = true;
|
|
@@ -9958,7 +10185,7 @@ async function runOctocodeFlow() {
|
|
|
9958
10185
|
firstRun = false;
|
|
9959
10186
|
} else {
|
|
9960
10187
|
const spinner = new Spinner(" Refreshing...").start();
|
|
9961
|
-
state = getAppState();
|
|
10188
|
+
state = await getAppState();
|
|
9962
10189
|
spinner.clear();
|
|
9963
10190
|
}
|
|
9964
10191
|
const choice = await showOctocodeMenu(state);
|
|
@@ -9971,6 +10198,45 @@ async function runOctocodeFlow() {
|
|
|
9971
10198
|
await runConfigOptionsFlow();
|
|
9972
10199
|
console.log();
|
|
9973
10200
|
break;
|
|
10201
|
+
case "install-skills": {
|
|
10202
|
+
console.log();
|
|
10203
|
+
const skillsSpinner = new Spinner(
|
|
10204
|
+
"Installing all Octocode skills..."
|
|
10205
|
+
).start();
|
|
10206
|
+
const result = await installAllOctocodeSkills();
|
|
10207
|
+
if (result.installed > 0) {
|
|
10208
|
+
skillsSpinner.succeed(
|
|
10209
|
+
`Installed ${result.installed} skill${result.installed !== 1 ? "s" : ""}!`
|
|
10210
|
+
);
|
|
10211
|
+
console.log();
|
|
10212
|
+
console.log(
|
|
10213
|
+
` ${c("green", "✓")} ${result.installed} skill${result.installed !== 1 ? "s" : ""} installed successfully`
|
|
10214
|
+
);
|
|
10215
|
+
if (result.alreadyInstalled > 0) {
|
|
10216
|
+
console.log(
|
|
10217
|
+
` ${dim(`(${result.alreadyInstalled} already installed)`)}`
|
|
10218
|
+
);
|
|
10219
|
+
}
|
|
10220
|
+
if (result.failed > 0) {
|
|
10221
|
+
console.log(
|
|
10222
|
+
` ${c("yellow", "⚠")} ${result.failed} skill${result.failed !== 1 ? "s" : ""} failed to install`
|
|
10223
|
+
);
|
|
10224
|
+
}
|
|
10225
|
+
console.log();
|
|
10226
|
+
console.log(` ${bold("Skills are now available in Claude Code!")}`);
|
|
10227
|
+
} else if (result.allInstalled) {
|
|
10228
|
+
skillsSpinner.succeed("All skills already installed!");
|
|
10229
|
+
console.log();
|
|
10230
|
+
console.log(` ${c("green", "✓")} All Octocode skills are installed`);
|
|
10231
|
+
} else {
|
|
10232
|
+
skillsSpinner.fail("Failed to install skills");
|
|
10233
|
+
console.log();
|
|
10234
|
+
console.log(` ${c("red", "✗")} Could not install skills`);
|
|
10235
|
+
}
|
|
10236
|
+
console.log();
|
|
10237
|
+
await pressEnterToContinue();
|
|
10238
|
+
break;
|
|
10239
|
+
}
|
|
9974
10240
|
case "back":
|
|
9975
10241
|
default:
|
|
9976
10242
|
inMenu = false;
|
|
@@ -10043,46 +10309,54 @@ async function runMCPConfigFlow() {
|
|
|
10043
10309
|
}
|
|
10044
10310
|
async function showAuthMenu(status) {
|
|
10045
10311
|
const choices = [];
|
|
10046
|
-
const
|
|
10047
|
-
const
|
|
10048
|
-
const
|
|
10049
|
-
|
|
10312
|
+
const isUsingEnv = status.tokenSource === "env";
|
|
10313
|
+
const ghCliToken = getGitHubCLIToken();
|
|
10314
|
+
const ghAuth = checkGitHubAuth();
|
|
10315
|
+
const octocodeCredentials = await getCredentials();
|
|
10316
|
+
const hasGhCli = !!ghCliToken;
|
|
10317
|
+
const hasOctocode = !!octocodeCredentials;
|
|
10318
|
+
const hasEnv = hasEnvToken();
|
|
10319
|
+
if (isUsingEnv && hasEnv) {
|
|
10320
|
+
const envVar = status.envTokenSource?.replace("env:", "") || "environment variable";
|
|
10050
10321
|
choices.push({
|
|
10051
|
-
name: "
|
|
10052
|
-
value: "
|
|
10053
|
-
|
|
10322
|
+
name: `ℹ️ Using ${c("cyan", envVar)} ${dim("(takes priority)")}`,
|
|
10323
|
+
value: "back",
|
|
10324
|
+
// No action, just info
|
|
10325
|
+
description: "Token set via environment variable"
|
|
10054
10326
|
});
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
|
|
10058
|
-
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10327
|
+
choices.push(
|
|
10328
|
+
new Separator()
|
|
10329
|
+
);
|
|
10330
|
+
}
|
|
10331
|
+
if (hasGhCli) {
|
|
10332
|
+
const userPart = ghAuth.username ? ` (@${ghAuth.username})` : "";
|
|
10333
|
+
choices.push({
|
|
10334
|
+
name: `🗑️ Delete gh CLI token${userPart}`,
|
|
10335
|
+
value: "gh-logout",
|
|
10336
|
+
description: "Opens gh auth logout"
|
|
10337
|
+
});
|
|
10338
|
+
}
|
|
10339
|
+
if (hasOctocode) {
|
|
10340
|
+
const userPart = octocodeCredentials.username ? ` (@${octocodeCredentials.username})` : "";
|
|
10341
|
+
const storageType = isUsingSecureStorage() ? "keychain" : "file";
|
|
10342
|
+
choices.push({
|
|
10343
|
+
name: `🗑️ Delete Octocode token${userPart}`,
|
|
10344
|
+
value: "logout",
|
|
10345
|
+
description: `Remove from ${storageType}`
|
|
10346
|
+
});
|
|
10347
|
+
}
|
|
10348
|
+
if (!hasOctocode) {
|
|
10069
10349
|
choices.push({
|
|
10070
10350
|
name: `🔐 Sign In via Octocode ${c("green", "(Recommended)")}`,
|
|
10071
10351
|
value: "login",
|
|
10072
10352
|
description: "Quick browser sign in"
|
|
10073
10353
|
});
|
|
10354
|
+
}
|
|
10355
|
+
if (!hasGhCli) {
|
|
10074
10356
|
choices.push({
|
|
10075
10357
|
name: "🔐 Sign In via gh CLI",
|
|
10076
10358
|
value: "gh-guidance",
|
|
10077
|
-
description: "Use existing GitHub CLI
|
|
10078
|
-
});
|
|
10079
|
-
choices.push(
|
|
10080
|
-
new Separator()
|
|
10081
|
-
);
|
|
10082
|
-
choices.push({
|
|
10083
|
-
name: `${dim("🔍 View all auth methods")}`,
|
|
10084
|
-
value: "check-token",
|
|
10085
|
-
description: "Check env vars, gh CLI, stored tokens"
|
|
10359
|
+
description: ghAuth.installed ? "Use existing GitHub CLI" : "GitHub CLI not installed"
|
|
10086
10360
|
});
|
|
10087
10361
|
}
|
|
10088
10362
|
choices.push(
|
|
@@ -10108,43 +10382,82 @@ async function showAuthMenu(status) {
|
|
|
10108
10382
|
}
|
|
10109
10383
|
async function runLoginFlow() {
|
|
10110
10384
|
console.log();
|
|
10385
|
+
console.log(c("blue", "━".repeat(66)));
|
|
10111
10386
|
console.log(` ${bold("🔐 GitHub Authentication")}`);
|
|
10387
|
+
console.log(c("blue", "━".repeat(66)));
|
|
10112
10388
|
console.log();
|
|
10113
10389
|
console.log(
|
|
10114
10390
|
` ${dim("This will open your browser to authenticate with GitHub.")}`
|
|
10115
10391
|
);
|
|
10116
10392
|
console.log();
|
|
10117
10393
|
let verificationShown = false;
|
|
10394
|
+
let authSpinner = null;
|
|
10118
10395
|
const spinner = new Spinner("Connecting to GitHub...").start();
|
|
10119
10396
|
const result = await login({
|
|
10120
10397
|
onVerification: (verification) => {
|
|
10121
10398
|
spinner.stop();
|
|
10122
10399
|
verificationShown = true;
|
|
10400
|
+
console.log();
|
|
10401
|
+
console.log(c("yellow", " ┌" + "─".repeat(50) + "┐"));
|
|
10123
10402
|
console.log(
|
|
10124
|
-
|
|
10403
|
+
c("yellow", " │ ") + `${c("yellow", "!")} Your one-time code: ${bold(c("cyan", verification.user_code))}` + " ".repeat(50 - 26 - verification.user_code.length) + c("yellow", "│")
|
|
10125
10404
|
);
|
|
10405
|
+
console.log(c("yellow", " └" + "─".repeat(50) + "┘"));
|
|
10126
10406
|
console.log();
|
|
10407
|
+
console.log(` ${bold("1.")} Copy the code above`);
|
|
10127
10408
|
console.log(
|
|
10128
|
-
` ${bold("Press Enter")} to open ${c("cyan", verification.verification_uri)}
|
|
10409
|
+
` ${bold("2.")} ${bold("Press Enter")} to open ${c("cyan", verification.verification_uri)}`
|
|
10129
10410
|
);
|
|
10411
|
+
console.log(` ${bold("3.")} Paste the code in your browser`);
|
|
10130
10412
|
console.log();
|
|
10131
|
-
|
|
10413
|
+
authSpinner = new Spinner(
|
|
10414
|
+
`Waiting for browser authentication... ${dim("(typically 10-30 seconds)")}`
|
|
10415
|
+
).start();
|
|
10132
10416
|
}
|
|
10133
10417
|
});
|
|
10418
|
+
if (authSpinner) {
|
|
10419
|
+
authSpinner.stop();
|
|
10420
|
+
}
|
|
10134
10421
|
if (!verificationShown) {
|
|
10135
10422
|
spinner.stop();
|
|
10136
10423
|
}
|
|
10137
10424
|
console.log();
|
|
10138
10425
|
if (result.success) {
|
|
10139
|
-
console.log(
|
|
10426
|
+
console.log(c("green", " ┌" + "─".repeat(50) + "┐"));
|
|
10140
10427
|
console.log(
|
|
10141
|
-
|
|
10428
|
+
c("green", " │ ") + `${c("green", "✓")} ${bold("Authentication successful!")}` + " ".repeat(22) + c("green", "│")
|
|
10142
10429
|
);
|
|
10430
|
+
console.log(c("green", " └" + "─".repeat(50) + "┘"));
|
|
10143
10431
|
console.log();
|
|
10432
|
+
console.log(
|
|
10433
|
+
` ${c("green", "✓")} Logged in as ${c("cyan", "@" + (result.username || "unknown"))}`
|
|
10434
|
+
);
|
|
10144
10435
|
console.log(` ${dim("Credentials stored in:")} ${getStoragePath()}`);
|
|
10436
|
+
console.log();
|
|
10437
|
+
console.log(` ${c("cyan", "💡")} ${bold("What's next?")}`);
|
|
10438
|
+
console.log(
|
|
10439
|
+
` ${dim("•")} Install ${c("magenta", "Skills")} for AI-powered research & PR reviews`
|
|
10440
|
+
);
|
|
10441
|
+
console.log(
|
|
10442
|
+
` ${dim("•")} Use ${c("cyan", "/research")} prompt to explore any GitHub repo`
|
|
10443
|
+
);
|
|
10444
|
+
console.log(
|
|
10445
|
+
` ${dim("•")} Add ${c("cyan", "AGENTS.md")} to your project for better AI context`
|
|
10446
|
+
);
|
|
10145
10447
|
} else {
|
|
10448
|
+
console.log(c("red", " ┌" + "─".repeat(50) + "┐"));
|
|
10146
10449
|
console.log(
|
|
10147
|
-
|
|
10450
|
+
c("red", " │ ") + `${c("red", "✗")} ${bold("Authentication failed")}` + " ".repeat(27) + c("red", "│")
|
|
10451
|
+
);
|
|
10452
|
+
console.log(c("red", " └" + "─".repeat(50) + "┘"));
|
|
10453
|
+
console.log();
|
|
10454
|
+
console.log(` ${c("red", "Error:")} ${result.error || "Unknown error"}`);
|
|
10455
|
+
console.log();
|
|
10456
|
+
console.log(` ${bold("Troubleshooting:")}`);
|
|
10457
|
+
console.log(` ${dim("•")} Make sure you copied the code correctly`);
|
|
10458
|
+
console.log(` ${dim("•")} Check your browser didn't block the popup`);
|
|
10459
|
+
console.log(
|
|
10460
|
+
` ${dim("•")} Try running ${c("cyan", "octocode login")} again`
|
|
10148
10461
|
);
|
|
10149
10462
|
}
|
|
10150
10463
|
console.log();
|
|
@@ -10152,7 +10465,7 @@ async function runLoginFlow() {
|
|
|
10152
10465
|
return result.success;
|
|
10153
10466
|
}
|
|
10154
10467
|
async function runLogoutFlow() {
|
|
10155
|
-
const status =
|
|
10468
|
+
const status = await getAuthStatusAsync();
|
|
10156
10469
|
console.log();
|
|
10157
10470
|
console.log(` ${bold("🔓 Sign Out")}`);
|
|
10158
10471
|
console.log(
|
|
@@ -10233,192 +10546,64 @@ async function showGhCliGuidance() {
|
|
|
10233
10546
|
await pressEnterToContinue();
|
|
10234
10547
|
}
|
|
10235
10548
|
}
|
|
10236
|
-
|
|
10237
|
-
|
|
10238
|
-
|
|
10239
|
-
|
|
10240
|
-
|
|
10241
|
-
|
|
10242
|
-
|
|
10243
|
-
|
|
10244
|
-
let ghCliAvailable = false;
|
|
10245
|
-
let ghCliUsername = "";
|
|
10246
|
-
if (ghCliToken) {
|
|
10247
|
-
ghCliAvailable = true;
|
|
10248
|
-
ghCliUsername = ghAuth.username || "";
|
|
10249
|
-
}
|
|
10250
|
-
const octocodeCredentials = await getCredentials();
|
|
10251
|
-
let octocodeAvailable = false;
|
|
10252
|
-
let octocodeUsername = "";
|
|
10253
|
-
const storageType = isUsingSecureStorage() ? "keychain" : "encrypted file";
|
|
10254
|
-
if (octocodeCredentials) {
|
|
10255
|
-
octocodeAvailable = true;
|
|
10256
|
-
octocodeUsername = octocodeCredentials.username || "";
|
|
10257
|
-
}
|
|
10258
|
-
if (ghCliAvailable) {
|
|
10259
|
-
const userPart = ghCliUsername ? ` (@${ghCliUsername})` : "";
|
|
10260
|
-
console.log(` ${c("green", "✓")} gh CLI${userPart}`);
|
|
10261
|
-
} else {
|
|
10262
|
-
const statusText = ghAuth.installed ? "Not authenticated" : "Not installed";
|
|
10263
|
-
console.log(` ${c("red", "✗")} gh CLI: ${dim(statusText)}`);
|
|
10264
|
-
}
|
|
10265
|
-
if (octocodeAvailable) {
|
|
10266
|
-
const userPart = octocodeUsername ? ` (@${octocodeUsername})` : "";
|
|
10267
|
-
console.log(` ${c("green", "✓")} Octocode (${storageType})${userPart}`);
|
|
10268
|
-
} else {
|
|
10269
|
-
console.log(
|
|
10270
|
-
` ${c("red", "✗")} Octocode (${storageType}): ${dim("Not stored")}`
|
|
10271
|
-
);
|
|
10272
|
-
}
|
|
10273
|
-
const availableCount = (ghCliAvailable ? 1 : 0) + (octocodeAvailable ? 1 : 0);
|
|
10274
|
-
console.log();
|
|
10275
|
-
if (availableCount > 0) {
|
|
10276
|
-
console.log(` ${dim("→")} ${availableCount} auth method(s) available`);
|
|
10277
|
-
} else {
|
|
10278
|
-
console.log(` ${c("yellow", "⚠")} No authentication methods available`);
|
|
10279
|
-
}
|
|
10280
|
-
console.log();
|
|
10281
|
-
const choices = [];
|
|
10282
|
-
if (ghCliAvailable) {
|
|
10283
|
-
choices.push({
|
|
10284
|
-
name: `🗑️ Delete gh CLI token`,
|
|
10285
|
-
value: "delete-gh",
|
|
10286
|
-
description: ghCliUsername ? `@${ghCliUsername}` : "Opens gh auth logout"
|
|
10287
|
-
});
|
|
10288
|
-
} else {
|
|
10289
|
-
choices.push({
|
|
10290
|
-
name: `🔐 Sign In via gh CLI`,
|
|
10291
|
-
value: "gh-guidance",
|
|
10292
|
-
description: "Use GitHub CLI to authenticate"
|
|
10293
|
-
});
|
|
10294
|
-
}
|
|
10295
|
-
if (octocodeAvailable) {
|
|
10296
|
-
choices.push({
|
|
10297
|
-
name: `🗑️ Delete Octocode token`,
|
|
10298
|
-
value: "delete-octocode",
|
|
10299
|
-
description: octocodeUsername ? `@${octocodeUsername}` : `From ${storageType}`
|
|
10300
|
-
});
|
|
10301
|
-
} else {
|
|
10302
|
-
choices.push({
|
|
10303
|
-
name: `🔐 Sign In via Octocode`,
|
|
10304
|
-
value: "login-octocode",
|
|
10305
|
-
description: "Quick browser sign in"
|
|
10306
|
-
});
|
|
10307
|
-
}
|
|
10308
|
-
choices.push(
|
|
10309
|
-
new Separator()
|
|
10310
|
-
);
|
|
10311
|
-
choices.push({
|
|
10312
|
-
name: `${c("dim", "← Back")}`,
|
|
10313
|
-
value: "back"
|
|
10314
|
-
});
|
|
10315
|
-
const choice = await selectWithCancel({
|
|
10316
|
-
message: "",
|
|
10317
|
-
choices,
|
|
10318
|
-
pageSize: 10,
|
|
10319
|
-
loop: false,
|
|
10320
|
-
theme: {
|
|
10321
|
-
prefix: " ",
|
|
10322
|
-
style: {
|
|
10323
|
-
highlight: (text) => c("magenta", text)
|
|
10324
|
-
}
|
|
10325
|
-
}
|
|
10326
|
-
});
|
|
10327
|
-
switch (choice) {
|
|
10328
|
-
case "delete-gh": {
|
|
10329
|
-
console.log();
|
|
10330
|
-
const confirmGh = await selectWithCancel({
|
|
10331
|
-
message: `Delete gh CLI token${ghCliUsername ? ` (@${ghCliUsername})` : ""}?`,
|
|
10332
|
-
choices: [
|
|
10333
|
-
{ name: "Yes, sign out", value: "yes" },
|
|
10334
|
-
{ name: "No, cancel", value: "no" }
|
|
10335
|
-
],
|
|
10336
|
-
theme: {
|
|
10337
|
-
prefix: " ",
|
|
10338
|
-
style: {
|
|
10339
|
-
highlight: (text) => c("red", text)
|
|
10340
|
-
}
|
|
10341
|
-
}
|
|
10342
|
-
});
|
|
10343
|
-
if (confirmGh === "yes") {
|
|
10344
|
-
console.log();
|
|
10345
|
-
console.log(` ${dim("Opening gh auth logout...")}`);
|
|
10346
|
-
const ghResult = runGitHubAuthLogout();
|
|
10347
|
-
if (ghResult.success) {
|
|
10348
|
-
console.log(` ${c("green", "✓")} Signed out of gh CLI`);
|
|
10349
|
-
} else {
|
|
10350
|
-
console.log(` ${c("yellow", "!")} Sign out was cancelled`);
|
|
10351
|
-
}
|
|
10352
|
-
console.log();
|
|
10353
|
-
await pressEnterToContinue();
|
|
10354
|
-
}
|
|
10355
|
-
break;
|
|
10356
|
-
}
|
|
10357
|
-
case "delete-octocode": {
|
|
10358
|
-
console.log();
|
|
10359
|
-
const confirmOctocode = await selectWithCancel({
|
|
10360
|
-
message: `Delete Octocode token${octocodeUsername ? ` (@${octocodeUsername})` : ""} from ${storageType}?`,
|
|
10361
|
-
choices: [
|
|
10362
|
-
{ name: "Yes, delete", value: "yes" },
|
|
10363
|
-
{ name: "No, cancel", value: "no" }
|
|
10364
|
-
],
|
|
10365
|
-
theme: {
|
|
10366
|
-
prefix: " ",
|
|
10367
|
-
style: {
|
|
10368
|
-
highlight: (text) => c("red", text)
|
|
10369
|
-
}
|
|
10370
|
-
}
|
|
10371
|
-
});
|
|
10372
|
-
if (confirmOctocode === "yes") {
|
|
10373
|
-
const result = await logout();
|
|
10374
|
-
if (result.success) {
|
|
10375
|
-
console.log();
|
|
10376
|
-
console.log(` ${c("green", "✓")} Octocode token deleted`);
|
|
10377
|
-
} else {
|
|
10378
|
-
console.log();
|
|
10379
|
-
console.log(
|
|
10380
|
-
` ${c("red", "✗")} Failed to delete: ${result.error || "Unknown error"}`
|
|
10381
|
-
);
|
|
10382
|
-
}
|
|
10383
|
-
console.log();
|
|
10384
|
-
await pressEnterToContinue();
|
|
10385
|
-
}
|
|
10386
|
-
break;
|
|
10387
|
-
}
|
|
10388
|
-
case "login-octocode": {
|
|
10389
|
-
const success = await runLoginFlow();
|
|
10390
|
-
if (success) {
|
|
10391
|
-
inTokenCheck = false;
|
|
10392
|
-
}
|
|
10393
|
-
break;
|
|
10394
|
-
}
|
|
10395
|
-
case "gh-guidance": {
|
|
10396
|
-
await showGhCliGuidance();
|
|
10397
|
-
break;
|
|
10549
|
+
function getDetailedAuthSource(status) {
|
|
10550
|
+
switch (status.tokenSource) {
|
|
10551
|
+
case "gh-cli":
|
|
10552
|
+
return "gh CLI";
|
|
10553
|
+
case "env": {
|
|
10554
|
+
if (status.envTokenSource) {
|
|
10555
|
+
const varName = status.envTokenSource.replace("env:", "");
|
|
10556
|
+
return `${varName} env var`;
|
|
10398
10557
|
}
|
|
10399
|
-
|
|
10400
|
-
default:
|
|
10401
|
-
inTokenCheck = false;
|
|
10402
|
-
break;
|
|
10558
|
+
return "environment variable";
|
|
10403
10559
|
}
|
|
10560
|
+
case "octocode":
|
|
10561
|
+
return isUsingSecureStorage() ? "keychain" : "file";
|
|
10562
|
+
default:
|
|
10563
|
+
return "unknown";
|
|
10404
10564
|
}
|
|
10405
10565
|
}
|
|
10406
10566
|
function displayAuthStatus(status) {
|
|
10407
10567
|
console.log(` ${bold("🔐 GitHub Authentication")}`);
|
|
10408
10568
|
console.log();
|
|
10409
10569
|
if (status.authenticated) {
|
|
10410
|
-
const source = status
|
|
10411
|
-
|
|
10412
|
-
|
|
10413
|
-
|
|
10570
|
+
const source = getDetailedAuthSource(status);
|
|
10571
|
+
if (status.tokenSource === "env") {
|
|
10572
|
+
const envVarName = status.envTokenSource ? status.envTokenSource.replace("env:", "") : "environment variable";
|
|
10573
|
+
console.log(
|
|
10574
|
+
` ${c("green", "✓")} Using ${c("cyan", envVarName)} ${dim("(token configured)")}`
|
|
10575
|
+
);
|
|
10576
|
+
} else {
|
|
10577
|
+
console.log(
|
|
10578
|
+
` ${c("green", "✓")} Signed in as ${c("cyan", "@" + (status.username || "unknown"))} ${dim(`via ${source}`)}`
|
|
10579
|
+
);
|
|
10580
|
+
}
|
|
10414
10581
|
if (status.tokenExpired) {
|
|
10415
10582
|
console.log(
|
|
10416
10583
|
` ${c("yellow", "⚠")} Session expired - please sign in again`
|
|
10417
10584
|
);
|
|
10418
10585
|
}
|
|
10586
|
+
console.log();
|
|
10587
|
+
console.log(
|
|
10588
|
+
` ${c("green", "✓")} ${dim("Ready to access GitHub repositories!")}`
|
|
10589
|
+
);
|
|
10419
10590
|
} else {
|
|
10420
|
-
console.log(
|
|
10421
|
-
console.log(
|
|
10591
|
+
console.log(c("yellow", " ┌" + "─".repeat(56) + "┐"));
|
|
10592
|
+
console.log(
|
|
10593
|
+
c("yellow", " │ ") + `${c("yellow", "⚠")} ${bold("Authentication Required")}` + " ".repeat(31) + c("yellow", "│")
|
|
10594
|
+
);
|
|
10595
|
+
console.log(c("yellow", " └" + "─".repeat(56) + "┘"));
|
|
10596
|
+
console.log();
|
|
10597
|
+
console.log(` ${dim("Without auth, Octocode cannot:")}`);
|
|
10598
|
+
console.log(` ${c("red", "✗")} Access private repositories`);
|
|
10599
|
+
console.log(` ${c("red", "✗")} Search code in your organization`);
|
|
10600
|
+
console.log(
|
|
10601
|
+
` ${c("red", "✗")} Provide full GitHub research capabilities`
|
|
10602
|
+
);
|
|
10603
|
+
console.log();
|
|
10604
|
+
console.log(
|
|
10605
|
+
` ${c("cyan", "→")} Select ${c("green", '"Sign In via Octocode"')} below to authenticate`
|
|
10606
|
+
);
|
|
10422
10607
|
}
|
|
10423
10608
|
console.log();
|
|
10424
10609
|
}
|
|
@@ -10427,14 +10612,10 @@ async function runAuthFlow() {
|
|
|
10427
10612
|
console.log();
|
|
10428
10613
|
let inAuthMenu = true;
|
|
10429
10614
|
while (inAuthMenu) {
|
|
10430
|
-
const status =
|
|
10615
|
+
const status = await getAuthStatusAsync();
|
|
10431
10616
|
displayAuthStatus(status);
|
|
10432
10617
|
const choice = await showAuthMenu(status);
|
|
10433
10618
|
switch (choice) {
|
|
10434
|
-
case "check-token":
|
|
10435
|
-
await runCheckTokenFlow();
|
|
10436
|
-
console.log();
|
|
10437
|
-
break;
|
|
10438
10619
|
case "login":
|
|
10439
10620
|
await runLoginFlow();
|
|
10440
10621
|
console.log();
|
|
@@ -10447,6 +10628,22 @@ async function runAuthFlow() {
|
|
|
10447
10628
|
console.log();
|
|
10448
10629
|
break;
|
|
10449
10630
|
case "gh-logout": {
|
|
10631
|
+
const confirmGh = await selectWithCancel({
|
|
10632
|
+
message: "Sign out of gh CLI?",
|
|
10633
|
+
choices: [
|
|
10634
|
+
{ name: "Yes, sign out", value: "yes" },
|
|
10635
|
+
{ name: "No, cancel", value: "no" }
|
|
10636
|
+
],
|
|
10637
|
+
theme: {
|
|
10638
|
+
prefix: " ",
|
|
10639
|
+
style: {
|
|
10640
|
+
highlight: (text) => c("red", text)
|
|
10641
|
+
}
|
|
10642
|
+
}
|
|
10643
|
+
});
|
|
10644
|
+
if (confirmGh !== "yes") {
|
|
10645
|
+
break;
|
|
10646
|
+
}
|
|
10450
10647
|
console.log();
|
|
10451
10648
|
console.log(` ${dim("Opening gh auth logout...")}`);
|
|
10452
10649
|
console.log();
|
|
@@ -10511,10 +10708,10 @@ async function runMenuLoop() {
|
|
|
10511
10708
|
while (running) {
|
|
10512
10709
|
let state;
|
|
10513
10710
|
if (firstRun) {
|
|
10514
|
-
state = getAppState();
|
|
10711
|
+
state = await getAppState();
|
|
10515
10712
|
} else {
|
|
10516
10713
|
const spinner = new Spinner(" Loading...").start();
|
|
10517
|
-
state = getAppState();
|
|
10714
|
+
state = await getAppState();
|
|
10518
10715
|
spinner.clear();
|
|
10519
10716
|
}
|
|
10520
10717
|
if (!firstRun) {
|
|
@@ -11203,7 +11400,7 @@ const tokenCommand = {
|
|
|
11203
11400
|
name: "token",
|
|
11204
11401
|
aliases: ["t"],
|
|
11205
11402
|
description: "Print the GitHub token (matches octocode-mcp priority)",
|
|
11206
|
-
usage: "octocode token [--type <auto|octocode|gh>] [--hostname <host>] [--source]",
|
|
11403
|
+
usage: "octocode token [--type <auto|octocode|gh>] [--hostname <host>] [--source] [--json]",
|
|
11207
11404
|
options: [
|
|
11208
11405
|
{
|
|
11209
11406
|
name: "type",
|
|
@@ -11222,12 +11419,18 @@ const tokenCommand = {
|
|
|
11222
11419
|
name: "source",
|
|
11223
11420
|
short: "s",
|
|
11224
11421
|
description: "Show token source and user info"
|
|
11422
|
+
},
|
|
11423
|
+
{
|
|
11424
|
+
name: "json",
|
|
11425
|
+
short: "j",
|
|
11426
|
+
description: 'Output as JSON: {"token": "...", "type": "..."}'
|
|
11225
11427
|
}
|
|
11226
11428
|
],
|
|
11227
11429
|
handler: async (args) => {
|
|
11228
11430
|
const hostnameOpt = args.options["hostname"] ?? args.options["H"];
|
|
11229
11431
|
const hostname = (typeof hostnameOpt === "string" ? hostnameOpt : void 0) || "github.com";
|
|
11230
11432
|
const showSource = Boolean(args.options["source"] || args.options["s"]);
|
|
11433
|
+
const jsonOutput = Boolean(args.options["json"] || args.options["j"]);
|
|
11231
11434
|
const typeOpt = args.options["type"] ?? args.options["t"];
|
|
11232
11435
|
const typeArg = (typeof typeOpt === "string" ? typeOpt : void 0) || "auto";
|
|
11233
11436
|
let tokenSource;
|
|
@@ -11246,6 +11449,11 @@ const tokenCommand = {
|
|
|
11246
11449
|
tokenSource = "auto";
|
|
11247
11450
|
break;
|
|
11248
11451
|
default:
|
|
11452
|
+
if (jsonOutput) {
|
|
11453
|
+
console.log(JSON.stringify({ token: null, type: "none" }));
|
|
11454
|
+
process.exitCode = 1;
|
|
11455
|
+
return;
|
|
11456
|
+
}
|
|
11249
11457
|
console.log();
|
|
11250
11458
|
console.log(` ${c("red", "✗")} Invalid token type: ${typeArg}`);
|
|
11251
11459
|
console.log(` ${dim("Valid options:")} octocode, gh, auto`);
|
|
@@ -11254,6 +11462,17 @@ const tokenCommand = {
|
|
|
11254
11462
|
return;
|
|
11255
11463
|
}
|
|
11256
11464
|
const result = await getToken(hostname, tokenSource);
|
|
11465
|
+
if (jsonOutput) {
|
|
11466
|
+
const output = {
|
|
11467
|
+
token: result.token,
|
|
11468
|
+
type: getTokenType(result.source, result.envSource)
|
|
11469
|
+
};
|
|
11470
|
+
console.log(JSON.stringify(output));
|
|
11471
|
+
if (!result.token) {
|
|
11472
|
+
process.exitCode = 1;
|
|
11473
|
+
}
|
|
11474
|
+
return;
|
|
11475
|
+
}
|
|
11257
11476
|
if (!result.token) {
|
|
11258
11477
|
console.log();
|
|
11259
11478
|
if (tokenSource === "octocode") {
|
|
@@ -11569,7 +11788,7 @@ function showCommandHelp(command) {
|
|
|
11569
11788
|
}
|
|
11570
11789
|
}
|
|
11571
11790
|
function showVersion() {
|
|
11572
|
-
const version = "1.
|
|
11791
|
+
const version = "1.2.2";
|
|
11573
11792
|
console.log(`octocode v${version}`);
|
|
11574
11793
|
}
|
|
11575
11794
|
async function runCLI(argv) {
|