@scotthamilton77/sidekick 0.0.8-alpha.0 → 0.0.8-alpha.1
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/assets/sidekick/defaults/features/session-summary.defaults.yaml +7 -1
- package/assets/sidekick/prompts/resume-message.prompt.txt +1 -1
- package/assets/sidekick/prompts/session-summary.prompt.txt +4 -4
- package/assets/sidekick/prompts/snarky-message.prompt.txt +1 -1
- package/dist/bin.js +532 -179
- package/dist/daemon.js +389 -104
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -16704,7 +16704,7 @@ var require_setup_status = __commonJS({
|
|
|
16704
16704
|
"../types/dist/setup-status.js"(exports2) {
|
|
16705
16705
|
"use strict";
|
|
16706
16706
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
16707
|
-
exports2.ProjectSetupStatusSchema = exports2.GitignoreStatusSchema = exports2.UserSetupStatusSchema = exports2.ProjectApiKeyHealthSchema = exports2.ApiKeyHealthSchema = void 0;
|
|
16707
|
+
exports2.ProjectSetupStatusSchema = exports2.ProjectApiKeyValueSchema = exports2.GitignoreStatusSchema = exports2.UserSetupStatusSchema = exports2.UserApiKeyValueSchema = exports2.StatuslineStatusSchema = exports2.ProjectApiKeyStatusSchema = exports2.UserApiKeyStatusSchema = exports2.ApiKeyScopeSchema = exports2.ScopeStatusSchema = exports2.ProjectApiKeyHealthSchema = exports2.ApiKeyHealthSchema = void 0;
|
|
16708
16708
|
var zod_1 = require_zod();
|
|
16709
16709
|
exports2.ApiKeyHealthSchema = zod_1.z.enum([
|
|
16710
16710
|
"missing",
|
|
@@ -16727,6 +16727,42 @@ var require_setup_status = __commonJS({
|
|
|
16727
16727
|
"user"
|
|
16728
16728
|
// Deferring to user-level
|
|
16729
16729
|
]);
|
|
16730
|
+
exports2.ScopeStatusSchema = zod_1.z.enum(["healthy", "invalid", "missing"]);
|
|
16731
|
+
exports2.ApiKeyScopeSchema = zod_1.z.enum(["project", "user", "env"]);
|
|
16732
|
+
exports2.UserApiKeyStatusSchema = zod_1.z.object({
|
|
16733
|
+
/** Which scope's key is being used (first valid in priority order) */
|
|
16734
|
+
used: zod_1.z.enum(["user", "env"]).nullable(),
|
|
16735
|
+
/** Overall status based on the used key */
|
|
16736
|
+
status: exports2.ScopeStatusSchema,
|
|
16737
|
+
/** Per-scope breakdown */
|
|
16738
|
+
scopes: zod_1.z.object({
|
|
16739
|
+
user: exports2.ScopeStatusSchema,
|
|
16740
|
+
env: exports2.ScopeStatusSchema
|
|
16741
|
+
})
|
|
16742
|
+
});
|
|
16743
|
+
exports2.ProjectApiKeyStatusSchema = zod_1.z.object({
|
|
16744
|
+
/** Which scope's key is being used (first valid in priority order) */
|
|
16745
|
+
used: zod_1.z.enum(["project", "user", "env"]).nullable(),
|
|
16746
|
+
/** Overall status based on the used key */
|
|
16747
|
+
status: exports2.ScopeStatusSchema,
|
|
16748
|
+
/** Per-scope breakdown */
|
|
16749
|
+
scopes: zod_1.z.object({
|
|
16750
|
+
project: exports2.ScopeStatusSchema,
|
|
16751
|
+
user: exports2.ScopeStatusSchema,
|
|
16752
|
+
env: exports2.ScopeStatusSchema
|
|
16753
|
+
})
|
|
16754
|
+
});
|
|
16755
|
+
exports2.StatuslineStatusSchema = zod_1.z.enum([
|
|
16756
|
+
"user",
|
|
16757
|
+
// Configured in ~/.claude/settings.json
|
|
16758
|
+
"project",
|
|
16759
|
+
// Configured in .claude/settings.local.json
|
|
16760
|
+
"both",
|
|
16761
|
+
// Configured in both (project overrides user)
|
|
16762
|
+
"none"
|
|
16763
|
+
// Not configured anywhere
|
|
16764
|
+
]);
|
|
16765
|
+
exports2.UserApiKeyValueSchema = zod_1.z.union([exports2.ApiKeyHealthSchema, exports2.UserApiKeyStatusSchema]);
|
|
16730
16766
|
exports2.UserSetupStatusSchema = zod_1.z.object({
|
|
16731
16767
|
version: zod_1.z.literal(1),
|
|
16732
16768
|
lastUpdatedAt: zod_1.z.string(),
|
|
@@ -16736,10 +16772,10 @@ var require_setup_status = __commonJS({
|
|
|
16736
16772
|
defaultStatuslineScope: zod_1.z.enum(["user", "project"]),
|
|
16737
16773
|
defaultApiKeyScope: zod_1.z.enum(["user", "project", "skip"])
|
|
16738
16774
|
}),
|
|
16739
|
-
statusline:
|
|
16775
|
+
statusline: exports2.StatuslineStatusSchema,
|
|
16740
16776
|
apiKeys: zod_1.z.object({
|
|
16741
|
-
OPENROUTER_API_KEY: exports2.
|
|
16742
|
-
OPENAI_API_KEY: exports2.
|
|
16777
|
+
OPENROUTER_API_KEY: exports2.UserApiKeyValueSchema,
|
|
16778
|
+
OPENAI_API_KEY: exports2.UserApiKeyValueSchema
|
|
16743
16779
|
}),
|
|
16744
16780
|
/** True if sidekick plugin detected at user scope via `claude plugin list` */
|
|
16745
16781
|
pluginDetected: zod_1.z.boolean().optional()
|
|
@@ -16754,15 +16790,16 @@ var require_setup_status = __commonJS({
|
|
|
16754
16790
|
"installed"
|
|
16755
16791
|
// Sidekick section present with all required entries
|
|
16756
16792
|
]);
|
|
16793
|
+
exports2.ProjectApiKeyValueSchema = zod_1.z.union([exports2.ProjectApiKeyHealthSchema, exports2.ProjectApiKeyStatusSchema]);
|
|
16757
16794
|
exports2.ProjectSetupStatusSchema = zod_1.z.object({
|
|
16758
16795
|
version: zod_1.z.literal(1),
|
|
16759
16796
|
lastUpdatedAt: zod_1.z.string(),
|
|
16760
16797
|
// ISO timestamp
|
|
16761
16798
|
autoConfigured: zod_1.z.boolean(),
|
|
16762
|
-
statusline:
|
|
16799
|
+
statusline: exports2.StatuslineStatusSchema,
|
|
16763
16800
|
apiKeys: zod_1.z.object({
|
|
16764
|
-
OPENROUTER_API_KEY: exports2.
|
|
16765
|
-
OPENAI_API_KEY: exports2.
|
|
16801
|
+
OPENROUTER_API_KEY: exports2.ProjectApiKeyValueSchema,
|
|
16802
|
+
OPENAI_API_KEY: exports2.ProjectApiKeyValueSchema
|
|
16766
16803
|
}),
|
|
16767
16804
|
gitignore: exports2.GitignoreStatusSchema.optional().default("unknown"),
|
|
16768
16805
|
/** True if sidekick plugin detected at project scope via `claude plugin list` */
|
|
@@ -50025,7 +50062,7 @@ var require_validation = __commonJS({
|
|
|
50025
50062
|
exports2.validateOpenRouterKey = validateOpenRouterKey;
|
|
50026
50063
|
exports2.validateOpenAIKey = validateOpenAIKey;
|
|
50027
50064
|
var VALIDATION_ENDPOINTS = {
|
|
50028
|
-
openrouter: "https://openrouter.ai/api/v1/
|
|
50065
|
+
openrouter: "https://openrouter.ai/api/v1/key",
|
|
50029
50066
|
openai: "https://api.openai.com/v1/models"
|
|
50030
50067
|
};
|
|
50031
50068
|
async function validateApiKey(provider, apiKey, logger) {
|
|
@@ -50185,6 +50222,25 @@ var require_setup_status_service = __commonJS({
|
|
|
50185
50222
|
function isDevModeCommand(command) {
|
|
50186
50223
|
return command.includes("dev-sidekick");
|
|
50187
50224
|
}
|
|
50225
|
+
function toScopeStatus(health) {
|
|
50226
|
+
if (health === "healthy")
|
|
50227
|
+
return "healthy";
|
|
50228
|
+
if (health === "invalid")
|
|
50229
|
+
return "invalid";
|
|
50230
|
+
return "missing";
|
|
50231
|
+
}
|
|
50232
|
+
function determineOverallStatus(used, anyKeyFound) {
|
|
50233
|
+
if (used)
|
|
50234
|
+
return "healthy";
|
|
50235
|
+
if (anyKeyFound)
|
|
50236
|
+
return "invalid";
|
|
50237
|
+
return "missing";
|
|
50238
|
+
}
|
|
50239
|
+
var SCOPE_TO_SOURCE = {
|
|
50240
|
+
project: "project-env",
|
|
50241
|
+
user: "user-env",
|
|
50242
|
+
env: "env-var"
|
|
50243
|
+
};
|
|
50188
50244
|
var SetupStatusService = class {
|
|
50189
50245
|
constructor(projectDir, options) {
|
|
50190
50246
|
this.projectDir = projectDir;
|
|
@@ -50272,47 +50328,152 @@ var require_setup_status_service = __commonJS({
|
|
|
50272
50328
|
// === Actual config detection ===
|
|
50273
50329
|
/**
|
|
50274
50330
|
* Detect actual API key by reading .env files.
|
|
50275
|
-
*
|
|
50276
|
-
* Returns the key value
|
|
50331
|
+
* Priority: project .env → user .env → environment variable.
|
|
50332
|
+
* Returns both the key value and where it was found.
|
|
50277
50333
|
*/
|
|
50278
50334
|
async detectActualApiKey(keyName) {
|
|
50335
|
+
const projectKey = await this.readKeyFromEnvFile(path.join(this.projectDir, ".sidekick", ".env"), keyName);
|
|
50336
|
+
if (projectKey) {
|
|
50337
|
+
return { key: projectKey, source: "project-env" };
|
|
50338
|
+
}
|
|
50339
|
+
const userKey = await this.readKeyFromEnvFile(path.join(this.homeDir, ".sidekick", ".env"), keyName);
|
|
50340
|
+
if (userKey) {
|
|
50341
|
+
return { key: userKey, source: "user-env" };
|
|
50342
|
+
}
|
|
50279
50343
|
if (process.env[keyName]) {
|
|
50280
|
-
return process.env[keyName];
|
|
50344
|
+
return { key: process.env[keyName] ?? null, source: "env-var" };
|
|
50281
50345
|
}
|
|
50282
|
-
|
|
50283
|
-
|
|
50284
|
-
|
|
50285
|
-
|
|
50286
|
-
|
|
50287
|
-
|
|
50288
|
-
|
|
50289
|
-
|
|
50290
|
-
|
|
50291
|
-
}
|
|
50346
|
+
return { key: null, source: null };
|
|
50347
|
+
}
|
|
50348
|
+
/**
|
|
50349
|
+
* Read API key from a specific .env file.
|
|
50350
|
+
* @returns The key value if found, null otherwise.
|
|
50351
|
+
*/
|
|
50352
|
+
async readKeyFromEnvFile(envPath, keyName) {
|
|
50353
|
+
try {
|
|
50354
|
+
const content = await fs.readFile(envPath, "utf-8");
|
|
50355
|
+
const match = content.match(new RegExp(`^${keyName}=(.+)$`, "m"));
|
|
50356
|
+
return match ? match[1] : null;
|
|
50357
|
+
} catch {
|
|
50358
|
+
return null;
|
|
50292
50359
|
}
|
|
50293
|
-
|
|
50360
|
+
}
|
|
50361
|
+
/**
|
|
50362
|
+
* Detect API key across ALL scopes and optionally validate each.
|
|
50363
|
+
* New priority order: project → user → env
|
|
50364
|
+
*
|
|
50365
|
+
* @param keyName - The API key name (OPENROUTER_API_KEY or OPENAI_API_KEY)
|
|
50366
|
+
* @param skipValidation - If true, found keys are marked 'healthy' without validation
|
|
50367
|
+
* @returns Detection results for all three scopes
|
|
50368
|
+
*/
|
|
50369
|
+
async detectAllApiKeys(keyName, skipValidation = false) {
|
|
50370
|
+
const validateFn = keyName === "OPENROUTER_API_KEY" ? shared_providers_1.validateOpenRouterKey : shared_providers_1.validateOpenAIKey;
|
|
50371
|
+
const projectEnvPath = path.join(this.projectDir, ".sidekick", ".env");
|
|
50372
|
+
const userEnvPath = path.join(this.homeDir, ".sidekick", ".env");
|
|
50373
|
+
const projectKey = await this.readKeyFromEnvFile(projectEnvPath, keyName);
|
|
50374
|
+
const userKey = await this.readKeyFromEnvFile(userEnvPath, keyName);
|
|
50375
|
+
const rawEnvKey = process.env[keyName] ?? null;
|
|
50376
|
+
const envKey = rawEnvKey && rawEnvKey !== projectKey && rawEnvKey !== userKey ? rawEnvKey : null;
|
|
50377
|
+
const getStatus = async (key) => {
|
|
50378
|
+
if (!key)
|
|
50379
|
+
return "missing";
|
|
50380
|
+
if (skipValidation)
|
|
50381
|
+
return "healthy";
|
|
50382
|
+
const result = await validateFn(key, this.logger);
|
|
50383
|
+
return result.valid ? "healthy" : "invalid";
|
|
50384
|
+
};
|
|
50385
|
+
const [projectStatus, userStatus, envStatus] = await Promise.all([
|
|
50386
|
+
getStatus(projectKey),
|
|
50387
|
+
getStatus(userKey),
|
|
50388
|
+
getStatus(envKey)
|
|
50389
|
+
]);
|
|
50390
|
+
return {
|
|
50391
|
+
project: { found: projectKey !== null, key: projectKey, status: projectStatus },
|
|
50392
|
+
user: { found: userKey !== null, key: userKey, status: userStatus },
|
|
50393
|
+
env: { found: envKey !== null, key: envKey, status: envStatus }
|
|
50394
|
+
};
|
|
50395
|
+
}
|
|
50396
|
+
/**
|
|
50397
|
+
* Build UserApiKeyStatus from detection results.
|
|
50398
|
+
* User-level status only includes 'user' and 'env' scopes.
|
|
50399
|
+
* Priority for 'used': user → env (first valid)
|
|
50400
|
+
*/
|
|
50401
|
+
buildUserApiKeyStatus(detection) {
|
|
50402
|
+
let used = null;
|
|
50403
|
+
if (detection.user.status === "healthy") {
|
|
50404
|
+
used = "user";
|
|
50405
|
+
} else if (detection.env.status === "healthy") {
|
|
50406
|
+
used = "env";
|
|
50407
|
+
}
|
|
50408
|
+
const status = determineOverallStatus(used, detection.user.found || detection.env.found);
|
|
50409
|
+
return {
|
|
50410
|
+
used,
|
|
50411
|
+
status,
|
|
50412
|
+
scopes: {
|
|
50413
|
+
user: detection.user.status,
|
|
50414
|
+
env: detection.env.status
|
|
50415
|
+
}
|
|
50416
|
+
};
|
|
50417
|
+
}
|
|
50418
|
+
/**
|
|
50419
|
+
* Build ProjectApiKeyStatus from detection results.
|
|
50420
|
+
* Project-level status includes all three scopes.
|
|
50421
|
+
* Priority for 'used': project → user → env (first valid)
|
|
50422
|
+
*/
|
|
50423
|
+
buildProjectApiKeyStatus(detection) {
|
|
50424
|
+
let used = null;
|
|
50425
|
+
if (detection.project.status === "healthy") {
|
|
50426
|
+
used = "project";
|
|
50427
|
+
} else if (detection.user.status === "healthy") {
|
|
50428
|
+
used = "user";
|
|
50429
|
+
} else if (detection.env.status === "healthy") {
|
|
50430
|
+
used = "env";
|
|
50431
|
+
}
|
|
50432
|
+
const status = determineOverallStatus(used, detection.project.found || detection.user.found || detection.env.found);
|
|
50433
|
+
return {
|
|
50434
|
+
used,
|
|
50435
|
+
status,
|
|
50436
|
+
scopes: {
|
|
50437
|
+
project: detection.project.status,
|
|
50438
|
+
user: detection.user.status,
|
|
50439
|
+
env: detection.env.status
|
|
50440
|
+
}
|
|
50441
|
+
};
|
|
50294
50442
|
}
|
|
50295
50443
|
/**
|
|
50296
50444
|
* Detect actual statusline configuration by reading Claude settings files.
|
|
50297
|
-
*
|
|
50445
|
+
* Returns WHERE the statusline is configured, matching PluginInstallationStatus pattern.
|
|
50298
50446
|
*/
|
|
50299
50447
|
async detectActualStatusline() {
|
|
50300
|
-
const
|
|
50301
|
-
|
|
50302
|
-
|
|
50303
|
-
|
|
50304
|
-
|
|
50305
|
-
|
|
50306
|
-
|
|
50307
|
-
|
|
50308
|
-
|
|
50309
|
-
|
|
50310
|
-
return "configured";
|
|
50311
|
-
}
|
|
50312
|
-
} catch {
|
|
50448
|
+
const userSettingsPath = path.join(this.homeDir, ".claude", "settings.json");
|
|
50449
|
+
const projectSettingsPath = path.join(this.projectDir, ".claude", "settings.local.json");
|
|
50450
|
+
let inUser = false;
|
|
50451
|
+
let inProject = false;
|
|
50452
|
+
try {
|
|
50453
|
+
const content = await fs.readFile(userSettingsPath, "utf-8");
|
|
50454
|
+
const settings = JSON.parse(content);
|
|
50455
|
+
const statusLine = settings.statusLine;
|
|
50456
|
+
if (isSidekickStatuslineCommand(statusLine?.command)) {
|
|
50457
|
+
inUser = true;
|
|
50313
50458
|
}
|
|
50459
|
+
} catch {
|
|
50314
50460
|
}
|
|
50315
|
-
|
|
50461
|
+
try {
|
|
50462
|
+
const content = await fs.readFile(projectSettingsPath, "utf-8");
|
|
50463
|
+
const settings = JSON.parse(content);
|
|
50464
|
+
const statusLine = settings.statusLine;
|
|
50465
|
+
if (isSidekickStatuslineCommand(statusLine?.command)) {
|
|
50466
|
+
inProject = true;
|
|
50467
|
+
}
|
|
50468
|
+
} catch {
|
|
50469
|
+
}
|
|
50470
|
+
if (inUser && inProject)
|
|
50471
|
+
return "both";
|
|
50472
|
+
if (inProject)
|
|
50473
|
+
return "project";
|
|
50474
|
+
if (inUser)
|
|
50475
|
+
return "user";
|
|
50476
|
+
return "none";
|
|
50316
50477
|
}
|
|
50317
50478
|
/**
|
|
50318
50479
|
* Detect plugin installation status.
|
|
@@ -50432,36 +50593,52 @@ var require_setup_status_service = __commonJS({
|
|
|
50432
50593
|
return false;
|
|
50433
50594
|
}
|
|
50434
50595
|
// === Merged getters ===
|
|
50435
|
-
|
|
50596
|
+
/**
|
|
50597
|
+
* Get cached statusline status. Project cache takes precedence over user cache.
|
|
50598
|
+
* Returns 'none' if no cache exists.
|
|
50599
|
+
*/
|
|
50600
|
+
async getStatuslineStatus() {
|
|
50436
50601
|
const project = await this.getProjectStatus();
|
|
50437
|
-
if (project?.statusline
|
|
50438
|
-
return
|
|
50439
|
-
if (project?.statusline === "skipped")
|
|
50440
|
-
return "skipped";
|
|
50441
|
-
if (project?.statusline === "user") {
|
|
50442
|
-
const user2 = await this.getUserStatus();
|
|
50443
|
-
return user2?.statusline ?? "not-setup";
|
|
50444
|
-
}
|
|
50602
|
+
if (project?.statusline)
|
|
50603
|
+
return project.statusline;
|
|
50445
50604
|
const user = await this.getUserStatus();
|
|
50446
|
-
|
|
50605
|
+
if (user?.statusline)
|
|
50606
|
+
return user.statusline;
|
|
50607
|
+
return "none";
|
|
50608
|
+
}
|
|
50609
|
+
/**
|
|
50610
|
+
* @deprecated Use getStatuslineStatus() instead
|
|
50611
|
+
*/
|
|
50612
|
+
async getStatuslineHealth() {
|
|
50613
|
+
return this.getStatuslineStatus();
|
|
50447
50614
|
}
|
|
50448
50615
|
async getApiKeyHealth(key) {
|
|
50449
50616
|
const project = await this.getProjectStatus();
|
|
50450
50617
|
const projectHealth = project?.apiKeys[key];
|
|
50451
50618
|
if (projectHealth && projectHealth !== "user") {
|
|
50619
|
+
if (typeof projectHealth === "object") {
|
|
50620
|
+
return toScopeStatus(projectHealth.status);
|
|
50621
|
+
}
|
|
50452
50622
|
return projectHealth;
|
|
50453
50623
|
}
|
|
50454
50624
|
const user = await this.getUserStatus();
|
|
50455
|
-
|
|
50625
|
+
const userHealth = user?.apiKeys[key];
|
|
50626
|
+
if (!userHealth)
|
|
50627
|
+
return "missing";
|
|
50628
|
+
if (typeof userHealth === "object") {
|
|
50629
|
+
return toScopeStatus(userHealth.status);
|
|
50630
|
+
}
|
|
50631
|
+
return userHealth;
|
|
50456
50632
|
}
|
|
50457
50633
|
async getEffectiveApiKeyHealth(key) {
|
|
50458
50634
|
const health = await this.getApiKeyHealth(key);
|
|
50459
50635
|
return health === "user" ? "missing" : health;
|
|
50460
50636
|
}
|
|
50461
50637
|
async isHealthy() {
|
|
50462
|
-
const statusline = await this.
|
|
50638
|
+
const statusline = await this.getStatuslineStatus();
|
|
50463
50639
|
const openrouterKey = await this.getEffectiveApiKeyHealth("OPENROUTER_API_KEY");
|
|
50464
|
-
|
|
50640
|
+
const statuslineOk = statusline !== "none";
|
|
50641
|
+
return statuslineOk && (openrouterKey === "healthy" || openrouterKey === "not-required");
|
|
50465
50642
|
}
|
|
50466
50643
|
// === Dev-mode and plugin detection helpers ===
|
|
50467
50644
|
/**
|
|
@@ -50474,21 +50651,38 @@ var require_setup_status_service = __commonJS({
|
|
|
50474
50651
|
}
|
|
50475
50652
|
/**
|
|
50476
50653
|
* Set the devMode flag in project status.
|
|
50477
|
-
* Creates project status if it doesn't exist.
|
|
50654
|
+
* Creates project status if it doesn't exist, detecting actual configuration state.
|
|
50478
50655
|
*/
|
|
50479
50656
|
async setDevMode(enabled) {
|
|
50480
50657
|
const current = await this.getProjectStatus();
|
|
50481
50658
|
if (current) {
|
|
50482
50659
|
await this.updateProjectStatus({ devMode: enabled });
|
|
50483
50660
|
} else {
|
|
50661
|
+
const statusline = await this.detectActualStatusline();
|
|
50662
|
+
const openRouterResult = await this.detectActualApiKey("OPENROUTER_API_KEY");
|
|
50663
|
+
const openAIResult = await this.detectActualApiKey("OPENAI_API_KEY");
|
|
50664
|
+
const getApiKeyHealth = async (result) => {
|
|
50665
|
+
if (result.source === "project-env") {
|
|
50666
|
+
return "pending-validation";
|
|
50667
|
+
}
|
|
50668
|
+
if (result.source === "user-env" || result.source === "env-var") {
|
|
50669
|
+
return "user";
|
|
50670
|
+
}
|
|
50671
|
+
const userStatus = await this.getUserStatus();
|
|
50672
|
+
if (userStatus) {
|
|
50673
|
+
return "user";
|
|
50674
|
+
}
|
|
50675
|
+
return "missing";
|
|
50676
|
+
};
|
|
50484
50677
|
await this.writeProjectStatus({
|
|
50485
50678
|
version: 1,
|
|
50486
50679
|
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
50487
50680
|
autoConfigured: false,
|
|
50488
|
-
statusline
|
|
50681
|
+
statusline,
|
|
50682
|
+
// Now directly uses 'user' | 'project' | 'both' | 'none'
|
|
50489
50683
|
apiKeys: {
|
|
50490
|
-
OPENROUTER_API_KEY:
|
|
50491
|
-
OPENAI_API_KEY:
|
|
50684
|
+
OPENROUTER_API_KEY: await getApiKeyHealth(openRouterResult),
|
|
50685
|
+
OPENAI_API_KEY: await getApiKeyHealth(openAIResult)
|
|
50492
50686
|
},
|
|
50493
50687
|
gitignore: "unknown",
|
|
50494
50688
|
devMode: enabled
|
|
@@ -50520,6 +50714,41 @@ var require_setup_status_service = __commonJS({
|
|
|
50520
50714
|
}
|
|
50521
50715
|
return !await this.isProjectConfigured();
|
|
50522
50716
|
}
|
|
50717
|
+
/**
|
|
50718
|
+
* Auto-configure the project using user's default preferences.
|
|
50719
|
+
* Creates project status that inherits from user-level settings.
|
|
50720
|
+
* @returns true if auto-configured, false if skipped (preference disabled or already configured)
|
|
50721
|
+
*/
|
|
50722
|
+
async autoConfigureProject() {
|
|
50723
|
+
const user = await this.getUserStatus();
|
|
50724
|
+
if (!user?.preferences.autoConfigureProjects) {
|
|
50725
|
+
this.logger?.debug("Skipping auto-configure: user preference disabled");
|
|
50726
|
+
return false;
|
|
50727
|
+
}
|
|
50728
|
+
if (await this.isProjectConfigured()) {
|
|
50729
|
+
this.logger?.debug("Skipping auto-configure: project already configured");
|
|
50730
|
+
return false;
|
|
50731
|
+
}
|
|
50732
|
+
this.logger?.info("Auto-configuring project with user defaults", {
|
|
50733
|
+
projectDir: this.projectDir
|
|
50734
|
+
});
|
|
50735
|
+
const projectStatus = {
|
|
50736
|
+
version: 1,
|
|
50737
|
+
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
50738
|
+
autoConfigured: true,
|
|
50739
|
+
statusline: "user",
|
|
50740
|
+
apiKeys: {
|
|
50741
|
+
OPENROUTER_API_KEY: "user",
|
|
50742
|
+
OPENAI_API_KEY: "user"
|
|
50743
|
+
},
|
|
50744
|
+
gitignore: "unknown"
|
|
50745
|
+
};
|
|
50746
|
+
await this.writeProjectStatus(projectStatus);
|
|
50747
|
+
this.logger?.info("Project auto-configured successfully", {
|
|
50748
|
+
projectDir: this.projectDir
|
|
50749
|
+
});
|
|
50750
|
+
return true;
|
|
50751
|
+
}
|
|
50523
50752
|
async setApiKeyHealth(key, health, scope) {
|
|
50524
50753
|
if (scope === "user") {
|
|
50525
50754
|
const current = await this.getUserStatus();
|
|
@@ -50547,62 +50776,110 @@ var require_setup_status_service = __commonJS({
|
|
|
50547
50776
|
async runDoctorCheck(options = {}) {
|
|
50548
50777
|
const fixes = [];
|
|
50549
50778
|
const actualStatusline = await this.detectActualStatusline();
|
|
50550
|
-
const cachedStatusline = await this.
|
|
50551
|
-
const statuslineFixed = actualStatusline !== cachedStatusline && actualStatusline
|
|
50779
|
+
const cachedStatusline = await this.getStatuslineStatus();
|
|
50780
|
+
const statuslineFixed = actualStatusline !== cachedStatusline && actualStatusline !== "none";
|
|
50552
50781
|
if (statuslineFixed) {
|
|
50553
|
-
fixes.push(
|
|
50554
|
-
|
|
50555
|
-
|
|
50556
|
-
|
|
50782
|
+
fixes.push(`Statusline was actually configured at ${actualStatusline} level (updated cache)`);
|
|
50783
|
+
if (actualStatusline === "project" || actualStatusline === "both") {
|
|
50784
|
+
const projectStatus = await this.getProjectStatus();
|
|
50785
|
+
if (projectStatus) {
|
|
50786
|
+
await this.updateProjectStatus({ statusline: actualStatusline });
|
|
50787
|
+
} else {
|
|
50788
|
+
await this.writeProjectStatus({
|
|
50789
|
+
version: 1,
|
|
50790
|
+
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
50791
|
+
autoConfigured: false,
|
|
50792
|
+
statusline: actualStatusline,
|
|
50793
|
+
apiKeys: {
|
|
50794
|
+
OPENROUTER_API_KEY: "user",
|
|
50795
|
+
OPENAI_API_KEY: "user"
|
|
50796
|
+
},
|
|
50797
|
+
gitignore: "unknown"
|
|
50798
|
+
});
|
|
50799
|
+
}
|
|
50557
50800
|
} else {
|
|
50558
|
-
await this.
|
|
50559
|
-
|
|
50560
|
-
|
|
50561
|
-
|
|
50562
|
-
|
|
50563
|
-
|
|
50564
|
-
|
|
50565
|
-
|
|
50566
|
-
|
|
50567
|
-
|
|
50568
|
-
|
|
50569
|
-
|
|
50570
|
-
|
|
50571
|
-
|
|
50801
|
+
const userStatus = await this.getUserStatus();
|
|
50802
|
+
if (userStatus) {
|
|
50803
|
+
await this.updateUserStatus({ statusline: actualStatusline });
|
|
50804
|
+
} else {
|
|
50805
|
+
await this.writeUserStatus({
|
|
50806
|
+
version: 1,
|
|
50807
|
+
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
50808
|
+
preferences: {
|
|
50809
|
+
autoConfigureProjects: false,
|
|
50810
|
+
defaultStatuslineScope: "user",
|
|
50811
|
+
defaultApiKeyScope: "user"
|
|
50812
|
+
},
|
|
50813
|
+
statusline: actualStatusline,
|
|
50814
|
+
apiKeys: {
|
|
50815
|
+
OPENROUTER_API_KEY: "missing",
|
|
50816
|
+
OPENAI_API_KEY: "not-required"
|
|
50817
|
+
}
|
|
50818
|
+
});
|
|
50819
|
+
}
|
|
50572
50820
|
}
|
|
50573
50821
|
}
|
|
50574
50822
|
const apiKeyResults = {};
|
|
50575
50823
|
const keysToCheck = ["OPENROUTER_API_KEY", "OPENAI_API_KEY"];
|
|
50576
50824
|
for (const keyName of keysToCheck) {
|
|
50577
|
-
const
|
|
50825
|
+
const detection = await this.detectAllApiKeys(keyName, options.skipValidation);
|
|
50826
|
+
const projectApiKeyStatus = this.buildProjectApiKeyStatus(detection);
|
|
50578
50827
|
const cachedHealth = await this.getEffectiveApiKeyHealth(keyName);
|
|
50579
|
-
|
|
50580
|
-
|
|
50581
|
-
|
|
50582
|
-
|
|
50583
|
-
|
|
50584
|
-
} else {
|
|
50585
|
-
const validateFn = keyName === "OPENROUTER_API_KEY" ? shared_providers_1.validateOpenRouterKey : shared_providers_1.validateOpenAIKey;
|
|
50586
|
-
const validationResult = await validateFn(actualKey, this.logger);
|
|
50587
|
-
actualHealth = validationResult.valid ? "healthy" : "invalid";
|
|
50588
|
-
}
|
|
50589
|
-
const keyFixed = actualKey !== null && cachedHealth === "missing";
|
|
50828
|
+
const actualHealth = toScopeStatus(projectApiKeyStatus.status);
|
|
50829
|
+
const source = projectApiKeyStatus.used ? SCOPE_TO_SOURCE[projectApiKeyStatus.used] ?? null : null;
|
|
50830
|
+
const anyKeyPresent = detection.project.found || detection.user.found || detection.env.found;
|
|
50831
|
+
const cacheNeedsUpdate = anyKeyPresent && cachedHealth === "missing" || !anyKeyPresent && cachedHealth !== "missing" && cachedHealth !== "not-required";
|
|
50832
|
+
const keyFixed = cacheNeedsUpdate;
|
|
50590
50833
|
if (keyFixed) {
|
|
50591
|
-
|
|
50592
|
-
|
|
50593
|
-
|
|
50594
|
-
|
|
50595
|
-
|
|
50834
|
+
const state = anyKeyPresent ? "present" : "missing";
|
|
50835
|
+
fixes.push(`${keyName} was actually ${state} (updated cache)`);
|
|
50836
|
+
const projectStatus = await this.getProjectStatus();
|
|
50837
|
+
if (projectStatus) {
|
|
50838
|
+
await this.updateProjectStatus({
|
|
50839
|
+
apiKeys: { ...projectStatus.apiKeys, [keyName]: projectApiKeyStatus }
|
|
50596
50840
|
});
|
|
50841
|
+
} else {
|
|
50842
|
+
const userApiKeyStatus = this.buildUserApiKeyStatus(detection);
|
|
50843
|
+
await this.writeProjectStatus({
|
|
50844
|
+
version: 1,
|
|
50845
|
+
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
50846
|
+
autoConfigured: false,
|
|
50847
|
+
statusline: actualStatusline,
|
|
50848
|
+
apiKeys: {
|
|
50849
|
+
OPENROUTER_API_KEY: keyName === "OPENROUTER_API_KEY" ? projectApiKeyStatus : "user",
|
|
50850
|
+
OPENAI_API_KEY: keyName === "OPENAI_API_KEY" ? projectApiKeyStatus : "user"
|
|
50851
|
+
},
|
|
50852
|
+
gitignore: "unknown"
|
|
50853
|
+
});
|
|
50854
|
+
const userStatus = await this.getUserStatus();
|
|
50855
|
+
if (!userStatus) {
|
|
50856
|
+
await this.writeUserStatus({
|
|
50857
|
+
version: 1,
|
|
50858
|
+
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
50859
|
+
preferences: {
|
|
50860
|
+
autoConfigureProjects: false,
|
|
50861
|
+
defaultStatuslineScope: "user",
|
|
50862
|
+
defaultApiKeyScope: "user"
|
|
50863
|
+
},
|
|
50864
|
+
statusline: actualStatusline,
|
|
50865
|
+
apiKeys: {
|
|
50866
|
+
OPENROUTER_API_KEY: keyName === "OPENROUTER_API_KEY" ? userApiKeyStatus : "missing",
|
|
50867
|
+
OPENAI_API_KEY: keyName === "OPENAI_API_KEY" ? userApiKeyStatus : "not-required"
|
|
50868
|
+
}
|
|
50869
|
+
});
|
|
50870
|
+
}
|
|
50597
50871
|
}
|
|
50598
50872
|
}
|
|
50599
50873
|
apiKeyResults[keyName] = {
|
|
50600
50874
|
actual: actualHealth,
|
|
50601
50875
|
cached: cachedHealth,
|
|
50602
|
-
fixed: keyFixed
|
|
50876
|
+
fixed: keyFixed,
|
|
50877
|
+
source,
|
|
50878
|
+
used: projectApiKeyStatus.used,
|
|
50879
|
+
scopes: projectApiKeyStatus.scopes
|
|
50603
50880
|
};
|
|
50604
50881
|
}
|
|
50605
|
-
const isStatuslineHealthy = actualStatusline
|
|
50882
|
+
const isStatuslineHealthy = actualStatusline !== "none";
|
|
50606
50883
|
const openRouterActual = apiKeyResults.OPENROUTER_API_KEY.actual;
|
|
50607
50884
|
const openRouterCached = apiKeyResults.OPENROUTER_API_KEY.cached;
|
|
50608
50885
|
const isApiKeyHealthy = openRouterActual !== "missing" || openRouterCached === "not-required";
|
|
@@ -66751,6 +67028,25 @@ var require_hook_command = __commonJS({
|
|
|
66751
67028
|
}
|
|
66752
67029
|
};
|
|
66753
67030
|
var VERBOSE_DEGRADED_HOOKS = /* @__PURE__ */ new Set(["SessionStart", "UserPromptSubmit"]);
|
|
67031
|
+
async function maybeAutoConfigureProject(projectRoot, logger) {
|
|
67032
|
+
try {
|
|
67033
|
+
const setupService = new core_1.SetupStatusService(projectRoot, { logger });
|
|
67034
|
+
const shouldAuto = await setupService.shouldAutoConfigureProject();
|
|
67035
|
+
if (!shouldAuto)
|
|
67036
|
+
return false;
|
|
67037
|
+
const configured = await setupService.autoConfigureProject();
|
|
67038
|
+
if (configured) {
|
|
67039
|
+
logger.info("Project auto-configured on SessionStart", { projectRoot });
|
|
67040
|
+
}
|
|
67041
|
+
return configured;
|
|
67042
|
+
} catch (err) {
|
|
67043
|
+
logger.warn("Failed to auto-configure project", {
|
|
67044
|
+
error: err instanceof Error ? err.message : String(err),
|
|
67045
|
+
projectRoot
|
|
67046
|
+
});
|
|
67047
|
+
return false;
|
|
67048
|
+
}
|
|
67049
|
+
}
|
|
66754
67050
|
async function checkSetupState(projectRoot, hookName, logger) {
|
|
66755
67051
|
let state;
|
|
66756
67052
|
try {
|
|
@@ -66818,6 +67114,9 @@ var require_hook_command = __commonJS({
|
|
|
66818
67114
|
});
|
|
66819
67115
|
}
|
|
66820
67116
|
}
|
|
67117
|
+
if (hookName === "SessionStart") {
|
|
67118
|
+
await maybeAutoConfigureProject(projectRoot, logger);
|
|
67119
|
+
}
|
|
66821
67120
|
const degradedResponse = await checkSetupState(projectRoot, hookName, logger);
|
|
66822
67121
|
if (degradedResponse !== null) {
|
|
66823
67122
|
const claudeResponse2 = translateToClaudeCodeFormat(hookName, degradedResponse);
|
|
@@ -67223,7 +67522,9 @@ var require_types4 = __commonJS({
|
|
|
67223
67522
|
includeAssistantThinking: false,
|
|
67224
67523
|
keepHistory: false,
|
|
67225
67524
|
maxTitleWords: 8,
|
|
67226
|
-
maxIntentWords:
|
|
67525
|
+
maxIntentWords: 15,
|
|
67526
|
+
maxSnarkyWords: 20,
|
|
67527
|
+
maxResumeWords: 20,
|
|
67227
67528
|
snarkyMessages: true,
|
|
67228
67529
|
countdown: {
|
|
67229
67530
|
lowConfidence: 5,
|
|
@@ -67603,7 +67904,7 @@ var require_update_summary = __commonJS({
|
|
|
67603
67904
|
await summaryState.summaryCountdown.write(sessionId, state);
|
|
67604
67905
|
}
|
|
67605
67906
|
function interpolatePrompt(template, context) {
|
|
67606
|
-
return template.replace(/\{\{transcript\}\}/g, context.transcript).replace(/\{\{previousConfidence\}\}/g, String(context.previousConfidence)).replace(/\{\{previousAnalysis\}\}/g, context.previousAnalysis);
|
|
67907
|
+
return template.replace(/\{\{transcript\}\}/g, context.transcript).replace(/\{\{previousConfidence\}\}/g, String(context.previousConfidence)).replace(/\{\{previousAnalysis\}\}/g, context.previousAnalysis).replace(/\{\{maxTitleWords\}\}/g, String(context.maxTitleWords)).replace(/\{\{maxIntentWords\}\}/g, String(context.maxIntentWords));
|
|
67607
67908
|
}
|
|
67608
67909
|
function parseResponse(content) {
|
|
67609
67910
|
try {
|
|
@@ -67645,7 +67946,9 @@ var require_update_summary = __commonJS({
|
|
|
67645
67946
|
const prompt = interpolatePrompt(promptTemplate, {
|
|
67646
67947
|
transcript,
|
|
67647
67948
|
previousConfidence,
|
|
67648
|
-
previousAnalysis
|
|
67949
|
+
previousAnalysis,
|
|
67950
|
+
maxTitleWords: config.maxTitleWords,
|
|
67951
|
+
maxIntentWords: config.maxIntentWords
|
|
67649
67952
|
});
|
|
67650
67953
|
const schemaContent = ctx.assets.resolve(SESSION_SUMMARY_SCHEMA_FILE);
|
|
67651
67954
|
const jsonSchema = schemaContent ? {
|
|
@@ -67777,7 +68080,8 @@ var require_update_summary = __commonJS({
|
|
|
67777
68080
|
latest_intent: summary.latest_intent,
|
|
67778
68081
|
turn_count: ctx.transcript.getMetrics().turnCount,
|
|
67779
68082
|
tool_count: ctx.transcript.getMetrics().toolCount,
|
|
67780
|
-
sessionSummary: JSON.stringify(summary, null, 2)
|
|
68083
|
+
sessionSummary: JSON.stringify(summary, null, 2),
|
|
68084
|
+
maxSnarkyWords: config.maxSnarkyWords
|
|
67781
68085
|
});
|
|
67782
68086
|
const llmConfig = config.llm?.snarkyComment ?? types_js_1.DEFAULT_SESSION_SUMMARY_CONFIG.llm.snarkyComment;
|
|
67783
68087
|
const provider = ctx.profileFactory.createForProfile(llmConfig.profile, llmConfig.fallbackProfile);
|
|
@@ -67843,7 +68147,8 @@ var require_update_summary = __commonJS({
|
|
|
67843
68147
|
confidence: summary.session_title_confidence,
|
|
67844
68148
|
latestIntent: summary.latest_intent,
|
|
67845
68149
|
keyPhrases,
|
|
67846
|
-
transcript: transcriptExcerpt
|
|
68150
|
+
transcript: transcriptExcerpt,
|
|
68151
|
+
maxResumeWords: config.maxResumeWords
|
|
67847
68152
|
});
|
|
67848
68153
|
const llmConfig = config.llm?.resumeMessage ?? types_js_1.DEFAULT_SESSION_SUMMARY_CONFIG.llm.resumeMessage;
|
|
67849
68154
|
const provider = ctx.profileFactory.createForProfile(llmConfig.profile, llmConfig.fallbackProfile);
|
|
@@ -67943,16 +68248,17 @@ var require_on_demand_generation = __commonJS({
|
|
|
67943
68248
|
};
|
|
67944
68249
|
}
|
|
67945
68250
|
const personaContext = (0, persona_utils_js_1.buildPersonaContext)(persona);
|
|
68251
|
+
const featureConfig = ctx.config.getFeature("session-summary");
|
|
68252
|
+
const config = { ...types_js_1.DEFAULT_SESSION_SUMMARY_CONFIG, ...featureConfig.settings };
|
|
67946
68253
|
const prompt = (0, update_summary_js_1.interpolateTemplate)(promptTemplate, {
|
|
67947
68254
|
...personaContext,
|
|
67948
68255
|
session_title: summary.session_title,
|
|
67949
68256
|
latest_intent: summary.latest_intent,
|
|
67950
68257
|
turn_count: ctx.transcript.getMetrics().turnCount,
|
|
67951
68258
|
tool_count: ctx.transcript.getMetrics().toolCount,
|
|
67952
|
-
sessionSummary: JSON.stringify(summary, null, 2)
|
|
68259
|
+
sessionSummary: JSON.stringify(summary, null, 2),
|
|
68260
|
+
maxSnarkyWords: config.maxSnarkyWords
|
|
67953
68261
|
});
|
|
67954
|
-
const featureConfig = ctx.config.getFeature("session-summary");
|
|
67955
|
-
const config = { ...types_js_1.DEFAULT_SESSION_SUMMARY_CONFIG, ...featureConfig.settings };
|
|
67956
68262
|
const llmConfig = config.llm?.snarkyComment ?? types_js_1.DEFAULT_SESSION_SUMMARY_CONFIG.llm.snarkyComment;
|
|
67957
68263
|
const provider = ctx.profileFactory.createForProfile(llmConfig.profile, llmConfig.fallbackProfile);
|
|
67958
68264
|
try {
|
|
@@ -68015,6 +68321,8 @@ var require_on_demand_generation = __commonJS({
|
|
|
68015
68321
|
};
|
|
68016
68322
|
}
|
|
68017
68323
|
const personaContext = (0, persona_utils_js_1.buildPersonaContext)(persona);
|
|
68324
|
+
const featureConfig = ctx.config.getFeature("session-summary");
|
|
68325
|
+
const config = { ...types_js_1.DEFAULT_SESSION_SUMMARY_CONFIG, ...featureConfig.settings };
|
|
68018
68326
|
const excerpt = ctx.transcript.getExcerpt({
|
|
68019
68327
|
maxLines: 50,
|
|
68020
68328
|
includeToolMessages: true,
|
|
@@ -68028,10 +68336,9 @@ var require_on_demand_generation = __commonJS({
|
|
|
68028
68336
|
confidence: summary.session_title_confidence,
|
|
68029
68337
|
latestIntent: summary.latest_intent,
|
|
68030
68338
|
keyPhrases,
|
|
68031
|
-
transcript: excerpt.content
|
|
68339
|
+
transcript: excerpt.content,
|
|
68340
|
+
maxResumeWords: config.maxResumeWords
|
|
68032
68341
|
});
|
|
68033
|
-
const featureConfig = ctx.config.getFeature("session-summary");
|
|
68034
|
-
const config = { ...types_js_1.DEFAULT_SESSION_SUMMARY_CONFIG, ...featureConfig.settings };
|
|
68035
68342
|
const llmConfig = config.llm?.resumeMessage ?? types_js_1.DEFAULT_SESSION_SUMMARY_CONFIG.llm.resumeMessage;
|
|
68036
68343
|
const provider = ctx.profileFactory.createForProfile(llmConfig.profile, llmConfig.fallbackProfile);
|
|
68037
68344
|
try {
|
|
@@ -70922,6 +71229,11 @@ var require_dev_mode = __commonJS({
|
|
|
70922
71229
|
log(stdout, "info", "No sessions directory found");
|
|
70923
71230
|
}
|
|
70924
71231
|
await removeDirectory(node_path_1.default.join(sidekickDir, "state"), "state", stdout);
|
|
71232
|
+
const setupStatusPath = node_path_1.default.join(sidekickDir, "setup-status.json");
|
|
71233
|
+
if (await fileExists(setupStatusPath)) {
|
|
71234
|
+
await (0, promises_12.unlink)(setupStatusPath);
|
|
71235
|
+
log(stdout, "info", `Removed ${setupStatusPath}`);
|
|
71236
|
+
}
|
|
70925
71237
|
const tmpDir = node_os_1.default.tmpdir();
|
|
70926
71238
|
try {
|
|
70927
71239
|
const files = await (0, promises_12.readdir)(tmpDir);
|
|
@@ -71051,7 +71363,7 @@ var require_prompts = __commonJS({
|
|
|
71051
71363
|
ctx.stdout.write(`${colorMap[type]}${icons[type]}${colors.reset} ${message}
|
|
71052
71364
|
`);
|
|
71053
71365
|
}
|
|
71054
|
-
async function promptSelect(ctx, question, options) {
|
|
71366
|
+
async function promptSelect(ctx, question, options, defaultIndex = 0) {
|
|
71055
71367
|
ctx.stdout.write(`${question}
|
|
71056
71368
|
|
|
71057
71369
|
`);
|
|
@@ -71069,17 +71381,30 @@ var require_prompts = __commonJS({
|
|
|
71069
71381
|
output: ctx.stdout,
|
|
71070
71382
|
terminal: false
|
|
71071
71383
|
});
|
|
71384
|
+
const defaultNum = defaultIndex + 1;
|
|
71385
|
+
const prompt = `Enter choice (1-${options.length}) [${defaultNum}]: `;
|
|
71072
71386
|
return new Promise((resolve3) => {
|
|
71073
|
-
|
|
71074
|
-
|
|
71075
|
-
rl.
|
|
71076
|
-
|
|
71077
|
-
|
|
71078
|
-
|
|
71079
|
-
|
|
71080
|
-
|
|
71081
|
-
|
|
71082
|
-
|
|
71387
|
+
const ask = () => {
|
|
71388
|
+
ctx.stdout.write(prompt);
|
|
71389
|
+
rl.once("line", (answer) => {
|
|
71390
|
+
const trimmed = answer.trim();
|
|
71391
|
+
if (trimmed === "") {
|
|
71392
|
+
rl.close();
|
|
71393
|
+
resolve3(options[defaultIndex].value);
|
|
71394
|
+
} else {
|
|
71395
|
+
const num = parseInt(trimmed, 10);
|
|
71396
|
+
if (num >= 1 && num <= options.length) {
|
|
71397
|
+
rl.close();
|
|
71398
|
+
resolve3(options[num - 1].value);
|
|
71399
|
+
} else {
|
|
71400
|
+
ctx.stdout.write(`${colors.yellow}Invalid choice. Enter a number between 1 and ${options.length}.${colors.reset}
|
|
71401
|
+
`);
|
|
71402
|
+
ask();
|
|
71403
|
+
}
|
|
71404
|
+
}
|
|
71405
|
+
});
|
|
71406
|
+
};
|
|
71407
|
+
ask();
|
|
71083
71408
|
});
|
|
71084
71409
|
}
|
|
71085
71410
|
async function promptConfirm(ctx, question, defaultYes = true) {
|
|
@@ -71089,17 +71414,29 @@ var require_prompts = __commonJS({
|
|
|
71089
71414
|
output: ctx.stdout,
|
|
71090
71415
|
terminal: false
|
|
71091
71416
|
});
|
|
71417
|
+
const prompt = `${question} ${hint} `;
|
|
71092
71418
|
return new Promise((resolve3) => {
|
|
71093
|
-
|
|
71094
|
-
|
|
71095
|
-
rl.
|
|
71096
|
-
|
|
71097
|
-
|
|
71098
|
-
|
|
71099
|
-
|
|
71100
|
-
|
|
71101
|
-
|
|
71102
|
-
|
|
71419
|
+
const ask = () => {
|
|
71420
|
+
ctx.stdout.write(prompt);
|
|
71421
|
+
rl.once("line", (answer) => {
|
|
71422
|
+
const normalized = answer.trim().toLowerCase();
|
|
71423
|
+
if (normalized === "") {
|
|
71424
|
+
rl.close();
|
|
71425
|
+
resolve3(defaultYes);
|
|
71426
|
+
} else if (normalized === "y" || normalized === "yes") {
|
|
71427
|
+
rl.close();
|
|
71428
|
+
resolve3(true);
|
|
71429
|
+
} else if (normalized === "n" || normalized === "no") {
|
|
71430
|
+
rl.close();
|
|
71431
|
+
resolve3(false);
|
|
71432
|
+
} else {
|
|
71433
|
+
ctx.stdout.write(`${colors.yellow}Please enter y or n.${colors.reset}
|
|
71434
|
+
`);
|
|
71435
|
+
ask();
|
|
71436
|
+
}
|
|
71437
|
+
});
|
|
71438
|
+
};
|
|
71439
|
+
ask();
|
|
71103
71440
|
});
|
|
71104
71441
|
}
|
|
71105
71442
|
async function promptInput(ctx, question) {
|
|
@@ -71247,6 +71584,12 @@ Examples:
|
|
|
71247
71584
|
return "check failed";
|
|
71248
71585
|
}
|
|
71249
71586
|
}
|
|
71587
|
+
function getScopeIcon(status) {
|
|
71588
|
+
return status === "healthy" ? "\u2713" : "\u2717";
|
|
71589
|
+
}
|
|
71590
|
+
function formatApiKeyScopes(scopes) {
|
|
71591
|
+
return `[project ${getScopeIcon(scopes.project)} user ${getScopeIcon(scopes.user)} env ${getScopeIcon(scopes.env)}]`;
|
|
71592
|
+
}
|
|
71250
71593
|
async function configureStatusline(settingsPath, logger) {
|
|
71251
71594
|
let settings = {};
|
|
71252
71595
|
try {
|
|
@@ -71286,27 +71629,6 @@ Examples:
|
|
|
71286
71629
|
}
|
|
71287
71630
|
await fs.writeFile(envPath, content);
|
|
71288
71631
|
}
|
|
71289
|
-
async function findExistingApiKey(keyName, homeDir, projectDir) {
|
|
71290
|
-
if (process.env[keyName]) {
|
|
71291
|
-
return process.env[keyName];
|
|
71292
|
-
}
|
|
71293
|
-
const envPaths = [
|
|
71294
|
-
path.join(homeDir, ".sidekick", ".env"),
|
|
71295
|
-
path.join(projectDir, ".sidekick", ".env"),
|
|
71296
|
-
path.join(projectDir, ".sidekick", ".env.local")
|
|
71297
|
-
];
|
|
71298
|
-
for (const envPath of envPaths) {
|
|
71299
|
-
try {
|
|
71300
|
-
const content = await fs.readFile(envPath, "utf-8");
|
|
71301
|
-
const match = content.match(new RegExp(`^${keyName}=(.+)$`, "m"));
|
|
71302
|
-
if (match) {
|
|
71303
|
-
return match[1];
|
|
71304
|
-
}
|
|
71305
|
-
} catch {
|
|
71306
|
-
}
|
|
71307
|
-
}
|
|
71308
|
-
return null;
|
|
71309
|
-
}
|
|
71310
71632
|
async function writePersonaConfig(_projectDir, homeDir, enabled) {
|
|
71311
71633
|
const configDir = path.join(homeDir, ".sidekick");
|
|
71312
71634
|
const featuresPath = path.join(configDir, "features.yaml");
|
|
@@ -71389,39 +71711,52 @@ Examples:
|
|
|
71389
71711
|
stdout.write("These require an OpenRouter API key (small cost per message).\n\n");
|
|
71390
71712
|
const wantPersonas = await (0, prompts_js_1.promptConfirm)(ctx, "Enable persona features?", true);
|
|
71391
71713
|
let apiKeyHealth = "not-required";
|
|
71714
|
+
let apiKeyDetection = null;
|
|
71392
71715
|
if (!wantPersonas) {
|
|
71393
71716
|
await writePersonaConfig(projectDir, homeDir, false);
|
|
71394
71717
|
(0, prompts_js_1.printStatus)(ctx, "info", "Personas disabled");
|
|
71395
71718
|
} else {
|
|
71396
71719
|
await writePersonaConfig(projectDir, homeDir, true);
|
|
71397
|
-
|
|
71720
|
+
const result = await configureApiKey(wctx);
|
|
71721
|
+
apiKeyHealth = result.health;
|
|
71722
|
+
apiKeyDetection = result.detection;
|
|
71398
71723
|
}
|
|
71399
|
-
return { wantPersonas, apiKeyHealth };
|
|
71724
|
+
return { wantPersonas, apiKeyHealth, apiKeyDetection };
|
|
71400
71725
|
}
|
|
71401
71726
|
async function configureApiKey(wctx) {
|
|
71402
|
-
const { ctx, homeDir, projectDir, logger } = wctx;
|
|
71727
|
+
const { ctx, homeDir, projectDir, logger, setupService } = wctx;
|
|
71403
71728
|
const stdout = ctx.stdout;
|
|
71404
|
-
|
|
71405
|
-
|
|
71406
|
-
|
|
71407
|
-
|
|
71408
|
-
|
|
71409
|
-
|
|
71410
|
-
|
|
71411
|
-
|
|
71412
|
-
|
|
71413
|
-
|
|
71729
|
+
stdout.write("\nChecking API keys...\n");
|
|
71730
|
+
const detection = await setupService.detectAllApiKeys("OPENROUTER_API_KEY");
|
|
71731
|
+
const scopeLabels = {
|
|
71732
|
+
project: "project (.sidekick/.env)",
|
|
71733
|
+
user: "user (~/.sidekick/.env)",
|
|
71734
|
+
env: "env (OPENROUTER_API_KEY)"
|
|
71735
|
+
};
|
|
71736
|
+
for (const scope of ["project", "user", "env"]) {
|
|
71737
|
+
const r = detection[scope];
|
|
71738
|
+
const icon = r.found ? r.status === "healthy" ? "\u2713" : "\u2717" : "-";
|
|
71739
|
+
const label = r.found ? r.status : "not found";
|
|
71740
|
+
stdout.write(` ${icon} ${scopeLabels[scope]}: ${label}
|
|
71414
71741
|
`);
|
|
71415
|
-
return "invalid";
|
|
71416
|
-
}
|
|
71417
71742
|
}
|
|
71418
|
-
|
|
71419
|
-
|
|
71743
|
+
const healthyScope = ["project", "user", "env"].find((s) => detection[s].status === "healthy");
|
|
71744
|
+
if (healthyScope) {
|
|
71745
|
+
(0, prompts_js_1.printStatus)(ctx, "success", `OpenRouter API Key: healthy (using ${healthyScope})`);
|
|
71746
|
+
return { health: "healthy", detection };
|
|
71747
|
+
}
|
|
71748
|
+
const invalidScope = ["project", "user", "env"].find((s) => detection[s].found);
|
|
71749
|
+
if (invalidScope) {
|
|
71750
|
+
(0, prompts_js_1.printStatus)(ctx, "warning", "API key found but validation failed");
|
|
71751
|
+
} else {
|
|
71752
|
+
(0, prompts_js_1.printStatus)(ctx, "warning", "OPENROUTER_API_KEY not found");
|
|
71753
|
+
}
|
|
71754
|
+
const configureNow = await (0, prompts_js_1.promptConfirm)(ctx, invalidScope ? "Replace API key?" : "Configure API key now?", true);
|
|
71420
71755
|
if (!configureNow) {
|
|
71421
71756
|
stdout.write("\n");
|
|
71422
71757
|
(0, prompts_js_1.printStatus)(ctx, "warning", "Persona features will show warnings in the statusline until an API key is configured.");
|
|
71423
71758
|
stdout.write("Run 'sidekick setup' again or ask Claude to help configure API keys using /sidekick-config.\n");
|
|
71424
|
-
return "missing";
|
|
71759
|
+
return { health: "missing", detection };
|
|
71425
71760
|
}
|
|
71426
71761
|
const keyScope = await (0, prompts_js_1.promptSelect)(ctx, "Where should the API key be stored?", [
|
|
71427
71762
|
{ value: "user", label: "User-level (~/.sidekick/.env)", description: "Works in all projects" },
|
|
@@ -71435,14 +71770,14 @@ Examples:
|
|
|
71435
71770
|
stdout.write("valid!\n");
|
|
71436
71771
|
await writeApiKeyToEnv(envPath, "OPENROUTER_API_KEY", apiKey);
|
|
71437
71772
|
(0, prompts_js_1.printStatus)(ctx, "success", `API key saved to ${envPath}`);
|
|
71438
|
-
return "healthy";
|
|
71439
71773
|
} else {
|
|
71440
71774
|
stdout.write(`invalid (${result.error})
|
|
71441
71775
|
`);
|
|
71442
71776
|
(0, prompts_js_1.printStatus)(ctx, "warning", "API key validation failed, saving anyway");
|
|
71443
71777
|
await writeApiKeyToEnv(envPath, "OPENROUTER_API_KEY", apiKey);
|
|
71444
|
-
return "invalid";
|
|
71445
71778
|
}
|
|
71779
|
+
const postDetection = await setupService.detectAllApiKeys("OPENROUTER_API_KEY");
|
|
71780
|
+
return { health: result.valid ? "healthy" : "invalid", detection: postDetection };
|
|
71446
71781
|
}
|
|
71447
71782
|
async function runStep4AutoConfig(wctx) {
|
|
71448
71783
|
const { ctx } = wctx;
|
|
@@ -71456,7 +71791,7 @@ Examples:
|
|
|
71456
71791
|
}
|
|
71457
71792
|
async function writeStatusFiles(wctx, state) {
|
|
71458
71793
|
const { setupService } = wctx;
|
|
71459
|
-
const { statuslineScope, gitignoreStatus, wantPersonas, apiKeyHealth, autoConfig } = state;
|
|
71794
|
+
const { statuslineScope, gitignoreStatus, wantPersonas, apiKeyHealth, apiKeyDetection, autoConfig } = state;
|
|
71460
71795
|
const userStatus = {
|
|
71461
71796
|
version: 1,
|
|
71462
71797
|
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -71465,9 +71800,9 @@ Examples:
|
|
|
71465
71800
|
defaultStatuslineScope: statuslineScope,
|
|
71466
71801
|
defaultApiKeyScope: wantPersonas ? "user" : "skip"
|
|
71467
71802
|
},
|
|
71468
|
-
statusline:
|
|
71803
|
+
statusline: statuslineScope,
|
|
71469
71804
|
apiKeys: {
|
|
71470
|
-
OPENROUTER_API_KEY: apiKeyHealth,
|
|
71805
|
+
OPENROUTER_API_KEY: apiKeyDetection ? setupService.buildUserApiKeyStatus(apiKeyDetection) : apiKeyHealth,
|
|
71471
71806
|
OPENAI_API_KEY: "not-required"
|
|
71472
71807
|
}
|
|
71473
71808
|
};
|
|
@@ -71476,9 +71811,9 @@ Examples:
|
|
|
71476
71811
|
version: 1,
|
|
71477
71812
|
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
71478
71813
|
autoConfigured: false,
|
|
71479
|
-
statusline: statuslineScope
|
|
71814
|
+
statusline: statuslineScope,
|
|
71480
71815
|
apiKeys: {
|
|
71481
|
-
OPENROUTER_API_KEY: apiKeyHealth === "healthy" ? "user" : apiKeyHealth,
|
|
71816
|
+
OPENROUTER_API_KEY: apiKeyDetection ? setupService.buildProjectApiKeyStatus(apiKeyDetection) : apiKeyHealth === "healthy" ? "user" : apiKeyHealth,
|
|
71482
71817
|
OPENAI_API_KEY: "not-required"
|
|
71483
71818
|
},
|
|
71484
71819
|
gitignore: gitignoreStatus
|
|
@@ -71497,6 +71832,15 @@ Examples:
|
|
|
71497
71832
|
(0, prompts_js_1.printStatus)(ctx, wantPersonas ? "success" : "info", `Personas: ${wantPersonas ? "Enabled" : "Disabled"}`);
|
|
71498
71833
|
const apiKeyStatusType = getApiKeyStatusType(apiKeyHealth);
|
|
71499
71834
|
(0, prompts_js_1.printStatus)(ctx, apiKeyStatusType, `API Key: ${apiKeyHealth}`);
|
|
71835
|
+
if (state.apiKeyDetection) {
|
|
71836
|
+
const scopes = {
|
|
71837
|
+
project: state.apiKeyDetection.project.status,
|
|
71838
|
+
user: state.apiKeyDetection.user.status,
|
|
71839
|
+
env: state.apiKeyDetection.env.status
|
|
71840
|
+
};
|
|
71841
|
+
stdout.write(` ${formatApiKeyScopes(scopes)}
|
|
71842
|
+
`);
|
|
71843
|
+
}
|
|
71500
71844
|
(0, prompts_js_1.printStatus)(ctx, "success", `Auto-configure: ${autoConfig === "auto" ? "Enabled" : "Disabled"}`);
|
|
71501
71845
|
stdout.write("\n");
|
|
71502
71846
|
stdout.write("Restart Claude Code to see your statusline: claude --continue\n");
|
|
@@ -71519,13 +71863,20 @@ Examples:
|
|
|
71519
71863
|
}
|
|
71520
71864
|
const statuslineScope = force ? "user" : await runStep1Statusline(wctx);
|
|
71521
71865
|
const gitignoreStatus = await runStep2Gitignore(wctx, force);
|
|
71522
|
-
const { wantPersonas, apiKeyHealth } = force ? { wantPersonas: true, apiKeyHealth: "not-required" } : await runStep3Personas(wctx);
|
|
71866
|
+
const { wantPersonas, apiKeyHealth, apiKeyDetection } = force ? { wantPersonas: true, apiKeyHealth: "not-required", apiKeyDetection: null } : await runStep3Personas(wctx);
|
|
71523
71867
|
const autoConfig = force ? "auto" : await runStep4AutoConfig(wctx);
|
|
71524
71868
|
if (force) {
|
|
71525
71869
|
const statuslinePath = path.join(homeDir, ".claude", "settings.json");
|
|
71526
71870
|
await configureStatusline(statuslinePath, logger);
|
|
71527
71871
|
}
|
|
71528
|
-
const state = {
|
|
71872
|
+
const state = {
|
|
71873
|
+
statuslineScope,
|
|
71874
|
+
gitignoreStatus,
|
|
71875
|
+
wantPersonas,
|
|
71876
|
+
apiKeyHealth,
|
|
71877
|
+
apiKeyDetection,
|
|
71878
|
+
autoConfig
|
|
71879
|
+
};
|
|
71529
71880
|
await writeStatusFiles(wctx, state);
|
|
71530
71881
|
if (!force) {
|
|
71531
71882
|
printSummary(wctx, state);
|
|
@@ -71609,8 +71960,8 @@ Examples:
|
|
|
71609
71960
|
defaultStatuslineScope: "user",
|
|
71610
71961
|
defaultApiKeyScope: "user"
|
|
71611
71962
|
},
|
|
71612
|
-
statusline: "
|
|
71613
|
-
// Default to
|
|
71963
|
+
statusline: "none",
|
|
71964
|
+
// Default to none if no prior setup
|
|
71614
71965
|
apiKeys: {
|
|
71615
71966
|
OPENROUTER_API_KEY: "not-required",
|
|
71616
71967
|
OPENAI_API_KEY: "not-required"
|
|
@@ -71655,10 +72006,12 @@ Configured ${configuredCount} setting${configuredCount === 1 ? "" : "s"}.
|
|
|
71655
72006
|
const pluginLabel = getPluginStatusLabel(pluginStatus);
|
|
71656
72007
|
const livenessIcon = getLivenessIcon(liveness);
|
|
71657
72008
|
const livenessLabel = getLivenessLabel(liveness);
|
|
71658
|
-
const statuslineIcon = doctorResult.statusline.actual
|
|
72009
|
+
const statuslineIcon = doctorResult.statusline.actual !== "none" ? "\u2713" : "\u26A0";
|
|
71659
72010
|
const gitignoreIcon = gitignore === "installed" ? "\u2713" : "\u26A0";
|
|
71660
|
-
const
|
|
72011
|
+
const openRouterResult = doctorResult.apiKeys.OPENROUTER_API_KEY;
|
|
72012
|
+
const openRouterHealth = openRouterResult.actual;
|
|
71661
72013
|
const apiKeyIcon = openRouterHealth === "healthy" || openRouterHealth === "not-required" ? "\u2713" : "\u26A0";
|
|
72014
|
+
const scopeBreakdown = formatApiKeyScopes(openRouterResult.scopes);
|
|
71662
72015
|
stdout.write("\n");
|
|
71663
72016
|
stdout.write(`${pluginIcon} Plugin: ${pluginLabel}
|
|
71664
72017
|
`);
|
|
@@ -71668,7 +72021,7 @@ Configured ${configuredCount} setting${configuredCount === 1 ? "" : "s"}.
|
|
|
71668
72021
|
`);
|
|
71669
72022
|
stdout.write(`${gitignoreIcon} Gitignore: ${gitignore}
|
|
71670
72023
|
`);
|
|
71671
|
-
stdout.write(`${apiKeyIcon} OpenRouter API Key: ${openRouterHealth}
|
|
72024
|
+
stdout.write(`${apiKeyIcon} OpenRouter API Key: ${openRouterHealth} ${scopeBreakdown}
|
|
71672
72025
|
`);
|
|
71673
72026
|
const isPluginOk = pluginStatus === "plugin" || pluginStatus === "dev-mode";
|
|
71674
72027
|
const isPluginLive = liveness === "active";
|
|
@@ -71764,7 +72117,7 @@ var require_cli = __commonJS({
|
|
|
71764
72117
|
var promises_12 = require("node:fs/promises");
|
|
71765
72118
|
var node_stream_1 = require("node:stream");
|
|
71766
72119
|
var yargs_parser_1 = __importDefault2(require_build());
|
|
71767
|
-
var VERSION = true ? "0.0.8-alpha.
|
|
72120
|
+
var VERSION = true ? "0.0.8-alpha.1" : "dev";
|
|
71768
72121
|
function isInSandbox() {
|
|
71769
72122
|
return process.env.SANDBOX_RUNTIME === "1";
|
|
71770
72123
|
}
|