@scotthamilton77/sidekick 0.0.2-alpha → 0.0.5-alpha
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 +1234 -16
- package/dist/daemon.js +70160 -0
- package/package.json +2 -5
package/dist/bin.js
CHANGED
|
@@ -16699,6 +16699,72 @@ var require_hook_input = __commonJS({
|
|
|
16699
16699
|
}
|
|
16700
16700
|
});
|
|
16701
16701
|
|
|
16702
|
+
// ../types/dist/setup-status.js
|
|
16703
|
+
var require_setup_status = __commonJS({
|
|
16704
|
+
"../types/dist/setup-status.js"(exports2) {
|
|
16705
|
+
"use strict";
|
|
16706
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
16707
|
+
exports2.ProjectSetupStatusSchema = exports2.GitignoreStatusSchema = exports2.UserSetupStatusSchema = exports2.ProjectApiKeyHealthSchema = exports2.ApiKeyHealthSchema = void 0;
|
|
16708
|
+
var zod_1 = require_zod();
|
|
16709
|
+
exports2.ApiKeyHealthSchema = zod_1.z.enum([
|
|
16710
|
+
"missing",
|
|
16711
|
+
// Key needed but not found
|
|
16712
|
+
"not-required",
|
|
16713
|
+
// No LLM profiles configured for provider
|
|
16714
|
+
"pending-validation",
|
|
16715
|
+
// Key exists but not validated
|
|
16716
|
+
"invalid",
|
|
16717
|
+
// Validation failed
|
|
16718
|
+
"healthy"
|
|
16719
|
+
// Validation succeeded
|
|
16720
|
+
]);
|
|
16721
|
+
exports2.ProjectApiKeyHealthSchema = zod_1.z.enum([
|
|
16722
|
+
"missing",
|
|
16723
|
+
"not-required",
|
|
16724
|
+
"pending-validation",
|
|
16725
|
+
"invalid",
|
|
16726
|
+
"healthy",
|
|
16727
|
+
"user"
|
|
16728
|
+
// Deferring to user-level
|
|
16729
|
+
]);
|
|
16730
|
+
exports2.UserSetupStatusSchema = zod_1.z.object({
|
|
16731
|
+
version: zod_1.z.literal(1),
|
|
16732
|
+
lastUpdatedAt: zod_1.z.string(),
|
|
16733
|
+
// ISO timestamp
|
|
16734
|
+
preferences: zod_1.z.object({
|
|
16735
|
+
autoConfigureProjects: zod_1.z.boolean(),
|
|
16736
|
+
defaultStatuslineScope: zod_1.z.enum(["user", "project"]),
|
|
16737
|
+
defaultApiKeyScope: zod_1.z.enum(["user", "project", "skip"])
|
|
16738
|
+
}),
|
|
16739
|
+
statusline: zod_1.z.enum(["configured", "skipped"]),
|
|
16740
|
+
apiKeys: zod_1.z.object({
|
|
16741
|
+
OPENROUTER_API_KEY: exports2.ApiKeyHealthSchema,
|
|
16742
|
+
OPENAI_API_KEY: exports2.ApiKeyHealthSchema
|
|
16743
|
+
})
|
|
16744
|
+
});
|
|
16745
|
+
exports2.GitignoreStatusSchema = zod_1.z.enum([
|
|
16746
|
+
"unknown",
|
|
16747
|
+
// Setup hasn't checked yet (legacy projects)
|
|
16748
|
+
"missing",
|
|
16749
|
+
// User declined or entries not present
|
|
16750
|
+
"installed"
|
|
16751
|
+
// Sidekick section present in .gitignore
|
|
16752
|
+
]);
|
|
16753
|
+
exports2.ProjectSetupStatusSchema = zod_1.z.object({
|
|
16754
|
+
version: zod_1.z.literal(1),
|
|
16755
|
+
lastUpdatedAt: zod_1.z.string(),
|
|
16756
|
+
// ISO timestamp
|
|
16757
|
+
autoConfigured: zod_1.z.boolean(),
|
|
16758
|
+
statusline: zod_1.z.enum(["configured", "skipped", "user"]),
|
|
16759
|
+
apiKeys: zod_1.z.object({
|
|
16760
|
+
OPENROUTER_API_KEY: exports2.ProjectApiKeyHealthSchema,
|
|
16761
|
+
OPENAI_API_KEY: exports2.ProjectApiKeyHealthSchema
|
|
16762
|
+
}),
|
|
16763
|
+
gitignore: exports2.GitignoreStatusSchema.optional().default("unknown")
|
|
16764
|
+
});
|
|
16765
|
+
}
|
|
16766
|
+
});
|
|
16767
|
+
|
|
16702
16768
|
// ../types/dist/index.js
|
|
16703
16769
|
var require_dist = __commonJS({
|
|
16704
16770
|
"../types/dist/index.js"(exports2) {
|
|
@@ -16729,6 +16795,7 @@ var require_dist = __commonJS({
|
|
|
16729
16795
|
__exportStar(require_context(), exports2);
|
|
16730
16796
|
__exportStar(require_tasks(), exports2);
|
|
16731
16797
|
__exportStar(require_hook_input(), exports2);
|
|
16798
|
+
__exportStar(require_setup_status(), exports2);
|
|
16732
16799
|
}
|
|
16733
16800
|
});
|
|
16734
16801
|
|
|
@@ -32722,7 +32789,10 @@ var require_package3 = __commonJS({
|
|
|
32722
32789
|
lint: "eslint packages",
|
|
32723
32790
|
"lint:fix": "eslint packages --fix",
|
|
32724
32791
|
format: 'prettier --write "packages/**/*.ts"',
|
|
32725
|
-
sidekick: "node packages/sidekick-cli/dist/bin.js"
|
|
32792
|
+
sidekick: "node packages/sidekick-cli/dist/bin.js",
|
|
32793
|
+
"test:dist:setup": "./scripts/test-dist.sh setup",
|
|
32794
|
+
"test:dist:teardown": "./scripts/test-dist.sh teardown",
|
|
32795
|
+
"test:dist:rebuild": "./scripts/test-dist.sh rebuild"
|
|
32726
32796
|
},
|
|
32727
32797
|
devDependencies: {
|
|
32728
32798
|
"@eslint/js": "^9.39.1",
|
|
@@ -32785,15 +32855,7 @@ var require_daemon_client2 = __commonJS({
|
|
|
32785
32855
|
await this.waitForShutdown();
|
|
32786
32856
|
}
|
|
32787
32857
|
this.logger.info("Starting daemon...");
|
|
32788
|
-
|
|
32789
|
-
try {
|
|
32790
|
-
const pkgPath = require.resolve("@sidekick/daemon/package.json");
|
|
32791
|
-
const pkg = require(pkgPath);
|
|
32792
|
-
const binPath = pkg.bin ? typeof pkg.bin === "string" ? pkg.bin : pkg.bin["sidekickd"] : pkg.main;
|
|
32793
|
-
daemonPath = path_1.default.resolve(path_1.default.dirname(pkgPath), binPath ?? "dist/index.js");
|
|
32794
|
-
} catch {
|
|
32795
|
-
daemonPath = path_1.default.resolve(__dirname, "../../sidekick-daemon/dist/index.js");
|
|
32796
|
-
}
|
|
32858
|
+
const daemonPath = await this.resolveDaemonPath();
|
|
32797
32859
|
const child = (0, child_process_1.spawn)("node", [daemonPath, this.projectDir], {
|
|
32798
32860
|
detached: true,
|
|
32799
32861
|
stdio: "ignore",
|
|
@@ -32872,6 +32934,33 @@ var require_daemon_client2 = __commonJS({
|
|
|
32872
32934
|
} catch {
|
|
32873
32935
|
}
|
|
32874
32936
|
}
|
|
32937
|
+
/**
|
|
32938
|
+
* Resolve the daemon entry point path.
|
|
32939
|
+
* Checks in order:
|
|
32940
|
+
* 1. Bundled context: daemon.js as sibling file (npm distribution)
|
|
32941
|
+
* 2. Dev mode: workspace package resolution
|
|
32942
|
+
*/
|
|
32943
|
+
async resolveDaemonPath() {
|
|
32944
|
+
const bundledPath = path_1.default.resolve(__dirname, "daemon.js");
|
|
32945
|
+
try {
|
|
32946
|
+
await promises_12.default.access(bundledPath);
|
|
32947
|
+
this.logger.debug("Using bundled daemon", { path: bundledPath });
|
|
32948
|
+
return bundledPath;
|
|
32949
|
+
} catch {
|
|
32950
|
+
}
|
|
32951
|
+
try {
|
|
32952
|
+
const pkgPath = require.resolve("@sidekick/daemon/package.json");
|
|
32953
|
+
const pkg = require(pkgPath);
|
|
32954
|
+
const binPath = pkg.bin ? typeof pkg.bin === "string" ? pkg.bin : pkg.bin["sidekickd"] : pkg.main;
|
|
32955
|
+
const daemonPath = path_1.default.resolve(path_1.default.dirname(pkgPath), binPath ?? "dist/index.js");
|
|
32956
|
+
this.logger.debug("Using workspace daemon", { path: daemonPath });
|
|
32957
|
+
return daemonPath;
|
|
32958
|
+
} catch {
|
|
32959
|
+
const fallbackPath = path_1.default.resolve(__dirname, "../../sidekick-daemon/dist/index.js");
|
|
32960
|
+
this.logger.debug("Using fallback daemon path", { path: fallbackPath });
|
|
32961
|
+
return fallbackPath;
|
|
32962
|
+
}
|
|
32963
|
+
}
|
|
32875
32964
|
/**
|
|
32876
32965
|
* Request daemon shutdown (fire-and-forget).
|
|
32877
32966
|
* Sends shutdown request, receives ack, closes connection immediately.
|
|
@@ -33113,6 +33202,346 @@ var require_daemon_client2 = __commonJS({
|
|
|
33113
33202
|
}
|
|
33114
33203
|
});
|
|
33115
33204
|
|
|
33205
|
+
// ../sidekick-core/dist/setup-status-service.js
|
|
33206
|
+
var require_setup_status_service = __commonJS({
|
|
33207
|
+
"../sidekick-core/dist/setup-status-service.js"(exports2) {
|
|
33208
|
+
"use strict";
|
|
33209
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
33210
|
+
if (k2 === void 0) k2 = k;
|
|
33211
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
33212
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
33213
|
+
desc = { enumerable: true, get: function() {
|
|
33214
|
+
return m[k];
|
|
33215
|
+
} };
|
|
33216
|
+
}
|
|
33217
|
+
Object.defineProperty(o, k2, desc);
|
|
33218
|
+
} : function(o, m, k, k2) {
|
|
33219
|
+
if (k2 === void 0) k2 = k;
|
|
33220
|
+
o[k2] = m[k];
|
|
33221
|
+
});
|
|
33222
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
33223
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
33224
|
+
} : function(o, v) {
|
|
33225
|
+
o["default"] = v;
|
|
33226
|
+
});
|
|
33227
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
33228
|
+
var ownKeys = function(o) {
|
|
33229
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
33230
|
+
var ar = [];
|
|
33231
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
33232
|
+
return ar;
|
|
33233
|
+
};
|
|
33234
|
+
return ownKeys(o);
|
|
33235
|
+
};
|
|
33236
|
+
return function(mod) {
|
|
33237
|
+
if (mod && mod.__esModule) return mod;
|
|
33238
|
+
var result = {};
|
|
33239
|
+
if (mod != null) {
|
|
33240
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
33241
|
+
}
|
|
33242
|
+
__setModuleDefault(result, mod);
|
|
33243
|
+
return result;
|
|
33244
|
+
};
|
|
33245
|
+
}();
|
|
33246
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
33247
|
+
exports2.SetupStatusService = void 0;
|
|
33248
|
+
exports2.createSetupStatusService = createSetupStatusService;
|
|
33249
|
+
var fs = __importStar(require("node:fs/promises"));
|
|
33250
|
+
var path = __importStar(require("node:path"));
|
|
33251
|
+
var os = __importStar(require("node:os"));
|
|
33252
|
+
var types_1 = require_dist();
|
|
33253
|
+
var SetupStatusService = class {
|
|
33254
|
+
constructor(projectDir, options) {
|
|
33255
|
+
this.projectDir = projectDir;
|
|
33256
|
+
this.homeDir = options?.homeDir ?? os.homedir();
|
|
33257
|
+
this.logger = options?.logger;
|
|
33258
|
+
}
|
|
33259
|
+
// === Paths ===
|
|
33260
|
+
get userStatusPath() {
|
|
33261
|
+
return path.join(this.homeDir, ".sidekick", "setup-status.json");
|
|
33262
|
+
}
|
|
33263
|
+
get projectStatusPath() {
|
|
33264
|
+
return path.join(this.projectDir, ".sidekick", "setup-status.json");
|
|
33265
|
+
}
|
|
33266
|
+
// === Low-level read/write ===
|
|
33267
|
+
async getUserStatus() {
|
|
33268
|
+
try {
|
|
33269
|
+
const content = await fs.readFile(this.userStatusPath, "utf-8");
|
|
33270
|
+
const parsed = types_1.UserSetupStatusSchema.safeParse(JSON.parse(content));
|
|
33271
|
+
if (!parsed.success) {
|
|
33272
|
+
this.logger?.warn("Invalid user setup status", { error: parsed.error });
|
|
33273
|
+
return null;
|
|
33274
|
+
}
|
|
33275
|
+
return parsed.data;
|
|
33276
|
+
} catch (err) {
|
|
33277
|
+
if (err.code === "ENOENT") {
|
|
33278
|
+
return null;
|
|
33279
|
+
}
|
|
33280
|
+
throw err;
|
|
33281
|
+
}
|
|
33282
|
+
}
|
|
33283
|
+
async getProjectStatus() {
|
|
33284
|
+
try {
|
|
33285
|
+
const content = await fs.readFile(this.projectStatusPath, "utf-8");
|
|
33286
|
+
const parsed = types_1.ProjectSetupStatusSchema.safeParse(JSON.parse(content));
|
|
33287
|
+
if (!parsed.success) {
|
|
33288
|
+
this.logger?.warn("Invalid project setup status", { error: parsed.error });
|
|
33289
|
+
return null;
|
|
33290
|
+
}
|
|
33291
|
+
return parsed.data;
|
|
33292
|
+
} catch (err) {
|
|
33293
|
+
if (err.code === "ENOENT") {
|
|
33294
|
+
return null;
|
|
33295
|
+
}
|
|
33296
|
+
throw err;
|
|
33297
|
+
}
|
|
33298
|
+
}
|
|
33299
|
+
async writeUserStatus(status) {
|
|
33300
|
+
const validated = types_1.UserSetupStatusSchema.parse(status);
|
|
33301
|
+
const dir = path.dirname(this.userStatusPath);
|
|
33302
|
+
await fs.mkdir(dir, { recursive: true });
|
|
33303
|
+
await fs.writeFile(this.userStatusPath, JSON.stringify(validated, null, 2) + "\n");
|
|
33304
|
+
this.logger?.debug("User setup status written", { path: this.userStatusPath });
|
|
33305
|
+
}
|
|
33306
|
+
async writeProjectStatus(status) {
|
|
33307
|
+
const validated = types_1.ProjectSetupStatusSchema.parse(status);
|
|
33308
|
+
const dir = path.dirname(this.projectStatusPath);
|
|
33309
|
+
await fs.mkdir(dir, { recursive: true });
|
|
33310
|
+
await fs.writeFile(this.projectStatusPath, JSON.stringify(validated, null, 2) + "\n");
|
|
33311
|
+
this.logger?.debug("Project setup status written", { path: this.projectStatusPath });
|
|
33312
|
+
}
|
|
33313
|
+
async updateUserStatus(updates) {
|
|
33314
|
+
const current = await this.getUserStatus();
|
|
33315
|
+
if (!current) {
|
|
33316
|
+
throw new Error("Cannot update user status: no existing status found");
|
|
33317
|
+
}
|
|
33318
|
+
const updated = {
|
|
33319
|
+
...current,
|
|
33320
|
+
...updates,
|
|
33321
|
+
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
33322
|
+
};
|
|
33323
|
+
await this.writeUserStatus(updated);
|
|
33324
|
+
}
|
|
33325
|
+
async updateProjectStatus(updates) {
|
|
33326
|
+
const current = await this.getProjectStatus();
|
|
33327
|
+
if (!current) {
|
|
33328
|
+
throw new Error("Cannot update project status: no existing status found");
|
|
33329
|
+
}
|
|
33330
|
+
const updated = {
|
|
33331
|
+
...current,
|
|
33332
|
+
...updates,
|
|
33333
|
+
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
33334
|
+
};
|
|
33335
|
+
await this.writeProjectStatus(updated);
|
|
33336
|
+
}
|
|
33337
|
+
// === Merged getters ===
|
|
33338
|
+
async getStatuslineHealth() {
|
|
33339
|
+
const project = await this.getProjectStatus();
|
|
33340
|
+
if (project?.statusline === "configured")
|
|
33341
|
+
return "configured";
|
|
33342
|
+
if (project?.statusline === "skipped")
|
|
33343
|
+
return "skipped";
|
|
33344
|
+
if (project?.statusline === "user") {
|
|
33345
|
+
const user2 = await this.getUserStatus();
|
|
33346
|
+
return user2?.statusline ?? "not-setup";
|
|
33347
|
+
}
|
|
33348
|
+
const user = await this.getUserStatus();
|
|
33349
|
+
return user?.statusline ?? "not-setup";
|
|
33350
|
+
}
|
|
33351
|
+
async getApiKeyHealth(key) {
|
|
33352
|
+
const project = await this.getProjectStatus();
|
|
33353
|
+
const projectHealth = project?.apiKeys[key];
|
|
33354
|
+
if (projectHealth && projectHealth !== "user") {
|
|
33355
|
+
return projectHealth;
|
|
33356
|
+
}
|
|
33357
|
+
const user = await this.getUserStatus();
|
|
33358
|
+
return user?.apiKeys[key] ?? "missing";
|
|
33359
|
+
}
|
|
33360
|
+
async getEffectiveApiKeyHealth(key) {
|
|
33361
|
+
const health = await this.getApiKeyHealth(key);
|
|
33362
|
+
return health === "user" ? "missing" : health;
|
|
33363
|
+
}
|
|
33364
|
+
async isHealthy() {
|
|
33365
|
+
const statusline = await this.getStatuslineHealth();
|
|
33366
|
+
const openrouterKey = await this.getEffectiveApiKeyHealth("OPENROUTER_API_KEY");
|
|
33367
|
+
return statusline === "configured" && (openrouterKey === "healthy" || openrouterKey === "not-required");
|
|
33368
|
+
}
|
|
33369
|
+
// === Auto-config helpers ===
|
|
33370
|
+
async isUserSetupComplete() {
|
|
33371
|
+
const user = await this.getUserStatus();
|
|
33372
|
+
return user !== null;
|
|
33373
|
+
}
|
|
33374
|
+
async isProjectConfigured() {
|
|
33375
|
+
const project = await this.getProjectStatus();
|
|
33376
|
+
return project !== null;
|
|
33377
|
+
}
|
|
33378
|
+
async shouldAutoConfigureProject() {
|
|
33379
|
+
const user = await this.getUserStatus();
|
|
33380
|
+
if (!user?.preferences.autoConfigureProjects) {
|
|
33381
|
+
return false;
|
|
33382
|
+
}
|
|
33383
|
+
return !await this.isProjectConfigured();
|
|
33384
|
+
}
|
|
33385
|
+
async setApiKeyHealth(key, health, scope) {
|
|
33386
|
+
if (scope === "user") {
|
|
33387
|
+
const current = await this.getUserStatus();
|
|
33388
|
+
if (!current) {
|
|
33389
|
+
throw new Error("Cannot update API key health: no user status found");
|
|
33390
|
+
}
|
|
33391
|
+
await this.updateUserStatus({
|
|
33392
|
+
apiKeys: { ...current.apiKeys, [key]: health }
|
|
33393
|
+
});
|
|
33394
|
+
} else {
|
|
33395
|
+
const current = await this.getProjectStatus();
|
|
33396
|
+
if (!current) {
|
|
33397
|
+
throw new Error("Cannot update API key health: no project status found");
|
|
33398
|
+
}
|
|
33399
|
+
await this.updateProjectStatus({
|
|
33400
|
+
apiKeys: { ...current.apiKeys, [key]: health }
|
|
33401
|
+
});
|
|
33402
|
+
}
|
|
33403
|
+
}
|
|
33404
|
+
// === Backward compatibility (for statusline service) ===
|
|
33405
|
+
/**
|
|
33406
|
+
* Get the overall setup state for statusline display.
|
|
33407
|
+
*
|
|
33408
|
+
* - `not-run` - Setup has never been run
|
|
33409
|
+
* - `partial` - User setup done, project not configured
|
|
33410
|
+
* - `healthy` - All configured and working
|
|
33411
|
+
* - `unhealthy` - Setup exists but has issues (invalid keys, etc)
|
|
33412
|
+
*/
|
|
33413
|
+
async getSetupState() {
|
|
33414
|
+
const userStatus = await this.getUserStatus();
|
|
33415
|
+
const projectStatus = await this.getProjectStatus();
|
|
33416
|
+
if (!userStatus) {
|
|
33417
|
+
return "not-run";
|
|
33418
|
+
}
|
|
33419
|
+
if (!projectStatus) {
|
|
33420
|
+
return "partial";
|
|
33421
|
+
}
|
|
33422
|
+
const isHealthy = await this.isHealthy();
|
|
33423
|
+
return isHealthy ? "healthy" : "unhealthy";
|
|
33424
|
+
}
|
|
33425
|
+
};
|
|
33426
|
+
exports2.SetupStatusService = SetupStatusService;
|
|
33427
|
+
function createSetupStatusService(projectDir, homeDir) {
|
|
33428
|
+
return new SetupStatusService(projectDir, { homeDir });
|
|
33429
|
+
}
|
|
33430
|
+
}
|
|
33431
|
+
});
|
|
33432
|
+
|
|
33433
|
+
// ../sidekick-core/dist/gitignore.js
|
|
33434
|
+
var require_gitignore = __commonJS({
|
|
33435
|
+
"../sidekick-core/dist/gitignore.js"(exports2) {
|
|
33436
|
+
"use strict";
|
|
33437
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
33438
|
+
if (k2 === void 0) k2 = k;
|
|
33439
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
33440
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
33441
|
+
desc = { enumerable: true, get: function() {
|
|
33442
|
+
return m[k];
|
|
33443
|
+
} };
|
|
33444
|
+
}
|
|
33445
|
+
Object.defineProperty(o, k2, desc);
|
|
33446
|
+
} : function(o, m, k, k2) {
|
|
33447
|
+
if (k2 === void 0) k2 = k;
|
|
33448
|
+
o[k2] = m[k];
|
|
33449
|
+
});
|
|
33450
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
33451
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
33452
|
+
} : function(o, v) {
|
|
33453
|
+
o["default"] = v;
|
|
33454
|
+
});
|
|
33455
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
33456
|
+
var ownKeys = function(o) {
|
|
33457
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
33458
|
+
var ar = [];
|
|
33459
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
33460
|
+
return ar;
|
|
33461
|
+
};
|
|
33462
|
+
return ownKeys(o);
|
|
33463
|
+
};
|
|
33464
|
+
return function(mod) {
|
|
33465
|
+
if (mod && mod.__esModule) return mod;
|
|
33466
|
+
var result = {};
|
|
33467
|
+
if (mod != null) {
|
|
33468
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
33469
|
+
}
|
|
33470
|
+
__setModuleDefault(result, mod);
|
|
33471
|
+
return result;
|
|
33472
|
+
};
|
|
33473
|
+
}();
|
|
33474
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
33475
|
+
exports2.GITIGNORE_ENTRIES = exports2.SIDEKICK_SECTION_END = exports2.SIDEKICK_SECTION_START = void 0;
|
|
33476
|
+
exports2.installGitignoreSection = installGitignoreSection;
|
|
33477
|
+
exports2.removeGitignoreSection = removeGitignoreSection;
|
|
33478
|
+
exports2.detectGitignoreStatus = detectGitignoreStatus;
|
|
33479
|
+
var fs = __importStar(require("node:fs/promises"));
|
|
33480
|
+
var path = __importStar(require("node:path"));
|
|
33481
|
+
exports2.SIDEKICK_SECTION_START = "# >>> sidekick";
|
|
33482
|
+
exports2.SIDEKICK_SECTION_END = "# <<< sidekick";
|
|
33483
|
+
exports2.GITIGNORE_ENTRIES = [
|
|
33484
|
+
".sidekick/logs/",
|
|
33485
|
+
".sidekick/sessions/",
|
|
33486
|
+
".sidekick/state/",
|
|
33487
|
+
".sidekick/.env",
|
|
33488
|
+
".sidekick/.env.local"
|
|
33489
|
+
];
|
|
33490
|
+
async function installGitignoreSection(projectDir) {
|
|
33491
|
+
const gitignorePath = path.join(projectDir, ".gitignore");
|
|
33492
|
+
let content = "";
|
|
33493
|
+
try {
|
|
33494
|
+
content = await fs.readFile(gitignorePath, "utf-8");
|
|
33495
|
+
} catch (err) {
|
|
33496
|
+
if (err.code !== "ENOENT") {
|
|
33497
|
+
return { status: "error", error: `Failed to read .gitignore: ${err.message}` };
|
|
33498
|
+
}
|
|
33499
|
+
}
|
|
33500
|
+
if (content.includes(exports2.SIDEKICK_SECTION_START)) {
|
|
33501
|
+
return { status: "already-installed" };
|
|
33502
|
+
}
|
|
33503
|
+
const section = ["", exports2.SIDEKICK_SECTION_START, ...exports2.GITIGNORE_ENTRIES, exports2.SIDEKICK_SECTION_END].join("\n");
|
|
33504
|
+
const newContent = content.trimEnd() + section + "\n";
|
|
33505
|
+
try {
|
|
33506
|
+
await fs.writeFile(gitignorePath, newContent);
|
|
33507
|
+
return { status: "installed", entriesAdded: exports2.GITIGNORE_ENTRIES };
|
|
33508
|
+
} catch (err) {
|
|
33509
|
+
return { status: "error", error: `Failed to write .gitignore: ${err.message}` };
|
|
33510
|
+
}
|
|
33511
|
+
}
|
|
33512
|
+
async function removeGitignoreSection(projectDir) {
|
|
33513
|
+
const gitignorePath = path.join(projectDir, ".gitignore");
|
|
33514
|
+
try {
|
|
33515
|
+
const content = await fs.readFile(gitignorePath, "utf-8");
|
|
33516
|
+
const startIdx = content.indexOf(exports2.SIDEKICK_SECTION_START);
|
|
33517
|
+
const endIdx = content.indexOf(exports2.SIDEKICK_SECTION_END);
|
|
33518
|
+
if (startIdx === -1 || endIdx === -1 || endIdx < startIdx) {
|
|
33519
|
+
return false;
|
|
33520
|
+
}
|
|
33521
|
+
const lineStartIdx = content.lastIndexOf("\n", startIdx - 1) + 1;
|
|
33522
|
+
const lineEndIdx = content.indexOf("\n", endIdx);
|
|
33523
|
+
const actualEndIdx = lineEndIdx === -1 ? content.length : lineEndIdx + 1;
|
|
33524
|
+
const before = content.slice(0, lineStartIdx).trimEnd();
|
|
33525
|
+
const after = content.slice(actualEndIdx).trimStart();
|
|
33526
|
+
const newContent = before + (after ? "\n" + after : "") + "\n";
|
|
33527
|
+
await fs.writeFile(gitignorePath, newContent);
|
|
33528
|
+
return true;
|
|
33529
|
+
} catch {
|
|
33530
|
+
return false;
|
|
33531
|
+
}
|
|
33532
|
+
}
|
|
33533
|
+
async function detectGitignoreStatus(projectDir) {
|
|
33534
|
+
const gitignorePath = path.join(projectDir, ".gitignore");
|
|
33535
|
+
try {
|
|
33536
|
+
const content = await fs.readFile(gitignorePath, "utf-8");
|
|
33537
|
+
return content.includes(exports2.SIDEKICK_SECTION_START) ? "installed" : "missing";
|
|
33538
|
+
} catch {
|
|
33539
|
+
return "missing";
|
|
33540
|
+
}
|
|
33541
|
+
}
|
|
33542
|
+
}
|
|
33543
|
+
});
|
|
33544
|
+
|
|
33116
33545
|
// ../sidekick-core/dist/state/errors.js
|
|
33117
33546
|
var require_errors4 = __commonJS({
|
|
33118
33547
|
"../sidekick-core/dist/state/errors.js"(exports2) {
|
|
@@ -38303,8 +38732,8 @@ var require_dist3 = __commonJS({
|
|
|
38303
38732
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
|
|
38304
38733
|
};
|
|
38305
38734
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
38306
|
-
exports2.
|
|
38307
|
-
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 = void 0;
|
|
38735
|
+
exports2.getHookDir = exports2.getStagingRoot = exports2.SessionScopedStagingService = exports2.StagingServiceCore = exports2.GITIGNORE_ENTRIES = exports2.SIDEKICK_SECTION_END = exports2.SIDEKICK_SECTION_START = exports2.detectGitignoreStatus = exports2.removeGitignoreSection = exports2.installGitignoreSection = 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;
|
|
38736
|
+
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 = void 0;
|
|
38308
38737
|
var types_1 = require_dist();
|
|
38309
38738
|
Object.defineProperty(exports2, "TaskTypes", { enumerable: true, get: function() {
|
|
38310
38739
|
return types_1.TaskTypes;
|
|
@@ -38437,6 +38866,32 @@ var require_dist3 = __commonJS({
|
|
|
38437
38866
|
Object.defineProperty(exports2, "DaemonClient", { enumerable: true, get: function() {
|
|
38438
38867
|
return daemon_client_1.DaemonClient;
|
|
38439
38868
|
} });
|
|
38869
|
+
var setup_status_service_1 = require_setup_status_service();
|
|
38870
|
+
Object.defineProperty(exports2, "SetupStatusService", { enumerable: true, get: function() {
|
|
38871
|
+
return setup_status_service_1.SetupStatusService;
|
|
38872
|
+
} });
|
|
38873
|
+
Object.defineProperty(exports2, "createSetupStatusService", { enumerable: true, get: function() {
|
|
38874
|
+
return setup_status_service_1.createSetupStatusService;
|
|
38875
|
+
} });
|
|
38876
|
+
var gitignore_1 = require_gitignore();
|
|
38877
|
+
Object.defineProperty(exports2, "installGitignoreSection", { enumerable: true, get: function() {
|
|
38878
|
+
return gitignore_1.installGitignoreSection;
|
|
38879
|
+
} });
|
|
38880
|
+
Object.defineProperty(exports2, "removeGitignoreSection", { enumerable: true, get: function() {
|
|
38881
|
+
return gitignore_1.removeGitignoreSection;
|
|
38882
|
+
} });
|
|
38883
|
+
Object.defineProperty(exports2, "detectGitignoreStatus", { enumerable: true, get: function() {
|
|
38884
|
+
return gitignore_1.detectGitignoreStatus;
|
|
38885
|
+
} });
|
|
38886
|
+
Object.defineProperty(exports2, "SIDEKICK_SECTION_START", { enumerable: true, get: function() {
|
|
38887
|
+
return gitignore_1.SIDEKICK_SECTION_START;
|
|
38888
|
+
} });
|
|
38889
|
+
Object.defineProperty(exports2, "SIDEKICK_SECTION_END", { enumerable: true, get: function() {
|
|
38890
|
+
return gitignore_1.SIDEKICK_SECTION_END;
|
|
38891
|
+
} });
|
|
38892
|
+
Object.defineProperty(exports2, "GITIGNORE_ENTRIES", { enumerable: true, get: function() {
|
|
38893
|
+
return gitignore_1.GITIGNORE_ENTRIES;
|
|
38894
|
+
} });
|
|
38440
38895
|
var staging_service_1 = require_staging_service();
|
|
38441
38896
|
Object.defineProperty(exports2, "StagingServiceCore", { enumerable: true, get: function() {
|
|
38442
38897
|
return staging_service_1.StagingServiceCore;
|
|
@@ -48831,6 +49286,7 @@ var require_hook_command = __commonJS({
|
|
|
48831
49286
|
exports2.translateToClaudeCodeFormat = translateToClaudeCodeFormat;
|
|
48832
49287
|
exports2.parseHookArg = parseHookArg;
|
|
48833
49288
|
exports2.handleUnifiedHookCommand = handleUnifiedHookCommand;
|
|
49289
|
+
var core_1 = require_dist3();
|
|
48834
49290
|
var hook_js_1 = require_hook();
|
|
48835
49291
|
function combineReasonAndContext(reason, additionalContext, separator = "\n\n") {
|
|
48836
49292
|
if (reason && additionalContext) {
|
|
@@ -48848,7 +49304,7 @@ var require_hook_command = __commonJS({
|
|
|
48848
49304
|
response.systemMessage = internal.userMessage;
|
|
48849
49305
|
}
|
|
48850
49306
|
if (internal.additionalContext) {
|
|
48851
|
-
response.hookSpecificOutput = { additionalContext: internal.additionalContext };
|
|
49307
|
+
response.hookSpecificOutput = { hookEventName: "SessionStart", additionalContext: internal.additionalContext };
|
|
48852
49308
|
}
|
|
48853
49309
|
return response;
|
|
48854
49310
|
}
|
|
@@ -48953,6 +49409,52 @@ var require_hook_command = __commonJS({
|
|
|
48953
49409
|
return void 0;
|
|
48954
49410
|
return HOOK_ARG_TO_NAME[arg];
|
|
48955
49411
|
}
|
|
49412
|
+
var DEGRADED_MODE_MESSAGES = {
|
|
49413
|
+
"not-run": {
|
|
49414
|
+
additionalContext: `Sidekick plugin detected but not configured. Features like reminders, personas, and statusline are unavailable until setup is complete. If you haven't already, ask the user if you should execute the sidekick-config skill.`,
|
|
49415
|
+
userMessage: `Sidekick is installed but not configured. Run 'sidekick setup' to configure.`
|
|
49416
|
+
},
|
|
49417
|
+
partial: {
|
|
49418
|
+
additionalContext: `Sidekick user setup is complete but this project is not configured. Features like reminders, personas, and statusline are unavailable until project setup is complete. If you haven't already, ask the user if you should execute the sidekick-config skill.`,
|
|
49419
|
+
userMessage: `Sidekick project setup incomplete. Run 'sidekick setup' in this project to configure.`
|
|
49420
|
+
},
|
|
49421
|
+
unhealthy: {
|
|
49422
|
+
additionalContext: `Sidekick is configured but unhealthy (possibly invalid API keys or missing configuration). Features are unavailable until issues are resolved. If you haven't already, ask the user if you should execute sidekick doctor.`,
|
|
49423
|
+
userMessage: `Sidekick configuration is unhealthy. Run 'sidekick doctor' to diagnose issues.`
|
|
49424
|
+
}
|
|
49425
|
+
};
|
|
49426
|
+
var VERBOSE_DEGRADED_HOOKS = /* @__PURE__ */ new Set(["SessionStart", "UserPromptSubmit"]);
|
|
49427
|
+
async function checkSetupState(projectRoot, hookName, logger) {
|
|
49428
|
+
let state;
|
|
49429
|
+
try {
|
|
49430
|
+
const setupService = new core_1.SetupStatusService(projectRoot);
|
|
49431
|
+
state = await setupService.getSetupState();
|
|
49432
|
+
} catch (err) {
|
|
49433
|
+
logger.warn("Failed to check setup state, assuming healthy", {
|
|
49434
|
+
error: err instanceof Error ? err.message : String(err),
|
|
49435
|
+
hookName,
|
|
49436
|
+
projectRoot
|
|
49437
|
+
});
|
|
49438
|
+
return null;
|
|
49439
|
+
}
|
|
49440
|
+
if (state === "healthy") {
|
|
49441
|
+
return null;
|
|
49442
|
+
}
|
|
49443
|
+
const logData = { setupState: state, hookName, projectRoot };
|
|
49444
|
+
if (state === "not-run") {
|
|
49445
|
+
logger.info("Hook operating in degraded mode - setup not run", logData);
|
|
49446
|
+
} else {
|
|
49447
|
+
logger.warn("Hook operating in degraded mode", logData);
|
|
49448
|
+
}
|
|
49449
|
+
if (!VERBOSE_DEGRADED_HOOKS.has(hookName)) {
|
|
49450
|
+
return {};
|
|
49451
|
+
}
|
|
49452
|
+
const messages = DEGRADED_MODE_MESSAGES[state];
|
|
49453
|
+
return {
|
|
49454
|
+
additionalContext: messages.additionalContext,
|
|
49455
|
+
userMessage: messages.userMessage
|
|
49456
|
+
};
|
|
49457
|
+
}
|
|
48956
49458
|
function parseInternalResponse(output, hookName, logger) {
|
|
48957
49459
|
if (!output)
|
|
48958
49460
|
return {};
|
|
@@ -48970,6 +49472,15 @@ var require_hook_command = __commonJS({
|
|
|
48970
49472
|
async function handleUnifiedHookCommand(hookName, options, logger, stdout) {
|
|
48971
49473
|
const { projectRoot, hookInput, correlationId, runtime } = options;
|
|
48972
49474
|
logger.debug("Unified hook command invoked", { hookName, sessionId: hookInput.sessionId });
|
|
49475
|
+
const degradedResponse = await checkSetupState(projectRoot, hookName, logger);
|
|
49476
|
+
if (degradedResponse !== null) {
|
|
49477
|
+
const claudeResponse2 = translateToClaudeCodeFormat(hookName, degradedResponse);
|
|
49478
|
+
const outputStr2 = JSON.stringify(claudeResponse2);
|
|
49479
|
+
stdout.write(`${outputStr2}
|
|
49480
|
+
`);
|
|
49481
|
+
logger.debug("Hook completed in degraded mode", { hookName });
|
|
49482
|
+
return { exitCode: 0, output: outputStr2 };
|
|
49483
|
+
}
|
|
48973
49484
|
let internalOutput = "";
|
|
48974
49485
|
const captureStream = {
|
|
48975
49486
|
write(chunk) {
|
|
@@ -51116,6 +51627,10 @@ var require_statusline_service = __commonJS({
|
|
|
51116
51627
|
theme: this.config.theme,
|
|
51117
51628
|
useColors: this.useColors
|
|
51118
51629
|
});
|
|
51630
|
+
this.setupService = serviceConfig.setupService ?? new core_1.SetupStatusService(serviceConfig.projectDir ?? serviceConfig.cwd, {
|
|
51631
|
+
homeDir: serviceConfig.homeDir,
|
|
51632
|
+
logger: serviceConfig.logger
|
|
51633
|
+
});
|
|
51119
51634
|
}
|
|
51120
51635
|
/**
|
|
51121
51636
|
* Load persona definition for the session.
|
|
@@ -51205,6 +51720,74 @@ var require_statusline_service = __commonJS({
|
|
|
51205
51720
|
}
|
|
51206
51721
|
return { ...types_js_2.DEFAULT_STATUSLINE_CONFIG, ...serviceConfig.config };
|
|
51207
51722
|
}
|
|
51723
|
+
/**
|
|
51724
|
+
* Check setup status and return warning message if unhealthy.
|
|
51725
|
+
* Returns empty warning for healthy state.
|
|
51726
|
+
*/
|
|
51727
|
+
async checkSetupStatus() {
|
|
51728
|
+
const state = await this.setupService.getSetupState();
|
|
51729
|
+
switch (state) {
|
|
51730
|
+
case "not-run":
|
|
51731
|
+
return {
|
|
51732
|
+
warning: "Sidekick not configured. Run 'sidekick setup' to get started.",
|
|
51733
|
+
state
|
|
51734
|
+
};
|
|
51735
|
+
case "partial":
|
|
51736
|
+
return {
|
|
51737
|
+
warning: "Project not configured. Run 'sidekick setup' for this project.",
|
|
51738
|
+
state
|
|
51739
|
+
};
|
|
51740
|
+
case "unhealthy": {
|
|
51741
|
+
const keyHealth = await this.setupService.getEffectiveApiKeyHealth("OPENROUTER_API_KEY");
|
|
51742
|
+
if (keyHealth === "missing") {
|
|
51743
|
+
return {
|
|
51744
|
+
warning: "OPENROUTER_API_KEY not found. Run 'sidekick doctor' or /sidekick-config",
|
|
51745
|
+
state
|
|
51746
|
+
};
|
|
51747
|
+
}
|
|
51748
|
+
if (keyHealth === "invalid") {
|
|
51749
|
+
return {
|
|
51750
|
+
warning: "API key invalid. Run 'sidekick doctor' to fix.",
|
|
51751
|
+
state
|
|
51752
|
+
};
|
|
51753
|
+
}
|
|
51754
|
+
return {
|
|
51755
|
+
warning: "Setup issue detected. Run 'sidekick doctor'.",
|
|
51756
|
+
state
|
|
51757
|
+
};
|
|
51758
|
+
}
|
|
51759
|
+
default:
|
|
51760
|
+
return { warning: "", state: "healthy" };
|
|
51761
|
+
}
|
|
51762
|
+
}
|
|
51763
|
+
/**
|
|
51764
|
+
* Build minimal view model for setup_warning display mode.
|
|
51765
|
+
* Only includes fields needed for warning display.
|
|
51766
|
+
*/
|
|
51767
|
+
buildMinimalViewModel(setupCheck) {
|
|
51768
|
+
return {
|
|
51769
|
+
model: "",
|
|
51770
|
+
contextWindow: "",
|
|
51771
|
+
tokenUsageActual: "",
|
|
51772
|
+
tokenUsageEffective: "",
|
|
51773
|
+
tokenPercentageActual: "",
|
|
51774
|
+
tokenPercentageEffective: "",
|
|
51775
|
+
tokensStatus: "normal",
|
|
51776
|
+
cost: "",
|
|
51777
|
+
costStatus: "normal",
|
|
51778
|
+
duration: "",
|
|
51779
|
+
cwd: "",
|
|
51780
|
+
branch: "",
|
|
51781
|
+
branchColor: "",
|
|
51782
|
+
displayMode: "setup_warning",
|
|
51783
|
+
summary: setupCheck.warning,
|
|
51784
|
+
title: "",
|
|
51785
|
+
warningCount: 0,
|
|
51786
|
+
errorCount: 0,
|
|
51787
|
+
logStatus: "normal",
|
|
51788
|
+
personaName: ""
|
|
51789
|
+
};
|
|
51790
|
+
}
|
|
51208
51791
|
/**
|
|
51209
51792
|
* Render the statusline by fetching all data in parallel and formatting.
|
|
51210
51793
|
*
|
|
@@ -51214,6 +51797,19 @@ var require_statusline_service = __commonJS({
|
|
|
51214
51797
|
* for model/tokens/cost/duration instead of reading from state files.
|
|
51215
51798
|
*/
|
|
51216
51799
|
async render() {
|
|
51800
|
+
const setupCheck = await this.checkSetupStatus();
|
|
51801
|
+
if (setupCheck.state !== "healthy") {
|
|
51802
|
+
const viewModel2 = this.buildMinimalViewModel(setupCheck);
|
|
51803
|
+
const ANSI_YELLOW = "\x1B[33m";
|
|
51804
|
+
const ANSI_RESET = "\x1B[0m";
|
|
51805
|
+
const text2 = this.useColors ? `${ANSI_YELLOW}${setupCheck.warning}${ANSI_RESET}` : setupCheck.warning;
|
|
51806
|
+
return {
|
|
51807
|
+
text: text2,
|
|
51808
|
+
displayMode: "setup_warning",
|
|
51809
|
+
staleData: false,
|
|
51810
|
+
viewModel: viewModel2
|
|
51811
|
+
};
|
|
51812
|
+
}
|
|
51217
51813
|
const hasHookInput = !!this.hookInput;
|
|
51218
51814
|
const [transcriptResult, summaryResult, resumeResult, snarkyResult, branchResult, baseline, logMetricsResult, personaResult] = await Promise.all([
|
|
51219
51815
|
this.stateReader.getTranscriptMetrics(),
|
|
@@ -53005,6 +53601,582 @@ ${USAGE_TEXT}`);
|
|
|
53005
53601
|
}
|
|
53006
53602
|
});
|
|
53007
53603
|
|
|
53604
|
+
// ../sidekick-cli/dist/commands/setup/prompts.js
|
|
53605
|
+
var require_prompts = __commonJS({
|
|
53606
|
+
"../sidekick-cli/dist/commands/setup/prompts.js"(exports2) {
|
|
53607
|
+
"use strict";
|
|
53608
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
53609
|
+
if (k2 === void 0) k2 = k;
|
|
53610
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
53611
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
53612
|
+
desc = { enumerable: true, get: function() {
|
|
53613
|
+
return m[k];
|
|
53614
|
+
} };
|
|
53615
|
+
}
|
|
53616
|
+
Object.defineProperty(o, k2, desc);
|
|
53617
|
+
} : function(o, m, k, k2) {
|
|
53618
|
+
if (k2 === void 0) k2 = k;
|
|
53619
|
+
o[k2] = m[k];
|
|
53620
|
+
});
|
|
53621
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
53622
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
53623
|
+
} : function(o, v) {
|
|
53624
|
+
o["default"] = v;
|
|
53625
|
+
});
|
|
53626
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
53627
|
+
var ownKeys = function(o) {
|
|
53628
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
53629
|
+
var ar = [];
|
|
53630
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
53631
|
+
return ar;
|
|
53632
|
+
};
|
|
53633
|
+
return ownKeys(o);
|
|
53634
|
+
};
|
|
53635
|
+
return function(mod) {
|
|
53636
|
+
if (mod && mod.__esModule) return mod;
|
|
53637
|
+
var result = {};
|
|
53638
|
+
if (mod != null) {
|
|
53639
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
53640
|
+
}
|
|
53641
|
+
__setModuleDefault(result, mod);
|
|
53642
|
+
return result;
|
|
53643
|
+
};
|
|
53644
|
+
}();
|
|
53645
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
53646
|
+
exports2.printHeader = printHeader;
|
|
53647
|
+
exports2.printStatus = printStatus;
|
|
53648
|
+
exports2.promptSelect = promptSelect;
|
|
53649
|
+
exports2.promptConfirm = promptConfirm;
|
|
53650
|
+
exports2.promptInput = promptInput;
|
|
53651
|
+
var readline = __importStar(require("node:readline"));
|
|
53652
|
+
var colors = {
|
|
53653
|
+
reset: "\x1B[0m",
|
|
53654
|
+
bold: "\x1B[1m",
|
|
53655
|
+
dim: "\x1B[2m",
|
|
53656
|
+
green: "\x1B[32m",
|
|
53657
|
+
yellow: "\x1B[33m",
|
|
53658
|
+
blue: "\x1B[34m",
|
|
53659
|
+
cyan: "\x1B[36m"
|
|
53660
|
+
};
|
|
53661
|
+
function printHeader(ctx, title, description) {
|
|
53662
|
+
ctx.stdout.write("\n");
|
|
53663
|
+
ctx.stdout.write(`${colors.bold}${title}${colors.reset}
|
|
53664
|
+
`);
|
|
53665
|
+
ctx.stdout.write("\u2500".repeat(Math.min(title.length + 10, 60)) + "\n");
|
|
53666
|
+
if (description) {
|
|
53667
|
+
ctx.stdout.write(`${colors.dim}${description}${colors.reset}
|
|
53668
|
+
`);
|
|
53669
|
+
}
|
|
53670
|
+
ctx.stdout.write("\n");
|
|
53671
|
+
}
|
|
53672
|
+
function printStatus(ctx, type, message) {
|
|
53673
|
+
const icons = { success: "\u2713", warning: "\u26A0", info: "\u2022", error: "\u2717" };
|
|
53674
|
+
const colorMap = { success: colors.green, warning: colors.yellow, info: colors.blue, error: "\x1B[31m" };
|
|
53675
|
+
ctx.stdout.write(`${colorMap[type]}${icons[type]}${colors.reset} ${message}
|
|
53676
|
+
`);
|
|
53677
|
+
}
|
|
53678
|
+
async function promptSelect(ctx, question, options) {
|
|
53679
|
+
ctx.stdout.write(`${question}
|
|
53680
|
+
|
|
53681
|
+
`);
|
|
53682
|
+
options.forEach((opt, i) => {
|
|
53683
|
+
ctx.stdout.write(` ${colors.cyan}${i + 1})${colors.reset} ${opt.label}
|
|
53684
|
+
`);
|
|
53685
|
+
if (opt.description) {
|
|
53686
|
+
ctx.stdout.write(` ${colors.dim}${opt.description}${colors.reset}
|
|
53687
|
+
`);
|
|
53688
|
+
}
|
|
53689
|
+
});
|
|
53690
|
+
ctx.stdout.write("\n");
|
|
53691
|
+
const rl = readline.createInterface({
|
|
53692
|
+
input: ctx.stdin,
|
|
53693
|
+
output: ctx.stdout,
|
|
53694
|
+
terminal: false
|
|
53695
|
+
});
|
|
53696
|
+
return new Promise((resolve3) => {
|
|
53697
|
+
ctx.stdout.write(`Enter choice (1-${options.length}): `);
|
|
53698
|
+
rl.once("line", (answer) => {
|
|
53699
|
+
rl.close();
|
|
53700
|
+
const num = parseInt(answer.trim(), 10);
|
|
53701
|
+
if (num >= 1 && num <= options.length) {
|
|
53702
|
+
resolve3(options[num - 1].value);
|
|
53703
|
+
} else {
|
|
53704
|
+
resolve3(options[0].value);
|
|
53705
|
+
}
|
|
53706
|
+
});
|
|
53707
|
+
});
|
|
53708
|
+
}
|
|
53709
|
+
async function promptConfirm(ctx, question, defaultYes = true) {
|
|
53710
|
+
const hint = defaultYes ? "[Y/n]" : "[y/N]";
|
|
53711
|
+
const rl = readline.createInterface({
|
|
53712
|
+
input: ctx.stdin,
|
|
53713
|
+
output: ctx.stdout,
|
|
53714
|
+
terminal: false
|
|
53715
|
+
});
|
|
53716
|
+
return new Promise((resolve3) => {
|
|
53717
|
+
ctx.stdout.write(`${question} ${hint} `);
|
|
53718
|
+
rl.once("line", (answer) => {
|
|
53719
|
+
rl.close();
|
|
53720
|
+
const normalized = answer.trim().toLowerCase();
|
|
53721
|
+
if (normalized === "") {
|
|
53722
|
+
resolve3(defaultYes);
|
|
53723
|
+
} else {
|
|
53724
|
+
resolve3(normalized === "y" || normalized === "yes");
|
|
53725
|
+
}
|
|
53726
|
+
});
|
|
53727
|
+
});
|
|
53728
|
+
}
|
|
53729
|
+
async function promptInput(ctx, question) {
|
|
53730
|
+
const rl = readline.createInterface({
|
|
53731
|
+
input: ctx.stdin,
|
|
53732
|
+
output: ctx.stdout,
|
|
53733
|
+
terminal: false
|
|
53734
|
+
});
|
|
53735
|
+
return new Promise((resolve3) => {
|
|
53736
|
+
ctx.stdout.write(`${question}: `);
|
|
53737
|
+
rl.once("line", (answer) => {
|
|
53738
|
+
rl.close();
|
|
53739
|
+
resolve3(answer.trim());
|
|
53740
|
+
});
|
|
53741
|
+
});
|
|
53742
|
+
}
|
|
53743
|
+
}
|
|
53744
|
+
});
|
|
53745
|
+
|
|
53746
|
+
// ../sidekick-cli/dist/commands/setup/validate-api-key.js
|
|
53747
|
+
var require_validate_api_key = __commonJS({
|
|
53748
|
+
"../sidekick-cli/dist/commands/setup/validate-api-key.js"(exports2) {
|
|
53749
|
+
"use strict";
|
|
53750
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
53751
|
+
exports2.validateOpenRouterKey = validateOpenRouterKey;
|
|
53752
|
+
exports2.validateOpenAIKey = validateOpenAIKey;
|
|
53753
|
+
var API_ENDPOINTS = {
|
|
53754
|
+
openrouter: "https://openrouter.ai/api/v1/models",
|
|
53755
|
+
openai: "https://api.openai.com/v1/models"
|
|
53756
|
+
};
|
|
53757
|
+
async function validateApiKey(provider, apiKey, logger) {
|
|
53758
|
+
try {
|
|
53759
|
+
const response = await fetch(API_ENDPOINTS[provider], {
|
|
53760
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
53761
|
+
});
|
|
53762
|
+
if (response.ok) {
|
|
53763
|
+
return { valid: true };
|
|
53764
|
+
}
|
|
53765
|
+
if (response.status === 401) {
|
|
53766
|
+
return { valid: false, error: "Invalid API key" };
|
|
53767
|
+
}
|
|
53768
|
+
return { valid: false, error: `API returned status ${response.status}` };
|
|
53769
|
+
} catch (err) {
|
|
53770
|
+
logger?.warn("API key validation failed", { error: err });
|
|
53771
|
+
return { valid: false, error: err instanceof Error ? err.message : "Network error" };
|
|
53772
|
+
}
|
|
53773
|
+
}
|
|
53774
|
+
function validateOpenRouterKey(apiKey, logger) {
|
|
53775
|
+
return validateApiKey("openrouter", apiKey, logger);
|
|
53776
|
+
}
|
|
53777
|
+
function validateOpenAIKey(apiKey, logger) {
|
|
53778
|
+
return validateApiKey("openai", apiKey, logger);
|
|
53779
|
+
}
|
|
53780
|
+
}
|
|
53781
|
+
});
|
|
53782
|
+
|
|
53783
|
+
// ../sidekick-cli/dist/commands/setup/index.js
|
|
53784
|
+
var require_setup = __commonJS({
|
|
53785
|
+
"../sidekick-cli/dist/commands/setup/index.js"(exports2) {
|
|
53786
|
+
"use strict";
|
|
53787
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
53788
|
+
if (k2 === void 0) k2 = k;
|
|
53789
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
53790
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
53791
|
+
desc = { enumerable: true, get: function() {
|
|
53792
|
+
return m[k];
|
|
53793
|
+
} };
|
|
53794
|
+
}
|
|
53795
|
+
Object.defineProperty(o, k2, desc);
|
|
53796
|
+
} : function(o, m, k, k2) {
|
|
53797
|
+
if (k2 === void 0) k2 = k;
|
|
53798
|
+
o[k2] = m[k];
|
|
53799
|
+
});
|
|
53800
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
53801
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
53802
|
+
} : function(o, v) {
|
|
53803
|
+
o["default"] = v;
|
|
53804
|
+
});
|
|
53805
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
53806
|
+
var ownKeys = function(o) {
|
|
53807
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
53808
|
+
var ar = [];
|
|
53809
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
53810
|
+
return ar;
|
|
53811
|
+
};
|
|
53812
|
+
return ownKeys(o);
|
|
53813
|
+
};
|
|
53814
|
+
return function(mod) {
|
|
53815
|
+
if (mod && mod.__esModule) return mod;
|
|
53816
|
+
var result = {};
|
|
53817
|
+
if (mod != null) {
|
|
53818
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
53819
|
+
}
|
|
53820
|
+
__setModuleDefault(result, mod);
|
|
53821
|
+
return result;
|
|
53822
|
+
};
|
|
53823
|
+
}();
|
|
53824
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
53825
|
+
exports2.handleSetupCommand = handleSetupCommand;
|
|
53826
|
+
var fs = __importStar(require("node:fs/promises"));
|
|
53827
|
+
var path = __importStar(require("node:path"));
|
|
53828
|
+
var os = __importStar(require("node:os"));
|
|
53829
|
+
var core_1 = require_dist3();
|
|
53830
|
+
var prompts_js_1 = require_prompts();
|
|
53831
|
+
var validate_api_key_js_1 = require_validate_api_key();
|
|
53832
|
+
var STATUSLINE_COMMAND = "npx @scotthamilton77/sidekick statusline --project-dir=$CLAUDE_PROJECT_DIR";
|
|
53833
|
+
function getApiKeyStatusType(health) {
|
|
53834
|
+
switch (health) {
|
|
53835
|
+
case "healthy":
|
|
53836
|
+
return "success";
|
|
53837
|
+
case "not-required":
|
|
53838
|
+
return "info";
|
|
53839
|
+
default:
|
|
53840
|
+
return "warning";
|
|
53841
|
+
}
|
|
53842
|
+
}
|
|
53843
|
+
async function configureStatusline(settingsPath, logger) {
|
|
53844
|
+
let settings = {};
|
|
53845
|
+
try {
|
|
53846
|
+
const content = await fs.readFile(settingsPath, "utf-8");
|
|
53847
|
+
settings = JSON.parse(content);
|
|
53848
|
+
} catch (err) {
|
|
53849
|
+
if (err.code !== "ENOENT") {
|
|
53850
|
+
throw err;
|
|
53851
|
+
}
|
|
53852
|
+
}
|
|
53853
|
+
settings.statusLine = {
|
|
53854
|
+
type: "command",
|
|
53855
|
+
command: STATUSLINE_COMMAND
|
|
53856
|
+
};
|
|
53857
|
+
const dir = path.dirname(settingsPath);
|
|
53858
|
+
await fs.mkdir(dir, { recursive: true });
|
|
53859
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
53860
|
+
logger?.info("Statusline configured", { path: settingsPath });
|
|
53861
|
+
}
|
|
53862
|
+
async function writeApiKeyToEnv(envPath, key, value) {
|
|
53863
|
+
const dir = path.dirname(envPath);
|
|
53864
|
+
await fs.mkdir(dir, { recursive: true });
|
|
53865
|
+
let content = "";
|
|
53866
|
+
try {
|
|
53867
|
+
content = await fs.readFile(envPath, "utf-8");
|
|
53868
|
+
} catch {
|
|
53869
|
+
}
|
|
53870
|
+
const keyRegex = new RegExp(`^${key}=.*$`, "m");
|
|
53871
|
+
if (keyRegex.test(content)) {
|
|
53872
|
+
content = content.replace(keyRegex, `${key}=${value}`);
|
|
53873
|
+
} else {
|
|
53874
|
+
if (content && !content.endsWith("\n")) {
|
|
53875
|
+
content += "\n";
|
|
53876
|
+
}
|
|
53877
|
+
content += `${key}=${value}
|
|
53878
|
+
`;
|
|
53879
|
+
}
|
|
53880
|
+
await fs.writeFile(envPath, content);
|
|
53881
|
+
}
|
|
53882
|
+
async function findExistingApiKey(keyName, homeDir, projectDir) {
|
|
53883
|
+
if (process.env[keyName]) {
|
|
53884
|
+
return process.env[keyName];
|
|
53885
|
+
}
|
|
53886
|
+
const envPaths = [
|
|
53887
|
+
path.join(homeDir, ".sidekick", ".env"),
|
|
53888
|
+
path.join(projectDir, ".sidekick", ".env"),
|
|
53889
|
+
path.join(projectDir, ".sidekick", ".env.local")
|
|
53890
|
+
];
|
|
53891
|
+
for (const envPath of envPaths) {
|
|
53892
|
+
try {
|
|
53893
|
+
const content = await fs.readFile(envPath, "utf-8");
|
|
53894
|
+
const match = content.match(new RegExp(`^${keyName}=(.+)$`, "m"));
|
|
53895
|
+
if (match) {
|
|
53896
|
+
return match[1];
|
|
53897
|
+
}
|
|
53898
|
+
} catch {
|
|
53899
|
+
}
|
|
53900
|
+
}
|
|
53901
|
+
return null;
|
|
53902
|
+
}
|
|
53903
|
+
async function writePersonaConfig(_projectDir, homeDir, enabled) {
|
|
53904
|
+
const configDir = path.join(homeDir, ".sidekick");
|
|
53905
|
+
const configPath = path.join(configDir, "config.yaml");
|
|
53906
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
53907
|
+
let content = "";
|
|
53908
|
+
try {
|
|
53909
|
+
content = await fs.readFile(configPath, "utf-8");
|
|
53910
|
+
} catch {
|
|
53911
|
+
}
|
|
53912
|
+
const personasRegex = /^features:\s*\n\s*personas:\s*\n\s*enabled:\s*(true|false)/m;
|
|
53913
|
+
const newPersonasBlock = `features:
|
|
53914
|
+
personas:
|
|
53915
|
+
enabled: ${enabled}`;
|
|
53916
|
+
if (personasRegex.test(content)) {
|
|
53917
|
+
content = content.replace(personasRegex, newPersonasBlock);
|
|
53918
|
+
} else {
|
|
53919
|
+
if (content && !content.endsWith("\n")) {
|
|
53920
|
+
content += "\n";
|
|
53921
|
+
}
|
|
53922
|
+
content += newPersonasBlock + "\n";
|
|
53923
|
+
}
|
|
53924
|
+
await fs.writeFile(configPath, content);
|
|
53925
|
+
}
|
|
53926
|
+
function printWizardHeader(stdout) {
|
|
53927
|
+
stdout.write("\n\u250C\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\u2510\n");
|
|
53928
|
+
stdout.write("\u2502 Sidekick Setup Wizard \u2502\n");
|
|
53929
|
+
stdout.write("\u2502 \u2502\n");
|
|
53930
|
+
stdout.write("\u2502 This wizard configures sidekick for Claude Code. \u2502\n");
|
|
53931
|
+
stdout.write("\u2502 Run 'sidekick setup' again anytime to reconfigure. \u2502\n");
|
|
53932
|
+
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");
|
|
53933
|
+
}
|
|
53934
|
+
async function runStep1Statusline(wctx) {
|
|
53935
|
+
const { ctx, homeDir, projectDir, logger } = wctx;
|
|
53936
|
+
(0, prompts_js_1.printHeader)(ctx, "Step 1: Statusline Configuration", "Claude Code plugins cannot provide statusline config directly.");
|
|
53937
|
+
const statuslineScope = await (0, prompts_js_1.promptSelect)(ctx, "Where should sidekick configure your statusline?", [
|
|
53938
|
+
{ value: "user", label: "User-level (~/.claude/settings.json)", description: "Works in all projects" },
|
|
53939
|
+
{ value: "project", label: "Project-level (.claude/settings.local.json)", description: "This project only" }
|
|
53940
|
+
]);
|
|
53941
|
+
const statuslinePath = statuslineScope === "user" ? path.join(homeDir, ".claude", "settings.json") : path.join(projectDir, ".claude", "settings.local.json");
|
|
53942
|
+
await configureStatusline(statuslinePath, logger);
|
|
53943
|
+
(0, prompts_js_1.printStatus)(ctx, "success", `Statusline configured in ${statuslinePath}`);
|
|
53944
|
+
return statuslineScope;
|
|
53945
|
+
}
|
|
53946
|
+
async function runStep2Gitignore(wctx, force) {
|
|
53947
|
+
const { ctx, projectDir } = wctx;
|
|
53948
|
+
const currentStatus = await (0, core_1.detectGitignoreStatus)(projectDir);
|
|
53949
|
+
if (currentStatus === "installed") {
|
|
53950
|
+
if (!force) {
|
|
53951
|
+
(0, prompts_js_1.printStatus)(ctx, "success", "Sidekick entries already present in .gitignore");
|
|
53952
|
+
}
|
|
53953
|
+
return "installed";
|
|
53954
|
+
}
|
|
53955
|
+
if (force) {
|
|
53956
|
+
const result2 = await (0, core_1.installGitignoreSection)(projectDir);
|
|
53957
|
+
return result2.status === "error" ? "missing" : "installed";
|
|
53958
|
+
}
|
|
53959
|
+
(0, prompts_js_1.printHeader)(ctx, "Step 2: Git Configuration", "Sidekick creates logs and session data that should not be committed.");
|
|
53960
|
+
const shouldInstall = await (0, prompts_js_1.promptConfirm)(ctx, "Update .gitignore to exclude sidekick transient files?", true);
|
|
53961
|
+
if (!shouldInstall) {
|
|
53962
|
+
(0, prompts_js_1.printStatus)(ctx, "info", "Skipping .gitignore configuration (you can manage it manually)");
|
|
53963
|
+
return "missing";
|
|
53964
|
+
}
|
|
53965
|
+
const result = await (0, core_1.installGitignoreSection)(projectDir);
|
|
53966
|
+
if (result.status === "error") {
|
|
53967
|
+
(0, prompts_js_1.printStatus)(ctx, "warning", `Failed to update .gitignore: ${result.error}`);
|
|
53968
|
+
return "missing";
|
|
53969
|
+
}
|
|
53970
|
+
const message = result.status === "already-installed" ? "Sidekick entries already present in .gitignore" : "Added sidekick section to .gitignore";
|
|
53971
|
+
(0, prompts_js_1.printStatus)(ctx, "success", message);
|
|
53972
|
+
return "installed";
|
|
53973
|
+
}
|
|
53974
|
+
async function runStep3Personas(wctx) {
|
|
53975
|
+
const { ctx, homeDir, projectDir } = wctx;
|
|
53976
|
+
const stdout = ctx.stdout;
|
|
53977
|
+
(0, prompts_js_1.printHeader)(ctx, "Step 3: Persona Features", "Sidekick includes AI personas (Marvin, GLaDOS, Skippy, etc.) that add\npersonality to your coding sessions with snarky messages and contextual nudges.");
|
|
53978
|
+
stdout.write("These require an OpenRouter API key (small cost per message).\n\n");
|
|
53979
|
+
const wantPersonas = await (0, prompts_js_1.promptConfirm)(ctx, "Enable persona features?", true);
|
|
53980
|
+
let apiKeyHealth = "not-required";
|
|
53981
|
+
if (!wantPersonas) {
|
|
53982
|
+
await writePersonaConfig(projectDir, homeDir, false);
|
|
53983
|
+
(0, prompts_js_1.printStatus)(ctx, "info", "Personas disabled");
|
|
53984
|
+
} else {
|
|
53985
|
+
await writePersonaConfig(projectDir, homeDir, true);
|
|
53986
|
+
apiKeyHealth = await configureApiKey(wctx);
|
|
53987
|
+
}
|
|
53988
|
+
return { wantPersonas, apiKeyHealth };
|
|
53989
|
+
}
|
|
53990
|
+
async function configureApiKey(wctx) {
|
|
53991
|
+
const { ctx, homeDir, projectDir, logger } = wctx;
|
|
53992
|
+
const stdout = ctx.stdout;
|
|
53993
|
+
const existingKey = await findExistingApiKey("OPENROUTER_API_KEY", homeDir, projectDir);
|
|
53994
|
+
if (existingKey) {
|
|
53995
|
+
(0, prompts_js_1.printStatus)(ctx, "success", "OPENROUTER_API_KEY found");
|
|
53996
|
+
stdout.write("Validating... ");
|
|
53997
|
+
const result2 = await (0, validate_api_key_js_1.validateOpenRouterKey)(existingKey, logger);
|
|
53998
|
+
if (result2.valid) {
|
|
53999
|
+
stdout.write("valid!\n");
|
|
54000
|
+
return "healthy";
|
|
54001
|
+
} else {
|
|
54002
|
+
stdout.write(`invalid (${result2.error})
|
|
54003
|
+
`);
|
|
54004
|
+
return "invalid";
|
|
54005
|
+
}
|
|
54006
|
+
}
|
|
54007
|
+
(0, prompts_js_1.printStatus)(ctx, "warning", "OPENROUTER_API_KEY not found");
|
|
54008
|
+
const configureNow = await (0, prompts_js_1.promptConfirm)(ctx, "Configure API key now?", true);
|
|
54009
|
+
if (!configureNow) {
|
|
54010
|
+
stdout.write("\n");
|
|
54011
|
+
(0, prompts_js_1.printStatus)(ctx, "warning", "Persona features will show warnings in the statusline until an API key is configured.");
|
|
54012
|
+
stdout.write("Run 'sidekick setup' again or ask Claude to help configure API keys using /sidekick-config.\n");
|
|
54013
|
+
return "missing";
|
|
54014
|
+
}
|
|
54015
|
+
const keyScope = await (0, prompts_js_1.promptSelect)(ctx, "Where should the API key be stored?", [
|
|
54016
|
+
{ value: "user", label: "User-level (~/.sidekick/.env)", description: "Works in all projects" },
|
|
54017
|
+
{ value: "project", label: "Project-level (.sidekick/.env)", description: "This project only" }
|
|
54018
|
+
]);
|
|
54019
|
+
const apiKey = await (0, prompts_js_1.promptInput)(ctx, "Paste your OpenRouter API key");
|
|
54020
|
+
stdout.write("Validating... ");
|
|
54021
|
+
const result = await (0, validate_api_key_js_1.validateOpenRouterKey)(apiKey, logger);
|
|
54022
|
+
const envPath = keyScope === "user" ? path.join(homeDir, ".sidekick", ".env") : path.join(projectDir, ".sidekick", ".env");
|
|
54023
|
+
if (result.valid) {
|
|
54024
|
+
stdout.write("valid!\n");
|
|
54025
|
+
await writeApiKeyToEnv(envPath, "OPENROUTER_API_KEY", apiKey);
|
|
54026
|
+
(0, prompts_js_1.printStatus)(ctx, "success", `API key saved to ${envPath}`);
|
|
54027
|
+
return "healthy";
|
|
54028
|
+
} else {
|
|
54029
|
+
stdout.write(`invalid (${result.error})
|
|
54030
|
+
`);
|
|
54031
|
+
(0, prompts_js_1.printStatus)(ctx, "warning", "API key validation failed, saving anyway");
|
|
54032
|
+
await writeApiKeyToEnv(envPath, "OPENROUTER_API_KEY", apiKey);
|
|
54033
|
+
return "invalid";
|
|
54034
|
+
}
|
|
54035
|
+
}
|
|
54036
|
+
async function runStep4AutoConfig(wctx) {
|
|
54037
|
+
const { ctx } = wctx;
|
|
54038
|
+
(0, prompts_js_1.printHeader)(ctx, "Step 4: Project Auto-Configuration");
|
|
54039
|
+
const autoConfig = await (0, prompts_js_1.promptSelect)(ctx, "When sidekick runs in a new project for the first time:", [
|
|
54040
|
+
{ value: "auto", label: "Auto-configure using my defaults", description: "Recommended" },
|
|
54041
|
+
{ value: "ask", label: "Ask me each time" },
|
|
54042
|
+
{ value: "manual", label: "Do nothing", description: "Manual setup only" }
|
|
54043
|
+
]);
|
|
54044
|
+
return autoConfig;
|
|
54045
|
+
}
|
|
54046
|
+
async function writeStatusFiles(wctx, state) {
|
|
54047
|
+
const { setupService } = wctx;
|
|
54048
|
+
const { statuslineScope, gitignoreStatus, wantPersonas, apiKeyHealth, autoConfig } = state;
|
|
54049
|
+
const userStatus = {
|
|
54050
|
+
version: 1,
|
|
54051
|
+
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
54052
|
+
preferences: {
|
|
54053
|
+
autoConfigureProjects: autoConfig === "auto",
|
|
54054
|
+
defaultStatuslineScope: statuslineScope,
|
|
54055
|
+
defaultApiKeyScope: wantPersonas ? "user" : "skip"
|
|
54056
|
+
},
|
|
54057
|
+
statusline: "configured",
|
|
54058
|
+
apiKeys: {
|
|
54059
|
+
OPENROUTER_API_KEY: apiKeyHealth,
|
|
54060
|
+
OPENAI_API_KEY: "not-required"
|
|
54061
|
+
}
|
|
54062
|
+
};
|
|
54063
|
+
await setupService.writeUserStatus(userStatus);
|
|
54064
|
+
const projectStatus = {
|
|
54065
|
+
version: 1,
|
|
54066
|
+
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
54067
|
+
autoConfigured: false,
|
|
54068
|
+
statusline: statuslineScope === "project" ? "configured" : "user",
|
|
54069
|
+
apiKeys: {
|
|
54070
|
+
OPENROUTER_API_KEY: apiKeyHealth === "healthy" ? "user" : apiKeyHealth,
|
|
54071
|
+
OPENAI_API_KEY: "not-required"
|
|
54072
|
+
},
|
|
54073
|
+
gitignore: gitignoreStatus
|
|
54074
|
+
};
|
|
54075
|
+
await setupService.writeProjectStatus(projectStatus);
|
|
54076
|
+
}
|
|
54077
|
+
function printSummary(wctx, state) {
|
|
54078
|
+
const { ctx } = wctx;
|
|
54079
|
+
const { statuslineScope, gitignoreStatus, wantPersonas, apiKeyHealth, autoConfig } = state;
|
|
54080
|
+
const stdout = ctx.stdout;
|
|
54081
|
+
(0, prompts_js_1.printHeader)(ctx, "Summary");
|
|
54082
|
+
(0, prompts_js_1.printStatus)(ctx, "success", `Statusline: ${statuslineScope === "user" ? "User-level" : "Project-level"}`);
|
|
54083
|
+
(0, prompts_js_1.printStatus)(ctx, gitignoreStatus === "installed" ? "success" : "info", `Gitignore: ${gitignoreStatus === "installed" ? "Configured" : "Skipped"}`);
|
|
54084
|
+
(0, prompts_js_1.printStatus)(ctx, wantPersonas ? "success" : "info", `Personas: ${wantPersonas ? "Enabled" : "Disabled"}`);
|
|
54085
|
+
const apiKeyStatusType = getApiKeyStatusType(apiKeyHealth);
|
|
54086
|
+
(0, prompts_js_1.printStatus)(ctx, apiKeyStatusType, `API Key: ${apiKeyHealth}`);
|
|
54087
|
+
(0, prompts_js_1.printStatus)(ctx, "success", `Auto-configure: ${autoConfig === "auto" ? "Enabled" : "Disabled"}`);
|
|
54088
|
+
stdout.write("\n");
|
|
54089
|
+
stdout.write("Restart Claude Code to see your statusline: claude --continue\n");
|
|
54090
|
+
}
|
|
54091
|
+
async function runWizard(projectDir, logger, stdout, options) {
|
|
54092
|
+
const homeDir = os.homedir();
|
|
54093
|
+
const wctx = {
|
|
54094
|
+
ctx: {
|
|
54095
|
+
stdin: options.stdin ?? process.stdin,
|
|
54096
|
+
stdout
|
|
54097
|
+
},
|
|
54098
|
+
homeDir,
|
|
54099
|
+
projectDir,
|
|
54100
|
+
logger,
|
|
54101
|
+
setupService: new core_1.SetupStatusService(projectDir, { homeDir, logger })
|
|
54102
|
+
};
|
|
54103
|
+
const force = options.force ?? false;
|
|
54104
|
+
if (!force) {
|
|
54105
|
+
printWizardHeader(stdout);
|
|
54106
|
+
}
|
|
54107
|
+
const statuslineScope = force ? "user" : await runStep1Statusline(wctx);
|
|
54108
|
+
const gitignoreStatus = await runStep2Gitignore(wctx, force);
|
|
54109
|
+
const { wantPersonas, apiKeyHealth } = force ? { wantPersonas: true, apiKeyHealth: "not-required" } : await runStep3Personas(wctx);
|
|
54110
|
+
const autoConfig = force ? "auto" : await runStep4AutoConfig(wctx);
|
|
54111
|
+
if (force) {
|
|
54112
|
+
const statuslinePath = path.join(homeDir, ".claude", "settings.json");
|
|
54113
|
+
await configureStatusline(statuslinePath, logger);
|
|
54114
|
+
}
|
|
54115
|
+
const state = { statuslineScope, gitignoreStatus, wantPersonas, apiKeyHealth, autoConfig };
|
|
54116
|
+
await writeStatusFiles(wctx, state);
|
|
54117
|
+
if (!force) {
|
|
54118
|
+
printSummary(wctx, state);
|
|
54119
|
+
} else {
|
|
54120
|
+
stdout.write("Setup complete (force mode):\n");
|
|
54121
|
+
stdout.write(` Statusline: user-level (~/.claude/settings.json)
|
|
54122
|
+
`);
|
|
54123
|
+
stdout.write(` Gitignore: ${gitignoreStatus === "installed" ? "configured" : "skipped"}
|
|
54124
|
+
`);
|
|
54125
|
+
stdout.write(` Personas: enabled (API key not configured)
|
|
54126
|
+
`);
|
|
54127
|
+
stdout.write(` Auto-configure: enabled
|
|
54128
|
+
`);
|
|
54129
|
+
}
|
|
54130
|
+
return { exitCode: 0 };
|
|
54131
|
+
}
|
|
54132
|
+
async function runDoctor(projectDir, logger, stdout) {
|
|
54133
|
+
const homeDir = os.homedir();
|
|
54134
|
+
const setupService = new core_1.SetupStatusService(projectDir, { homeDir, logger });
|
|
54135
|
+
stdout.write("\nSidekick Doctor\n");
|
|
54136
|
+
stdout.write("===============\n\n");
|
|
54137
|
+
const statusline = await setupService.getStatuslineHealth();
|
|
54138
|
+
const gitignore = await (0, core_1.detectGitignoreStatus)(projectDir);
|
|
54139
|
+
const apiKey = await setupService.getEffectiveApiKeyHealth("OPENROUTER_API_KEY");
|
|
54140
|
+
const isHealthy = await setupService.isHealthy();
|
|
54141
|
+
const statuslineIcon = statusline === "configured" ? "\u2713" : "\u26A0";
|
|
54142
|
+
const gitignoreIcon = gitignore === "installed" ? "\u2713" : "\u26A0";
|
|
54143
|
+
const apiKeyIcon = apiKey === "healthy" || apiKey === "not-required" ? "\u2713" : "\u26A0";
|
|
54144
|
+
const overallIcon = isHealthy && gitignore === "installed" ? "\u2713" : "\u26A0";
|
|
54145
|
+
stdout.write(`${statuslineIcon} Statusline: ${statusline}
|
|
54146
|
+
`);
|
|
54147
|
+
stdout.write(`${gitignoreIcon} Gitignore: ${gitignore}
|
|
54148
|
+
`);
|
|
54149
|
+
stdout.write(`${apiKeyIcon} OpenRouter API Key: ${apiKey}
|
|
54150
|
+
`);
|
|
54151
|
+
stdout.write(`${overallIcon} Overall: ${isHealthy && gitignore === "installed" ? "healthy" : "needs attention"}
|
|
54152
|
+
`);
|
|
54153
|
+
if (!isHealthy || gitignore !== "installed") {
|
|
54154
|
+
stdout.write("\nRun 'sidekick setup' to configure.\n");
|
|
54155
|
+
}
|
|
54156
|
+
return { exitCode: isHealthy && gitignore === "installed" ? 0 : 1 };
|
|
54157
|
+
}
|
|
54158
|
+
async function handleSetupCommand(projectDir, logger, stdout, options = {}) {
|
|
54159
|
+
if (options.checkOnly) {
|
|
54160
|
+
return runDoctor(projectDir, logger, stdout);
|
|
54161
|
+
}
|
|
54162
|
+
return runWizard(projectDir, logger, stdout, options);
|
|
54163
|
+
}
|
|
54164
|
+
}
|
|
54165
|
+
});
|
|
54166
|
+
|
|
54167
|
+
// ../sidekick-cli/dist/commands/setup.js
|
|
54168
|
+
var require_setup2 = __commonJS({
|
|
54169
|
+
"../sidekick-cli/dist/commands/setup.js"(exports2) {
|
|
54170
|
+
"use strict";
|
|
54171
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
54172
|
+
exports2.handleSetupCommand = void 0;
|
|
54173
|
+
var index_js_1 = require_setup();
|
|
54174
|
+
Object.defineProperty(exports2, "handleSetupCommand", { enumerable: true, get: function() {
|
|
54175
|
+
return index_js_1.handleSetupCommand;
|
|
54176
|
+
} });
|
|
54177
|
+
}
|
|
54178
|
+
});
|
|
54179
|
+
|
|
53008
54180
|
// ../sidekick-cli/dist/cli.js
|
|
53009
54181
|
var require_cli = __commonJS({
|
|
53010
54182
|
"../sidekick-cli/dist/cli.js"(exports2) {
|
|
@@ -53059,6 +54231,7 @@ var require_cli = __commonJS({
|
|
|
53059
54231
|
var promises_12 = require("node:fs/promises");
|
|
53060
54232
|
var node_stream_1 = require("node:stream");
|
|
53061
54233
|
var yargs_parser_1 = __importDefault2(require_build());
|
|
54234
|
+
var VERSION = true ? "0.0.5-alpha" : "dev";
|
|
53062
54235
|
function isInSandbox() {
|
|
53063
54236
|
return process.env.SANDBOX_RUNTIME === "1";
|
|
53064
54237
|
}
|
|
@@ -53073,11 +54246,12 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
|
|
|
53073
54246
|
var runtime_1 = require_runtime();
|
|
53074
54247
|
function parseArgs(argv) {
|
|
53075
54248
|
const parsed = (0, yargs_parser_1.default)(argv, {
|
|
53076
|
-
boolean: ["wait", "open", "prefer-project", "help", "kill", "force"],
|
|
54249
|
+
boolean: ["wait", "open", "prefer-project", "help", "version", "kill", "force"],
|
|
53077
54250
|
string: ["project-dir", "log-level", "format", "host", "session-id", "type"],
|
|
53078
54251
|
number: ["port", "width"],
|
|
53079
54252
|
alias: {
|
|
53080
|
-
h: "help"
|
|
54253
|
+
h: "help",
|
|
54254
|
+
v: "version"
|
|
53081
54255
|
},
|
|
53082
54256
|
configuration: {
|
|
53083
54257
|
"camel-case-expansion": false
|
|
@@ -53098,6 +54272,7 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
|
|
|
53098
54272
|
sessionIdArg: parsed["session-id"],
|
|
53099
54273
|
messageType: parsed.type,
|
|
53100
54274
|
help: Boolean(parsed.help),
|
|
54275
|
+
version: Boolean(parsed.version),
|
|
53101
54276
|
kill: Boolean(parsed.kill),
|
|
53102
54277
|
force: Boolean(parsed.force),
|
|
53103
54278
|
_: parsed._
|
|
@@ -53174,6 +54349,19 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
|
|
|
53174
54349
|
if (!hookMode || !projectRoot) {
|
|
53175
54350
|
return { started: false };
|
|
53176
54351
|
}
|
|
54352
|
+
try {
|
|
54353
|
+
const { SetupStatusService } = await Promise.resolve().then(() => __importStar(require_dist3()));
|
|
54354
|
+
const setupService = new SetupStatusService(projectRoot);
|
|
54355
|
+
const setupState = await setupService.getSetupState();
|
|
54356
|
+
if (setupState !== "healthy") {
|
|
54357
|
+
logger.debug("Skipping daemon start - setup not healthy", { setupState });
|
|
54358
|
+
return { started: false };
|
|
54359
|
+
}
|
|
54360
|
+
} catch (err) {
|
|
54361
|
+
logger.warn("Failed to check setup status, proceeding with daemon start", {
|
|
54362
|
+
error: err instanceof Error ? err.message : String(err)
|
|
54363
|
+
});
|
|
54364
|
+
}
|
|
53177
54365
|
try {
|
|
53178
54366
|
const { DaemonClient } = await Promise.resolve().then(() => __importStar(require_dist3()));
|
|
53179
54367
|
const daemonClient = new DaemonClient(projectRoot, logger);
|
|
@@ -53198,9 +54386,12 @@ Commands:
|
|
|
53198
54386
|
statusline Render the status line (used by hooks)
|
|
53199
54387
|
dev-mode <subcommand> Manage development hooks (enable, disable, status, clean)
|
|
53200
54388
|
ui Launch the web UI
|
|
54389
|
+
setup Run the setup wizard (configure statusline, API keys)
|
|
54390
|
+
doctor Check sidekick health (alias: setup --check)
|
|
53201
54391
|
|
|
53202
54392
|
Global Options:
|
|
53203
54393
|
--help, -h Show this help message
|
|
54394
|
+
--version, -v Show version number
|
|
53204
54395
|
--format=<format> Output format: json or table (command-specific)
|
|
53205
54396
|
--width=<n> Table width in characters (default: 100)
|
|
53206
54397
|
--project-dir=<path> Override project directory
|
|
@@ -53284,7 +54475,9 @@ Run 'sidekick hook --help' for available hooks.
|
|
|
53284
54475
|
return { exitCode: 1, stdout: SANDBOX_ERROR_MESSAGE, stderr: "" };
|
|
53285
54476
|
}
|
|
53286
54477
|
const { handleDaemonCommand } = await Promise.resolve().then(() => __importStar(require_daemon()));
|
|
53287
|
-
const result = await handleDaemonCommand(subcommand, runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
|
|
54478
|
+
const result = await handleDaemonCommand(subcommand, runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
|
|
54479
|
+
wait: parsed.wait
|
|
54480
|
+
});
|
|
53288
54481
|
return { exitCode: result.exitCode, stdout: "", stderr: "" };
|
|
53289
54482
|
}
|
|
53290
54483
|
if (parsed.command === "statusline") {
|
|
@@ -53339,6 +54532,21 @@ Run 'sidekick hook --help' for available hooks.
|
|
|
53339
54532
|
const result = await handleDevModeCommand(subcommand, runtime.projectRoot || process.cwd(), runtime.logger, stdout, { force: Boolean(parsed.force) });
|
|
53340
54533
|
return { exitCode: result.exitCode, stdout: "", stderr: "" };
|
|
53341
54534
|
}
|
|
54535
|
+
if (parsed.command === "setup") {
|
|
54536
|
+
const { handleSetupCommand } = await Promise.resolve().then(() => __importStar(require_setup2()));
|
|
54537
|
+
const result = await handleSetupCommand(runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
|
|
54538
|
+
checkOnly: parsed.help ? false : parsed._?.[1] === "--check",
|
|
54539
|
+
stdin: process.stdin
|
|
54540
|
+
});
|
|
54541
|
+
return { exitCode: result.exitCode, stdout: "", stderr: "" };
|
|
54542
|
+
}
|
|
54543
|
+
if (parsed.command === "doctor") {
|
|
54544
|
+
const { handleSetupCommand } = await Promise.resolve().then(() => __importStar(require_setup2()));
|
|
54545
|
+
const result = await handleSetupCommand(runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
|
|
54546
|
+
checkOnly: true
|
|
54547
|
+
});
|
|
54548
|
+
return { exitCode: result.exitCode, stdout: "", stderr: "" };
|
|
54549
|
+
}
|
|
53342
54550
|
const errorMsg = `Unknown command: ${parsed.command}
|
|
53343
54551
|
Run 'sidekick help' for available commands.
|
|
53344
54552
|
`;
|
|
@@ -53365,6 +54573,16 @@ Run 'sidekick help' for available commands.
|
|
|
53365
54573
|
}
|
|
53366
54574
|
async function runCli(options) {
|
|
53367
54575
|
const stdout = options.stdout ?? new node_stream_1.PassThrough();
|
|
54576
|
+
const quickParsed = (0, yargs_parser_1.default)(options.argv, {
|
|
54577
|
+
boolean: ["version"],
|
|
54578
|
+
alias: { v: "version" }
|
|
54579
|
+
});
|
|
54580
|
+
if (quickParsed.version) {
|
|
54581
|
+
const versionOutput = `${VERSION}
|
|
54582
|
+
`;
|
|
54583
|
+
stdout.write(versionOutput);
|
|
54584
|
+
return { exitCode: 0, stdout: versionOutput, stderr: "" };
|
|
54585
|
+
}
|
|
53368
54586
|
const initResult = initializeRuntime(options);
|
|
53369
54587
|
if (initResult.shouldExit) {
|
|
53370
54588
|
return { exitCode: 0, stdout: "", stderr: "" };
|