@scotthamilton77/sidekick 0.0.1-alpha → 0.0.4-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.
Files changed (3) hide show
  1. package/dist/bin.js +1310 -294
  2. package/dist/daemon.js +70160 -0
  3. 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
 
@@ -27684,134 +27751,19 @@ var require_runtime_context = __commonJS({
27684
27751
  }
27685
27752
  });
27686
27753
 
27687
- // ../sidekick-core/dist/scope.js
27688
- var require_scope = __commonJS({
27689
- "../sidekick-core/dist/scope.js"(exports2) {
27754
+ // ../sidekick-core/dist/project-root.js
27755
+ var require_project_root = __commonJS({
27756
+ "../sidekick-core/dist/project-root.js"(exports2) {
27690
27757
  "use strict";
27691
27758
  var __importDefault2 = exports2 && exports2.__importDefault || function(mod) {
27692
27759
  return mod && mod.__esModule ? mod : { "default": mod };
27693
27760
  };
27694
27761
  Object.defineProperty(exports2, "__esModule", { value: true });
27695
- exports2.resolveScope = resolveScope;
27696
- var node_fs_1 = require("node:fs");
27697
- var node_os_1 = require("node:os");
27762
+ exports2.resolveProjectRoot = resolveProjectRoot;
27698
27763
  var node_path_1 = __importDefault2(require("node:path"));
27699
- var SIDEKICK_HOOK_SEGMENT = `${node_path_1.default.sep}.claude${node_path_1.default.sep}hooks${node_path_1.default.sep}sidekick${node_path_1.default.sep}`;
27700
- function normalizeHookPath(hookScriptPath) {
27701
- try {
27702
- return (0, node_fs_1.realpathSync)(hookScriptPath);
27703
- } catch {
27704
- return node_path_1.default.resolve(hookScriptPath);
27705
- }
27706
- }
27707
- function deriveProjectRootFromHook(hookScriptPath) {
27708
- const index = hookScriptPath.lastIndexOf(SIDEKICK_HOOK_SEGMENT);
27709
- if (index === -1) {
27710
- return void 0;
27711
- }
27712
- return hookScriptPath.slice(0, index);
27713
- }
27714
- function findNearestSidekickDir(startDir) {
27715
- let current = node_path_1.default.resolve(startDir);
27716
- while (true) {
27717
- const candidate = node_path_1.default.join(current, ".claude", "hooks", "sidekick");
27718
- if ((0, node_fs_1.existsSync)(candidate)) {
27719
- return candidate;
27720
- }
27721
- const parent = node_path_1.default.dirname(current);
27722
- if (parent === current) {
27723
- return void 0;
27724
- }
27725
- current = parent;
27726
- }
27727
- }
27728
- function projectHasSidekickInstall(projectDir) {
27729
- const targets = [
27730
- node_path_1.default.join(projectDir, ".claude", "settings.json"),
27731
- node_path_1.default.join(projectDir, ".claude", "settings.json.local")
27732
- ];
27733
- return targets.some((filePath) => {
27734
- if (!(0, node_fs_1.existsSync)(filePath)) {
27735
- return false;
27736
- }
27737
- try {
27738
- const contents = (0, node_fs_1.readFileSync)(filePath, "utf8");
27739
- return contents.toLowerCase().includes("sidekick");
27740
- } catch {
27741
- return false;
27742
- }
27743
- });
27744
- }
27745
- function normalizeDir(dir) {
27746
- try {
27747
- return (0, node_fs_1.realpathSync)(node_path_1.default.resolve(dir));
27748
- } catch {
27749
- return node_path_1.default.resolve(dir);
27750
- }
27751
- }
27752
- function resolveScope(input) {
27753
- const warnings = [];
27754
- const cwd = input.cwd ? node_path_1.default.resolve(input.cwd) : process.cwd();
27755
- const hookScriptPath = input.hookScriptPath ? normalizeHookPath(input.hookScriptPath) : void 0;
27756
- const providedProjectDir = input.projectDir ? node_path_1.default.resolve(input.projectDir) : void 0;
27757
- const resolvedHomeDir = input.homeDir ? normalizeDir(input.homeDir) : normalizeDir((0, node_os_1.homedir)());
27758
- if (input.scopeOverride) {
27759
- return {
27760
- scope: input.scopeOverride,
27761
- source: "override",
27762
- hookScriptPath,
27763
- projectRoot: input.scopeOverride === "project" ? providedProjectDir : void 0,
27764
- warnings,
27765
- dualInstallDetected: false
27766
- };
27767
- }
27768
- if (hookScriptPath) {
27769
- if (hookScriptPath.startsWith(node_path_1.default.join(resolvedHomeDir, ".claude", "hooks", "sidekick"))) {
27770
- const dualInstallDetected2 = Boolean(providedProjectDir && projectHasSidekickInstall(providedProjectDir));
27771
- return {
27772
- scope: "user",
27773
- source: "hook-script-path",
27774
- hookScriptPath,
27775
- projectRoot: void 0,
27776
- warnings,
27777
- dualInstallDetected: dualInstallDetected2
27778
- };
27779
- }
27780
- const projectRoot = deriveProjectRootFromHook(hookScriptPath);
27781
- if (projectRoot) {
27782
- if (providedProjectDir && node_path_1.default.resolve(projectRoot) !== providedProjectDir) {
27783
- warnings.push("Project directory hint from --project-dir does not match path derived from --hook-script-path. Using hook-script-path.");
27784
- }
27785
- return {
27786
- scope: "project",
27787
- source: "hook-script-path",
27788
- hookScriptPath,
27789
- projectRoot,
27790
- warnings,
27791
- dualInstallDetected: false
27792
- };
27793
- }
27794
- }
27795
- const sidekickDir = findNearestSidekickDir(cwd);
27796
- if (sidekickDir && !sidekickDir.startsWith(node_path_1.default.join(resolvedHomeDir, ".claude", "hooks", "sidekick"))) {
27797
- return {
27798
- scope: "project",
27799
- source: "cwd-fallback",
27800
- hookScriptPath,
27801
- projectRoot: node_path_1.default.resolve(sidekickDir, "..", "..", ".."),
27802
- warnings,
27803
- dualInstallDetected: false
27804
- };
27805
- }
27806
- const dualInstallDetected = Boolean(providedProjectDir && projectHasSidekickInstall(providedProjectDir));
27807
- return {
27808
- scope: "user",
27809
- source: "default",
27810
- hookScriptPath,
27811
- projectRoot: void 0,
27812
- warnings,
27813
- dualInstallDetected
27814
- };
27764
+ function resolveProjectRoot(input) {
27765
+ const projectRoot = input.projectDir ? node_path_1.default.resolve(input.projectDir) : void 0;
27766
+ return { projectRoot };
27815
27767
  }
27816
27768
  }
27817
27769
  });
@@ -32460,7 +32412,6 @@ var require_structured_logging = __commonJS({
32460
32412
  source: "cli",
32461
32413
  context: {
32462
32414
  sessionId: context.sessionId,
32463
- scope: context.scope,
32464
32415
  correlationId: context.correlationId,
32465
32416
  traceId: context.traceId,
32466
32417
  hook: context.hook
@@ -32480,7 +32431,6 @@ var require_structured_logging = __commonJS({
32480
32431
  source: "cli",
32481
32432
  context: {
32482
32433
  sessionId: context.sessionId,
32483
- scope: context.scope,
32484
32434
  correlationId: context.correlationId,
32485
32435
  traceId: context.traceId,
32486
32436
  hook: context.hook
@@ -32502,7 +32452,6 @@ var require_structured_logging = __commonJS({
32502
32452
  source: "daemon",
32503
32453
  context: {
32504
32454
  sessionId: context.sessionId,
32505
- scope: context.scope,
32506
32455
  correlationId: context.correlationId,
32507
32456
  traceId: context.traceId,
32508
32457
  hook: context.hook,
@@ -32524,7 +32473,6 @@ var require_structured_logging = __commonJS({
32524
32473
  source: "daemon",
32525
32474
  context: {
32526
32475
  sessionId: context.sessionId,
32527
- scope: context.scope,
32528
32476
  correlationId: context.correlationId,
32529
32477
  traceId: context.traceId,
32530
32478
  hook: context.hook,
@@ -32546,7 +32494,6 @@ var require_structured_logging = __commonJS({
32546
32494
  source: "daemon",
32547
32495
  context: {
32548
32496
  sessionId: context.sessionId,
32549
- scope: context.scope,
32550
32497
  correlationId: context.correlationId,
32551
32498
  traceId: context.traceId,
32552
32499
  hook: context.hook,
@@ -32568,8 +32515,7 @@ var require_structured_logging = __commonJS({
32568
32515
  time: Date.now(),
32569
32516
  source: "daemon",
32570
32517
  context: {
32571
- sessionId: "",
32572
- scope: "project"
32518
+ sessionId: ""
32573
32519
  },
32574
32520
  payload: {
32575
32521
  metadata
@@ -32585,8 +32531,7 @@ var require_structured_logging = __commonJS({
32585
32531
  time: Date.now(),
32586
32532
  source: "daemon",
32587
32533
  context: {
32588
- sessionId: "",
32589
- scope: "project"
32534
+ sessionId: ""
32590
32535
  },
32591
32536
  payload: {
32592
32537
  metadata
@@ -32602,8 +32547,7 @@ var require_structured_logging = __commonJS({
32602
32547
  time: Date.now(),
32603
32548
  source: "daemon",
32604
32549
  context: {
32605
- sessionId: "",
32606
- scope: "project"
32550
+ sessionId: ""
32607
32551
  },
32608
32552
  payload: {
32609
32553
  metadata
@@ -32619,8 +32563,7 @@ var require_structured_logging = __commonJS({
32619
32563
  time: Date.now(),
32620
32564
  source: "daemon",
32621
32565
  context: {
32622
- sessionId: "",
32623
- scope: "project"
32566
+ sessionId: ""
32624
32567
  },
32625
32568
  payload: {
32626
32569
  metadata
@@ -32636,8 +32579,7 @@ var require_structured_logging = __commonJS({
32636
32579
  time: Date.now(),
32637
32580
  source: "daemon",
32638
32581
  context: {
32639
- sessionId: "",
32640
- scope: "project"
32582
+ sessionId: ""
32641
32583
  },
32642
32584
  payload: {
32643
32585
  metadata
@@ -32656,7 +32598,6 @@ var require_structured_logging = __commonJS({
32656
32598
  source: "cli",
32657
32599
  context: {
32658
32600
  sessionId: context.sessionId,
32659
- scope: context.scope,
32660
32601
  correlationId: context.correlationId,
32661
32602
  traceId: context.traceId,
32662
32603
  hook: context.hook,
@@ -32679,7 +32620,6 @@ var require_structured_logging = __commonJS({
32679
32620
  source: "cli",
32680
32621
  context: {
32681
32622
  sessionId: context.sessionId,
32682
- scope: context.scope,
32683
32623
  correlationId: context.correlationId,
32684
32624
  traceId: context.traceId,
32685
32625
  hook: context.hook,
@@ -32703,7 +32643,6 @@ var require_structured_logging = __commonJS({
32703
32643
  source: "daemon",
32704
32644
  context: {
32705
32645
  sessionId: context.sessionId,
32706
- scope: context.scope,
32707
32646
  correlationId: context.correlationId,
32708
32647
  traceId: context.traceId,
32709
32648
  hook: context.hook,
@@ -32726,7 +32665,6 @@ var require_structured_logging = __commonJS({
32726
32665
  source: "daemon",
32727
32666
  context: {
32728
32667
  sessionId: context.sessionId,
32729
- scope: context.scope,
32730
32668
  correlationId: context.correlationId,
32731
32669
  traceId: context.traceId,
32732
32670
  hook: context.hook,
@@ -32749,7 +32687,6 @@ var require_structured_logging = __commonJS({
32749
32687
  source: "daemon",
32750
32688
  context: {
32751
32689
  sessionId: context.sessionId,
32752
- scope: context.scope,
32753
32690
  correlationId: context.correlationId,
32754
32691
  traceId: context.traceId,
32755
32692
  hook: context.hook,
@@ -32773,7 +32710,6 @@ var require_structured_logging = __commonJS({
32773
32710
  source: "transcript",
32774
32711
  context: {
32775
32712
  sessionId: context.sessionId,
32776
- scope: context.scope,
32777
32713
  correlationId: context.correlationId,
32778
32714
  traceId: context.traceId,
32779
32715
  hook: context.hook,
@@ -32796,7 +32732,6 @@ var require_structured_logging = __commonJS({
32796
32732
  source: "transcript",
32797
32733
  context: {
32798
32734
  sessionId: context.sessionId,
32799
- scope: context.scope,
32800
32735
  correlationId: context.correlationId,
32801
32736
  traceId: context.traceId,
32802
32737
  hook: context.hook,
@@ -32854,7 +32789,10 @@ var require_package3 = __commonJS({
32854
32789
  lint: "eslint packages",
32855
32790
  "lint:fix": "eslint packages --fix",
32856
32791
  format: 'prettier --write "packages/**/*.ts"',
32857
- 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"
32858
32796
  },
32859
32797
  devDependencies: {
32860
32798
  "@eslint/js": "^9.39.1",
@@ -32917,15 +32855,7 @@ var require_daemon_client2 = __commonJS({
32917
32855
  await this.waitForShutdown();
32918
32856
  }
32919
32857
  this.logger.info("Starting daemon...");
32920
- let daemonPath;
32921
- try {
32922
- const pkgPath = require.resolve("@sidekick/daemon/package.json");
32923
- const pkg = require(pkgPath);
32924
- const binPath = pkg.bin ? typeof pkg.bin === "string" ? pkg.bin : pkg.bin["sidekickd"] : pkg.main;
32925
- daemonPath = path_1.default.resolve(path_1.default.dirname(pkgPath), binPath ?? "dist/index.js");
32926
- } catch {
32927
- daemonPath = path_1.default.resolve(__dirname, "../../sidekick-daemon/dist/index.js");
32928
- }
32858
+ const daemonPath = await this.resolveDaemonPath();
32929
32859
  const child = (0, child_process_1.spawn)("node", [daemonPath, this.projectDir], {
32930
32860
  detached: true,
32931
32861
  stdio: "ignore",
@@ -33004,6 +32934,33 @@ var require_daemon_client2 = __commonJS({
33004
32934
  } catch {
33005
32935
  }
33006
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
+ }
33007
32964
  /**
33008
32965
  * Request daemon shutdown (fire-and-forget).
33009
32966
  * Sends shutdown request, receives ack, closes connection immediately.
@@ -33245,6 +33202,346 @@ var require_daemon_client2 = __commonJS({
33245
33202
  }
33246
33203
  });
33247
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
+
33248
33545
  // ../sidekick-core/dist/state/errors.js
33249
33546
  var require_errors4 = __commonJS({
33250
33547
  "../sidekick-core/dist/state/errors.js"(exports2) {
@@ -33395,7 +33692,6 @@ var require_staging_service = __commonJS({
33395
33692
  await this.options.stateService.write(reminderPath, data, types_1.StagedReminderSchema);
33396
33693
  const event = structured_logging_1.LogEvents.reminderStaged({
33397
33694
  sessionId,
33398
- scope: this.options.scope,
33399
33695
  hook: hookName
33400
33696
  }, {
33401
33697
  reminderName: data.name,
@@ -33541,10 +33837,9 @@ var require_staging_service = __commonJS({
33541
33837
  };
33542
33838
  exports2.StagingServiceCore = StagingServiceCore;
33543
33839
  var SessionScopedStagingService = class {
33544
- constructor(core, sessionId, scope) {
33840
+ constructor(core, sessionId) {
33545
33841
  this.core = core;
33546
33842
  this.sessionId = sessionId;
33547
- this.scope = scope;
33548
33843
  }
33549
33844
  // ============================================================================
33550
33845
  // StagingService Interface Implementation (delegates to core)
@@ -33585,12 +33880,6 @@ var require_staging_service = __commonJS({
33585
33880
  getSessionId() {
33586
33881
  return this.sessionId;
33587
33882
  }
33588
- /**
33589
- * Get the scope (for testing/debugging).
33590
- */
33591
- getScope() {
33592
- return this.scope;
33593
- }
33594
33883
  };
33595
33884
  exports2.SessionScopedStagingService = SessionScopedStagingService;
33596
33885
  }
@@ -33685,8 +33974,7 @@ var require_handler_registry2 = __commonJS({
33685
33974
  this.handlers = [];
33686
33975
  this.context = {
33687
33976
  sessionId: options.sessionId,
33688
- transcriptPath: options.transcriptPath,
33689
- scope: options.scope
33977
+ transcriptPath: options.transcriptPath
33690
33978
  };
33691
33979
  }
33692
33980
  /**
@@ -33802,7 +34090,7 @@ var require_handler_registry2 = __commonJS({
33802
34090
  const event = this.buildTranscriptEvent(eventType, entry, lineNumber, isBulkProcessing);
33803
34091
  const metrics = this.options.getMetrics?.();
33804
34092
  if (metrics) {
33805
- (0, structured_logging_js_1.logEvent)(this.options.logger, structured_logging_js_1.LogEvents.transcriptEventEmitted({ sessionId: this.options.sessionId, scope: this.options.scope }, {
34093
+ (0, structured_logging_js_1.logEvent)(this.options.logger, structured_logging_js_1.LogEvents.transcriptEventEmitted({ sessionId: this.options.sessionId }, {
33806
34094
  eventType,
33807
34095
  lineNumber,
33808
34096
  uuid: entry.uuid,
@@ -33828,8 +34116,7 @@ var require_handler_registry2 = __commonJS({
33828
34116
  async invokeTranscriptHandler(handler, event) {
33829
34117
  const startTime = Date.now();
33830
34118
  const logContext = {
33831
- sessionId: this.options.sessionId,
33832
- scope: this.options.scope
34119
+ sessionId: this.options.sessionId
33833
34120
  };
33834
34121
  try {
33835
34122
  await handler.handler(event, this.context);
@@ -33875,8 +34162,7 @@ var require_handler_registry2 = __commonJS({
33875
34162
  buildTranscriptEvent(eventType, entry, lineNumber, isBulkProcessing = false) {
33876
34163
  const context = {
33877
34164
  sessionId: this.options.sessionId,
33878
- timestamp: Date.now(),
33879
- scope: this.options.scope
34165
+ timestamp: Date.now()
33880
34166
  };
33881
34167
  const metrics = this.options.getMetrics?.() ?? this.createEmptyMetrics();
33882
34168
  return {
@@ -37247,7 +37533,7 @@ var require_transcript_service = __commonJS({
37247
37533
  await this.persistCompactionHistory();
37248
37534
  if (this.sessionId) {
37249
37535
  const lineCount = (0, node_fs_1.statSync)(snapshotPath).size > 0 ? this.metrics.lastProcessedLine : 0;
37250
- (0, structured_logging_js_1.logEvent)(this.options.logger, structured_logging_js_1.LogEvents.preCompactCaptured({ sessionId: this.sessionId, scope: "project" }, { snapshotPath, lineCount }, { transcriptPath: this.transcriptPath ?? "", metrics: this.deepCloneMetrics() }));
37536
+ (0, structured_logging_js_1.logEvent)(this.options.logger, structured_logging_js_1.LogEvents.preCompactCaptured({ sessionId: this.sessionId }, { snapshotPath, lineCount }, { transcriptPath: this.transcriptPath ?? "", metrics: this.deepCloneMetrics() }));
37251
37537
  }
37252
37538
  this.options.logger.info("Captured pre-compact state", { sessionId: this.sessionId, snapshotPath });
37253
37539
  }
@@ -37831,7 +38117,6 @@ var require_service_factory2 = __commonJS({
37831
38117
  this.stagingCore = new staging_service_js_1.StagingServiceCore({
37832
38118
  stateDir: options.stateDir,
37833
38119
  logger: options.logger,
37834
- scope: options.scope,
37835
38120
  stateService: stagingStateService
37836
38121
  });
37837
38122
  }
@@ -37841,7 +38126,7 @@ var require_service_factory2 = __commonJS({
37841
38126
  */
37842
38127
  getStagingService(sessionId) {
37843
38128
  this.touchSession(sessionId);
37844
- return new staging_service_js_1.SessionScopedStagingService(this.stagingCore, sessionId, this.options.scope);
38129
+ return new staging_service_js_1.SessionScopedStagingService(this.stagingCore, sessionId);
37845
38130
  }
37846
38131
  /**
37847
38132
  * Prepare a session-scoped TranscriptService without starting event emission.
@@ -38447,8 +38732,8 @@ var require_dist3 = __commonJS({
38447
38732
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
38448
38733
  };
38449
38734
  Object.defineProperty(exports2, "__esModule", { value: true });
38450
- exports2.HandlerRegistryImpl = exports2.extractConsumedTimestamp = exports2.createConsumedFilePattern = exports2.CONSUMED_FILE_PATTERN = exports2.filterActiveReminderFiles = exports2.validatePathSegment = exports2.isValidPathSegment = exports2.getReminderPath = exports2.getHookDir = exports2.getStagingRoot = exports2.SessionScopedStagingService = exports2.StagingServiceCore = 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;
38451
- 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;
38452
38737
  var types_1 = require_dist();
38453
38738
  Object.defineProperty(exports2, "TaskTypes", { enumerable: true, get: function() {
38454
38739
  return types_1.TaskTypes;
@@ -38554,7 +38839,7 @@ var require_dist3 = __commonJS({
38554
38839
  return logger_1.createConsoleLogger;
38555
38840
  } });
38556
38841
  __exportStar(require_runtime_context(), exports2);
38557
- __exportStar(require_scope(), exports2);
38842
+ __exportStar(require_project_root(), exports2);
38558
38843
  var structured_logging_1 = require_structured_logging();
38559
38844
  Object.defineProperty(exports2, "createLogManager", { enumerable: true, get: function() {
38560
38845
  return structured_logging_1.createLogManager;
@@ -38581,6 +38866,32 @@ var require_dist3 = __commonJS({
38581
38866
  Object.defineProperty(exports2, "DaemonClient", { enumerable: true, get: function() {
38582
38867
  return daemon_client_1.DaemonClient;
38583
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
+ } });
38584
38895
  var staging_service_1 = require_staging_service();
38585
38896
  Object.defineProperty(exports2, "StagingServiceCore", { enumerable: true, get: function() {
38586
38897
  return staging_service_1.StagingServiceCore;
@@ -38726,9 +39037,9 @@ var require_runtime = __commonJS({
38726
39037
  var node_crypto_1 = require("node:crypto");
38727
39038
  var node_os_1 = require("node:os");
38728
39039
  var node_path_1 = require("node:path");
38729
- function getLogFilePath(scope) {
38730
- if (scope.scope === "project" && scope.projectRoot) {
38731
- return (0, node_path_1.join)(scope.projectRoot, ".sidekick", "logs", "sidekick.log");
39040
+ function getLogFilePath(projectRoot) {
39041
+ if (projectRoot) {
39042
+ return (0, node_path_1.join)(projectRoot, ".sidekick", "logs", "sidekick.log");
38732
39043
  }
38733
39044
  return (0, node_path_1.join)((0, node_os_1.homedir)(), ".sidekick", "logs", "sidekick.log");
38734
39045
  }
@@ -38739,17 +39050,17 @@ var require_runtime = __commonJS({
38739
39050
  bootstrapSink: options.stderrSink ?? process.stderr,
38740
39051
  bufferPreUpgrade: true
38741
39052
  });
38742
- const scope = (0, core_1.resolveScope)(options);
39053
+ const { projectRoot } = (0, core_1.resolveProjectRoot)(options);
38743
39054
  const defaultAssetsDir = options.defaultAssetsDir ?? (0, core_1.getDefaultAssetsDir)();
38744
39055
  const assets = (0, core_1.createAssetResolver)({
38745
39056
  defaultAssetsDir,
38746
- projectRoot: scope.projectRoot,
39057
+ projectRoot,
38747
39058
  homeDir: options.homeDir
38748
39059
  });
38749
39060
  let config;
38750
39061
  try {
38751
39062
  config = (0, core_1.createConfigService)({
38752
- projectRoot: scope.projectRoot,
39063
+ projectRoot,
38753
39064
  homeDir: options.homeDir,
38754
39065
  assets
38755
39066
  });
@@ -38761,15 +39072,14 @@ var require_runtime = __commonJS({
38761
39072
  }
38762
39073
  const effectiveLogLevel = options.logLevel ?? config.core.logging.level;
38763
39074
  let logContext = {
38764
- scope: scope.scope,
38765
39075
  correlationId,
38766
39076
  command: options.command
38767
39077
  };
38768
- const logFilePath = getLogFilePath(scope);
39078
+ const logFilePath = getLogFilePath(projectRoot);
38769
39079
  const isInteractive = options.interactive ?? process.env.SIDEKICK_INTERACTIVE === "1";
38770
39080
  const enableFileLogging = options.enableFileLogging ?? true;
38771
39081
  const logManager = (0, core_1.createLogManager)({
38772
- name: scope.scope === "project" ? "sidekick:cli" : "sidekick:cli:user",
39082
+ name: "sidekick:cli",
38773
39083
  level: effectiveLogLevel,
38774
39084
  context: logContext,
38775
39085
  destinations: {
@@ -38782,7 +39092,7 @@ var require_runtime = __commonJS({
38782
39092
  }
38783
39093
  });
38784
39094
  loggerFacade.upgrade({
38785
- name: scope.scope === "project" ? "sidekick:cli" : "sidekick:cli:user",
39095
+ name: "sidekick:cli",
38786
39096
  level: effectiveLogLevel,
38787
39097
  context: logContext,
38788
39098
  destinations: {
@@ -38810,28 +39120,21 @@ var require_runtime = __commonJS({
38810
39120
  const telemetry = logManager.getTelemetry();
38811
39121
  const cleanupErrorHandlers = (0, core_1.setupGlobalErrorHandlers)(logger);
38812
39122
  logger.debug("Runtime bootstrap complete", {
38813
- scope: scope.scope,
38814
- projectRoot: scope.projectRoot ?? null,
38815
- source: scope.source,
38816
- warnings: scope.warnings,
39123
+ projectRoot: projectRoot ?? null,
38817
39124
  logFile: enableFileLogging ? logFilePath : null
38818
39125
  });
38819
39126
  if (config.sources.length > 0) {
38820
39127
  logger.debug("Configuration sources loaded", { sources: config.sources });
38821
39128
  }
38822
39129
  logger.debug("Asset resolver initialized", { cascadeLayers: assets.cascadeLayers });
38823
- if (scope.dualInstallDetected) {
38824
- logger.warn("Detected project-scope installation while running from user hooks. Deferring to project scope.");
38825
- telemetry.increment("dual_install_detected", { scope: scope.scope });
38826
- }
38827
- const stateRoot = scope.projectRoot ?? (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude");
39130
+ const stateRoot = projectRoot ?? (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude");
38828
39131
  const stateService = new core_1.StateService(stateRoot, { logger });
38829
39132
  return {
38830
39133
  get logger() {
38831
39134
  return logger;
38832
39135
  },
38833
39136
  telemetry,
38834
- scope,
39137
+ projectRoot,
38835
39138
  config,
38836
39139
  assets,
38837
39140
  stateService,
@@ -38842,7 +39145,7 @@ var require_runtime = __commonJS({
38842
39145
  bindSessionId: (sessionId) => {
38843
39146
  logContext = { ...logContext, sessionId };
38844
39147
  const newLogManager = (0, core_1.createLogManager)({
38845
- name: scope.scope === "project" ? "sidekick:cli" : "sidekick:cli:user",
39148
+ name: "sidekick:cli",
38846
39149
  level: effectiveLogLevel,
38847
39150
  context: logContext,
38848
39151
  destinations: {
@@ -47929,7 +48232,6 @@ var require_events2 = __commonJS({
47929
48232
  source: "cli",
47930
48233
  context: {
47931
48234
  sessionId: context.sessionId,
47932
- scope: context.scope,
47933
48235
  correlationId: context.correlationId,
47934
48236
  traceId: context.traceId,
47935
48237
  hook: context.hook,
@@ -47952,7 +48254,6 @@ var require_events2 = __commonJS({
47952
48254
  source: "daemon",
47953
48255
  context: {
47954
48256
  sessionId: context.sessionId,
47955
- scope: context.scope,
47956
48257
  correlationId: context.correlationId,
47957
48258
  traceId: context.traceId,
47958
48259
  hook: context.hook,
@@ -48095,7 +48396,6 @@ var require_consumption_handler_factory = __commonJS({
48095
48396
  }
48096
48397
  (0, core_1.logEvent)(cliCtx.logger, events_js_1.ReminderEvents.reminderConsumed({
48097
48398
  sessionId,
48098
- scope: cliCtx.paths.projectDir ? "project" : "user",
48099
48399
  hook
48100
48400
  }, {
48101
48401
  reminderName: reminder.name,
@@ -48204,7 +48504,7 @@ var require_inject_stop = __commonJS({
48204
48504
  }
48205
48505
  return (0, consumption_handler_factory_js_1.buildDefaultResponse)(reminder, supportsBlocking);
48206
48506
  } else {
48207
- const metrics = reminder.stagedAt ?? { turnCount: 0, toolsThisTurn: 0 };
48507
+ const metrics = reminder.stagedAt ?? { turnCount: 0, toolsThisTurn: 0, toolCount: 0 };
48208
48508
  try {
48209
48509
  await ipc.send("vc-unverified.set", {
48210
48510
  sessionId,
@@ -48214,7 +48514,8 @@ var require_inject_stop = __commonJS({
48214
48514
  },
48215
48515
  metrics: {
48216
48516
  turnCount: metrics.turnCount,
48217
- toolsThisTurn: metrics.toolsThisTurn
48517
+ toolsThisTurn: metrics.toolsThisTurn,
48518
+ toolCount: metrics.toolCount
48218
48519
  }
48219
48520
  });
48220
48521
  } catch (setErr) {
@@ -48243,13 +48544,14 @@ var require_inject_stop = __commonJS({
48243
48544
  if (projectDir) {
48244
48545
  const ipc = new core_1.IpcService(projectDir, cliCtx.logger);
48245
48546
  try {
48246
- const metrics = reminder.stagedAt ?? { turnCount: 0, toolsThisTurn: 0 };
48547
+ const metrics = reminder.stagedAt ?? { turnCount: 0, toolsThisTurn: 0, toolCount: 0 };
48247
48548
  await ipc.send("reminder.consumed", {
48248
48549
  sessionId,
48249
48550
  reminderName: reminder.name,
48250
48551
  metrics: {
48251
48552
  turnCount: metrics.turnCount,
48252
- toolsThisTurn: metrics.toolsThisTurn
48553
+ toolsThisTurn: metrics.toolsThisTurn,
48554
+ toolCount: metrics.toolCount
48253
48555
  }
48254
48556
  });
48255
48557
  } finally {
@@ -48702,21 +49004,19 @@ var require_context2 = __commonJS({
48702
49004
  var feature_reminders_1 = require_dist4();
48703
49005
  function buildCLIContext(options) {
48704
49006
  const { runtime, sessionId, transcriptPath } = options;
48705
- if (!runtime.scope.projectRoot) {
48706
- throw new Error("Cannot build CLIContext without project root - reminder consumption requires project scope");
49007
+ if (!runtime.projectRoot) {
49008
+ throw new Error("Cannot build CLIContext without project root - reminder consumption requires project root");
48707
49009
  }
48708
- const projectRoot = runtime.scope.projectRoot;
49010
+ const projectRoot = runtime.projectRoot;
48709
49011
  const paths = {
48710
49012
  projectDir: projectRoot,
48711
49013
  userConfigDir: (0, node_path_1.join)((0, node_os_1.homedir)(), ".sidekick"),
48712
- projectConfigDir: (0, node_path_1.join)(projectRoot, ".sidekick"),
48713
- hookScriptPath: runtime.scope.hookScriptPath
49014
+ projectConfigDir: (0, node_path_1.join)(projectRoot, ".sidekick")
48714
49015
  };
48715
49016
  const handlers = new core_1.HandlerRegistryImpl({
48716
49017
  logger: runtime.logger,
48717
49018
  sessionId,
48718
- transcriptPath,
48719
- scope: runtime.scope.scope
49019
+ transcriptPath
48720
49020
  });
48721
49021
  const daemon = new core_1.DaemonClient(projectRoot, runtime.logger);
48722
49022
  const context = {
@@ -48871,11 +49171,10 @@ var require_hook = __commonJS({
48871
49171
  }
48872
49172
  };
48873
49173
  }
48874
- function buildHookEvent(hookName, input, correlationId, scope) {
49174
+ function buildHookEvent(hookName, input, correlationId) {
48875
49175
  const context = {
48876
49176
  sessionId: input.sessionId,
48877
49177
  timestamp: Date.now(),
48878
- scope,
48879
49178
  correlationId
48880
49179
  };
48881
49180
  switch (hookName) {
@@ -48914,16 +49213,15 @@ ${daemonResponse.additionalContext}` : cliResponse.additionalContext;
48914
49213
  return merged;
48915
49214
  }
48916
49215
  async function handleHookCommand(hookName, options, logger, stdout) {
48917
- const { projectRoot, hookInput, correlationId, scope, runtime } = options;
49216
+ const { projectRoot, hookInput, correlationId, runtime } = options;
48918
49217
  const startTime = Date.now();
48919
49218
  const logContext = {
48920
49219
  sessionId: hookInput.sessionId,
48921
- scope,
48922
49220
  correlationId,
48923
49221
  hook: hookName
48924
49222
  };
48925
49223
  (0, core_1.logEvent)(logger, core_1.LogEvents.hookReceived(logContext, { cwd: hookInput.cwd, mode: "hook" }));
48926
- const event = buildHookEvent(hookName, hookInput, correlationId, scope);
49224
+ const event = buildHookEvent(hookName, hookInput, correlationId);
48927
49225
  logger.debug("Dispatching hook event to daemon", {
48928
49226
  hook: hookName,
48929
49227
  sessionId: hookInput.sessionId
@@ -49125,7 +49423,7 @@ var require_hook_command = __commonJS({
49125
49423
  }
49126
49424
  }
49127
49425
  async function handleUnifiedHookCommand(hookName, options, logger, stdout) {
49128
- const { projectRoot, hookInput, correlationId, scope, runtime } = options;
49426
+ const { projectRoot, hookInput, correlationId, runtime } = options;
49129
49427
  logger.debug("Unified hook command invoked", { hookName, sessionId: hookInput.sessionId });
49130
49428
  let internalOutput = "";
49131
49429
  const captureStream = {
@@ -49139,7 +49437,6 @@ var require_hook_command = __commonJS({
49139
49437
  sessionId: hookInput.sessionId,
49140
49438
  hookInput,
49141
49439
  correlationId,
49142
- scope,
49143
49440
  runtime
49144
49441
  }, logger, captureStream);
49145
49442
  const internalResponse = parseInternalResponse(internalOutput.trim(), hookName, logger);
@@ -49672,7 +49969,6 @@ var require_events3 = __commonJS({
49672
49969
  source: "daemon",
49673
49970
  context: {
49674
49971
  sessionId: context.sessionId,
49675
- scope: context.scope,
49676
49972
  correlationId: context.correlationId,
49677
49973
  traceId: context.traceId,
49678
49974
  hook: context.hook,
@@ -49696,7 +49992,6 @@ var require_events3 = __commonJS({
49696
49992
  source: "daemon",
49697
49993
  context: {
49698
49994
  sessionId: context.sessionId,
49699
- scope: context.scope,
49700
49995
  correlationId: context.correlationId,
49701
49996
  traceId: context.traceId,
49702
49997
  hook: context.hook,
@@ -50097,7 +50392,7 @@ var require_update_summary = __commonJS({
50097
50392
  async function generateResumeMessage(ctx, summaryState, eventContext, summary, transcriptExcerpt, config) {
50098
50393
  const { sessionId } = eventContext;
50099
50394
  if (summary.session_title_confidence < types_js_1.RESUME_MIN_CONFIDENCE || summary.latest_intent_confidence < types_js_1.RESUME_MIN_CONFIDENCE) {
50100
- (0, core_1.logEvent)(ctx.logger, core_1.LogEvents.resumeSkipped({ sessionId, scope: eventContext.scope }, {
50395
+ (0, core_1.logEvent)(ctx.logger, core_1.LogEvents.resumeSkipped({ sessionId }, {
50101
50396
  title_confidence: summary.session_title_confidence,
50102
50397
  intent_confidence: summary.latest_intent_confidence,
50103
50398
  min_confidence: types_js_1.RESUME_MIN_CONFIDENCE
@@ -50114,7 +50409,7 @@ var require_update_summary = __commonJS({
50114
50409
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
50115
50410
  };
50116
50411
  await summaryState.resumeMessage.write(sessionId, resumeState);
50117
- (0, core_1.logEvent)(ctx.logger, core_1.LogEvents.resumeUpdated({ sessionId, scope: eventContext.scope }, {
50412
+ (0, core_1.logEvent)(ctx.logger, core_1.LogEvents.resumeUpdated({ sessionId }, {
50118
50413
  snarky_comment: resumeState.snarky_comment,
50119
50414
  timestamp: resumeState.timestamp
50120
50415
  }));
@@ -50125,7 +50420,7 @@ var require_update_summary = __commonJS({
50125
50420
  ctx.logger.warn("Resume message prompt not found", { path: RESUME_PROMPT_FILE });
50126
50421
  return;
50127
50422
  }
50128
- (0, core_1.logEvent)(ctx.logger, core_1.LogEvents.resumeGenerating({ sessionId, scope: eventContext.scope }, {
50423
+ (0, core_1.logEvent)(ctx.logger, core_1.LogEvents.resumeGenerating({ sessionId }, {
50129
50424
  title_confidence: summary.session_title_confidence,
50130
50425
  intent_confidence: summary.latest_intent_confidence
50131
50426
  }));
@@ -50154,7 +50449,7 @@ var require_update_summary = __commonJS({
50154
50449
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
50155
50450
  };
50156
50451
  await summaryState.resumeMessage.write(sessionId, resumeState);
50157
- (0, core_1.logEvent)(ctx.logger, core_1.LogEvents.resumeUpdated({ sessionId, scope: eventContext.scope }, {
50452
+ (0, core_1.logEvent)(ctx.logger, core_1.LogEvents.resumeUpdated({ sessionId }, {
50158
50453
  snarky_comment: resumeState.snarky_comment,
50159
50454
  timestamp: resumeState.timestamp
50160
50455
  }));
@@ -51276,6 +51571,10 @@ var require_statusline_service = __commonJS({
51276
51571
  theme: this.config.theme,
51277
51572
  useColors: this.useColors
51278
51573
  });
51574
+ this.setupService = serviceConfig.setupService ?? new core_1.SetupStatusService(serviceConfig.projectDir ?? serviceConfig.cwd, {
51575
+ homeDir: serviceConfig.homeDir,
51576
+ logger: serviceConfig.logger
51577
+ });
51279
51578
  }
51280
51579
  /**
51281
51580
  * Load persona definition for the session.
@@ -51365,6 +51664,74 @@ var require_statusline_service = __commonJS({
51365
51664
  }
51366
51665
  return { ...types_js_2.DEFAULT_STATUSLINE_CONFIG, ...serviceConfig.config };
51367
51666
  }
51667
+ /**
51668
+ * Check setup status and return warning message if unhealthy.
51669
+ * Returns empty warning for healthy state.
51670
+ */
51671
+ async checkSetupStatus() {
51672
+ const state = await this.setupService.getSetupState();
51673
+ switch (state) {
51674
+ case "not-run":
51675
+ return {
51676
+ warning: "Sidekick not configured. Run 'sidekick setup' to get started.",
51677
+ state
51678
+ };
51679
+ case "partial":
51680
+ return {
51681
+ warning: "Project not configured. Run 'sidekick setup' for this project.",
51682
+ state
51683
+ };
51684
+ case "unhealthy": {
51685
+ const keyHealth = await this.setupService.getEffectiveApiKeyHealth("OPENROUTER_API_KEY");
51686
+ if (keyHealth === "missing") {
51687
+ return {
51688
+ warning: "OPENROUTER_API_KEY not found. Run 'sidekick doctor' or /sidekick-config",
51689
+ state
51690
+ };
51691
+ }
51692
+ if (keyHealth === "invalid") {
51693
+ return {
51694
+ warning: "API key invalid. Run 'sidekick doctor' to fix.",
51695
+ state
51696
+ };
51697
+ }
51698
+ return {
51699
+ warning: "Setup issue detected. Run 'sidekick doctor'.",
51700
+ state
51701
+ };
51702
+ }
51703
+ default:
51704
+ return { warning: "", state: "healthy" };
51705
+ }
51706
+ }
51707
+ /**
51708
+ * Build minimal view model for setup_warning display mode.
51709
+ * Only includes fields needed for warning display.
51710
+ */
51711
+ buildMinimalViewModel(setupCheck) {
51712
+ return {
51713
+ model: "",
51714
+ contextWindow: "",
51715
+ tokenUsageActual: "",
51716
+ tokenUsageEffective: "",
51717
+ tokenPercentageActual: "",
51718
+ tokenPercentageEffective: "",
51719
+ tokensStatus: "normal",
51720
+ cost: "",
51721
+ costStatus: "normal",
51722
+ duration: "",
51723
+ cwd: "",
51724
+ branch: "",
51725
+ branchColor: "",
51726
+ displayMode: "setup_warning",
51727
+ summary: setupCheck.warning,
51728
+ title: "",
51729
+ warningCount: 0,
51730
+ errorCount: 0,
51731
+ logStatus: "normal",
51732
+ personaName: ""
51733
+ };
51734
+ }
51368
51735
  /**
51369
51736
  * Render the statusline by fetching all data in parallel and formatting.
51370
51737
  *
@@ -51374,6 +51741,19 @@ var require_statusline_service = __commonJS({
51374
51741
  * for model/tokens/cost/duration instead of reading from state files.
51375
51742
  */
51376
51743
  async render() {
51744
+ const setupCheck = await this.checkSetupStatus();
51745
+ if (setupCheck.state !== "healthy") {
51746
+ const viewModel2 = this.buildMinimalViewModel(setupCheck);
51747
+ const ANSI_YELLOW = "\x1B[33m";
51748
+ const ANSI_RESET = "\x1B[0m";
51749
+ const text2 = this.useColors ? `${ANSI_YELLOW}${setupCheck.warning}${ANSI_RESET}` : setupCheck.warning;
51750
+ return {
51751
+ text: text2,
51752
+ displayMode: "setup_warning",
51753
+ staleData: false,
51754
+ viewModel: viewModel2
51755
+ };
51756
+ }
51377
51757
  const hasHookInput = !!this.hookInput;
51378
51758
  const [transcriptResult, summaryResult, resumeResult, snarkyResult, branchResult, baseline, logMetricsResult, personaResult] = await Promise.all([
51379
51759
  this.stateReader.getTranscriptMetrics(),
@@ -51855,8 +52235,7 @@ Examples:
51855
52235
  const sessionId = options.sessionId ?? "current";
51856
52236
  const stateService = new core_1.StateService(projectDir);
51857
52237
  const eventContext = {
51858
- sessionId,
51859
- scope: "project"
52238
+ sessionId
51860
52239
  };
51861
52240
  const cwd = options.hookInput?.cwd ?? process.cwd();
51862
52241
  logger.debug("Statusline hookInput received", { hookInput: options.hookInput });
@@ -52735,6 +53114,38 @@ var require_dev_mode = __commonJS({
52735
53114
  }
52736
53115
  return zombies;
52737
53116
  }
53117
+ async function cleanupStalePidFiles(logger) {
53118
+ const daemonsDir = (0, core_1.getUserDaemonsDir)();
53119
+ let cleanedCount = 0;
53120
+ let files;
53121
+ try {
53122
+ files = await (0, promises_12.readdir)(daemonsDir);
53123
+ } catch {
53124
+ return 0;
53125
+ }
53126
+ const pidFiles = files.filter((f) => f.endsWith(".pid"));
53127
+ for (const pidFile of pidFiles) {
53128
+ const pidPath = node_path_1.default.join(daemonsDir, pidFile);
53129
+ try {
53130
+ const content = await (0, promises_12.readFile)(pidPath, "utf-8");
53131
+ const info = JSON.parse(content);
53132
+ try {
53133
+ process.kill(info.pid, 0);
53134
+ } catch {
53135
+ await (0, promises_12.unlink)(pidPath).catch(() => {
53136
+ });
53137
+ logger.debug("Removed stale PID file", { pidFile, pid: info.pid });
53138
+ cleanedCount++;
53139
+ }
53140
+ } catch {
53141
+ await (0, promises_12.unlink)(pidPath).catch(() => {
53142
+ });
53143
+ logger.debug("Removed corrupt PID file", { pidFile });
53144
+ cleanedCount++;
53145
+ }
53146
+ }
53147
+ return cleanedCount;
53148
+ }
52738
53149
  function isDevHookCommand(command) {
52739
53150
  return command?.includes("dev-hooks") ?? false;
52740
53151
  }
@@ -53007,13 +53418,42 @@ var require_dev_mode = __commonJS({
53007
53418
  stdout.write("\n");
53008
53419
  const shouldKill = force || await promptConfirm("Kill these processes?", stdout, stdin);
53009
53420
  if (shouldKill) {
53010
- const results = await (0, core_1.killAllDaemons)(logger);
53011
- const killedCount = results.filter((r) => r.killed).length;
53421
+ let killedCount = 0;
53422
+ for (const zombie of zombies) {
53423
+ const filesToRemove = [
53424
+ (0, core_1.getUserPidPath)(zombie.projectDir),
53425
+ (0, core_1.getPidPath)(zombie.projectDir),
53426
+ (0, core_1.getSocketPath)(zombie.projectDir),
53427
+ (0, core_1.getTokenPath)(zombie.projectDir)
53428
+ ];
53429
+ try {
53430
+ process.kill(zombie.pid, "SIGKILL");
53431
+ logger.info("Killed zombie daemon", { pid: zombie.pid, projectDir: zombie.projectDir });
53432
+ killedCount++;
53433
+ } catch (err) {
53434
+ const code = err.code;
53435
+ if (code === "ESRCH") {
53436
+ logger.info("Zombie daemon already exited", { pid: zombie.pid, projectDir: zombie.projectDir });
53437
+ killedCount++;
53438
+ } else {
53439
+ logger.warn("Failed to kill zombie daemon", {
53440
+ pid: zombie.pid,
53441
+ error: err instanceof Error ? err.message : String(err)
53442
+ });
53443
+ }
53444
+ }
53445
+ await Promise.all(filesToRemove.map((f) => (0, promises_12.unlink)(f).catch(() => {
53446
+ })));
53447
+ }
53012
53448
  log(stdout, "info", `Killed ${killedCount} zombie daemon(s)`);
53013
53449
  } else {
53014
53450
  log(stdout, "info", "Skipping zombie cleanup");
53015
53451
  }
53016
53452
  }
53453
+ const staleCount = await cleanupStalePidFiles(logger);
53454
+ if (staleCount > 0) {
53455
+ log(stdout, "info", `Cleaned up ${staleCount} stale PID file(s)`);
53456
+ }
53017
53457
  stdout.write("\n");
53018
53458
  log(stdout, "info", "Clean complete. Restart Claude Code with: claude --continue");
53019
53459
  return { exitCode: 0 };
@@ -53105,6 +53545,582 @@ ${USAGE_TEXT}`);
53105
53545
  }
53106
53546
  });
53107
53547
 
53548
+ // ../sidekick-cli/dist/commands/setup/prompts.js
53549
+ var require_prompts = __commonJS({
53550
+ "../sidekick-cli/dist/commands/setup/prompts.js"(exports2) {
53551
+ "use strict";
53552
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) {
53553
+ if (k2 === void 0) k2 = k;
53554
+ var desc = Object.getOwnPropertyDescriptor(m, k);
53555
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
53556
+ desc = { enumerable: true, get: function() {
53557
+ return m[k];
53558
+ } };
53559
+ }
53560
+ Object.defineProperty(o, k2, desc);
53561
+ } : function(o, m, k, k2) {
53562
+ if (k2 === void 0) k2 = k;
53563
+ o[k2] = m[k];
53564
+ });
53565
+ var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
53566
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
53567
+ } : function(o, v) {
53568
+ o["default"] = v;
53569
+ });
53570
+ var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
53571
+ var ownKeys = function(o) {
53572
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
53573
+ var ar = [];
53574
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
53575
+ return ar;
53576
+ };
53577
+ return ownKeys(o);
53578
+ };
53579
+ return function(mod) {
53580
+ if (mod && mod.__esModule) return mod;
53581
+ var result = {};
53582
+ if (mod != null) {
53583
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
53584
+ }
53585
+ __setModuleDefault(result, mod);
53586
+ return result;
53587
+ };
53588
+ }();
53589
+ Object.defineProperty(exports2, "__esModule", { value: true });
53590
+ exports2.printHeader = printHeader;
53591
+ exports2.printStatus = printStatus;
53592
+ exports2.promptSelect = promptSelect;
53593
+ exports2.promptConfirm = promptConfirm;
53594
+ exports2.promptInput = promptInput;
53595
+ var readline = __importStar(require("node:readline"));
53596
+ var colors = {
53597
+ reset: "\x1B[0m",
53598
+ bold: "\x1B[1m",
53599
+ dim: "\x1B[2m",
53600
+ green: "\x1B[32m",
53601
+ yellow: "\x1B[33m",
53602
+ blue: "\x1B[34m",
53603
+ cyan: "\x1B[36m"
53604
+ };
53605
+ function printHeader(ctx, title, description) {
53606
+ ctx.stdout.write("\n");
53607
+ ctx.stdout.write(`${colors.bold}${title}${colors.reset}
53608
+ `);
53609
+ ctx.stdout.write("\u2500".repeat(Math.min(title.length + 10, 60)) + "\n");
53610
+ if (description) {
53611
+ ctx.stdout.write(`${colors.dim}${description}${colors.reset}
53612
+ `);
53613
+ }
53614
+ ctx.stdout.write("\n");
53615
+ }
53616
+ function printStatus(ctx, type, message) {
53617
+ const icons = { success: "\u2713", warning: "\u26A0", info: "\u2022", error: "\u2717" };
53618
+ const colorMap = { success: colors.green, warning: colors.yellow, info: colors.blue, error: "\x1B[31m" };
53619
+ ctx.stdout.write(`${colorMap[type]}${icons[type]}${colors.reset} ${message}
53620
+ `);
53621
+ }
53622
+ async function promptSelect(ctx, question, options) {
53623
+ ctx.stdout.write(`${question}
53624
+
53625
+ `);
53626
+ options.forEach((opt, i) => {
53627
+ ctx.stdout.write(` ${colors.cyan}${i + 1})${colors.reset} ${opt.label}
53628
+ `);
53629
+ if (opt.description) {
53630
+ ctx.stdout.write(` ${colors.dim}${opt.description}${colors.reset}
53631
+ `);
53632
+ }
53633
+ });
53634
+ ctx.stdout.write("\n");
53635
+ const rl = readline.createInterface({
53636
+ input: ctx.stdin,
53637
+ output: ctx.stdout,
53638
+ terminal: false
53639
+ });
53640
+ return new Promise((resolve3) => {
53641
+ ctx.stdout.write(`Enter choice (1-${options.length}): `);
53642
+ rl.once("line", (answer) => {
53643
+ rl.close();
53644
+ const num = parseInt(answer.trim(), 10);
53645
+ if (num >= 1 && num <= options.length) {
53646
+ resolve3(options[num - 1].value);
53647
+ } else {
53648
+ resolve3(options[0].value);
53649
+ }
53650
+ });
53651
+ });
53652
+ }
53653
+ async function promptConfirm(ctx, question, defaultYes = true) {
53654
+ const hint = defaultYes ? "[Y/n]" : "[y/N]";
53655
+ const rl = readline.createInterface({
53656
+ input: ctx.stdin,
53657
+ output: ctx.stdout,
53658
+ terminal: false
53659
+ });
53660
+ return new Promise((resolve3) => {
53661
+ ctx.stdout.write(`${question} ${hint} `);
53662
+ rl.once("line", (answer) => {
53663
+ rl.close();
53664
+ const normalized = answer.trim().toLowerCase();
53665
+ if (normalized === "") {
53666
+ resolve3(defaultYes);
53667
+ } else {
53668
+ resolve3(normalized === "y" || normalized === "yes");
53669
+ }
53670
+ });
53671
+ });
53672
+ }
53673
+ async function promptInput(ctx, question) {
53674
+ const rl = readline.createInterface({
53675
+ input: ctx.stdin,
53676
+ output: ctx.stdout,
53677
+ terminal: false
53678
+ });
53679
+ return new Promise((resolve3) => {
53680
+ ctx.stdout.write(`${question}: `);
53681
+ rl.once("line", (answer) => {
53682
+ rl.close();
53683
+ resolve3(answer.trim());
53684
+ });
53685
+ });
53686
+ }
53687
+ }
53688
+ });
53689
+
53690
+ // ../sidekick-cli/dist/commands/setup/validate-api-key.js
53691
+ var require_validate_api_key = __commonJS({
53692
+ "../sidekick-cli/dist/commands/setup/validate-api-key.js"(exports2) {
53693
+ "use strict";
53694
+ Object.defineProperty(exports2, "__esModule", { value: true });
53695
+ exports2.validateOpenRouterKey = validateOpenRouterKey;
53696
+ exports2.validateOpenAIKey = validateOpenAIKey;
53697
+ var API_ENDPOINTS = {
53698
+ openrouter: "https://openrouter.ai/api/v1/models",
53699
+ openai: "https://api.openai.com/v1/models"
53700
+ };
53701
+ async function validateApiKey(provider, apiKey, logger) {
53702
+ try {
53703
+ const response = await fetch(API_ENDPOINTS[provider], {
53704
+ headers: { Authorization: `Bearer ${apiKey}` }
53705
+ });
53706
+ if (response.ok) {
53707
+ return { valid: true };
53708
+ }
53709
+ if (response.status === 401) {
53710
+ return { valid: false, error: "Invalid API key" };
53711
+ }
53712
+ return { valid: false, error: `API returned status ${response.status}` };
53713
+ } catch (err) {
53714
+ logger?.warn("API key validation failed", { error: err });
53715
+ return { valid: false, error: err instanceof Error ? err.message : "Network error" };
53716
+ }
53717
+ }
53718
+ function validateOpenRouterKey(apiKey, logger) {
53719
+ return validateApiKey("openrouter", apiKey, logger);
53720
+ }
53721
+ function validateOpenAIKey(apiKey, logger) {
53722
+ return validateApiKey("openai", apiKey, logger);
53723
+ }
53724
+ }
53725
+ });
53726
+
53727
+ // ../sidekick-cli/dist/commands/setup/index.js
53728
+ var require_setup = __commonJS({
53729
+ "../sidekick-cli/dist/commands/setup/index.js"(exports2) {
53730
+ "use strict";
53731
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) {
53732
+ if (k2 === void 0) k2 = k;
53733
+ var desc = Object.getOwnPropertyDescriptor(m, k);
53734
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
53735
+ desc = { enumerable: true, get: function() {
53736
+ return m[k];
53737
+ } };
53738
+ }
53739
+ Object.defineProperty(o, k2, desc);
53740
+ } : function(o, m, k, k2) {
53741
+ if (k2 === void 0) k2 = k;
53742
+ o[k2] = m[k];
53743
+ });
53744
+ var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
53745
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
53746
+ } : function(o, v) {
53747
+ o["default"] = v;
53748
+ });
53749
+ var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
53750
+ var ownKeys = function(o) {
53751
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
53752
+ var ar = [];
53753
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
53754
+ return ar;
53755
+ };
53756
+ return ownKeys(o);
53757
+ };
53758
+ return function(mod) {
53759
+ if (mod && mod.__esModule) return mod;
53760
+ var result = {};
53761
+ if (mod != null) {
53762
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
53763
+ }
53764
+ __setModuleDefault(result, mod);
53765
+ return result;
53766
+ };
53767
+ }();
53768
+ Object.defineProperty(exports2, "__esModule", { value: true });
53769
+ exports2.handleSetupCommand = handleSetupCommand;
53770
+ var fs = __importStar(require("node:fs/promises"));
53771
+ var path = __importStar(require("node:path"));
53772
+ var os = __importStar(require("node:os"));
53773
+ var core_1 = require_dist3();
53774
+ var prompts_js_1 = require_prompts();
53775
+ var validate_api_key_js_1 = require_validate_api_key();
53776
+ var STATUSLINE_COMMAND = "npx @scotthamilton77/sidekick statusline --project-dir=$CLAUDE_PROJECT_DIR";
53777
+ function getApiKeyStatusType(health) {
53778
+ switch (health) {
53779
+ case "healthy":
53780
+ return "success";
53781
+ case "not-required":
53782
+ return "info";
53783
+ default:
53784
+ return "warning";
53785
+ }
53786
+ }
53787
+ async function configureStatusline(settingsPath, logger) {
53788
+ let settings = {};
53789
+ try {
53790
+ const content = await fs.readFile(settingsPath, "utf-8");
53791
+ settings = JSON.parse(content);
53792
+ } catch (err) {
53793
+ if (err.code !== "ENOENT") {
53794
+ throw err;
53795
+ }
53796
+ }
53797
+ settings.statusLine = {
53798
+ type: "command",
53799
+ command: STATUSLINE_COMMAND
53800
+ };
53801
+ const dir = path.dirname(settingsPath);
53802
+ await fs.mkdir(dir, { recursive: true });
53803
+ await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n");
53804
+ logger?.info("Statusline configured", { path: settingsPath });
53805
+ }
53806
+ async function writeApiKeyToEnv(envPath, key, value) {
53807
+ const dir = path.dirname(envPath);
53808
+ await fs.mkdir(dir, { recursive: true });
53809
+ let content = "";
53810
+ try {
53811
+ content = await fs.readFile(envPath, "utf-8");
53812
+ } catch {
53813
+ }
53814
+ const keyRegex = new RegExp(`^${key}=.*$`, "m");
53815
+ if (keyRegex.test(content)) {
53816
+ content = content.replace(keyRegex, `${key}=${value}`);
53817
+ } else {
53818
+ if (content && !content.endsWith("\n")) {
53819
+ content += "\n";
53820
+ }
53821
+ content += `${key}=${value}
53822
+ `;
53823
+ }
53824
+ await fs.writeFile(envPath, content);
53825
+ }
53826
+ async function findExistingApiKey(keyName, homeDir, projectDir) {
53827
+ if (process.env[keyName]) {
53828
+ return process.env[keyName];
53829
+ }
53830
+ const envPaths = [
53831
+ path.join(homeDir, ".sidekick", ".env"),
53832
+ path.join(projectDir, ".sidekick", ".env"),
53833
+ path.join(projectDir, ".sidekick", ".env.local")
53834
+ ];
53835
+ for (const envPath of envPaths) {
53836
+ try {
53837
+ const content = await fs.readFile(envPath, "utf-8");
53838
+ const match = content.match(new RegExp(`^${keyName}=(.+)$`, "m"));
53839
+ if (match) {
53840
+ return match[1];
53841
+ }
53842
+ } catch {
53843
+ }
53844
+ }
53845
+ return null;
53846
+ }
53847
+ async function writePersonaConfig(_projectDir, homeDir, enabled) {
53848
+ const configDir = path.join(homeDir, ".sidekick");
53849
+ const configPath = path.join(configDir, "config.yaml");
53850
+ await fs.mkdir(configDir, { recursive: true });
53851
+ let content = "";
53852
+ try {
53853
+ content = await fs.readFile(configPath, "utf-8");
53854
+ } catch {
53855
+ }
53856
+ const personasRegex = /^features:\s*\n\s*personas:\s*\n\s*enabled:\s*(true|false)/m;
53857
+ const newPersonasBlock = `features:
53858
+ personas:
53859
+ enabled: ${enabled}`;
53860
+ if (personasRegex.test(content)) {
53861
+ content = content.replace(personasRegex, newPersonasBlock);
53862
+ } else {
53863
+ if (content && !content.endsWith("\n")) {
53864
+ content += "\n";
53865
+ }
53866
+ content += newPersonasBlock + "\n";
53867
+ }
53868
+ await fs.writeFile(configPath, content);
53869
+ }
53870
+ function printWizardHeader(stdout) {
53871
+ 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");
53872
+ stdout.write("\u2502 Sidekick Setup Wizard \u2502\n");
53873
+ stdout.write("\u2502 \u2502\n");
53874
+ stdout.write("\u2502 This wizard configures sidekick for Claude Code. \u2502\n");
53875
+ stdout.write("\u2502 Run 'sidekick setup' again anytime to reconfigure. \u2502\n");
53876
+ 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");
53877
+ }
53878
+ async function runStep1Statusline(wctx) {
53879
+ const { ctx, homeDir, projectDir, logger } = wctx;
53880
+ (0, prompts_js_1.printHeader)(ctx, "Step 1: Statusline Configuration", "Claude Code plugins cannot provide statusline config directly.");
53881
+ const statuslineScope = await (0, prompts_js_1.promptSelect)(ctx, "Where should sidekick configure your statusline?", [
53882
+ { value: "user", label: "User-level (~/.claude/settings.json)", description: "Works in all projects" },
53883
+ { value: "project", label: "Project-level (.claude/settings.local.json)", description: "This project only" }
53884
+ ]);
53885
+ const statuslinePath = statuslineScope === "user" ? path.join(homeDir, ".claude", "settings.json") : path.join(projectDir, ".claude", "settings.local.json");
53886
+ await configureStatusline(statuslinePath, logger);
53887
+ (0, prompts_js_1.printStatus)(ctx, "success", `Statusline configured in ${statuslinePath}`);
53888
+ return statuslineScope;
53889
+ }
53890
+ async function runStep2Gitignore(wctx, force) {
53891
+ const { ctx, projectDir } = wctx;
53892
+ const currentStatus = await (0, core_1.detectGitignoreStatus)(projectDir);
53893
+ if (currentStatus === "installed") {
53894
+ if (!force) {
53895
+ (0, prompts_js_1.printStatus)(ctx, "success", "Sidekick entries already present in .gitignore");
53896
+ }
53897
+ return "installed";
53898
+ }
53899
+ if (force) {
53900
+ const result2 = await (0, core_1.installGitignoreSection)(projectDir);
53901
+ return result2.status === "error" ? "missing" : "installed";
53902
+ }
53903
+ (0, prompts_js_1.printHeader)(ctx, "Step 2: Git Configuration", "Sidekick creates logs and session data that should not be committed.");
53904
+ const shouldInstall = await (0, prompts_js_1.promptConfirm)(ctx, "Update .gitignore to exclude sidekick transient files?", true);
53905
+ if (!shouldInstall) {
53906
+ (0, prompts_js_1.printStatus)(ctx, "info", "Skipping .gitignore configuration (you can manage it manually)");
53907
+ return "missing";
53908
+ }
53909
+ const result = await (0, core_1.installGitignoreSection)(projectDir);
53910
+ if (result.status === "error") {
53911
+ (0, prompts_js_1.printStatus)(ctx, "warning", `Failed to update .gitignore: ${result.error}`);
53912
+ return "missing";
53913
+ }
53914
+ const message = result.status === "already-installed" ? "Sidekick entries already present in .gitignore" : "Added sidekick section to .gitignore";
53915
+ (0, prompts_js_1.printStatus)(ctx, "success", message);
53916
+ return "installed";
53917
+ }
53918
+ async function runStep3Personas(wctx) {
53919
+ const { ctx, homeDir, projectDir } = wctx;
53920
+ const stdout = ctx.stdout;
53921
+ (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.");
53922
+ stdout.write("These require an OpenRouter API key (small cost per message).\n\n");
53923
+ const wantPersonas = await (0, prompts_js_1.promptConfirm)(ctx, "Enable persona features?", true);
53924
+ let apiKeyHealth = "not-required";
53925
+ if (!wantPersonas) {
53926
+ await writePersonaConfig(projectDir, homeDir, false);
53927
+ (0, prompts_js_1.printStatus)(ctx, "info", "Personas disabled");
53928
+ } else {
53929
+ await writePersonaConfig(projectDir, homeDir, true);
53930
+ apiKeyHealth = await configureApiKey(wctx);
53931
+ }
53932
+ return { wantPersonas, apiKeyHealth };
53933
+ }
53934
+ async function configureApiKey(wctx) {
53935
+ const { ctx, homeDir, projectDir, logger } = wctx;
53936
+ const stdout = ctx.stdout;
53937
+ const existingKey = await findExistingApiKey("OPENROUTER_API_KEY", homeDir, projectDir);
53938
+ if (existingKey) {
53939
+ (0, prompts_js_1.printStatus)(ctx, "success", "OPENROUTER_API_KEY found");
53940
+ stdout.write("Validating... ");
53941
+ const result2 = await (0, validate_api_key_js_1.validateOpenRouterKey)(existingKey, logger);
53942
+ if (result2.valid) {
53943
+ stdout.write("valid!\n");
53944
+ return "healthy";
53945
+ } else {
53946
+ stdout.write(`invalid (${result2.error})
53947
+ `);
53948
+ return "invalid";
53949
+ }
53950
+ }
53951
+ (0, prompts_js_1.printStatus)(ctx, "warning", "OPENROUTER_API_KEY not found");
53952
+ const configureNow = await (0, prompts_js_1.promptConfirm)(ctx, "Configure API key now?", true);
53953
+ if (!configureNow) {
53954
+ stdout.write("\n");
53955
+ (0, prompts_js_1.printStatus)(ctx, "warning", "Persona features will show warnings in the statusline until an API key is configured.");
53956
+ stdout.write("Run 'sidekick setup' again or ask Claude to help configure API keys using /sidekick-config.\n");
53957
+ return "missing";
53958
+ }
53959
+ const keyScope = await (0, prompts_js_1.promptSelect)(ctx, "Where should the API key be stored?", [
53960
+ { value: "user", label: "User-level (~/.sidekick/.env)", description: "Works in all projects" },
53961
+ { value: "project", label: "Project-level (.sidekick/.env)", description: "This project only" }
53962
+ ]);
53963
+ const apiKey = await (0, prompts_js_1.promptInput)(ctx, "Paste your OpenRouter API key");
53964
+ stdout.write("Validating... ");
53965
+ const result = await (0, validate_api_key_js_1.validateOpenRouterKey)(apiKey, logger);
53966
+ const envPath = keyScope === "user" ? path.join(homeDir, ".sidekick", ".env") : path.join(projectDir, ".sidekick", ".env");
53967
+ if (result.valid) {
53968
+ stdout.write("valid!\n");
53969
+ await writeApiKeyToEnv(envPath, "OPENROUTER_API_KEY", apiKey);
53970
+ (0, prompts_js_1.printStatus)(ctx, "success", `API key saved to ${envPath}`);
53971
+ return "healthy";
53972
+ } else {
53973
+ stdout.write(`invalid (${result.error})
53974
+ `);
53975
+ (0, prompts_js_1.printStatus)(ctx, "warning", "API key validation failed, saving anyway");
53976
+ await writeApiKeyToEnv(envPath, "OPENROUTER_API_KEY", apiKey);
53977
+ return "invalid";
53978
+ }
53979
+ }
53980
+ async function runStep4AutoConfig(wctx) {
53981
+ const { ctx } = wctx;
53982
+ (0, prompts_js_1.printHeader)(ctx, "Step 4: Project Auto-Configuration");
53983
+ const autoConfig = await (0, prompts_js_1.promptSelect)(ctx, "When sidekick runs in a new project for the first time:", [
53984
+ { value: "auto", label: "Auto-configure using my defaults", description: "Recommended" },
53985
+ { value: "ask", label: "Ask me each time" },
53986
+ { value: "manual", label: "Do nothing", description: "Manual setup only" }
53987
+ ]);
53988
+ return autoConfig;
53989
+ }
53990
+ async function writeStatusFiles(wctx, state) {
53991
+ const { setupService } = wctx;
53992
+ const { statuslineScope, gitignoreStatus, wantPersonas, apiKeyHealth, autoConfig } = state;
53993
+ const userStatus = {
53994
+ version: 1,
53995
+ lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
53996
+ preferences: {
53997
+ autoConfigureProjects: autoConfig === "auto",
53998
+ defaultStatuslineScope: statuslineScope,
53999
+ defaultApiKeyScope: wantPersonas ? "user" : "skip"
54000
+ },
54001
+ statusline: "configured",
54002
+ apiKeys: {
54003
+ OPENROUTER_API_KEY: apiKeyHealth,
54004
+ OPENAI_API_KEY: "not-required"
54005
+ }
54006
+ };
54007
+ await setupService.writeUserStatus(userStatus);
54008
+ const projectStatus = {
54009
+ version: 1,
54010
+ lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
54011
+ autoConfigured: false,
54012
+ statusline: statuslineScope === "project" ? "configured" : "user",
54013
+ apiKeys: {
54014
+ OPENROUTER_API_KEY: apiKeyHealth === "healthy" ? "user" : apiKeyHealth,
54015
+ OPENAI_API_KEY: "not-required"
54016
+ },
54017
+ gitignore: gitignoreStatus
54018
+ };
54019
+ await setupService.writeProjectStatus(projectStatus);
54020
+ }
54021
+ function printSummary(wctx, state) {
54022
+ const { ctx } = wctx;
54023
+ const { statuslineScope, gitignoreStatus, wantPersonas, apiKeyHealth, autoConfig } = state;
54024
+ const stdout = ctx.stdout;
54025
+ (0, prompts_js_1.printHeader)(ctx, "Summary");
54026
+ (0, prompts_js_1.printStatus)(ctx, "success", `Statusline: ${statuslineScope === "user" ? "User-level" : "Project-level"}`);
54027
+ (0, prompts_js_1.printStatus)(ctx, gitignoreStatus === "installed" ? "success" : "info", `Gitignore: ${gitignoreStatus === "installed" ? "Configured" : "Skipped"}`);
54028
+ (0, prompts_js_1.printStatus)(ctx, wantPersonas ? "success" : "info", `Personas: ${wantPersonas ? "Enabled" : "Disabled"}`);
54029
+ const apiKeyStatusType = getApiKeyStatusType(apiKeyHealth);
54030
+ (0, prompts_js_1.printStatus)(ctx, apiKeyStatusType, `API Key: ${apiKeyHealth}`);
54031
+ (0, prompts_js_1.printStatus)(ctx, "success", `Auto-configure: ${autoConfig === "auto" ? "Enabled" : "Disabled"}`);
54032
+ stdout.write("\n");
54033
+ stdout.write("Restart Claude Code to see your statusline: claude --continue\n");
54034
+ }
54035
+ async function runWizard(projectDir, logger, stdout, options) {
54036
+ const homeDir = os.homedir();
54037
+ const wctx = {
54038
+ ctx: {
54039
+ stdin: options.stdin ?? process.stdin,
54040
+ stdout
54041
+ },
54042
+ homeDir,
54043
+ projectDir,
54044
+ logger,
54045
+ setupService: new core_1.SetupStatusService(projectDir, { homeDir, logger })
54046
+ };
54047
+ const force = options.force ?? false;
54048
+ if (!force) {
54049
+ printWizardHeader(stdout);
54050
+ }
54051
+ const statuslineScope = force ? "user" : await runStep1Statusline(wctx);
54052
+ const gitignoreStatus = await runStep2Gitignore(wctx, force);
54053
+ const { wantPersonas, apiKeyHealth } = force ? { wantPersonas: true, apiKeyHealth: "not-required" } : await runStep3Personas(wctx);
54054
+ const autoConfig = force ? "auto" : await runStep4AutoConfig(wctx);
54055
+ if (force) {
54056
+ const statuslinePath = path.join(homeDir, ".claude", "settings.json");
54057
+ await configureStatusline(statuslinePath, logger);
54058
+ }
54059
+ const state = { statuslineScope, gitignoreStatus, wantPersonas, apiKeyHealth, autoConfig };
54060
+ await writeStatusFiles(wctx, state);
54061
+ if (!force) {
54062
+ printSummary(wctx, state);
54063
+ } else {
54064
+ stdout.write("Setup complete (force mode):\n");
54065
+ stdout.write(` Statusline: user-level (~/.claude/settings.json)
54066
+ `);
54067
+ stdout.write(` Gitignore: ${gitignoreStatus === "installed" ? "configured" : "skipped"}
54068
+ `);
54069
+ stdout.write(` Personas: enabled (API key not configured)
54070
+ `);
54071
+ stdout.write(` Auto-configure: enabled
54072
+ `);
54073
+ }
54074
+ return { exitCode: 0 };
54075
+ }
54076
+ async function runDoctor(projectDir, logger, stdout) {
54077
+ const homeDir = os.homedir();
54078
+ const setupService = new core_1.SetupStatusService(projectDir, { homeDir, logger });
54079
+ stdout.write("\nSidekick Doctor\n");
54080
+ stdout.write("===============\n\n");
54081
+ const statusline = await setupService.getStatuslineHealth();
54082
+ const gitignore = await (0, core_1.detectGitignoreStatus)(projectDir);
54083
+ const apiKey = await setupService.getEffectiveApiKeyHealth("OPENROUTER_API_KEY");
54084
+ const isHealthy = await setupService.isHealthy();
54085
+ const statuslineIcon = statusline === "configured" ? "\u2713" : "\u26A0";
54086
+ const gitignoreIcon = gitignore === "installed" ? "\u2713" : "\u26A0";
54087
+ const apiKeyIcon = apiKey === "healthy" || apiKey === "not-required" ? "\u2713" : "\u26A0";
54088
+ const overallIcon = isHealthy && gitignore === "installed" ? "\u2713" : "\u26A0";
54089
+ stdout.write(`${statuslineIcon} Statusline: ${statusline}
54090
+ `);
54091
+ stdout.write(`${gitignoreIcon} Gitignore: ${gitignore}
54092
+ `);
54093
+ stdout.write(`${apiKeyIcon} OpenRouter API Key: ${apiKey}
54094
+ `);
54095
+ stdout.write(`${overallIcon} Overall: ${isHealthy && gitignore === "installed" ? "healthy" : "needs attention"}
54096
+ `);
54097
+ if (!isHealthy || gitignore !== "installed") {
54098
+ stdout.write("\nRun 'sidekick setup' to configure.\n");
54099
+ }
54100
+ return { exitCode: isHealthy && gitignore === "installed" ? 0 : 1 };
54101
+ }
54102
+ async function handleSetupCommand(projectDir, logger, stdout, options = {}) {
54103
+ if (options.checkOnly) {
54104
+ return runDoctor(projectDir, logger, stdout);
54105
+ }
54106
+ return runWizard(projectDir, logger, stdout, options);
54107
+ }
54108
+ }
54109
+ });
54110
+
54111
+ // ../sidekick-cli/dist/commands/setup.js
54112
+ var require_setup2 = __commonJS({
54113
+ "../sidekick-cli/dist/commands/setup.js"(exports2) {
54114
+ "use strict";
54115
+ Object.defineProperty(exports2, "__esModule", { value: true });
54116
+ exports2.handleSetupCommand = void 0;
54117
+ var index_js_1 = require_setup();
54118
+ Object.defineProperty(exports2, "handleSetupCommand", { enumerable: true, get: function() {
54119
+ return index_js_1.handleSetupCommand;
54120
+ } });
54121
+ }
54122
+ });
54123
+
53108
54124
  // ../sidekick-cli/dist/cli.js
53109
54125
  var require_cli = __commonJS({
53110
54126
  "../sidekick-cli/dist/cli.js"(exports2) {
@@ -53159,6 +54175,7 @@ var require_cli = __commonJS({
53159
54175
  var promises_12 = require("node:fs/promises");
53160
54176
  var node_stream_1 = require("node:stream");
53161
54177
  var yargs_parser_1 = __importDefault2(require_build());
54178
+ var VERSION = true ? "0.0.4-alpha" : "dev";
53162
54179
  function isInSandbox() {
53163
54180
  return process.env.SANDBOX_RUNTIME === "1";
53164
54181
  }
@@ -53171,26 +54188,23 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
53171
54188
  `;
53172
54189
  var types_1 = require_dist();
53173
54190
  var runtime_1 = require_runtime();
53174
- var hook_js_1 = require_hook();
53175
54191
  function parseArgs(argv) {
53176
54192
  const parsed = (0, yargs_parser_1.default)(argv, {
53177
- boolean: ["hook", "wait", "open", "prefer-project", "help", "kill", "force"],
53178
- string: ["hook-script-path", "project-dir", "scope", "log-level", "format", "host", "session-id", "type"],
54193
+ boolean: ["wait", "open", "prefer-project", "help", "version", "kill", "force"],
54194
+ string: ["project-dir", "log-level", "format", "host", "session-id", "type"],
53179
54195
  number: ["port", "width"],
53180
54196
  alias: {
53181
- h: "help"
54197
+ h: "help",
54198
+ v: "version"
53182
54199
  },
53183
54200
  configuration: {
53184
54201
  "camel-case-expansion": false
53185
54202
  }
53186
54203
  });
53187
- const command = parsed._[0] ?? "session-start";
54204
+ const command = parsed._[0];
53188
54205
  return {
53189
54206
  command,
53190
- hookMode: Boolean(parsed.hook),
53191
- hookScriptPath: parsed["hook-script-path"],
53192
54207
  projectDir: parsed["project-dir"],
53193
- scopeOverride: parsed.scope,
53194
54208
  logLevel: parsed["log-level"],
53195
54209
  wait: Boolean(parsed.wait),
53196
54210
  format: parsed.format,
@@ -53202,6 +54216,7 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
53202
54216
  sessionIdArg: parsed["session-id"],
53203
54217
  messageType: parsed.type,
53204
54218
  help: Boolean(parsed.help),
54219
+ version: Boolean(parsed.version),
53205
54220
  kill: Boolean(parsed.kill),
53206
54221
  force: Boolean(parsed.force),
53207
54222
  _: parsed._
@@ -53240,12 +54255,9 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
53240
54255
  const homeDir = options.homeDir ?? options.env?.HOME;
53241
54256
  const hookInput = parseHookInput(options.stdinData);
53242
54257
  const runtime = (0, runtime_1.bootstrapRuntime)({
53243
- hookScriptPath: parsed.hookScriptPath,
53244
54258
  projectDir: parsed.projectDir,
53245
- scopeOverride: parsed.scopeOverride,
53246
54259
  logLevel: parsed.logLevel,
53247
54260
  stderrSink: stderr,
53248
- cwd: options.cwd,
53249
54261
  homeDir,
53250
54262
  command: parsed.command,
53251
54263
  interactive: options.interactive ?? false,
@@ -53254,15 +54266,12 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
53254
54266
  if (hookInput) {
53255
54267
  runtime.logger.debug("Hook input received", { hookInput: hookInput.raw });
53256
54268
  }
53257
- const shouldExit = runtime.scope.dualInstallDetected && parsed.scopeOverride !== "project";
53258
- if (shouldExit) {
53259
- runtime.logger.warn("User-scope hook detected project installation. Exiting to prevent duplicate execution.");
53260
- }
53261
54269
  return {
53262
54270
  runtime,
53263
54271
  hookInput,
53264
54272
  parsed,
53265
- shouldExit
54273
+ shouldExit: false
54274
+ // No longer needed - Claude Code handles deduplication
53266
54275
  };
53267
54276
  }
53268
54277
  async function initializeSession(options) {
@@ -53308,9 +54317,12 @@ Commands:
53308
54317
  statusline Render the status line (used by hooks)
53309
54318
  dev-mode <subcommand> Manage development hooks (enable, disable, status, clean)
53310
54319
  ui Launch the web UI
54320
+ setup Run the setup wizard (configure statusline, API keys)
54321
+ doctor Check sidekick health (alias: setup --check)
53311
54322
 
53312
54323
  Global Options:
53313
54324
  --help, -h Show this help message
54325
+ --version, -v Show version number
53314
54326
  --format=<format> Output format: json or table (command-specific)
53315
54327
  --width=<n> Table width in characters (default: 100)
53316
54328
  --project-dir=<path> Override project directory
@@ -53327,36 +54339,10 @@ Examples:
53327
54339
  return { exitCode: 0, stdout: GLOBAL_HELP_TEXT, stderr: "" };
53328
54340
  }
53329
54341
  async function routeCommand(context) {
53330
- const { parsed, runtime, hookInput, stdout, daemonStarted } = context;
54342
+ const { parsed, runtime, hookInput, stdout } = context;
53331
54343
  runtime.logger.debug("Raw hook input", { hookInput });
53332
- if (parsed.hookMode && hookInput && runtime.scope.projectRoot) {
53333
- const hookName = (0, hook_js_1.validateHookName)(hookInput.hookEventName) ?? (0, hook_js_1.getHookName)(parsed.command);
53334
- if (hookName) {
53335
- const result = await (0, hook_js_1.handleHookCommand)(hookName, {
53336
- projectRoot: runtime.scope.projectRoot,
53337
- sessionId: hookInput.sessionId,
53338
- hookInput,
53339
- correlationId: runtime.correlationId,
53340
- scope: runtime.scope.scope,
53341
- runtime
53342
- }, runtime.logger, stdout);
53343
- return { exitCode: result.exitCode, stdout: result.output, stderr: "" };
53344
- }
53345
- }
53346
- if (!parsed.hookMode) {
53347
- const isDefaultCommand = parsed.command === "session-start";
53348
- const isHelpCommand = parsed.command === "help" || parsed.command === "--help" || parsed.command === "-h";
53349
- if (parsed.help && isDefaultCommand || isHelpCommand) {
53350
- return showGlobalHelp(stdout);
53351
- }
53352
- }
53353
- if (parsed.hookMode && !hookInput) {
53354
- runtime.logger.warn("Hook mode invoked without valid hook input, returning empty response", {
53355
- command: parsed.command,
53356
- daemonStarted
53357
- });
53358
- stdout.write("{}\n");
53359
- return { exitCode: 0, stdout: "{}", stderr: "" };
54344
+ if (!parsed.command || parsed.command === "help") {
54345
+ return showGlobalHelp(stdout);
53360
54346
  }
53361
54347
  if (parsed.command === "hook") {
53362
54348
  const { parseHookArg, handleUnifiedHookCommand } = await Promise.resolve().then(() => __importStar(require_hook_command()));
@@ -53384,27 +54370,31 @@ Examples:
53384
54370
  return { exitCode: 0, stdout: helpText, stderr: "" };
53385
54371
  }
53386
54372
  if (!hookName) {
53387
- const errorMsg = `Error: Unknown hook name '${hookArg}'
54373
+ const errorMsg2 = `Error: Unknown hook name '${hookArg}'
53388
54374
  Run 'sidekick hook --help' for available hooks.
53389
54375
  `;
53390
- stdout.write(errorMsg);
53391
- return { exitCode: 1, stdout: errorMsg, stderr: "" };
54376
+ stdout.write(errorMsg2);
54377
+ return { exitCode: 1, stdout: errorMsg2, stderr: "" };
54378
+ }
54379
+ if (!parsed.projectDir) {
54380
+ const errorMsg2 = "Hook command requires --project-dir to be specified\n";
54381
+ stdout.write(errorMsg2);
54382
+ return { exitCode: 1, stdout: errorMsg2, stderr: "" };
53392
54383
  }
53393
54384
  if (!hookInput) {
53394
54385
  runtime.logger.warn("Hook command invoked without valid hook input", { hookName });
53395
54386
  stdout.write("{}\n");
53396
54387
  return { exitCode: 0, stdout: "{}", stderr: "" };
53397
54388
  }
53398
- if (!runtime.scope.projectRoot) {
54389
+ if (!runtime.projectRoot) {
53399
54390
  runtime.logger.warn("Hook command invoked without project root", { hookName });
53400
54391
  stdout.write("{}\n");
53401
54392
  return { exitCode: 0, stdout: "{}", stderr: "" };
53402
54393
  }
53403
54394
  const result = await handleUnifiedHookCommand(hookName, {
53404
- projectRoot: runtime.scope.projectRoot,
54395
+ projectRoot: runtime.projectRoot,
53405
54396
  hookInput,
53406
54397
  correlationId: runtime.correlationId,
53407
- scope: runtime.scope.scope,
53408
54398
  runtime
53409
54399
  }, runtime.logger, stdout);
53410
54400
  return { exitCode: result.exitCode, stdout: result.output, stderr: "" };
@@ -53416,7 +54406,9 @@ Run 'sidekick hook --help' for available hooks.
53416
54406
  return { exitCode: 1, stdout: SANDBOX_ERROR_MESSAGE, stderr: "" };
53417
54407
  }
53418
54408
  const { handleDaemonCommand } = await Promise.resolve().then(() => __importStar(require_daemon()));
53419
- const result = await handleDaemonCommand(subcommand, runtime.scope.projectRoot || process.cwd(), runtime.logger, stdout, { wait: parsed.wait });
54409
+ const result = await handleDaemonCommand(subcommand, runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
54410
+ wait: parsed.wait
54411
+ });
53420
54412
  return { exitCode: result.exitCode, stdout: "", stderr: "" };
53421
54413
  }
53422
54414
  if (parsed.command === "statusline") {
@@ -53424,7 +54416,7 @@ Run 'sidekick hook --help' for available hooks.
53424
54416
  const sessionId = parsed.sessionIdArg ?? hookInput?.sessionId;
53425
54417
  const parsedHookInput = hookInput?.raw ? parseStatuslineInput(hookInput.raw) : void 0;
53426
54418
  const statuslineFormat = parsed.format === "text" || parsed.format === "json" ? parsed.format : void 0;
53427
- const result = await handleStatuslineCommand(runtime.scope.projectRoot || process.cwd(), runtime.logger, stdout, {
54419
+ const result = await handleStatuslineCommand(runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
53428
54420
  format: statuslineFormat,
53429
54421
  sessionId,
53430
54422
  hookInput: parsedHookInput,
@@ -53436,7 +54428,7 @@ Run 'sidekick hook --help' for available hooks.
53436
54428
  }
53437
54429
  if (parsed.command === "ui") {
53438
54430
  const { handleUiCommand } = await Promise.resolve().then(() => __importStar(require_ui()));
53439
- const result = await handleUiCommand(runtime.scope.projectRoot || process.cwd(), runtime.logger, stdout, {
54431
+ const result = await handleUiCommand(runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
53440
54432
  port: parsed.port,
53441
54433
  host: parsed.host,
53442
54434
  open: parsed.open,
@@ -53448,7 +54440,7 @@ Run 'sidekick hook --help' for available hooks.
53448
54440
  const { handlePersonaCommand } = await Promise.resolve().then(() => __importStar(require_persona2()));
53449
54441
  const subcommand = parsed.help ? "--help" : parsed._?.[1];
53450
54442
  const args = parsed._?.slice(2) ?? [];
53451
- const result = await handlePersonaCommand(subcommand, args, runtime.scope.projectRoot || process.cwd(), runtime.logger, stdout, {
54443
+ const result = await handlePersonaCommand(subcommand, args, runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
53452
54444
  sessionId: parsed.sessionIdArg,
53453
54445
  format: parsed.format === "json" || parsed.format === "table" ? parsed.format : void 0,
53454
54446
  testType: parsed.messageType,
@@ -53458,7 +54450,7 @@ Run 'sidekick hook --help' for available hooks.
53458
54450
  }
53459
54451
  if (parsed.command === "sessions") {
53460
54452
  const { handleSessionsCommand } = await Promise.resolve().then(() => __importStar(require_sessions()));
53461
- const result = await handleSessionsCommand(runtime.scope.projectRoot || process.cwd(), runtime.logger, stdout, {
54453
+ const result = await handleSessionsCommand(runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
53462
54454
  format: parsed.format === "json" || parsed.format === "table" ? parsed.format : void 0,
53463
54455
  help: parsed.help,
53464
54456
  width: parsed.width
@@ -53468,14 +54460,29 @@ Run 'sidekick hook --help' for available hooks.
53468
54460
  if (parsed.command === "dev-mode") {
53469
54461
  const subcommand = parsed.help ? "--help" : parsed._ && parsed._[1] || "status";
53470
54462
  const { handleDevModeCommand } = await Promise.resolve().then(() => __importStar(require_dev_mode()));
53471
- const result = await handleDevModeCommand(subcommand, runtime.scope.projectRoot || process.cwd(), runtime.logger, stdout, { force: Boolean(parsed.force) });
54463
+ const result = await handleDevModeCommand(subcommand, runtime.projectRoot || process.cwd(), runtime.logger, stdout, { force: Boolean(parsed.force) });
53472
54464
  return { exitCode: result.exitCode, stdout: "", stderr: "" };
53473
54465
  }
53474
- if (!parsed.hookMode) {
53475
- stdout.write(`Sidekick CLI executed ${parsed.command} in ${runtime.scope.scope} scope
53476
- `);
54466
+ if (parsed.command === "setup") {
54467
+ const { handleSetupCommand } = await Promise.resolve().then(() => __importStar(require_setup2()));
54468
+ const result = await handleSetupCommand(runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
54469
+ checkOnly: parsed.help ? false : parsed._?.[1] === "--check",
54470
+ stdin: process.stdin
54471
+ });
54472
+ return { exitCode: result.exitCode, stdout: "", stderr: "" };
54473
+ }
54474
+ if (parsed.command === "doctor") {
54475
+ const { handleSetupCommand } = await Promise.resolve().then(() => __importStar(require_setup2()));
54476
+ const result = await handleSetupCommand(runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
54477
+ checkOnly: true
54478
+ });
54479
+ return { exitCode: result.exitCode, stdout: "", stderr: "" };
53477
54480
  }
53478
- return Promise.resolve({ exitCode: 0, stdout: "", stderr: "" });
54481
+ const errorMsg = `Unknown command: ${parsed.command}
54482
+ Run 'sidekick help' for available commands.
54483
+ `;
54484
+ stdout.write(errorMsg);
54485
+ return { exitCode: 1, stdout: errorMsg, stderr: "" };
53479
54486
  }
53480
54487
  async function persistCliLogMetrics(stateService, sessionId, counts, logger) {
53481
54488
  const logMetricsPath = stateService.sessionStatePath(sessionId, "cli-log-metrics.json");
@@ -53497,6 +54504,16 @@ Run 'sidekick hook --help' for available hooks.
53497
54504
  }
53498
54505
  async function runCli(options) {
53499
54506
  const stdout = options.stdout ?? new node_stream_1.PassThrough();
54507
+ const quickParsed = (0, yargs_parser_1.default)(options.argv, {
54508
+ boolean: ["version"],
54509
+ alias: { v: "version" }
54510
+ });
54511
+ if (quickParsed.version) {
54512
+ const versionOutput = `${VERSION}
54513
+ `;
54514
+ stdout.write(versionOutput);
54515
+ return { exitCode: 0, stdout: versionOutput, stderr: "" };
54516
+ }
53500
54517
  const initResult = initializeRuntime(options);
53501
54518
  if (initResult.shouldExit) {
53502
54519
  return { exitCode: 0, stdout: "", stderr: "" };
@@ -53512,18 +54529,17 @@ Run 'sidekick hook --help' for available hooks.
53512
54529
  stateService: runtime.stateService,
53513
54530
  logger: runtime.logger
53514
54531
  });
53515
- const isHookExecution = parsed.hookMode || parsed.command === "hook";
53516
- const { started: daemonStarted } = await ensureDaemon({
54532
+ const isHookExecution = parsed.command === "hook" && Boolean(parsed.projectDir);
54533
+ await ensureDaemon({
53517
54534
  hookMode: isHookExecution,
53518
- projectRoot: runtime.scope.projectRoot,
54535
+ projectRoot: runtime.projectRoot,
53519
54536
  logger: runtime.logger
53520
54537
  });
53521
54538
  const result = await routeCommand({
53522
54539
  parsed,
53523
54540
  runtime,
53524
54541
  hookInput,
53525
- stdout,
53526
- daemonStarted
54542
+ stdout
53527
54543
  });
53528
54544
  if (sessionId) {
53529
54545
  await persistCliLogMetrics(runtime.stateService, sessionId, runtime.getLogCounts(), runtime.logger);