handsoff 0.1.1 → 0.1.2-beta.0

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/cli/index.js CHANGED
@@ -296,7 +296,8 @@ var en_default = {
296
296
  noHooksSelected: "No hooks selected, skipping injection.",
297
297
  injecting: "Injecting hooks...",
298
298
  injected: "Hooks injected",
299
- failed: "Failed: {{error}}"
299
+ failed: "Failed: {{error}}",
300
+ remoteModeSkipHooks: "Remote mode does not require hooks; skipping injection."
300
301
  },
301
302
  channel: {
302
303
  configuring: "Configuring {{channel}}",
@@ -668,12 +669,18 @@ var ConfigApplicator = class {
668
669
  };
669
670
  let finalConfig = merged;
670
671
  if (bindings && Object.keys(bindings).length > 0) {
672
+ const finalBindings = { ...merged.bindings || {} };
673
+ for (const [agent, channelId] of Object.entries(bindings)) {
674
+ for (const [a, cid] of Object.entries(finalBindings)) {
675
+ if (cid === channelId && a !== agent) {
676
+ delete finalBindings[a];
677
+ }
678
+ }
679
+ finalBindings[agent] = channelId;
680
+ }
671
681
  finalConfig = {
672
682
  ...merged,
673
- bindings: {
674
- ...merged.bindings || {},
675
- ...bindings
676
- }
683
+ bindings: finalBindings
677
684
  };
678
685
  }
679
686
  const configDir = join4(homedir4(), ".handsoff");
@@ -850,6 +857,13 @@ var prompts = {
850
857
  { name: t("wizard.cli.backAction"), value: "back" }
851
858
  ]
852
859
  }),
