@shipwellapp/cli 0.1.2 → 0.2.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/dist/index.js +397 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
+
import chalk7 from "chalk";
|
|
5
6
|
|
|
6
7
|
// src/commands/analyze.ts
|
|
7
8
|
import ora from "ora";
|
|
@@ -735,6 +736,66 @@ function extractTag(text, tag) {
|
|
|
735
736
|
return match ? match[1].trim() : null;
|
|
736
737
|
}
|
|
737
738
|
|
|
739
|
+
// src/lib/store.ts
|
|
740
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
741
|
+
import { join as join2 } from "path";
|
|
742
|
+
import { homedir } from "os";
|
|
743
|
+
var CONFIG_DIR = join2(homedir(), ".shipwell");
|
|
744
|
+
var CONFIG_FILE = join2(CONFIG_DIR, "config.json");
|
|
745
|
+
function ensureDir() {
|
|
746
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
747
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
function loadConfig() {
|
|
751
|
+
try {
|
|
752
|
+
ensureDir();
|
|
753
|
+
if (existsSync(CONFIG_FILE)) {
|
|
754
|
+
return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
755
|
+
}
|
|
756
|
+
} catch {
|
|
757
|
+
}
|
|
758
|
+
return {};
|
|
759
|
+
}
|
|
760
|
+
function saveConfig(config2) {
|
|
761
|
+
ensureDir();
|
|
762
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config2, null, 2) + "\n", { mode: 384 });
|
|
763
|
+
}
|
|
764
|
+
function getUser() {
|
|
765
|
+
return loadConfig().user;
|
|
766
|
+
}
|
|
767
|
+
function setUser(user) {
|
|
768
|
+
const config2 = loadConfig();
|
|
769
|
+
config2.user = user;
|
|
770
|
+
saveConfig(config2);
|
|
771
|
+
}
|
|
772
|
+
function clearUser() {
|
|
773
|
+
const config2 = loadConfig();
|
|
774
|
+
delete config2.user;
|
|
775
|
+
saveConfig(config2);
|
|
776
|
+
}
|
|
777
|
+
function getApiKey() {
|
|
778
|
+
return loadConfig().apiKey;
|
|
779
|
+
}
|
|
780
|
+
function setApiKey(key) {
|
|
781
|
+
const config2 = loadConfig();
|
|
782
|
+
config2.apiKey = key;
|
|
783
|
+
saveConfig(config2);
|
|
784
|
+
}
|
|
785
|
+
function clearApiKey() {
|
|
786
|
+
const config2 = loadConfig();
|
|
787
|
+
delete config2.apiKey;
|
|
788
|
+
saveConfig(config2);
|
|
789
|
+
}
|
|
790
|
+
function getModel() {
|
|
791
|
+
return loadConfig().model;
|
|
792
|
+
}
|
|
793
|
+
function setModel(model) {
|
|
794
|
+
const config2 = loadConfig();
|
|
795
|
+
config2.model = model;
|
|
796
|
+
saveConfig(config2);
|
|
797
|
+
}
|
|
798
|
+
|
|
738
799
|
// src/commands/analyze.ts
|
|
739
800
|
var accent = chalk.hex("#6366f1");
|
|
740
801
|
var dim = chalk.dim;
|
|
@@ -775,14 +836,21 @@ function formatMetric(m) {
|
|
|
775
836
|
return ` ${dim("\u2022")} ${m.label}: ${chalk.red(m.before)} ${dim("\u2192")} ${chalk.green(m.after)}${m.unit ? dim(` ${m.unit}`) : ""}`;
|
|
776
837
|
}
|
|
777
838
|
async function analyzeCommand(operation, source, options) {
|
|
778
|
-
const
|
|
839
|
+
const user = getUser();
|
|
840
|
+
if (!user) {
|
|
841
|
+
console.error(chalk.red("\n Error: Not logged in.\n"));
|
|
842
|
+
console.error(dim(" Run ") + chalk.cyan("shipwell login") + dim(" to sign in with Google.\n"));
|
|
843
|
+
process.exit(1);
|
|
844
|
+
}
|
|
845
|
+
const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY || getApiKey();
|
|
779
846
|
if (!apiKey) {
|
|
780
847
|
console.error(chalk.red("\n Error: Anthropic API key is required.\n"));
|
|
781
|
-
console.error(dim(" Set
|
|
782
|
-
console.error(dim("
|
|
848
|
+
console.error(dim(" Set it with: ") + chalk.cyan("shipwell config set api-key sk-ant-..."));
|
|
849
|
+
console.error(dim(" Or pass it: ") + chalk.cyan("shipwell audit ./repo --api-key sk-ant-..."));
|
|
850
|
+
console.error(dim(" Or set env: ") + chalk.cyan("export ANTHROPIC_API_KEY=sk-ant-...\n"));
|
|
783
851
|
process.exit(1);
|
|
784
852
|
}
|
|
785
|
-
const model = options.model || process.env.SHIPWELL_MODEL || "claude-sonnet-4-5-20250929";
|
|
853
|
+
const model = options.model || process.env.SHIPWELL_MODEL || getModel() || "claude-sonnet-4-5-20250929";
|
|
786
854
|
const startTime = Date.now();
|
|
787
855
|
console.log();
|
|
788
856
|
console.log(accent(" \u26F5 Shipwell"), dim("\u2014 Full Codebase Autopilot"));
|
|
@@ -879,9 +947,292 @@ async function analyzeCommand(operation, source, options) {
|
|
|
879
947
|
console.log();
|
|
880
948
|
}
|
|
881
949
|
|
|
950
|
+
// src/commands/login.ts
|
|
951
|
+
import chalk2 from "chalk";
|
|
952
|
+
import ora2 from "ora";
|
|
953
|
+
|
|
954
|
+
// src/lib/auth.ts
|
|
955
|
+
import http from "http";
|
|
956
|
+
import { exec } from "child_process";
|
|
957
|
+
import { platform } from "os";
|
|
958
|
+
function openBrowser(url) {
|
|
959
|
+
const plat = platform();
|
|
960
|
+
if (plat === "darwin") exec(`open "${url}"`);
|
|
961
|
+
else if (plat === "win32") exec(`start "" "${url}"`);
|
|
962
|
+
else exec(`xdg-open "${url}"`);
|
|
963
|
+
}
|
|
964
|
+
function startAuthFlow(baseUrl) {
|
|
965
|
+
return new Promise((resolve, reject) => {
|
|
966
|
+
const server = http.createServer((req, res) => {
|
|
967
|
+
const url = new URL(req.url || "/", "http://localhost");
|
|
968
|
+
if (url.pathname === "/callback") {
|
|
969
|
+
const name = url.searchParams.get("name") || "";
|
|
970
|
+
const email = url.searchParams.get("email") || "";
|
|
971
|
+
const uid = url.searchParams.get("uid") || "";
|
|
972
|
+
const photo = url.searchParams.get("photo") || void 0;
|
|
973
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
974
|
+
res.end(`<!DOCTYPE html>
|
|
975
|
+
<html>
|
|
976
|
+
<head><title>Shipwell</title>
|
|
977
|
+
<style>
|
|
978
|
+
body { background: #0a0a0f; color: #e4e4e7; font-family: -apple-system, BlinkMacSystemFont, sans-serif; display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; }
|
|
979
|
+
.card { text-align: center; padding: 3rem; }
|
|
980
|
+
.icon { font-size: 3rem; margin-bottom: 1rem; }
|
|
981
|
+
h1 { font-size: 1.5rem; margin: 0 0 0.5rem; }
|
|
982
|
+
p { color: #71717a; font-size: 0.875rem; margin: 0.25rem 0; }
|
|
983
|
+
.name { color: #818cf8; font-weight: 600; }
|
|
984
|
+
.hint { margin-top: 1.5rem; padding: 1rem; background: #18181b; border-radius: 0.75rem; border: 1px solid #27272a; }
|
|
985
|
+
code { color: #22d3ee; font-size: 0.8rem; }
|
|
986
|
+
</style>
|
|
987
|
+
</head>
|
|
988
|
+
<body>
|
|
989
|
+
<div class="card">
|
|
990
|
+
<div class="icon">\u26F5</div>
|
|
991
|
+
<h1>Welcome to Shipwell</h1>
|
|
992
|
+
<p>Logged in as <span class="name">${name}</span></p>
|
|
993
|
+
<p style="margin-top: 1rem; color: #52525b;">You can close this tab and return to your terminal.</p>
|
|
994
|
+
<div class="hint">
|
|
995
|
+
<p style="color: #a1a1aa; margin-bottom: 0.5rem;">Next, set your API key:</p>
|
|
996
|
+
<code>shipwell config set api-key sk-ant-...</code>
|
|
997
|
+
</div>
|
|
998
|
+
</div>
|
|
999
|
+
</body>
|
|
1000
|
+
</html>`);
|
|
1001
|
+
server.close();
|
|
1002
|
+
resolve({ name, email, uid, photo });
|
|
1003
|
+
} else {
|
|
1004
|
+
res.writeHead(404);
|
|
1005
|
+
res.end("Not found");
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
server.listen(0, "127.0.0.1", () => {
|
|
1009
|
+
const addr = server.address();
|
|
1010
|
+
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
1011
|
+
openBrowser(`${baseUrl}/cli-auth?port=${port}`);
|
|
1012
|
+
});
|
|
1013
|
+
setTimeout(() => {
|
|
1014
|
+
server.close();
|
|
1015
|
+
reject(new Error("Authentication timed out (5 minutes)"));
|
|
1016
|
+
}, 5 * 60 * 1e3);
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// src/commands/login.ts
|
|
1021
|
+
var accent2 = chalk2.hex("#6366f1");
|
|
1022
|
+
async function loginCommand() {
|
|
1023
|
+
const existing = getUser();
|
|
1024
|
+
if (existing) {
|
|
1025
|
+
console.log();
|
|
1026
|
+
console.log(` Already logged in as ${accent2(existing.name)} (${chalk2.dim(existing.email)})`);
|
|
1027
|
+
console.log(chalk2.dim(" Run 'shipwell logout' first to switch accounts."));
|
|
1028
|
+
console.log();
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
console.log();
|
|
1032
|
+
console.log(` ${accent2("\u26F5")} Opening browser to sign in...`);
|
|
1033
|
+
console.log();
|
|
1034
|
+
const spinner = ora2({ text: "Waiting for authentication...", color: "cyan", prefixText: " " }).start();
|
|
1035
|
+
try {
|
|
1036
|
+
const result = await startAuthFlow("https://shipwell.app");
|
|
1037
|
+
setUser(result);
|
|
1038
|
+
spinner.succeed(`Logged in as ${accent2(result.name)} (${chalk2.dim(result.email)})`);
|
|
1039
|
+
console.log();
|
|
1040
|
+
console.log(chalk2.dim(" Next, set your API key:"));
|
|
1041
|
+
console.log(` ${chalk2.cyan("shipwell config set api-key")} ${chalk2.dim("sk-ant-...")}`);
|
|
1042
|
+
console.log();
|
|
1043
|
+
} catch (err) {
|
|
1044
|
+
spinner.fail(err.message);
|
|
1045
|
+
process.exit(1);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// src/commands/logout.ts
|
|
1050
|
+
import chalk3 from "chalk";
|
|
1051
|
+
var accent3 = chalk3.hex("#6366f1");
|
|
1052
|
+
function logoutCommand() {
|
|
1053
|
+
const user = getUser();
|
|
1054
|
+
if (!user) {
|
|
1055
|
+
console.log();
|
|
1056
|
+
console.log(chalk3.dim(" Not logged in."));
|
|
1057
|
+
console.log();
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
clearUser();
|
|
1061
|
+
console.log();
|
|
1062
|
+
console.log(` ${chalk3.green("\u2713")} Logged out ${accent3(user.name)}`);
|
|
1063
|
+
console.log();
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// src/commands/whoami.ts
|
|
1067
|
+
import chalk4 from "chalk";
|
|
1068
|
+
var accent4 = chalk4.hex("#6366f1");
|
|
1069
|
+
var dim2 = chalk4.dim;
|
|
1070
|
+
function whoamiCommand() {
|
|
1071
|
+
const user = getUser();
|
|
1072
|
+
const apiKey = getApiKey();
|
|
1073
|
+
const model = getModel();
|
|
1074
|
+
console.log();
|
|
1075
|
+
if (user) {
|
|
1076
|
+
console.log(` ${accent4("\u26F5")} ${chalk4.bold(user.name)}`);
|
|
1077
|
+
console.log(` ${dim2(user.email)}`);
|
|
1078
|
+
} else {
|
|
1079
|
+
console.log(` ${dim2("Not logged in.")} Run ${chalk4.cyan("shipwell login")} to sign in.`);
|
|
1080
|
+
}
|
|
1081
|
+
console.log();
|
|
1082
|
+
console.log(` ${dim2("API Key")} ${apiKey ? chalk4.green("\u25CF") + " configured " + dim2(`(${apiKey.slice(0, 12)}...)`) : chalk4.yellow("\u25CF") + " not set"}`);
|
|
1083
|
+
console.log(` ${dim2("Model")} ${accent4(model || DEFAULT_MODEL)}${!model ? dim2(" (default)") : ""}`);
|
|
1084
|
+
console.log(` ${dim2("Config")} ${dim2("~/.shipwell/config.json")}`);
|
|
1085
|
+
console.log();
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// src/commands/config-cmd.ts
|
|
1089
|
+
import chalk5 from "chalk";
|
|
1090
|
+
var accent5 = chalk5.hex("#6366f1");
|
|
1091
|
+
var dim3 = chalk5.dim;
|
|
1092
|
+
function configShowCommand() {
|
|
1093
|
+
const config2 = loadConfig();
|
|
1094
|
+
console.log();
|
|
1095
|
+
console.log(` ${chalk5.bold("Configuration")} ${dim3("~/.shipwell/config.json")}`);
|
|
1096
|
+
console.log();
|
|
1097
|
+
console.log(` ${dim3("api-key")} ${config2.apiKey ? chalk5.green("\u25CF") + " " + dim3(`${config2.apiKey.slice(0, 12)}...`) : chalk5.yellow("\u25CF") + " not set"}`);
|
|
1098
|
+
console.log(` ${dim3("model")} ${accent5(config2.model || DEFAULT_MODEL)}${!config2.model ? dim3(" (default)") : ""}`);
|
|
1099
|
+
if (config2.user) {
|
|
1100
|
+
console.log(` ${dim3("user")} ${config2.user.name} ${dim3(`(${config2.user.email})`)}`);
|
|
1101
|
+
}
|
|
1102
|
+
console.log();
|
|
1103
|
+
console.log(dim3(" Set values:"));
|
|
1104
|
+
console.log(` ${chalk5.cyan("shipwell config set api-key")} ${dim3("<key>")}`);
|
|
1105
|
+
console.log(` ${chalk5.cyan("shipwell config set model")} ${dim3("<model-id>")}`);
|
|
1106
|
+
console.log();
|
|
1107
|
+
}
|
|
1108
|
+
function configSetCommand(key, value) {
|
|
1109
|
+
switch (key) {
|
|
1110
|
+
case "api-key": {
|
|
1111
|
+
if (!value.startsWith("sk-ant-")) {
|
|
1112
|
+
console.log(chalk5.yellow("\n Warning: API key doesn't look like an Anthropic key (expected sk-ant-...).\n"));
|
|
1113
|
+
}
|
|
1114
|
+
setApiKey(value);
|
|
1115
|
+
console.log(`
|
|
1116
|
+
${chalk5.green("\u2713")} API key saved
|
|
1117
|
+
`);
|
|
1118
|
+
break;
|
|
1119
|
+
}
|
|
1120
|
+
case "model": {
|
|
1121
|
+
const validIds = AVAILABLE_MODELS.map((m) => m.id);
|
|
1122
|
+
if (!validIds.includes(value)) {
|
|
1123
|
+
console.error(chalk5.red(`
|
|
1124
|
+
Unknown model: ${value}`));
|
|
1125
|
+
console.error(dim3(` Available: ${validIds.join(", ")}
|
|
1126
|
+
`));
|
|
1127
|
+
process.exit(1);
|
|
1128
|
+
}
|
|
1129
|
+
setModel(value);
|
|
1130
|
+
console.log(`
|
|
1131
|
+
${chalk5.green("\u2713")} Default model set to ${accent5(value)}
|
|
1132
|
+
`);
|
|
1133
|
+
break;
|
|
1134
|
+
}
|
|
1135
|
+
default:
|
|
1136
|
+
console.error(chalk5.red(`
|
|
1137
|
+
Unknown config key: ${key}`));
|
|
1138
|
+
console.error(dim3(` Available keys: api-key, model
|
|
1139
|
+
`));
|
|
1140
|
+
process.exit(1);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
function configDeleteCommand(key) {
|
|
1144
|
+
switch (key) {
|
|
1145
|
+
case "api-key":
|
|
1146
|
+
clearApiKey();
|
|
1147
|
+
console.log(`
|
|
1148
|
+
${chalk5.green("\u2713")} API key removed
|
|
1149
|
+
`);
|
|
1150
|
+
break;
|
|
1151
|
+
case "model":
|
|
1152
|
+
setModel("");
|
|
1153
|
+
console.log(`
|
|
1154
|
+
${chalk5.green("\u2713")} Model reset to default (${DEFAULT_MODEL})
|
|
1155
|
+
`);
|
|
1156
|
+
break;
|
|
1157
|
+
default:
|
|
1158
|
+
console.error(chalk5.red(`
|
|
1159
|
+
Unknown config key: ${key}`));
|
|
1160
|
+
process.exit(1);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// src/commands/models.ts
|
|
1165
|
+
import chalk6 from "chalk";
|
|
1166
|
+
var accent6 = chalk6.hex("#6366f1");
|
|
1167
|
+
var dim4 = chalk6.dim;
|
|
1168
|
+
function modelsCommand() {
|
|
1169
|
+
const currentModel = getModel() || DEFAULT_MODEL;
|
|
1170
|
+
console.log();
|
|
1171
|
+
console.log(` ${chalk6.bold("Available Models")}`);
|
|
1172
|
+
console.log();
|
|
1173
|
+
for (const m of AVAILABLE_MODELS) {
|
|
1174
|
+
const isCurrent = m.id === currentModel;
|
|
1175
|
+
const marker = isCurrent ? accent6("\u25CF") : dim4("\u25CB");
|
|
1176
|
+
const label = isCurrent ? chalk6.bold(m.label) : m.label;
|
|
1177
|
+
const id = dim4(m.id);
|
|
1178
|
+
const ctx = dim4(`${Math.round(m.contextWindow / 1e3)}K context`);
|
|
1179
|
+
const dflt = "default" in m && m.default ? chalk6.green(" default") : "";
|
|
1180
|
+
const active = isCurrent ? accent6(" \u2190 active") : "";
|
|
1181
|
+
console.log(` ${marker} ${label} ${id} ${ctx}${dflt}${active}`);
|
|
1182
|
+
}
|
|
1183
|
+
console.log();
|
|
1184
|
+
console.log(dim4(` Switch: shipwell config set model <model-id>`));
|
|
1185
|
+
console.log();
|
|
1186
|
+
}
|
|
1187
|
+
|
|
882
1188
|
// src/index.ts
|
|
1189
|
+
var VERSION = "0.2.0";
|
|
1190
|
+
var accent7 = chalk7.hex("#6366f1");
|
|
1191
|
+
var dim5 = chalk7.dim;
|
|
1192
|
+
function showBanner() {
|
|
1193
|
+
const user = getUser();
|
|
1194
|
+
const apiKey = getApiKey();
|
|
1195
|
+
console.log();
|
|
1196
|
+
console.log(` ${accent7("\u26F5")} ${chalk7.bold("Shipwell")} ${dim5(`v${VERSION}`)}`);
|
|
1197
|
+
console.log(` ${dim5("Full Codebase Autopilot \u2014 powered by Claude")}`);
|
|
1198
|
+
console.log();
|
|
1199
|
+
if (!user) {
|
|
1200
|
+
console.log(` ${chalk7.yellow("\u25CF")} Not logged in`);
|
|
1201
|
+
console.log(` ${dim5("Get started:")} ${chalk7.cyan("shipwell login")}`);
|
|
1202
|
+
console.log();
|
|
1203
|
+
} else if (!apiKey) {
|
|
1204
|
+
console.log(` ${chalk7.green("\u25CF")} ${user.name} ${dim5(`(${user.email})`)}`);
|
|
1205
|
+
console.log(` ${chalk7.yellow("\u25CF")} API key not set`);
|
|
1206
|
+
console.log(` ${dim5("Set it:")} ${chalk7.cyan("shipwell config set api-key")} ${dim5("sk-ant-...")}`);
|
|
1207
|
+
console.log();
|
|
1208
|
+
} else {
|
|
1209
|
+
console.log(` ${chalk7.green("\u25CF")} ${user.name} ${dim5(`(${user.email})`)}`);
|
|
1210
|
+
console.log(` ${chalk7.green("\u25CF")} API key configured`);
|
|
1211
|
+
console.log();
|
|
1212
|
+
}
|
|
1213
|
+
console.log(` ${chalk7.bold("Analysis Commands")}`);
|
|
1214
|
+
console.log(` ${chalk7.cyan("shipwell audit")} ${dim5("<path>")} Security audit`);
|
|
1215
|
+
console.log(` ${chalk7.cyan("shipwell migrate")} ${dim5("<path>")} Migration plan`);
|
|
1216
|
+
console.log(` ${chalk7.cyan("shipwell refactor")} ${dim5("<path>")} Refactor analysis`);
|
|
1217
|
+
console.log(` ${chalk7.cyan("shipwell docs")} ${dim5("<path>")} Generate documentation`);
|
|
1218
|
+
console.log(` ${chalk7.cyan("shipwell upgrade")} ${dim5("<path>")} Dependency upgrade plan`);
|
|
1219
|
+
console.log();
|
|
1220
|
+
console.log(` ${chalk7.bold("Account & Config")}`);
|
|
1221
|
+
console.log(` ${chalk7.cyan("shipwell login")} Sign in with Google`);
|
|
1222
|
+
console.log(` ${chalk7.cyan("shipwell logout")} Sign out`);
|
|
1223
|
+
console.log(` ${chalk7.cyan("shipwell whoami")} Show current user & config`);
|
|
1224
|
+
console.log(` ${chalk7.cyan("shipwell config")} View configuration`);
|
|
1225
|
+
console.log(` ${chalk7.cyan("shipwell config set")} ${dim5("<k> <v>")} Set a config value`);
|
|
1226
|
+
console.log(` ${chalk7.cyan("shipwell models")} List available models`);
|
|
1227
|
+
console.log(` ${chalk7.cyan("shipwell update")} Update to latest version`);
|
|
1228
|
+
console.log();
|
|
1229
|
+
console.log(` ${dim5("Docs: https://shipwell.app \xB7 v" + VERSION)}`);
|
|
1230
|
+
console.log();
|
|
1231
|
+
}
|
|
883
1232
|
var program = new Command();
|
|
884
|
-
program.name("shipwell").description("Full Codebase Autopilot \u2014 deep cross-file analysis powered by Claude").version(
|
|
1233
|
+
program.name("shipwell").description("Full Codebase Autopilot \u2014 deep cross-file analysis powered by Claude").version(VERSION).action(() => {
|
|
1234
|
+
showBanner();
|
|
1235
|
+
});
|
|
885
1236
|
var operations = ["audit", "migrate", "refactor", "docs", "upgrade"];
|
|
886
1237
|
var opDesc = {
|
|
887
1238
|
audit: "Run a security audit on a codebase",
|
|
@@ -891,8 +1242,48 @@ var opDesc = {
|
|
|
891
1242
|
upgrade: "Analyze dependencies & plan safe upgrades"
|
|
892
1243
|
};
|
|
893
1244
|
for (const op of operations) {
|
|
894
|
-
program.command(op).description(opDesc[op] || `Run ${op} analysis on a codebase`).argument("<source>", "Local path or GitHub URL").option("-k, --api-key <key>", "Anthropic API key
|
|
1245
|
+
program.command(op).description(opDesc[op] || `Run ${op} analysis on a codebase`).argument("<source>", "Local path or GitHub URL").option("-k, --api-key <key>", "Anthropic API key").option("-m, --model <model>", "Claude model to use").option("-t, --target <target>", "Migration target (for migrate)").option("-c, --context <context>", "Additional context for the analysis").option("-r, --raw", "Also print raw streaming output").action((source, options) => {
|
|
895
1246
|
analyzeCommand(op, source, options);
|
|
896
1247
|
});
|
|
897
1248
|
}
|
|
1249
|
+
program.command("login").description("Sign in with Google via browser").action(() => {
|
|
1250
|
+
loginCommand();
|
|
1251
|
+
});
|
|
1252
|
+
program.command("logout").description("Sign out and clear stored credentials").action(() => {
|
|
1253
|
+
logoutCommand();
|
|
1254
|
+
});
|
|
1255
|
+
program.command("whoami").description("Show current user, API key status, and model").action(() => {
|
|
1256
|
+
whoamiCommand();
|
|
1257
|
+
});
|
|
1258
|
+
var config = program.command("config").description("View or modify configuration").action(() => {
|
|
1259
|
+
configShowCommand();
|
|
1260
|
+
});
|
|
1261
|
+
config.command("set").description("Set a config value (api-key, model)").argument("<key>", "Config key (api-key, model)").argument("<value>", "Config value").action((key, value) => {
|
|
1262
|
+
configSetCommand(key, value);
|
|
1263
|
+
});
|
|
1264
|
+
config.command("delete").description("Delete a config value").argument("<key>", "Config key (api-key, model)").action((key) => {
|
|
1265
|
+
configDeleteCommand(key);
|
|
1266
|
+
});
|
|
1267
|
+
program.command("models").description("List available Claude models").action(() => {
|
|
1268
|
+
modelsCommand();
|
|
1269
|
+
});
|
|
1270
|
+
program.command("update").description("Update Shipwell CLI to the latest version").action(async () => {
|
|
1271
|
+
const { execSync } = await import("child_process");
|
|
1272
|
+
const ora3 = (await import("ora")).default;
|
|
1273
|
+
const spinner = ora3({ text: "Checking for updates...", prefixText: " " }).start();
|
|
1274
|
+
try {
|
|
1275
|
+
const latest = execSync("npm view @shipwellapp/cli version", { encoding: "utf-8" }).trim();
|
|
1276
|
+
if (latest === VERSION) {
|
|
1277
|
+
spinner.succeed(`Already on the latest version (${accent7(VERSION)})`);
|
|
1278
|
+
} else {
|
|
1279
|
+
spinner.text = `Updating to v${latest}...`;
|
|
1280
|
+
execSync("npm install -g @shipwellapp/cli@latest", { stdio: "pipe" });
|
|
1281
|
+
spinner.succeed(`Updated to v${accent7(latest)} ${dim5(`(was v${VERSION})`)}`);
|
|
1282
|
+
}
|
|
1283
|
+
} catch (err) {
|
|
1284
|
+
spinner.fail("Update failed. Try manually:");
|
|
1285
|
+
console.log(` ${chalk7.cyan("npm install -g @shipwellapp/cli@latest")}`);
|
|
1286
|
+
}
|
|
1287
|
+
console.log();
|
|
1288
|
+
});
|
|
898
1289
|
program.parse();
|