@scotthamilton77/sidekick 0.0.8-alpha.6 → 0.0.8-alpha.7
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/bin.js +177 -86
- package/dist/daemon.js +66 -37
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -41408,7 +41408,7 @@ var require_abort_controller = __commonJS({
|
|
|
41408
41408
|
"use strict";
|
|
41409
41409
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
41410
41410
|
var eventTargetShim = require_event_target_shim();
|
|
41411
|
-
var
|
|
41411
|
+
var AbortSignal2 = class extends eventTargetShim.EventTarget {
|
|
41412
41412
|
/**
|
|
41413
41413
|
* AbortSignal cannot be constructed directly.
|
|
41414
41414
|
*/
|
|
@@ -41427,9 +41427,9 @@ var require_abort_controller = __commonJS({
|
|
|
41427
41427
|
return aborted;
|
|
41428
41428
|
}
|
|
41429
41429
|
};
|
|
41430
|
-
eventTargetShim.defineEventAttribute(
|
|
41430
|
+
eventTargetShim.defineEventAttribute(AbortSignal2.prototype, "abort");
|
|
41431
41431
|
function createAbortSignal() {
|
|
41432
|
-
const signal = Object.create(
|
|
41432
|
+
const signal = Object.create(AbortSignal2.prototype);
|
|
41433
41433
|
eventTargetShim.EventTarget.call(signal);
|
|
41434
41434
|
abortedFlags.set(signal, false);
|
|
41435
41435
|
return signal;
|
|
@@ -41442,11 +41442,11 @@ var require_abort_controller = __commonJS({
|
|
|
41442
41442
|
signal.dispatchEvent({ type: "abort" });
|
|
41443
41443
|
}
|
|
41444
41444
|
var abortedFlags = /* @__PURE__ */ new WeakMap();
|
|
41445
|
-
Object.defineProperties(
|
|
41445
|
+
Object.defineProperties(AbortSignal2.prototype, {
|
|
41446
41446
|
aborted: { enumerable: true }
|
|
41447
41447
|
});
|
|
41448
41448
|
if (typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol") {
|
|
41449
|
-
Object.defineProperty(
|
|
41449
|
+
Object.defineProperty(AbortSignal2.prototype, Symbol.toStringTag, {
|
|
41450
41450
|
configurable: true,
|
|
41451
41451
|
value: "AbortSignal"
|
|
41452
41452
|
});
|
|
@@ -41490,11 +41490,11 @@ var require_abort_controller = __commonJS({
|
|
|
41490
41490
|
});
|
|
41491
41491
|
}
|
|
41492
41492
|
exports2.AbortController = AbortController2;
|
|
41493
|
-
exports2.AbortSignal =
|
|
41493
|
+
exports2.AbortSignal = AbortSignal2;
|
|
41494
41494
|
exports2.default = AbortController2;
|
|
41495
41495
|
module2.exports = AbortController2;
|
|
41496
41496
|
module2.exports.AbortController = module2.exports["default"] = AbortController2;
|
|
41497
|
-
module2.exports.AbortSignal =
|
|
41497
|
+
module2.exports.AbortSignal = AbortSignal2;
|
|
41498
41498
|
}
|
|
41499
41499
|
});
|
|
41500
41500
|
|
|
@@ -51201,11 +51201,12 @@ var require_validation = __commonJS({
|
|
|
51201
51201
|
openrouter: "https://openrouter.ai/api/v1/key",
|
|
51202
51202
|
openai: "https://api.openai.com/v1/models"
|
|
51203
51203
|
};
|
|
51204
|
-
async function validateApiKey(provider, apiKey, logger) {
|
|
51204
|
+
async function validateApiKey(provider, apiKey, logger, timeoutMs) {
|
|
51205
51205
|
const endpoint = VALIDATION_ENDPOINTS[provider];
|
|
51206
51206
|
try {
|
|
51207
51207
|
const response = await fetch(endpoint, {
|
|
51208
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
51208
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
51209
|
+
...timeoutMs !== void 0 && { signal: AbortSignal.timeout(timeoutMs) }
|
|
51209
51210
|
});
|
|
51210
51211
|
if (response.ok) {
|
|
51211
51212
|
return { valid: true };
|
|
@@ -51215,15 +51216,17 @@ var require_validation = __commonJS({
|
|
|
51215
51216
|
}
|
|
51216
51217
|
return { valid: false, error: `API returned status ${response.status}` };
|
|
51217
51218
|
} catch (err) {
|
|
51218
|
-
|
|
51219
|
-
|
|
51219
|
+
const isTimeout = err instanceof DOMException && err.name === "TimeoutError";
|
|
51220
|
+
const errorMsg = isTimeout ? `Validation timed out after ${timeoutMs}ms` : err instanceof Error ? err.message : "Network error";
|
|
51221
|
+
logger?.warn("API key validation failed", { provider, error: errorMsg, isTimeout });
|
|
51222
|
+
return { valid: false, error: errorMsg };
|
|
51220
51223
|
}
|
|
51221
51224
|
}
|
|
51222
|
-
function validateOpenRouterKey(apiKey, logger) {
|
|
51223
|
-
return validateApiKey("openrouter", apiKey, logger);
|
|
51225
|
+
function validateOpenRouterKey(apiKey, logger, timeoutMs) {
|
|
51226
|
+
return validateApiKey("openrouter", apiKey, logger, timeoutMs);
|
|
51224
51227
|
}
|
|
51225
|
-
function validateOpenAIKey(apiKey, logger) {
|
|
51226
|
-
return validateApiKey("openai", apiKey, logger);
|
|
51228
|
+
function validateOpenAIKey(apiKey, logger, timeoutMs) {
|
|
51229
|
+
return validateApiKey("openai", apiKey, logger, timeoutMs);
|
|
51227
51230
|
}
|
|
51228
51231
|
}
|
|
51229
51232
|
});
|
|
@@ -51358,6 +51361,14 @@ var require_setup_status_service = __commonJS({
|
|
|
51358
51361
|
function isDevModeCommand(command) {
|
|
51359
51362
|
return command.includes("dev-sidekick");
|
|
51360
51363
|
}
|
|
51364
|
+
var DOCTOR_TIMEOUTS = {
|
|
51365
|
+
apiKeyValidation: 1e4,
|
|
51366
|
+
pluginDetection: 1e4,
|
|
51367
|
+
pluginLiveness: 3e4
|
|
51368
|
+
};
|
|
51369
|
+
function getDoctorTimeout(defaultMs) {
|
|
51370
|
+
return process.env.DISABLE_DOCTOR_TIMEOUTS === "1" ? void 0 : defaultMs;
|
|
51371
|
+
}
|
|
51361
51372
|
function toScopeStatus(health) {
|
|
51362
51373
|
if (health === "healthy")
|
|
51363
51374
|
return "healthy";
|
|
@@ -51518,7 +51529,7 @@ var require_setup_status_service = __commonJS({
|
|
|
51518
51529
|
return "missing";
|
|
51519
51530
|
if (skipValidation)
|
|
51520
51531
|
return "healthy";
|
|
51521
|
-
const result = await validateFn(key, this.logger);
|
|
51532
|
+
const result = await validateFn(key, this.logger, getDoctorTimeout(DOCTOR_TIMEOUTS.apiKeyValidation));
|
|
51522
51533
|
return result.valid ? "healthy" : "invalid";
|
|
51523
51534
|
};
|
|
51524
51535
|
const [projectStatus, userStatus, envStatus] = await Promise.all([
|
|
@@ -51643,8 +51654,13 @@ var require_setup_status_service = __commonJS({
|
|
|
51643
51654
|
* - 'none': No sidekick hooks detected
|
|
51644
51655
|
*/
|
|
51645
51656
|
async detectPluginInstallation() {
|
|
51646
|
-
const
|
|
51657
|
+
const cliResult = await this.detectPluginFromCLI();
|
|
51647
51658
|
const hasDevMode = await this.detectDevModeFromSettings();
|
|
51659
|
+
if (cliResult === "timeout")
|
|
51660
|
+
return hasDevMode ? "dev-mode" : "timeout";
|
|
51661
|
+
if (cliResult === "error")
|
|
51662
|
+
return hasDevMode ? "dev-mode" : "error";
|
|
51663
|
+
const hasPlugin = cliResult === "found";
|
|
51648
51664
|
if (hasPlugin && hasDevMode)
|
|
51649
51665
|
return "both";
|
|
51650
51666
|
if (hasPlugin)
|
|
@@ -51655,14 +51671,17 @@ var require_setup_status_service = __commonJS({
|
|
|
51655
51671
|
}
|
|
51656
51672
|
/**
|
|
51657
51673
|
* Detect if sidekick plugin is installed via `claude plugin list --json`.
|
|
51674
|
+
* Returns a discriminated result to distinguish timeout from genuine absence.
|
|
51658
51675
|
*/
|
|
51659
51676
|
async detectPluginFromCLI() {
|
|
51677
|
+
this.logger?.info("Plugin detection started (claude plugin list --json)");
|
|
51660
51678
|
return new Promise((resolve3) => {
|
|
51661
51679
|
let resolved = false;
|
|
51662
51680
|
const safeResolve = (value) => {
|
|
51663
51681
|
if (!resolved) {
|
|
51664
51682
|
resolved = true;
|
|
51665
51683
|
clearTimeout(timeout);
|
|
51684
|
+
this.logger?.info("Plugin detection completed", { result: value });
|
|
51666
51685
|
resolve3(value);
|
|
51667
51686
|
}
|
|
51668
51687
|
};
|
|
@@ -51674,32 +51693,33 @@ var require_setup_status_service = __commonJS({
|
|
|
51674
51693
|
child.stdout?.on("data", (data) => {
|
|
51675
51694
|
stdout += data.toString();
|
|
51676
51695
|
});
|
|
51677
|
-
const
|
|
51678
|
-
|
|
51696
|
+
const timeoutMs = getDoctorTimeout(DOCTOR_TIMEOUTS.pluginDetection);
|
|
51697
|
+
const timeout = timeoutMs !== void 0 ? setTimeout(() => {
|
|
51698
|
+
this.logger?.warn(`Plugin detection timed out after ${timeoutMs / 1e3}s`);
|
|
51679
51699
|
child.kill("SIGTERM");
|
|
51680
|
-
safeResolve(
|
|
51681
|
-
},
|
|
51700
|
+
safeResolve("timeout");
|
|
51701
|
+
}, timeoutMs) : void 0;
|
|
51682
51702
|
child.on("close", (code) => {
|
|
51683
51703
|
if (code !== 0) {
|
|
51684
|
-
this.logger?.
|
|
51685
|
-
safeResolve(
|
|
51704
|
+
this.logger?.warn("claude plugin list failed", { code });
|
|
51705
|
+
safeResolve("error");
|
|
51686
51706
|
return;
|
|
51687
51707
|
}
|
|
51688
51708
|
try {
|
|
51689
51709
|
const plugins = JSON.parse(stdout);
|
|
51690
51710
|
const hasSidekick = plugins.some((p) => p.id.toLowerCase().includes("sidekick"));
|
|
51691
|
-
this.logger?.debug("Plugin detection
|
|
51692
|
-
safeResolve(hasSidekick);
|
|
51711
|
+
this.logger?.debug("Plugin detection parsed", { pluginCount: plugins.length, hasSidekick });
|
|
51712
|
+
safeResolve(hasSidekick ? "found" : "not-found");
|
|
51693
51713
|
} catch (err) {
|
|
51694
|
-
this.logger?.
|
|
51714
|
+
this.logger?.warn("Failed to parse plugin list JSON", {
|
|
51695
51715
|
error: err instanceof Error ? err.message : String(err)
|
|
51696
51716
|
});
|
|
51697
|
-
safeResolve(
|
|
51717
|
+
safeResolve("error");
|
|
51698
51718
|
}
|
|
51699
51719
|
});
|
|
51700
51720
|
child.on("error", (err) => {
|
|
51701
|
-
this.logger?.
|
|
51702
|
-
safeResolve(
|
|
51721
|
+
this.logger?.warn("claude plugin list spawn error", { error: err.message });
|
|
51722
|
+
safeResolve("error");
|
|
51703
51723
|
});
|
|
51704
51724
|
});
|
|
51705
51725
|
}
|
|
@@ -52078,17 +52098,20 @@ var require_setup_status_service = __commonJS({
|
|
|
52078
52098
|
* Useful for detecting plugins loaded via --plugin-dir that don't
|
|
52079
52099
|
* appear in settings.json.
|
|
52080
52100
|
*
|
|
52081
|
-
* @returns 'active' if hooks respond, 'inactive' if not, 'error' on failure
|
|
52101
|
+
* @returns 'active' if hooks respond, 'inactive' if not, 'timeout' on timeout, 'error' on failure
|
|
52082
52102
|
*/
|
|
52083
52103
|
async detectPluginLiveness() {
|
|
52084
52104
|
const safeWord = crypto.randomUUID().slice(0, 8);
|
|
52085
52105
|
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.)";
|
|
52106
|
+
this.logger?.info("Plugin liveness check started", { safeWord });
|
|
52086
52107
|
return new Promise((resolve3) => {
|
|
52087
52108
|
let resolved = false;
|
|
52109
|
+
let timedOut = false;
|
|
52088
52110
|
const safeResolve = (value) => {
|
|
52089
52111
|
if (!resolved) {
|
|
52090
52112
|
resolved = true;
|
|
52091
52113
|
clearTimeout(timeout);
|
|
52114
|
+
this.logger?.info("Plugin liveness check completed", { result: value });
|
|
52092
52115
|
resolve3(value);
|
|
52093
52116
|
}
|
|
52094
52117
|
};
|
|
@@ -52099,20 +52122,22 @@ var require_setup_status_service = __commonJS({
|
|
|
52099
52122
|
});
|
|
52100
52123
|
let stdout = "";
|
|
52101
52124
|
let stderr = "";
|
|
52102
|
-
this.logger?.debug("Plugin liveness check
|
|
52125
|
+
this.logger?.debug("Plugin liveness check spawned", { pid: child.pid });
|
|
52103
52126
|
child.stdout?.on("data", (data) => {
|
|
52104
52127
|
stdout += data.toString();
|
|
52105
52128
|
});
|
|
52106
52129
|
child.stderr?.on("data", (data) => {
|
|
52107
52130
|
stderr += data.toString();
|
|
52108
52131
|
});
|
|
52109
|
-
const
|
|
52110
|
-
|
|
52132
|
+
const timeoutMs = getDoctorTimeout(DOCTOR_TIMEOUTS.pluginLiveness);
|
|
52133
|
+
const timeout = timeoutMs !== void 0 ? setTimeout(() => {
|
|
52134
|
+
timedOut = true;
|
|
52135
|
+
this.logger?.warn(`Plugin liveness check timed out after ${timeoutMs / 1e3}s`);
|
|
52111
52136
|
child.kill("SIGTERM");
|
|
52112
|
-
},
|
|
52137
|
+
}, timeoutMs) : void 0;
|
|
52113
52138
|
child.on("close", (code, signal) => {
|
|
52114
|
-
if (signal === "SIGTERM") {
|
|
52115
|
-
safeResolve("
|
|
52139
|
+
if (timedOut || signal === "SIGTERM") {
|
|
52140
|
+
safeResolve("timeout");
|
|
52116
52141
|
return;
|
|
52117
52142
|
}
|
|
52118
52143
|
if (code !== 0) {
|
|
@@ -52121,7 +52146,11 @@ var require_setup_status_service = __commonJS({
|
|
|
52121
52146
|
return;
|
|
52122
52147
|
}
|
|
52123
52148
|
const isActive = stdout.includes(safeWord);
|
|
52124
|
-
this.logger?.debug("Plugin liveness check
|
|
52149
|
+
this.logger?.debug("Plugin liveness check response", {
|
|
52150
|
+
isActive,
|
|
52151
|
+
stdoutLength: stdout.length,
|
|
52152
|
+
response: stdout.slice(0, 500)
|
|
52153
|
+
});
|
|
52125
52154
|
safeResolve(isActive ? "active" : "inactive");
|
|
52126
52155
|
});
|
|
52127
52156
|
child.on("error", (err) => {
|
|
@@ -72756,6 +72785,8 @@ When scripting flags are provided, runs non-interactively for those settings onl
|
|
|
72756
72785
|
|
|
72757
72786
|
Options:
|
|
72758
72787
|
--check Check configuration status (alias: sidekick doctor)
|
|
72788
|
+
--only=<checks> Run only specific doctor checks (comma-separated)
|
|
72789
|
+
Valid checks: api-keys, statusline, gitignore, plugin, liveness
|
|
72759
72790
|
--force Apply all defaults non-interactively
|
|
72760
72791
|
--help Show this help message
|
|
72761
72792
|
|
|
@@ -72771,6 +72802,8 @@ Scripting Flags (for non-interactive/partial setup):
|
|
|
72771
72802
|
Examples:
|
|
72772
72803
|
sidekick setup Interactive wizard
|
|
72773
72804
|
sidekick setup --check Check current status
|
|
72805
|
+
sidekick doctor --only=liveness Run only the liveness check
|
|
72806
|
+
sidekick doctor --only=plugin,liveness Run plugin and liveness checks
|
|
72774
72807
|
sidekick setup --statusline-scope=user Configure statusline only
|
|
72775
72808
|
sidekick setup --gitignore --personas Configure gitignore and enable personas
|
|
72776
72809
|
OPENROUTER_API_KEY=sk-xxx sidekick setup --personas --api-key-scope=user
|
|
@@ -72786,6 +72819,10 @@ Examples:
|
|
|
72786
72819
|
return "conflict (both plugin and dev-mode detected!)";
|
|
72787
72820
|
case "none":
|
|
72788
72821
|
return "not installed";
|
|
72822
|
+
case "timeout":
|
|
72823
|
+
return "check timed out";
|
|
72824
|
+
case "error":
|
|
72825
|
+
return "check failed";
|
|
72789
72826
|
}
|
|
72790
72827
|
}
|
|
72791
72828
|
function getApiKeyStatusType(health) {
|
|
@@ -72804,6 +72841,8 @@ Examples:
|
|
|
72804
72841
|
case "dev-mode":
|
|
72805
72842
|
return "\u2713";
|
|
72806
72843
|
case "both":
|
|
72844
|
+
case "timeout":
|
|
72845
|
+
case "error":
|
|
72807
72846
|
return "\u26A0";
|
|
72808
72847
|
case "none":
|
|
72809
72848
|
return "\u2717";
|
|
@@ -72815,6 +72854,7 @@ Examples:
|
|
|
72815
72854
|
return "\u2713";
|
|
72816
72855
|
case "inactive":
|
|
72817
72856
|
return "\u2717";
|
|
72857
|
+
case "timeout":
|
|
72818
72858
|
case "error":
|
|
72819
72859
|
return "\u26A0";
|
|
72820
72860
|
}
|
|
@@ -72825,6 +72865,8 @@ Examples:
|
|
|
72825
72865
|
return "hooks responding";
|
|
72826
72866
|
case "inactive":
|
|
72827
72867
|
return "hooks not detected";
|
|
72868
|
+
case "timeout":
|
|
72869
|
+
return "check timed out";
|
|
72828
72870
|
case "error":
|
|
72829
72871
|
return "check failed";
|
|
72830
72872
|
}
|
|
@@ -73306,64 +73348,110 @@ Configured ${configuredCount} setting${configuredCount === 1 ? "" : "s"}.
|
|
|
73306
73348
|
}
|
|
73307
73349
|
return { exitCode: 0 };
|
|
73308
73350
|
}
|
|
73351
|
+
var DOCTOR_CHECK_NAMES = ["api-keys", "statusline", "gitignore", "plugin", "liveness"];
|
|
73352
|
+
function parseDoctorOnly(only) {
|
|
73353
|
+
if (!only)
|
|
73354
|
+
return null;
|
|
73355
|
+
const requested = only.split(",").map((s) => s.trim());
|
|
73356
|
+
const invalid = requested.filter((s) => !DOCTOR_CHECK_NAMES.includes(s));
|
|
73357
|
+
if (invalid.length > 0) {
|
|
73358
|
+
throw new Error(`Unknown doctor check(s): ${invalid.join(", ")}. Valid: ${DOCTOR_CHECK_NAMES.join(", ")}`);
|
|
73359
|
+
}
|
|
73360
|
+
return new Set(requested);
|
|
73361
|
+
}
|
|
73309
73362
|
async function runDoctor(projectDir, logger, stdout, options) {
|
|
73310
73363
|
const homeDir = options?.homeDir ?? os.homedir();
|
|
73311
|
-
const skipLiveness = options?.skipLiveness ?? false;
|
|
73312
73364
|
const setupService = new core_1.SetupStatusService(projectDir, { homeDir, logger });
|
|
73365
|
+
let filter;
|
|
73366
|
+
try {
|
|
73367
|
+
filter = parseDoctorOnly(options?.only);
|
|
73368
|
+
} catch (err) {
|
|
73369
|
+
stdout.write(`${err instanceof Error ? err.message : String(err)}
|
|
73370
|
+
`);
|
|
73371
|
+
return { exitCode: 1 };
|
|
73372
|
+
}
|
|
73373
|
+
const shouldRun = (check) => filter === null || filter.has(check);
|
|
73313
73374
|
stdout.write("\nSidekick Doctor\n");
|
|
73314
73375
|
stdout.write("===============\n\n");
|
|
73315
|
-
|
|
73316
|
-
|
|
73317
|
-
|
|
73318
|
-
|
|
73319
|
-
|
|
73320
|
-
|
|
73321
|
-
|
|
73322
|
-
|
|
73376
|
+
const promises = [];
|
|
73377
|
+
let doctorResult = null;
|
|
73378
|
+
if (shouldRun("api-keys") || shouldRun("statusline")) {
|
|
73379
|
+
promises.push(setupService.runDoctorCheck().then((result) => {
|
|
73380
|
+
doctorResult = result;
|
|
73381
|
+
if (result.fixes.length > 0) {
|
|
73382
|
+
stdout.write("Cache corrections:\n");
|
|
73383
|
+
for (const fix of result.fixes) {
|
|
73384
|
+
stdout.write(` \u2713 ${fix}
|
|
73323
73385
|
`);
|
|
73324
|
-
|
|
73325
|
-
|
|
73326
|
-
|
|
73327
|
-
|
|
73328
|
-
|
|
73329
|
-
|
|
73330
|
-
|
|
73331
|
-
|
|
73332
|
-
|
|
73333
|
-
|
|
73334
|
-
|
|
73335
|
-
|
|
73336
|
-
|
|
73337
|
-
|
|
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}
|
|
73386
|
+
}
|
|
73387
|
+
}
|
|
73388
|
+
if (shouldRun("api-keys")) {
|
|
73389
|
+
const openRouterResult = result.apiKeys.OPENROUTER_API_KEY;
|
|
73390
|
+
const openRouterHealth = openRouterResult.actual;
|
|
73391
|
+
const apiKeyIcon = openRouterHealth === "healthy" || openRouterHealth === "not-required" ? "\u2713" : "\u26A0";
|
|
73392
|
+
const scopeBreakdown = formatApiKeyScopes(openRouterResult.scopes);
|
|
73393
|
+
const usedToSource = {
|
|
73394
|
+
project: "project-env",
|
|
73395
|
+
user: "user-env",
|
|
73396
|
+
env: "env-var"
|
|
73397
|
+
};
|
|
73398
|
+
const sourceLabel = formatApiKeySource(openRouterResult.used ? usedToSource[openRouterResult.used] ?? null : null);
|
|
73399
|
+
stdout.write(`${apiKeyIcon} OpenRouter API Key: ${openRouterHealth}${sourceLabel} ${scopeBreakdown}
|
|
73344
73400
|
`);
|
|
73345
|
-
|
|
73346
|
-
|
|
73347
|
-
|
|
73348
|
-
|
|
73401
|
+
}
|
|
73402
|
+
if (shouldRun("statusline")) {
|
|
73403
|
+
const statuslineIcon = result.statusline.actual !== "none" ? "\u2713" : "\u26A0";
|
|
73404
|
+
stdout.write(`${statuslineIcon} Statusline: ${result.statusline.actual}
|
|
73349
73405
|
`);
|
|
73406
|
+
}
|
|
73407
|
+
}));
|
|
73350
73408
|
}
|
|
73351
|
-
|
|
73409
|
+
let gitignore = null;
|
|
73410
|
+
if (shouldRun("gitignore")) {
|
|
73411
|
+
promises.push((0, core_1.detectGitignoreStatus)(projectDir).then((result) => {
|
|
73412
|
+
gitignore = result;
|
|
73413
|
+
const gitignoreIcon = result === "installed" ? "\u2713" : "\u26A0";
|
|
73414
|
+
stdout.write(`${gitignoreIcon} Gitignore: ${result}
|
|
73352
73415
|
`);
|
|
73353
|
-
|
|
73416
|
+
}));
|
|
73417
|
+
}
|
|
73418
|
+
let pluginStatus = null;
|
|
73419
|
+
let liveness = null;
|
|
73420
|
+
if (shouldRun("plugin") || shouldRun("liveness")) {
|
|
73421
|
+
promises.push(setupService.detectPluginInstallation().then(async (status) => {
|
|
73422
|
+
pluginStatus = status;
|
|
73423
|
+
if (shouldRun("plugin")) {
|
|
73424
|
+
const pluginIcon = getPluginStatusIcon(status);
|
|
73425
|
+
const pluginLabel = getPluginStatusLabel(status);
|
|
73426
|
+
stdout.write(`${pluginIcon} Plugin: ${pluginLabel}
|
|
73354
73427
|
`);
|
|
73355
|
-
|
|
73428
|
+
}
|
|
73429
|
+
const isPluginPresent = status === "plugin" || status === "dev-mode" || status === "both";
|
|
73430
|
+
if (shouldRun("liveness") && isPluginPresent) {
|
|
73431
|
+
logger.info("Starting plugin liveness check");
|
|
73432
|
+
liveness = await setupService.detectPluginLiveness();
|
|
73433
|
+
const livenessIcon = getLivenessIcon(liveness);
|
|
73434
|
+
const livenessLabel = getLivenessLabel(liveness);
|
|
73435
|
+
stdout.write(`${livenessIcon} Plugin Liveness: ${livenessLabel}
|
|
73356
73436
|
`);
|
|
73357
|
-
|
|
73358
|
-
|
|
73359
|
-
|
|
73360
|
-
|
|
73361
|
-
|
|
73437
|
+
logger.info("Plugin liveness check reported", { status: liveness });
|
|
73438
|
+
}
|
|
73439
|
+
}));
|
|
73440
|
+
}
|
|
73441
|
+
await Promise.all(promises);
|
|
73442
|
+
if (filter === null) {
|
|
73443
|
+
const isPluginOk = pluginStatus === "plugin" || pluginStatus === "dev-mode";
|
|
73444
|
+
const isPluginLive = liveness === null || liveness === "active";
|
|
73445
|
+
const isHealthy = doctorResult.overallHealth === "healthy" && gitignore === "installed" && isPluginOk && isPluginLive;
|
|
73446
|
+
const overallIcon = isHealthy ? "\u2713" : "\u26A0";
|
|
73447
|
+
stdout.write(`${overallIcon} Overall: ${isHealthy ? "healthy" : "needs attention"}
|
|
73362
73448
|
`);
|
|
73363
|
-
|
|
73364
|
-
|
|
73449
|
+
if (!isHealthy) {
|
|
73450
|
+
stdout.write("\nRun 'sidekick setup' to configure.\n");
|
|
73451
|
+
}
|
|
73452
|
+
return { exitCode: isHealthy ? 0 : 1 };
|
|
73365
73453
|
}
|
|
73366
|
-
return { exitCode:
|
|
73454
|
+
return { exitCode: 0 };
|
|
73367
73455
|
}
|
|
73368
73456
|
async function handleSetupCommand(projectDir, logger, stdout, options = {}) {
|
|
73369
73457
|
if (options.help) {
|
|
@@ -73371,7 +73459,7 @@ Configured ${configuredCount} setting${configuredCount === 1 ? "" : "s"}.
|
|
|
73371
73459
|
return { exitCode: 0 };
|
|
73372
73460
|
}
|
|
73373
73461
|
if (options.checkOnly) {
|
|
73374
|
-
return runDoctor(projectDir, logger, stdout, { homeDir: options.homeDir });
|
|
73462
|
+
return runDoctor(projectDir, logger, stdout, { homeDir: options.homeDir, only: options.only });
|
|
73375
73463
|
}
|
|
73376
73464
|
if (hasScriptingFlags(options)) {
|
|
73377
73465
|
return runScripted(projectDir, logger, stdout, options);
|
|
@@ -73855,7 +73943,7 @@ var require_cli = __commonJS({
|
|
|
73855
73943
|
var promises_12 = require("node:fs/promises");
|
|
73856
73944
|
var node_stream_1 = require("node:stream");
|
|
73857
73945
|
var yargs_parser_1 = __importDefault2(require_build());
|
|
73858
|
-
var VERSION = true ? "0.0.8-alpha.
|
|
73946
|
+
var VERSION = true ? "0.0.8-alpha.7" : "dev";
|
|
73859
73947
|
function isInSandbox() {
|
|
73860
73948
|
return process.env.SANDBOX_RUNTIME === "1";
|
|
73861
73949
|
}
|
|
@@ -73901,7 +73989,8 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
|
|
|
73901
73989
|
"scope",
|
|
73902
73990
|
"statusline-scope",
|
|
73903
73991
|
"api-key-scope",
|
|
73904
|
-
"auto-config"
|
|
73992
|
+
"auto-config",
|
|
73993
|
+
"only"
|
|
73905
73994
|
],
|
|
73906
73995
|
number: ["port", "width"],
|
|
73907
73996
|
alias: { h: "help", v: "version" }
|
|
@@ -73955,7 +74044,8 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
|
|
|
73955
74044
|
gitignore: hasGitignoreFlag ? Boolean(parsed.gitignore) : void 0,
|
|
73956
74045
|
personas: hasPersonasFlag ? Boolean(parsed.personas) : void 0,
|
|
73957
74046
|
apiKeyScope: parsed["api-key-scope"],
|
|
73958
|
-
autoConfig: parsed["auto-config"]
|
|
74047
|
+
autoConfig: parsed["auto-config"],
|
|
74048
|
+
only: parsed.only
|
|
73959
74049
|
};
|
|
73960
74050
|
}
|
|
73961
74051
|
function parseHookInput(stdinData) {
|
|
@@ -74210,7 +74300,8 @@ Run 'sidekick hook --help' for available hooks.
|
|
|
74210
74300
|
if (parsed.command === "doctor") {
|
|
74211
74301
|
const { handleSetupCommand } = await Promise.resolve().then(() => __importStar(require_setup2()));
|
|
74212
74302
|
const result = await handleSetupCommand(runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
|
|
74213
|
-
checkOnly: true
|
|
74303
|
+
checkOnly: true,
|
|
74304
|
+
only: parsed.only
|
|
74214
74305
|
});
|
|
74215
74306
|
return { exitCode: result.exitCode, stdout: "", stderr: "" };
|
|
74216
74307
|
}
|
package/dist/daemon.js
CHANGED
|
@@ -40432,7 +40432,7 @@ var require_abort_controller = __commonJS({
|
|
|
40432
40432
|
"use strict";
|
|
40433
40433
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
40434
40434
|
var eventTargetShim = require_event_target_shim();
|
|
40435
|
-
var
|
|
40435
|
+
var AbortSignal2 = class extends eventTargetShim.EventTarget {
|
|
40436
40436
|
/**
|
|
40437
40437
|
* AbortSignal cannot be constructed directly.
|
|
40438
40438
|
*/
|
|
@@ -40451,9 +40451,9 @@ var require_abort_controller = __commonJS({
|
|
|
40451
40451
|
return aborted;
|
|
40452
40452
|
}
|
|
40453
40453
|
};
|
|
40454
|
-
eventTargetShim.defineEventAttribute(
|
|
40454
|
+
eventTargetShim.defineEventAttribute(AbortSignal2.prototype, "abort");
|
|
40455
40455
|
function createAbortSignal() {
|
|
40456
|
-
const signal = Object.create(
|
|
40456
|
+
const signal = Object.create(AbortSignal2.prototype);
|
|
40457
40457
|
eventTargetShim.EventTarget.call(signal);
|
|
40458
40458
|
abortedFlags.set(signal, false);
|
|
40459
40459
|
return signal;
|
|
@@ -40466,11 +40466,11 @@ var require_abort_controller = __commonJS({
|
|
|
40466
40466
|
signal.dispatchEvent({ type: "abort" });
|
|
40467
40467
|
}
|
|
40468
40468
|
var abortedFlags = /* @__PURE__ */ new WeakMap();
|
|
40469
|
-
Object.defineProperties(
|
|
40469
|
+
Object.defineProperties(AbortSignal2.prototype, {
|
|
40470
40470
|
aborted: { enumerable: true }
|
|
40471
40471
|
});
|
|
40472
40472
|
if (typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol") {
|
|
40473
|
-
Object.defineProperty(
|
|
40473
|
+
Object.defineProperty(AbortSignal2.prototype, Symbol.toStringTag, {
|
|
40474
40474
|
configurable: true,
|
|
40475
40475
|
value: "AbortSignal"
|
|
40476
40476
|
});
|
|
@@ -40514,11 +40514,11 @@ var require_abort_controller = __commonJS({
|
|
|
40514
40514
|
});
|
|
40515
40515
|
}
|
|
40516
40516
|
exports2.AbortController = AbortController2;
|
|
40517
|
-
exports2.AbortSignal =
|
|
40517
|
+
exports2.AbortSignal = AbortSignal2;
|
|
40518
40518
|
exports2.default = AbortController2;
|
|
40519
40519
|
module2.exports = AbortController2;
|
|
40520
40520
|
module2.exports.AbortController = module2.exports["default"] = AbortController2;
|
|
40521
|
-
module2.exports.AbortSignal =
|
|
40521
|
+
module2.exports.AbortSignal = AbortSignal2;
|
|
40522
40522
|
}
|
|
40523
40523
|
});
|
|
40524
40524
|
|
|
@@ -50225,11 +50225,12 @@ var require_validation = __commonJS({
|
|
|
50225
50225
|
openrouter: "https://openrouter.ai/api/v1/key",
|
|
50226
50226
|
openai: "https://api.openai.com/v1/models"
|
|
50227
50227
|
};
|
|
50228
|
-
async function validateApiKey(provider, apiKey, logger) {
|
|
50228
|
+
async function validateApiKey(provider, apiKey, logger, timeoutMs) {
|
|
50229
50229
|
const endpoint = VALIDATION_ENDPOINTS[provider];
|
|
50230
50230
|
try {
|
|
50231
50231
|
const response = await fetch(endpoint, {
|
|
50232
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
50232
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
50233
|
+
...timeoutMs !== void 0 && { signal: AbortSignal.timeout(timeoutMs) }
|
|
50233
50234
|
});
|
|
50234
50235
|
if (response.ok) {
|
|
50235
50236
|
return { valid: true };
|
|
@@ -50239,15 +50240,17 @@ var require_validation = __commonJS({
|
|
|
50239
50240
|
}
|
|
50240
50241
|
return { valid: false, error: `API returned status ${response.status}` };
|
|
50241
50242
|
} catch (err) {
|
|
50242
|
-
|
|
50243
|
-
|
|
50243
|
+
const isTimeout = err instanceof DOMException && err.name === "TimeoutError";
|
|
50244
|
+
const errorMsg = isTimeout ? `Validation timed out after ${timeoutMs}ms` : err instanceof Error ? err.message : "Network error";
|
|
50245
|
+
logger?.warn("API key validation failed", { provider, error: errorMsg, isTimeout });
|
|
50246
|
+
return { valid: false, error: errorMsg };
|
|
50244
50247
|
}
|
|
50245
50248
|
}
|
|
50246
|
-
function validateOpenRouterKey(apiKey, logger) {
|
|
50247
|
-
return validateApiKey("openrouter", apiKey, logger);
|
|
50249
|
+
function validateOpenRouterKey(apiKey, logger, timeoutMs) {
|
|
50250
|
+
return validateApiKey("openrouter", apiKey, logger, timeoutMs);
|
|
50248
50251
|
}
|
|
50249
|
-
function validateOpenAIKey(apiKey, logger) {
|
|
50250
|
-
return validateApiKey("openai", apiKey, logger);
|
|
50252
|
+
function validateOpenAIKey(apiKey, logger, timeoutMs) {
|
|
50253
|
+
return validateApiKey("openai", apiKey, logger, timeoutMs);
|
|
50251
50254
|
}
|
|
50252
50255
|
}
|
|
50253
50256
|
});
|
|
@@ -50382,6 +50385,14 @@ var require_setup_status_service = __commonJS({
|
|
|
50382
50385
|
function isDevModeCommand(command) {
|
|
50383
50386
|
return command.includes("dev-sidekick");
|
|
50384
50387
|
}
|
|
50388
|
+
var DOCTOR_TIMEOUTS = {
|
|
50389
|
+
apiKeyValidation: 1e4,
|
|
50390
|
+
pluginDetection: 1e4,
|
|
50391
|
+
pluginLiveness: 3e4
|
|
50392
|
+
};
|
|
50393
|
+
function getDoctorTimeout(defaultMs) {
|
|
50394
|
+
return process.env.DISABLE_DOCTOR_TIMEOUTS === "1" ? void 0 : defaultMs;
|
|
50395
|
+
}
|
|
50385
50396
|
function toScopeStatus(health) {
|
|
50386
50397
|
if (health === "healthy")
|
|
50387
50398
|
return "healthy";
|
|
@@ -50542,7 +50553,7 @@ var require_setup_status_service = __commonJS({
|
|
|
50542
50553
|
return "missing";
|
|
50543
50554
|
if (skipValidation)
|
|
50544
50555
|
return "healthy";
|
|
50545
|
-
const result = await validateFn(key, this.logger);
|
|
50556
|
+
const result = await validateFn(key, this.logger, getDoctorTimeout(DOCTOR_TIMEOUTS.apiKeyValidation));
|
|
50546
50557
|
return result.valid ? "healthy" : "invalid";
|
|
50547
50558
|
};
|
|
50548
50559
|
const [projectStatus, userStatus, envStatus] = await Promise.all([
|
|
@@ -50667,8 +50678,13 @@ var require_setup_status_service = __commonJS({
|
|
|
50667
50678
|
* - 'none': No sidekick hooks detected
|
|
50668
50679
|
*/
|
|
50669
50680
|
async detectPluginInstallation() {
|
|
50670
|
-
const
|
|
50681
|
+
const cliResult = await this.detectPluginFromCLI();
|
|
50671
50682
|
const hasDevMode = await this.detectDevModeFromSettings();
|
|
50683
|
+
if (cliResult === "timeout")
|
|
50684
|
+
return hasDevMode ? "dev-mode" : "timeout";
|
|
50685
|
+
if (cliResult === "error")
|
|
50686
|
+
return hasDevMode ? "dev-mode" : "error";
|
|
50687
|
+
const hasPlugin = cliResult === "found";
|
|
50672
50688
|
if (hasPlugin && hasDevMode)
|
|
50673
50689
|
return "both";
|
|
50674
50690
|
if (hasPlugin)
|
|
@@ -50679,14 +50695,17 @@ var require_setup_status_service = __commonJS({
|
|
|
50679
50695
|
}
|
|
50680
50696
|
/**
|
|
50681
50697
|
* Detect if sidekick plugin is installed via `claude plugin list --json`.
|
|
50698
|
+
* Returns a discriminated result to distinguish timeout from genuine absence.
|
|
50682
50699
|
*/
|
|
50683
50700
|
async detectPluginFromCLI() {
|
|
50701
|
+
this.logger?.info("Plugin detection started (claude plugin list --json)");
|
|
50684
50702
|
return new Promise((resolve3) => {
|
|
50685
50703
|
let resolved = false;
|
|
50686
50704
|
const safeResolve = (value) => {
|
|
50687
50705
|
if (!resolved) {
|
|
50688
50706
|
resolved = true;
|
|
50689
50707
|
clearTimeout(timeout);
|
|
50708
|
+
this.logger?.info("Plugin detection completed", { result: value });
|
|
50690
50709
|
resolve3(value);
|
|
50691
50710
|
}
|
|
50692
50711
|
};
|
|
@@ -50698,32 +50717,33 @@ var require_setup_status_service = __commonJS({
|
|
|
50698
50717
|
child.stdout?.on("data", (data) => {
|
|
50699
50718
|
stdout += data.toString();
|
|
50700
50719
|
});
|
|
50701
|
-
const
|
|
50702
|
-
|
|
50720
|
+
const timeoutMs = getDoctorTimeout(DOCTOR_TIMEOUTS.pluginDetection);
|
|
50721
|
+
const timeout = timeoutMs !== void 0 ? setTimeout(() => {
|
|
50722
|
+
this.logger?.warn(`Plugin detection timed out after ${timeoutMs / 1e3}s`);
|
|
50703
50723
|
child.kill("SIGTERM");
|
|
50704
|
-
safeResolve(
|
|
50705
|
-
},
|
|
50724
|
+
safeResolve("timeout");
|
|
50725
|
+
}, timeoutMs) : void 0;
|
|
50706
50726
|
child.on("close", (code) => {
|
|
50707
50727
|
if (code !== 0) {
|
|
50708
|
-
this.logger?.
|
|
50709
|
-
safeResolve(
|
|
50728
|
+
this.logger?.warn("claude plugin list failed", { code });
|
|
50729
|
+
safeResolve("error");
|
|
50710
50730
|
return;
|
|
50711
50731
|
}
|
|
50712
50732
|
try {
|
|
50713
50733
|
const plugins = JSON.parse(stdout);
|
|
50714
50734
|
const hasSidekick = plugins.some((p) => p.id.toLowerCase().includes("sidekick"));
|
|
50715
|
-
this.logger?.debug("Plugin detection
|
|
50716
|
-
safeResolve(hasSidekick);
|
|
50735
|
+
this.logger?.debug("Plugin detection parsed", { pluginCount: plugins.length, hasSidekick });
|
|
50736
|
+
safeResolve(hasSidekick ? "found" : "not-found");
|
|
50717
50737
|
} catch (err) {
|
|
50718
|
-
this.logger?.
|
|
50738
|
+
this.logger?.warn("Failed to parse plugin list JSON", {
|
|
50719
50739
|
error: err instanceof Error ? err.message : String(err)
|
|
50720
50740
|
});
|
|
50721
|
-
safeResolve(
|
|
50741
|
+
safeResolve("error");
|
|
50722
50742
|
}
|
|
50723
50743
|
});
|
|
50724
50744
|
child.on("error", (err) => {
|
|
50725
|
-
this.logger?.
|
|
50726
|
-
safeResolve(
|
|
50745
|
+
this.logger?.warn("claude plugin list spawn error", { error: err.message });
|
|
50746
|
+
safeResolve("error");
|
|
50727
50747
|
});
|
|
50728
50748
|
});
|
|
50729
50749
|
}
|
|
@@ -51102,17 +51122,20 @@ var require_setup_status_service = __commonJS({
|
|
|
51102
51122
|
* Useful for detecting plugins loaded via --plugin-dir that don't
|
|
51103
51123
|
* appear in settings.json.
|
|
51104
51124
|
*
|
|
51105
|
-
* @returns 'active' if hooks respond, 'inactive' if not, 'error' on failure
|
|
51125
|
+
* @returns 'active' if hooks respond, 'inactive' if not, 'timeout' on timeout, 'error' on failure
|
|
51106
51126
|
*/
|
|
51107
51127
|
async detectPluginLiveness() {
|
|
51108
51128
|
const safeWord = crypto.randomUUID().slice(0, 8);
|
|
51109
51129
|
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.)";
|
|
51130
|
+
this.logger?.info("Plugin liveness check started", { safeWord });
|
|
51110
51131
|
return new Promise((resolve3) => {
|
|
51111
51132
|
let resolved = false;
|
|
51133
|
+
let timedOut = false;
|
|
51112
51134
|
const safeResolve = (value) => {
|
|
51113
51135
|
if (!resolved) {
|
|
51114
51136
|
resolved = true;
|
|
51115
51137
|
clearTimeout(timeout);
|
|
51138
|
+
this.logger?.info("Plugin liveness check completed", { result: value });
|
|
51116
51139
|
resolve3(value);
|
|
51117
51140
|
}
|
|
51118
51141
|
};
|
|
@@ -51123,20 +51146,22 @@ var require_setup_status_service = __commonJS({
|
|
|
51123
51146
|
});
|
|
51124
51147
|
let stdout = "";
|
|
51125
51148
|
let stderr = "";
|
|
51126
|
-
this.logger?.debug("Plugin liveness check
|
|
51149
|
+
this.logger?.debug("Plugin liveness check spawned", { pid: child.pid });
|
|
51127
51150
|
child.stdout?.on("data", (data) => {
|
|
51128
51151
|
stdout += data.toString();
|
|
51129
51152
|
});
|
|
51130
51153
|
child.stderr?.on("data", (data) => {
|
|
51131
51154
|
stderr += data.toString();
|
|
51132
51155
|
});
|
|
51133
|
-
const
|
|
51134
|
-
|
|
51156
|
+
const timeoutMs = getDoctorTimeout(DOCTOR_TIMEOUTS.pluginLiveness);
|
|
51157
|
+
const timeout = timeoutMs !== void 0 ? setTimeout(() => {
|
|
51158
|
+
timedOut = true;
|
|
51159
|
+
this.logger?.warn(`Plugin liveness check timed out after ${timeoutMs / 1e3}s`);
|
|
51135
51160
|
child.kill("SIGTERM");
|
|
51136
|
-
},
|
|
51161
|
+
}, timeoutMs) : void 0;
|
|
51137
51162
|
child.on("close", (code, signal) => {
|
|
51138
|
-
if (signal === "SIGTERM") {
|
|
51139
|
-
safeResolve("
|
|
51163
|
+
if (timedOut || signal === "SIGTERM") {
|
|
51164
|
+
safeResolve("timeout");
|
|
51140
51165
|
return;
|
|
51141
51166
|
}
|
|
51142
51167
|
if (code !== 0) {
|
|
@@ -51145,7 +51170,11 @@ var require_setup_status_service = __commonJS({
|
|
|
51145
51170
|
return;
|
|
51146
51171
|
}
|
|
51147
51172
|
const isActive = stdout.includes(safeWord);
|
|
51148
|
-
this.logger?.debug("Plugin liveness check
|
|
51173
|
+
this.logger?.debug("Plugin liveness check response", {
|
|
51174
|
+
isActive,
|
|
51175
|
+
stdoutLength: stdout.length,
|
|
51176
|
+
response: stdout.slice(0, 500)
|
|
51177
|
+
});
|
|
51149
51178
|
safeResolve(isActive ? "active" : "inactive");
|
|
51150
51179
|
});
|
|
51151
51180
|
child.on("error", (err) => {
|
package/package.json
CHANGED