syntaur 0.17.2 → 0.17.4

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 (64) hide show
  1. package/dashboard/dist/assets/{_basePickBy-DiPrdxAJ.js → _basePickBy-BpPf286H.js} +1 -1
  2. package/dashboard/dist/assets/{_baseUniq-CChFfAnf.js → _baseUniq-B7C5cCku.js} +1 -1
  3. package/dashboard/dist/assets/{arc-CfkGktFw.js → arc-D_9vDkbY.js} +1 -1
  4. package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-hFcMu5eO.js → architectureDiagram-2XIMDMQ5-DiF6MOTJ.js} +1 -1
  5. package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-GZDth9sZ.js → blockDiagram-WCTKOSBZ-BP2h0BR5.js} +1 -1
  6. package/dashboard/dist/assets/{c4Diagram-IC4MRINW-BVajqiYz.js → c4Diagram-IC4MRINW-CoGNxzdx.js} +1 -1
  7. package/dashboard/dist/assets/channel-DWNCxH-g.js +1 -0
  8. package/dashboard/dist/assets/{chunk-4BX2VUAB-L7ajwHyw.js → chunk-4BX2VUAB-ChDAnHIH.js} +1 -1
  9. package/dashboard/dist/assets/{chunk-55IACEB6-BACmeqT4.js → chunk-55IACEB6-C07him-W.js} +1 -1
  10. package/dashboard/dist/assets/{chunk-FMBD7UC4-BYQ2kuuH.js → chunk-FMBD7UC4-cI4qw9DG.js} +1 -1
  11. package/dashboard/dist/assets/{chunk-JSJVCQXG-akulVsXk.js → chunk-JSJVCQXG-DoAIOEFg.js} +1 -1
  12. package/dashboard/dist/assets/{chunk-KX2RTZJC-4ibFxLB_.js → chunk-KX2RTZJC-DTLZqkWh.js} +1 -1
  13. package/dashboard/dist/assets/{chunk-NQ4KR5QH-DU4iJf0Y.js → chunk-NQ4KR5QH-IN-EHGhx.js} +1 -1
  14. package/dashboard/dist/assets/{chunk-QZHKN3VN-ouyGBhQB.js → chunk-QZHKN3VN-ut9Jnyz_.js} +1 -1
  15. package/dashboard/dist/assets/{chunk-WL4C6EOR-Pq-ySwIY.js → chunk-WL4C6EOR-BTcLjq-Y.js} +1 -1
  16. package/dashboard/dist/assets/classDiagram-VBA2DB6C-6MS3NF8D.js +1 -0
  17. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-6MS3NF8D.js +1 -0
  18. package/dashboard/dist/assets/clone-Cf64SBgm.js +1 -0
  19. package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-BfkMWgQb.js → cose-bilkent-S5V4N54A-DBFXAFoa.js} +1 -1
  20. package/dashboard/dist/assets/{dagre-KLK3FWXG-0yhY8ZsO.js → dagre-KLK3FWXG-DPhNnahu.js} +1 -1
  21. package/dashboard/dist/assets/{diagram-E7M64L7V-DBkanpFi.js → diagram-E7M64L7V-DKJElW2H.js} +1 -1
  22. package/dashboard/dist/assets/{diagram-IFDJBPK2-CrucCpxV.js → diagram-IFDJBPK2-DA-z6BUh.js} +1 -1
  23. package/dashboard/dist/assets/{diagram-P4PSJMXO-BJrQ6lK3.js → diagram-P4PSJMXO-CAChe-6K.js} +1 -1
  24. package/dashboard/dist/assets/{erDiagram-INFDFZHY--joDpxAm.js → erDiagram-INFDFZHY-nZ7KF-S5.js} +1 -1
  25. package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-BUXiVwju.js → flowDiagram-PKNHOUZH-BnnXo1yn.js} +1 -1
  26. package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-u2XuIMRA.js → ganttDiagram-A5KZAMGK-CojjofKB.js} +1 -1
  27. package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-Ayf0-gwT.js → gitGraphDiagram-K3NZZRJ6-CHNAukVc.js} +1 -1
  28. package/dashboard/dist/assets/{graph-BUGVXq3c.js → graph-u-e4vu0v.js} +1 -1
  29. package/dashboard/dist/assets/index-D5XP-t_-.js +535 -0
  30. package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-DYehx6g7.js → infoDiagram-LFFYTUFH-B9Plf0Ic.js} +1 -1
  31. package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-Bo8-BClf.js → ishikawaDiagram-PHBUUO56-BObZ33Nn.js} +1 -1
  32. package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-DLm-bRYd.js → journeyDiagram-4ABVD52K-N-D6qfr_.js} +1 -1
  33. package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-stPdEvds.js → kanban-definition-K7BYSVSG-BU-obPjd.js} +1 -1
  34. package/dashboard/dist/assets/{layout-B7aPbDvN.js → layout-DsEk1Bju.js} +1 -1
  35. package/dashboard/dist/assets/{linear-CtQ_RMAp.js → linear-C5o0Iw5q.js} +1 -1
  36. package/dashboard/dist/assets/{mermaid.core-CcJoQ5At.js → mermaid.core-BZfPXHU-.js} +4 -4
  37. package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-DahXO6Ui.js → mindmap-definition-YRQLILUH-Bd90glMx.js} +1 -1
  38. package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-DXv973_0.js → pieDiagram-SKSYHLDU-CVIfxVHv.js} +1 -1
  39. package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-5U0ryvjr.js → quadrantDiagram-337W2JSQ-78_3pp5P.js} +1 -1
  40. package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-CvGflSyl.js → requirementDiagram-Z7DCOOCP-hM_bPeDu.js} +1 -1
  41. package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-AeetdpCi.js → sankeyDiagram-WA2Y5GQK-CvHEs1ZK.js} +1 -1
  42. package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-BhRR4qzn.js → sequenceDiagram-2WXFIKYE-_b8Wyf2w.js} +1 -1
  43. package/dashboard/dist/assets/{stateDiagram-RAJIS63D-CxxF58nH.js → stateDiagram-RAJIS63D-CchV-vMd.js} +1 -1
  44. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-cSA5qPUH.js +1 -0
  45. package/dashboard/dist/assets/{timeline-definition-YZTLITO2-BEQIeygp.js → timeline-definition-YZTLITO2-CSkhkM76.js} +1 -1
  46. package/dashboard/dist/assets/{treemap-KZPCXAKY-BCWNTFfj.js → treemap-KZPCXAKY-8vhHU5oB.js} +1 -1
  47. package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-BcuC_UIz.js → vennDiagram-LZ73GAT5-9ZOmuz-V.js} +1 -1
  48. package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-CWGez3_f.js → xychartDiagram-JWTSCODW-CRmoX8BH.js} +1 -1
  49. package/dashboard/dist/index.html +1 -1
  50. package/dist/dashboard/server.js +120 -17
  51. package/dist/dashboard/server.js.map +1 -1
  52. package/dist/index.js +284 -140
  53. package/dist/index.js.map +1 -1
  54. package/dist/launch/index.d.ts +1 -1
  55. package/dist/launch/index.js +66 -33
  56. package/dist/launch/index.js.map +1 -1
  57. package/package.json +1 -1
  58. package/scripts/install-macos-url-handler.mjs +26 -1
  59. package/dashboard/dist/assets/channel-C3CI1Xiu.js +0 -1
  60. package/dashboard/dist/assets/classDiagram-VBA2DB6C-Ck2tXWRf.js +0 -1
  61. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-Ck2tXWRf.js +0 -1
  62. package/dashboard/dist/assets/clone-BEaO4u3F.js +0 -1
  63. package/dashboard/dist/assets/index-Bv6vn4_x.js +0 -535
  64. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DHCNHbs7.js +0 -1
package/dist/index.js CHANGED
@@ -7856,6 +7856,52 @@ var init_git_worktree = __esm({
7856
7856
  }
7857
7857
  });
7858
7858
 
