@appchy/jarvis 0.1.18 → 0.1.19

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
@@ -1,27 +1,28 @@
1
- // src/bin.ts
2
- import dotenv from "dotenv";
3
- import fs11 from "fs";
4
- import path11 from "path";
5
-
6
- // src/cli.ts
7
- import { Command } from "commander";
8
- import { spawn as spawn3, execSync as execSync2 } from "child_process";
9
- import fs10 from "fs";
10
- import path10 from "path";
11
- import os5 from "os";
12
-
13
1
  // src/config.ts
14
2
  import fs from "fs";
15
3
  import path from "path";
16
4
  import os from "os";
5
+ import { fileURLToPath } from "url";
6
+ var __filename = fileURLToPath(import.meta.url);
7
+ var __dirname = path.dirname(__filename);
8
+ function isDev() {
9
+ try {
10
+ const envFile = path.join(__dirname, "..", "env.json");
11
+ const data = JSON.parse(fs.readFileSync(envFile, "utf-8"));
12
+ return data.env === "development";
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
17
+ var PROD_APP_URL = "https://jarvis.appchy.com";
18
+ var DEV_APP_URL = "http://localhost:3000";
19
+ function getDefaultAppUrl() {
20
+ return isDev() ? process.env.APP_URL || DEV_APP_URL : PROD_APP_URL;
21
+ }
17
22
  function resolveConfigDir() {
18
23
  if (process.env.JARVIS_CONFIG_DIR) return process.env.JARVIS_CONFIG_DIR;
19
24
  const base = path.join(os.homedir(), ".jarvis");
20
- const appUrl = process.env.APP_URL ?? "";
21
- if (appUrl && !appUrl.includes("appchy.com") && appUrl !== "") {
22
- return path.join(base, "dev");
23
- }
24
- return base;
25
+ return isDev() ? path.join(base, "dev") : base;
25
26
  }
26
27
  var CONFIG_DIR = resolveConfigDir();
27
28
  var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
@@ -62,6 +63,13 @@ function getConfigPath() {
62
63
  return CONFIG_FILE;
63
64
  }
64
65
 
66
+ // src/cli.ts
67
+ import { Command } from "commander";
68
+ import { spawn as spawn3, execSync as execSync2 } from "child_process";
69
+ import fs10 from "fs";
70
+ import path10 from "path";
71
+ import os6 from "os";
72
+
65
73
  // ../../packages/logger/src/logger.ts
66
74
  function resolveError(meta) {
67
75
  if (!meta) return {};
@@ -1982,42 +1990,58 @@ function formatToolDescription(toolName, input) {
1982
1990
  // ../../packages/agent/src/worktree.ts
1983
1991
  import { exec as execCb } from "child_process";
1984
1992
  import fs2 from "fs/promises";
1993
+ import os2 from "os";
1985
1994
  import path2 from "path";
1986
1995
  import { promisify } from "util";
1987
1996
  var exec = promisify(execCb);
1988
- var WORKTREE_DIR = ".jarvis/worktrees";
1989
- async function createWorktree(repoPath, taskId, baseBranch) {
1990
- const branchName = `jarvis/task-${taskId}`;
1991
- const worktreeDir = path2.join(repoPath, WORKTREE_DIR, `task-${taskId}`);
1992
- await fs2.mkdir(path2.dirname(worktreeDir), { recursive: true });
1993
- await ensureGitignore(repoPath);
1994
- if (await worktreeExists(repoPath, taskId)) {
1995
- return worktreeDir;
1996
- }
1997
- const startPoint = baseBranch ?? "HEAD";
1998
- await exec(`git worktree add -b "${branchName}" "${worktreeDir}" "${startPoint}"`, {
1999
- cwd: repoPath
1997
+ var DEFAULT_WORKTREE_DIR = path2.join(os2.homedir(), ".jarvis", "worktrees");
1998
+ function resolveDir(worktreeDir) {
1999
+ if (!worktreeDir) return DEFAULT_WORKTREE_DIR;
2000
+ return worktreeDir.replace(/^~/, os2.homedir());
2001
+ }
2002
+ async function createWorktree(req) {
2003
+ const branchName = `jarvis/task-${req.taskId}`;
2004
+ const baseDir = resolveDir(req.worktreeDir);
2005
+ const worktreePath = path2.join(baseDir, `task-${req.taskId}`);
2006
+ await fs2.mkdir(path2.dirname(worktreePath), { recursive: true });
2007
+ if (await worktreeExists({ taskId: req.taskId, worktreeDir: req.worktreeDir })) {
2008
+ return { worktreePath, branchName };
2009
+ }
2010
+ const startPoint = req.branch ?? "HEAD";
2011
+ await exec(`git worktree add -b "${branchName}" "${worktreePath}" "${startPoint}"`, {
2012
+ cwd: req.repoPath
2000
2013
  });
2001
- return worktreeDir;
2014
+ return { worktreePath, branchName };
2002
2015
  }
2003
- async function worktreeExists(repoPath, taskId) {
2004
- const worktreeDir = path2.join(repoPath, WORKTREE_DIR, `task-${taskId}`);
2005
- try {
2006
- await fs2.access(worktreeDir);
2007
- return true;
2008
- } catch {
2009
- return false;
2016
+ async function listWorktrees(req) {
2017
+ const { stdout } = await exec("git worktree list --porcelain", { cwd: req.repoPath });
2018
+ const worktrees = [];
2019
+ let currentPath = "";
2020
+ let currentBranch = "";
2021
+ for (const line of stdout.split("\n")) {
2022
+ if (line.startsWith("worktree ")) {
2023
+ currentPath = line.slice(9);
2024
+ } else if (line.startsWith("branch refs/heads/")) {
2025
+ currentBranch = line.slice(18);
2026
+ if (currentBranch.startsWith("jarvis/task-")) {
2027
+ worktrees.push({
2028
+ taskId: currentBranch.replace("jarvis/task-", ""),
2029
+ path: currentPath,
2030
+ branch: currentBranch
2031
+ });
2032
+ }
2033
+ }
2010
2034
  }
2035
+ return worktrees;
2011
2036
  }
2012
- async function ensureGitignore(repoPath) {
2013
- const gitignorePath = path2.join(repoPath, ".gitignore");
2037
+ async function worktreeExists(req) {
2038
+ const baseDir = resolveDir(req.worktreeDir);
2039
+ const worktreePath = path2.join(baseDir, `task-${req.taskId}`);
2014
2040
  try {
2015
- const content = await fs2.readFile(gitignorePath, "utf-8");
2016
- if (!content.includes(".jarvis/")) {
2017
- await fs2.appendFile(gitignorePath, "\n.jarvis/\n");
2018
- }
2041
+ await fs2.access(worktreePath);
2042
+ return true;
2019
2043
  } catch {
2020
- await fs2.writeFile(gitignorePath, ".jarvis/\n");
2044
+ return false;
2021
2045
  }
2022
2046
  }
2023
2047
 
@@ -2257,7 +2281,7 @@ function isImageFile(mimeType) {
2257
2281
  }
2258
2282
  function resolveUrl(url, baseUrl) {
2259
2283
  if (url.startsWith("http://") || url.startsWith("https://")) return url;
2260
- const origin = baseUrl || process.env.APP_URL || "http://localhost:3000";
2284
+ const origin = baseUrl || getDefaultAppUrl();
2261
2285
  return `${origin}${url}`;
2262
2286
  }
2263
2287
  async function resolveAttachments(attachments, workspacePath, baseUrl) {
@@ -2338,7 +2362,7 @@ function createAgent(deps) {
2338
2362
  const systemMsg = msg.messages.find((m) => m.role === "system");
2339
2363
  const nonSystemMessages = msg.messages.filter((m) => m.role !== "system");
2340
2364
  if (msg.attachments?.length) {
2341
- const attachmentContext = await resolveAttachments(msg.attachments, wsPath);
2365
+ const attachmentContext = await resolveAttachments(msg.attachments, wsPath, getDefaultAppUrl());
2342
2366
  if (attachmentContext) {
2343
2367
  let lastUserIdx = -1;
2344
2368
  for (let i = nonSystemMessages.length - 1; i >= 0; i--) {
@@ -2576,6 +2600,12 @@ function createGitHandler(workspacePath, broadcast) {
2576
2600
  case "git:diffStats":
2577
2601
  await handleDiffStats(msg);
2578
2602
  return true;
2603
+ case "git:listWorktrees":
2604
+ await handleListWorktrees(msg);
2605
+ return true;
2606
+ case "git:browseDir":
2607
+ await handleBrowseDir(msg);
2608
+ return true;
2579
2609
  default:
2580
2610
  return false;
2581
2611
  }
@@ -2583,34 +2613,55 @@ function createGitHandler(workspacePath, broadcast) {
2583
2613
  function respond(requestId, result, error) {
2584
2614
  broadcast({ type: "git:response", requestId, result, error });
2585
2615
  }
2616
+ async function hasGitDir(dir) {
2617
+ return fs5.access(path5.join(dir, ".git")).then(() => true).catch(() => false);
2618
+ }
2619
+ async function fetchAndCheckout(repoDir, branch) {
2620
+ await exec2("git fetch origin", { cwd: repoDir, timeout: 12e4 });
2621
+ try {
2622
+ await exec2(`git checkout "${branch}"`, { cwd: repoDir });
2623
+ } catch {
2624
+ await exec2(`git checkout -b "${branch}" "origin/${branch}"`, { cwd: repoDir });
2625
+ }
2626
+ await exec2("git pull --ff-only", { cwd: repoDir }).catch(() => {
2627
+ });
2628
+ }
2586
2629
  async function handleResolveRepo(msg) {
2587
2630
  try {
2588
- const repoDir = path5.join(workspacePath, msg.owner, msg.name);
2589
- const isCloned = await fs5.access(path5.join(repoDir, ".git")).then(() => true).catch(() => false);
2590
- if (!isCloned) {
2591
- logger.sys.info("[Git] Cloning repo...", { repoDir });
2592
- await fs5.mkdir(path5.join(workspacePath, msg.owner), { recursive: true });
2593
- const cloneUrl = `git@github.com:${msg.owner}/${msg.name}.git`;
2594
- await exec2(`git clone "${cloneUrl}" "${repoDir}"`, { timeout: 3e5 });
2631
+ if (msg.repoPath && await hasGitDir(msg.repoPath)) {
2632
+ logger.sys.info("[Git] Using explicit repo path", { repoPath: msg.repoPath });
2633
+ await fetchAndCheckout(msg.repoPath, msg.branch);
2634
+ respond(msg.requestId, { repoPath: msg.repoPath });
2635
+ return;
2595
2636
  }
2596
- await exec2("git fetch origin", { cwd: repoDir, timeout: 12e4 });
2597
- try {
2598
- await exec2(`git checkout "${msg.branch}"`, { cwd: repoDir });
2599
- } catch {
2600
- await exec2(`git checkout -b "${msg.branch}" "origin/${msg.branch}"`, { cwd: repoDir });
2637
+ const byName = path5.join(workspacePath, msg.name);
2638
+ if (await hasGitDir(byName)) {
2639
+ logger.sys.info("[Git] Found repo by name", { repoPath: byName });
2640
+ await fetchAndCheckout(byName, msg.branch);
2641
+ respond(msg.requestId, { repoPath: byName });
2642
+ return;
2601
2643
  }
2602
- await exec2("git pull --ff-only", { cwd: repoDir }).catch(() => {
2603
- });
2604
- logger.sys.info("[Git] Repo resolved", { repoDir, branch: msg.branch });
2605
- respond(msg.requestId, { repoPath: repoDir });
2644
+ const byOwnerName = path5.join(workspacePath, msg.owner, msg.name);
2645
+ if (await hasGitDir(byOwnerName)) {
2646
+ logger.sys.info("[Git] Found repo by owner/name", { repoPath: byOwnerName });
2647
+ await fetchAndCheckout(byOwnerName, msg.branch);
2648
+ respond(msg.requestId, { repoPath: byOwnerName });
2649
+ return;
2650
+ }
2651
+ logger.sys.info("[Git] Cloning repo...", { repoDir: byOwnerName });
2652
+ await fs5.mkdir(path5.join(workspacePath, msg.owner), { recursive: true });
2653
+ const cloneUrl = `git@github.com:${msg.owner}/${msg.name}.git`;
2654
+ await exec2(`git clone "${cloneUrl}" "${byOwnerName}"`, { timeout: 3e5 });
2655
+ await fetchAndCheckout(byOwnerName, msg.branch);
2656
+ respond(msg.requestId, { repoPath: byOwnerName });
2606
2657
  } catch (err) {
2607
2658
  respond(msg.requestId, null, err instanceof Error ? err.message : String(err));
2608
2659
  }
2609
2660
  }
2610
2661
  async function handleCreateWorktree(msg) {
2611
2662
  try {
2612
- const worktreePath = await createWorktree(msg.repoPath, msg.taskId, msg.branch);
2613
- respond(msg.requestId, { worktreePath, branchName: `jarvis/task-${msg.taskId}` });
2663
+ const result = await createWorktree({ repoPath: msg.repoPath, taskId: msg.taskId, branch: msg.branch });
2664
+ respond(msg.requestId, result);
2614
2665
  } catch (err) {
2615
2666
  respond(msg.requestId, null, err instanceof Error ? err.message : String(err));
2616
2667
  }
@@ -2652,6 +2703,26 @@ function createGitHandler(workspacePath, broadcast) {
2652
2703
  respond(msg.requestId, { files: [], additions: 0, deletions: 0 });
2653
2704
  }
2654
2705
  }
2706
+ async function handleListWorktrees(msg) {
2707
+ try {
2708
+ const worktrees = await listWorktrees({ repoPath: msg.repoPath });
2709
+ respond(msg.requestId, worktrees);
2710
+ } catch (err) {
2711
+ respond(msg.requestId, null, err instanceof Error ? err.message : String(err));
2712
+ }
2713
+ }
2714
+ async function handleBrowseDir(msg) {
2715
+ try {
2716
+ const { stdout } = await exec2(
2717
+ `osascript -e 'set theFolder to POSIX path of (choose folder with prompt "Select worktree directory")' -e 'return theFolder'`,
2718
+ { timeout: 6e4 }
2719
+ );
2720
+ const selectedPath = stdout.trim();
2721
+ respond(msg.requestId, { path: selectedPath });
2722
+ } catch (err) {
2723
+ respond(msg.requestId, null, err instanceof Error ? err.message : String(err));
2724
+ }
2725
+ }
2655
2726
  return { handle };
2656
2727
  }
2657
2728
 
@@ -2994,16 +3065,16 @@ async function startAgent(options) {
2994
3065
  import React6 from "react";
2995
3066
  import { render as render2 } from "ink";
2996
3067
  import { spawn } from "child_process";
2997
- import { fileURLToPath } from "url";
3068
+ import { fileURLToPath as fileURLToPath2 } from "url";
2998
3069
  import path8 from "path";
2999
3070
  import fs8 from "fs";
3000
- import os3 from "os";
3071
+ import os4 from "os";
3001
3072
  import WebSocket4 from "ws";
3002
3073
 
3003
3074
  // src/tui/settings.ts
3004
3075
  import fs6 from "fs";
3005
3076
  import path6 from "path";
3006
- import os2 from "os";
3077
+ import os3 from "os";
3007
3078
  var DEFAULT_SETTINGS = {
3008
3079
  model: "claude-sonnet-4-20250514",
3009
3080
  thinking: { type: "adaptive" },
@@ -3012,7 +3083,7 @@ var DEFAULT_SETTINGS = {
3012
3083
  theme: "dark",
3013
3084
  disallowedTools: []
3014
3085
  };
3015
- var SETTINGS_DIR = path6.join(os2.homedir(), ".jarvis");
3086
+ var SETTINGS_DIR = path6.join(os3.homedir(), ".jarvis");
3016
3087
  var SETTINGS_FILE = path6.join(SETTINGS_DIR, "settings.json");
3017
3088
  function loadSettings() {
3018
3089
  try {
@@ -4950,12 +5021,12 @@ async function launchChat(opts) {
4950
5021
  }
4951
5022
  function spawnAgentProcess(port, workspacePath) {
4952
5023
  const config = loadConfig();
4953
- const logDir = path8.join(os3.homedir(), ".jarvis");
5024
+ const logDir = path8.join(os4.homedir(), ".jarvis");
4954
5025
  fs8.mkdirSync(logDir, { recursive: true });
4955
5026
  const logFile = path8.join(logDir, "agent.log");
4956
5027
  const logFd = fs8.openSync(logFile, "a");
4957
- const __filename = fileURLToPath(import.meta.url);
4958
- const cliRoot = path8.resolve(path8.dirname(__filename), "../..");
5028
+ const __filename2 = fileURLToPath2(import.meta.url);
5029
+ const cliRoot = path8.resolve(path8.dirname(__filename2), "../..");
4959
5030
  const binPath = path8.join(cliRoot, "bin", "jarvis.mjs");
4960
5031
  const args = ["start", "--port", String(port), "--workspace", workspacePath];
4961
5032
  if (config?.apiUrl && config?.token) {
@@ -5010,7 +5081,7 @@ async function waitForAgent(port, maxAttempts = 30) {
5010
5081
  import { execSync, spawn as spawn2 } from "child_process";
5011
5082
  import fs9 from "fs";
5012
5083
  import path9 from "path";
5013
- import os4 from "os";
5084
+ import os5 from "os";
5014
5085
  function createServiceManager() {
5015
5086
  if (process.platform === "darwin") return new MacOSService();
5016
5087
  if (process.platform === "win32") return new WindowsService();
@@ -5018,7 +5089,7 @@ function createServiceManager() {
5018
5089
  return new FallbackService();
5019
5090
  }
5020
5091
  var PLIST_LABEL = "com.appchy.jarvis";
5021
- var PLIST_DIR = path9.join(os4.homedir(), "Library", "LaunchAgents");
5092
+ var PLIST_DIR = path9.join(os5.homedir(), "Library", "LaunchAgents");
5022
5093
  var PLIST_PATH = path9.join(PLIST_DIR, `${PLIST_LABEL}.plist`);
5023
5094
  function escapeXml(s) {
5024
5095
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
@@ -5038,7 +5109,7 @@ var MacOSService = class {
5038
5109
  if (!opts.upstream) {
5039
5110
  args.push("--no-upstream");
5040
5111
  }
5041
- const logFile = path9.join(os4.homedir(), ".jarvis", "agent.log");
5112
+ const logFile = path9.join(os5.homedir(), ".jarvis", "agent.log");
5042
5113
  const envPath = process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin";
5043
5114
  const plist = `<?xml version="1.0" encoding="UTF-8"?>
5044
5115
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -5060,7 +5131,7 @@ ${args.map((a) => ` <string>${escapeXml(a)}</string>`).join("\n")}
5060
5131
  <key>PATH</key>
5061
5132
  <string>${escapeXml(envPath)}</string>
5062
5133
  <key>HOME</key>
5063
- <string>${escapeXml(os4.homedir())}</string>
5134
+ <string>${escapeXml(os5.homedir())}</string>
5064
5135
  <key>NODE_NO_WARNINGS</key>
5065
5136
  <string>1</string>
5066
5137
  ${Object.entries(opts.env ?? {}).map(([k, v]) => ` <key>${escapeXml(k)}</key>
@@ -5198,7 +5269,7 @@ var WindowsService = class {
5198
5269
  </Actions>
5199
5270
  </Task>
5200
5271
  `;
5201
- const tmpDir = os4.tmpdir();
5272
+ const tmpDir = os5.tmpdir();
5202
5273
  const tmpFile = path9.join(tmpDir, `jarvis-task-${Date.now()}.xml`);
5203
5274
  fs9.writeFileSync(tmpFile, xml, { encoding: "utf-16le" });
5204
5275
  try {
@@ -5245,7 +5316,7 @@ var WindowsService = class {
5245
5316
  }
5246
5317
  }
5247
5318
  };
5248
- var SYSTEMD_DIR = path9.join(os4.homedir(), ".config", "systemd", "user");
5319
+ var SYSTEMD_DIR = path9.join(os5.homedir(), ".config", "systemd", "user");
5249
5320
  var UNIT_NAME = "jarvis.service";
5250
5321
  var UNIT_PATH = path9.join(SYSTEMD_DIR, UNIT_NAME);
5251
5322
  var LinuxService = class {
@@ -5262,7 +5333,7 @@ var LinuxService = class {
5262
5333
  if (!opts.upstream) {
5263
5334
  args.push("--no-upstream");
5264
5335
  }
5265
- const logFile = path9.join(os4.homedir(), ".jarvis", "agent.log");
5336
+ const logFile = path9.join(os5.homedir(), ".jarvis", "agent.log");
5266
5337
  const envPath = process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin";
5267
5338
  const unit = `[Unit]
5268
5339
  Description=Jarvis AI Agent
@@ -5273,7 +5344,7 @@ Type=simple
5273
5344
  ExecStart=${opts.nodePath} ${args.join(" ")}
5274
5345
  WorkingDirectory=${opts.workspacePath}
5275
5346
  Environment=PATH=${envPath}
5276
- Environment=HOME=${os4.homedir()}
5347
+ Environment=HOME=${os5.homedir()}
5277
5348
  Environment=NODE_NO_WARNINGS=1
5278
5349
  Restart=always
5279
5350
  RestartSec=5
@@ -5296,7 +5367,7 @@ WantedBy=default.target
5296
5367
  execSync("systemctl --user enable jarvis.service", { stdio: "ignore" });
5297
5368
  execSync("systemctl --user start jarvis.service", { stdio: "ignore" });
5298
5369
  try {
5299
- execSync(`loginctl enable-linger ${os4.userInfo().username}`, { stdio: "ignore" });
5370
+ execSync(`loginctl enable-linger ${os5.userInfo().username}`, { stdio: "ignore" });
5300
5371
  } catch {
5301
5372
  }
5302
5373
  }
@@ -5349,12 +5420,12 @@ WantedBy=default.target
5349
5420
  };
5350
5421
  var FallbackService = class {
5351
5422
  constructor() {
5352
- this.pidFile = path9.join(os4.homedir(), ".jarvis", "agent.pid");
5353
- this.logFile = path9.join(os4.homedir(), ".jarvis", "agent.log");
5354
- this.markerFile = path9.join(os4.homedir(), ".jarvis", "service-installed");
5423
+ this.pidFile = path9.join(os5.homedir(), ".jarvis", "agent.pid");
5424
+ this.logFile = path9.join(os5.homedir(), ".jarvis", "agent.log");
5425
+ this.markerFile = path9.join(os5.homedir(), ".jarvis", "service-installed");
5355
5426
  }
5356
5427
  install(opts) {
5357
- const jarvisDir = path9.join(os4.homedir(), ".jarvis");
5428
+ const jarvisDir = path9.join(os5.homedir(), ".jarvis");
5358
5429
  fs9.mkdirSync(jarvisDir, { recursive: true });
5359
5430
  this.stop();
5360
5431
  const args = [
@@ -5439,7 +5510,7 @@ var FallbackService = class {
5439
5510
  import { createRequire } from "module";
5440
5511
  var _require = createRequire(import.meta.url);
5441
5512
  var PKG_VERSION = _require("../package.json").version ?? "dev";
5442
- var LOG_DIR = path10.join(os5.homedir(), ".jarvis");
5513
+ var LOG_DIR = path10.join(os6.homedir(), ".jarvis");
5443
5514
  var LOG_FILE = path10.join(LOG_DIR, "agent.log");
5444
5515
  var PID_FILE = path10.join(LOG_DIR, "agent.pid");
5445
5516
  function readPid() {
@@ -5540,9 +5611,8 @@ async function browserAuth(appUrl) {
5540
5611
  }
5541
5612
  }
5542
5613
  function getAppUrl() {
5543
- if ("https://jarvis.appchy.com") return "https://jarvis.appchy.com";
5544
- if (process.env.APP_URL) return process.env.APP_URL;
5545
- return "https://jarvis.appchy.com";
5614
+ const config = loadConfig();
5615
+ return config?.appUrl ?? getDefaultAppUrl();
5546
5616
  }
5547
5617
  async function ensureSetup() {
5548
5618
  const config = loadConfig();
@@ -5552,6 +5622,45 @@ async function ensureSetup() {
5552
5622
  clearConfig();
5553
5623
  return browserAuth(getAppUrl());
5554
5624
  }
5625
+ function parseGitRemote(url) {
5626
+ const sshMatch = url.match(/[:\/]([^/]+)\/([^/]+?)(?:\.git)?$/);
5627
+ if (sshMatch) return { owner: sshMatch[1], name: sshMatch[2] };
5628
+ return null;
5629
+ }
5630
+ async function autoRegisterRepo(workspacePath, config) {
5631
+ try {
5632
+ const remoteUrl = execSync2("git config --get remote.origin.url", {
5633
+ cwd: workspacePath,
5634
+ encoding: "utf-8",
5635
+ timeout: 5e3
5636
+ }).trim();
5637
+ if (!remoteUrl) return;
5638
+ const parsed = parseGitRemote(remoteUrl);
5639
+ if (!parsed) return;
5640
+ const slug = `${parsed.owner}/${parsed.name}`;
5641
+ const appUrl = config?.appUrl ?? getDefaultAppUrl();
5642
+ const token = config?.token;
5643
+ if (!token) return;
5644
+ console.log(`Registering repo ${slug} \u2192 ${workspacePath}`);
5645
+ const envId = config?.envId ?? "local";
5646
+ const res = await fetch(`${appUrl}/api/v1/settings/preferences/repos?envId=${envId}`, {
5647
+ method: "PATCH",
5648
+ headers: {
5649
+ "Content-Type": "application/json",
5650
+ Authorization: `Bearer ${token}`
5651
+ },
5652
+ body: JSON.stringify({ [slug]: workspacePath })
5653
+ });
5654
+ if (res.ok) {
5655
+ console.log(` Repo registered`);
5656
+ } else {
5657
+ const body = await res.text().catch(() => "");
5658
+ console.error(` Failed to register repo (${res.status}): ${body}`);
5659
+ }
5660
+ } catch (err) {
5661
+ console.error(` Failed to register repo: ${err instanceof Error ? err.message : err}`);
5662
+ }
5663
+ }
5555
5664
  function createCli() {
5556
5665
  const program = new Command().name("jarvis").description("Jarvis local agent \u2014 runs Claude Code on your machine").version(PKG_VERSION);
5557
5666
  program.command("connect <token>").description("Connect to Jarvis cloud using a token from the web UI").option("-w, --workspace <path>", "Workspace root path for repo operations").action((token, opts) => {
@@ -5593,6 +5702,9 @@ function createCli() {
5593
5702
  const port = parseInt(opts.port, 10);
5594
5703
  const workspacePath = opts.workspace ?? config?.workspacePath ?? process.cwd();
5595
5704
  const userId = config?.userId ?? process.env.JARVIS_USER_ID ?? "local";
5705
+ if (!opts.foreground) {
5706
+ await autoRegisterRepo(workspacePath, config);
5707
+ }
5596
5708
  const explicitApiKey = opts.apiKey ?? config?.anthropicApiKey;
5597
5709
  const useSubscription = !explicitApiKey;
5598
5710
  const anthropicApiKey = explicitApiKey;
@@ -5666,17 +5778,19 @@ function createCli() {
5666
5778
  const binPath = process.argv[1];
5667
5779
  const cliRoot = path10.resolve(path10.dirname(binPath), "..");
5668
5780
  const distEntry = path10.join(cliRoot, "dist", "bin.js");
5669
- const entryPath = fs10.existsSync(distEntry) ? distEntry : binPath;
5781
+ const srcEntry = path10.join(cliRoot, "src", "bin.ts");
5782
+ const useSrc = isDev() && fs10.existsSync(srcEntry);
5783
+ const entryPath = useSrc ? srcEntry : fs10.existsSync(distEntry) ? distEntry : binPath;
5784
+ const tsxBin = path10.join(cliRoot, "node_modules", ".bin", "tsx");
5785
+ const nodePath = useSrc && fs10.existsSync(tsxBin) ? tsxBin : process.execPath;
5670
5786
  const serviceEnv = {};
5671
- if (process.env.APP_URL) serviceEnv.APP_URL = process.env.APP_URL;
5672
- if ("https://jarvis.appchy.com") serviceEnv.JARVIS_APP_URL = "https://jarvis.appchy.com";
5673
5787
  if (process.env.JARVIS_CONFIG_DIR) serviceEnv.JARVIS_CONFIG_DIR = process.env.JARVIS_CONFIG_DIR;
5674
5788
  if (process.env.JARVIS_USER_ID) serviceEnv.JARVIS_USER_ID = process.env.JARVIS_USER_ID;
5675
5789
  if (process.env.JARVIS_ENV_ID) serviceEnv.JARVIS_ENV_ID = process.env.JARVIS_ENV_ID;
5676
5790
  service.install({
5677
5791
  port,
5678
5792
  workspacePath,
5679
- nodePath: process.execPath,
5793
+ nodePath,
5680
5794
  entryPath,
5681
5795
  upstream: opts.upstream,
5682
5796
  ...Object.keys(serviceEnv).length > 0 ? { env: serviceEnv } : {}
@@ -5813,12 +5927,11 @@ function createCli() {
5813
5927
  const binPath = process.argv[1];
5814
5928
  const cliRoot = path10.resolve(path10.dirname(binPath), "..");
5815
5929
  const distEntry = path10.join(cliRoot, "dist", "bin.js");
5816
- const entryPath = fs10.existsSync(distEntry) ? distEntry : binPath;
5817
- if (!fs10.existsSync(distEntry)) {
5818
- console.log("\x1B[33mWarning:\x1B[0m Using dev mode entry point (tsx).");
5819
- console.log(" For reliability, build first: pnpm build");
5820
- console.log();
5821
- }
5930
+ const srcEntry = path10.join(cliRoot, "src", "bin.ts");
5931
+ const useSrc = isDev() && fs10.existsSync(srcEntry);
5932
+ const entryPath = useSrc ? srcEntry : fs10.existsSync(distEntry) ? distEntry : binPath;
5933
+ const tsxBin = path10.join(cliRoot, "node_modules", ".bin", "tsx");
5934
+ const nodePath = useSrc && fs10.existsSync(tsxBin) ? tsxBin : process.execPath;
5822
5935
  const pid = readPid();
5823
5936
  if (pid) {
5824
5937
  try {
@@ -5828,8 +5941,6 @@ function createCli() {
5828
5941
  clearPid();
5829
5942
  }
5830
5943
  const installEnv = {};
5831
- if (process.env.APP_URL) installEnv.APP_URL = process.env.APP_URL;
5832
- if ("https://jarvis.appchy.com") installEnv.JARVIS_APP_URL = "https://jarvis.appchy.com";
5833
5944
  if (process.env.JARVIS_CONFIG_DIR) installEnv.JARVIS_CONFIG_DIR = process.env.JARVIS_CONFIG_DIR;
5834
5945
  if (process.env.JARVIS_USER_ID) installEnv.JARVIS_USER_ID = process.env.JARVIS_USER_ID;
5835
5946
  if (process.env.JARVIS_ENV_ID) installEnv.JARVIS_ENV_ID = process.env.JARVIS_ENV_ID;
@@ -5837,7 +5948,7 @@ function createCli() {
5837
5948
  service.install({
5838
5949
  port,
5839
5950
  workspacePath,
5840
- nodePath: process.execPath,
5951
+ nodePath,
5841
5952
  entryPath,
5842
5953
  upstream: opts.upstream,
5843
5954
  ...Object.keys(installEnv).length > 0 ? { env: installEnv } : {}
@@ -5885,22 +5996,19 @@ function createCli() {
5885
5996
  }
5886
5997
 
5887
5998
  // src/bin.ts
5888
- function findEnv() {
5999
+ if (isDev()) {
6000
+ const dotenv = await import("dotenv");
6001
+ const fs11 = await import("fs");
6002
+ const path11 = await import("path");
5889
6003
  let dir = process.cwd();
5890
6004
  while (dir !== path11.dirname(dir)) {
5891
6005
  const envPath = path11.join(dir, ".env");
5892
- if (fs11.existsSync(envPath)) return envPath;
6006
+ if (fs11.existsSync(envPath)) {
6007
+ dotenv.config({ path: envPath });
6008
+ break;
6009
+ }
5893
6010
  dir = path11.dirname(dir);
5894
6011
  }
5895
- return void 0;
5896
- }
5897
- var envFile = findEnv();
5898
- if (envFile) {
5899
- const prevApiKey = process.env.ANTHROPIC_API_KEY;
5900
- const prevAppUrl = process.env.APP_URL;
5901
- dotenv.config({ path: envFile });
5902
- if (prevApiKey === void 0) delete process.env.ANTHROPIC_API_KEY;
5903
- if (prevAppUrl === void 0) delete process.env.APP_URL;
5904
6012
  }
5905
6013
  createCli().parse();
5906
6014
  //# sourceMappingURL=bin.js.map