deepline 0.1.259 → 0.1.260

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
@@ -182,8 +182,8 @@ configureProxyFromEnv();
182
182
 
183
183
  // src/cli/index.ts
184
184
  var import_promises6 = require("fs/promises");
185
- var import_node_path20 = require("path");
186
- var import_node_os15 = require("os");
185
+ var import_node_path22 = require("path");
186
+ var import_node_os17 = require("os");
187
187
  var import_commander4 = require("commander");
188
188
 
189
189
  // src/config.ts
@@ -499,6 +499,21 @@ function resolveApiKeyForBaseUrl(baseUrl, explicitApiKey) {
499
499
  cliEnv[API_KEY_ENV]
500
500
  );
501
501
  }
502
+ function resolveProjectApiKeyForBaseUrl(baseUrl, startDir = process.cwd()) {
503
+ const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
504
+ return firstNonEmpty(
505
+ ...loadProjectEnvCandidates(startDir).map(({ env }) => {
506
+ const projectBaseUrl = normalizeBaseUrl(env[HOST_URL_ENV] ?? "");
507
+ return projectBaseUrl === normalizedBaseUrl ? env[API_KEY_ENV] : "";
508
+ })
509
+ );
510
+ }
511
+ function resolveGlobalApiKeyForBaseUrl(baseUrl) {
512
+ return firstNonEmpty(
513
+ process.env[API_KEY_ENV],
514
+ loadCliEnv(normalizeBaseUrl(baseUrl) || baseUrl)[API_KEY_ENV]
515
+ );
516
+ }
502
517
  function getResolvedProjectAuthSource(baseUrl, apiKey, startDir = process.cwd()) {
503
518
  const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
504
519
  const normalizedApiKey = apiKey.trim();
@@ -542,14 +557,79 @@ function mergeProjectEnvFile(filePath, values) {
542
557
  `, "utf-8");
543
558
  }
544
559
  function ensureProjectEnvIsIgnored(dir) {
560
+ ensureProjectPrivatePathsIgnored(dir, [
561
+ PROJECT_DEEPLINE_ENV_FILE,
562
+ ".deepline/"
563
+ ]);
564
+ }
565
+ function ensureProjectPrivatePathsIgnored(dir, entries) {
566
+ const gitDir = findNearestGitCommonDir(dir);
567
+ if (gitDir) {
568
+ const excludePath = (0, import_node_path.join)(gitDir, "info", "exclude");
569
+ const existing2 = (0, import_node_fs.existsSync)(excludePath) ? (0, import_node_fs.readFileSync)(excludePath, "utf-8") : "";
570
+ const existingEntries2 = new Set(
571
+ existing2.split(/\r?\n/).map((line) => line.trim()).filter(Boolean)
572
+ );
573
+ const missing2 = entries.filter(
574
+ (entry) => !existingEntries2.has(entry) && !existingEntries2.has(`/${entry}`)
575
+ );
576
+ if (missing2.length === 0) return;
577
+ (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(excludePath), { recursive: true });
578
+ const prefix2 = existing2 && !existing2.endsWith("\n") ? "\n" : "";
579
+ (0, import_node_fs.writeFileSync)(
580
+ excludePath,
581
+ `${existing2}${prefix2}${missing2.join("\n")}
582
+ `,
583
+ "utf-8"
584
+ );
585
+ return;
586
+ }
545
587
  const gitignorePath = (0, import_node_path.join)(dir, ".gitignore");
546
- const entry = PROJECT_DEEPLINE_ENV_FILE;
547
588
  const existing = (0, import_node_fs.existsSync)(gitignorePath) ? (0, import_node_fs.readFileSync)(gitignorePath, "utf-8") : "";
548
- const alreadyIgnored = existing.split(/\r?\n/).map((line) => line.trim()).some((line) => line === entry || line === `/${entry}`);
549
- if (alreadyIgnored) return;
589
+ const existingEntries = new Set(
590
+ existing.split(/\r?\n/).map((line) => line.trim()).filter(Boolean)
591
+ );
592
+ const missing = entries.filter(
593
+ (entry) => !existingEntries.has(entry) && !existingEntries.has(`/${entry}`)
594
+ );
595
+ if (missing.length === 0) return;
550
596
  const prefix = existing && !existing.endsWith("\n") ? "\n" : "";
551
- (0, import_node_fs.writeFileSync)(gitignorePath, `${existing}${prefix}${entry}
552
- `, "utf-8");
597
+ (0, import_node_fs.writeFileSync)(
598
+ gitignorePath,
599
+ `${existing}${prefix}${missing.join("\n")}
600
+ `,
601
+ "utf-8"
602
+ );
603
+ }
604
+ function findNearestGitCommonDir(startDir) {
605
+ let current = (0, import_node_path.resolve)(startDir);
606
+ while (true) {
607
+ const candidate = (0, import_node_path.join)(current, ".git");
608
+ if ((0, import_node_fs.existsSync)(candidate)) {
609
+ try {
610
+ const stat2 = (0, import_node_fs.statSync)(candidate);
611
+ if (stat2.isDirectory()) return candidate;
612
+ if (stat2.isFile()) {
613
+ const match = (0, import_node_fs.readFileSync)(candidate, "utf8").match(
614
+ /^gitdir:\s*(.+)$/m
615
+ );
616
+ const rawGitDir = match?.[1]?.trim();
617
+ if (rawGitDir) {
618
+ const gitDir = (0, import_node_path.isAbsolute)(rawGitDir) ? rawGitDir : (0, import_node_path.resolve)((0, import_node_path.dirname)(candidate), rawGitDir);
619
+ const commonDirPath = (0, import_node_path.join)(gitDir, "commondir");
620
+ if (!(0, import_node_fs.existsSync)(commonDirPath)) return gitDir;
621
+ const rawCommonDir = (0, import_node_fs.readFileSync)(commonDirPath, "utf8").trim();
622
+ return rawCommonDir ? (0, import_node_path.isAbsolute)(rawCommonDir) ? rawCommonDir : (0, import_node_path.resolve)(gitDir, rawCommonDir) : gitDir;
623
+ }
624
+ }
625
+ } catch {
626
+ return null;
627
+ }
628
+ }
629
+ const parent = (0, import_node_path.dirname)(current);
630
+ if (parent === current) return null;
631
+ current = parent;
632
+ }
553
633
  }
554
634
  function saveProjectDeeplineEnvValues(values, startDir = process.cwd()) {
555
635
  const target = resolveProjectPinTarget(startDir);
@@ -636,7 +716,7 @@ var SDK_RELEASE = {
636
716
  // Deepline-native radars. Older clients must update before discovering,
637
717
  // checking, or deploying an unlaunched monitor integration.
638
718
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
639
- version: "0.1.259",
719
+ version: "0.1.260",
640
720
  apiContract: "2026-07-native-monitor-launch-hard-cutover",
641
721
  supportPolicy: {
642
722
  minimumSupported: "0.1.53",
@@ -1321,7 +1401,7 @@ function decodeSseFrame(frame) {
1321
1401
  return parsed;
1322
1402
  }
1323
1403
  function sleep(ms) {
1324
- return new Promise((resolve14) => setTimeout(resolve14, ms));
1404
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
1325
1405
  }
1326
1406
  function withCoworkNetworkHint(message) {
1327
1407
  if (!isCoworkLikeSandbox2() || message.includes(COWORK_NETWORK_HINT)) {
@@ -2293,14 +2373,14 @@ async function* observeRunEvents(options) {
2293
2373
  try {
2294
2374
  for (; ; ) {
2295
2375
  if (queue.length === 0) {
2296
- const waitForItem = new Promise((resolve14) => {
2297
- wake = resolve14;
2376
+ const waitForItem = new Promise((resolve15) => {
2377
+ wake = resolve15;
2298
2378
  });
2299
2379
  if (!sawFirstSnapshot) {
2300
2380
  const timedOut = await Promise.race([
2301
2381
  waitForItem.then(() => false),
2302
2382
  new Promise(
2303
- (resolve14) => setTimeout(() => resolve14(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
2383
+ (resolve15) => setTimeout(() => resolve15(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
2304
2384
  )
2305
2385
  ]);
2306
2386
  if (timedOut && queue.length === 0) {
@@ -2518,7 +2598,7 @@ function parseEnvTestPolicyOverrides() {
2518
2598
  return normalizeTestPolicyOverrides(parsed, "DEEPLINE_TEST_POLICY_OVERRIDES");
2519
2599
  }
2520
2600
  function sleep2(ms) {
2521
- return new Promise((resolve14) => setTimeout(resolve14, ms));
2601
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
2522
2602
  }
2523
2603
  function isTransientCompileManifestError(error) {
2524
2604
  if (error instanceof DeeplineError && typeof error.statusCode === "number") {
@@ -5679,30 +5759,86 @@ var EXIT_SERVER = 5;
5679
5759
  function envFilePath(baseUrl) {
5680
5760
  return hostEnvFilePath(baseUrl);
5681
5761
  }
5682
- function pendingClaimTokenPath(baseUrl) {
5762
+ function normalizeAuthScope(value) {
5763
+ if (!value || value === "global") return "global";
5764
+ if (value === "folder") return "folder";
5765
+ throw new Error("--auth-scope must be one of: folder, global");
5766
+ }
5767
+ function parseAuthScope(args) {
5768
+ const index = args.indexOf("--auth-scope");
5769
+ return normalizeAuthScope(index >= 0 ? args[index + 1] : void 0);
5770
+ }
5771
+ function pendingClaimPath(baseUrl, scope) {
5772
+ if (scope === "folder") {
5773
+ const target = resolveProjectPinTarget();
5774
+ if (!target.ok) {
5775
+ throw new Error(
5776
+ `Cowork project folder is ambiguous. Candidate folders: ${target.candidates.join(
5777
+ ", "
5778
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder.`
5779
+ );
5780
+ }
5781
+ return (0, import_node_path5.join)(target.dir, ".deepline", "setup", "pending-auth.json");
5782
+ }
5783
+ return `${hostConfigDirPath(baseUrl)}/pending-auth.json`;
5784
+ }
5785
+ function legacyPendingClaimTokenPath(baseUrl) {
5683
5786
  return `${hostConfigDirPath(baseUrl)}/pending-claim-token`;
5684
5787
  }
5685
- function savePendingClaimToken(baseUrl, claimToken) {
5686
- const filePath = pendingClaimTokenPath(baseUrl);
5788
+ function savePendingClaim(baseUrl, claim) {
5789
+ const filePath = pendingClaimPath(baseUrl, claim.scope);
5687
5790
  const dir = (0, import_node_path5.dirname)(filePath);
5688
5791
  if (!(0, import_node_fs5.existsSync)(dir)) {
5689
5792
  (0, import_node_fs5.mkdirSync)(dir, { recursive: true });
5690
5793
  }
5691
- (0, import_node_fs5.writeFileSync)(filePath, `${claimToken}
5692
- `, "utf-8");
5794
+ (0, import_node_fs5.writeFileSync)(filePath, `${JSON.stringify(claim, null, 2)}
5795
+ `, {
5796
+ encoding: "utf-8",
5797
+ mode: 384
5798
+ });
5693
5799
  }
5694
- function readPendingClaimToken(baseUrl) {
5695
- const filePath = pendingClaimTokenPath(baseUrl);
5696
- if (!(0, import_node_fs5.existsSync)(filePath)) return "";
5800
+ function readPendingAuthClaim(baseUrl, scope) {
5801
+ let filePath;
5697
5802
  try {
5698
- return (0, import_node_fs5.readFileSync)(filePath, "utf-8").trim();
5803
+ filePath = pendingClaimPath(baseUrl, scope);
5699
5804
  } catch {
5700
- return "";
5805
+ return null;
5806
+ }
5807
+ if (!(0, import_node_fs5.existsSync)(filePath)) {
5808
+ if (scope !== "global") return null;
5809
+ try {
5810
+ const claimToken = (0, import_node_fs5.readFileSync)(
5811
+ legacyPendingClaimTokenPath(baseUrl),
5812
+ "utf-8"
5813
+ ).trim();
5814
+ return claimToken ? {
5815
+ claimToken,
5816
+ claimUrl: `${baseUrl}/api/v2/auth/cli/claim/${encodeURIComponent(claimToken)}`,
5817
+ scope
5818
+ } : null;
5819
+ } catch {
5820
+ return null;
5821
+ }
5822
+ }
5823
+ try {
5824
+ const parsed = JSON.parse((0, import_node_fs5.readFileSync)(filePath, "utf-8"));
5825
+ const claimToken = typeof parsed.claimToken === "string" ? parsed.claimToken.trim() : "";
5826
+ if (!claimToken) return null;
5827
+ return {
5828
+ claimToken,
5829
+ claimUrl: typeof parsed.claimUrl === "string" ? parsed.claimUrl.trim() : "",
5830
+ scope
5831
+ };
5832
+ } catch {
5833
+ return null;
5701
5834
  }
5702
5835
  }
5703
- function clearPendingClaimToken(baseUrl) {
5836
+ function clearPendingClaim(baseUrl, scope) {
5704
5837
  try {
5705
- (0, import_node_fs5.rmSync)(pendingClaimTokenPath(baseUrl), { force: true });
5838
+ (0, import_node_fs5.rmSync)(pendingClaimPath(baseUrl, scope), { force: true });
5839
+ if (scope === "global") {
5840
+ (0, import_node_fs5.rmSync)(legacyPendingClaimTokenPath(baseUrl), { force: true });
5841
+ }
5706
5842
  } catch {
5707
5843
  }
5708
5844
  }
@@ -5727,12 +5863,16 @@ function shouldWaitForRegisterClaim(mode) {
5727
5863
  if (mode === "no") return false;
5728
5864
  return detectAgentRuntime() !== "claude_cowork";
5729
5865
  }
5730
- function saveEnvValues(values, baseUrl) {
5866
+ function saveEnvValues(values, baseUrl, scope) {
5731
5867
  const filtered = {
5732
5868
  ...values[HOST_URL_ENV] ? { [HOST_URL_ENV]: values[HOST_URL_ENV] } : {},
5733
5869
  ...values[API_KEY_ENV] ? { [API_KEY_ENV]: values[API_KEY_ENV] } : {}
5734
5870
  };
5735
- saveHostEnvValues(baseUrl, filtered);
5871
+ if (scope === "folder") {
5872
+ saveProjectDeeplineEnvValues(filtered);
5873
+ } else {
5874
+ saveHostEnvValues(baseUrl, filtered);
5875
+ }
5736
5876
  }
5737
5877
  async function httpJson(method, url, apiKey, body) {
5738
5878
  const headers = {
@@ -5795,7 +5935,7 @@ function buildCandidateUrls2(url) {
5795
5935
  }
5796
5936
  }
5797
5937
  function sleep4(ms) {
5798
- return new Promise((resolve14) => setTimeout(resolve14, ms));
5938
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
5799
5939
  }
5800
5940
  function printDeeplineLogo() {
5801
5941
  if (process.stdout.isTTY && (process.stdout.columns ?? 80) >= 70) {
@@ -5856,8 +5996,13 @@ async function handleRegister(args) {
5856
5996
  let orgName = "";
5857
5997
  let agentName = "";
5858
5998
  let waitMode;
5999
+ let authScope;
5859
6000
  try {
5860
6001
  waitMode = parseRegisterWaitMode(args);
6002
+ authScope = parseAuthScope(args);
6003
+ if (authScope === "folder") {
6004
+ pendingClaimPath(baseUrl, authScope);
6005
+ }
5861
6006
  } catch (error) {
5862
6007
  console.error(error instanceof Error ? error.message : String(error));
5863
6008
  return 2;
@@ -5890,18 +6035,22 @@ async function handleRegister(args) {
5890
6035
  const claimUrl = String(data.claim_url || "");
5891
6036
  const claimToken = String(data.claim_token || "");
5892
6037
  if (claimToken) {
5893
- savePendingClaimToken(baseUrl, claimToken);
6038
+ savePendingClaim(baseUrl, { claimToken, claimUrl, scope: authScope });
5894
6039
  saveEnvValues(
5895
6040
  {
5896
6041
  [HOST_URL_ENV]: baseUrl
5897
6042
  },
5898
- baseUrl
6043
+ baseUrl,
6044
+ authScope
5899
6045
  );
5900
6046
  }
5901
6047
  if (claimUrl) {
5902
- console.log(" Opening approval page in your browser.");
5903
- console.log(` If it didn't open, cmd+click: ${claimUrl}`);
5904
- openInBrowser(claimUrl);
6048
+ const shouldOpen = waitMode !== "no" && detectAgentRuntime() !== "claude_cowork";
6049
+ console.log(
6050
+ shouldOpen ? " Opening approval page in your browser." : " Open this approval page in your browser:"
6051
+ );
6052
+ console.log(` ${claimUrl}`);
6053
+ if (shouldOpen) openInBrowser(claimUrl);
5905
6054
  }
5906
6055
  if (data.cli_message) {
5907
6056
  console.log(String(data.cli_message));
@@ -5919,7 +6068,7 @@ async function handleRegister(args) {
5919
6068
  { claim_token: claimToken, reveal: true }
5920
6069
  );
5921
6070
  if (s === 401 || s === 403) {
5922
- clearPendingClaimToken(baseUrl);
6071
+ clearPendingClaim(baseUrl, authScope);
5923
6072
  console.log("Status: unauthorized");
5924
6073
  return EXIT_AUTH;
5925
6074
  }
@@ -5940,15 +6089,16 @@ async function handleRegister(args) {
5940
6089
  [HOST_URL_ENV]: baseUrl,
5941
6090
  [API_KEY_ENV]: apiKey
5942
6091
  },
5943
- baseUrl
6092
+ baseUrl,
6093
+ authScope
5944
6094
  );
5945
- clearPendingClaimToken(baseUrl);
6095
+ clearPendingClaim(baseUrl, authScope);
5946
6096
  await printClaimSuccessBanner(baseUrl, apiKey, statusData);
5947
6097
  return EXIT_OK;
5948
6098
  }
5949
6099
  }
5950
6100
  if (state === "expired") {
5951
- clearPendingClaimToken(baseUrl);
6101
+ clearPendingClaim(baseUrl, authScope);
5952
6102
  console.log(
5953
6103
  "That approval link expired. Please run: deepline auth register"
5954
6104
  );
@@ -5960,6 +6110,13 @@ async function handleRegister(args) {
5960
6110
  async function handleWait(args) {
5961
6111
  const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
5962
6112
  let timeoutSeconds = 300;
6113
+ let authScope;
6114
+ try {
6115
+ authScope = parseAuthScope(args);
6116
+ } catch (error) {
6117
+ console.error(error instanceof Error ? error.message : String(error));
6118
+ return 2;
6119
+ }
5963
6120
  for (let i = 0; i < args.length; i++) {
5964
6121
  if (args[i] === "--timeout" && args[i + 1]) {
5965
6122
  const parsed = Number.parseInt(args[++i], 10);
@@ -5968,9 +6125,11 @@ async function handleWait(args) {
5968
6125
  }
5969
6126
  }
5970
6127
  }
5971
- const claimToken = readPendingClaimToken(baseUrl);
5972
- if (!claimToken) {
5973
- if (resolveApiKeyForBaseUrl(baseUrl)) {
6128
+ const pendingClaim = readPendingAuthClaim(baseUrl, authScope);
6129
+ const claimToken = pendingClaim?.claimToken ?? "";
6130
+ if (!pendingClaim) {
6131
+ const scopedApiKey = authScope === "folder" ? resolveProjectApiKeyForBaseUrl(baseUrl) : resolveGlobalApiKeyForBaseUrl(baseUrl);
6132
+ if (scopedApiKey) {
5974
6133
  console.log("Already connected.");
5975
6134
  return EXIT_OK;
5976
6135
  }
@@ -5986,7 +6145,7 @@ async function handleWait(args) {
5986
6145
  { claim_token: claimToken, reveal: true }
5987
6146
  );
5988
6147
  if (status === 401 || status === 403) {
5989
- clearPendingClaimToken(baseUrl);
6148
+ clearPendingClaim(baseUrl, authScope);
5990
6149
  console.error("Claim is invalid. Run: deepline auth register");
5991
6150
  return EXIT_AUTH;
5992
6151
  }
@@ -6007,15 +6166,16 @@ async function handleWait(args) {
6007
6166
  [HOST_URL_ENV]: baseUrl,
6008
6167
  [API_KEY_ENV]: apiKey
6009
6168
  },
6010
- baseUrl
6169
+ baseUrl,
6170
+ authScope
6011
6171
  );
6012
- clearPendingClaimToken(baseUrl);
6172
+ clearPendingClaim(baseUrl, authScope);
6013
6173
  await printClaimSuccessBanner(baseUrl, apiKey, data);
6014
6174
  return EXIT_OK;
6015
6175
  }
6016
6176
  }
6017
6177
  if (state === "expired") {
6018
- clearPendingClaimToken(baseUrl);
6178
+ clearPendingClaim(baseUrl, authScope);
6019
6179
  console.error("That approval link expired. Run: deepline auth register");
6020
6180
  return EXIT_AUTH;
6021
6181
  }
@@ -6030,6 +6190,16 @@ async function handleStatus(args) {
6030
6190
  const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
6031
6191
  const reveal = args.includes("--reveal");
6032
6192
  const jsonOutput = argsWantJson(args);
6193
+ const hasExplicitAuthScope = args.includes("--auth-scope");
6194
+ let authScope = null;
6195
+ if (hasExplicitAuthScope) {
6196
+ try {
6197
+ authScope = parseAuthScope(args);
6198
+ } catch (error) {
6199
+ console.error(error instanceof Error ? error.message : String(error));
6200
+ return 2;
6201
+ }
6202
+ }
6033
6203
  let hostStatusPayload = null;
6034
6204
  const hostLines = [];
6035
6205
  try {
@@ -6056,14 +6226,16 @@ async function handleStatus(args) {
6056
6226
  };
6057
6227
  hostLines.push(`Host: ${baseUrl} (unreachable)`);
6058
6228
  }
6059
- const apiKey = resolveApiKeyForBaseUrl(baseUrl);
6229
+ const apiKey = authScope === "folder" ? resolveProjectApiKeyForBaseUrl(baseUrl) : authScope === "global" ? resolveGlobalApiKeyForBaseUrl(baseUrl) : resolveApiKeyForBaseUrl(baseUrl);
6060
6230
  if (!apiKey) {
6061
- if (readPendingClaimToken(baseUrl)) {
6231
+ const pendingClaim = authScope ? readPendingAuthClaim(baseUrl, authScope) : readPendingAuthClaim(baseUrl, "folder") ?? readPendingAuthClaim(baseUrl, "global");
6232
+ if (pendingClaim) {
6062
6233
  printCommandEnvelope(
6063
6234
  {
6064
6235
  ...hostStatusPayload ?? { host: baseUrl },
6065
6236
  status: "pending",
6066
6237
  connected: false,
6238
+ authorization_url: pendingClaim.claimUrl || null,
6067
6239
  next: "deepline auth wait",
6068
6240
  render: {
6069
6241
  sections: [
@@ -6145,7 +6317,8 @@ async function handleStatus(args) {
6145
6317
  console.error(`Auth status error (status ${status}).`);
6146
6318
  return EXIT_SERVER;
6147
6319
  }
6148
- clearPendingClaimToken(baseUrl);
6320
+ const resolvedAuthScope = authScope ?? (getResolvedProjectAuthSource(baseUrl, apiKey) ? "folder" : "global");
6321
+ clearPendingClaim(baseUrl, resolvedAuthScope);
6149
6322
  const payload = {
6150
6323
  ...hostStatusPayload ?? { host: baseUrl },
6151
6324
  status: data.status || "(unknown)",
@@ -6170,9 +6343,15 @@ async function handleStatus(args) {
6170
6343
  [HOST_URL_ENV]: baseUrl,
6171
6344
  [API_KEY_ENV]: apiKeyResp
6172
6345
  },
6173
- baseUrl
6346
+ baseUrl,
6347
+ resolvedAuthScope
6174
6348
  );
6175
- savedApiKeyPath = envFilePath(baseUrl);
6349
+ if (resolvedAuthScope === "folder") {
6350
+ const target = resolveProjectPinTarget();
6351
+ savedApiKeyPath = target.ok ? (0, import_node_path5.join)(target.dir, ".env.deepline") : null;
6352
+ } else {
6353
+ savedApiKeyPath = envFilePath(baseUrl);
6354
+ }
6176
6355
  }
6177
6356
  }
6178
6357
  printCommandEnvelope(
@@ -6237,12 +6416,19 @@ Examples:
6237
6416
  deepline auth register
6238
6417
  deepline auth register --org-name Acme --agent-name local-cli
6239
6418
  deepline auth register --wait no
6419
+ deepline auth register --auth-scope folder --wait no
6240
6420
  `
6241
- ).option("--org-name <name>", "Workspace name to prefill").option("--agent-name <name>", "Agent name to register").option("--wait <mode>", "Wait mode: auto, yes, or no", "auto").option("--no-wait", "Alias for --wait no").action(async (options) => {
6421
+ ).option("--org-name <name>", "Workspace name to prefill").option("--agent-name <name>", "Agent name to register").option("--wait <mode>", "Wait mode: auto, yes, or no", "auto").option("--no-wait", "Alias for --wait no").option(
6422
+ "--auth-scope <scope>",
6423
+ "Credential scope: global or folder",
6424
+ "global"
6425
+ ).action(async (options) => {
6242
6426
  process.exitCode = await handleRegister([
6243
6427
  ...options.orgName ? ["--org-name", options.orgName] : [],
6244
6428
  ...options.agentName ? ["--agent-name", options.agentName] : [],
6245
- ...options.noWait || options.wait === false ? ["--wait", "no"] : ["--wait", String(options.wait ?? "auto")]
6429
+ ...options.noWait || options.wait === false ? ["--wait", "no"] : ["--wait", String(options.wait ?? "auto")],
6430
+ "--auth-scope",
6431
+ String(options.authScope ?? "global")
6246
6432
  ]);
6247
6433
  });
6248
6434
  auth.command("wait").description("Wait for a pending browser approval and save the API key.").addHelpText(
@@ -6255,10 +6441,17 @@ Notes:
6255
6441
  Examples:
6256
6442
  deepline auth wait
6257
6443
  deepline auth wait --timeout 120
6444
+ deepline auth wait --auth-scope folder --timeout 120
6258
6445
  `
6259
- ).option("--timeout <seconds>", "Maximum seconds to wait", "300").action(async (options) => {
6446
+ ).option("--timeout <seconds>", "Maximum seconds to wait", "300").option(
6447
+ "--auth-scope <scope>",
6448
+ "Credential scope: global or folder",
6449
+ "global"
6450
+ ).action(async (options) => {
6260
6451
  process.exitCode = await handleWait([
6261
- ...options.timeout ? ["--timeout", options.timeout] : []
6452
+ ...options.timeout ? ["--timeout", options.timeout] : [],
6453
+ "--auth-scope",
6454
+ String(options.authScope ?? "global")
6262
6455
  ]);
6263
6456
  });
6264
6457
  auth.command("status").description("Show the current CLI auth and workspace status.").addHelpText(
@@ -6271,14 +6464,16 @@ Notes:
6271
6464
 
6272
6465
  Examples:
6273
6466
  deepline auth status
6467
+ deepline auth status --auth-scope folder
6274
6468
  deepline auth status --json
6275
6469
  `
6276
6470
  ).option(
6277
6471
  "--reveal",
6278
6472
  "Persist the revealed API key back to the host auth file"
6279
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
6473
+ ).option("--auth-scope <scope>", "Read only folder or global auth").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
6280
6474
  process.exitCode = await handleStatus([
6281
6475
  ...options.reveal ? ["--reveal"] : [],
6476
+ ...options.authScope ? ["--auth-scope", String(options.authScope)] : [],
6282
6477
  ...options.json ? ["--json"] : []
6283
6478
  ]);
6284
6479
  });
@@ -11302,7 +11497,7 @@ function traceCliSync(phase, fields, run) {
11302
11497
  }
11303
11498
  }
11304
11499
  function sleep5(ms) {
11305
- return new Promise((resolve14) => setTimeout(resolve14, ms));
11500
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
11306
11501
  }
11307
11502
  function parseReferencedPlayTarget2(target) {
11308
11503
  const trimmed = target.trim();
@@ -19617,7 +19812,7 @@ function emitEnrichDebug(message) {
19617
19812
  );
19618
19813
  }
19619
19814
  function sleep6(ms) {
19620
- return new Promise((resolve14) => setTimeout(resolve14, ms));
19815
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
19621
19816
  }
19622
19817
  function enrichExportBackingRowsWaitMs() {
19623
19818
  const raw = process.env.DEEPLINE_ENRICH_EXPORT_BACKING_ROWS_WAIT_MS?.trim();
@@ -25044,7 +25239,7 @@ Examples:
25044
25239
  async function fetchOrganizations(http2, apiKey) {
25045
25240
  return http2.post("/api/v2/auth/cli/organizations", { api_key: apiKey });
25046
25241
  }
25047
- function normalizeAuthScope(value) {
25242
+ function normalizeAuthScope2(value) {
25048
25243
  if (!value) return "auto";
25049
25244
  if (value === "auto" || value === "folder" || value === "global") {
25050
25245
  return value;
@@ -25247,7 +25442,7 @@ async function handleOrgStatus(options) {
25247
25442
  );
25248
25443
  }
25249
25444
  async function handleOrgSwitch(selection, options) {
25250
- const authScope = normalizeAuthScope(options.authScope);
25445
+ const authScope = normalizeAuthScope2(options.authScope);
25251
25446
  const config = resolveConfig();
25252
25447
  const http2 = new HttpClient(config);
25253
25448
  const payload = await fetchOrganizations(http2, config.apiKey);
@@ -25624,17 +25819,17 @@ function hasClaudeBinary() {
25624
25819
  }
25625
25820
  }
25626
25821
  function launchClaude(prompt) {
25627
- return new Promise((resolve14) => {
25822
+ return new Promise((resolve15) => {
25628
25823
  const child = (0, import_node_child_process2.spawn)("claude", [prompt], {
25629
25824
  stdio: "inherit",
25630
25825
  shell: process.platform === "win32"
25631
25826
  });
25632
- child.on("error", () => resolve14(EXIT_SERVER3));
25633
- child.on("close", (status) => resolve14(status ?? EXIT_OK2));
25827
+ child.on("error", () => resolve15(EXIT_SERVER3));
25828
+ child.on("close", (status) => resolve15(status ?? EXIT_OK2));
25634
25829
  });
25635
25830
  }
25636
25831
  function readBody(req) {
25637
- return new Promise((resolve14, reject) => {
25832
+ return new Promise((resolve15, reject) => {
25638
25833
  let raw = "";
25639
25834
  req.setEncoding("utf8");
25640
25835
  req.on("data", (chunk) => {
@@ -25644,7 +25839,7 @@ function readBody(req) {
25644
25839
  req.destroy();
25645
25840
  }
25646
25841
  });
25647
- req.on("end", () => resolve14(raw));
25842
+ req.on("end", () => resolve15(raw));
25648
25843
  req.on("error", reject);
25649
25844
  });
25650
25845
  }
@@ -25699,7 +25894,7 @@ function startCallbackServer(input2) {
25699
25894
  writeJson(res, 400, { error: "Invalid request body." });
25700
25895
  });
25701
25896
  });
25702
- return new Promise((resolve14, reject) => {
25897
+ return new Promise((resolve15, reject) => {
25703
25898
  server.once("error", reject);
25704
25899
  server.listen(0, "127.0.0.1", () => {
25705
25900
  const address = server.address();
@@ -25707,7 +25902,7 @@ function startCallbackServer(input2) {
25707
25902
  reject(new Error("Failed to bind quickstart callback server."));
25708
25903
  return;
25709
25904
  }
25710
- resolve14({ server, port: address.port });
25905
+ resolve15({ server, port: address.port });
25711
25906
  });
25712
25907
  });
25713
25908
  }
@@ -25733,8 +25928,8 @@ async function handleQuickstart(options) {
25733
25928
  }
25734
25929
  const state = (0, import_node_crypto6.randomBytes)(32).toString("hex");
25735
25930
  let resolveSelection;
25736
- const selectionPromise = new Promise((resolve14) => {
25737
- resolveSelection = resolve14;
25931
+ const selectionPromise = new Promise((resolve15) => {
25932
+ resolveSelection = resolve15;
25738
25933
  });
25739
25934
  let callback;
25740
25935
  try {
@@ -25873,7 +26068,7 @@ async function readHiddenLine(prompt, streams = {}) {
25873
26068
  }
25874
26069
  let value = "";
25875
26070
  inputStream.resume();
25876
- return await new Promise((resolve14, reject) => {
26071
+ return await new Promise((resolve15, reject) => {
25877
26072
  let settled = false;
25878
26073
  const cleanup = () => {
25879
26074
  inputStream.off("data", onData);
@@ -25891,7 +26086,7 @@ async function readHiddenLine(prompt, streams = {}) {
25891
26086
  settled = true;
25892
26087
  outputStream.write("\n");
25893
26088
  cleanup();
25894
- resolve14(line);
26089
+ resolve15(line);
25895
26090
  };
25896
26091
  const fail = (error) => {
25897
26092
  if (settled) return;
@@ -28694,12 +28889,13 @@ Notes:
28694
28889
  // src/cli/commands/update.ts
28695
28890
  var import_node_child_process4 = require("child_process");
28696
28891
  var import_node_fs17 = require("fs");
28697
- var import_node_os13 = require("os");
28892
+ var import_node_os14 = require("os");
28698
28893
  var import_node_path19 = require("path");
28699
28894
 
28700
- // src/cli/skills-sync.ts
28895
+ // src/cli/commands/skills.ts
28701
28896
  var import_node_child_process3 = require("child_process");
28702
28897
  var import_node_fs16 = require("fs");
28898
+ var import_node_os13 = require("os");
28703
28899
  var import_node_path18 = require("path");
28704
28900
 
28705
28901
  // ../shared_libs/cli/install-commands.json
@@ -28716,7 +28912,7 @@ var install_commands_default = {
28716
28912
  npx_binary: "npx",
28717
28913
  npx_add_args_template: [
28718
28914
  "--yes",
28719
- "skills",
28915
+ "skills@latest",
28720
28916
  "add",
28721
28917
  "{skills_index_url}",
28722
28918
  "--agent",
@@ -28806,311 +29002,319 @@ function sdkNpmGlobalInstallCommand() {
28806
29002
  return INSTALL_COMMANDS.cli.sdk_npm_global;
28807
29003
  }
28808
29004
 
28809
- // src/cli/skills-sync.ts
28810
- var CHECK_TIMEOUT_MS2 = 3e3;
28811
- var attemptedSync = false;
28812
- function shouldSkipSkillsSync() {
28813
- if (detectAgentRuntime() === "claude_cowork") {
28814
- return true;
28815
- }
28816
- const value = process.env.DEEPLINE_SKIP_SKILLS_SYNC?.trim().toLowerCase();
28817
- return value === "1" || value === "true" || value === "yes" || value === "on";
29005
+ // src/cli/commands/skills.ts
29006
+ var RUNTIME_TO_SKILLS_AGENT = {
29007
+ antigravity: "antigravity",
29008
+ claude_code: "claude-code",
29009
+ claude_cowork: "claude-code",
29010
+ cline: "cline",
29011
+ codex: "codex",
29012
+ cursor: "cursor",
29013
+ gemini: "gemini-cli",
29014
+ windsurf: "windsurf"
29015
+ };
29016
+ var AGENT_MARKERS = [
29017
+ { agent: "codex", paths: [".codex"] },
29018
+ { agent: "claude-code", paths: [".claude"] },
29019
+ { agent: "cursor", paths: [".cursor"] },
29020
+ { agent: "gemini-cli", paths: [".gemini", ".gemini-cli"] },
29021
+ { agent: "antigravity", paths: [".antigravity"] }
29022
+ ];
29023
+ var WORKSPACE_SKILL_ROOTS_BY_AGENT = {
29024
+ antigravity: [".agents"],
29025
+ "claude-code": [".claude"],
29026
+ cline: [".agents"],
29027
+ codex: [".agents"],
29028
+ cursor: [".agents"],
29029
+ "gemini-cli": [".agents"],
29030
+ windsurf: [".windsurf"],
29031
+ "*": [".agents", ".claude", ".windsurf"]
29032
+ };
29033
+ function workspaceSkillRootsForAgents(agents) {
29034
+ return [
29035
+ ...new Set(
29036
+ agents.flatMap((agent) => WORKSPACE_SKILL_ROOTS_BY_AGENT[agent] ?? [])
29037
+ )
29038
+ ].sort((a, b) => a.localeCompare(b));
28818
29039
  }
28819
- function activePluginSkillsDir() {
28820
- const pluginMode = process.env.DEEPLINE_PLUGIN_MODE?.trim().toLowerCase();
28821
- if (pluginMode !== "true" && pluginMode !== "1" && pluginMode !== "yes" && pluginMode !== "on") {
28822
- return "";
28823
- }
28824
- const dir = process.env.DEEPLINE_PLUGIN_SKILLS_DIR?.trim() ?? "";
28825
- return dir && (0, import_node_fs16.existsSync)(dir) ? dir : "";
29040
+ function normalizeScope(value) {
29041
+ if (value === "global") return "global";
29042
+ if (value === "local") return "local";
29043
+ if (!value) return inferActiveSkillsScope();
29044
+ throw new Error("--scope must be one of: global, local");
28826
29045
  }
28827
- function readPluginSkillsVersion() {
28828
- const dir = activePluginSkillsDir();
28829
- if (!dir) return "";
28830
- try {
28831
- return (0, import_node_fs16.readFileSync)((0, import_node_path18.join)(dir, ".version"), "utf-8").trim();
28832
- } catch {
28833
- return "";
29046
+ function inferActiveSkillsScope(entrypoint = process.argv[1] ?? "") {
29047
+ return /[\\/]\.deepline[\\/]runtime[\\/]/.test(entrypoint) ? "local" : "global";
29048
+ }
29049
+ function resolveLocalRoot() {
29050
+ const target = resolveProjectPinTarget();
29051
+ if (!target.ok) {
29052
+ throw new Error(
29053
+ `Cowork project folder is ambiguous. Candidate folders: ${target.candidates.join(
29054
+ ", "
29055
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder.`
29056
+ );
28834
29057
  }
29058
+ return target.dir;
28835
29059
  }
28836
- function sdkSkillsVersionPath(baseUrl) {
28837
- return (0, import_node_path18.join)(sdkCliStateDirPath(baseUrl), "skills-version");
29060
+ function detectSkillsAgents(input2) {
29061
+ const runtime = input2.runtime ?? detectAgentRuntime();
29062
+ const knownAgent = RUNTIME_TO_SKILLS_AGENT[runtime];
29063
+ if (knownAgent) return [knownAgent];
29064
+ const roots = [
29065
+ ...input2.scope === "local" && input2.root ? [input2.root] : [],
29066
+ input2.homeDir ?? (0, import_node_os13.homedir)()
29067
+ ];
29068
+ const detected = AGENT_MARKERS.filter(
29069
+ (marker) => roots.some(
29070
+ (root) => marker.paths.some((path) => (0, import_node_fs16.existsSync)((0, import_node_path18.join)(root, path)))
29071
+ )
29072
+ ).map((marker) => marker.agent);
29073
+ return detected.length > 0 ? detected : ["*"];
28838
29074
  }
28839
- function legacySdkSkillsVersionPath(baseUrl) {
28840
- return (0, import_node_path18.join)((0, import_node_path18.dirname)(sdkCliStateDirPath(baseUrl)), "sdk-skills", ".version");
29075
+ async function fetchSkillCatalog(baseUrl) {
29076
+ const response = await fetch(skillsIndexUrl(baseUrl));
29077
+ if (!response.ok) {
29078
+ throw new Error(
29079
+ `Skill catalog request failed (status ${response.status}).`
29080
+ );
29081
+ }
29082
+ const index = await response.json();
29083
+ const skillNames = (index.skills ?? []).filter((skill) => skill.install_surface === "v1").map((skill) => skill.name).filter((name) => typeof name === "string" && Boolean(name)).sort((a, b) => a.localeCompare(b));
29084
+ if (skillNames.length === 0) {
29085
+ throw new Error(
29086
+ "The Deepline skill catalog contains no installable skills."
29087
+ );
29088
+ }
29089
+ return {
29090
+ skillNames,
29091
+ version: typeof index.version === "string" && index.version.trim() ? index.version.trim() : "unversioned"
29092
+ };
28841
29093
  }
28842
- function unavailableSkillsNoticePath(baseUrl) {
28843
- return (0, import_node_path18.join)(sdkCliStateDirPath(baseUrl), "skills-sync-unavailable-version");
29094
+ function skillsStatePathForScope(baseUrl, scope, root) {
29095
+ return scope === "local" && root ? (0, import_node_path18.join)(root, ".deepline", "setup", "skills.json") : (0, import_node_path18.join)(sdkCliStateDirPath(baseUrl), "skills-install.json");
28844
29096
  }
28845
- function readSdkSkillsLocalVersion(baseUrl) {
28846
- const pluginVersion = readPluginSkillsVersion();
28847
- if (pluginVersion) return pluginVersion;
28848
- const path = (0, import_node_fs16.existsSync)(sdkSkillsVersionPath(baseUrl)) ? sdkSkillsVersionPath(baseUrl) : legacySdkSkillsVersionPath(baseUrl);
28849
- if (!(0, import_node_fs16.existsSync)(path)) return "";
28850
- try {
28851
- return (0, import_node_fs16.readFileSync)(path, "utf-8").trim();
28852
- } catch {
28853
- return "";
28854
- }
29097
+ function buildSkillsPlan(input2) {
29098
+ const scopeArgs = input2.scope === "global" ? ["--global"] : [];
29099
+ const allManagedNames = [
29100
+ .../* @__PURE__ */ new Set([...input2.skillNames, ...LEGACY_SKILL_NAMES_TO_REMOVE])
29101
+ ].sort((a, b) => a.localeCompare(b));
29102
+ return {
29103
+ scope: input2.scope,
29104
+ root: input2.root,
29105
+ agents: input2.agents,
29106
+ skillNames: input2.skillNames,
29107
+ version: input2.version,
29108
+ remove: {
29109
+ command: "npx",
29110
+ args: [
29111
+ "--yes",
29112
+ "skills@latest",
29113
+ "remove",
29114
+ ...scopeArgs,
29115
+ "--agent",
29116
+ ...input2.agents,
29117
+ "-y",
29118
+ ...allManagedNames
29119
+ ]
29120
+ },
29121
+ install: {
29122
+ command: "npx",
29123
+ args: [
29124
+ "--yes",
29125
+ "skills@latest",
29126
+ "add",
29127
+ skillsIndexUrl(input2.baseUrl),
29128
+ "--agent",
29129
+ ...input2.agents,
29130
+ ...scopeArgs,
29131
+ "--yes",
29132
+ ...input2.skillNames.flatMap((name) => ["--skill", name]),
29133
+ "--full-depth"
29134
+ ]
29135
+ },
29136
+ statePath: skillsStatePathForScope(input2.baseUrl, input2.scope, input2.root)
29137
+ };
28855
29138
  }
28856
- function writeLocalSkillsVersion(baseUrl, version) {
28857
- const path = sdkSkillsVersionPath(baseUrl);
28858
- (0, import_node_fs16.mkdirSync)((0, import_node_path18.dirname)(path), { recursive: true });
28859
- (0, import_node_fs16.writeFileSync)(path, `${version}
28860
- `, "utf-8");
29139
+ function runProcess(command, args, cwd) {
29140
+ return new Promise((resolve15, reject) => {
29141
+ const child = (0, import_node_child_process3.spawn)(command, args, {
29142
+ cwd,
29143
+ env: process.env,
29144
+ stdio: ["ignore", "ignore", "pipe"],
29145
+ shell: process.platform === "win32"
29146
+ });
29147
+ let stderr = "";
29148
+ child.stderr.on("data", (chunk) => {
29149
+ stderr += chunk.toString("utf8");
29150
+ process.stderr.write(chunk);
29151
+ });
29152
+ child.once("error", reject);
29153
+ child.once("close", (code) => {
29154
+ if (code && stderr.trim()) {
29155
+ process.stderr.write(`skills@latest exited ${code}.
29156
+ `);
29157
+ }
29158
+ resolve15(code ?? 1);
29159
+ });
29160
+ });
28861
29161
  }
28862
- function writeUnavailableSkillsNotice(baseUrl, remoteVersion, skillNames) {
28863
- const path = unavailableSkillsNoticePath(baseUrl);
29162
+ async function runSkillsCommand(options) {
29163
+ let scope;
29164
+ let root;
28864
29165
  try {
28865
- if ((0, import_node_fs16.existsSync)(path) && (0, import_node_fs16.readFileSync)(path, "utf-8").trim() === remoteVersion) {
28866
- return;
28867
- }
28868
- (0, import_node_fs16.mkdirSync)((0, import_node_path18.dirname)(path), { recursive: true });
28869
- (0, import_node_fs16.writeFileSync)(path, `${remoteVersion}
28870
- `, "utf-8");
28871
- } catch {
29166
+ scope = normalizeScope(options.scope);
29167
+ root = scope === "local" ? resolveLocalRoot() : null;
29168
+ } catch (error) {
29169
+ printCommandEnvelope(
29170
+ {
29171
+ ok: false,
29172
+ status: "failed",
29173
+ code: "INVALID_SKILLS_SCOPE",
29174
+ exitCode: 2,
29175
+ message: error instanceof Error ? error.message : String(error)
29176
+ },
29177
+ { json: options.json }
29178
+ );
29179
+ return 2;
28872
29180
  }
28873
- const manualCommand = `npx ${buildSkillsInstallArgs(baseUrl, skillNames).join(" ")}`;
28874
- writeSdkSkillsStatusLine(
28875
- `Deepline agent skills are out of date, but neither \`bunx\` nor \`npx\` is available. Install Node.js/npm or Bun, then run:
28876
- ${manualCommand}`
28877
- );
28878
- }
28879
- function clearUnavailableSkillsNotice(baseUrl) {
29181
+ const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
29182
+ let catalog;
28880
29183
  try {
28881
- (0, import_node_fs16.unlinkSync)(unavailableSkillsNoticePath(baseUrl));
28882
- } catch {
29184
+ catalog = options.dryRun ? { skillNames: [...DEFAULT_SDK_SKILL_NAMES], version: "latest" } : await fetchSkillCatalog(baseUrl);
29185
+ } catch (error) {
29186
+ printCommandEnvelope(
29187
+ {
29188
+ ok: false,
29189
+ status: "failed",
29190
+ code: "SKILLS_CATALOG_UNAVAILABLE",
29191
+ exitCode: 5,
29192
+ message: error instanceof Error ? error.message : String(error),
29193
+ next: "Retry: deepline skills"
29194
+ },
29195
+ { json: options.json }
29196
+ );
29197
+ return 5;
28883
29198
  }
28884
- }
28885
- function sortedUniqueSkillNames(names) {
28886
- return [...new Set(names.map((name) => name.trim()).filter(Boolean))].sort(
28887
- (a, b) => a.localeCompare(b)
29199
+ const agents = options.agent ? [options.agent] : detectSkillsAgents({ scope, root });
29200
+ const plan = buildSkillsPlan({ baseUrl, scope, root, agents, ...catalog });
29201
+ if (options.dryRun) {
29202
+ printCommandEnvelope(
29203
+ { ok: true, status: "planned", dryRun: true, ...plan },
29204
+ { json: options.json }
29205
+ );
29206
+ return 0;
29207
+ }
29208
+ if (scope === "local" && root) {
29209
+ const managedNames = [
29210
+ .../* @__PURE__ */ new Set([...plan.skillNames, ...LEGACY_SKILL_NAMES_TO_REMOVE])
29211
+ ];
29212
+ const agentRoots = workspaceSkillRootsForAgents(plan.agents);
29213
+ ensureProjectPrivatePathsIgnored(root, [
29214
+ ".deepline/",
29215
+ ...agentRoots.flatMap(
29216
+ (agentRoot) => managedNames.map((name) => `${agentRoot}/skills/${name}/`)
29217
+ )
29218
+ ]);
29219
+ }
29220
+ process.stderr.write(
29221
+ `Replacing Deepline skills for ${agents.join(", ")} (${scope})...
29222
+ `
28888
29223
  );
28889
- }
28890
- async function fetchV1SkillNames(baseUrl) {
28891
- const controller = new AbortController();
28892
- const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS2);
28893
29224
  try {
28894
- const response = await fetch(
28895
- new URL("/.well-known/skills/index.json", baseUrl),
28896
- { signal: controller.signal }
29225
+ const removeCode = await runProcess(
29226
+ plan.remove.command,
29227
+ plan.remove.args,
29228
+ root ?? void 0
28897
29229
  );
28898
- if (!response.ok) return [];
28899
- const data = await response.json().catch(() => null);
28900
- const names = (data?.skills ?? []).filter((skill) => skill.install_surface === "v1").map((skill) => skill.name).filter(
28901
- (name) => typeof name === "string" && name.length > 0
29230
+ if (removeCode !== 0) {
29231
+ throw new Error("Could not remove the existing Deepline skills.");
29232
+ }
29233
+ const installCode = await runProcess(
29234
+ plan.install.command,
29235
+ plan.install.args,
29236
+ root ?? void 0
28902
29237
  );
28903
- return sortedUniqueSkillNames(names);
28904
- } catch {
28905
- return [];
28906
- } finally {
28907
- clearTimeout(timeout);
28908
- }
28909
- }
28910
- function buildSdkSkillNames(v1SkillNames) {
28911
- return sortedUniqueSkillNames(v1SkillNames);
28912
- }
28913
- async function fetchSkillsUpdate(baseUrl, localVersion) {
28914
- const controller = new AbortController();
28915
- const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS2);
28916
- try {
28917
- const response = await fetch(new URL("/api/v2/cli/update-check", baseUrl), {
28918
- method: "POST",
28919
- headers: { "Content-Type": "application/json" },
28920
- body: JSON.stringify({
28921
- skills: {
28922
- version: localVersion
28923
- }
28924
- }),
28925
- signal: controller.signal
28926
- });
28927
- if (!response.ok) return null;
28928
- const data = await response.json().catch(() => null);
28929
- const skills = data?.skills;
28930
- if (!skills) return null;
28931
- return {
28932
- needsUpdate: skills.needs_update === true,
28933
- remoteVersion: typeof skills.remote?.version === "string" ? skills.remote.version.trim() : ""
28934
- };
28935
- } catch {
28936
- return null;
28937
- } finally {
28938
- clearTimeout(timeout);
28939
- }
28940
- }
28941
- function buildSkillsInstallArgs(baseUrl, skillNames = DEFAULT_SDK_SKILL_NAMES) {
28942
- return buildSkillsAddArgs(baseUrl, sortedUniqueSkillNames(skillNames));
28943
- }
28944
- function buildBunxSkillsInstallArgs(baseUrl, skillNames) {
28945
- return buildSkillsAddArgs(baseUrl, sortedUniqueSkillNames(skillNames), {
28946
- firstArg: "--bun"
28947
- });
28948
- }
28949
- function hasCommand(command) {
28950
- const result = (0, import_node_child_process3.spawnSync)(command, ["--version"], {
28951
- stdio: "ignore",
28952
- shell: process.platform === "win32"
28953
- });
28954
- return result.status === 0;
28955
- }
28956
- function shellQuote5(arg) {
28957
- return `'${arg.replace(/'/g, `'\\''`)}'`;
28958
- }
28959
- function resolveSkillsInstallCommands(baseUrl, skillNames = DEFAULT_SDK_SKILL_NAMES) {
28960
- const commands = [];
28961
- if (hasCommand("bunx")) {
28962
- const bunxArgs = buildBunxSkillsInstallArgs(baseUrl, skillNames);
28963
- commands.push({
28964
- command: "bunx",
28965
- args: bunxArgs,
28966
- manualCommand: `bunx ${bunxArgs.map(shellQuote5).join(" ")}`
28967
- });
28968
- }
28969
- if (hasCommand("npx")) {
28970
- const npxArgs = buildSkillsInstallArgs(baseUrl, skillNames);
28971
- commands.push({
28972
- command: "npx",
28973
- args: npxArgs,
28974
- manualCommand: `npx ${npxArgs.map(shellQuote5).join(" ")}`
28975
- });
28976
- }
28977
- return commands;
28978
- }
28979
- function runOneSkillsInstall(install) {
28980
- return new Promise((resolve14) => {
28981
- const child = (0, import_node_child_process3.spawn)(install.command, install.args, {
28982
- stdio: ["ignore", "ignore", "pipe"],
28983
- env: process.env
28984
- });
28985
- let stderr = "";
28986
- child.stderr.on("data", (chunk) => {
28987
- stderr += chunk.toString("utf-8");
28988
- });
28989
- child.on("error", (error) => {
28990
- resolve14({
28991
- ok: false,
28992
- detail: `failed to start ${install.command}: ${error.message}`,
28993
- manualCommand: install.manualCommand
28994
- });
28995
- });
28996
- child.on("close", (code) => {
28997
- if (code === 0) {
28998
- resolve14({ ok: true, detail: "", manualCommand: install.manualCommand });
28999
- return;
29000
- }
29001
- const detail = stderr.trim();
29002
- resolve14({
29238
+ if (installCode !== 0) {
29239
+ throw new Error("Could not install the current Deepline skills.");
29240
+ }
29241
+ } catch (error) {
29242
+ printCommandEnvelope(
29243
+ {
29003
29244
  ok: false,
29004
- detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
29005
- manualCommand: install.manualCommand
29006
- });
29007
- });
29008
- });
29009
- }
29010
- async function runSkillsInstall(installs) {
29011
- const failures = [];
29012
- for (const install of installs) {
29013
- const result = await runOneSkillsInstall(install);
29014
- if (result.ok) return true;
29015
- failures.push(result);
29245
+ status: "failed",
29246
+ code: "SKILLS_INSTALL_FAILED",
29247
+ exitCode: 5,
29248
+ scope,
29249
+ agents,
29250
+ message: error instanceof Error ? error.message : String(error),
29251
+ next: `deepline skills --scope ${scope} --json`
29252
+ },
29253
+ { json: options.json }
29254
+ );
29255
+ return 5;
29016
29256
  }
29017
- const details = failures.map((failure) => failure.detail).filter(Boolean).join("\n");
29018
- const manualCommand = failures.at(-1)?.manualCommand;
29019
- process.stderr.write(
29020
- `SDK skills sync failed${details ? `:
29021
- ${details}` : ""}
29022
- ` + (manualCommand ? `Run manually: ${manualCommand}
29023
- ` : "")
29257
+ (0, import_node_fs16.mkdirSync)((0, import_node_path18.dirname)(plan.statePath), { recursive: true });
29258
+ (0, import_node_fs16.writeFileSync)(
29259
+ plan.statePath,
29260
+ `${JSON.stringify(
29261
+ {
29262
+ schemaVersion: 1,
29263
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
29264
+ cliVersion: SDK_VERSION,
29265
+ scope,
29266
+ agents,
29267
+ skillsVersion: plan.version,
29268
+ skillNames: plan.skillNames
29269
+ },
29270
+ null,
29271
+ 2
29272
+ )}
29273
+ `,
29274
+ "utf8"
29024
29275
  );
29025
- return false;
29026
- }
29027
- function runLegacySkillsCleanup() {
29028
- const candidates = hasCommand("bunx") ? [
29276
+ printCommandEnvelope(
29029
29277
  {
29030
- command: "bunx",
29031
- args: [
29032
- "--bun",
29033
- "skills",
29034
- "remove",
29035
- "--global",
29036
- "-y",
29037
- ...LEGACY_SKILL_NAMES_TO_REMOVE
29038
- ]
29278
+ ok: true,
29279
+ status: "complete",
29280
+ scope,
29281
+ agents,
29282
+ skillsVersion: plan.version,
29283
+ skillCount: plan.skillNames.length,
29284
+ statePath: plan.statePath,
29285
+ render: {
29286
+ sections: [
29287
+ {
29288
+ title: "skills",
29289
+ lines: [
29290
+ `Scope: ${scope}`,
29291
+ `Agents: ${agents.join(", ")}`,
29292
+ `Installed: ${plan.skillNames.length}`
29293
+ ]
29294
+ }
29295
+ ]
29296
+ }
29039
29297
  },
29040
- {
29041
- command: "npx",
29042
- args: [
29043
- "--yes",
29044
- "skills",
29045
- "remove",
29046
- "--global",
29047
- "-y",
29048
- ...LEGACY_SKILL_NAMES_TO_REMOVE
29049
- ]
29050
- }
29051
- ] : [
29052
- {
29053
- command: "npx",
29054
- args: [
29055
- "--yes",
29056
- "skills",
29057
- "remove",
29058
- "--global",
29059
- "-y",
29060
- ...LEGACY_SKILL_NAMES_TO_REMOVE
29061
- ]
29062
- }
29063
- ];
29064
- for (const candidate of candidates) {
29065
- const result = (0, import_node_child_process3.spawnSync)(candidate.command, candidate.args, {
29066
- stdio: "ignore",
29067
- env: process.env,
29068
- shell: process.platform === "win32"
29069
- });
29070
- if (result.status === 0) return;
29071
- }
29072
- }
29073
- function writeSdkSkillsStatusLine(line) {
29074
- const progress = getActiveCliProgress();
29075
- if (progress) {
29076
- progress.writeLine(line);
29077
- return;
29078
- }
29079
- process.stderr.write(`${line}
29080
- `);
29081
- }
29082
- async function syncSdkSkillsIfNeeded(baseUrl, options = {}) {
29083
- if (attemptedSync || shouldSkipSkillsSync()) return;
29084
- attemptedSync = true;
29085
- const usingPluginSkills = Boolean(activePluginSkillsDir());
29086
- if (usingPluginSkills) {
29087
- return;
29088
- }
29089
- const localVersion = readSdkSkillsLocalVersion(baseUrl);
29090
- const update = options.update === void 0 ? await fetchSkillsUpdate(baseUrl, localVersion) : options.update ? {
29091
- needsUpdate: options.update.needs_update,
29092
- remoteVersion: options.update.remote.version
29093
- } : null;
29094
- if (!update?.needsUpdate || !update.remoteVersion) {
29095
- return;
29096
- }
29097
- const remoteSkillNames = await fetchV1SkillNames(baseUrl);
29098
- const skillNames = buildSdkSkillNames(
29099
- remoteSkillNames.length > 0 ? remoteSkillNames : DEFAULT_SDK_SKILL_NAMES
29298
+ { json: options.json }
29100
29299
  );
29101
- if (skillNames.length === 0) return;
29102
- const installs = resolveSkillsInstallCommands(baseUrl, skillNames);
29103
- if (installs.length === 0) {
29104
- writeUnavailableSkillsNotice(baseUrl, update.remoteVersion, skillNames);
29105
- return;
29106
- }
29107
- writeSdkSkillsStatusLine("Deepline skills changed; syncing agent skills...");
29108
- const installed = await runSkillsInstall(installs);
29109
- if (!installed) return;
29110
- runLegacySkillsCleanup();
29111
- writeLocalSkillsVersion(baseUrl, update.remoteVersion);
29112
- clearUnavailableSkillsNotice(baseUrl);
29113
- writeSdkSkillsStatusLine("Deepline agent skills are up to date.");
29300
+ return 0;
29301
+ }
29302
+ function registerSkillsCommand(program) {
29303
+ program.command("skills").description("Replace Deepline agent skills with the current release.").option("--scope <scope>", "Install scope: global or local").option("--agent <agent>", "Override the detected agent target").option("--dry-run", "Print the install plan without changing files").option("--json", "Emit one JSON result envelope").addHelpText(
29304
+ "after",
29305
+ `
29306
+ Notes:
29307
+ This command removes and reinstalls only Deepline-managed skill names using
29308
+ skills@latest. Local scope writes into the resolved persistent project.
29309
+
29310
+ Examples:
29311
+ deepline skills --json
29312
+ deepline skills --scope local --json
29313
+ deepline skills --agent codex --dry-run --json
29314
+ `
29315
+ ).action(async (options) => {
29316
+ process.exitCode = await runSkillsCommand(options);
29317
+ });
29114
29318
  }
29115
29319
 
29116
29320
  // src/cli/commands/update.ts
@@ -29142,14 +29346,14 @@ function posixShellQuote(value) {
29142
29346
  function windowsCmdQuote(value) {
29143
29347
  return `"${value.replace(/"/g, '""')}"`;
29144
29348
  }
29145
- function shellQuote6(value) {
29349
+ function shellQuote5(value) {
29146
29350
  if (process.platform === "win32") {
29147
29351
  return /^[A-Za-z0-9_./:@%+=,-]+$/.test(value) ? value : windowsCmdQuote(value);
29148
29352
  }
29149
29353
  return posixShellQuote(value);
29150
29354
  }
29151
29355
  function buildSourceUpdateCommand(sourceRoot) {
29152
- const quotedRoot = shellQuote6(sourceRoot);
29356
+ const quotedRoot = shellQuote5(sourceRoot);
29153
29357
  const cdCommand = process.platform === "win32" ? `cd /d ${quotedRoot}` : `cd ${quotedRoot}`;
29154
29358
  return `${cdCommand} && git fetch origin main --tags && git merge --ff-only origin/main`;
29155
29359
  }
@@ -29161,7 +29365,7 @@ function buildSidecarProjectConfigCommand(versionDir, nodeBin) {
29161
29365
  "fs.mkdirSync(dir,{recursive:true});",
29162
29366
  `fs.writeFileSync(path.join(dir,'package.json'),${JSON.stringify(NPM_SDK_SIDECAR_PACKAGE_JSON)});`
29163
29367
  ].join("");
29164
- return `${shellQuote6(nodeBin)} -e ${shellQuote6(script)} ${shellQuote6(versionDir)}`;
29368
+ return `${shellQuote5(nodeBin)} -e ${shellQuote5(script)} ${shellQuote5(versionDir)}`;
29165
29369
  }
29166
29370
  function sidecarStateDir(input2) {
29167
29371
  const scope = input2.env.DEEPLINE_CONFIG_SCOPE?.trim();
@@ -29224,7 +29428,7 @@ function resolvePythonSidecarUpdatePlan(options) {
29224
29428
  const npmCommand = "npm";
29225
29429
  const registryUrl = sidecarRegistryUrl(hostUrl);
29226
29430
  const versionDir = (0, import_node_path19.join)(stateDir, "versions", "<version>");
29227
- const manualCommand = `${buildSidecarProjectConfigCommand(versionDir, nodeBin)} && ${npmCommand} install --prefix ${shellQuote6(versionDir)} --registry ${shellQuote6(registryUrl)} ${NPM_SDK_INSTALL_COMMON_FLAGS.map(shellQuote6).join(" ")} ${shellQuote6(packageSpec)}`;
29431
+ const manualCommand = `${buildSidecarProjectConfigCommand(versionDir, nodeBin)} && ${npmCommand} install --prefix ${shellQuote5(versionDir)} --registry ${shellQuote5(registryUrl)} ${NPM_SDK_INSTALL_COMMON_FLAGS.map(shellQuote5).join(" ")} ${shellQuote5(packageSpec)}`;
29228
29432
  return {
29229
29433
  kind: "python-sidecar",
29230
29434
  stateDir,
@@ -29272,7 +29476,7 @@ function inferNpmGlobalPrefixFromEntrypoint(entrypoint) {
29272
29476
  }
29273
29477
  function resolveUpdatePlan(options = {}) {
29274
29478
  const env = options.env ?? process.env;
29275
- const homeDir2 = options.homeDir ?? (0, import_node_os13.homedir)();
29479
+ const homeDir2 = options.homeDir ?? (0, import_node_os14.homedir)();
29276
29480
  const entrypoint = options.entrypoint ?? (process.argv[1] ? (0, import_node_path19.resolve)(process.argv[1]) : "");
29277
29481
  const sourceRoot = entrypoint ? findRepoBackedSdkRoot((0, import_node_path19.dirname)(entrypoint)) : null;
29278
29482
  if (sourceRoot) {
@@ -29308,7 +29512,7 @@ function resolveUpdatePlan(options = {}) {
29308
29512
  fallbackRegistryUrl: publicNpmFallbackRegistryUrl(
29309
29513
  env.DEEPLINE_HOST_URL?.trim() || autoDetectBaseUrl()
29310
29514
  ),
29311
- manualCommand: `${command} ${args.map(shellQuote6).join(" ")}`
29515
+ manualCommand: `${command} ${args.map(shellQuote5).join(" ")}`
29312
29516
  };
29313
29517
  }
29314
29518
  var AUTO_UPDATE_FAILURE_FILE = ".auto-update-failure.json";
@@ -29318,7 +29522,7 @@ function autoUpdateFailurePath(plan) {
29318
29522
  return (0, import_node_path19.join)(plan.stateDir, AUTO_UPDATE_FAILURE_FILE);
29319
29523
  }
29320
29524
  return (0, import_node_path19.join)(
29321
- (0, import_node_os13.homedir)(),
29525
+ (0, import_node_os14.homedir)(),
29322
29526
  ".local",
29323
29527
  "deepline",
29324
29528
  "sdk-cli",
@@ -29513,9 +29717,9 @@ function writeSidecarLauncher(input2) {
29513
29717
  input2.path,
29514
29718
  [
29515
29719
  "#!/usr/bin/env sh",
29516
- `export DEEPLINE_HOST_URL=${shellQuote6(input2.hostUrl)}`,
29517
- `export DEEPLINE_CONFIG_SCOPE=${shellQuote6(input2.scope)}`,
29518
- `exec ${shellQuote6(input2.nodeBin)} ${shellQuote6(input2.entryPath)} "$@"`,
29720
+ `export DEEPLINE_HOST_URL=${shellQuote5(input2.hostUrl)}`,
29721
+ `export DEEPLINE_CONFIG_SCOPE=${shellQuote5(input2.scope)}`,
29722
+ `exec ${shellQuote5(input2.nodeBin)} ${shellQuote5(input2.entryPath)} "$@"`,
29519
29723
  ""
29520
29724
  ].join("\n"),
29521
29725
  { encoding: "utf8", mode: 493 }
@@ -29645,7 +29849,20 @@ async function runUpdateCommand(options, dependencies = {}) {
29645
29849
  const detectBaseUrl = dependencies.detectBaseUrl ?? autoDetectBaseUrl;
29646
29850
  const resolvePlan = dependencies.resolvePlan ?? resolveUpdatePlan;
29647
29851
  const runPlan = dependencies.runPlan ?? runUpdatePlan;
29648
- const syncSkills = dependencies.syncSkillsIfNeeded ?? syncSdkSkillsIfNeeded;
29852
+ const syncSkills = dependencies.syncSkillsIfNeeded ?? (async () => {
29853
+ const originalWrite = process.stdout.write.bind(process.stdout);
29854
+ process.stdout.write = (() => true);
29855
+ try {
29856
+ const exitCode = await runSkillsCommand({ json: true });
29857
+ if (exitCode !== 0) {
29858
+ throw new Error(
29859
+ `Deepline skills update failed with exit ${exitCode}.`
29860
+ );
29861
+ }
29862
+ } finally {
29863
+ process.stdout.write = originalWrite;
29864
+ }
29865
+ });
29649
29866
  const stderr = dependencies.stderr ?? process.stderr;
29650
29867
  const plan = resolvePlan();
29651
29868
  const render = {
@@ -29713,27 +29930,644 @@ Examples:
29713
29930
  });
29714
29931
  }
29715
29932
 
29716
- // ../shared_libs/cli/command-compatibility.json
29717
- var command_compatibility_default = {
29718
- enrich: {
29719
- family: "python",
29720
- label: "a legacy Python CLI enrichment command",
29721
- sdk_alternative: "Use `deepline plays ...` for durable workflows or `deepline tools execute ...` for one tool call."
29722
- },
29723
- session: {
29724
- family: "python",
29725
- label: "a legacy Python CLI session/playground command",
29726
- sdk_alternative: "Use `deepline sessions send ...` or `deepline sessions render ...` for transcript workflows."
29727
- },
29728
- workflows: {
29729
- family: "python",
29730
- label: "a legacy Python CLI workflow command",
29731
- sdk_alternative: "Use `deepline plays ...` in the SDK CLI."
29732
- },
29733
- events: {
29734
- family: "python",
29735
- label: "a legacy Python CLI event command"
29736
- },
29933
+ // src/cli/commands/setup.ts
29934
+ var import_node_child_process5 = require("child_process");
29935
+ var import_node_fs18 = require("fs");
29936
+ var import_node_os15 = require("os");
29937
+ var import_node_path20 = require("path");
29938
+ function normalizeScope2(value) {
29939
+ if (!value || value === "global") return "global";
29940
+ if (value === "local") return "local";
29941
+ throw new Error("--scope must be one of: global, local");
29942
+ }
29943
+ function resolveScopeRoot(scope) {
29944
+ if (scope === "global") return null;
29945
+ const target = resolveProjectPinTarget();
29946
+ if (!target.ok) {
29947
+ throw new Error(
29948
+ `Cowork project folder is ambiguous. Candidate folders: ${target.candidates.join(
29949
+ ", "
29950
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder.`
29951
+ );
29952
+ }
29953
+ return target.dir;
29954
+ }
29955
+ function authScopeForSetup(scope) {
29956
+ return scope === "local" ? "folder" : "global";
29957
+ }
29958
+ function setupStatePath(baseUrl, scope, root) {
29959
+ return scope === "local" && root ? (0, import_node_path20.join)(root, ".deepline", "setup", "state.json") : (0, import_node_path20.join)(sdkCliStateDirPath(baseUrl), "setup.json");
29960
+ }
29961
+ async function captureStdout2(run) {
29962
+ let stdout = "";
29963
+ const originalWrite = process.stdout.write.bind(process.stdout);
29964
+ process.stdout.write = ((chunk) => {
29965
+ stdout += typeof chunk === "string" ? chunk : String(chunk);
29966
+ return true;
29967
+ });
29968
+ try {
29969
+ return { exitCode: await run(), stdout, error: null };
29970
+ } catch (error) {
29971
+ return { exitCode: 4, stdout, error };
29972
+ } finally {
29973
+ process.stdout.write = originalWrite;
29974
+ }
29975
+ }
29976
+ function parseCapturedJson(stdout) {
29977
+ try {
29978
+ return JSON.parse(stdout.trim());
29979
+ } catch {
29980
+ return null;
29981
+ }
29982
+ }
29983
+ function asRecord2(value) {
29984
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
29985
+ }
29986
+ function printCapturedAuthorizationUrl(stdout) {
29987
+ const urls = stdout.match(/https:\/\/[^\s]+/g) ?? [];
29988
+ for (const url of new Set(
29989
+ urls.map((value) => value.replace(/[).,]+$/, ""))
29990
+ )) {
29991
+ process.stderr.write(`Authorize Deepline: ${url}
29992
+ `);
29993
+ }
29994
+ }
29995
+ function safeRead(path) {
29996
+ try {
29997
+ return (0, import_node_fs18.readFileSync)(path, "utf8");
29998
+ } catch {
29999
+ return "";
30000
+ }
30001
+ }
30002
+ function isNpmManagedDeeplinePath(path) {
30003
+ try {
30004
+ return (0, import_node_fs18.realpathSync)(path).includes(`${(0, import_node_path20.join)("node_modules", "deepline")}`);
30005
+ } catch {
30006
+ return false;
30007
+ }
30008
+ }
30009
+ function removeKnownLegacyPaths(baseUrl) {
30010
+ const home = (0, import_node_os15.homedir)();
30011
+ const hostDir = (0, import_node_path20.join)(home, ".local", "deepline", baseUrlSlug(baseUrl));
30012
+ const installerCommandPath = safeRead(
30013
+ (0, import_node_path20.join)(hostDir, "sdk", ".command-path")
30014
+ ).trim();
30015
+ const candidates = [
30016
+ (0, import_node_path20.join)(home, ".local", "bin", "deepline-real"),
30017
+ (0, import_node_path20.join)(hostDir, "bin", "deepline"),
30018
+ (0, import_node_path20.join)(hostDir, "bin", "deepline-real"),
30019
+ (0, import_node_path20.join)(hostDir, "cli", ".install-method"),
30020
+ (0, import_node_path20.join)(hostDir, "cli", ".version"),
30021
+ (0, import_node_path20.join)(hostDir, "sdk", ".install-method"),
30022
+ (0, import_node_path20.join)(hostDir, "sdk", ".command-path"),
30023
+ ...installerCommandPath ? [
30024
+ installerCommandPath,
30025
+ (0, import_node_path20.join)((0, import_node_path20.dirname)(installerCommandPath), "deepline-sdk")
30026
+ ] : []
30027
+ ];
30028
+ const removed = [];
30029
+ for (const path of candidates) {
30030
+ if (!(0, import_node_fs18.existsSync)(path)) continue;
30031
+ if (path === installerCommandPath && isNpmManagedDeeplinePath(path)) {
30032
+ continue;
30033
+ }
30034
+ (0, import_node_fs18.rmSync)(path, { force: true });
30035
+ removed.push(path);
30036
+ }
30037
+ return removed;
30038
+ }
30039
+ function resolvePathCommands(command) {
30040
+ const lookup = (0, import_node_child_process5.spawnSync)(
30041
+ process.platform === "win32" ? "where" : "which",
30042
+ process.platform === "win32" ? [command] : ["-a", command],
30043
+ { encoding: "utf8", shell: process.platform === "win32" }
30044
+ );
30045
+ return [
30046
+ ...new Set(
30047
+ String(lookup.stdout ?? "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((path) => (0, import_node_path20.resolve)(path))
30048
+ )
30049
+ ];
30050
+ }
30051
+ function resolvePathCommand(command) {
30052
+ return resolvePathCommands(command)[0] ?? null;
30053
+ }
30054
+ function resolvePersistentGlobalCommand() {
30055
+ const prefix = (0, import_node_child_process5.spawnSync)("npm", ["prefix", "-g"], { encoding: "utf8" });
30056
+ if (prefix.status !== 0) return null;
30057
+ const root = String(prefix.stdout ?? "").trim();
30058
+ if (!root) return null;
30059
+ const candidates = process.platform === "win32" ? [(0, import_node_path20.join)(root, "deepline.cmd"), (0, import_node_path20.join)(root, "deepline")] : [(0, import_node_path20.join)(root, "bin", "deepline")];
30060
+ return candidates.find((candidate) => (0, import_node_fs18.existsSync)(candidate)) ?? null;
30061
+ }
30062
+ function inspectGlobalCliAvailability(input2) {
30063
+ const persistentPath = input2 && "persistentGlobalCli" in input2 ? input2.persistentGlobalCli ?? null : resolvePersistentGlobalCommand();
30064
+ const pathClis = input2?.pathClis ?? resolvePathCommands("deepline");
30065
+ const path = persistentPath ? pathClis.find(
30066
+ (candidate) => pathsResolveToSameFile(candidate, persistentPath)
30067
+ ) ?? null : null;
30068
+ return { ok: Boolean(path), persistentPath, path };
30069
+ }
30070
+ function pathsResolveToSameFile(left, right) {
30071
+ try {
30072
+ return (0, import_node_fs18.realpathSync)(left) === (0, import_node_fs18.realpathSync)(right);
30073
+ } catch {
30074
+ return (0, import_node_path20.resolve)(left) === (0, import_node_path20.resolve)(right);
30075
+ }
30076
+ }
30077
+ function isKnownDeeplineCommand(path) {
30078
+ const entrypoint = process.argv[1] ? (0, import_node_path20.resolve)(process.argv[1]) : "";
30079
+ let resolvedPath = path;
30080
+ try {
30081
+ resolvedPath = (0, import_node_fs18.realpathSync)(path);
30082
+ } catch {
30083
+ }
30084
+ if (entrypoint && resolvedPath === entrypoint) return true;
30085
+ if (resolvedPath.includes(`${(0, import_node_path20.join)("node_modules", "deepline")}`)) return true;
30086
+ const content = safeRead(path);
30087
+ return content.includes("node_modules/deepline") || content.includes("node_modules\\deepline") || content.includes("DEEPLINE_CONFIG_SCOPE") || content.includes("deepline-real");
30088
+ }
30089
+ function inspectPathConflict() {
30090
+ const commandPath = resolvePathCommand("deepline");
30091
+ if (!commandPath || isKnownDeeplineCommand(commandPath)) return null;
30092
+ try {
30093
+ if ((0, import_node_fs18.lstatSync)(commandPath).isSymbolicLink()) {
30094
+ const target = (0, import_node_fs18.realpathSync)(commandPath);
30095
+ if (target.includes(`${(0, import_node_path20.join)("node_modules", "deepline")}`)) return null;
30096
+ }
30097
+ } catch {
30098
+ }
30099
+ return commandPath;
30100
+ }
30101
+ function writeSetupState(input2) {
30102
+ const path = setupStatePath(input2.baseUrl, input2.scope, input2.root);
30103
+ (0, import_node_fs18.mkdirSync)((0, import_node_path20.dirname)(path), { recursive: true });
30104
+ (0, import_node_fs18.writeFileSync)(
30105
+ path,
30106
+ `${JSON.stringify(
30107
+ {
30108
+ schemaVersion: 1,
30109
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
30110
+ cliVersion: SDK_VERSION,
30111
+ host: input2.baseUrl,
30112
+ scope: input2.scope,
30113
+ root: input2.root,
30114
+ status: input2.status
30115
+ },
30116
+ null,
30117
+ 2
30118
+ )}
30119
+ `,
30120
+ "utf8"
30121
+ );
30122
+ return path;
30123
+ }
30124
+ function rollbackCommand(scope, root) {
30125
+ const prefix = scope === "local" && root ? ` --prefix ${JSON.stringify((0, import_node_path20.join)(root, ".deepline", "runtime"))}` : "";
30126
+ return `npm install -g${prefix} --no-audit --no-fund --include=optional --allow-scripts=esbuild deepline@${SDK_VERSION}`;
30127
+ }
30128
+ function setupQuickstartCommand(baseUrl) {
30129
+ return baseUrl === "https://code.deepline.com" ? "deepline quickstart" : `DEEPLINE_HOST_URL=${JSON.stringify(baseUrl)} deepline quickstart`;
30130
+ }
30131
+ function setupResumeCommand(baseUrl, scope) {
30132
+ const hostPrefix = baseUrl === "https://code.deepline.com" ? "" : `DEEPLINE_HOST_URL=${JSON.stringify(baseUrl)} `;
30133
+ return `${hostPrefix}deepline setup --scope ${scope} --json`;
30134
+ }
30135
+ async function readAuthStatus(authScope) {
30136
+ try {
30137
+ const captured = await captureStdout2(
30138
+ () => handleStatus([
30139
+ "--json",
30140
+ ...authScope ? ["--auth-scope", authScope] : []
30141
+ ])
30142
+ );
30143
+ return {
30144
+ exitCode: captured.exitCode,
30145
+ payload: parseCapturedJson(captured.stdout),
30146
+ error: captured.error
30147
+ };
30148
+ } catch (error) {
30149
+ return { exitCode: 4, payload: null, error };
30150
+ }
30151
+ }
30152
+ function reportSetupAuthStatusFailure(input2) {
30153
+ if (!input2.auth.error) return null;
30154
+ printCommandEnvelope(
30155
+ {
30156
+ ok: false,
30157
+ status: "failed",
30158
+ code: "AUTH_STATUS_FAILED",
30159
+ exitCode: 4,
30160
+ scope: input2.scope,
30161
+ message: "Deepline could not verify authorization with the configured host.",
30162
+ next: `Check network access, then rerun: deepline setup --scope ${input2.scope} --json`,
30163
+ authScope: input2.authScope
30164
+ },
30165
+ { json: input2.json }
30166
+ );
30167
+ return 4;
30168
+ }
30169
+ async function runDoctorCommand(options) {
30170
+ let scope;
30171
+ let root;
30172
+ try {
30173
+ scope = normalizeScope2(options.scope);
30174
+ root = resolveScopeRoot(scope);
30175
+ } catch (error) {
30176
+ printCommandEnvelope(
30177
+ {
30178
+ ok: false,
30179
+ status: "failed",
30180
+ code: "INVALID_SETUP_SCOPE",
30181
+ exitCode: 2,
30182
+ message: error instanceof Error ? error.message : String(error)
30183
+ },
30184
+ { json: options.json }
30185
+ );
30186
+ return 2;
30187
+ }
30188
+ const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
30189
+ const skillsStatePath = skillsStatePathForScope(baseUrl, scope, root);
30190
+ const skillsState = parseCapturedJson(safeRead(skillsStatePath));
30191
+ const authScope = authScopeForSetup(scope);
30192
+ const apiKey = scope === "local" ? resolveProjectApiKeyForBaseUrl(baseUrl) : resolveGlobalApiKeyForBaseUrl(baseUrl);
30193
+ const projectAuth = scope === "local" && apiKey ? getResolvedProjectAuthSource(baseUrl, apiKey) : null;
30194
+ const authStatus = await readAuthStatus(authScope);
30195
+ const connected = authStatus.payload?.connected === true;
30196
+ const authScopeOk = scope === "local" ? Boolean(projectAuth) : Boolean(apiKey && !projectAuth);
30197
+ const skillsOk = skillsState?.scope === scope && Array.isArray(skillsState.agents) && skillsState.agents.length > 0;
30198
+ const runningCliPath = process.argv[1] ? (0, import_node_path20.resolve)(process.argv[1]) : null;
30199
+ const globalCli = scope === "global" ? inspectGlobalCliAvailability() : null;
30200
+ const pathGlobalCli = globalCli?.path ?? null;
30201
+ const cliPath = scope === "global" ? pathGlobalCli : runningCliPath;
30202
+ const cliScopeOk = scope === "global" ? Boolean(pathGlobalCli) : Boolean(
30203
+ root && runningCliPath?.includes((0, import_node_path20.join)(root, ".deepline", "runtime"))
30204
+ );
30205
+ const checks = {
30206
+ cli: {
30207
+ ok: Boolean(cliPath) && cliScopeOk,
30208
+ version: SDK_VERSION,
30209
+ path: cliPath,
30210
+ scope
30211
+ },
30212
+ skills: {
30213
+ ok: skillsOk,
30214
+ statePath: skillsStatePath,
30215
+ version: skillsState?.skillsVersion ?? null,
30216
+ agents: skillsState?.agents ?? []
30217
+ },
30218
+ auth: {
30219
+ ok: connected && authScopeOk,
30220
+ scope: projectAuth ? "local" : apiKey ? "global" : null,
30221
+ status: authStatus.payload?.status ?? "not_connected"
30222
+ },
30223
+ api: {
30224
+ ok: connected && authStatus.exitCode === 0,
30225
+ host: baseUrl,
30226
+ workspace: authStatus.payload?.workspace ?? null,
30227
+ providerSpend: false
30228
+ }
30229
+ };
30230
+ const ok = Object.values(checks).every((check) => check.ok);
30231
+ const quickstart = setupQuickstartCommand(baseUrl);
30232
+ printCommandEnvelope(
30233
+ {
30234
+ ok,
30235
+ status: ok ? "complete" : "failed",
30236
+ code: ok ? "DOCTOR_OK" : "DOCTOR_FAILED",
30237
+ exitCode: ok ? 0 : 7,
30238
+ checks,
30239
+ next: ok ? quickstart : "Review failed checks, then rerun: deepline doctor --json",
30240
+ render: {
30241
+ sections: [
30242
+ {
30243
+ title: "doctor",
30244
+ lines: Object.entries(checks).map(
30245
+ ([name, check]) => `${check.ok ? "pass" : "fail"} ${name}`
30246
+ )
30247
+ }
30248
+ ]
30249
+ }
30250
+ },
30251
+ { json: options.json }
30252
+ );
30253
+ return ok ? 0 : 7;
30254
+ }
30255
+ function pendingResult(input2) {
30256
+ const statePath = writeSetupState({
30257
+ baseUrl: input2.baseUrl,
30258
+ scope: input2.scope,
30259
+ root: input2.root,
30260
+ status: "authorization_pending"
30261
+ });
30262
+ if (input2.authorizationUrl) {
30263
+ process.stderr.write(`Authorize Deepline: ${input2.authorizationUrl}
30264
+ `);
30265
+ }
30266
+ printCommandEnvelope(
30267
+ {
30268
+ ok: true,
30269
+ status: "authorization_pending",
30270
+ complete: false,
30271
+ scope: input2.scope,
30272
+ authorizationUrl: input2.authorizationUrl || null,
30273
+ statePath,
30274
+ next: `Approve the link, then run: ${setupResumeCommand(input2.baseUrl, input2.scope)}`,
30275
+ render: {
30276
+ sections: [
30277
+ {
30278
+ title: "setup",
30279
+ lines: [
30280
+ "Authorization is waiting for browser approval.",
30281
+ ...input2.authorizationUrl ? [input2.authorizationUrl] : []
30282
+ ]
30283
+ }
30284
+ ]
30285
+ }
30286
+ },
30287
+ { json: input2.json }
30288
+ );
30289
+ return 0;
30290
+ }
30291
+ async function runSetupCommand(options) {
30292
+ let scope;
30293
+ let root;
30294
+ try {
30295
+ scope = normalizeScope2(options.scope);
30296
+ root = resolveScopeRoot(scope);
30297
+ } catch (error) {
30298
+ printCommandEnvelope(
30299
+ {
30300
+ ok: false,
30301
+ status: "failed",
30302
+ code: "INVALID_SETUP_SCOPE",
30303
+ exitCode: 2,
30304
+ message: error instanceof Error ? error.message : String(error)
30305
+ },
30306
+ { json: options.json }
30307
+ );
30308
+ return 2;
30309
+ }
30310
+ const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
30311
+ if (scope === "global") {
30312
+ const globalCli = inspectGlobalCliAvailability();
30313
+ if (!globalCli.ok) {
30314
+ const installedButUnreachable = Boolean(globalCli.persistentPath);
30315
+ printCommandEnvelope(
30316
+ {
30317
+ ok: false,
30318
+ status: "failed",
30319
+ code: installedButUnreachable ? "GLOBAL_CLI_NOT_ON_PATH" : "GLOBAL_CLI_NOT_INSTALLED",
30320
+ exitCode: 7,
30321
+ scope,
30322
+ persistentPath: globalCli.persistentPath,
30323
+ message: installedButUnreachable ? `The global Deepline executable is not reachable on PATH: ${globalCli.persistentPath}` : "No persistent global Deepline executable was found.",
30324
+ next: "Use the automatic project-local fallback in https://code.deepline.com/SKILL.md"
30325
+ },
30326
+ { json: options.json }
30327
+ );
30328
+ return 7;
30329
+ }
30330
+ }
30331
+ const conflict = inspectPathConflict();
30332
+ if (conflict) {
30333
+ printCommandEnvelope(
30334
+ {
30335
+ ok: false,
30336
+ status: "failed",
30337
+ code: "PATH_CONFLICT",
30338
+ exitCode: 7,
30339
+ path: conflict,
30340
+ message: `An unknown executable named deepline is first on PATH: ${conflict}`,
30341
+ next: "Move or rename that executable, then rerun: deepline setup --json"
30342
+ },
30343
+ { json: options.json }
30344
+ );
30345
+ return 7;
30346
+ }
30347
+ const removedLegacyPaths = removeKnownLegacyPaths(baseUrl);
30348
+ if (removedLegacyPaths.length > 0) {
30349
+ process.stderr.write(
30350
+ `Removed ${removedLegacyPaths.length} known legacy Deepline path(s).
30351
+ `
30352
+ );
30353
+ }
30354
+ process.stderr.write(`Installing Deepline skills (${scope})...
30355
+ `);
30356
+ const skills = await captureStdout2(
30357
+ () => runSkillsCommand({ scope, json: true })
30358
+ );
30359
+ const skillsPayload = parseCapturedJson(skills.stdout);
30360
+ if (skills.exitCode !== 0) {
30361
+ printCommandEnvelope(
30362
+ {
30363
+ ok: false,
30364
+ status: "failed",
30365
+ code: "SKILLS_INSTALL_FAILED",
30366
+ exitCode: skills.exitCode,
30367
+ scope,
30368
+ detail: skillsPayload,
30369
+ next: `deepline skills --scope ${scope} --json`
30370
+ },
30371
+ { json: options.json }
30372
+ );
30373
+ return skills.exitCode;
30374
+ }
30375
+ writeSetupState({ baseUrl, scope, root, status: "skills_installed" });
30376
+ const authScope = authScopeForSetup(scope);
30377
+ let auth = await readAuthStatus(authScope);
30378
+ const initialAuthFailure = reportSetupAuthStatusFailure({
30379
+ auth,
30380
+ authScope,
30381
+ scope,
30382
+ json: options.json
30383
+ });
30384
+ if (initialAuthFailure !== null) return initialAuthFailure;
30385
+ if (auth.payload?.connected !== true) {
30386
+ const pending = readPendingAuthClaim(baseUrl, authScope);
30387
+ if (pending) {
30388
+ process.stderr.write("Checking pending Deepline authorization...\n");
30389
+ const waited = await captureStdout2(
30390
+ () => handleWait(["--timeout", "1", "--auth-scope", authScope])
30391
+ );
30392
+ printCapturedAuthorizationUrl(waited.stdout);
30393
+ auth = await readAuthStatus(authScope);
30394
+ const resumedAuthFailure = reportSetupAuthStatusFailure({
30395
+ auth,
30396
+ authScope,
30397
+ scope,
30398
+ json: options.json
30399
+ });
30400
+ if (resumedAuthFailure !== null) return resumedAuthFailure;
30401
+ if (auth.payload?.connected !== true) {
30402
+ const stillPending = readPendingAuthClaim(baseUrl, authScope);
30403
+ if (stillPending) {
30404
+ return pendingResult({
30405
+ baseUrl,
30406
+ scope,
30407
+ root,
30408
+ authorizationUrl: stillPending.claimUrl,
30409
+ json: options.json
30410
+ });
30411
+ }
30412
+ }
30413
+ }
30414
+ }
30415
+ if (auth.payload?.connected !== true) {
30416
+ process.stderr.write("Starting Deepline browser authorization...\n");
30417
+ const agentRuntime = detectAgentRuntime();
30418
+ const agentLed = agentRuntime !== "unknown";
30419
+ const waitMode = options.json || agentLed || !process.stdin.isTTY ? "no" : "auto";
30420
+ const registered = await captureStdout2(
30421
+ () => handleRegister(["--wait", waitMode, "--auth-scope", authScope])
30422
+ );
30423
+ printCapturedAuthorizationUrl(registered.stdout);
30424
+ if (registered.exitCode !== 0) {
30425
+ printCommandEnvelope(
30426
+ {
30427
+ ok: false,
30428
+ status: "failed",
30429
+ code: "AUTH_REGISTER_FAILED",
30430
+ exitCode: registered.exitCode,
30431
+ scope,
30432
+ next: `deepline auth register --auth-scope ${authScope}`
30433
+ },
30434
+ { json: options.json }
30435
+ );
30436
+ return registered.exitCode;
30437
+ }
30438
+ auth = await readAuthStatus(authScope);
30439
+ const registeredAuthFailure = reportSetupAuthStatusFailure({
30440
+ auth,
30441
+ authScope,
30442
+ scope,
30443
+ json: options.json
30444
+ });
30445
+ if (registeredAuthFailure !== null) return registeredAuthFailure;
30446
+ if (auth.payload?.connected !== true) {
30447
+ const pending = readPendingAuthClaim(baseUrl, authScope);
30448
+ return pendingResult({
30449
+ baseUrl,
30450
+ scope,
30451
+ root,
30452
+ authorizationUrl: pending?.claimUrl ?? "",
30453
+ json: options.json
30454
+ });
30455
+ }
30456
+ }
30457
+ process.stderr.write("Verifying Deepline setup...\n");
30458
+ const doctor = await captureStdout2(
30459
+ () => runDoctorCommand({ scope, json: true })
30460
+ );
30461
+ const doctorPayload = parseCapturedJson(doctor.stdout);
30462
+ if (doctor.exitCode !== 0) {
30463
+ printCommandEnvelope(
30464
+ {
30465
+ ok: false,
30466
+ status: "failed",
30467
+ code: "DOCTOR_FAILED",
30468
+ exitCode: doctor.exitCode,
30469
+ scope,
30470
+ doctor: doctorPayload,
30471
+ next: "Review the doctor result and choose a repair, then rerun: deepline setup --json"
30472
+ },
30473
+ { json: options.json }
30474
+ );
30475
+ return doctor.exitCode;
30476
+ }
30477
+ const statePath = writeSetupState({
30478
+ baseUrl,
30479
+ scope,
30480
+ root,
30481
+ status: "complete"
30482
+ });
30483
+ const doctorChecks = asRecord2(doctorPayload?.checks);
30484
+ const apiCheck = asRecord2(doctorChecks?.api);
30485
+ const quickstart = setupQuickstartCommand(baseUrl);
30486
+ printCommandEnvelope(
30487
+ {
30488
+ ok: true,
30489
+ status: "complete",
30490
+ complete: true,
30491
+ scope,
30492
+ cliVersion: SDK_VERSION,
30493
+ agents: skillsPayload?.agents ?? [],
30494
+ workspace: apiCheck?.workspace ?? null,
30495
+ rollbackCommand: rollbackCommand(scope, root),
30496
+ statePath,
30497
+ doctor: doctorPayload,
30498
+ next: quickstart,
30499
+ render: {
30500
+ sections: [
30501
+ {
30502
+ title: "setup",
30503
+ lines: [
30504
+ "Deepline is installed and connected.",
30505
+ `Next: ${quickstart}`
30506
+ ]
30507
+ }
30508
+ ]
30509
+ }
30510
+ },
30511
+ { json: options.json }
30512
+ );
30513
+ return 0;
30514
+ }
30515
+ function registerSetupCommands(program) {
30516
+ program.command("setup").description("Install skills, authenticate, and verify Deepline.").option("--scope <scope>", "Setup scope: global or local", "global").option("--json", "Emit one final JSON result envelope").addHelpText(
30517
+ "after",
30518
+ `
30519
+ Notes:
30520
+ Setup is idempotent. Bare setup resumes pending browser authorization.
30521
+ It installs skills before auth and does not run quickstart.
30522
+
30523
+ Examples:
30524
+ deepline setup --json
30525
+ deepline setup --scope local --json
30526
+ `
30527
+ ).action(async (options) => {
30528
+ process.exitCode = await runSetupCommand(options);
30529
+ });
30530
+ program.command("doctor").description("Verify CLI, skills, auth, workspace, and API connectivity.").option(
30531
+ "--scope <scope>",
30532
+ "Expected setup scope: global or local",
30533
+ "global"
30534
+ ).option("--json", "Emit one JSON result envelope").addHelpText(
30535
+ "after",
30536
+ `
30537
+ Notes:
30538
+ Doctor is read-only and makes no paid provider calls. It reports repairs but
30539
+ does not apply them automatically.
30540
+
30541
+ Examples:
30542
+ deepline doctor --json
30543
+ deepline doctor --scope local --json
30544
+ `
30545
+ ).action(async (options) => {
30546
+ process.exitCode = await runDoctorCommand(options);
30547
+ });
30548
+ }
30549
+
30550
+ // ../shared_libs/cli/command-compatibility.json
30551
+ var command_compatibility_default = {
30552
+ enrich: {
30553
+ family: "python",
30554
+ label: "a legacy Python CLI enrichment command",
30555
+ sdk_alternative: "Use `deepline plays ...` for durable workflows or `deepline tools execute ...` for one tool call."
30556
+ },
30557
+ session: {
30558
+ family: "python",
30559
+ label: "a legacy Python CLI session/playground command",
30560
+ sdk_alternative: "Use `deepline sessions send ...` or `deepline sessions render ...` for transcript workflows."
30561
+ },
30562
+ workflows: {
30563
+ family: "python",
30564
+ label: "a legacy Python CLI workflow command",
30565
+ sdk_alternative: "Use `deepline plays ...` in the SDK CLI."
30566
+ },
30567
+ events: {
30568
+ family: "python",
30569
+ label: "a legacy Python CLI event command"
30570
+ },
29737
30571
  plays: {
29738
30572
  family: "sdk",
29739
30573
  label: "an SDK CLI play command",
@@ -29797,130 +30631,440 @@ function commandCompatibilityHint(currentFamily, commandName, baseUrl) {
29797
30631
  lines.push(` Python alternative: ${compatibility.python_alternative}`);
29798
30632
  }
29799
30633
  }
29800
- return lines.join("\n");
29801
- }
29802
- function unknownCommandNameFromMessage(message) {
29803
- const match = message.match(/unknown command ['"]([^'"]+)['"]/i);
29804
- const command = match?.[1]?.trim();
29805
- return command ? command : null;
30634
+ return lines.join("\n");
30635
+ }
30636
+ function unknownCommandNameFromMessage(message) {
30637
+ const match = message.match(/unknown command ['"]([^'"]+)['"]/i);
30638
+ const command = match?.[1]?.trim();
30639
+ return command ? command : null;
30640
+ }
30641
+
30642
+ // src/cli/self-update.ts
30643
+ var import_node_child_process6 = require("child_process");
30644
+ function envTruthy(name) {
30645
+ const value = process.env[name]?.trim().toLowerCase();
30646
+ return value === "1" || value === "true" || value === "yes";
30647
+ }
30648
+ function isCi() {
30649
+ return envTruthy("CI") || envTruthy("GITHUB_ACTIONS");
30650
+ }
30651
+ function shouldSkipSelfUpdate() {
30652
+ return envTruthy("DEEPLINE_SKIP_SELF_UPDATE") || envTruthy("DEEPLINE_NO_AUTO_UPDATE") || envTruthy("DEEPLINE_SKIP_SDK_AUTO_UPDATE") || envTruthy("DEEPLINE_DISABLE_AUTO_UPDATE") || isCi();
30653
+ }
30654
+ function parseSemver(version) {
30655
+ const trimmed = version?.trim();
30656
+ if (!trimmed) return null;
30657
+ const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(
30658
+ trimmed
30659
+ );
30660
+ if (!match) return null;
30661
+ return {
30662
+ major: Number(match[1]),
30663
+ minor: Number(match[2]),
30664
+ patch: Number(match[3]),
30665
+ prerelease: match[4] ?? ""
30666
+ };
30667
+ }
30668
+ function compareSemver(left, right) {
30669
+ const a = parseSemver(left);
30670
+ const b = parseSemver(right);
30671
+ if (!a || !b) {
30672
+ return left.localeCompare(right);
30673
+ }
30674
+ for (const key of ["major", "minor", "patch"]) {
30675
+ if (a[key] !== b[key]) return a[key] > b[key] ? 1 : -1;
30676
+ }
30677
+ if (a.prerelease === b.prerelease) return 0;
30678
+ if (!a.prerelease) return 1;
30679
+ if (!b.prerelease) return -1;
30680
+ return a.prerelease.localeCompare(b.prerelease);
30681
+ }
30682
+ function isDowngradeAutoUpdateResponse(response) {
30683
+ const target = response?.latest?.trim();
30684
+ const current = response?.current?.trim() || SDK_VERSION;
30685
+ if (!target) return false;
30686
+ return compareSemver(target, current) < 0;
30687
+ }
30688
+ function relaunchCurrentCommand(plan) {
30689
+ return new Promise((resolve15) => {
30690
+ const command = plan.kind === "python-sidecar" ? plan.sidecarPath : process.execPath;
30691
+ const args = plan.kind === "python-sidecar" ? process.argv.slice(2) : process.argv.slice(1);
30692
+ const child = (0, import_node_child_process6.spawn)(command, args, {
30693
+ stdio: "inherit",
30694
+ shell: process.platform === "win32",
30695
+ env: {
30696
+ ...process.env,
30697
+ DEEPLINE_NO_AUTO_UPDATE: "1"
30698
+ }
30699
+ });
30700
+ child.on("error", (error) => {
30701
+ process.stderr.write(
30702
+ `Deepline SDK/CLI updated, but relaunch failed: ${error.message}
30703
+ `
30704
+ );
30705
+ resolve15(1);
30706
+ });
30707
+ child.on("close", (code) => resolve15(code ?? 1));
30708
+ });
30709
+ }
30710
+ async function maybeAutoUpdateAndRelaunch(response) {
30711
+ const autoUpdate = response?.auto_update;
30712
+ if (!response || !autoUpdate?.should_auto_update || shouldSkipSelfUpdate()) {
30713
+ return false;
30714
+ }
30715
+ if (isDowngradeAutoUpdateResponse(response)) {
30716
+ const target = response.latest;
30717
+ const current = response.current?.trim() || SDK_VERSION;
30718
+ process.stderr.write(
30719
+ `Deepline SDK/CLI auto-update refused: server advertised older ${target} than current ${current}. Continuing without mutating the CLI.
30720
+ `
30721
+ );
30722
+ return false;
30723
+ }
30724
+ const packageSpec = response.latest ? `deepline@${response.latest}` : void 0;
30725
+ const plan = resolveUpdatePlan({ packageSpec });
30726
+ if (plan.kind === "source") {
30727
+ return false;
30728
+ }
30729
+ const label = autoUpdate.reason === "rollback_forced" ? "has a server rollback pending and needs the latest rollback-aware CLI" : autoUpdate.reason === "deprecated" ? "is deprecated and will update automatically" : autoUpdate.required ? "requires an update" : "is more than the supported auto-update lag behind";
30730
+ process.stderr.write(
30731
+ `Deepline SDK/CLI ${label}; running ${plan.manualCommand}
30732
+ `
30733
+ );
30734
+ const updateResult = await runAutomaticUpdatePlan(plan);
30735
+ if (updateResult.status === "skipped_previous_failure") {
30736
+ return false;
30737
+ }
30738
+ if (updateResult.exitCode !== 0) {
30739
+ if (autoUpdate.required) {
30740
+ throw new Error(
30741
+ `Automatic Deepline SDK/CLI update failed with exit code ${updateResult.exitCode}. ${response.message}`
30742
+ );
30743
+ }
30744
+ process.stderr.write(
30745
+ `Deepline SDK/CLI auto-update failed with exit code ${updateResult.exitCode}; continuing with ${response.current ?? "current version"}.
30746
+ `
30747
+ );
30748
+ return false;
30749
+ }
30750
+ process.stderr.write("Deepline SDK/CLI updated; rerunning command.\n");
30751
+ const exitCode = await relaunchCurrentCommand(plan);
30752
+ process.exit(exitCode);
30753
+ return true;
30754
+ }
30755
+
30756
+ // src/cli/skills-sync.ts
30757
+ var import_node_child_process7 = require("child_process");
30758
+ var import_node_fs19 = require("fs");
30759
+ var import_node_path21 = require("path");
30760
+ var CHECK_TIMEOUT_MS2 = 3e3;
30761
+ var attemptedSync = false;
30762
+ function shouldSkipSkillsSync() {
30763
+ if (detectAgentRuntime() === "claude_cowork") {
30764
+ return true;
30765
+ }
30766
+ const value = process.env.DEEPLINE_SKIP_SKILLS_SYNC?.trim().toLowerCase();
30767
+ return value === "1" || value === "true" || value === "yes" || value === "on";
30768
+ }
30769
+ function activePluginSkillsDir() {
30770
+ const pluginMode = process.env.DEEPLINE_PLUGIN_MODE?.trim().toLowerCase();
30771
+ if (pluginMode !== "true" && pluginMode !== "1" && pluginMode !== "yes" && pluginMode !== "on") {
30772
+ return "";
30773
+ }
30774
+ const dir = process.env.DEEPLINE_PLUGIN_SKILLS_DIR?.trim() ?? "";
30775
+ return dir && (0, import_node_fs19.existsSync)(dir) ? dir : "";
30776
+ }
30777
+ function readPluginSkillsVersion() {
30778
+ const dir = activePluginSkillsDir();
30779
+ if (!dir) return "";
30780
+ try {
30781
+ return (0, import_node_fs19.readFileSync)((0, import_node_path21.join)(dir, ".version"), "utf-8").trim();
30782
+ } catch {
30783
+ return "";
30784
+ }
30785
+ }
30786
+ function sdkSkillsVersionPath(baseUrl) {
30787
+ return (0, import_node_path21.join)(sdkCliStateDirPath(baseUrl), "skills-version");
30788
+ }
30789
+ function legacySdkSkillsVersionPath(baseUrl) {
30790
+ return (0, import_node_path21.join)((0, import_node_path21.dirname)(sdkCliStateDirPath(baseUrl)), "sdk-skills", ".version");
30791
+ }
30792
+ function unavailableSkillsNoticePath(baseUrl) {
30793
+ return (0, import_node_path21.join)(sdkCliStateDirPath(baseUrl), "skills-sync-unavailable-version");
30794
+ }
30795
+ function readSdkSkillsLocalVersion(baseUrl) {
30796
+ const pluginVersion = readPluginSkillsVersion();
30797
+ if (pluginVersion) return pluginVersion;
30798
+ const path = (0, import_node_fs19.existsSync)(sdkSkillsVersionPath(baseUrl)) ? sdkSkillsVersionPath(baseUrl) : legacySdkSkillsVersionPath(baseUrl);
30799
+ if (!(0, import_node_fs19.existsSync)(path)) return "";
30800
+ try {
30801
+ return (0, import_node_fs19.readFileSync)(path, "utf-8").trim();
30802
+ } catch {
30803
+ return "";
30804
+ }
30805
+ }
30806
+ function writeLocalSkillsVersion(baseUrl, version) {
30807
+ const path = sdkSkillsVersionPath(baseUrl);
30808
+ (0, import_node_fs19.mkdirSync)((0, import_node_path21.dirname)(path), { recursive: true });
30809
+ (0, import_node_fs19.writeFileSync)(path, `${version}
30810
+ `, "utf-8");
30811
+ }
30812
+ function writeUnavailableSkillsNotice(baseUrl, remoteVersion, skillNames) {
30813
+ const path = unavailableSkillsNoticePath(baseUrl);
30814
+ try {
30815
+ if ((0, import_node_fs19.existsSync)(path) && (0, import_node_fs19.readFileSync)(path, "utf-8").trim() === remoteVersion) {
30816
+ return;
30817
+ }
30818
+ (0, import_node_fs19.mkdirSync)((0, import_node_path21.dirname)(path), { recursive: true });
30819
+ (0, import_node_fs19.writeFileSync)(path, `${remoteVersion}
30820
+ `, "utf-8");
30821
+ } catch {
30822
+ }
30823
+ const manualCommand = `npx ${buildSkillsInstallArgs(baseUrl, skillNames).join(" ")}`;
30824
+ writeSdkSkillsStatusLine(
30825
+ `Deepline agent skills are out of date, but neither \`bunx\` nor \`npx\` is available. Install Node.js/npm or Bun, then run:
30826
+ ${manualCommand}`
30827
+ );
30828
+ }
30829
+ function clearUnavailableSkillsNotice(baseUrl) {
30830
+ try {
30831
+ (0, import_node_fs19.unlinkSync)(unavailableSkillsNoticePath(baseUrl));
30832
+ } catch {
30833
+ }
30834
+ }
30835
+ function sortedUniqueSkillNames(names) {
30836
+ return [...new Set(names.map((name) => name.trim()).filter(Boolean))].sort(
30837
+ (a, b) => a.localeCompare(b)
30838
+ );
30839
+ }
30840
+ async function fetchV1SkillNames(baseUrl) {
30841
+ const controller = new AbortController();
30842
+ const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS2);
30843
+ try {
30844
+ const response = await fetch(
30845
+ new URL("/.well-known/skills/index.json", baseUrl),
30846
+ { signal: controller.signal }
30847
+ );
30848
+ if (!response.ok) return [];
30849
+ const data = await response.json().catch(() => null);
30850
+ const names = (data?.skills ?? []).filter((skill) => skill.install_surface === "v1").map((skill) => skill.name).filter(
30851
+ (name) => typeof name === "string" && name.length > 0
30852
+ );
30853
+ return sortedUniqueSkillNames(names);
30854
+ } catch {
30855
+ return [];
30856
+ } finally {
30857
+ clearTimeout(timeout);
30858
+ }
30859
+ }
30860
+ function buildSdkSkillNames(v1SkillNames) {
30861
+ return sortedUniqueSkillNames(v1SkillNames);
30862
+ }
30863
+ async function fetchSkillsUpdate(baseUrl, localVersion) {
30864
+ const controller = new AbortController();
30865
+ const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS2);
30866
+ try {
30867
+ const response = await fetch(new URL("/api/v2/cli/update-check", baseUrl), {
30868
+ method: "POST",
30869
+ headers: { "Content-Type": "application/json" },
30870
+ body: JSON.stringify({
30871
+ skills: {
30872
+ version: localVersion
30873
+ }
30874
+ }),
30875
+ signal: controller.signal
30876
+ });
30877
+ if (!response.ok) return null;
30878
+ const data = await response.json().catch(() => null);
30879
+ const skills = data?.skills;
30880
+ if (!skills) return null;
30881
+ return {
30882
+ needsUpdate: skills.needs_update === true,
30883
+ remoteVersion: typeof skills.remote?.version === "string" ? skills.remote.version.trim() : ""
30884
+ };
30885
+ } catch {
30886
+ return null;
30887
+ } finally {
30888
+ clearTimeout(timeout);
30889
+ }
29806
30890
  }
29807
-
29808
- // src/cli/self-update.ts
29809
- var import_node_child_process5 = require("child_process");
29810
- function envTruthy(name) {
29811
- const value = process.env[name]?.trim().toLowerCase();
29812
- return value === "1" || value === "true" || value === "yes";
30891
+ function buildSkillsInstallArgs(baseUrl, skillNames = DEFAULT_SDK_SKILL_NAMES) {
30892
+ return buildSkillsAddArgs(baseUrl, sortedUniqueSkillNames(skillNames));
29813
30893
  }
29814
- function isCi() {
29815
- return envTruthy("CI") || envTruthy("GITHUB_ACTIONS");
30894
+ function buildBunxSkillsInstallArgs(baseUrl, skillNames) {
30895
+ return buildSkillsAddArgs(baseUrl, sortedUniqueSkillNames(skillNames), {
30896
+ firstArg: "--bun"
30897
+ });
29816
30898
  }
29817
- function shouldSkipSelfUpdate() {
29818
- return envTruthy("DEEPLINE_SKIP_SELF_UPDATE") || envTruthy("DEEPLINE_NO_AUTO_UPDATE") || envTruthy("DEEPLINE_SKIP_SDK_AUTO_UPDATE") || envTruthy("DEEPLINE_DISABLE_AUTO_UPDATE") || isCi();
30899
+ function hasCommand(command) {
30900
+ const result = (0, import_node_child_process7.spawnSync)(command, ["--version"], {
30901
+ stdio: "ignore",
30902
+ shell: process.platform === "win32"
30903
+ });
30904
+ return result.status === 0;
29819
30905
  }
29820
- function parseSemver(version) {
29821
- const trimmed = version?.trim();
29822
- if (!trimmed) return null;
29823
- const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(
29824
- trimmed
29825
- );
29826
- if (!match) return null;
29827
- return {
29828
- major: Number(match[1]),
29829
- minor: Number(match[2]),
29830
- patch: Number(match[3]),
29831
- prerelease: match[4] ?? ""
29832
- };
30906
+ function shellQuote6(arg) {
30907
+ return `'${arg.replace(/'/g, `'\\''`)}'`;
29833
30908
  }
29834
- function compareSemver(left, right) {
29835
- const a = parseSemver(left);
29836
- const b = parseSemver(right);
29837
- if (!a || !b) {
29838
- return left.localeCompare(right);
30909
+ function resolveSkillsInstallCommands(baseUrl, skillNames = DEFAULT_SDK_SKILL_NAMES) {
30910
+ const commands = [];
30911
+ if (hasCommand("bunx")) {
30912
+ const bunxArgs = buildBunxSkillsInstallArgs(baseUrl, skillNames);
30913
+ commands.push({
30914
+ command: "bunx",
30915
+ args: bunxArgs,
30916
+ manualCommand: `bunx ${bunxArgs.map(shellQuote6).join(" ")}`
30917
+ });
29839
30918
  }
29840
- for (const key of ["major", "minor", "patch"]) {
29841
- if (a[key] !== b[key]) return a[key] > b[key] ? 1 : -1;
30919
+ if (hasCommand("npx")) {
30920
+ const npxArgs = buildSkillsInstallArgs(baseUrl, skillNames);
30921
+ commands.push({
30922
+ command: "npx",
30923
+ args: npxArgs,
30924
+ manualCommand: `npx ${npxArgs.map(shellQuote6).join(" ")}`
30925
+ });
29842
30926
  }
29843
- if (a.prerelease === b.prerelease) return 0;
29844
- if (!a.prerelease) return 1;
29845
- if (!b.prerelease) return -1;
29846
- return a.prerelease.localeCompare(b.prerelease);
29847
- }
29848
- function isDowngradeAutoUpdateResponse(response) {
29849
- const target = response?.latest?.trim();
29850
- const current = response?.current?.trim() || SDK_VERSION;
29851
- if (!target) return false;
29852
- return compareSemver(target, current) < 0;
30927
+ return commands;
29853
30928
  }
29854
- function relaunchCurrentCommand(plan) {
29855
- return new Promise((resolve14) => {
29856
- const command = plan.kind === "python-sidecar" ? plan.sidecarPath : process.execPath;
29857
- const args = plan.kind === "python-sidecar" ? process.argv.slice(2) : process.argv.slice(1);
29858
- const child = (0, import_node_child_process5.spawn)(command, args, {
29859
- stdio: "inherit",
29860
- shell: process.platform === "win32",
29861
- env: {
29862
- ...process.env,
29863
- DEEPLINE_NO_AUTO_UPDATE: "1"
29864
- }
30929
+ function runOneSkillsInstall(install) {
30930
+ return new Promise((resolve15) => {
30931
+ const child = (0, import_node_child_process7.spawn)(install.command, install.args, {
30932
+ stdio: ["ignore", "ignore", "pipe"],
30933
+ env: process.env
30934
+ });
30935
+ let stderr = "";
30936
+ child.stderr.on("data", (chunk) => {
30937
+ stderr += chunk.toString("utf-8");
29865
30938
  });
29866
30939
  child.on("error", (error) => {
29867
- process.stderr.write(
29868
- `Deepline SDK/CLI updated, but relaunch failed: ${error.message}
29869
- `
29870
- );
29871
- resolve14(1);
30940
+ resolve15({
30941
+ ok: false,
30942
+ detail: `failed to start ${install.command}: ${error.message}`,
30943
+ manualCommand: install.manualCommand
30944
+ });
30945
+ });
30946
+ child.on("close", (code) => {
30947
+ if (code === 0) {
30948
+ resolve15({ ok: true, detail: "", manualCommand: install.manualCommand });
30949
+ return;
30950
+ }
30951
+ const detail = stderr.trim();
30952
+ resolve15({
30953
+ ok: false,
30954
+ detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
30955
+ manualCommand: install.manualCommand
30956
+ });
29872
30957
  });
29873
- child.on("close", (code) => resolve14(code ?? 1));
29874
30958
  });
29875
30959
  }
29876
- async function maybeAutoUpdateAndRelaunch(response) {
29877
- const autoUpdate = response?.auto_update;
29878
- if (!response || !autoUpdate?.should_auto_update || shouldSkipSelfUpdate()) {
29879
- return false;
29880
- }
29881
- if (isDowngradeAutoUpdateResponse(response)) {
29882
- const target = response.latest;
29883
- const current = response.current?.trim() || SDK_VERSION;
29884
- process.stderr.write(
29885
- `Deepline SDK/CLI auto-update refused: server advertised older ${target} than current ${current}. Continuing without mutating the CLI.
29886
- `
29887
- );
29888
- return false;
29889
- }
29890
- const packageSpec = response.latest ? `deepline@${response.latest}` : void 0;
29891
- const plan = resolveUpdatePlan({ packageSpec });
29892
- if (plan.kind === "source") {
29893
- return false;
30960
+ async function runSkillsInstall(installs) {
30961
+ const failures = [];
30962
+ for (const install of installs) {
30963
+ const result = await runOneSkillsInstall(install);
30964
+ if (result.ok) return true;
30965
+ failures.push(result);
29894
30966
  }
29895
- const label = autoUpdate.reason === "rollback_forced" ? "has a server rollback pending and needs the latest rollback-aware CLI" : autoUpdate.reason === "deprecated" ? "is deprecated and will update automatically" : autoUpdate.required ? "requires an update" : "is more than the supported auto-update lag behind";
30967
+ const details = failures.map((failure) => failure.detail).filter(Boolean).join("\n");
30968
+ const manualCommand = failures.at(-1)?.manualCommand;
29896
30969
  process.stderr.write(
29897
- `Deepline SDK/CLI ${label}; running ${plan.manualCommand}
29898
- `
30970
+ `SDK skills sync failed${details ? `:
30971
+ ${details}` : ""}
30972
+ ` + (manualCommand ? `Run manually: ${manualCommand}
30973
+ ` : "")
29899
30974
  );
29900
- const updateResult = await runAutomaticUpdatePlan(plan);
29901
- if (updateResult.status === "skipped_previous_failure") {
29902
- return false;
29903
- }
29904
- if (updateResult.exitCode !== 0) {
29905
- if (autoUpdate.required) {
29906
- throw new Error(
29907
- `Automatic Deepline SDK/CLI update failed with exit code ${updateResult.exitCode}. ${response.message}`
29908
- );
30975
+ return false;
30976
+ }
30977
+ function runLegacySkillsCleanup() {
30978
+ const candidates = hasCommand("bunx") ? [
30979
+ {
30980
+ command: "bunx",
30981
+ args: [
30982
+ "--bun",
30983
+ "skills",
30984
+ "remove",
30985
+ "--global",
30986
+ "-y",
30987
+ ...LEGACY_SKILL_NAMES_TO_REMOVE
30988
+ ]
30989
+ },
30990
+ {
30991
+ command: "npx",
30992
+ args: [
30993
+ "--yes",
30994
+ "skills",
30995
+ "remove",
30996
+ "--global",
30997
+ "-y",
30998
+ ...LEGACY_SKILL_NAMES_TO_REMOVE
30999
+ ]
29909
31000
  }
29910
- process.stderr.write(
29911
- `Deepline SDK/CLI auto-update failed with exit code ${updateResult.exitCode}; continuing with ${response.current ?? "current version"}.
29912
- `
29913
- );
29914
- return false;
31001
+ ] : [
31002
+ {
31003
+ command: "npx",
31004
+ args: [
31005
+ "--yes",
31006
+ "skills",
31007
+ "remove",
31008
+ "--global",
31009
+ "-y",
31010
+ ...LEGACY_SKILL_NAMES_TO_REMOVE
31011
+ ]
31012
+ }
31013
+ ];
31014
+ for (const candidate of candidates) {
31015
+ const result = (0, import_node_child_process7.spawnSync)(candidate.command, candidate.args, {
31016
+ stdio: "ignore",
31017
+ env: process.env,
31018
+ shell: process.platform === "win32"
31019
+ });
31020
+ if (result.status === 0) return;
29915
31021
  }
29916
- process.stderr.write("Deepline SDK/CLI updated; rerunning command.\n");
29917
- const exitCode = await relaunchCurrentCommand(plan);
29918
- process.exit(exitCode);
29919
- return true;
31022
+ }
31023
+ function writeSdkSkillsStatusLine(line) {
31024
+ const progress = getActiveCliProgress();
31025
+ if (progress) {
31026
+ progress.writeLine(line);
31027
+ return;
31028
+ }
31029
+ process.stderr.write(`${line}
31030
+ `);
31031
+ }
31032
+ async function syncSdkSkillsIfNeeded(baseUrl, options = {}) {
31033
+ if (attemptedSync || shouldSkipSkillsSync()) return;
31034
+ attemptedSync = true;
31035
+ const usingPluginSkills = Boolean(activePluginSkillsDir());
31036
+ if (usingPluginSkills) {
31037
+ return;
31038
+ }
31039
+ const localVersion = readSdkSkillsLocalVersion(baseUrl);
31040
+ const update = options.update === void 0 ? await fetchSkillsUpdate(baseUrl, localVersion) : options.update ? {
31041
+ needsUpdate: options.update.needs_update,
31042
+ remoteVersion: options.update.remote.version
31043
+ } : null;
31044
+ if (!update?.needsUpdate || !update.remoteVersion) {
31045
+ return;
31046
+ }
31047
+ const remoteSkillNames = await fetchV1SkillNames(baseUrl);
31048
+ const skillNames = buildSdkSkillNames(
31049
+ remoteSkillNames.length > 0 ? remoteSkillNames : DEFAULT_SDK_SKILL_NAMES
31050
+ );
31051
+ if (skillNames.length === 0) return;
31052
+ const installs = resolveSkillsInstallCommands(baseUrl, skillNames);
31053
+ if (installs.length === 0) {
31054
+ writeUnavailableSkillsNotice(baseUrl, update.remoteVersion, skillNames);
31055
+ return;
31056
+ }
31057
+ writeSdkSkillsStatusLine("Deepline skills changed; syncing agent skills...");
31058
+ const installed = await runSkillsInstall(installs);
31059
+ if (!installed) return;
31060
+ runLegacySkillsCleanup();
31061
+ writeLocalSkillsVersion(baseUrl, update.remoteVersion);
31062
+ clearUnavailableSkillsNotice(baseUrl);
31063
+ writeSdkSkillsStatusLine("Deepline agent skills are up to date.");
29920
31064
  }
29921
31065
 
29922
31066
  // src/cli/failure-reporting.ts
29923
- var import_node_os14 = require("os");
31067
+ var import_node_os16 = require("os");
29924
31068
  var FAILURE_REPORT_DISABLE_ENV = "DEEPLINE_DISABLE_FAILURE_REPORTING";
29925
31069
  var REPORT_FAILURE_TIMEOUT_MS = 1e4;
29926
31070
  var MAX_FAILURE_TEXT_CHARS = 4e3;
@@ -30022,12 +31166,12 @@ function isNetworkFailure(error) {
30022
31166
  }
30023
31167
  function buildEnvironmentContext() {
30024
31168
  const context = {
30025
- os: (0, import_node_os14.platform)(),
30026
- os_release: (0, import_node_os14.release)(),
30027
- platform: `${(0, import_node_os14.platform)()}-${(0, import_node_os14.release)()}-${process.arch}`,
31169
+ os: (0, import_node_os16.platform)(),
31170
+ os_release: (0, import_node_os16.release)(),
31171
+ platform: `${(0, import_node_os16.platform)()}-${(0, import_node_os16.release)()}-${process.arch}`,
30028
31172
  node_version: process.version,
30029
31173
  runtime: "Node.js",
30030
- hostname: (0, import_node_os14.hostname)(),
31174
+ hostname: (0, import_node_os16.hostname)(),
30031
31175
  agent_runtime: detectAgentRuntime()
30032
31176
  };
30033
31177
  for (const key of ["CLAUDE_CODE_REMOTE", "DEEPLINE_PLUGIN_MODE"]) {
@@ -30220,8 +31364,8 @@ function topLevelCommandKnown(program, commandName) {
30220
31364
  );
30221
31365
  }
30222
31366
  async function runPlayRunnerHealthCheck() {
30223
- const dir = await (0, import_promises6.mkdtemp)((0, import_node_path20.join)((0, import_node_os15.tmpdir)(), "deepline-health-play-"));
30224
- const file = (0, import_node_path20.join)(dir, "health-check.play.ts");
31367
+ const dir = await (0, import_promises6.mkdtemp)((0, import_node_path22.join)((0, import_node_os17.tmpdir)(), "deepline-health-play-"));
31368
+ const file = (0, import_node_path22.join)(dir, "health-check.play.ts");
30225
31369
  try {
30226
31370
  await (0, import_promises6.writeFile)(
30227
31371
  file,
@@ -30439,7 +31583,7 @@ Exit codes:
30439
31583
  `
30440
31584
  );
30441
31585
  program.hook("preAction", async (_thisCommand, actionCommand) => {
30442
- if (actionCommand.name() === "version" || actionCommand.name() === "update" || actionCommand.name() === "switch" || isLegacyNoopInvocation()) {
31586
+ if (actionCommand.name() === "version" || actionCommand.name() === "update" || actionCommand.name() === "switch" || actionCommand.name() === "setup" || actionCommand.name() === "skills" || actionCommand.name() === "doctor" || isLegacyNoopInvocation()) {
30443
31587
  return;
30444
31588
  }
30445
31589
  if (printStartupPhase) {
@@ -30511,6 +31655,8 @@ Exit codes:
30511
31655
  registerFeedbackCommands(program);
30512
31656
  registerLegacyNoopCommands(program);
30513
31657
  registerUpdateCommand(program);
31658
+ registerSkillsCommand(program);
31659
+ registerSetupCommands(program);
30514
31660
  registerQuickstartCommands(program);
30515
31661
  registerSwitchCommands(program);
30516
31662
  program.command("preflight").description("Run compact health, auth, and Deepline billing checks.").option("--json", "Force JSON output.").addHelpText(