start-vibing 4.4.14 → 4.4.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -130,7 +130,7 @@ var STALE_FILES = [
130
130
  {
131
131
  category: "agents",
132
132
  relPath: "research-web.md",
133
- replacedBy: "research skill (scout \u2192 query \u2192 synthesize \u2192 verify)",
133
+ replacedBy: "research skill (scout query synthesize verify)",
134
134
  staleChecksums: [
135
135
  "49d0cb862d199d1dcb59ed932ed04d8abd0064de3b65ef4f346268528dc21772",
136
136
  "72ba14b6cad76a2d8718234775883242e94e82497e4edeec7fb35faf24327d6a",
@@ -219,8 +219,7 @@ function tryDelete(claudeDir, locationLabel, stale, result) {
219
219
  location: locationLabel,
220
220
  replacedBy: stale.replacedBy
221
221
  });
222
- } catch {
223
- }
222
+ } catch {}
224
223
  }
225
224
  function cleanupStaleAgents(targetDir) {
226
225
  const result = { deleted: [], keptCustomized: [] };
@@ -240,8 +239,8 @@ function cleanupStaleAgents(targetDir) {
240
239
  }
241
240
 
242
241
  // src/cli.ts
243
- import { existsSync as existsSync6, readFileSync as readFileSync5, appendFileSync, writeFileSync as writeFileSync5 } from "fs";
244
- import { join as join6, dirname as dirname2 } from "path";
242
+ import { existsSync as existsSync7, readFileSync as readFileSync6, appendFileSync as appendFileSync2, writeFileSync as writeFileSync6 } from "fs";
243
+ import { join as join7, dirname as dirname3 } from "path";
245
244
  import { fileURLToPath as fileURLToPath2 } from "url";
246
245
  import { execSync as execSync3 } from "child_process";
247
246
 
@@ -359,8 +358,7 @@ function writeCache(latestVersion) {
359
358
  latestVersion
360
359
  };
361
360
  writeFileSync2(CACHE_FILE, JSON.stringify(entry, null, 2));
362
- } catch {
363
- }
361
+ } catch {}
364
362
  }
365
363
  function isCacheValid(entry) {
366
364
  if (!entry)
@@ -509,8 +507,7 @@ async function installClaudeWindows(shell) {
509
507
  stdio: "ignore",
510
508
  shell: "cmd.exe"
511
509
  });
512
- } catch {
513
- }
510
+ } catch {}
514
511
  }
515
512
  return { success: true, alreadyInstalled: false };