860
+ claudeMode: () => select({
861
+ message: "Choose Claude integration mode:",
862
+ choices: [
863
+ { name: "Hook mode (local one-way)", value: "hooks" },
864
+ { name: "Remote mode (managed two-way)", value: "remote" }
865
+ ]
866
+ }),
853
867
  cliHookSelect: (selected) => checkbox({
854
868
  message: t("wizard.cli.hookCheckbox"),
855
869
  choices: [
@@ -938,6 +952,7 @@ import { writeFileSync as writeFileSync4, existsSync as existsSync5, readFileSyn
938
952
  import { dirname as dirname3 } from "path";
939
953
  import pc from "picocolors";
940
954
  import ora from "ora";
955
+ import { execSync as execSync3 } from "child_process";
941
956
  var STEP = "STEP 2";
942
957
  async function stepCli(state) {
943
958
  const cliName = state.currentCliName || "claude";
@@ -967,34 +982,48 @@ ${STEP} \u2014 Configure Agent
967
982
  console.log(pc.yellow(` ${t("wizard.cli.hooksNotInstalled")}`));
968
983
  }
969
984
  console.log("");
970
- const action = await prompts.cliActionSelect(detection.hooked);
971
- if (action === "back") {
972
- console.log("");
973
- return { action: "next", next: "cli-menu" };
974
- }
975
- const selectedHooks = await prompts.cliHookSelect();
976
- if (selectedHooks.length === 0) {
977
- console.log(pc.yellow(`! ${t("wizard.cli.noHooksSelected")}
985
+ const mode = await prompts.claudeMode();
986
+ if (mode === "hooks") {
987
+ const action = await prompts.cliActionSelect(detection.hooked);
988
+ if (action === "back") {
989
+ console.log("");
990
+ return { action: "next", next: "cli-menu" };
991
+ }
992
+ const selectedHooks = await prompts.cliHookSelect();
993
+ if (selectedHooks.length === 0) {
994
+ console.log(pc.yellow(`! ${t("wizard.cli.noHooksSelected")}
978
995
  `));
979
- return { action: "next", next: "cli-menu" };
980
- }
981
- const spinner = ora(t("wizard.cli.injecting")).start();
982
- try {
983
- injectClaudeHooks(detection.settingsPath, selectedHooks);
984
- spinner.succeed(t("wizard.cli.injected"));
985
- state.itemStatus.set("claude", "configured");
986
- state.sessionConfigured.add("claude");
987
- state.pendingChanges.agent = {
988
- ...state.pendingChanges.agent || {},
989
- claude: { adapter: "hooks" }
990
- };
991
- state.validationResults.set("claude", { ok: true });
992
- return { action: "next", next: "channel-select" };
993
- } catch (err) {
994
- spinner.fail(t("wizard.cli.failed", { error: String(err) }));
995
- state.itemStatus.set("claude", "error");
996
- state.validationResults.set("claude", { ok: false, message: String(err) });
996
+ return { action: "next", next: "cli-menu" };
997
+ }
998
+ const spinner = ora(t("wizard.cli.injecting")).start();
999
+ try {
1000
+ injectClaudeHooks(detection.settingsPath, selectedHooks);
1001
+ spinner.succeed(t("wizard.cli.injected"));
1002
+ } catch (err) {
1003
+ spinner.fail(t("wizard.cli.failed", { error: String(err) }));
1004
+ state.itemStatus.set("claude", "error");
1005
+ state.validationResults.set("claude", { ok: false, message: String(err) });
1006
+ return { action: "next", next: "cli-menu" };
1007
+ }
1008
+ } else {
1009
+ console.log(pc.yellow(`! ${t("wizard.cli.remoteModeSkipHooks")}`));
1010
+ try {
1011
+ execSync3("claude --version", { stdio: "ignore" });
1012
+ } catch {
1013
+ console.log(pc.yellow("! claude CLI not found in PATH; remote mode may not work until it is installed."));
1014
+ }
997
1015
  }
1016
+ state.itemStatus.set("claude", "configured");
1017
+ state.sessionConfigured.add("claude");
1018
+ state.pendingChanges.agent = {
1019
+ ...state.pendingChanges.agent || {},
1020
+ claude: {
1021
+ ...state.pendingChanges.agent?.claude || {},
1022
+ adapter: mode
1023
+ }
1024
+ };
1025
+ state.validationResults.set("claude", { ok: true });
1026
+ return { action: "next", next: "channel-select" };
998
1027
  }
999
1028
  if (cliName === "codex") {
1000
1029
  const detection = state.codexDetection ?? detectCodex();
@@ -2309,7 +2338,7 @@ function registerClaudeCommand(program2) {
2309
2338
  import { spawn as spawn4 } from "child_process";
2310
2339
  import { homedir as homedir14 } from "os";
2311
2340
  import { join as join16 } from "path";
2312
- import { execSync as execSync3 } from "child_process";
2341
+ import { execSync as execSync4 } from "child_process";
2313
2342
 
2314
2343
  // src/shared/logger.ts
2315
2344
  import pino from "pino";
@@ -2387,7 +2416,7 @@ function createAgentLogger(agentType) {
2387
2416
  // src/cli/agent/codex.ts
2388
2417
  function isCodexInstalled() {
2389
2418
  try {
2390
- execSync3("codex --version", { encoding: "utf8", stdio: "pipe", windowsHide: true });
2419
+ execSync4("codex --version", { encoding: "utf8", stdio: "pipe", windowsHide: true });
2391
2420
  return true;
2392
2421
  } catch {
2393
2422
  return false;
@@ -2395,7 +2424,7 @@ function isCodexInstalled() {
2395
2424
  }
2396
2425
  function getCodexVersion() {
2397
2426
  try {
2398
- return execSync3("codex --version", { encoding: "utf8", stdio: "pipe", windowsHide: true }).trim();
2427
+ return execSync4("codex --version", { encoding: "utf8", stdio: "pipe", windowsHide: true }).trim();
2399
2428
  } catch {
2400
2429
  return void 0;
2401
2430
  }