7859
+ // src/launch/cwd.ts
7860
+ import { existsSync as existsSync2, statSync as statSync2 } from "fs";
7861
+ import { isAbsolute as isAbsolute3 } from "path";
7862
+ function isExistingDir(p) {
7863
+ if (!p || !isAbsolute3(p)) return false;
7864
+ try {
7865
+ return existsSync2(p) && statSync2(p).isDirectory();
7866
+ } catch {
7867
+ return false;
7868
+ }
7869
+ }
7870
+ function resolveWorkspaceCwd(input4) {
7871
+ const { worktreePath, repository, branch, assignmentSlug } = input4;
7872
+ if (isExistingDir(worktreePath)) {
7873
+ return { cwd: worktreePath, fallbackWarning: null, invalidReason: null };
7874
+ }
7875
+ if (isExistingDir(repository)) {
7876
+ const fallbackWarning = worktreePath ? `syntaur: workspace.worktreePath ${worktreePath} is not an existing directory for ${assignmentSlug} \u2014 launching in ${repository}` : formatFallbackCwdWarning({
7877
+ assignmentSlug,
7878
+ workspaceDir: repository,
7879
+ worktreePath,
7880
+ branch
7881
+ });
7882
+ return { cwd: repository, fallbackWarning, invalidReason: null };
7883
+ }
7884
+ const shown = (p) => p && p.trim().length > 0 ? p : "(unset)";
7885
+ return {
7886
+ cwd: null,
7887
+ fallbackWarning: null,
7888
+ invalidReason: `workspace path invalid for ${assignmentSlug}: tried worktreePath ${shown(worktreePath)} and repository ${shown(repository)} \u2014 neither is an existing directory`
7889
+ };
7890
+ }
7891
+ function formatFallbackCwdWarning(opts) {
7892
+ const missing = [];
7893
+ if (!opts.worktreePath) missing.push("worktreePath");
7894
+ if (!opts.branch) missing.push("branch");
7895
+ if (missing.length === 0) return null;
7896
+ const fields = missing.map((m) => `workspace.${m}`).join(" and ");
7897
+ return `syntaur: ${fields} not set for ${opts.assignmentSlug} \u2014 launching in ${opts.workspaceDir}`;
7898
+ }
7899
+ var init_cwd = __esm({
7900
+ "src/launch/cwd.ts"() {
7901
+ "use strict";
7902
+ }
7903
+ });
7904
+
7859
7905
  // src/utils/promote-todos.ts
7860
7906
  var promote_todos_exports = {};
