@scotthamilton77/sidekick 0.0.4-alpha → 0.0.6-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -33485,7 +33485,9 @@ var require_gitignore = __commonJS({
33485
33485
  ".sidekick/sessions/",
33486
33486
  ".sidekick/state/",
33487
33487
  ".sidekick/.env",
33488
- ".sidekick/.env.local"
33488
+ ".sidekick/.env.local",
33489
+ ".sidekick/sidekick*.pid",
33490
+ ".sidekick/sidekick*.token"
33489
33491
  ];
33490
33492
  async function installGitignoreSection(projectDir) {
33491
33493
  const gitignorePath = path.join(projectDir, ".gitignore");
@@ -49286,6 +49288,7 @@ var require_hook_command = __commonJS({
49286
49288
  exports2.translateToClaudeCodeFormat = translateToClaudeCodeFormat;
49287
49289
  exports2.parseHookArg = parseHookArg;
49288
49290
  exports2.handleUnifiedHookCommand = handleUnifiedHookCommand;
49291
+ var core_1 = require_dist3();
49289
49292
  var hook_js_1 = require_hook();
49290
49293
  function combineReasonAndContext(reason, additionalContext, separator = "\n\n") {
49291
49294
  if (reason && additionalContext) {
@@ -49303,7 +49306,7 @@ var require_hook_command = __commonJS({
49303
49306
  response.systemMessage = internal.userMessage;
49304
49307
  }
49305
49308
  if (internal.additionalContext) {
49306
- response.hookSpecificOutput = { additionalContext: internal.additionalContext };
49309
+ response.hookSpecificOutput = { hookEventName: "SessionStart", additionalContext: internal.additionalContext };
49307
49310
  }
49308
49311
  return response;
49309
49312
  }
@@ -49408,6 +49411,52 @@ var require_hook_command = __commonJS({
49408
49411
  return void 0;
49409
49412
  return HOOK_ARG_TO_NAME[arg];
49410
49413
  }
49414
+ var DEGRADED_MODE_MESSAGES = {
49415
+ "not-run": {
49416
+ additionalContext: `Sidekick plugin detected but not configured. Features like reminders, personas, and statusline are unavailable until setup is complete. If you haven't already, ask the user if you should execute the sidekick-config skill.`,
49417
+ userMessage: `Sidekick is installed but not configured. Run 'sidekick setup' to configure.`
49418
+ },
49419
+ partial: {
49420
+ additionalContext: `Sidekick user setup is complete but this project is not configured. Features like reminders, personas, and statusline are unavailable until project setup is complete. If you haven't already, ask the user if you should execute the sidekick-config skill.`,
49421
+ userMessage: `Sidekick project setup incomplete. Run 'sidekick setup' in this project to configure.`
49422
+ },
49423
+ unhealthy: {
49424
+ additionalContext: `Sidekick is configured but unhealthy (possibly invalid API keys or missing configuration). Features are unavailable until issues are resolved. If you haven't already, ask the user if you should execute sidekick doctor.`,
49425
+ userMessage: `Sidekick configuration is unhealthy. Run 'sidekick doctor' to diagnose issues.`
49426
+ }
49427
+ };
49428
+ var VERBOSE_DEGRADED_HOOKS = /* @__PURE__ */ new Set(["SessionStart", "UserPromptSubmit"]);
49429
+ async function checkSetupState(projectRoot, hookName, logger) {
49430
+ let state;
49431
+ try {
49432
+ const setupService = new core_1.SetupStatusService(projectRoot);
49433
+ state = await setupService.getSetupState();
49434
+ } catch (err) {
49435
+ logger.warn("Failed to check setup state, assuming healthy", {
49436
+ error: err instanceof Error ? err.message : String(err),
49437
+ hookName,
49438
+ projectRoot
49439
+ });
49440
+ return null;
49441
+ }
49442
+ if (state === "healthy") {
49443
+ return null;
49444
+ }
49445
+ const logData = { setupState: state, hookName, projectRoot };
49446
+ if (state === "not-run") {
49447
+ logger.info("Hook operating in degraded mode - setup not run", logData);
49448
+ } else {
49449
+ logger.warn("Hook operating in degraded mode", logData);
49450
+ }
49451
+ if (!VERBOSE_DEGRADED_HOOKS.has(hookName)) {
49452
+ return {};
49453
+ }
49454
+ const messages = DEGRADED_MODE_MESSAGES[state];
49455
+ return {
49456
+ additionalContext: messages.additionalContext,
49457
+ userMessage: messages.userMessage
49458
+ };
49459
+ }
49411
49460
  function parseInternalResponse(output, hookName, logger) {
49412
49461
  if (!output)
49413
49462
  return {};
@@ -49425,6 +49474,15 @@ var require_hook_command = __commonJS({
49425
49474
  async function handleUnifiedHookCommand(hookName, options, logger, stdout) {
49426
49475
  const { projectRoot, hookInput, correlationId, runtime } = options;
49427
49476
  logger.debug("Unified hook command invoked", { hookName, sessionId: hookInput.sessionId });
49477
+ const degradedResponse = await checkSetupState(projectRoot, hookName, logger);
49478
+ if (degradedResponse !== null) {
49479
+ const claudeResponse2 = translateToClaudeCodeFormat(hookName, degradedResponse);
49480
+ const outputStr2 = JSON.stringify(claudeResponse2);
49481
+ stdout.write(`${outputStr2}
49482
+ `);
49483
+ logger.debug("Hook completed in degraded mode", { hookName });
49484
+ return { exitCode: 0, output: outputStr2 };
49485
+ }
49428
49486
  let internalOutput = "";
49429
49487
  const captureStream = {
49430
49488
  write(chunk) {
@@ -53773,6 +53831,32 @@ var require_setup = __commonJS({
53773
53831
  var core_1 = require_dist3();
53774
53832
  var prompts_js_1 = require_prompts();
53775
53833
  var validate_api_key_js_1 = require_validate_api_key();
53834
+ var USAGE_TEXT = `Usage: sidekick setup [options]
53835
+
53836
+ Run the interactive setup wizard to configure sidekick for Claude Code.
53837
+ When scripting flags are provided, runs non-interactively for those settings only.
53838
+
53839
+ Options:
53840
+ --check Check configuration status (alias: sidekick doctor)
53841
+ --force Apply all defaults non-interactively
53842
+ --help Show this help message
53843
+
53844
+ Scripting Flags (for non-interactive/partial setup):
53845
+ --statusline-scope=<scope> Configure statusline: user | project
53846
+ --gitignore Update .gitignore to exclude sidekick files
53847
+ --no-gitignore Skip .gitignore configuration
53848
+ --personas Enable persona features
53849
+ --no-personas Disable persona features
53850
+ --api-key-scope=<scope> Save API key from OPENROUTER_API_KEY env: user | project
53851
+ --auto-config=<mode> Auto-configure preference: auto | ask | manual
53852
+
53853
+ Examples:
53854
+ sidekick setup Interactive wizard
53855
+ sidekick setup --check Check current status
53856
+ sidekick setup --statusline-scope=user Configure statusline only
53857
+ sidekick setup --gitignore --personas Configure gitignore and enable personas
53858
+ OPENROUTER_API_KEY=sk-xxx sidekick setup --personas --api-key-scope=user
53859
+ `;
53776
53860
  var STATUSLINE_COMMAND = "npx @scotthamilton77/sidekick statusline --project-dir=$CLAUDE_PROJECT_DIR";
53777
53861
  function getApiKeyStatusType(health) {
53778
53862
  switch (health) {
@@ -53846,17 +53930,16 @@ var require_setup = __commonJS({
53846
53930
  }
53847
53931
  async function writePersonaConfig(_projectDir, homeDir, enabled) {
53848
53932
  const configDir = path.join(homeDir, ".sidekick");
53849
- const configPath = path.join(configDir, "config.yaml");
53933
+ const featuresPath = path.join(configDir, "features.yaml");
53850
53934
  await fs.mkdir(configDir, { recursive: true });
53851
53935
  let content = "";
53852
53936
  try {
53853
- content = await fs.readFile(configPath, "utf-8");
53937
+ content = await fs.readFile(featuresPath, "utf-8");
53854
53938
  } catch {
53855
53939
  }
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}`;
53940
+ const personasRegex = /^personas:\s*\n\s*enabled:\s*(true|false)/m;
53941
+ const newPersonasBlock = `personas:
53942
+ enabled: ${enabled}`;
53860
53943
  if (personasRegex.test(content)) {
53861
53944
  content = content.replace(personasRegex, newPersonasBlock);
53862
53945
  } else {
@@ -53865,7 +53948,7 @@ var require_setup = __commonJS({
53865
53948
  }
53866
53949
  content += newPersonasBlock + "\n";
53867
53950
  }
53868
- await fs.writeFile(configPath, content);
53951
+ await fs.writeFile(featuresPath, content);
53869
53952
  }
53870
53953
  function printWizardHeader(stdout) {
53871
53954
  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");
@@ -54069,6 +54152,96 @@ var require_setup = __commonJS({
54069
54152
  stdout.write(` Personas: enabled (API key not configured)
54070
54153
  `);
54071
54154
  stdout.write(` Auto-configure: enabled
54155
+ `);
54156
+ }
54157
+ return { exitCode: 0 };
54158
+ }
54159
+ function hasScriptingFlags(options) {
54160
+ return options.statuslineScope !== void 0 || options.gitignore !== void 0 || options.personas !== void 0 || options.apiKeyScope !== void 0 || options.autoConfig !== void 0;
54161
+ }
54162
+ async function runScripted(projectDir, logger, stdout, options) {
54163
+ const homeDir = os.homedir();
54164
+ const setupService = new core_1.SetupStatusService(projectDir, { homeDir, logger });
54165
+ let configuredCount = 0;
54166
+ if (options.statuslineScope) {
54167
+ const statuslinePath = options.statuslineScope === "user" ? path.join(homeDir, ".claude", "settings.json") : path.join(projectDir, ".claude", "settings.local.json");
54168
+ await configureStatusline(statuslinePath, logger);
54169
+ stdout.write(`\u2713 Statusline configured (${options.statuslineScope}-level)
54170
+ `);
54171
+ configuredCount++;
54172
+ }
54173
+ if (options.gitignore === true) {
54174
+ const result = await (0, core_1.installGitignoreSection)(projectDir);
54175
+ if (result.status === "error") {
54176
+ stdout.write(`\u26A0 Failed to update .gitignore: ${result.error}
54177
+ `);
54178
+ } else if (result.status === "already-installed") {
54179
+ stdout.write("\u2713 Gitignore already configured\n");
54180
+ } else {
54181
+ stdout.write("\u2713 Gitignore configured\n");
54182
+ }
54183
+ configuredCount++;
54184
+ } else if (options.gitignore === false) {
54185
+ stdout.write("- Gitignore skipped\n");
54186
+ }
54187
+ if (options.personas !== void 0) {
54188
+ await writePersonaConfig(projectDir, homeDir, options.personas);
54189
+ stdout.write(`\u2713 Personas ${options.personas ? "enabled" : "disabled"}
54190
+ `);
54191
+ configuredCount++;
54192
+ }
54193
+ if (options.apiKeyScope) {
54194
+ const apiKey = process.env.OPENROUTER_API_KEY;
54195
+ if (apiKey) {
54196
+ const envPath = options.apiKeyScope === "user" ? path.join(homeDir, ".sidekick", ".env") : path.join(projectDir, ".sidekick", ".env");
54197
+ stdout.write("Validating API key... ");
54198
+ const result = await (0, validate_api_key_js_1.validateOpenRouterKey)(apiKey, logger);
54199
+ if (result.valid) {
54200
+ stdout.write("valid\n");
54201
+ await writeApiKeyToEnv(envPath, "OPENROUTER_API_KEY", apiKey);
54202
+ stdout.write(`\u2713 API key saved (${options.apiKeyScope}-level)
54203
+ `);
54204
+ } else {
54205
+ stdout.write(`invalid (${result.error})
54206
+ `);
54207
+ stdout.write(`\u26A0 API key saved anyway (${options.apiKeyScope}-level)
54208
+ `);
54209
+ await writeApiKeyToEnv(envPath, "OPENROUTER_API_KEY", apiKey);
54210
+ }
54211
+ configuredCount++;
54212
+ } else {
54213
+ stdout.write("\u26A0 --api-key-scope specified but OPENROUTER_API_KEY not set in environment\n");
54214
+ }
54215
+ }
54216
+ if (options.autoConfig) {
54217
+ const existingUserStatus = await setupService.getUserStatus();
54218
+ const userStatus = existingUserStatus ?? {
54219
+ version: 1,
54220
+ lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
54221
+ preferences: {
54222
+ autoConfigureProjects: options.autoConfig === "auto",
54223
+ defaultStatuslineScope: "user",
54224
+ defaultApiKeyScope: "user"
54225
+ },
54226
+ statusline: "skipped",
54227
+ // Default to skipped if no prior setup
54228
+ apiKeys: {
54229
+ OPENROUTER_API_KEY: "not-required",
54230
+ OPENAI_API_KEY: "not-required"
54231
+ }
54232
+ };
54233
+ userStatus.preferences.autoConfigureProjects = options.autoConfig === "auto";
54234
+ userStatus.lastUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
54235
+ await setupService.writeUserStatus(userStatus);
54236
+ stdout.write(`\u2713 Auto-config set to '${options.autoConfig}'
54237
+ `);
54238
+ configuredCount++;
54239
+ }
54240
+ if (configuredCount === 0) {
54241
+ stdout.write("No configuration changes made. Use --help to see available options.\n");
54242
+ } else {
54243
+ stdout.write(`
54244
+ Configured ${configuredCount} setting${configuredCount === 1 ? "" : "s"}.
54072
54245
  `);
54073
54246
  }
54074
54247
  return { exitCode: 0 };
@@ -54100,9 +54273,16 @@ var require_setup = __commonJS({
54100
54273
  return { exitCode: isHealthy && gitignore === "installed" ? 0 : 1 };
54101
54274
  }
54102
54275
  async function handleSetupCommand(projectDir, logger, stdout, options = {}) {
54276
+ if (options.help) {
54277
+ stdout.write(USAGE_TEXT);
54278
+ return { exitCode: 0 };
54279
+ }
54103
54280
  if (options.checkOnly) {
54104
54281
  return runDoctor(projectDir, logger, stdout);
54105
54282
  }
54283
+ if (hasScriptingFlags(options)) {
54284
+ return runScripted(projectDir, logger, stdout, options);
54285
+ }
54106
54286
  return runWizard(projectDir, logger, stdout, options);
54107
54287
  }
54108
54288
  }
@@ -54175,7 +54355,7 @@ var require_cli = __commonJS({
54175
54355
  var promises_12 = require("node:fs/promises");
54176
54356
  var node_stream_1 = require("node:stream");
54177
54357
  var yargs_parser_1 = __importDefault2(require_build());
54178
- var VERSION = true ? "0.0.4-alpha" : "dev";
54358
+ var VERSION = true ? "0.0.6-alpha" : "dev";
54179
54359
  function isInSandbox() {
54180
54360
  return process.env.SANDBOX_RUNTIME === "1";
54181
54361
  }
@@ -54190,8 +54370,18 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
54190
54370
  var runtime_1 = require_runtime();
54191
54371
  function parseArgs(argv) {
54192
54372
  const parsed = (0, yargs_parser_1.default)(argv, {
54193
- boolean: ["wait", "open", "prefer-project", "help", "version", "kill", "force"],
54194
- string: ["project-dir", "log-level", "format", "host", "session-id", "type"],
54373
+ boolean: ["wait", "open", "prefer-project", "help", "version", "kill", "force", "gitignore", "personas"],
54374
+ string: [
54375
+ "project-dir",
54376
+ "log-level",
54377
+ "format",
54378
+ "host",
54379
+ "session-id",
54380
+ "type",
54381
+ "statusline-scope",
54382
+ "api-key-scope",
54383
+ "auto-config"
54384
+ ],
54195
54385
  number: ["port", "width"],
54196
54386
  alias: {
54197
54387
  h: "help",
@@ -54202,6 +54392,8 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
54202
54392
  }
54203
54393
  });
54204
54394
  const command = parsed._[0];
54395
+ const hasGitignoreFlag = argv.some((arg) => arg === "--gitignore" || arg === "--no-gitignore");
54396
+ const hasPersonasFlag = argv.some((arg) => arg === "--personas" || arg === "--no-personas");
54205
54397
  return {
54206
54398
  command,
54207
54399
  projectDir: parsed["project-dir"],
@@ -54219,7 +54411,13 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
54219
54411
  version: Boolean(parsed.version),
54220
54412
  kill: Boolean(parsed.kill),
54221
54413
  force: Boolean(parsed.force),
54222
- _: parsed._
54414
+ _: parsed._,
54415
+ // Setup command scripting flags - only set if explicitly provided
54416
+ statuslineScope: parsed["statusline-scope"],
54417
+ gitignore: hasGitignoreFlag ? Boolean(parsed.gitignore) : void 0,
54418
+ personas: hasPersonasFlag ? Boolean(parsed.personas) : void 0,
54419
+ apiKeyScope: parsed["api-key-scope"],
54420
+ autoConfig: parsed["auto-config"]
54223
54421
  };
54224
54422
  }
54225
54423
  function parseHookInput(stdinData) {
@@ -54293,6 +54491,19 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
54293
54491
  if (!hookMode || !projectRoot) {
54294
54492
  return { started: false };
54295
54493
  }
54494
+ try {
54495
+ const { SetupStatusService } = await Promise.resolve().then(() => __importStar(require_dist3()));
54496
+ const setupService = new SetupStatusService(projectRoot);
54497
+ const setupState = await setupService.getSetupState();
54498
+ if (setupState !== "healthy") {
54499
+ logger.debug("Skipping daemon start - setup not healthy", { setupState });
54500
+ return { started: false };
54501
+ }
54502
+ } catch (err) {
54503
+ logger.warn("Failed to check setup status, proceeding with daemon start", {
54504
+ error: err instanceof Error ? err.message : String(err)
54505
+ });
54506
+ }
54296
54507
  try {
54297
54508
  const { DaemonClient } = await Promise.resolve().then(() => __importStar(require_dist3()));
54298
54509
  const daemonClient = new DaemonClient(projectRoot, logger);
@@ -54466,8 +54677,15 @@ Run 'sidekick hook --help' for available hooks.
54466
54677
  if (parsed.command === "setup") {
54467
54678
  const { handleSetupCommand } = await Promise.resolve().then(() => __importStar(require_setup2()));
54468
54679
  const result = await handleSetupCommand(runtime.projectRoot || process.cwd(), runtime.logger, stdout, {
54469
- checkOnly: parsed.help ? false : parsed._?.[1] === "--check",
54470
- stdin: process.stdin
54680
+ help: parsed.help,
54681
+ checkOnly: parsed._?.[1] === "--check",
54682
+ stdin: process.stdin,
54683
+ // Scripting flags for non-interactive setup
54684
+ statuslineScope: parsed.statuslineScope,
54685
+ gitignore: parsed.gitignore,
54686
+ personas: parsed.personas,
54687
+ apiKeyScope: parsed.apiKeyScope,
54688
+ autoConfig: parsed.autoConfig
54471
54689
  });
54472
54690
  return { exitCode: result.exitCode, stdout: "", stderr: "" };
54473
54691
  }
package/dist/daemon.js CHANGED
@@ -32509,7 +32509,9 @@ var require_gitignore = __commonJS({
32509
32509
  ".sidekick/sessions/",
32510
32510
  ".sidekick/state/",
32511
32511
  ".sidekick/.env",
32512
- ".sidekick/.env.local"
32512
+ ".sidekick/.env.local",
32513
+ ".sidekick/sidekick*.pid",
32514
+ ".sidekick/sidekick*.token"
32513
32515
  ];
32514
32516
  async function installGitignoreSection(projectDir2) {
32515
32517
  const gitignorePath = path.join(projectDir2, ".gitignore");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scotthamilton77/sidekick",
3
- "version": "0.0.4-alpha",
3
+ "version": "0.0.6-alpha",
4
4
  "description": "AI pair programming assistant with personas, session tracking, and contextual nudges",
5
5
  "bin": {
6
6
  "sidekick": "dist/bin.js"