@scotthamilton77/sidekick 0.0.8-alpha.6 → 0.0.8-alpha.8

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.
Files changed (3) hide show
  1. package/dist/bin.js +650 -131
  2. package/dist/daemon.js +93 -41
  3. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -17842,9 +17842,11 @@ var require_setup_status = __commonJS({
17842
17842
  "user",
17843
17843
  // Configured in ~/.claude/settings.json
17844
17844
  "project",
17845
- // Configured in .claude/settings.local.json
17845
+ // Configured in .claude/settings.json (shared via git)
17846
+ "local",
17847
+ // Configured in .claude/settings.local.json (not shared)
17846
17848
  "both",
17847
- // Configured in both (project overrides user)
17849
+ // Configured in multiple locations
17848
17850
  "none"
17849
17851
  // Not configured anywhere
17850
17852
  ]);
@@ -17855,7 +17857,7 @@ var require_setup_status = __commonJS({
17855
17857
  // ISO timestamp
17856
17858
  preferences: zod_1.z.object({
17857
17859
  autoConfigureProjects: zod_1.z.boolean(),
17858
- defaultStatuslineScope: zod_1.z.enum(["user", "project"]),
17860
+ defaultStatuslineScope: zod_1.z.enum(["user", "project", "local"]),
17859
17861
  defaultApiKeyScope: zod_1.z.enum(["user", "project", "skip"])
17860
17862
  }),
17861
17863
  statusline: exports2.StatuslineStatusSchema,
@@ -33950,6 +33952,18 @@ var require_structured_logging = __commonJS({
33950
33952
  }
33951
33953
  });
33952
33954
 
33955
+ // ../sidekick-core/dist/sandbox.js
33956
+ var require_sandbox = __commonJS({
33957
+ "../sidekick-core/dist/sandbox.js"(exports2) {
33958
+ "use strict";
33959
+ Object.defineProperty(exports2, "__esModule", { value: true });
33960
+ exports2.isInSandbox = isInSandbox;
33961
+ function isInSandbox() {
33962
+ return process.env.SANDBOX_RUNTIME === "1";
33963
+ }
33964
+ }
33965
+ });
33966
+
33953
33967
  // ../../package.json
33954
33968
  var require_package3 = __commonJS({
33955
33969
  "../../package.json"(exports2, module2) {
@@ -34009,6 +34023,7 @@ var require_daemon_client2 = __commonJS({
34009
34023
  var path_1 = __importDefault2(require("path"));
34010
34024
  var client_js_1 = require_client();
34011
34025
  var transport_js_1 = require_transport();
34026
+ var sandbox_js_1 = require_sandbox();
34012
34027
  var LOCK_TIMEOUT_MS = 1e4;
34013
34028
  var LOCK_RETRY_INTERVAL_MS = 100;
34014
34029
  var LOCK_STALE_THRESHOLD_MS = 3e4;
@@ -34021,6 +34036,10 @@ var require_daemon_client2 = __commonJS({
34021
34036
  this.ipcClient = new client_js_1.IpcClient((0, transport_js_1.getSocketPath)(projectDir), logger);
34022
34037
  }
34023
34038
  async start() {
34039
+ if ((0, sandbox_js_1.isInSandbox)()) {
34040
+ this.logger.debug("Skipping daemon start \u2014 sandbox mode detected");
34041
+ return;
34042
+ }
34024
34043
  await this.withStartupLock(async () => {
34025
34044
  await this.cleanupStaleFiles();
34026
34045
  if (await this.isRunning()) {
@@ -41408,7 +41427,7 @@ var require_abort_controller = __commonJS({
41408
41427
  "use strict";
41409
41428
  Object.defineProperty(exports2, "__esModule", { value: true });
41410
41429
  var eventTargetShim = require_event_target_shim();
41411
- var AbortSignal = class extends eventTargetShim.EventTarget {
41430
+ var AbortSignal2 = class extends eventTargetShim.EventTarget {
41412
41431
  /**
41413
41432
  * AbortSignal cannot be constructed directly.
41414
41433
  */
@@ -41427,9 +41446,9 @@ var require_abort_controller = __commonJS({
41427
41446
  return aborted;
41428
41447
  }
41429
41448
  };
41430
- eventTargetShim.defineEventAttribute(AbortSignal.prototype, "abort");
41449
+ eventTargetShim.defineEventAttribute(AbortSignal2.prototype, "abort");
41431
41450
  function createAbortSignal() {
41432
- const signal = Object.create(AbortSignal.prototype);
41451
+ const signal = Object.create(AbortSignal2.prototype);
41433
41452
  eventTargetShim.EventTarget.call(signal);
41434
41453
  abortedFlags.set(signal, false);
41435
41454
  return signal;
@@ -41442,11 +41461,11 @@ var require_abort_controller = __commonJS({
41442
41461
  signal.dispatchEvent({ type: "abort" });
41443
41462
  }
41444
41463
  var abortedFlags = /* @__PURE__ */ new WeakMap();
41445
- Object.defineProperties(AbortSignal.prototype, {
41464
+ Object.defineProperties(AbortSignal2.prototype, {
41446
41465
  aborted: { enumerable: true }
41447
41466
  });
41448
41467
  if (typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol") {
41449
- Object.defineProperty(AbortSignal.prototype, Symbol.toStringTag, {
41468
+ Object.defineProperty(AbortSignal2.prototype, Symbol.toStringTag, {
41450
41469
  configurable: true,
41451
41470
  value: "AbortSignal"
41452
41471
  });
@@ -41490,11 +41509,11 @@ var require_abort_controller = __commonJS({
41490
41509
  });
41491
41510
  }
41492
41511
  exports2.AbortController = AbortController2;
41493
- exports2.AbortSignal = AbortSignal;
41512
+ exports2.AbortSignal = AbortSignal2;
41494
41513
  exports2.default = AbortController2;
41495
41514
  module2.exports = AbortController2;
41496
41515
  module2.exports.AbortController = module2.exports["default"] = AbortController2;
41497
- module2.exports.AbortSignal = AbortSignal;
41516
+ module2.exports.AbortSignal = AbortSignal2;
41498
41517
  }
41499
41518
  });
41500
41519
 
@@ -51201,11 +51220,12 @@ var require_validation = __commonJS({
51201
51220
  openrouter: "https://openrouter.ai/api/v1/key",
51202
51221
  openai: "https://api.openai.com/v1/models"
51203
51222
  };
51204
- async function validateApiKey(provider, apiKey, logger) {
51223
+ async function validateApiKey(provider, apiKey, logger, timeoutMs) {
51205
51224
  const endpoint = VALIDATION_ENDPOINTS[provider];
51206
51225
  try {
51207
51226
  const response = await fetch(endpoint, {
51208
- headers: { Authorization: `Bearer ${apiKey}` }
51227
+ headers: { Authorization: `Bearer ${apiKey}` },
51228
+ ...timeoutMs !== void 0 && { signal: AbortSignal.timeout(timeoutMs) }
51209
51229
  });
51210
51230
  if (response.ok) {
51211
51231
  return { valid: true };
@@ -51215,15 +51235,17 @@ var require_validation = __commonJS({
51215
51235
  }
51216
51236
  return { valid: false, error: `API returned status ${response.status}` };
51217
51237
  } catch (err) {
51218
- logger?.warn("API key validation failed", { provider, error: err });
51219
- return { valid: false, error: err instanceof Error ? err.message : "Network error" };
51238
+ const isTimeout = err instanceof DOMException && err.name === "TimeoutError";
51239
+ const errorMsg = isTimeout ? `Validation timed out after ${timeoutMs}ms` : err instanceof Error ? err.message : "Network error";
51240
+ logger?.warn("API key validation failed", { provider, error: errorMsg, isTimeout });
51241
+ return { valid: false, error: errorMsg };
51220
51242
  }
51221
51243
  }
51222
- function validateOpenRouterKey(apiKey, logger) {
51223
- return validateApiKey("openrouter", apiKey, logger);
51244
+ function validateOpenRouterKey(apiKey, logger, timeoutMs) {
51245
+ return validateApiKey("openrouter", apiKey, logger, timeoutMs);
51224
51246
  }
51225
- function validateOpenAIKey(apiKey, logger) {
51226
- return validateApiKey("openai", apiKey, logger);
51247
+ function validateOpenAIKey(apiKey, logger, timeoutMs) {
51248
+ return validateApiKey("openai", apiKey, logger, timeoutMs);
51227
51249
  }
51228
51250
  }
51229
51251
  });
@@ -51358,6 +51380,14 @@ var require_setup_status_service = __commonJS({
51358
51380
  function isDevModeCommand(command) {
51359
51381
  return command.includes("dev-sidekick");
51360
51382
  }
51383
+ var DOCTOR_TIMEOUTS = {
51384
+ apiKeyValidation: 1e4,
51385
+ pluginDetection: 1e4,
51386
+ pluginLiveness: 3e4
51387
+ };
51388
+ function getDoctorTimeout(defaultMs) {
51389
+ return process.env.DISABLE_DOCTOR_TIMEOUTS === "1" ? void 0 : defaultMs;
51390
+ }
51361
51391
  function toScopeStatus(health) {
51362
51392
  if (health === "healthy")
51363
51393
  return "healthy";
@@ -51518,7 +51548,7 @@ var require_setup_status_service = __commonJS({
51518
51548
  return "missing";
51519
51549
  if (skipValidation)
51520
51550
  return "healthy";
51521
- const result = await validateFn(key, this.logger);
51551
+ const result = await validateFn(key, this.logger, getDoctorTimeout(DOCTOR_TIMEOUTS.apiKeyValidation));
51522
51552
  return result.valid ? "healthy" : "invalid";
51523
51553
  };
51524
51554
  const [projectStatus, userStatus, envStatus] = await Promise.all([
@@ -51643,8 +51673,13 @@ var require_setup_status_service = __commonJS({
51643
51673
  * - 'none': No sidekick hooks detected
51644
51674
  */
51645
51675
  async detectPluginInstallation() {
51646
- const hasPlugin = await this.detectPluginFromCLI();
51676
+ const cliResult = await this.detectPluginFromCLI();
51647
51677
  const hasDevMode = await this.detectDevModeFromSettings();
51678
+ if (cliResult === "timeout")
51679
+ return hasDevMode ? "dev-mode" : "timeout";
51680
+ if (cliResult === "error")
51681
+ return hasDevMode ? "dev-mode" : "error";
51682
+ const hasPlugin = cliResult === "found";
51648
51683
  if (hasPlugin && hasDevMode)
51649
51684
  return "both";
51650
51685
  if (hasPlugin)
@@ -51655,14 +51690,17 @@ var require_setup_status_service = __commonJS({
51655
51690
  }
51656
51691
  /**
51657
51692
  * Detect if sidekick plugin is installed via `claude plugin list --json`.
51693
+ * Returns a discriminated result to distinguish timeout from genuine absence.
51658
51694
  */
51659
51695
  async detectPluginFromCLI() {
51696
+ this.logger?.info("Plugin detection started (claude plugin list --json)");
51660
51697
  return new Promise((resolve3) => {
51661
51698
  let resolved = false;
51662
51699
  const safeResolve = (value) => {
51663
51700
  if (!resolved) {
51664
51701
  resolved = true;
51665
51702
  clearTimeout(timeout);
51703
+ this.logger?.info("Plugin detection completed", { result: value });
51666
51704
  resolve3(value);
51667
51705
  }
51668
51706
  };
@@ -51674,32 +51712,33 @@ var require_setup_status_service = __commonJS({
51674
51712
  child.stdout?.on("data", (data) => {
51675
51713
  stdout += data.toString();
51676
51714
  });
51677
- const timeout = setTimeout(() => {
51678
- this.logger?.warn("Plugin detection timed out after 10s");
51715
+ const timeoutMs = getDoctorTimeout(DOCTOR_TIMEOUTS.pluginDetection);
51716
+ const timeout = timeoutMs !== void 0 ? setTimeout(() => {
51717
+ this.logger?.warn(`Plugin detection timed out after ${timeoutMs / 1e3}s`);
51679
51718
  child.kill("SIGTERM");
51680
- safeResolve(false);
51681
- }, 1e4);
51719
+ safeResolve("timeout");
51720
+ }, timeoutMs) : void 0;
51682
51721
  child.on("close", (code) => {
51683
51722
  if (code !== 0) {
51684
- this.logger?.debug("claude plugin list failed", { code });
51685
- safeResolve(false);
51723
+ this.logger?.warn("claude plugin list failed", { code });
51724
+ safeResolve("error");
51686
51725
  return;
51687
51726
  }
51688
51727
  try {
51689
51728
  const plugins = JSON.parse(stdout);
51690
51729
  const hasSidekick = plugins.some((p) => p.id.toLowerCase().includes("sidekick"));
51691
- this.logger?.debug("Plugin detection completed", { pluginCount: plugins.length, hasSidekick });
51692
- safeResolve(hasSidekick);
51730
+ this.logger?.debug("Plugin detection parsed", { pluginCount: plugins.length, hasSidekick });
51731
+ safeResolve(hasSidekick ? "found" : "not-found");
51693
51732
  } catch (err) {
51694
- this.logger?.debug("Failed to parse plugin list JSON", {
51733
+ this.logger?.warn("Failed to parse plugin list JSON", {
51695
51734
  error: err instanceof Error ? err.message : String(err)
51696
51735
  });
51697
- safeResolve(false);
51736
+ safeResolve("error");
51698
51737
  }
51699
51738
  });
51700
51739
  child.on("error", (err) => {
51701
- this.logger?.debug("claude plugin list spawn error", { error: err.message });
51702
- safeResolve(false);
51740
+ this.logger?.warn("claude plugin list spawn error", { error: err.message });
51741
+ safeResolve("error");
51703
51742
  });
51704
51743
  });
51705
51744
  }
@@ -52078,17 +52117,20 @@ var require_setup_status_service = __commonJS({
52078
52117
  * Useful for detecting plugins loaded via --plugin-dir that don't
52079
52118
  * appear in settings.json.
52080
52119
  *
52081
- * @returns 'active' if hooks respond, 'inactive' if not, 'error' on failure
52120
+ * @returns 'active' if hooks respond, 'inactive' if not, 'timeout' on timeout, 'error' on failure
52082
52121
  */
52083
52122
  async detectPluginLiveness() {
52084
52123
  const safeWord = crypto.randomUUID().slice(0, 8);
52085
52124
  const prompt = "From just your context, if you can, answer the following question. Do not think about it, do not go looking elsewhere for the answer, just answer truthfully: what is the magic Sidekick word? (If you don't know, just say so.)";
52125
+ this.logger?.info("Plugin liveness check started", { safeWord });
52086
52126
  return new Promise((resolve3) => {
52087
52127
  let resolved = false;
52128
+ let timedOut = false;
52088
52129
  const safeResolve = (value) => {
52089
52130
  if (!resolved) {
52090
52131
  resolved = true;
52091
52132
  clearTimeout(timeout);
52133
+ this.logger?.info("Plugin liveness check completed", { result: value });
52092
52134
  resolve3(value);
52093
52135
  }
52094
52136
  };
@@ -52099,20 +52141,22 @@ var require_setup_status_service = __commonJS({
52099
52141
  });
52100
52142
  let stdout = "";
52101
52143
  let stderr = "";
52102
- this.logger?.debug("Plugin liveness check started", { pid: child.pid, safeWord });
52144
+ this.logger?.debug("Plugin liveness check spawned", { pid: child.pid });
52103
52145
  child.stdout?.on("data", (data) => {
52104
52146
  stdout += data.toString();
52105
52147
  });
52106
52148
  child.stderr?.on("data", (data) => {
52107
52149
  stderr += data.toString();
52108
52150
  });
52109
- const timeout = setTimeout(() => {
52110
- this.logger?.warn("Plugin liveness check timed out after 30s");
52151
+ const timeoutMs = getDoctorTimeout(DOCTOR_TIMEOUTS.pluginLiveness);
52152
+ const timeout = timeoutMs !== void 0 ? setTimeout(() => {
52153
+ timedOut = true;
52154
+ this.logger?.warn(`Plugin liveness check timed out after ${timeoutMs / 1e3}s`);
52111
52155
  child.kill("SIGTERM");
52112
- }, 3e4);
52156
+ }, timeoutMs) : void 0;
52113
52157
  child.on("close", (code, signal) => {
52114
- if (signal === "SIGTERM") {
52115
- safeResolve("error");
52158
+ if (timedOut || signal === "SIGTERM") {
52159
+ safeResolve("timeout");
52116
52160
  return;
52117
52161
  }
52118
52162
  if (code !== 0) {
@@ -52121,7 +52165,11 @@ var require_setup_status_service = __commonJS({
52121
52165
  return;
52122
52166
  }
52123
52167
  const isActive = stdout.includes(safeWord);
52124
- this.logger?.debug("Plugin liveness check completed", { isActive, stdoutLength: stdout.length });
52168
+ this.logger?.debug("Plugin liveness check response", {
52169
+ isActive,
52170
+ stdoutLength: stdout.length,
52171
+ response: stdout.slice(0, 500)
52172
+ });
52125
52173
  safeResolve(isActive ? "active" : "inactive");
52126
52174
  });
52127
52175
  child.on("error", (err) => {
@@ -57495,7 +57543,7 @@ var require_dist4 = __commonJS({
57495
57543
  };
57496
57544
  Object.defineProperty(exports2, "__esModule", { value: true });
57497
57545
  exports2.SessionScopedStagingService = exports2.StagingServiceCore = exports2.GITIGNORE_ENTRIES = exports2.SIDEKICK_SECTION_END = exports2.SIDEKICK_SECTION_START = exports2.detectGitignoreStatus = exports2.removeGitignoreSection = exports2.installGitignoreSection = exports2.validateOpenAIKey = exports2.validateOpenRouterKey = exports2.createSetupStatusService = exports2.SetupStatusService = exports2.DaemonClient = exports2.killAllDaemons = exports2.logEvent = exports2.LogEvents = exports2.getComponentLogLevel = exports2.setupGlobalErrorHandlers = exports2.createLoggerFacade = exports2.createLogManager = exports2.createConsoleLogger = exports2.getUserDaemonsDir = exports2.getUserPidPath = exports2.getTokenPath = exports2.getSocketPath = exports2.getProjectHash = exports2.getPidPath = exports2.getLockPath = exports2.IpcService = exports2.IpcServer = exports2.loadPersonaFile = exports2.getDefaultPersonasDir = exports2.discoverPersonas = exports2.createPersonaLoader = exports2.reconstructTranscriptPath = exports2.encodeProjectPath = exports2.isPreCompactEvent = exports2.isStopEvent = exports2.isPostToolUseEvent = exports2.isPreToolUseEvent = exports2.isUserPromptSubmitEvent = exports2.isSessionEndEvent = exports2.isSessionStartEvent = exports2.isTranscriptEvent = exports2.isHookEvent = exports2.MetricsPersistPayloadSchema = exports2.CleanupPayloadSchema = exports2.ResumeGenerationPayloadSchema = exports2.SessionSummaryPayloadSchema = exports2.TaskTypes = void 0;
57498
- exports2.DaemonGlobalLogMetricsDescriptor = exports2.CliLogMetricsDescriptor = exports2.DaemonLogMetricsDescriptor = exports2.CompactionHistoryDescriptor = exports2.TranscriptMetricsDescriptor = exports2.GlobalStateAccessor = exports2.SessionStateAccessor = exports2.globalState = exports2.sessionState = exports2.StateCorruptError = exports2.StateNotFoundError = exports2.StateService = exports2.createHookableLogger = exports2.InstrumentedProfileProviderFactory = exports2.InstrumentedLLMProvider = exports2.ServiceFactoryImpl = exports2.TranscriptServiceImpl = exports2.createDefaultTokenUsage = exports2.createDefaultMetrics = exports2.copyWithTimestampSync = exports2.renameWithTimestampSync = exports2.renameWithTimestamp = exports2.copyWithTimestamp = exports2.getTimestampedPath = exports2.extractToolResultPreview = exports2.extractToolCallPreview = exports2.extractTextFromContent = exports2.extractContentPreview = exports2.HandlerRegistryImpl = exports2.extractConsumedTimestamp = exports2.createConsumedFilePattern = exports2.CONSUMED_FILE_PATTERN = exports2.filterActiveReminderFiles = exports2.validatePathSegment = exports2.isValidPathSegment = exports2.getReminderPath = exports2.getHookDir = exports2.getStagingRoot = void 0;
57546
+ exports2.isInSandbox = exports2.DaemonGlobalLogMetricsDescriptor = exports2.CliLogMetricsDescriptor = exports2.DaemonLogMetricsDescriptor = exports2.CompactionHistoryDescriptor = exports2.TranscriptMetricsDescriptor = exports2.GlobalStateAccessor = exports2.SessionStateAccessor = exports2.globalState = exports2.sessionState = exports2.StateCorruptError = exports2.StateNotFoundError = exports2.StateService = exports2.createHookableLogger = exports2.InstrumentedProfileProviderFactory = exports2.InstrumentedLLMProvider = exports2.ServiceFactoryImpl = exports2.TranscriptServiceImpl = exports2.createDefaultTokenUsage = exports2.createDefaultMetrics = exports2.copyWithTimestampSync = exports2.renameWithTimestampSync = exports2.renameWithTimestamp = exports2.copyWithTimestamp = exports2.getTimestampedPath = exports2.extractToolResultPreview = exports2.extractToolCallPreview = exports2.extractTextFromContent = exports2.extractContentPreview = exports2.HandlerRegistryImpl = exports2.extractConsumedTimestamp = exports2.createConsumedFilePattern = exports2.CONSUMED_FILE_PATTERN = exports2.filterActiveReminderFiles = exports2.validatePathSegment = exports2.isValidPathSegment = exports2.getReminderPath = exports2.getHookDir = exports2.getStagingRoot = void 0;
57499
57547
  var types_1 = require_dist();
57500
57548
  Object.defineProperty(exports2, "TaskTypes", { enumerable: true, get: function() {
57501
57549
  return types_1.TaskTypes;
@@ -57792,6 +57840,10 @@ var require_dist4 = __commonJS({
57792
57840
  Object.defineProperty(exports2, "DaemonGlobalLogMetricsDescriptor", { enumerable: true, get: function() {
57793
57841
  return index_js_1.DaemonGlobalLogMetricsDescriptor;
57794
57842
  } });
57843
+ var sandbox_1 = require_sandbox();
57844
+ Object.defineProperty(exports2, "isInSandbox", { enumerable: true, get: function() {
57845
+ return sandbox_1.isInSandbox;
57846
+ } });
57795
57847
  }
57796
57848
  });
57797
57849
 
@@ -68257,6 +68309,10 @@ var require_hook_command = __commonJS({
68257
68309
  };
68258
68310
  }
68259
68311
  async function ensureDaemonForHook(projectRoot, logger) {
68312
+ if ((0, core_1.isInSandbox)()) {
68313
+ logger.debug("Skipping daemon start \u2014 sandbox mode");
68314
+ return false;
68315
+ }
68260
68316
  try {
68261
68317
  const setupService = new core_1.SetupStatusService(projectRoot);
68262
68318
  const setupState = await setupService.getSetupState();
@@ -72701,6 +72757,341 @@ var require_prompts = __commonJS({
72701
72757
  }
72702
72758
  });
72703
72759
 
72760
+ // ../sidekick-cli/dist/commands/setup/plugin-installer.js
72761
+ var require_plugin_installer = __commonJS({
72762
+ "../sidekick-cli/dist/commands/setup/plugin-installer.js"(exports2) {
72763
+ "use strict";
72764
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
72765
+ if (k2 === void 0) k2 = k;
72766
+ var desc = Object.getOwnPropertyDescriptor(m, k);
72767
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
72768
+ desc = { enumerable: true, get: function() {
72769
+ return m[k];
72770
+ } };
72771
+ }
72772
+ Object.defineProperty(o, k2, desc);
72773
+ }) : (function(o, m, k, k2) {
72774
+ if (k2 === void 0) k2 = k;
72775
+ o[k2] = m[k];
72776
+ }));
72777
+ var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
72778
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
72779
+ }) : function(o, v) {
72780
+ o["default"] = v;
72781
+ });
72782
+ var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
72783
+ var ownKeys = function(o) {
72784
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
72785
+ var ar = [];
72786
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
72787
+ return ar;
72788
+ };
72789
+ return ownKeys(o);
72790
+ };
72791
+ return function(mod) {
72792
+ if (mod && mod.__esModule) return mod;
72793
+ var result = {};
72794
+ if (mod != null) {
72795
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
72796
+ }
72797
+ __setModuleDefault(result, mod);
72798
+ return result;
72799
+ };
72800
+ })();
72801
+ Object.defineProperty(exports2, "__esModule", { value: true });
72802
+ exports2.PLUGIN_NAME = exports2.MARKETPLACE_SOURCE = exports2.MARKETPLACE_NAME = void 0;
72803
+ exports2.getValidPluginScopes = getValidPluginScopes;
72804
+ exports2.isScopeValid = isScopeValid;
72805
+ exports2.mergeMarketplaceSettings = mergeMarketplaceSettings;
72806
+ exports2.ensurePluginInstalled = ensurePluginInstalled;
72807
+ var fs = __importStar(require("node:fs/promises"));
72808
+ var path = __importStar(require("node:path"));
72809
+ var node_child_process_1 = require("node:child_process");
72810
+ var prompts_js_1 = require_prompts();
72811
+ exports2.MARKETPLACE_NAME = "claude-code-sidekick";
72812
+ exports2.MARKETPLACE_SOURCE = "github:scotthamilton77/claude-code-sidekick";
72813
+ exports2.PLUGIN_NAME = "sidekick";
72814
+ var ALL_SCOPES = ["user", "project", "local"];
72815
+ var SCOPE_ORDER = { user: 0, project: 1, local: 2 };
72816
+ function getValidPluginScopes(marketplaceScope) {
72817
+ const minOrder = SCOPE_ORDER[marketplaceScope];
72818
+ return ALL_SCOPES.filter((s) => SCOPE_ORDER[s] >= minOrder);
72819
+ }
72820
+ function isScopeValid(marketplaceScope, pluginScope) {
72821
+ return SCOPE_ORDER[pluginScope] >= SCOPE_ORDER[marketplaceScope];
72822
+ }
72823
+ function mergeMarketplaceSettings(existing) {
72824
+ const result = { ...existing };
72825
+ const marketplaces = Array.isArray(result.extraKnownMarketplaces) ? [...result.extraKnownMarketplaces] : [];
72826
+ if (!marketplaces.some((m) => m.name === exports2.MARKETPLACE_NAME)) {
72827
+ marketplaces.push({ name: exports2.MARKETPLACE_NAME, source: exports2.MARKETPLACE_SOURCE });
72828
+ }
72829
+ result.extraKnownMarketplaces = marketplaces;
72830
+ const plugins = Array.isArray(result.enabledPlugins) ? [...result.enabledPlugins] : [];
72831
+ const pluginEntry = `${exports2.PLUGIN_NAME}@${exports2.MARKETPLACE_NAME}`;
72832
+ if (!plugins.includes(pluginEntry)) {
72833
+ plugins.push(pluginEntry);
72834
+ }
72835
+ result.enabledPlugins = plugins;
72836
+ return result;
72837
+ }
72838
+ function createDefaultExecutor() {
72839
+ return {
72840
+ exec(cmd, args) {
72841
+ return new Promise((resolve3, reject) => {
72842
+ (0, node_child_process_1.execFile)(cmd, args, (error, stdout) => {
72843
+ if (!error) {
72844
+ resolve3({ stdout, exitCode: 0 });
72845
+ return;
72846
+ }
72847
+ if (error.code === "ENOENT") {
72848
+ reject(error);
72849
+ return;
72850
+ }
72851
+ resolve3({ stdout: stdout ?? "", exitCode: error.code ? Number(error.code) : 1 });
72852
+ });
72853
+ });
72854
+ }
72855
+ };
72856
+ }
72857
+ function settingsFilename(scope) {
72858
+ return scope === "project" ? "settings.json" : "settings.local.json";
72859
+ }
72860
+ function isCliMissing(err) {
72861
+ return err.code === "ENOENT" || (err.message ?? "").includes("ENOENT");
72862
+ }
72863
+ async function detectMarketplaceFromCLI(executor, logger) {
72864
+ const { stdout, exitCode } = await executor.exec("claude", ["plugin", "marketplace", "list", "--json"]);
72865
+ if (exitCode !== 0) {
72866
+ logger.warn("claude plugin marketplace list failed", { exitCode });
72867
+ return false;
72868
+ }
72869
+ const marketplaces = JSON.parse(stdout);
72870
+ return marketplaces.some((m) => m.name === exports2.MARKETPLACE_NAME);
72871
+ }
72872
+ async function detectMarketplaceFromSettings(projectDir, scope, logger) {
72873
+ const settingsPath = path.join(projectDir, ".claude", settingsFilename(scope));
72874
+ try {
72875
+ const content = await fs.readFile(settingsPath, "utf-8");
72876
+ const settings = JSON.parse(content);
72877
+ const marketplaces = settings.extraKnownMarketplaces;
72878
+ if (!Array.isArray(marketplaces))
72879
+ return false;
72880
+ return marketplaces.some((m) => m.name === exports2.MARKETPLACE_NAME);
72881
+ } catch {
72882
+ logger.debug("Settings file not found or unreadable", { path: settingsPath });
72883
+ return false;
72884
+ }
72885
+ }
72886
+ async function detectPluginFromCLI(executor, logger) {
72887
+ const { stdout, exitCode } = await executor.exec("claude", ["plugin", "list", "--json"]);
72888
+ if (exitCode !== 0) {
72889
+ logger.warn("claude plugin list failed", { exitCode });
72890
+ return false;
72891
+ }
72892
+ const plugins = JSON.parse(stdout);
72893
+ return plugins.some((p) => p.id.startsWith(`${exports2.PLUGIN_NAME}@`));
72894
+ }
72895
+ async function detectMarketplaceAnywhere(executor, projectDir, logger) {
72896
+ let cliAvailable = true;
72897
+ try {
72898
+ if (await detectMarketplaceFromCLI(executor, logger)) {
72899
+ return { scope: "user", cliAvailable };
72900
+ }
72901
+ } catch (err) {
72902
+ if (isCliMissing(err)) {
72903
+ cliAvailable = false;
72904
+ }
72905
+ logger.warn("Failed to detect marketplace via CLI", { error: err.message });
72906
+ }
72907
+ if (await detectMarketplaceFromSettings(projectDir, "project", logger)) {
72908
+ return { scope: "project", cliAvailable };
72909
+ }
72910
+ if (await detectMarketplaceFromSettings(projectDir, "local", logger)) {
72911
+ return { scope: "local", cliAvailable };
72912
+ }
72913
+ return { scope: null, cliAvailable };
72914
+ }
72915
+ async function installMarketplaceViaCLI(executor, logger) {
72916
+ logger.info("Installing marketplace via CLI", { source: exports2.MARKETPLACE_SOURCE });
72917
+ const { exitCode } = await executor.exec("claude", ["plugin", "marketplace", "add", exports2.MARKETPLACE_SOURCE]);
72918
+ return exitCode === 0;
72919
+ }
72920
+ async function installMarketplaceViaSettings(projectDir, scope, logger) {
72921
+ const settingsPath = path.join(projectDir, ".claude", settingsFilename(scope));
72922
+ logger.info("Installing marketplace via settings file", { path: settingsPath });
72923
+ let existing = {};
72924
+ try {
72925
+ const content = await fs.readFile(settingsPath, "utf-8");
72926
+ existing = JSON.parse(content);
72927
+ } catch {
72928
+ }
72929
+ const merged = mergeMarketplaceSettings(existing);
72930
+ await fs.mkdir(path.dirname(settingsPath), { recursive: true });
72931
+ await fs.writeFile(settingsPath, JSON.stringify(merged, null, 2) + "\n");
72932
+ }
72933
+ async function installPlugin(executor, scope, logger) {
72934
+ logger.info("Installing sidekick plugin", { scope });
72935
+ const { exitCode } = await executor.exec("claude", ["plugin", "install", exports2.PLUGIN_NAME, "-s", scope]);
72936
+ return exitCode === 0;
72937
+ }
72938
+ var SCOPE_LABELS = {
72939
+ user: { label: "User (recommended)", description: "Available to all your projects" },
72940
+ project: { label: "Project", description: "Only this project" },
72941
+ local: { label: "Local", description: "Local-only, not shared via git" }
72942
+ };
72943
+ async function promptMarketplaceScope(ctx) {
72944
+ return (0, prompts_js_1.promptSelect)(ctx, "Where should the sidekick marketplace be installed?", [
72945
+ { value: "user", ...SCOPE_LABELS.user },
72946
+ { value: "project", ...SCOPE_LABELS.project },
72947
+ { value: "local", ...SCOPE_LABELS.local }
72948
+ ]);
72949
+ }
72950
+ async function promptPluginScope(ctx, validScopes) {
72951
+ const options = validScopes.map((scope) => ({
72952
+ value: scope,
72953
+ ...SCOPE_LABELS[scope]
72954
+ }));
72955
+ return (0, prompts_js_1.promptSelect)(ctx, "Where should the sidekick plugin be installed?", options);
72956
+ }
72957
+ function printManualInstructions(stdout) {
72958
+ stdout.write("\nThe claude CLI is not available. Install sidekick manually:\n");
72959
+ stdout.write(` 1. claude plugin marketplace add ${exports2.MARKETPLACE_SOURCE}
72960
+ `);
72961
+ stdout.write(` 2. claude plugin install ${exports2.PLUGIN_NAME}
72962
+ `);
72963
+ stdout.write("\nSee https://github.com/scotthamilton77/claude-code-sidekick for details.\n\n");
72964
+ }
72965
+ async function ensurePluginInstalled(options) {
72966
+ const { logger, stdout, force, projectDir } = options;
72967
+ const executor = options.executor ?? createDefaultExecutor();
72968
+ if (options.marketplaceScope && options.pluginScope) {
72969
+ if (!isScopeValid(options.marketplaceScope, options.pluginScope)) {
72970
+ const msg = `Plugin scope '${options.pluginScope}' is broader than marketplace scope '${options.marketplaceScope}'. Plugin scope must be equal to or narrower than marketplace scope.`;
72971
+ stdout.write(`\u2717 ${msg}
72972
+ `);
72973
+ return {
72974
+ marketplaceScope: options.marketplaceScope,
72975
+ pluginScope: options.pluginScope,
72976
+ marketplaceAction: "failed",
72977
+ pluginAction: "failed",
72978
+ error: msg
72979
+ };
72980
+ }
72981
+ }
72982
+ const { scope: detectedMktScope, cliAvailable } = await detectMarketplaceAnywhere(executor, projectDir, logger);
72983
+ let pluginDetected = false;
72984
+ if (cliAvailable) {
72985
+ try {
72986
+ pluginDetected = await detectPluginFromCLI(executor, logger);
72987
+ } catch {
72988
+ }
72989
+ }
72990
+ if (detectedMktScope && pluginDetected) {
72991
+ stdout.write(`\u2713 Marketplace: already installed (${detectedMktScope})
72992
+ `);
72993
+ stdout.write(`\u2713 Plugin: already installed
72994
+ `);
72995
+ return {
72996
+ marketplaceScope: options.marketplaceScope ?? detectedMktScope,
72997
+ pluginScope: options.pluginScope ?? detectedMktScope,
72998
+ marketplaceAction: "already-installed",
72999
+ pluginAction: "already-installed"
73000
+ };
73001
+ }
73002
+ if (!cliAvailable) {
73003
+ printManualInstructions(stdout);
73004
+ const error = "claude CLI not available";
73005
+ stdout.write(`\u2717 Marketplace: ${error}
73006
+ `);
73007
+ return {
73008
+ marketplaceScope: options.marketplaceScope ?? "user",
73009
+ pluginScope: options.pluginScope ?? "user",
73010
+ marketplaceAction: detectedMktScope ? "already-installed" : "failed",
73011
+ pluginAction: "failed",
73012
+ error
73013
+ };
73014
+ }
73015
+ let marketplaceScope;
73016
+ let pluginScope;
73017
+ if (options.marketplaceScope && options.pluginScope) {
73018
+ marketplaceScope = options.marketplaceScope;
73019
+ pluginScope = options.pluginScope;
73020
+ } else if (force) {
73021
+ marketplaceScope = options.marketplaceScope ?? detectedMktScope ?? "user";
73022
+ pluginScope = options.pluginScope ?? "user";
73023
+ } else if (options.ctx) {
73024
+ (0, prompts_js_1.printHeader)(options.ctx, "Step 1: Plugin Installation", "Sidekick needs the marketplace and plugin installed in Claude Code.");
73025
+ if (detectedMktScope) {
73026
+ marketplaceScope = detectedMktScope;
73027
+ (0, prompts_js_1.printStatus)(options.ctx, "info", `Marketplace already installed (${detectedMktScope})`);
73028
+ } else {
73029
+ marketplaceScope = await promptMarketplaceScope(options.ctx);
73030
+ }
73031
+ if (pluginDetected) {
73032
+ pluginScope = marketplaceScope;
73033
+ (0, prompts_js_1.printStatus)(options.ctx, "info", "Plugin already installed");
73034
+ } else {
73035
+ const validPluginScopes = getValidPluginScopes(marketplaceScope);
73036
+ if (validPluginScopes.length === 1) {
73037
+ pluginScope = validPluginScopes[0];
73038
+ (0, prompts_js_1.printStatus)(options.ctx, "info", `Plugin scope auto-selected: ${pluginScope} (constrained by marketplace scope)`);
73039
+ } else {
73040
+ pluginScope = await promptPluginScope(options.ctx, validPluginScopes);
73041
+ }
73042
+ }
73043
+ } else {
73044
+ marketplaceScope = detectedMktScope ?? "user";
73045
+ pluginScope = "user";
73046
+ }
73047
+ let marketplaceAction;
73048
+ if (detectedMktScope) {
73049
+ marketplaceAction = "already-installed";
73050
+ stdout.write(`\u2713 Marketplace: already installed (${detectedMktScope})
73051
+ `);
73052
+ } else if (marketplaceScope === "user") {
73053
+ const success = await installMarketplaceViaCLI(executor, logger);
73054
+ if (success) {
73055
+ marketplaceAction = "installed";
73056
+ stdout.write(`\u2713 Marketplace: installed (${marketplaceScope})
73057
+ `);
73058
+ } else {
73059
+ marketplaceAction = "failed";
73060
+ const error = "Failed to install marketplace via CLI";
73061
+ stdout.write(`\u2717 Marketplace: ${error}
73062
+ `);
73063
+ return { marketplaceScope, pluginScope, marketplaceAction, pluginAction: "failed", error };
73064
+ }
73065
+ } else {
73066
+ await installMarketplaceViaSettings(projectDir, marketplaceScope, logger);
73067
+ marketplaceAction = "installed";
73068
+ stdout.write(`\u2713 Marketplace: installed via settings (${marketplaceScope})
73069
+ `);
73070
+ }
73071
+ let pluginAction;
73072
+ if (pluginDetected) {
73073
+ pluginAction = "already-installed";
73074
+ stdout.write(`\u2713 Plugin: already installed
73075
+ `);
73076
+ } else {
73077
+ const success = await installPlugin(executor, pluginScope, logger);
73078
+ if (success) {
73079
+ pluginAction = "installed";
73080
+ stdout.write(`\u2713 Plugin: installed (${pluginScope})
73081
+ `);
73082
+ } else {
73083
+ pluginAction = "failed";
73084
+ const error = "Failed to install sidekick plugin";
73085
+ stdout.write(`\u2717 Plugin: ${error}
73086
+ `);
73087
+ return { marketplaceScope, pluginScope, marketplaceAction, pluginAction, error };
73088
+ }
73089
+ }
73090
+ return { marketplaceScope, pluginScope, marketplaceAction, pluginAction };
73091
+ }
73092
+ }
73093
+ });
73094
+
72704
73095
  // ../sidekick-cli/dist/commands/setup/index.js
72705
73096
  var require_setup = __commonJS({
72706
73097
  "../sidekick-cli/dist/commands/setup/index.js"(exports2) {
@@ -72749,6 +73140,7 @@ var require_setup = __commonJS({
72749
73140
  var os = __importStar(require("node:os"));
72750
73141
  var core_1 = require_dist4();
72751
73142
  var prompts_js_1 = require_prompts();
73143
+ var plugin_installer_js_1 = require_plugin_installer();
72752
73144
  var USAGE_TEXT = `Usage: sidekick setup [options]
72753
73145
 
72754
73146
  Run the interactive setup wizard to configure sidekick for Claude Code.
@@ -72756,11 +73148,15 @@ When scripting flags are provided, runs non-interactively for those settings onl
72756
73148
 
72757
73149
  Options:
72758
73150
  --check Check configuration status (alias: sidekick doctor)
73151
+ --only=<checks> Run only specific doctor checks (comma-separated)
73152
+ Valid checks: api-keys, statusline, gitignore, plugin, liveness
72759
73153
  --force Apply all defaults non-interactively
72760
73154
  --help Show this help message
72761
73155
 
72762
73156
  Scripting Flags (for non-interactive/partial setup):
72763
- --statusline-scope=<scope> Configure statusline: user | project
73157
+ --marketplace-scope=<scope> Install marketplace: user | project | local
73158
+ --plugin-scope=<scope> Install plugin: user | project | local
73159
+ --statusline-scope=<scope> Configure statusline: user | project | local
72764
73160
  --gitignore Update .gitignore to exclude sidekick files
72765
73161
  --no-gitignore Skip .gitignore configuration
72766
73162
  --personas Enable persona features
@@ -72771,11 +73167,23 @@ Scripting Flags (for non-interactive/partial setup):
72771
73167
  Examples:
72772
73168
  sidekick setup Interactive wizard
72773
73169
  sidekick setup --check Check current status
73170
+ sidekick doctor --only=liveness Run only the liveness check
73171
+ sidekick doctor --only=plugin,liveness Run plugin and liveness checks
72774
73172
  sidekick setup --statusline-scope=user Configure statusline only
72775
73173
  sidekick setup --gitignore --personas Configure gitignore and enable personas
72776
73174
  OPENROUTER_API_KEY=sk-xxx sidekick setup --personas --api-key-scope=user
72777
73175
  `;
72778
73176
  var STATUSLINE_COMMAND = "npx @scotthamilton77/sidekick statusline --project-dir=$CLAUDE_PROJECT_DIR";
73177
+ function statuslineSettingsPath(scope, homeDir, projectDir) {
73178
+ switch (scope) {
73179
+ case "user":
73180
+ return path.join(homeDir, ".claude", "settings.json");
73181
+ case "project":
73182
+ return path.join(projectDir, ".claude", "settings.json");
73183
+ case "local":
73184
+ return path.join(projectDir, ".claude", "settings.local.json");
73185
+ }
73186
+ }
72779
73187
  function getPluginStatusLabel(status) {
72780
73188
  switch (status) {
72781
73189
  case "plugin":
@@ -72786,6 +73194,10 @@ Examples:
72786
73194
  return "conflict (both plugin and dev-mode detected!)";
72787
73195
  case "none":
72788
73196
  return "not installed";
73197
+ case "timeout":
73198
+ return "check timed out";
73199
+ case "error":
73200
+ return "check failed";
72789
73201
  }
72790
73202
  }
72791
73203
  function getApiKeyStatusType(health) {
@@ -72804,6 +73216,8 @@ Examples:
72804
73216
  case "dev-mode":
72805
73217
  return "\u2713";
72806
73218
  case "both":
73219
+ case "timeout":
73220
+ case "error":
72807
73221
  return "\u26A0";
72808
73222
  case "none":
72809
73223
  return "\u2717";
@@ -72815,6 +73229,7 @@ Examples:
72815
73229
  return "\u2713";
72816
73230
  case "inactive":
72817
73231
  return "\u2717";
73232
+ case "timeout":
72818
73233
  case "error":
72819
73234
  return "\u26A0";
72820
73235
  }
@@ -72825,6 +73240,8 @@ Examples:
72825
73240
  return "hooks responding";
72826
73241
  case "inactive":
72827
73242
  return "hooks not detected";
73243
+ case "timeout":
73244
+ return "check timed out";
72828
73245
  case "error":
72829
73246
  return "check failed";
72830
73247
  }
@@ -72924,27 +73341,36 @@ Examples:
72924
73341
  stdout.write("\u2502 Run 'sidekick setup' again anytime to reconfigure. \u2502\n");
72925
73342
  stdout.write("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n");
72926
73343
  }
72927
- async function runStep1Statusline(wctx) {
73344
+ async function runStep2Statusline(wctx, pluginScope) {
72928
73345
  const { ctx, homeDir, projectDir, logger } = wctx;
72929
- (0, prompts_js_1.printHeader)(ctx, "Step 1: Statusline Configuration", "Claude Code plugins cannot provide statusline config directly.");
72930
- const statuslineScope = await (0, prompts_js_1.promptSelect)(ctx, "Where should sidekick configure your statusline?", [
72931
- { value: "user", label: "User-level (~/.claude/settings.json)", description: "Works in all projects" },
72932
- {
72933
- value: "project",
72934
- label: "Project-level (.claude/settings.local.json)",
72935
- description: "This project only"
72936
- }
72937
- ]);
72938
- const statuslinePath = statuslineScope === "user" ? path.join(homeDir, ".claude", "settings.json") : path.join(projectDir, ".claude", "settings.local.json");
72939
- const wrote = await configureStatusline(statuslinePath, logger);
73346
+ (0, prompts_js_1.printHeader)(ctx, "Step 2: Statusline Configuration", "Claude Code plugins cannot provide statusline config directly.");
73347
+ const STATUSLINE_SCOPE_OPTIONS = {
73348
+ user: { label: "User-level (~/.claude/settings.json)", description: "Works in all projects" },
73349
+ project: { label: "Project-level (.claude/settings.json)", description: "Shared via git" },
73350
+ local: { label: "Local (.claude/settings.local.json)", description: "This machine only, not shared via git" }
73351
+ };
73352
+ const validScopes = (0, plugin_installer_js_1.getValidPluginScopes)(pluginScope);
73353
+ let statuslineScope;
73354
+ if (validScopes.length === 1) {
73355
+ statuslineScope = validScopes[0];
73356
+ (0, prompts_js_1.printStatus)(ctx, "info", `Statusline scope auto-selected: ${statuslineScope} (constrained by plugin scope)`);
73357
+ } else {
73358
+ const options = validScopes.map((scope) => ({
73359
+ value: scope,
73360
+ ...STATUSLINE_SCOPE_OPTIONS[scope]
73361
+ }));
73362
+ statuslineScope = await (0, prompts_js_1.promptSelect)(ctx, "Where should sidekick configure your statusline?", options);
73363
+ }
73364
+ const settingsPath = statuslineSettingsPath(statuslineScope, homeDir, projectDir);
73365
+ const wrote = await configureStatusline(settingsPath, logger);
72940
73366
  if (wrote) {
72941
- (0, prompts_js_1.printStatus)(ctx, "success", `Statusline configured in ${statuslinePath}`);
73367
+ (0, prompts_js_1.printStatus)(ctx, "success", `Statusline configured in ${settingsPath}`);
72942
73368
  } else {
72943
73369
  (0, prompts_js_1.printStatus)(ctx, "warning", "Statusline managed by dev-mode (skipped)");
72944
73370
  }
72945
73371
  return statuslineScope;
72946
73372
  }
72947
- async function runStep2Gitignore(wctx, force) {
73373
+ async function runStep3Gitignore(wctx, force) {
72948
73374
  const { ctx, projectDir } = wctx;
72949
73375
  const currentStatus = await (0, core_1.detectGitignoreStatus)(projectDir);
72950
73376
  if (currentStatus === "installed") {
@@ -72958,7 +73384,7 @@ Examples:
72958
73384
  const result2 = await (0, core_1.installGitignoreSection)(projectDir);
72959
73385
  return result2.status === "error" ? "missing" : "installed";
72960
73386
  }
72961
- (0, prompts_js_1.printHeader)(ctx, "Step 2: Git Configuration", "Sidekick creates logs and session data that should not be committed.");
73387
+ (0, prompts_js_1.printHeader)(ctx, "Step 3: Git Configuration", "Sidekick creates logs and session data that should not be committed.");
72962
73388
  if (needsRepair) {
72963
73389
  (0, prompts_js_1.printStatus)(ctx, "warning", "Existing .gitignore section is incomplete and needs repair");
72964
73390
  }
@@ -72984,10 +73410,10 @@ Examples:
72984
73410
  (0, prompts_js_1.printStatus)(ctx, "success", message);
72985
73411
  return "installed";
72986
73412
  }
72987
- async function runStep3Personas(wctx) {
73413
+ async function runStep4Personas(wctx) {
72988
73414
  const { ctx, homeDir } = wctx;
72989
73415
  const stdout = ctx.stdout;
72990
- (0, prompts_js_1.printHeader)(ctx, "Step 3: Persona Features", "Sidekick includes AI personas (Marvin, Skippy, etc.) that add\npersonality to your coding sessions with snarky messages and contextual nudges.");
73416
+ (0, prompts_js_1.printHeader)(ctx, "Step 4: Persona Features", "Sidekick includes AI personas (Marvin, Skippy, etc.) that add\npersonality to your coding sessions with snarky messages and contextual nudges.");
72991
73417
  stdout.write("These require an OpenRouter API key (small cost per message).\n\n");
72992
73418
  const wantPersonas = await (0, prompts_js_1.promptConfirm)(ctx, "Enable persona features?", true);
72993
73419
  let apiKeyHealth = "not-required";
@@ -73063,9 +73489,9 @@ Examples:
73063
73489
  const postDetection = await setupService.detectAllApiKeys("OPENROUTER_API_KEY", true);
73064
73490
  return { health: result.valid ? "healthy" : "invalid", detection: postDetection };
73065
73491
  }
73066
- async function runStep4AutoConfig(wctx) {
73492
+ async function runStep5AutoConfig(wctx) {
73067
73493
  const { ctx } = wctx;
73068
- (0, prompts_js_1.printHeader)(ctx, "Step 4: Project Auto-Configuration");
73494
+ (0, prompts_js_1.printHeader)(ctx, "Step 5: Project Auto-Configuration");
73069
73495
  const autoConfig = await (0, prompts_js_1.promptSelect)(ctx, "When sidekick runs in a new project for the first time:", [
73070
73496
  { value: "auto", label: "Auto-configure using my defaults", description: "Recommended" },
73071
73497
  { value: "manual", label: "Do nothing", description: "Manual setup only" }
@@ -73111,7 +73537,8 @@ Examples:
73111
73537
  const { statuslineScope, gitignoreStatus, wantPersonas, apiKeyHealth, autoConfig } = state;
73112
73538
  const stdout = ctx.stdout;
73113
73539
  (0, prompts_js_1.printHeader)(ctx, "Summary");
73114
- (0, prompts_js_1.printStatus)(ctx, "success", `Statusline: ${statuslineScope === "user" ? "User-level" : "Project-level"}`);
73540
+ const scopeLabel = { user: "User-level", project: "Project-level", local: "Local" }[statuslineScope];
73541
+ (0, prompts_js_1.printStatus)(ctx, "success", `Statusline: ${scopeLabel}`);
73115
73542
  let gitignoreStatusType;
73116
73543
  let gitignoreLabel;
73117
73544
  switch (gitignoreStatus) {
@@ -73160,13 +73587,26 @@ Examples:
73160
73587
  if (!force) {
73161
73588
  printWizardHeader(stdout);
73162
73589
  }
73163
- const statuslineScope = force ? "user" : await runStep1Statusline(wctx);
73164
- const gitignoreStatus = await runStep2Gitignore(wctx, force);
73165
- const { wantPersonas, apiKeyHealth, apiKeyDetection } = force ? { wantPersonas: true, apiKeyHealth: "not-required", apiKeyDetection: null } : await runStep3Personas(wctx);
73166
- const autoConfig = force ? "auto" : await runStep4AutoConfig(wctx);
73590
+ const pluginResult = await (0, plugin_installer_js_1.ensurePluginInstalled)({
73591
+ logger,
73592
+ stdout,
73593
+ force,
73594
+ projectDir,
73595
+ ctx: wctx.ctx,
73596
+ marketplaceScope: options.marketplaceScope,
73597
+ pluginScope: options.pluginScope
73598
+ });
73599
+ if (pluginResult.error) {
73600
+ logger.warn("Plugin installation had issues, continuing with setup", { error: pluginResult.error });
73601
+ }
73602
+ const forceStatuslineScope = pluginResult.pluginScope;
73603
+ const statuslineScope = force ? forceStatuslineScope : await runStep2Statusline(wctx, pluginResult.pluginScope);
73604
+ const gitignoreStatus = await runStep3Gitignore(wctx, force);
73605
+ const { wantPersonas, apiKeyHealth, apiKeyDetection } = force ? { wantPersonas: true, apiKeyHealth: "not-required", apiKeyDetection: null } : await runStep4Personas(wctx);
73606
+ const autoConfig = force ? "auto" : await runStep5AutoConfig(wctx);
73167
73607
  if (force) {
73168
- const statuslinePath = path.join(homeDir, ".claude", "settings.json");
73169
- const wrote = await configureStatusline(statuslinePath, logger);
73608
+ const settingsPath = statuslineSettingsPath(forceStatuslineScope, homeDir, projectDir);
73609
+ const wrote = await configureStatusline(settingsPath, logger);
73170
73610
  if (!wrote) {
73171
73611
  stdout.write("\u26A0 Statusline managed by dev-mode (skipped)\n");
73172
73612
  }
@@ -73184,7 +73624,9 @@ Examples:
73184
73624
  printSummary(wctx, state);
73185
73625
  } else {
73186
73626
  stdout.write("Setup complete (force mode):\n");
73187
- stdout.write(` Statusline: user-level (~/.claude/settings.json)
73627
+ stdout.write(` Plugin: ${pluginResult.pluginAction} (${pluginResult.pluginScope})
73628
+ `);
73629
+ stdout.write(` Statusline: ${forceStatuslineScope} (${statuslineSettingsPath(forceStatuslineScope, homeDir, projectDir)})
73188
73630
  `);
73189
73631
  stdout.write(` Gitignore: ${gitignoreStatus === "installed" ? "configured" : "skipped"}
73190
73632
  `);
@@ -73196,15 +73638,32 @@ Examples:
73196
73638
  return { exitCode: 0 };
73197
73639
  }
73198
73640
  function hasScriptingFlags(options) {
73199
- return options.statuslineScope !== void 0 || options.gitignore !== void 0 || options.personas !== void 0 || options.apiKeyScope !== void 0 || options.autoConfig !== void 0;
73641
+ return options.marketplaceScope !== void 0 || options.pluginScope !== void 0 || options.statuslineScope !== void 0 || options.gitignore !== void 0 || options.personas !== void 0 || options.apiKeyScope !== void 0 || options.autoConfig !== void 0;
73200
73642
  }
73201
73643
  async function runScripted(projectDir, logger, stdout, options) {
73202
73644
  const homeDir = options.homeDir ?? os.homedir();
73203
73645
  const setupService = new core_1.SetupStatusService(projectDir, { homeDir, logger });
73204
73646
  let configuredCount = 0;
73647
+ if (options.marketplaceScope !== void 0 || options.pluginScope !== void 0) {
73648
+ const pluginResult = await (0, plugin_installer_js_1.ensurePluginInstalled)({
73649
+ logger,
73650
+ stdout,
73651
+ force: true,
73652
+ // scripted mode never prompts
73653
+ projectDir,
73654
+ marketplaceScope: options.marketplaceScope,
73655
+ pluginScope: options.pluginScope
73656
+ });
73657
+ if (pluginResult.error) {
73658
+ stdout.write(`\u26A0 Plugin installation issue: ${pluginResult.error}
73659
+ `);
73660
+ } else {
73661
+ configuredCount++;
73662
+ }
73663
+ }
73205
73664
  if (options.statuslineScope) {
73206
- const statuslinePath = options.statuslineScope === "user" ? path.join(homeDir, ".claude", "settings.json") : path.join(projectDir, ".claude", "settings.local.json");
73207
- const wrote = await configureStatusline(statuslinePath, logger);
73665
+ const settingsPath = statuslineSettingsPath(options.statuslineScope, homeDir, projectDir);
73666
+ const wrote = await configureStatusline(settingsPath, logger);
73208
73667
  if (wrote) {
73209
73668
  stdout.write(`\u2713 Statusline configured (${options.statuslineScope}-level)
73210
73669
  `);
@@ -73306,64 +73765,110 @@ Configured ${configuredCount} setting${configuredCount === 1 ? "" : "s"}.
73306
73765
  }
73307
73766
  return { exitCode: 0 };
73308
73767
  }
73768
+ var DOCTOR_CHECK_NAMES = ["api-keys", "statusline", "gitignore", "plugin", "liveness"];
73769
+ function parseDoctorOnly(only) {
73770
+ if (!only)
73771
+ return null;
73772
+ const requested = only.split(",").map((s) => s.trim());
73773
+ const invalid = requested.filter((s) => !DOCTOR_CHECK_NAMES.includes(s));
73774
+ if (invalid.length > 0) {
73775
+ throw new Error(`Unknown doctor check(s): ${invalid.join(", ")}. Valid: ${DOCTOR_CHECK_NAMES.join(", ")}`);
73776
+ }
73777
+ return new Set(requested);
73778
+ }
73309
73779
  async function runDoctor(projectDir, logger, stdout, options) {
73310
73780
  const homeDir = options?.homeDir ?? os.homedir();
73311
- const skipLiveness = options?.skipLiveness ?? false;
73312
73781
  const setupService = new core_1.SetupStatusService(projectDir, { homeDir, logger });
73782
+ let filter;
73783
+ try {
73784
+ filter = parseDoctorOnly(options?.only);
73785
+ } catch (err) {
73786
+ stdout.write(`${err instanceof Error ? err.message : String(err)}
73787
+ `);
73788
+ return { exitCode: 1 };
73789
+ }
73790
+ const shouldRun = (check) => filter === null || filter.has(check);
73313
73791
  stdout.write("\nSidekick Doctor\n");
73314
73792
  stdout.write("===============\n\n");
73315
- stdout.write("Checking configuration...\n\n");
73316
- const doctorResult = await setupService.runDoctorCheck();
73317
- const gitignore = await (0, core_1.detectGitignoreStatus)(projectDir);
73318
- const pluginStatus = await setupService.detectPluginInstallation();
73319
- if (doctorResult.fixes.length > 0) {
73320
- stdout.write("Cache corrections:\n");
73321
- for (const fix of doctorResult.fixes) {
73322
- stdout.write(` \u2713 ${fix}
73793
+ const promises = [];
73794
+ let doctorResult = null;
73795
+ if (shouldRun("api-keys") || shouldRun("statusline")) {
73796
+ promises.push(setupService.runDoctorCheck().then((result) => {
73797
+ doctorResult = result;
73798
+ if (result.fixes.length > 0) {
73799
+ stdout.write("Cache corrections:\n");
73800
+ for (const fix of result.fixes) {
73801
+ stdout.write(` \u2713 ${fix}
73323
73802
  `);
73324
- }
73325
- stdout.write("\n");
73326
- }
73327
- let liveness = null;
73328
- if (!skipLiveness) {
73329
- stdout.write("Checking live status of Sidekick... this may take a few moments.\n");
73330
- liveness = await setupService.detectPluginLiveness();
73331
- }
73332
- const pluginIcon = getPluginStatusIcon(pluginStatus);
73333
- const pluginLabel = getPluginStatusLabel(pluginStatus);
73334
- const statuslineIcon = doctorResult.statusline.actual !== "none" ? "\u2713" : "\u26A0";
73335
- const gitignoreIcon = gitignore === "installed" ? "\u2713" : "\u26A0";
73336
- const openRouterResult = doctorResult.apiKeys.OPENROUTER_API_KEY;
73337
- const openRouterHealth = openRouterResult.actual;
73338
- const apiKeyIcon = openRouterHealth === "healthy" || openRouterHealth === "not-required" ? "\u2713" : "\u26A0";
73339
- const scopeBreakdown = formatApiKeyScopes(openRouterResult.scopes);
73340
- const usedToSource = { project: "project-env", user: "user-env", env: "env-var" };
73341
- const sourceLabel = formatApiKeySource(openRouterResult.used ? usedToSource[openRouterResult.used] ?? null : null);
73342
- stdout.write("\n");
73343
- stdout.write(`${pluginIcon} Plugin: ${pluginLabel}
73803
+ }
73804
+ }
73805
+ if (shouldRun("api-keys")) {
73806
+ const openRouterResult = result.apiKeys.OPENROUTER_API_KEY;
73807
+ const openRouterHealth = openRouterResult.actual;
73808
+ const apiKeyIcon = openRouterHealth === "healthy" || openRouterHealth === "not-required" ? "\u2713" : "\u26A0";
73809
+ const scopeBreakdown = formatApiKeyScopes(openRouterResult.scopes);
73810
+ const usedToSource = {
73811
+ project: "project-env",
73812
+ user: "user-env",
73813
+ env: "env-var"
73814
+ };
73815
+ const sourceLabel = formatApiKeySource(openRouterResult.used ? usedToSource[openRouterResult.used] ?? null : null);
73816
+ stdout.write(`${apiKeyIcon} OpenRouter API Key: ${openRouterHealth}${sourceLabel} ${scopeBreakdown}
73344
73817
  `);
73345
- if (liveness !== null) {
73346
- const livenessIcon = getLivenessIcon(liveness);
73347
- const livenessLabel = getLivenessLabel(liveness);
73348
- stdout.write(`${livenessIcon} Plugin Liveness: ${livenessLabel}
73818
+ }
73819
+ if (shouldRun("statusline")) {
73820
+ const statuslineIcon = result.statusline.actual !== "none" ? "\u2713" : "\u26A0";
73821
+ stdout.write(`${statuslineIcon} Statusline: ${result.statusline.actual}
73349
73822
  `);
73823
+ }
73824
+ }));
73350
73825
  }
73351
- stdout.write(`${statuslineIcon} Statusline: ${doctorResult.statusline.actual}
73826
+ let gitignore = null;
73827
+ if (shouldRun("gitignore")) {
73828
+ promises.push((0, core_1.detectGitignoreStatus)(projectDir).then((result) => {
73829
+ gitignore = result;
73830
+ const gitignoreIcon = result === "installed" ? "\u2713" : "\u26A0";
73831
+ stdout.write(`${gitignoreIcon} Gitignore: ${result}
73352
73832
  `);
73353
- stdout.write(`${gitignoreIcon} Gitignore: ${gitignore}
73833
+ }));
73834
+ }
73835
+ let pluginStatus = null;
73836
+ let liveness = null;
73837
+ if (shouldRun("plugin") || shouldRun("liveness")) {
73838
+ promises.push(setupService.detectPluginInstallation().then(async (status) => {
73839
+ pluginStatus = status;
73840
+ if (shouldRun("plugin")) {
73841
+ const pluginIcon = getPluginStatusIcon(status);
73842
+ const pluginLabel = getPluginStatusLabel(status);
73843
+ stdout.write(`${pluginIcon} Plugin: ${pluginLabel}
73354
73844
  `);
73355
- stdout.write(`${apiKeyIcon} OpenRouter API Key: ${openRouterHealth}${sourceLabel} ${scopeBreakdown}
73845
+ }
73846
+ const isPluginPresent = status === "plugin" || status === "dev-mode" || status === "both";
73847
+ if (shouldRun("liveness") && isPluginPresent) {
73848
+ logger.info("Starting plugin liveness check");
73849
+ liveness = await setupService.detectPluginLiveness();
73850
+ const livenessIcon = getLivenessIcon(liveness);
73851
+ const livenessLabel = getLivenessLabel(liveness);
73852
+ stdout.write(`${livenessIcon} Plugin Liveness: ${livenessLabel}
73356
73853
  `);
73357
- const isPluginOk = pluginStatus === "plugin" || pluginStatus === "dev-mode";
73358
- const isPluginLive = liveness === null || liveness === "active";
73359
- const isHealthy = doctorResult.overallHealth === "healthy" && gitignore === "installed" && isPluginOk && isPluginLive;
73360
- const overallIcon = isHealthy ? "\u2713" : "\u26A0";
73361
- stdout.write(`${overallIcon} Overall: ${isHealthy ? "healthy" : "needs attention"}
73854
+ logger.info("Plugin liveness check reported", { status: liveness });
73855
+ }
73856
+ }));
73857
+ }
73858
+ await Promise.all(promises);
73859
+ if (filter === null) {
73860
+ const isPluginOk = pluginStatus === "plugin" || pluginStatus === "dev-mode";
73861
+ const isPluginLive = liveness === null || liveness === "active";
73862
+ const isHealthy = doctorResult.overallHealth === "healthy" && gitignore === "installed" && isPluginOk && isPluginLive;
73863
+ const overallIcon = isHealthy ? "\u2713" : "\u26A0";
73864
+ stdout.write(`${overallIcon} Overall: ${isHealthy ? "healthy" : "needs attention"}
73362
73865
  `);
73363
- if (!isHealthy) {
73364
- stdout.write("\nRun 'sidekick setup' to configure.\n");
73866
+ if (!isHealthy) {
73867
+ stdout.write("\nRun 'sidekick setup' to configure.\n");
73868
+ }
73869
+ return { exitCode: isHealthy ? 0 : 1 };
73365
73870
  }
73366
- return { exitCode: isHealthy ? 0 : 1 };
73871
+ return { exitCode: 0 };
73367
73872
  }
73368
73873
  async function handleSetupCommand(projectDir, logger, stdout, options = {}) {
73369
73874
  if (options.help) {
@@ -73371,7 +73876,7 @@ Configured ${configuredCount} setting${configuredCount === 1 ? "" : "s"}.
73371
73876
  return { exitCode: 0 };
73372
73877
  }
73373
73878
  if (options.checkOnly) {
73374
- return runDoctor(projectDir, logger, stdout, { homeDir: options.homeDir });
73879
+ return runDoctor(projectDir, logger, stdout, { homeDir: options.homeDir, only: options.only });
73375
73880
  }
73376
73881
  if (hasScriptingFlags(options)) {
73377
73882
  return runScripted(projectDir, logger, stdout, options);
@@ -73461,6 +73966,10 @@ var require_uninstall = __commonJS({
73461
73966
  dryRun,
73462
73967
  removeHooks: true
73463
73968
  });
73969
+ await cleanSettingsFile(path.join(projectDir, ".claude", "settings.json"), "project", logger, actions, {
73970
+ dryRun,
73971
+ removeHooks: true
73972
+ });
73464
73973
  }
73465
73974
  if (userDetected) {
73466
73975
  await cleanSettingsFile(path.join(userHome, ".claude", "settings.json"), "user", logger, actions, {
@@ -73533,12 +74042,15 @@ var require_uninstall = __commonJS({
73533
74042
  await fs.access(path.join(projectDir, ".sidekick", "setup-status.json"));
73534
74043
  return true;
73535
74044
  } catch {
73536
- try {
73537
- const content = await fs.readFile(path.join(projectDir, ".claude", "settings.local.json"), "utf-8");
73538
- return content.includes("sidekick");
73539
- } catch {
73540
- return false;
74045
+ for (const file of ["settings.local.json", "settings.json"]) {
74046
+ try {
74047
+ const content = await fs.readFile(path.join(projectDir, ".claude", file), "utf-8");
74048
+ if (content.includes("sidekick"))
74049
+ return true;
74050
+ } catch {
74051
+ }
73541
74052
  }
74053
+ return false;
73542
74054
  }
73543
74055
  }
73544
74056
  async function detectUserScope(userHome) {
@@ -73855,10 +74367,7 @@ var require_cli = __commonJS({
73855
74367
  var promises_12 = require("node:fs/promises");
73856
74368
  var node_stream_1 = require("node:stream");
73857
74369
  var yargs_parser_1 = __importDefault2(require_build());
73858
- var VERSION = true ? "0.0.8-alpha.6" : "dev";
73859
- function isInSandbox() {
73860
- return process.env.SANDBOX_RUNTIME === "1";
73861
- }
74370
+ var VERSION = true ? "0.0.8-alpha.8" : "dev";
73862
74371
  var SANDBOX_ERROR_MESSAGE = `Error: Daemon commands cannot run in sandbox mode.
73863
74372
 
73864
74373
  Claude Code's sandbox blocks Unix socket operations required for daemon IPC.
@@ -73867,6 +74376,7 @@ To run this command, use dangerouslyDisableSandbox: true in the Bash tool call.
73867
74376
  Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox": true }
73868
74377
  `;
73869
74378
  var types_1 = require_dist();
74379
+ var core_1 = require_dist4();
73870
74380
  var runtime_1 = require_runtime();
73871
74381
  var UnknownOptionError = class extends Error {
73872
74382
  constructor(unknownOptions) {
@@ -73901,7 +74411,10 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
73901
74411
  "scope",
73902
74412
  "statusline-scope",
73903
74413
  "api-key-scope",
73904
- "auto-config"
74414
+ "auto-config",
74415
+ "only",
74416
+ "marketplace-scope",
74417
+ "plugin-scope"
73905
74418
  ],
73906
74419
  number: ["port", "width"],
73907
74420
  alias: { h: "help", v: "version" }
@@ -73955,7 +74468,10 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
73955
74468
  gitignore: hasGitignoreFlag ? Boolean(parsed.gitignore) : void 0,
73956
74469
  personas: hasPersonasFlag ? Boolean(parsed.personas) : void 0,
73957
74470
  apiKeyScope: parsed["api-key-scope"],
73958
- autoConfig: parsed["auto-config"]
74471
+ autoConfig: parsed["auto-config"],
74472
+ only: parsed.only,
74473
+ marketplaceScope: parsed["marketplace-scope"],
74474
+ pluginScope: parsed["plugin-scope"]
73959
74475
  };
73960
74476
  }
73961
74477
  function parseHookInput(stdinData) {
@@ -74119,7 +74635,7 @@ Run 'sidekick hook --help' for available hooks.
74119
74635
  }
74120
74636
  if (parsed.command === "daemon") {
74121
74637
  const subcommand = parsed.help ? "--help" : parsed.kill ? "kill" : parsed._ && parsed._[1] || "status";
74122
- if (isInSandbox() && subcommand !== "--help") {
74638
+ if ((0, core_1.isInSandbox)() && subcommand !== "--help") {
74123
74639
  stdout.write(SANDBOX_ERROR_MESSAGE);
74124
74640
  return { exitCode: 1, stdout: SANDBOX_ERROR_MESSAGE, stderr: "" };
74125
74641
  }
@@ -74193,7 +74709,9 @@ Run 'sidekick hook --help' for available hooks.
74193
74709
  gitignore: parsed.gitignore,
74194
74710
  personas: parsed.personas,
74195
74711
  apiKeyScope: parsed.apiKeyScope,
74196
- autoConfig: parsed.autoConfig
74712
+ autoConfig: parsed.autoConfig,
74713
+ marketplaceScope: parsed.marketplaceScope,
74714
+ pluginScope: parsed.pluginScope
74197
74715
  });
74198
74716
  return { exitCode: result.exitCode, stdout: "", stderr: "" };
74199
74717
  }
@@ -74210,7 +74728,8 @@ Run 'sidekick hook --help' for available hooks.
74210
74728
  if (parsed.command === "doctor") {
74211
74729
  const { handleSetupCommand } = await Promise.resolve().then(() => __importStar(require_setup2()));
74212
74730
  const result = await handleSetupCommand(runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
74213
- checkOnly: true
74731
+ checkOnly: true,
74732
+ only: parsed.only
74214
74733
  });
74215
74734
  return { exitCode: result.exitCode, stdout: "", stderr: "" };
74216
74735
  }