516
513
  } catch (error) {
@@ -539,9 +536,15 @@ async function installClaudeUnix() {
539
536
  function encodeProjectPath(absolutePath) {
540
537
  return absolutePath.replace(/\\/g, "/").replace(/\//g, "-");
541
538
  }
539
+ function getActiveClaudeDir() {
540
+ const fromEnv = process.env["CLAUDE_CONFIG_DIR"];
541
+ if (fromEnv && fromEnv.trim().length > 0)
542
+ return fromEnv;
543
+ return join4(homedir2(), ".claude");
544
+ }
542
545
  function hasExistingSession(projectDir) {
543
546
  const encodedPath = encodeProjectPath(projectDir);
544
- const sessionDir = join4(homedir2(), ".claude", "projects", encodedPath);
547
+ const sessionDir = join4(getActiveClaudeDir(), "projects", encodedPath);
545
548
  const indexFile = join4(sessionDir, "sessions-index.json");
546
549
  if (existsSync4(indexFile)) {
547
550
  try {
@@ -552,15 +555,13 @@ function hasExistingSession(projectDir) {
552
555
  if (mainSessions.length > 0)
553
556
  return true;
554
557
  }
555
- } catch {
556
- }
558
+ } catch {}
557
559
  }
558
560
  if (existsSync4(sessionDir)) {
559
561
  try {
560
562
  const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/;
561
563
  return readdirSync2(sessionDir).some((f) => uuidPattern.test(f));
562
- } catch {
563
- }
564
+ } catch {}
564
565
  }
565
566
  return false;
566
567
  }
@@ -597,8 +598,7 @@ function launchClaude(cwd, options = {}) {
597
598
  }
598
599
  function ensureHooksEnabled() {
599
600
  try {
600
- const home = homedir2();
601
- const globalSettingsPath = join4(home, ".claude", "settings.json");
601
+ const globalSettingsPath = join4(getActiveClaudeDir(), "settings.json");
602
602
  if (!existsSync4(globalSettingsPath)) {
603
603
  return { modified: false };
604
604
  }
@@ -801,9 +801,12 @@ async function installPlugin(plugin) {
801
801
  let stdout = "";
802
802
  let stderr = "";
803
803
  if (proc.stdin) {
804
- proc.stdin.write("y\n");
805
- proc.stdin.write("y\n");
806
- proc.stdin.write("y\n");
804
+ proc.stdin.write(`y
805
+ `);
806
+ proc.stdin.write(`y
807
+ `);
808
+ proc.stdin.write(`y
809
+ `);
807
810
  proc.stdin.end();
808
811
  }
809
812
  proc.stdout?.on("data", (data) => {
@@ -938,6 +941,12 @@ function getRecommendedSkills() {
938
941
  return RECOMMENDED_SKILLS;
939
942
  }
940
943
 
944
+ // src/profile.ts
945
+ import { spawn as spawn4, spawnSync as spawnSync4 } from "child_process";
946
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync5, appendFileSync, writeFileSync as writeFileSync5 } from "fs";
947
+ import { join as join6, dirname as dirname2 } from "path";
948
+ import { homedir as homedir3 } from "os";
949
+
941
950
  // src/ui.ts
942
951
  var c = {
943
952
  reset: "\x1B[0m",
@@ -953,7 +962,7 @@ var c = {
953
962
  bgRed: "\x1B[41m",
954
963
  redBright: "\x1B[91m"
955
964
  };
956
- var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
965
+ var SPINNER_FRAMES = ["", "", "", "", "", "", "", "", "", ""];
957
966
  function createBanner(version) {
958
967
  const date = new Date().toISOString().slice(0, 10);
959
968
  return `
@@ -963,7 +972,7 @@ ${c.cyan} \\ \\ / / | | | _ \\ | | | \\| || | _
963
972
  ${c.cyan} \\ \\/ / | | | |_) | | | | |\\ || |_| |
964
973
  ${c.cyan} \\__/ |___||____/ |___||_| \\_| \\____|${c.reset}
965
974
 
966
- ${c.bright} v${version}${c.reset}${c.dim} \xB7 ${date} \xB7 9 plugins \xB7 17 skills \xB7 8 MCPs${c.reset}
975
+ ${c.bright} v${version}${c.reset}${c.dim} · ${date} · 9 plugins · 17 skills · 8 MCPs${c.reset}
967
976
  `;
968
977
  }
969
978
  function createSpinner(initialText) {
@@ -984,13 +993,13 @@ function createSpinner(initialText) {
984
993
  succeed(finalText) {
985
994
  if (interval)
986
995
  clearInterval(interval);
987
- process.stdout.write(`\r ${c.green}\u2713${c.reset} ${finalText}\x1B[K
996
+ process.stdout.write(`\r ${c.green}✓${c.reset} ${finalText}\x1B[K
988
997
  `);
989
998
  },
990
999
  fail(finalText) {
991
1000
  if (interval)
992
1001
  clearInterval(interval);
993
- process.stdout.write(`\r ${c.red}\u2717${c.reset} ${finalText}\x1B[K
1002
+ process.stdout.write(`\r ${c.red}✗${c.reset} ${finalText}\x1B[K
994
1003
  `);
995
1004
  },
996
1005
  stop() {
@@ -1004,8 +1013,8 @@ function phaseHeader(step, total, label) {
1004
1013
  return `${c.dim}[${step}/${total}]${c.reset} ${label}`;
1005
1014
  }
1006
1015
  function treeItem(name, description, isLast, success) {
1007
- const branch = isLast ? "\u2514" : "\u251C";
1008
- const icon = success ? `${c.green}\u2713${c.reset}` : `${c.red}\u2717${c.reset}`;
1016
+ const branch = isLast ? "" : "";
1017
+ const icon = success ? `${c.green}✓${c.reset}` : `${c.red}✗${c.reset}`;
1009
1018
  return ` ${c.dim}${branch}${c.reset} ${c.cyan}${name.padEnd(21)}${c.reset}${icon} ${c.dim}${description}${c.reset}`;
1010
1019
  }
1011
1020
  function formatElapsed(startMs) {
@@ -1015,27 +1024,159 @@ function formatElapsed(startMs) {
1015
1024
  function printOptionalMcps() {
1016
1025
  console.log("");
1017
1026
  console.log(` ${c.dim}Optional MCPs (install manually):${c.reset}`);
1018
- console.log(` ${c.cyan}github${c.reset} ${c.dim}\xB7${c.reset} claude mcp add --transport http -s user github https://api.githubcopilot.com/mcp/`);
1019
- console.log(` ${c.cyan}sentry${c.reset} ${c.dim}\xB7${c.reset} claude mcp add --transport http -s user sentry https://mcp.sentry.dev/mcp`);
1020
- console.log(` ${c.cyan}figma${c.reset} ${c.dim}\xB7${c.reset} claude mcp add --transport http -s user figma https://mcp.figma.com/mcp`);
1021
- console.log(` ${c.cyan}linear${c.reset} ${c.dim}\xB7${c.reset} claude mcp add --transport http -s user linear https://mcp.linear.app/sse`);
1022
- console.log(` ${c.cyan}stripe${c.reset} ${c.dim}\xB7${c.reset} claude mcp add --transport http -s user stripe https://mcp.stripe.com`);
1023
- console.log(` ${c.cyan}vercel${c.reset} ${c.dim}\xB7${c.reset} claude mcp add --transport http -s user vercel https://mcp.vercel.com`);
1027
+ console.log(` ${c.cyan}github${c.reset} ${c.dim}·${c.reset} claude mcp add --transport http -s user github https://api.githubcopilot.com/mcp/`);
1028
+ console.log(` ${c.cyan}sentry${c.reset} ${c.dim}·${c.reset} claude mcp add --transport http -s user sentry https://mcp.sentry.dev/mcp`);
1029
+ console.log(` ${c.cyan}figma${c.reset} ${c.dim}·${c.reset} claude mcp add --transport http -s user figma https://mcp.figma.com/mcp`);
1030
+ console.log(` ${c.cyan}linear${c.reset} ${c.dim}·${c.reset} claude mcp add --transport http -s user linear https://mcp.linear.app/sse`);
1031
+ console.log(` ${c.cyan}stripe${c.reset} ${c.dim}·${c.reset} claude mcp add --transport http -s user stripe https://mcp.stripe.com`);
1032
+ console.log(` ${c.cyan}vercel${c.reset} ${c.dim}·${c.reset} claude mcp add --transport http -s user vercel https://mcp.vercel.com`);
1033
+ }
1034
+
1035
+ // src/profile.ts
1036
+ var RESERVED_NAMES = new Set(["default", "main", "claude"]);
1037
+ var NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
1038
+ function parseProfileArg(args) {
1039
+ const idx = args.indexOf("--profile");
1040
+ if (idx === -1)
1041
+ return null;
1042
+ const value = args[idx + 1];
1043
+ if (!value || value.startsWith("--")) {
1044
+ throw new Error("--profile requires a name (e.g. --profile work)");
1045
+ }
1046
+ if (RESERVED_NAMES.has(value.toLowerCase())) {
1047
+ throw new Error(`Profile name "${value}" is reserved. Use a different name (e.g. work, personal, client-x).`);
1048
+ }
1049
+ if (!NAME_PATTERN.test(value)) {
1050
+ throw new Error(`Profile name "${value}" is invalid. Use lowercase letters, digits, and hyphens (e.g. work, my-client).`);
1051
+ }
1052
+ return value;
1053
+ }
1054
+ function resolveProfileDir(name) {
1055
+ const home = getHomeDir() || homedir3();
1056
+ const dir = join6(home, `.claude-${name}`);
1057
+ return { name, dir, isNew: !existsSync6(dir) };
1058
+ }
1059
+ function ensureProfileDir(dir) {
1060
+ if (!existsSync6(dir)) {
1061
+ mkdirSync4(dir, { recursive: true });
1062
+ }
1063
+ }
1064
+ async function loginToProfile(name) {
1065
+ console.log("");
1066
+ console.log(` ${c.cyan}First-time setup for profile "${name}"${c.reset}`);
1067
+ console.log(` ${c.dim}Claude needs to authenticate this profile. A browser will open for SSO login.${c.reset}`);
1068
+ console.log(` ${c.dim}When done, type ${c.bright}/quit${c.reset}${c.dim} (or close the window) to continue.${c.reset}`);
1069
+ console.log("");
1070
+ await new Promise((resolve2) => {
1071
+ const proc = spawn4("claude", ["--dangerously-skip-permissions"], {
1072
+ stdio: "inherit",
1073
+ shell: process.platform === "win32"
1074
+ });
1075
+ proc.on("exit", () => resolve2());
1076
+ proc.on("error", () => resolve2());
1077
+ });
1078
+ console.log("");
1079
+ console.log(` ${c.green}✓${c.reset} Login complete. Resuming setup...`);
1080
+ console.log("");
1081
+ }
1082
+ function getShellConfigPath() {
1083
+ const shell = detectShell();
1084
+ const home = getHomeDir() || homedir3();
1085
+ if (shell === "powershell") {
1086
+ try {
1087
+ const result = spawnSync4("powershell", ["-NoProfile", "-NonInteractive", "-Command", "$PROFILE"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000 });
1088
+ if (result.status === 0 && result.stdout) {
1089
+ return { path: result.stdout.trim(), shell: "powershell" };
1090
+ }
1091
+ } catch {}
1092
+ return {
1093
+ path: join6(home, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1"),
1094
+ shell: "powershell"
1095
+ };
1096
+ }
1097
+ if (shell === "zsh")
1098
+ return { path: join6(home, ".zshrc"), shell: "zsh" };
1099
+ if (shell === "bash")
1100
+ return { path: join6(home, ".bashrc"), shell: "bash" };
1101
+ return null;
1102
+ }
1103
+ function buildShellFunction(name, configDir, shell) {
1104
+ const marker = `# start-vibing profile: ${name}`;
1105
+ if (shell === "powershell") {
1106
+ const dirLiteral = configDir.replace(/'/g, "''");
1107
+ return [
1108
+ "",
1109
+ marker,
1110
+ `function claude-${name} {`,
1111
+ ` $env:CLAUDE_CONFIG_DIR = '${dirLiteral}'`,
1112
+ ` claude @args`,
1113
+ `}`,
1114
+ ""
1115
+ ].join(`
1116
+ `);
1117
+ }
1118
+ const home = getHomeDir() || homedir3();
1119
+ const portable = configDir.startsWith(home) ? `$HOME${configDir.slice(home.length).replace(/\\/g, "/")}` : configDir.replace(/\\/g, "/");
1120
+ return [
1121
+ "",
1122
+ marker,
1123
+ `claude-${name}() {`,
1124
+ ` CLAUDE_CONFIG_DIR="${portable}" claude "$@"`,
1125
+ `}`,
1126
+ ""
1127
+ ].join(`
1128
+ `);
1129
+ }
1130
+ function injectShellFunction(name, configDir) {
1131
+ const target = getShellConfigPath();
1132
+ if (!target) {
1133
+ return { injected: false, skipped: false, path: null, reloadHint: null };
1134
+ }
1135
+ const marker = `# start-vibing profile: ${name}`;
1136
+ let existing = "";
1137
+ if (existsSync6(target.path)) {
1138
+ try {
1139
+ existing = readFileSync5(target.path, "utf-8");
1140
+ } catch {
1141
+ existing = "";
1142
+ }
1143
+ }
1144
+ if (existing.includes(marker)) {
1145
+ return { injected: false, skipped: true, path: target.path, reloadHint: null };
1146
+ }
1147
+ const snippet = buildShellFunction(name, configDir, target.shell);
1148
+ try {
1149
+ const parent = dirname2(target.path);
1150
+ if (!existsSync6(parent))
1151
+ mkdirSync4(parent, { recursive: true });
1152
+ if (existsSync6(target.path)) {
1153
+ const prefix = existing.endsWith(`
1154
+ `) ? "" : `
1155
+ `;
1156
+ appendFileSync(target.path, prefix + snippet, "utf-8");
1157
+ } else {
1158
+ writeFileSync5(target.path, snippet, "utf-8");
1159
+ }
1160
+ } catch {
1161
+ return { injected: false, skipped: false, path: target.path, reloadHint: null };
1162
+ }
1163
+ const reload = target.shell === "powershell" ? ". $PROFILE" : target.shell === "zsh" ? "source ~/.zshrc" : "source ~/.bashrc";
1164
+ return { injected: true, skipped: false, path: target.path, reloadHint: reload };
1024
1165
  }
1025
1166
 
1026
1167
  // src/cli.ts
1027
1168
  var __filename3 = fileURLToPath2(import.meta.url);
1028
- var __dirname3 = dirname2(__filename3);
1169
+ var __dirname3 = dirname3(__filename3);
1029
1170
  var TOTAL_PHASES = 6;
1030
1171
  function getVersion() {
1031
1172
  try {
1032
1173
  const paths = [
1033
- join6(__dirname3, "..", "package.json"),
1034
- join6(__dirname3, "..", "..", "package.json")
1174
+ join7(__dirname3, "..", "package.json"),
1175
+ join7(__dirname3, "..", "..", "package.json")
1035
1176
  ];
1036
1177
  for (const pkgPath of paths) {
1037
- if (existsSync6(pkgPath)) {
1038
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
1178
+ if (existsSync7(pkgPath)) {
1179
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
1039
1180
  return pkg.version || "4.0.0";
1040
1181
  }
1041
1182
  }
@@ -1085,21 +1226,30 @@ function syncGitignore(targetDir) {
1085
1226
  else
1086
1227
  toIgnore.push(p);
1087
1228
  }
1088
- const gitignorePath = join6(targetDir, ".gitignore");
1229
+ const gitignorePath = join7(targetDir, ".gitignore");
1089
1230
  const header = "# start-vibing (local Claude setup - not tracked in this repo)";
1090
1231
  let current = "";
1091
- if (existsSync6(gitignorePath))
1092
- current = readFileSync5(gitignorePath, "utf-8");
1232
+ if (existsSync7(gitignorePath))
1233
+ current = readFileSync6(gitignorePath, "utf-8");
1093
1234
  const existing = new Set(current.split(/\r?\n/).map((l) => l.trim()).filter(Boolean));
1094
1235
  const missing = toIgnore.filter((e) => !existing.has(e));
1095
1236
  if (missing.length === 0) {
1096
1237
  return { success: true, added: [], tracked, message: "Already in .gitignore" };
1097
1238
  }
1098
1239
  if (current.length === 0) {
1099
- writeFileSync5(gitignorePath, `${header}\n${missing.join("\n")}\n`, "utf-8");
1240
+ writeFileSync6(gitignorePath, `${header}
1241
+ ${missing.join(`
1242
+ `)}
1243
+ `, "utf-8");
1100
1244
  } else {
1101
- const prefix = current.endsWith("\n") ? "" : "\n";
1102
- appendFileSync(gitignorePath, `${prefix}\n${header}\n${missing.join("\n")}\n`, "utf-8");
1245
+ const prefix = current.endsWith(`
1246
+ `) ? "" : `
1247
+ `;
1248
+ appendFileSync2(gitignorePath, `${prefix}
1249
+ ${header}
1250
+ ${missing.join(`
1251
+ `)}
1252
+ `, "utf-8");
1103
1253
  }
1104
1254
  return {
1105
1255
  success: true,
@@ -1115,6 +1265,7 @@ var HELP = `${createBanner(VERSION)}
1115
1265
  npx start-vibing [options]
1116
1266
 
1117
1267
  ${c.bright}Options:${c.reset}
1268
+ --profile <name> Use a separate Claude account profile (~/.claude-<name>/)
1118
1269
  --new Start fresh Claude session (default: resume last)
1119
1270
  --force Overwrite all files (including custom domains)
1120
1271
  --no-claude Skip Claude Code installation and launch
@@ -1124,6 +1275,12 @@ var HELP = `${createBanner(VERSION)}
1124
1275
  --help, -h Show this help message
1125
1276
  --version, -v Show version
1126
1277
 
1278
+ ${c.bright}Profiles:${c.reset}
1279
+ Each profile is an isolated Claude account (separate creds, MCPs, plugins).
1280
+ First run on a new profile triggers SSO login before MCP/plugin install.
1281
+ A shell function ${c.dim}claude-<name>${c.reset} is auto-injected for quick switching.
1282
+ Reload your shell ($PROFILE / .bashrc / .zshrc) after first run.
1283
+
1127
1284
  ${c.bright}What it does:${c.reset}
1128
1285
  [1] Copies template files (agents, skills, config)
1129
1286
  [2] Sets up Claude Code (install/migrate if needed)
@@ -1160,6 +1317,20 @@ async function main() {
1160
1317
  const newSession = args.includes("--new");
1161
1318
  const targetDir = process.cwd();
1162
1319
  const globalStart = Date.now();
1320
+ let profile = null;
1321
+ try {
1322
+ const profileName = parseProfileArg(args);
1323
+ if (profileName) {
1324
+ profile = resolveProfileDir(profileName);
1325
+ ensureProfileDir(profile.dir);
1326
+ process.env["CLAUDE_CONFIG_DIR"] = profile.dir;
1327
+ }
1328
+ } catch (err) {
1329
+ console.error("");
1330
+ console.error(` ${c.red}${err instanceof Error ? err.message : err}${c.reset}`);
1331
+ console.error("");
1332
+ process.exit(1);
1333
+ }
1163
1334
  if (!skipUpdateCheck) {
1164
1335
  try {
1165
1336
  const updateResult = await checkForUpdates(VERSION);
@@ -1169,14 +1340,19 @@ async function main() {
1169
1340
  console.log(` ${c.dim}Run: ${getUpdateCommand()}${c.reset}`);
1170
1341
  console.log("");
1171
1342
  }
1172
- } catch {
1173
- }
1343
+ } catch {}
1174
1344
  }
1175
1345
  console.log(createBanner(VERSION));
1176
1346
  if (isRunningViaNpx()) {
1177
1347
  console.log(` ${c.dim}TIP: npm install -g start-vibing for permanent access${c.reset}`);
1178
1348
  console.log("");
1179
1349
  }
1350
+ if (profile) {
1351
+ const verb = profile.isNew ? "Creating" : "Reusing";
1352
+ const colour = profile.isNew ? c.cyan : c.green;
1353
+ console.log(` ${colour}→ ${verb} profile "${profile.name}"${c.reset} ${c.dim}at ${profile.dir}${c.reset}`);
1354
+ console.log("");
1355
+ }
1180
1356
  const phase1Start = Date.now();
1181
1357
  const spinner1 = createSpinner(phaseHeader(1, TOTAL_PHASES, "Copying template files..."));
1182
1358
  try {
@@ -1185,7 +1361,7 @@ async function main() {
1185
1361
  const ignoreResult = syncGitignore(targetDir);
1186
1362
  ensureHooksEnabled();
1187
1363
  const counts = `${result.agents} agents, ${result.skills} skills`;
1188
- spinner1.succeed(phaseHeader(1, TOTAL_PHASES, `Template files ${c.dim}${"\xB7".repeat(14)}${c.reset} ${counts} ${c.dim}${formatElapsed(phase1Start)}${c.reset}`));
1364
+ spinner1.succeed(phaseHeader(1, TOTAL_PHASES, `Template files ${c.dim}${"·".repeat(14)}${c.reset} ${counts} ${c.dim}${formatElapsed(phase1Start)}${c.reset}`));
1189
1365
  if (ignoreResult.success && ignoreResult.added.length > 0) {
1190
1366
  console.log(` ${c.dim}gitignored ${ignoreResult.added.length} path(s) not tracked upstream (remove from .gitignore to commit)${c.reset}`);
1191
1367
  }
@@ -1227,7 +1403,10 @@ async function main() {
1227
1403
  process.exit(1);
1228
1404
  }
1229
1405
  const claudeStatus = installResult.migrated ? "migrated to native" : installResult.alreadyInstalled ? "ready (native)" : "installed";
1230
- spinner2.succeed(phaseHeader(2, TOTAL_PHASES, `Claude Code ${c.dim}${"\xB7".repeat(18)}${c.reset} ${claudeStatus} ${c.dim}${formatElapsed(phase2Start)}${c.reset}`));
1406
+ spinner2.succeed(phaseHeader(2, TOTAL_PHASES, `Claude Code ${c.dim}${"·".repeat(18)}${c.reset} ${claudeStatus} ${c.dim}${formatElapsed(phase2Start)}${c.reset}`));
1407
+ if (profile && profile.isNew) {
1408
+ await loginToProfile(profile.name);
1409
+ }
1231
1410
  const phase3Start = Date.now();
1232
1411
  if (!skipMcp && isClaudeMcpReady()) {
1233
1412
  const spinner3 = createSpinner(phaseHeader(3, TOTAL_PHASES, "Installing MCP servers (parallel)..."));
@@ -1237,7 +1416,7 @@ async function main() {
1237
1416
  const mcpOk = mcpResult.installed + mcpResult.skipped;
1238
1417
  const mcpTotal = mcpOk + mcpResult.failed;
1239
1418
  const mcpSummary = `${mcpOk}/${mcpTotal} installed`;
1240
- spinner3.succeed(phaseHeader(3, TOTAL_PHASES, `MCP servers ${c.dim}${"\xB7".repeat(17)}${c.reset} ${mcpSummary} ${c.dim}${formatElapsed(phase3Start)}${c.reset}`));
1419
+ spinner3.succeed(phaseHeader(3, TOTAL_PHASES, `MCP servers ${c.dim}${"·".repeat(17)}${c.reset} ${mcpSummary} ${c.dim}${formatElapsed(phase3Start)}${c.reset}`));
1241
1420
  const coreMcps = getCoreMcps();
1242
1421
  for (let i = 0;i < mcpResult.results.length; i++) {
1243
1422
  const r = mcpResult.results[i];
@@ -1248,9 +1427,9 @@ async function main() {
1248
1427
  } else {
1249
1428
  const spinner3 = createSpinner(phaseHeader(3, TOTAL_PHASES, "Installing MCP servers..."));
1250
1429
  if (skipMcp) {
1251
- spinner3.succeed(phaseHeader(3, TOTAL_PHASES, `MCP servers ${c.dim}${"\xB7".repeat(17)}${c.reset} skipped (--no-mcp)`));
1430
+ spinner3.succeed(phaseHeader(3, TOTAL_PHASES, `MCP servers ${c.dim}${"·".repeat(17)}${c.reset} skipped (--no-mcp)`));
1252
1431
  } else {
1253
- spinner3.fail(phaseHeader(3, TOTAL_PHASES, `MCP servers ${c.dim}${"\xB7".repeat(17)}${c.reset} skipped (CLI not ready)`));
1432
+ spinner3.fail(phaseHeader(3, TOTAL_PHASES, `MCP servers ${c.dim}${"·".repeat(17)}${c.reset} skipped (CLI not ready)`));
1254
1433
  }
1255
1434
  }
1256
1435
  const phase4Start = Date.now();
@@ -1262,7 +1441,7 @@ async function main() {
1262
1441
  const pluginOk = pluginResult.installed + pluginResult.skipped;
1263
1442
  const pluginTotal = pluginOk + pluginResult.failed;
1264
1443
  const pluginSummary = `${pluginOk}/${pluginTotal} installed`;
1265
- spinner4.succeed(phaseHeader(4, TOTAL_PHASES, `Plugins ${c.dim}${"\xB7".repeat(21)}${c.reset} ${pluginSummary} ${c.dim}${formatElapsed(phase4Start)}${c.reset}`));
1444
+ spinner4.succeed(phaseHeader(4, TOTAL_PHASES, `Plugins ${c.dim}${"·".repeat(21)}${c.reset} ${pluginSummary} ${c.dim}${formatElapsed(phase4Start)}${c.reset}`));
1266
1445
  const plugins = getRecommendedPlugins();
1267
1446
  for (let i = 0;i < pluginResult.results.length; i++) {
1268
1447
  const r = pluginResult.results[i];
@@ -1272,7 +1451,7 @@ async function main() {
1272
1451
  }
1273
1452
  } else {
1274
1453
  const spinner4 = createSpinner(phaseHeader(4, TOTAL_PHASES, "Installing plugins..."));
1275
- spinner4.succeed(phaseHeader(4, TOTAL_PHASES, `Plugins ${c.dim}${"\xB7".repeat(21)}${c.reset} ${c.dim}deferred (will auto-prompt)${c.reset}`));
1454
+ spinner4.succeed(phaseHeader(4, TOTAL_PHASES, `Plugins ${c.dim}${"·".repeat(21)}${c.reset} ${c.dim}deferred (will auto-prompt)${c.reset}`));
1276
1455
  }
1277
1456
  const phase5Start = Date.now();
1278
1457
  if (!skipSkills && isSkillInstallReady()) {
@@ -1281,7 +1460,7 @@ async function main() {
1281
1460
  spinner5.update(phaseHeader(5, TOTAL_PHASES, `Installing skills... ${c.dim}${current}/${total} done${c.reset}`));
1282
1461
  });
1283
1462
  const skillSummary = `${skillResult.installed}/${skillResult.installed + skillResult.failed} installed`;
1284
- spinner5.succeed(phaseHeader(5, TOTAL_PHASES, `Community skills ${c.dim}${"\xB7".repeat(11)}${c.reset} ${skillSummary} ${c.dim}${formatElapsed(phase5Start)}${c.reset}`));
1463
+ spinner5.succeed(phaseHeader(5, TOTAL_PHASES, `Community skills ${c.dim}${"·".repeat(11)}${c.reset} ${skillSummary} ${c.dim}${formatElapsed(phase5Start)}${c.reset}`));
1285
1464
  const skills = getRecommendedSkills();
1286
1465
  for (let i = 0;i < skillResult.results.length; i++) {
1287
1466
  const r = skillResult.results[i];
@@ -1292,15 +1471,29 @@ async function main() {
1292
1471
  } else {
1293
1472
  const spinner5 = createSpinner(phaseHeader(5, TOTAL_PHASES, "Installing community skills..."));
1294
1473
  if (skipSkills) {
1295
- spinner5.succeed(phaseHeader(5, TOTAL_PHASES, `Community skills ${c.dim}${"\xB7".repeat(11)}${c.reset} skipped (--no-skills)`));
1474
+ spinner5.succeed(phaseHeader(5, TOTAL_PHASES, `Community skills ${c.dim}${"·".repeat(11)}${c.reset} skipped (--no-skills)`));
1296
1475
  } else {
1297
- spinner5.fail(phaseHeader(5, TOTAL_PHASES, `Community skills ${c.dim}${"\xB7".repeat(11)}${c.reset} skipped (npx not found)`));
1476
+ spinner5.fail(phaseHeader(5, TOTAL_PHASES, `Community skills ${c.dim}${"·".repeat(11)}${c.reset} skipped (npx not found)`));
1477
+ }
1478
+ }
1479
+ if (profile) {
1480
+ const injection = injectShellFunction(profile.name, profile.dir);
1481
+ if (injection.injected && injection.path && injection.reloadHint) {
1482
+ console.log("");
1483
+ console.log(` ${c.green}✓${c.reset} Shell function ${c.bright}claude-${profile.name}${c.reset} added to ${c.dim}${injection.path}${c.reset}`);
1484
+ console.log(` ${c.dim}Reload your shell with:${c.reset} ${c.bright}${injection.reloadHint}${c.reset}`);
1485
+ } else if (injection.skipped && injection.path) {
1486
+ console.log("");
1487
+ console.log(` ${c.dim}Shell function claude-${profile.name} already present in ${injection.path}${c.reset}`);
1488
+ } else if (!injection.path) {
1489
+ console.log("");
1490
+ console.log(` ${c.yellow}Could not detect shell config${c.reset}${c.dim} — set CLAUDE_CONFIG_DIR=${profile.dir} manually before running 'claude'.${c.reset}`);
1298
1491
  }
1299
1492
  }
1300
1493
  const sessionExists = hasExistingSession(targetDir);
1301
1494
  const willResume = !newSession && sessionExists;
1302
1495
  const launchMode = newSession ? "new session" : willResume ? "resuming last session" : "new session";
1303
- console.log(` ${c.green}\u2713${c.reset} ${phaseHeader(6, TOTAL_PHASES, `Launching Claude Code ${c.dim}${"\xB7".repeat(7)}${c.reset} ${launchMode}`)}`);
1496
+ console.log(` ${c.green}✓${c.reset} ${phaseHeader(6, TOTAL_PHASES, `Launching Claude Code ${c.dim}${"·".repeat(7)}${c.reset} ${launchMode}`)}`);
1304
1497
  printOptionalMcps();
1305
1498
  console.log("");
1306
1499
  console.log(` ${c.green}Setup complete${c.reset} in ${formatElapsed(globalStart)}`);
package/package.json CHANGED
@@ -1,42 +1,42 @@
1
- {
2
- "name": "start-vibing",
3
- "version": "4.4.14",
4
- "description": "Setup Claude Code with 9 plugins, 6 community skills, and 8 MCP servers. Parallel install, auto-accept, superpowers + ralph-loop. e2e-audit 0.2.0 refactor (skill-only, no agents): SessionStart hook + slash command make the skill keyword-invokable (\"e2e audit\", \"roda o e2e\", \"integration test\", \"test coverage gaps\"). Source-first discovery via detect-stack, discover-routes (Next app/pages/Remix/SvelteKit/Nuxt/Astro), discover-api-surface (HTTP handlers, tRPC procedures, GraphQL, server actions, middleware auth), inventory-existing-tests (preserve prior corpus + sha256 drift hash), and detect-uncovered (branch-diff vs origin/main finds changes not covered by existing specs). Report-then-ask between mapping and Playwright run; post-run-feedback report before writing findings. SHOT+TRACE+ASSERT+SOURCE evidence quad per non-meta finding; meta rules (coverage-gap-*, uncovered-*, test-drift, stack-detect, post-run-feedback) exempt. verify-audit.sh enforces schema + quad. Generic (no project leakage). super-design 0.7.0 carries over.",
5
- "type": "module",
6
- "bin": {
7
- "start-vibing": "./dist/cli.js"
8
- },
9
- "files": [
10
- "dist",
11
- "template"
12
- ],
13
- "scripts": {
14
- "build": "bun build ./src/cli.ts --outdir ./dist --target node",
15
- "dev": "bun run ./src/cli.ts",
16
- "prepublishOnly": "bun run build"
17
- },
18
- "keywords": [
19
- "claude",
20
- "claude-code",
21
- "ai",
22
- "agents",
23
- "skills",
24
- "hooks",
25
- "automation",
26
- "workflow",
27
- "cli"
28
- ],
29
- "author": "joaov",
30
- "license": "MIT",
31
- "repository": {
32
- "type": "git",
33
- "url": "https://github.com/LimaTechnologies/ai-development"
34
- },
35
- "engines": {
36
- "node": ">=18.0.0"
37
- },
38
- "devDependencies": {
39
- "@types/node": "^20.0.0",
40
- "typescript": "^5.0.0"
41
- }
42
- }
1
+ {
2
+ "name": "start-vibing",
3
+ "version": "4.4.16",
4
+ "description": "Setup Claude Code with 9 plugins, 6 community skills, and 8 MCP servers. Parallel install, auto-accept, superpowers + ralph-loop. e2e-audit 0.2.0 refactor (skill-only, no agents): SessionStart hook + slash command make the skill keyword-invokable (\"e2e audit\", \"roda o e2e\", \"integration test\", \"test coverage gaps\"). Source-first discovery via detect-stack, discover-routes (Next app/pages/Remix/SvelteKit/Nuxt/Astro), discover-api-surface (HTTP handlers, tRPC procedures, GraphQL, server actions, middleware auth), inventory-existing-tests (preserve prior corpus + sha256 drift hash), and detect-uncovered (branch-diff vs origin/main finds changes not covered by existing specs). Report-then-ask between mapping and Playwright run; post-run-feedback report before writing findings. SHOT+TRACE+ASSERT+SOURCE evidence quad per non-meta finding; meta rules (coverage-gap-*, uncovered-*, test-drift, stack-detect, post-run-feedback) exempt. verify-audit.sh enforces schema + quad. Generic (no project leakage). super-design 0.7.0 carries over.",
5
+ "type": "module",
6
+ "bin": {
7
+ "start-vibing": "./dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "template"
12
+ ],
13
+ "scripts": {
14
+ "build": "bun build ./src/cli.ts --outdir ./dist --target node",
15
+ "dev": "bun run ./src/cli.ts",
16
+ "prepublishOnly": "bun run build"
17
+ },
18
+ "keywords": [
19
+ "claude",
20
+ "claude-code",
21
+ "ai",
22
+ "agents",
23
+ "skills",
24
+ "hooks",
25
+ "automation",
26
+ "workflow",
27
+ "cli"
28
+ ],
29
+ "author": "joaov",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/LimaTechnologies/ai-development"
34
+ },
35
+ "engines": {
36
+ "node": ">=18.0.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^20.0.0",
40
+ "typescript": "^5.0.0"
41
+ }
42
+ }