7861
7907
  __export(promote_todos_exports, {
@@ -8047,15 +8093,7 @@ __export(launch_exports, {
8047
8093
  });
8048
8094
  import { spawn as spawn3 } from "child_process";
8049
8095
  import { mkdir as mkdir6, writeFile as writeFile10 } from "fs/promises";
8050
- import { isAbsolute as isAbsolute4, resolve as resolve43 } from "path";
8051
- function formatFallbackCwdWarning(opts) {
8052
- const missing = [];
8053
- if (!opts.worktreePath) missing.push("worktreePath");
8054
- if (!opts.branch) missing.push("branch");
8055
- if (missing.length === 0) return null;
8056
- const fields = missing.map((m) => `workspace.${m}`).join(" and ");
8057
- return `syntaur: ${fields} not set for ${opts.assignmentSlug} \u2014 launching in ${opts.workspaceDir}`;
8058
- }
8096
+ import { isAbsolute as isAbsolute5, resolve as resolve43 } from "path";
8059
8097
  function shellQuote(arg) {
8060
8098
  if (arg === "") return "''";
8061
8099
  return `'${arg.replace(/'/g, `'\\''`)}'`;
@@ -8068,7 +8106,7 @@ function buildAgentArgv(agent, prompt, env = process.env) {
8068
8106
  const requested = env.SHELL;
8069
8107
  let shell = requested;
8070
8108
  let warning = null;
8071
- if (!shell || !isAbsolute4(shell)) {
8109
+ if (!shell || !isAbsolute5(shell)) {
8072
8110
  warning = `syntaur: $SHELL ${requested ? `("${requested}") is not absolute` : "is unset"} \u2014 falling back to /bin/sh for shell-alias resolution`;
8073
8111
  shell = "/bin/sh";
8074
8112
  }
@@ -8093,10 +8131,30 @@ async function launchAgent(options) {
8093
8131
  }
8094
8132
  const projectDir = resolve43(projectsDir2, projectSlug);
8095
8133
  const assignmentDir = resolve43(projectDir, "assignments", assignmentSlug);
8096
- const resolvedFromWorkspace = cwdOverride ?? detail.workspace.worktreePath ?? (detail.workspace.repository?.startsWith("/") ? detail.workspace.repository : null);
8097
- const workspaceDir = resolvedFromWorkspace ?? process.cwd();
8098
- if (!cwdOverride) {
8099
- const warning = formatFallbackCwdWarning({
8134
+ let workspaceDir;
8135
+ if (cwdOverride) {
8136
+ if (!isExistingDir(cwdOverride)) {
8137
+ console.error(
8138
+ `syntaur: --cwd ${cwdOverride} is not an existing directory \u2014 refusing to launch.`
8139
+ );
8140
+ exitWith(1);
8141
+ return;
8142
+ }
8143
+ workspaceDir = cwdOverride;
8144
+ } else {
8145
+ const picked = resolveWorkspaceCwd({
8146
+ worktreePath: detail.workspace.worktreePath,
8147
+ repository: detail.workspace.repository,
8148
+ branch: detail.workspace.branch,
8149
+ assignmentSlug
8150
+ });
8151
+ if (picked.cwd === null) {
8152
+ console.error(`syntaur: ${picked.invalidReason} \u2014 refusing to launch.`);
8153
+ exitWith(1);
8154
+ return;
8155
+ }
8156
+ workspaceDir = picked.cwd;
8157
+ const warning = picked.fallbackWarning ?? formatFallbackCwdWarning({
8100
8158
  assignmentSlug,
8101
8159
  workspaceDir,
8102
8160
  worktreePath: detail.workspace.worktreePath,
@@ -8127,8 +8185,9 @@ async function launchAgent(options) {
8127
8185
  if (shellFallbackWarning) {
8128
8186
  console.warn(shellFallbackWarning);
8129
8187
  }
8188
+ const spawnImpl = options.spawnFn ?? spawn3;
8130
8189
  return new Promise((resolvePromise) => {
8131
- const child = spawn3(argv.command, argv.args, {
8190
+ const child = spawnImpl(argv.command, argv.args, {
8132
8191
  cwd: workspaceDir,
8133
8192
  stdio: "inherit"
8134
8193
  });
@@ -8161,6 +8220,8 @@ var init_launch = __esm({
8161
8220
  "src/tui/launch.ts"() {
8162
8221
  "use strict";
8163
8222
  init_api();
8223
+ init_cwd();
8224
+ init_cwd();
8164
8225
  INITIAL_PROMPT = (params2) => {
8165
8226
  if (params2.projectSlug) {
8166
8227
  return `/grab-assignment ${params2.projectSlug} ${params2.assignmentSlug}`;
@@ -8179,7 +8240,7 @@ __export(install_macos_url_handler_exports, {
8179
8240
  registerMacosUrlHandler: () => registerMacosUrlHandler
8180
8241
  });
8181
8242
  import {
8182
- existsSync as existsSync2,
8243
+ existsSync as existsSync4,
8183
8244
  mkdirSync as mkdirSync2,
8184
8245
  rmSync,
8185
8246
  writeFileSync,
@@ -8189,15 +8250,15 @@ import {
8189
8250
  unlinkSync
8190
8251
  } from "fs";
8191
8252
  import { fileURLToPath as fileURLToPath6, pathToFileURL } from "url";
8192
- import { dirname as dirname14, resolve as resolve45, join as join5 } from "path";
8193
- import { homedir as homedir6, tmpdir as tmpdir2 } from "os";
8253
+ import { dirname as dirname14, resolve as resolve45, join as join6 } from "path";
8254
+ import { homedir as homedir7, tmpdir as tmpdir2 } from "os";
8194
8255
  import { spawnSync as spawnSync6 } from "child_process";
8195
8256
  function syntaurRootMjs() {
8196
8257
  const override = process.env.SYNTAUR_HOME;
8197
8258
  if (override && override.length > 0) {
8198
8259
  return override;
8199
8260
  }
8200
- return join5(homedir6(), ".syntaur");
8261
+ return join6(homedir7(), ".syntaur");
8201
8262
  }
8202
8263
  async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
8203
8264
  const { throwOnFailure } = options;
@@ -8230,19 +8291,19 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
8230
8291
  const pkgRoot = resolve45(dirname14(fileURLToPath6(import.meta.url)), "..");
8231
8292
  const cliBin = realpathSync2(resolve45(pkgRoot, "bin/syntaur.js"));
8232
8293
  const nodeBin = process.execPath;
8233
- const bundleParent = join5(
8234
- homedir6(),
8294
+ const bundleParent = join6(
8295
+ homedir7(),
8235
8296
  "Library",
8236
8297
  "Application Support",
8237
8298
  "Syntaur"
8238
8299
  );
8239
- const bundlePath = join5(bundleParent, "syntaur-url.app");
8300
+ const bundlePath = join6(bundleParent, "syntaur-url.app");
8240
8301
  mkdirSync2(bundleParent, { recursive: true });
8241
- if (existsSync2(bundlePath)) {
8302
+ if (existsSync4(bundlePath)) {
8242
8303
  rmSync(bundlePath, { recursive: true, force: true });
8243
8304
  }
8244
8305
  const installedTerminals = detectInstalledTerminals();
8245
- const scriptPath = join5(
8306
+ const scriptPath = join6(
8246
8307
  tmpdir2(),
8247
8308
  `syntaur-url-handler-${process.pid}.applescript`
8248
8309
  );
@@ -8261,8 +8322,8 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
8261
8322
  `osacompile exited with code ${compile.status}: ${compile.stderr || compile.stdout}`
8262
8323
  );
8263
8324
  }
8264
- const infoPlistPath = join5(bundlePath, "Contents", "Info.plist");
8265
- if (!existsSync2(infoPlistPath)) {
8325
+ const infoPlistPath = join6(bundlePath, "Contents", "Info.plist");
8326
+ if (!existsSync4(infoPlistPath)) {
8266
8327
  throw new Error(`osacompile did not produce ${infoPlistPath}`);
8267
8328
  }
8268
8329
  runPlistBuddy(infoPlistPath, [
@@ -8351,6 +8412,12 @@ function detectInstalledTerminals() {
8351
8412
  ghostty: "com.mitchellh.ghostty",
8352
8413
  warp: "dev.warp.Warp-Stable"
8353
8414
  };
8415
+ const bundleNames = {
8416
+ iterm: "iTerm.app",
8417
+ ghostty: "Ghostty.app",
8418
+ warp: "Warp.app"
8419
+ };
8420
+ const appDirs = ["/Applications", join6(homedir7(), "Applications")];
8354
8421
  for (const [id, bundleId] of Object.entries(bundleIds)) {
8355
8422
  const r = spawnSync6(
8356
8423
  "mdfind",
@@ -8359,6 +8426,11 @@ function detectInstalledTerminals() {
8359
8426
  );
8360
8427
  if (r.status === 0 && r.stdout.trim().length > 0) {
8361
8428
  installed.add(id);
8429
+ continue;
8430
+ }
8431
+ const name = bundleNames[id];
8432
+ if (name && appDirs.some((dir) => existsSync4(join6(dir, name)))) {
8433
+ installed.add(id);
8362
8434
  }
8363
8435
  }
8364
8436
  return installed;
@@ -8452,11 +8524,17 @@ function renderAppleScript({ nodeBin, cliBin, installedTerminals }) {
8452
8524
  " -- Step 1: ask the CLI for the launch plan (two lines: terminal, shellCmd).",
8453
8525
  ' set planOutput to ""',
8454
8526
  " try",
8455
- ` set planOutput to (do shell script quoted form of nodeBin & " " & quoted form of cliBin & " url --print-plan " & quoted form of theURL & " 2>> " & quoted form of logFile)`,
8527
+ ` set planOutput to (do shell script quoted form of nodeBin & " " & quoted form of cliBin & " url --print-plan " & quoted form of theURL)`,
8456
8528
  " on error errMsg number errNum",
8529
+ " -- No `2>>` redirect above, so a non-zero CLI exit surfaces its stderr",
8530
+ " -- (the formatUrlCommandError message, e.g. workspace-path-invalid) in",
8531
+ " -- errMsg. Log it AND show the user a visible alert.",
8457
8532
  " try",
8458
8533
  ` do shell script "/usr/bin/printf '%s\\\\n' " & quoted form of ("syntaur:// plan resolution failed for " & theURL & ": " & errMsg & " (" & errNum & ")") & " >> " & quoted form of logFile`,
8459
8534
  " end try",
8535
+ " try",
8536
+ ' display alert "Open in agent failed" message errMsg as critical',
8537
+ " end try",
8460
8538
  " return",
8461
8539
  " end try",
8462
8540
  "",
@@ -8481,6 +8559,9 @@ function renderAppleScript({ nodeBin, cliBin, installedTerminals }) {
8481
8559
  " try",
8482
8560
  ` do shell script "/usr/bin/printf '%s\\\\n' " & quoted form of ("syntaur:// launch failed for " & theURL & " (terminal=" & theTerminal & "): " & errMsg & " (" & errNum & ")") & " >> " & quoted form of logFile`,
8483
8561
  " end try",
8562
+ " try",
8563
+ ' display alert "Open in agent failed" message errMsg as critical',
8564
+ " end try",
8484
8565
  " end try",
8485
8566
  "end open location",
8486
8567
  "",
@@ -13091,17 +13172,43 @@ import { Router as Router6 } from "express";
13091
13172
 
13092
13173
  // src/utils/terminal-probe.ts
13093
13174
  import { spawnSync as spawnSync4 } from "child_process";
13175
+ import { existsSync } from "fs";
13176
+ import { homedir as homedir2 } from "os";
13177
+ import { join as join2 } from "path";
13094
13178
  var APP_BUNDLE_IDS = {
13095
13179
  "terminal-app": "com.apple.Terminal",
13096
13180
  iterm: "com.googlecode.iterm2",
13097
13181
  ghostty: "com.mitchellh.ghostty",
13098
13182
  warp: "dev.warp.Warp-Stable"
13099
13183
  };
13184
+ var APP_BUNDLE_NAMES = {
13185
+ iterm: "iTerm.app",
13186
+ ghostty: "Ghostty.app",
13187
+ warp: "Warp.app"
13188
+ };
13189
+ var APP_FIXED_PATHS = {
13190
+ "terminal-app": "/System/Applications/Utilities/Terminal.app"
13191
+ };
13192
+ function defaultApplicationsDirs() {
13193
+ return ["/Applications", join2(homedir2(), "Applications")];
13194
+ }
13195
+ function findAppBundle(terminal, dirs = defaultApplicationsDirs()) {
13196
+ const fixed = APP_FIXED_PATHS[terminal];
13197
+ if (fixed && existsSync(fixed)) return fixed;
13198
+ const bundleName = APP_BUNDLE_NAMES[terminal];
13199
+ if (bundleName) {
13200
+ for (const dir of dirs) {
13201
+ const candidate = join2(dir, bundleName);
13202
+ if (existsSync(candidate)) return candidate;
13203
+ }
13204
+ }
13205
+ return null;
13206
+ }
13100
13207
  var CLI_NAMES = {
13101
13208
  alacritty: "alacritty",
13102
13209
  kitty: "kitty"
13103
13210
  };
13104
- function probeTerminalInstalled(terminal) {
13211
+ function probeTerminalInstalled(terminal, opts = {}) {
13105
13212
  const bundleId = APP_BUNDLE_IDS[terminal];
13106
13213
  if (bundleId) {
13107
13214
  const result = spawnSync4(
@@ -13112,6 +13219,10 @@ function probeTerminalInstalled(terminal) {
13112
13219
  if (result.status === 0 && result.stdout.trim().length > 0) {
13113
13220
  return { ok: true, foundPath: result.stdout.trim().split("\n")[0] };
13114
13221
  }
13222
+ const bundlePath = findAppBundle(terminal, opts.applicationsDirsOverride);
13223
+ if (bundlePath) {
13224
+ return { ok: true, foundPath: bundlePath };
13225
+ }
13115
13226
  return { ok: false, reason: "not-installed" };
13116
13227
  }
13117
13228
  const cliName = CLI_NAMES[terminal];
@@ -13126,7 +13237,9 @@ function probeTerminalInstalled(terminal) {
13126
13237
  }
13127
13238
 
13128
13239
  // src/dashboard/api-launch-preflight.ts
13129
- function createLaunchPreflightRouter() {
13240
+ init_api();
13241
+ init_cwd();
13242
+ function createLaunchPreflightRouter(projectsDir2, assignmentsDir2) {
13130
13243
  const router = Router6();
13131
13244
  router.post("/preflight", async (req, res) => {
13132
13245
  try {
@@ -13140,18 +13253,44 @@ function createLaunchPreflightRouter() {
13140
13253
  const config = await readConfig();
13141
13254
  const terminal = body.terminal ?? getTerminal(config);
13142
13255
  const probe = probeTerminalInstalled(terminal);
13143
- if (probe.ok) {
13144
- const response2 = { ok: true, terminal };
13256
+ if (!probe.ok) {
13257
+ const suggestedFallback = getTerminal({ ...config, terminal: null });
13258
+ const response2 = {
13259
+ ok: false,
13260
+ terminal,
13261
+ reason: "not-installed",
13262
+ suggestedFallback
13263
+ };
13145
13264
  res.json(response2);
13146
13265
  return;
13147
13266
  }
13148
- const suggestedFallback = getTerminal({ ...config, terminal: null });
13149
- const response = {
13150
- ok: false,
13151
- terminal,
13152
- reason: "not-installed",
13153
- suggestedFallback
13154
- };
13267
+ const target = body.target;
13268
+ if (target && target.kind === "assignment" && typeof target.id === "string") {
13269
+ const detail = await getAssignmentDetailById(
13270
+ projectsDir2,
13271
+ assignmentsDir2,
13272
+ target.id
13273
+ );
13274
+ if (detail) {
13275
+ const picked = resolveWorkspaceCwd({
13276
+ worktreePath: detail.workspace.worktreePath,
13277
+ repository: detail.workspace.repository,
13278
+ branch: detail.workspace.branch,
13279
+ assignmentSlug: detail.slug
13280
+ });
13281
+ if (picked.cwd === null) {
13282
+ const response2 = {
13283
+ ok: false,
13284
+ terminal,
13285
+ reason: "workspace-path-invalid",
13286
+ message: picked.invalidReason ?? "This assignment has no valid workspace directory."
13287
+ };
13288
+ res.json(response2);
13289
+ return;
13290
+ }
13291
+ }
13292
+ }
13293
+ const response = { ok: true, terminal };
13155
13294
  res.json(response);
13156
13295
  } catch (error) {
13157
13296
  console.error("Error in launch preflight:", error);
@@ -16909,7 +17048,7 @@ init_config2();
16909
17048
  import { execFile as execFile2 } from "child_process";
16910
17049
  import { promisify as promisify2 } from "util";
16911
17050
  import { cp, mkdtemp, rm as rm3, readFile as readFile20, writeFile as writeFile5, unlink as unlink5, stat, open as open2, rename as rename7 } from "fs/promises";
16912
- import { resolve as resolve29, join as join2 } from "path";
17051
+ import { resolve as resolve29, join as join3 } from "path";
16913
17052
  import { tmpdir } from "os";
16914
17053
  var exec2 = promisify2(execFile2);
16915
17054
  var VALID_CATEGORIES = ["projects", "playbooks", "todos", "servers", "config"];
@@ -17039,11 +17178,11 @@ async function backupToGithub(overrides) {
17039
17178
  let tmpDir = null;
17040
17179
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
17041
17180
  try {
17042
- tmpDir = await mkdtemp(join2(tmpdir(), "syntaur-backup-"));
17181
+ tmpDir = await mkdtemp(join3(tmpdir(), "syntaur-backup-"));
17043
17182
  await cloneOrInit(repo, tmpDir);
17044
17183
  for (const category of categories) {
17045
17184
  const { sourcePath, repoPath, isFile } = await resolveCategoryPath(category);
17046
- const destPath = join2(tmpDir, repoPath);
17185
+ const destPath = join3(tmpDir, repoPath);
17047
17186
  if (isFile) {
17048
17187
  await rm3(destPath, { force: true });
17049
17188
  } else {
@@ -17171,7 +17310,7 @@ async function restoreFromGithub(overrides) {
17171
17310
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
17172
17311
  try {
17173
17312
  await updateBackupConfig({ lastRestore: timestamp });
17174
- tmpDir = await mkdtemp(join2(tmpdir(), "syntaur-restore-"));
17313
+ tmpDir = await mkdtemp(join3(tmpdir(), "syntaur-restore-"));
17175
17314
  await cloneOrInit(repo, tmpDir);
17176
17315
  for (const category of categories) {
17177
17316
  if (category === "config") {
@@ -17180,7 +17319,7 @@ async function restoreFromGithub(overrides) {
17180
17319
  }
17181
17320
  try {
17182
17321
  const { sourcePath: localPath, repoPath, isFile } = await resolveCategoryPath(category);
17183
- const repoSrcPath = join2(tmpDir, repoPath);
17322
+ const repoSrcPath = join3(tmpDir, repoPath);
17184
17323
  if (!await fileExists(repoSrcPath)) {
17185
17324
  console.warn(`Category "${category}" not found in backup repo, skipping.`);
17186
17325
  continue;
@@ -18034,7 +18173,10 @@ function createDashboardServer(options) {
18034
18173
  app.use("/api/usage", createUsageRouter());
18035
18174
  app.use("/api/agent-sessions", createAgentSessionsRouter(projectsDir2, broadcast, assignmentsDir2));
18036
18175
  app.use("/api/config/agents", createAgentsRouter());
18037
- app.use("/api/launch", createLaunchPreflightRouter());
18176
+ app.use(
18177
+ "/api/launch",
18178
+ createLaunchPreflightRouter(projectsDir2, assignmentsDir2)
18179
+ );
18038
18180
  app.use("/api/playbooks", createPlaybooksRouter(playbooksDir3));
18039
18181
  app.get("/api/memories", async (_req, res) => {
18040
18182
  try {
@@ -18570,9 +18712,9 @@ import {
18570
18712
  unlink as unlink7,
18571
18713
  writeFile as writeFile7
18572
18714
  } from "fs/promises";
18573
- import { existsSync } from "fs";
18574
- import { homedir as homedir2 } from "os";
18575
- import { basename as basename4, dirname as dirname10, isAbsolute as isAbsolute3, relative as relative2, resolve as resolve35 } from "path";
18715
+ import { existsSync as existsSync3 } from "fs";
18716
+ import { homedir as homedir3 } from "os";
18717
+ import { basename as basename4, dirname as dirname10, isAbsolute as isAbsolute4, relative as relative2, resolve as resolve35 } from "path";
18576
18718
 
18577
18719
  // src/utils/package-root.ts
18578
18720
  init_fs();
@@ -18605,20 +18747,20 @@ function getPluginManifestRelativePath(pluginKind) {
18605
18747
  return pluginKind === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json";
18606
18748
  }
18607
18749
  function getDefaultPluginTargetDir(pluginKind) {
18608
- const home = homedir2();
18750
+ const home = homedir3();
18609
18751
  return pluginKind === "claude" ? resolve35(home, ".claude", "plugins", "syntaur") : resolve35(home, "plugins", "syntaur");
18610
18752
  }
18611
18753
  function getDefaultMarketplacePath() {
18612
- return resolve35(homedir2(), ".agents", "plugins", "marketplace.json");
18754
+ return resolve35(homedir3(), ".agents", "plugins", "marketplace.json");
18613
18755
  }
18614
18756
  function getClaudeMarketplacesRoot() {
18615
- return resolve35(homedir2(), ".claude", "plugins", "marketplaces");
18757
+ return resolve35(homedir3(), ".claude", "plugins", "marketplaces");
18616
18758
  }
18617
18759
  function getClaudeKnownMarketplacesPath() {
18618
- return resolve35(homedir2(), ".claude", "plugins", "known_marketplaces.json");
18760
+ return resolve35(homedir3(), ".claude", "plugins", "known_marketplaces.json");
18619
18761
  }
18620
18762
  function getClaudeInstalledPluginsPath() {
18621
- return resolve35(homedir2(), ".claude", "plugins", "installed_plugins.json");
18763
+ return resolve35(homedir3(), ".claude", "plugins", "installed_plugins.json");
18622
18764
  }
18623
18765
  function getInstallMarkerPath(targetDir) {
18624
18766
  return resolve35(targetDir, INSTALL_MARKER_FILENAME);
@@ -18728,7 +18870,7 @@ async function removeInstallMarker(targetDir) {
18728
18870
  }
18729
18871
  function normalizeAbsoluteInstallPath(pathValue, label) {
18730
18872
  const expanded = expandHome(pathValue.trim());
18731
- if (!isAbsolute3(expanded)) {
18873
+ if (!isAbsolute4(expanded)) {
18732
18874
  throw new Error(`${label} must be an absolute path.`);
18733
18875
  }
18734
18876
  return resolve35(expanded);
@@ -18970,7 +19112,7 @@ async function ensureKnownClaudeMarketplaceForRoot(options) {
18970
19112
  return registerKnownClaudeMarketplace(options.name, options.rootDir);
18971
19113
  }
18972
19114
  async function setSyntaurPluginEnabled(options) {
18973
- const settingsPath = resolve35(homedir2(), ".claude", "settings.json");
19115
+ const settingsPath = resolve35(homedir3(), ".claude", "settings.json");
18974
19116
  const key = `syntaur@${options.marketplaceName}`;
18975
19117
  let parsed = {};
18976
19118
  if (await fileExists(settingsPath)) {
@@ -19468,16 +19610,16 @@ async function textPrompt(question, defaultValue) {
19468
19610
  // src/utils/install-skills.ts
19469
19611
  init_fs();
19470
19612
  import { readFile as readFile24, readdir as readdir15, mkdir as mkdir4, copyFile, rm as rm5, lstat as lstat2 } from "fs/promises";
19471
- import { resolve as resolve37, relative as relative3, join as join3 } from "path";
19472
- import { homedir as homedir4 } from "os";
19613
+ import { resolve as resolve37, relative as relative3, join as join4 } from "path";
19614
+ import { homedir as homedir5 } from "os";
19473
19615
 
19474
19616
  // src/utils/plugin-state.ts
19475
19617
  init_fs();
19476
19618
  import { readFile as readFile23 } from "fs/promises";
19477
19619
  import { resolve as resolve36 } from "path";
19478
- import { homedir as homedir3 } from "os";
19620
+ import { homedir as homedir4 } from "os";
19479
19621
  function settingsPathFor(agent) {
19480
- if (agent === "claude") return resolve36(homedir3(), ".claude", "settings.json");
19622
+ if (agent === "claude") return resolve36(homedir4(), ".claude", "settings.json");
19481
19623
  return null;
19482
19624
  }
19483
19625
  async function readJsonOrNull(path) {
@@ -19536,15 +19678,15 @@ async function getSkillsDir() {
19536
19678
  return resolve37(packageRoot, "skills");
19537
19679
  }
19538
19680
  function defaultSkillTargetDir(target) {
19539
- if (target === "claude") return resolve37(homedir4(), ".claude", "skills");
19540
- return resolve37(homedir4(), ".codex", "skills");
19681
+ if (target === "claude") return resolve37(homedir5(), ".claude", "skills");
19682
+ return resolve37(homedir5(), ".codex", "skills");
19541
19683
  }
19542
19684
  async function walkFiles(root) {
19543
19685
  const out = [];
19544
19686
  async function walk(dir) {
19545
19687
  const entries = await readdir15(dir, { withFileTypes: true });
19546
19688
  for (const entry of entries) {
19547
- const full = join3(dir, entry.name);
19689
+ const full = join4(dir, entry.name);
19548
19690
  if (entry.isDirectory()) {
19549
19691
  await walk(full);
19550
19692
  } else if (entry.isFile()) {
@@ -19568,8 +19710,8 @@ async function copyDir(srcDir, destDir) {
19568
19710
  await mkdir4(destDir, { recursive: true });
19569
19711
  const entries = await readdir15(srcDir, { withFileTypes: true });
19570
19712
  for (const entry of entries) {
19571
- const src = join3(srcDir, entry.name);
19572
- const dest = join3(destDir, entry.name);
19713
+ const src = join4(srcDir, entry.name);
19714
+ const dest = join4(destDir, entry.name);
19573
19715
  if (entry.isDirectory()) {
19574
19716
  await copyDir(src, dest);
19575
19717
  } else if (entry.isFile()) {
@@ -19582,7 +19724,7 @@ async function skillMatches(srcDir, destDir) {
19582
19724
  const srcFiles = await walkFiles(srcDir);
19583
19725
  for (const srcFile of srcFiles) {
19584
19726
  const rel = relative3(srcDir, srcFile);
19585
- const destFile = join3(destDir, rel);
19727
+ const destFile = join4(destDir, rel);
19586
19728
  if (!await filesEqual(srcFile, destFile)) return false;
19587
19729
  }
19588
19730
  const destFiles = await walkFiles(destDir);
@@ -19625,7 +19767,7 @@ async function discoverSkillNames(sourceDir) {
19625
19767
  for (const entry of entries) {
19626
19768
  if (!entry.isDirectory()) continue;
19627
19769
  if (entry.name.startsWith(".")) continue;
19628
- if (await fileExists(join3(sourceDir, entry.name, "SKILL.md"))) {
19770
+ if (await fileExists(join4(sourceDir, entry.name, "SKILL.md"))) {
19629
19771
  names.push(entry.name);
19630
19772
  }
19631
19773
  }
@@ -19652,8 +19794,8 @@ async function installSkillsWithReport(options) {
19652
19794
  const results = [];
19653
19795
  await mkdir4(targetRoot, { recursive: true });
19654
19796
  for (const skill of skillNames) {
19655
- const srcDir = join3(source, skill);
19656
- const destDir = join3(targetRoot, skill);
19797
+ const srcDir = join4(source, skill);
19798
+ const destDir = join4(targetRoot, skill);
19657
19799
  results.push(await installSkillDir(srcDir, destDir, skill, force));
19658
19800
  }
19659
19801
  return { results };
@@ -19674,10 +19816,10 @@ async function uninstallSkills(options) {
19674
19816
  }
19675
19817
  const removed = [];
19676
19818
  for (const skill of known) {
19677
- const destDir = join3(targetRoot, skill);
19819
+ const destDir = join4(targetRoot, skill);
19678
19820
  if (!await fileExists(destDir)) continue;
19679
19821
  if (await isSymlink(destDir)) continue;
19680
- const skillMd = join3(destDir, "SKILL.md");
19822
+ const skillMd = join4(destDir, "SKILL.md");
19681
19823
  if (!await fileExists(skillMd)) continue;
19682
19824
  const content = await readFile24(skillMd, "utf-8").catch(() => "");
19683
19825
  const match = content.match(/^name:\s*(\S+)\s*$/m);
@@ -19870,7 +20012,7 @@ init_paths();
19870
20012
  init_fs();
19871
20013
  import { readFile as readFile26, writeFile as writeFile9, copyFile as copyFile2, rm as rm6, stat as stat2, symlink as symlink2, unlink as unlink8, lstat as lstat3 } from "fs/promises";
19872
20014
  import { resolve as resolve39, dirname as dirname12 } from "path";
19873
- import { homedir as homedir5 } from "os";
20015
+ import { homedir as homedir6 } from "os";
19874
20016
  import { fileURLToPath as fileURLToPath4 } from "url";
19875
20017
 
19876
20018
  // src/commands/configure-statusline.ts
@@ -20171,7 +20313,7 @@ async function installScript(sourceScript, destScript, link) {
20171
20313
  }
20172
20314
  async function installStatuslineCommand(options = {}) {
20173
20315
  const mode = options.mode ?? "ask";
20174
- const settingsPath = options.settingsPath ?? resolve39(homedir5(), ".claude", "settings.json");
20316
+ const settingsPath = options.settingsPath ?? resolve39(homedir6(), ".claude", "settings.json");
20175
20317
  const installRoot = options.installRoot ?? syntaurRoot();
20176
20318
  const sourceScript = options.sourceScript ?? getPackageStatuslineSource();
20177
20319
  const destScript = resolve39(installRoot, "statusline.sh");
@@ -20267,7 +20409,7 @@ async function chmodExec(path) {
20267
20409
  }
20268
20410
  }
20269
20411
  async function uninstallStatuslineCommand(options = {}) {
20270
- const settingsPath = options.settingsPath ?? resolve39(homedir5(), ".claude", "settings.json");
20412
+ const settingsPath = options.settingsPath ?? resolve39(homedir6(), ".claude", "settings.json");
20271
20413
  const installRoot = options.installRoot ?? syntaurRoot();
20272
20414
  const destScript = resolve39(installRoot, "statusline.sh");
20273
20415
  const confPath = resolve39(installRoot, "statusline.conf");
@@ -20926,12 +21068,12 @@ init_config2();
20926
21068
  init_assignment_resolver();
20927
21069
  init_api();
20928
21070
  init_launch();
21071
+ init_cwd();
20929
21072
  init_agent_sessions();
20930
- import { isAbsolute as isAbsolute6 } from "path";
20931
21073
 
20932
21074
  // src/launch/argv.ts
20933
21075
  init_launch();
20934
- import { isAbsolute as isAbsolute5 } from "path";
21076
+ import { isAbsolute as isAbsolute6 } from "path";
20935
21077
  var buildFreshArgv = buildAgentArgv;
20936
21078
  function buildSessionArgv(agent, sessionId, mode, env = process.env) {
20937
21079
  const invocation = agent[mode];
@@ -20950,7 +21092,7 @@ function buildSessionArgv(agent, sessionId, mode, env = process.env) {
20950
21092
  const requested = env.SHELL;
20951
21093
  let shell = requested;
20952
21094
  let warning = null;
20953
- if (!shell || !isAbsolute5(shell)) {
21095
+ if (!shell || !isAbsolute6(shell)) {
20954
21096
  warning = `syntaur: $SHELL ${requested ? `("${requested}") is not absolute` : "is unset"} \u2014 falling back to /bin/sh for shell-alias resolution`;
20955
21097
  shell = "/bin/sh";
20956
21098
  }
@@ -21015,13 +21157,17 @@ async function resolveAssignmentPlan(input4, terminal) {
21015
21157
  `Assignment ${input4.id} resolver returned a directory but detail could not be loaded`
21016
21158
  );
21017
21159
  }
21018
- const { cwd, fallbackWarning } = pickCwd({
21160
+ const picked = resolveWorkspaceCwd({
21019
21161
  worktreePath: detail.workspace.worktreePath,
21020
21162
  repository: detail.workspace.repository,
21021
21163
  branch: detail.workspace.branch,
21022
- assignmentSlug: resolved.assignmentSlug,
21023
- fallbackPath: process.cwd()
21164
+ assignmentSlug: resolved.assignmentSlug
21024
21165
  });
21166
+ if (picked.cwd === null) {
21167
+ throw new LaunchError("workspace-path-invalid", picked.invalidReason);
21168
+ }
21169
+ const cwd = picked.cwd;
21170
+ const fallbackWarning = picked.fallbackWarning;
21025
21171
  const agent = pickAgent(input4.config);
21026
21172
  const { argv, shellFallbackWarning } = buildFreshArgv(
21027
21173
  agent,
@@ -21058,15 +21204,23 @@ async function resolveSessionPlan(input4, terminal) {
21058
21204
  session.assignmentSlug
21059
21205
  );
21060
21206
  if (detail) {
21061
- const picked = pickCwd({
21207
+ const picked = resolveWorkspaceCwd({
21062
21208
  worktreePath: detail.workspace.worktreePath,
21063
21209
  repository: detail.workspace.repository,
21064
21210
  branch: detail.workspace.branch,
21065
- assignmentSlug: session.assignmentSlug,
21066
- fallbackPath: session.path
21211
+ assignmentSlug: session.assignmentSlug
21067
21212
  });
21068
- cwd = picked.cwd;
21069
- fallbackWarning = picked.fallbackWarning;
21213
+ if (picked.cwd !== null) {
21214
+ cwd = picked.cwd;
21215
+ fallbackWarning = picked.fallbackWarning;
21216
+ } else {
21217
+ fallbackWarning = formatFallbackCwdWarning({
21218
+ assignmentSlug: session.assignmentSlug,
21219
+ workspaceDir: session.path,
21220
+ worktreePath: detail.workspace.worktreePath,
21221
+ branch: detail.workspace.branch
21222
+ });
21223
+ }
21070
21224
  }
21071
21225
  }
21072
21226
  const agent = getAgents(input4.config).find((a) => a.id === session.agent);
@@ -21091,19 +21245,6 @@ async function resolveSessionPlan(input4, terminal) {
21091
21245
  shellFallbackWarning
21092
21246
  };
21093
21247
  }
21094
- function pickCwd(input4) {
21095
- if (input4.worktreePath && isAbsolute6(input4.worktreePath)) {
21096
- return { cwd: input4.worktreePath, fallbackWarning: null };
21097
- }
21098
- const workspaceDir = input4.repository && isAbsolute6(input4.repository) ? input4.repository : input4.fallbackPath;
21099
- const fallbackWarning = formatFallbackCwdWarning({
21100
- assignmentSlug: input4.assignmentSlug,
21101
- workspaceDir,
21102
- worktreePath: input4.worktreePath,
21103
- branch: input4.branch
21104
- });
21105
- return { cwd: workspaceDir, fallbackWarning };
21106
- }
21107
21248
 
21108
21249
  // src/launch/execute.ts
21109
21250
  init_launch();
@@ -21286,7 +21427,7 @@ function appleScriptString(value) {
21286
21427
  init_paths();
21287
21428
  init_fs();
21288
21429
  import { fileURLToPath as fileURLToPath5 } from "url";
21289
- import { dirname as dirname13, resolve as resolve44, join as join4 } from "path";
21430
+ import { dirname as dirname13, resolve as resolve44, join as join5 } from "path";
21290
21431
  import { realpathSync, readFileSync, mkdirSync } from "fs";
21291
21432
  var NPX_PATTERNS = [
21292
21433
  { kind: "npm", re: /\/_npx\/([^/]+)\/node_modules(?:\/|$)/ },
@@ -21330,7 +21471,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
21330
21471
  }
21331
21472
  let dir = dirname13(resolved);
21332
21473
  for (let depth = 0; depth < 8; depth++) {
21333
- const pkgJsonPath = join4(dir, "package.json");
21474
+ const pkgJsonPath = join5(dir, "package.json");
21334
21475
  let raw;
21335
21476
  try {
21336
21477
  raw = readFile53(pkgJsonPath);
@@ -21371,7 +21512,7 @@ function sanitizeHash(hash) {
21371
21512
  return hash.replace(/[^A-Za-z0-9_-]/g, "_") || "_";
21372
21513
  }
21373
21514
  function nudgeStampPath(hash) {
21374
- return join4(nudgeStampDir(), sanitizeHash(hash));
21515
+ return join5(nudgeStampDir(), sanitizeHash(hash));
21375
21516
  }
21376
21517
  async function hasNudgedHash(hash) {
21377
21518
  return fileExists(nudgeStampPath(hash));
@@ -21456,6 +21597,9 @@ function formatUrlCommandError(err2) {
21456
21597
  return `Invalid syntaur:// URL (${err2.code}): ${err2.message}`;
21457
21598
  }
21458
21599
  if (err2 instanceof LaunchError) {
21600
+ if (err2.code === "workspace-path-invalid") {
21601
+ return `Open in agent failed \u2014 ${err2.message}. Set a valid workspace.worktreePath or workspace.repository for this assignment.`;
21602
+ }
21459
21603
  return `Could not launch (${err2.code}): ${err2.message}`;
21460
21604
  }
21461
21605
  if (err2 instanceof TerminalNotFoundError) {
@@ -23213,7 +23357,7 @@ import { isAbsolute as isAbsolute10, resolve as resolve62 } from "path";
23213
23357
  // src/utils/doctor/index.ts
23214
23358
  import { fileURLToPath as fileURLToPath8 } from "url";
23215
23359
  import { readFile as readFile37 } from "fs/promises";
23216
- import { dirname as dirname17, join as join8 } from "path";
23360
+ import { dirname as dirname17, join as join9 } from "path";
23217
23361
 
23218
23362
  // src/utils/doctor/context.ts
23219
23363
  init_config2();
@@ -23261,7 +23405,7 @@ init_paths();
23261
23405
  import { resolve as resolve52, isAbsolute as isAbsolute8 } from "path";
23262
23406
  import { readFile as readFile31, stat as stat3 } from "fs/promises";
23263
23407
  import { fileURLToPath as fileURLToPath7 } from "url";
23264
- import { dirname as dirname15, join as join6 } from "path";
23408
+ import { dirname as dirname15, join as join7 } from "path";
23265
23409
  var CATEGORY = "env";
23266
23410
  var syntaurRootExists = {
23267
23411
  id: "env.syntaur-root-exists",
@@ -23594,7 +23738,7 @@ async function readLocalPkg() {
23594
23738
  const here = fileURLToPath7(import.meta.url);
23595
23739
  let dir = dirname15(here);
23596
23740
  for (let i = 0; i < 6; i++) {
23597
- const candidate = join6(dir, "package.json");
23741
+ const candidate = join7(dir, "package.json");
23598
23742
  try {
23599
23743
  const text = await readFile31(candidate, "utf-8");
23600
23744
  return JSON.parse(text);
@@ -24598,7 +24742,7 @@ function skipped(check, reason) {
24598
24742
  init_fs();
24599
24743
  import { resolve as resolve57, dirname as dirname16, basename as basename5 } from "path";
24600
24744
  import { readdir as readdir21, readFile as readFile33 } from "fs/promises";
24601
- import { homedir as homedir7 } from "os";
24745
+ import { homedir as homedir8 } from "os";
24602
24746
  var CATEGORY6 = "integrations";
24603
24747
  var claudePluginLinked = {
24604
24748
  id: "integrations.claude-plugin-linked",
@@ -24679,7 +24823,7 @@ var backupConfigured = {
24679
24823
  }
24680
24824
  };
24681
24825
  async function readKnownMarketplaces() {
24682
- const path = resolve57(homedir7(), ".claude", "plugins", "known_marketplaces.json");
24826
+ const path = resolve57(homedir8(), ".claude", "plugins", "known_marketplaces.json");
24683
24827
  if (!await fileExists(path)) return {};
24684
24828
  try {
24685
24829
  const raw = await readFile33(path, "utf-8");
@@ -24767,7 +24911,7 @@ var claudeMarketplaceRegistered = {
24767
24911
  title: this.title,
24768
24912
  status: "error",
24769
24913
  detail: issues.join("; "),
24770
- affected: [marketplaceManifest, resolve57(homedir7(), ".claude", "plugins", "known_marketplaces.json")],
24914
+ affected: [marketplaceManifest, resolve57(homedir8(), ".claude", "plugins", "known_marketplaces.json")],
24771
24915
  remediation: {
24772
24916
  kind: "manual",
24773
24917
  suggestion: "Re-run install-plugin to ensure both files are in sync.",
@@ -25196,7 +25340,7 @@ var terminalInstalled = {
25196
25340
  autoFixable: false
25197
25341
  };
25198
25342
  }
25199
- const detail = bundleId ? `${terminal} (bundle id ${bundleId}) not found via Spotlight` : `${cliName} not found on PATH`;
25343
+ const detail = bundleId ? `${terminal} (bundle id ${bundleId}) not found via Spotlight or in /Applications` : `${cliName} not found on PATH`;
25200
25344
  const suggestion = bundleId ? `Install ${terminal} or change \`terminal:\` in ~/.syntaur/config.md to a different choice` : `Install ${cliName} or change \`terminal:\` in ~/.syntaur/config.md to a different choice`;
25201
25345
  return {
25202
25346
  id: this.id,
@@ -25265,13 +25409,13 @@ var terminalChecks = [
25265
25409
 
25266
25410
  // src/utils/doctor/checks/skills.ts
25267
25411
  init_fs();
25268
- import { resolve as resolve60, join as join7 } from "path";
25412
+ import { resolve as resolve60, join as join8 } from "path";
25269
25413
  import { readdir as readdir22, readFile as readFile36, lstat as lstat4 } from "fs/promises";
25270
- import { homedir as homedir8 } from "os";
25414
+ import { homedir as homedir9 } from "os";
25271
25415
  var CATEGORY10 = "skills";
25272
25416
  var skillTargets = [
25273
- { agent: "claude", dir: resolve60(homedir8(), ".claude", "skills"), label: "~/.claude/skills" },
25274
- { agent: "codex", dir: resolve60(homedir8(), ".codex", "skills"), label: "~/.codex/skills" }
25417
+ { agent: "claude", dir: resolve60(homedir9(), ".claude", "skills"), label: "~/.claude/skills" },
25418
+ { agent: "codex", dir: resolve60(homedir9(), ".codex", "skills"), label: "~/.codex/skills" }
25275
25419
  ];
25276
25420
  var skillsDedupCheck = {
25277
25421
  id: "skills.dedup",
@@ -25293,14 +25437,14 @@ var skillsDedupCheck = {
25293
25437
  for (const entry of entries) {
25294
25438
  if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
25295
25439
  if (!KNOWN_SKILLS.includes(entry.name)) continue;
25296
- const skillMd = join7(dir, entry.name, "SKILL.md");
25440
+ const skillMd = join8(dir, entry.name, "SKILL.md");
25297
25441
  if (!await fileExists(skillMd)) continue;
25298
25442
  const content = await readFile36(skillMd, "utf-8").catch(() => "");
25299
25443
  const match = content.match(/^name:\s*(\S+)\s*$/m);
25300
25444
  if (!match || match[1] !== entry.name) continue;
25301
25445
  let isSymlink2 = false;
25302
25446
  try {
25303
- isSymlink2 = (await lstat4(join7(dir, entry.name))).isSymbolicLink();
25447
+ isSymlink2 = (await lstat4(join8(dir, entry.name))).isSymbolicLink();
25304
25448
  } catch {
25305
25449
  }
25306
25450
  present.push({ name: entry.name, isSymlink: isSymlink2 });
@@ -25312,7 +25456,7 @@ var skillsDedupCheck = {
25312
25456
  findings.push(
25313
25457
  `${label}: ${nonSymlink.length} syntaur skill(s) installed globally while the syntaur plugin is enabled (${agent}) \u2014 duplicate registrations`
25314
25458
  );
25315
- for (const p of nonSymlink) affected.push(join7(dir, p.name));
25459
+ for (const p of nonSymlink) affected.push(join8(dir, p.name));
25316
25460
  }
25317
25461
  }
25318
25462
  }
@@ -25667,7 +25811,7 @@ async function readVersion() {
25667
25811
  let dir = dirname17(here);
25668
25812
  for (let i = 0; i < 6; i++) {
25669
25813
  try {
25670
- const raw = await readFile37(join8(dir, "package.json"), "utf-8");
25814
+ const raw = await readFile37(join9(dir, "package.json"), "utf-8");
25671
25815
  const parsed = JSON.parse(raw);
25672
25816
  return typeof parsed.version === "string" ? parsed.version : null;
25673
25817
  } catch {
@@ -26184,7 +26328,7 @@ ${entry}`;
26184
26328
  // src/commands/capture.ts
26185
26329
  import { resolve as resolve67, relative as relative4, dirname as dirname18 } from "path";
26186
26330
  import { copyFile as copyFile3, mkdir as mkdir9, realpath as realpath2, rm as rm12, stat as stat8, writeFile as writeFile13 } from "fs/promises";
26187
- import { existsSync as existsSync3 } from "fs";
26331
+ import { existsSync as existsSync5 } from "fs";
26188
26332
 
26189
26333
  // src/utils/assignment-target.ts
26190
26334
  init_paths();
@@ -26381,7 +26525,7 @@ init_fs();
26381
26525
  import { spawn as spawn5 } from "child_process";
26382
26526
  import { mkdtemp as mkdtemp2, rm as rm8, stat as stat6 } from "fs/promises";
26383
26527
  import { tmpdir as tmpdir3 } from "os";
26384
- import { join as join9 } from "path";
26528
+ import { join as join10 } from "path";
26385
26529
  function argsFor(mode, pngPath) {
26386
26530
  switch (mode) {
26387
26531
  case "interactive":
@@ -26414,8 +26558,8 @@ async function captureScreenshot(mode) {
26414
26558
  "screencapture is only available on macOS. Use --file <path> to attach an existing image."
26415
26559
  );
26416
26560
  }
26417
- const tmpDir = await mkdtemp2(join9(tmpdir3(), "syntaur-screenshot-"));
26418
- const pngPath = join9(tmpDir, "shot.png");
26561
+ const tmpDir = await mkdtemp2(join10(tmpdir3(), "syntaur-screenshot-"));
26562
+ const pngPath = join10(tmpDir, "shot.png");
26419
26563
  const cleanup = async () => {
26420
26564
  await rm8(tmpDir, { recursive: true, force: true }).catch(() => {
26421
26565
  });
@@ -26455,7 +26599,7 @@ async function captureScreenshot(mode) {
26455
26599
  import { spawn as spawn6 } from "child_process";
26456
26600
  import { mkdtemp as mkdtemp3, readFile as readFile41, rm as rm9 } from "fs/promises";
26457
26601
  import { tmpdir as tmpdir4 } from "os";
26458
- import { join as join10 } from "path";
26602
+ import { join as join11 } from "path";
26459
26603
  var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
26460
26604
  function shellQuote2(s) {
26461
26605
  if (s.length === 0) return `''`;
@@ -26492,8 +26636,8 @@ function runAsciinema(args, stdio) {
26492
26636
  });
26493
26637
  }
26494
26638
  async function captureAsciinema(opts) {
26495
- const tmpDir = await mkdtemp3(join10(tmpdir4(), "syntaur-asciinema-"));
26496
- const castPath = join10(tmpDir, "session.cast");
26639
+ const tmpDir = await mkdtemp3(join11(tmpdir4(), "syntaur-asciinema-"));
26640
+ const castPath = join11(tmpDir, "session.cast");
26497
26641
  const cleanup = async () => {
26498
26642
  await rm9(tmpDir, { recursive: true, force: true }).catch(() => {
26499
26643
  });
@@ -26546,7 +26690,7 @@ init_paths();
26546
26690
  import { spawn as spawn7 } from "child_process";
26547
26691
  import { mkdir as mkdir8, mkdtemp as mkdtemp4, open as open3, readFile as readFile42, rm as rm10, stat as stat7, unlink as unlink9, writeFile as writeFile12 } from "fs/promises";
26548
26692
  import { tmpdir as tmpdir5 } from "os";
26549
- import { join as join11, resolve as resolve65 } from "path";
26693
+ import { join as join12, resolve as resolve65 } from "path";
26550
26694
  import { setTimeout as sleep } from "timers/promises";
26551
26695
  function sigintPollIntervalMs() {
26552
26696
  const raw = process.env.SYNTAUR_RECORDING_POLL_INTERVAL_MS;
@@ -26671,8 +26815,8 @@ async function startRecording(input4) {
26671
26815
  let acquiredPid = null;
26672
26816
  try {
26673
26817
  logHandle = await open3(log, "w");
26674
- tmpDir = await mkdtemp4(join11(tmpdir5(), "syntaur-recording-"));
26675
- const mp4Path = join11(tmpDir, "recording.mp4");
26818
+ tmpDir = await mkdtemp4(join12(tmpdir5(), "syntaur-recording-"));
26819
+ const mp4Path = join12(tmpDir, "recording.mp4");
26676
26820
  let child;
26677
26821
  try {
26678
26822
  child = spawn7("ffmpeg", ffmpegArgs(input4.device, input4.fps, mp4Path), {
@@ -26922,7 +27066,7 @@ function listArtifactsByAssignment(assignmentId) {
26922
27066
  import { spawn as spawn8 } from "child_process";
26923
27067
  import { mkdtemp as mkdtemp5, readFile as readFile43, rm as rm11 } from "fs/promises";
26924
27068
  import { tmpdir as tmpdir6 } from "os";
26925
- import { join as join12 } from "path";
27069
+ import { join as join13 } from "path";
26926
27070
  var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
26927
27071
  var NO_AUDIO_MARKERS = [
26928
27072
  "Stream map '0:a:0' matches no streams",
@@ -27012,8 +27156,8 @@ var elevenLabsScribe = {
27012
27156
  "ELEVENLABS_API_KEY is not set. Export it (e.g. `export ELEVENLABS_API_KEY=\u2026`) and re-run. A config-file slot will land later."
27013
27157
  );
27014
27158
  }
27015
- const tmp = await mkdtemp5(join12(tmpdir6(), "syntaur-transcribe-"));
27016
- const wav = join12(tmp, "audio.wav");
27159
+ const tmp = await mkdtemp5(join13(tmpdir6(), "syntaur-transcribe-"));
27160
+ const wav = join13(tmp, "audio.wav");
27017
27161
  try {
27018
27162
  await extractAudio(videoAbsPath, wav);
27019
27163
  return await callScribe(wav, apiKey, opts);
@@ -27395,7 +27539,7 @@ async function captureCommand(target, options = {}) {
27395
27539
  }
27396
27540
  if (options.transcribe && kind === "video" && absPath && id) {
27397
27541
  const sidecarPath2 = resolve67(destDir, `${id}.transcript.md`);
27398
- if (existsSync3(sidecarPath2)) {
27542
+ if (existsSync5(sidecarPath2)) {
27399
27543
  console.warn(
27400
27544
  `transcript: ${sidecarPath2} already exists, skipping (delete to re-transcribe)`
27401
27545
  );
@@ -28534,8 +28678,8 @@ function isoToCcusageDate(iso) {
28534
28678
  // src/usage/cwd-extractor.ts
28535
28679
  init_paths();
28536
28680
  import { open as open4, readdir as readdir24, stat as stat10 } from "fs/promises";
28537
- import { join as join13 } from "path";
28538
- import { homedir as homedir9 } from "os";
28681
+ import { join as join14 } from "path";
28682
+ import { homedir as homedir10 } from "os";
28539
28683
  var SCAN_LINE_CAP = 50;
28540
28684
  var TAIL_READ_BYTES = 8 * 1024;
28541
28685
  var TAIL_READ_BYTES_MAX = 64 * 1024;
@@ -28609,12 +28753,12 @@ async function* walkClaudeProjects(opts = {}) {
28609
28753
  const dirs = await listDirSafe(root);
28610
28754
  for (const dirent of dirs) {
28611
28755
  if (!dirent.isDirectory) continue;
28612
- const dirPath = join13(root, dirent.name);
28756
+ const dirPath = join14(root, dirent.name);
28613
28757
  const files = await listDirSafe(dirPath);
28614
28758
  let cachedCwd = null;
28615
28759
  for (const f of files) {
28616
28760
  if (!f.isFile || !f.name.endsWith(".jsonl")) continue;
28617
- const filePath = join13(dirPath, f.name);
28761
+ const filePath = join14(dirPath, f.name);
28618
28762
  if (opts.sinceMtimeMs !== void 0) {
28619
28763
  const mtime = await mtimeMs(filePath);
28620
28764
  if (mtime !== null && mtime < opts.sinceMtimeMs) continue;
@@ -28651,8 +28795,8 @@ function resolveCodexSessionsRoot(override) {
28651
28795
  const fromSessionsEnv = process.env.CODEX_SESSIONS_DIR;
28652
28796
  if (fromSessionsEnv && fromSessionsEnv.length > 0) return expandHome(fromSessionsEnv);
28653
28797
  const fromHomeEnv = process.env.CODEX_HOME;
28654
- if (fromHomeEnv && fromHomeEnv.length > 0) return join13(expandHome(fromHomeEnv), "sessions");
28655
- return join13(homedir9(), ".codex", "sessions");
28798
+ if (fromHomeEnv && fromHomeEnv.length > 0) return join14(expandHome(fromHomeEnv), "sessions");
28799
+ return join14(homedir10(), ".codex", "sessions");
28656
28800
  }
28657
28801
  async function listDirSafe(path) {
28658
28802
  try {
@@ -28672,7 +28816,7 @@ async function* walkJsonlRecursive(root) {
28672
28816
  const current = stack.pop();
28673
28817
  const entries = await listDirSafe(current);
28674
28818
  for (const e of entries) {
28675
- const full = join13(current, e.name);
28819
+ const full = join14(current, e.name);
28676
28820
  if (e.isDirectory) {
28677
28821
  stack.push(full);
28678
28822
  } else if (e.isFile && e.name.endsWith(".jsonl")) {
@@ -29890,19 +30034,19 @@ init_paths();
29890
30034
  init_fs();
29891
30035
  import { fileURLToPath as fileURLToPath10 } from "url";
29892
30036
  import { readFile as readFile52 } from "fs/promises";
29893
- import { dirname as dirname21, join as join15, resolve as resolve77 } from "path";
30037
+ import { dirname as dirname21, join as join16, resolve as resolve77 } from "path";
29894
30038
  import { spawn as spawn10 } from "child_process";
29895
30039
  import { createInterface as createInterface2 } from "readline/promises";
29896
30040
 
29897
30041
  // src/utils/version.ts
29898
30042
  import { fileURLToPath as fileURLToPath9 } from "url";
29899
30043
  import { readFile as readFile51 } from "fs/promises";
29900
- import { dirname as dirname20, join as join14 } from "path";
30044
+ import { dirname as dirname20, join as join15 } from "path";
29901
30045
  async function readPackageVersion(scriptUrl) {
29902
30046
  try {
29903
30047
  const scriptPath = fileURLToPath9(scriptUrl);
29904
30048
  const pkgRoot = dirname20(dirname20(scriptPath));
29905
- const raw = await readFile51(join14(pkgRoot, "package.json"), "utf-8");
30049
+ const raw = await readFile51(join15(pkgRoot, "package.json"), "utf-8");
29906
30050
  const parsed = JSON.parse(raw);
29907
30051
  return typeof parsed.version === "string" ? parsed.version : null;
29908
30052
  } catch {
@@ -29946,7 +30090,7 @@ async function resolveNpmBin() {
29946
30090
  const nodeDir = dirname21(process.execPath);
29947
30091
  const isWin = process.platform === "win32";
29948
30092
  const npmName = isWin ? "npm.cmd" : "npm";
29949
- const nearNode = join15(nodeDir, npmName);
30093
+ const nearNode = join16(nodeDir, npmName);
29950
30094
  if (await fileExists(nearNode)) {
29951
30095
  return { cmd: nearNode, shell: false };
29952
30096
  }
@@ -29989,7 +30133,7 @@ async function readGlobalVersion() {
29989
30133
  });
29990
30134
  if (!rootPath) return null;
29991
30135
  try {
29992
- const manifestPath = join15(rootPath, "syntaur", "package.json");
30136
+ const manifestPath = join16(rootPath, "syntaur", "package.json");
29993
30137
  if (!await fileExists(manifestPath)) return null;
29994
30138
  const raw = await readFile52(manifestPath, "utf-8");
29995
30139
  const parsed = JSON.parse(raw);