deepline 0.3.293 → 0.3.295

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/dist/bundling-sources/sdk/src/compat.ts +35 -7
  2. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  3. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +269 -267
  4. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +3 -4
  5. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +14 -1
  6. package/dist/cli/auth-EDTRERZP.mjs +31 -0
  7. package/dist/cli/auth-TDHKB277.js +31 -0
  8. package/dist/cli/billing-ELIDPILY.js +1749 -0
  9. package/dist/cli/billing-EXGXJVAU.mjs +1749 -0
  10. package/dist/cli/chunk-2S5Y3TZP.js +5688 -0
  11. package/dist/cli/chunk-3KC45UVF.js +150 -0
  12. package/dist/cli/chunk-3XX5TEA4.js +15 -0
  13. package/dist/cli/chunk-4MBOJQPL.mjs +0 -0
  14. package/dist/cli/chunk-4NEFYBF7.js +2240 -0
  15. package/dist/cli/chunk-5JLQPVNX.js +3056 -0
  16. package/dist/cli/chunk-5KDKFHZI.js +1269 -0
  17. package/dist/cli/chunk-6A3TJUWC.js +76 -0
  18. package/dist/cli/chunk-6O5YZOVN.mjs +890 -0
  19. package/dist/cli/chunk-7P5AA3SN.js +344 -0
  20. package/dist/cli/chunk-AYHLUHE5.js +52 -0
  21. package/dist/cli/chunk-CBVIJSHF.mjs +560 -0
  22. package/dist/cli/chunk-CFVVCN4A.js +735 -0
  23. package/dist/cli/chunk-COGTRVWW.js +30 -0
  24. package/dist/cli/chunk-D4AZUZO4.js +190 -0
  25. package/dist/cli/chunk-DLIRSHV6.mjs +28 -0
  26. package/dist/cli/chunk-EACVZI6N.mjs +1995 -0
  27. package/dist/cli/chunk-EDWJY6T4.mjs +3056 -0
  28. package/dist/cli/chunk-EXMLR72R.mjs +0 -0
  29. package/dist/cli/chunk-F35VOIQL.js +890 -0
  30. package/dist/cli/chunk-GAMFK2ZP.mjs +148 -0
  31. package/dist/cli/chunk-GHRQQPW2.mjs +2240 -0
  32. package/dist/cli/chunk-GONRLVV3.js +3440 -0
  33. package/dist/cli/chunk-GVUJJJ5G.mjs +76 -0
  34. package/dist/cli/chunk-H7HIDGZ7.mjs +150 -0
  35. package/dist/cli/chunk-HUZXWOSK.mjs +503 -0
  36. package/dist/cli/chunk-I5YZLGMG.js +1 -0
  37. package/dist/cli/chunk-ITOFSXIU.mjs +15 -0
  38. package/dist/cli/chunk-J5NA2TTX.js +503 -0
  39. package/dist/cli/chunk-JCS5TPNY.mjs +3440 -0
  40. package/dist/cli/chunk-JNNOCMQY.js +47 -0
  41. package/dist/cli/chunk-KRD6H754.mjs +30 -0
  42. package/dist/cli/chunk-LA46MMTL.js +560 -0
  43. package/dist/cli/chunk-LDWT7RJK.mjs +190 -0
  44. package/dist/cli/chunk-M2V26OR5.js +499 -0
  45. package/dist/cli/chunk-MIMASXO4.mjs +389 -0
  46. package/dist/cli/chunk-MROPCKW2.mjs +190 -0
  47. package/dist/cli/chunk-NXDRDJ4K.js +389 -0
  48. package/dist/cli/chunk-NXGCO4JB.mjs +344 -0
  49. package/dist/cli/chunk-OG64RBAC.js +6 -0
  50. package/dist/cli/chunk-OQYEC4GL.mjs +34 -0
  51. package/dist/cli/chunk-OWJZUE3Y.mjs +103 -0
  52. package/dist/cli/chunk-SDK3ZA2C.js +103 -0
  53. package/dist/cli/chunk-SFLARO4Y.mjs +47 -0
  54. package/dist/cli/chunk-SVIIIYIQ.js +923 -0
  55. package/dist/cli/chunk-TAC3JIFE.mjs +354 -0
  56. package/dist/cli/chunk-U3PI7HBM.js +34 -0
  57. package/dist/cli/chunk-UFS752TU.mjs +52 -0
  58. package/dist/cli/chunk-UI37DQE7.mjs +499 -0
  59. package/dist/cli/chunk-UNEMDKXN.js +1 -0
  60. package/dist/cli/chunk-WB6Z2Q44.mjs +1622 -0
  61. package/dist/cli/chunk-WJYPBHTE.js +148 -0
  62. package/dist/cli/chunk-WOHBA5PV.js +354 -0
  63. package/dist/cli/chunk-WQICC2B3.js +190 -0
  64. package/dist/cli/chunk-WZYKRD7T.mjs +6 -0
  65. package/dist/cli/chunk-XL2FO6CW.mjs +5688 -0
  66. package/dist/cli/chunk-XNYIZMNQ.js +28 -0
  67. package/dist/cli/chunk-XRK2TZT5.js +1995 -0
  68. package/dist/cli/chunk-YJYPL5OK.mjs +1269 -0
  69. package/dist/cli/chunk-YWNDYCXR.mjs +735 -0
  70. package/dist/cli/chunk-YWWAF7KZ.mjs +923 -0
  71. package/dist/cli/chunk-ZDO3TO3M.js +1622 -0
  72. package/dist/cli/client-4JVOYVC5.js +20 -0
  73. package/dist/cli/client-EEN2IJN2.mjs +20 -0
  74. package/dist/cli/csv-BOHBEP2I.mjs +585 -0
  75. package/dist/cli/csv-S5NYRUEX.js +585 -0
  76. package/dist/cli/db-CLODWCOB.mjs +486 -0
  77. package/dist/cli/db-F6PKFOOV.js +486 -0
  78. package/dist/cli/executions-5TQAKLJU.js +28 -0
  79. package/dist/cli/executions-MAKW6QOY.mjs +28 -0
  80. package/dist/cli/feedback-GV4QJFEN.js +107 -0
  81. package/dist/cli/feedback-XY24TW7H.mjs +107 -0
  82. package/dist/cli/getting-started.js +18 -76
  83. package/dist/cli/getting-started.mjs +10 -37
  84. package/dist/cli/git-6S2C22T5.js +23 -0
  85. package/dist/cli/git-OZLBD5HH.mjs +23 -0
  86. package/dist/cli/index.js +291 -54294
  87. package/dist/cli/index.mjs +209 -54352
  88. package/dist/cli/legacy-noops-LLCGDPQE.js +157 -0
  89. package/dist/cli/legacy-noops-TYH3TW2D.mjs +157 -0
  90. package/dist/cli/monitors-CABMB4DW.js +3251 -0
  91. package/dist/cli/monitors-TTD55HMK.mjs +3251 -0
  92. package/dist/cli/org-MSF3RG76.js +849 -0
  93. package/dist/cli/org-SFJ77MWY.mjs +849 -0
  94. package/dist/cli/play-NXHJSCKL.mjs +18439 -0
  95. package/dist/cli/play-OKSFNNO4.js +18439 -0
  96. package/dist/cli/providers-QC2ERERL.js +95 -0
  97. package/dist/cli/providers-TIIAZEUA.mjs +95 -0
  98. package/dist/cli/proxy-env-EBANZHPV.js +161 -0
  99. package/dist/cli/proxy-env-M4ZHXHRL.mjs +161 -0
  100. package/dist/cli/quickstart-ICANHLMI.js +69 -0
  101. package/dist/cli/quickstart-KWUOP3FI.mjs +69 -0
  102. package/dist/cli/search-U5H2FDOO.mjs +18 -0
  103. package/dist/cli/search-XQRQOLWS.js +18 -0
  104. package/dist/cli/secrets-3J6YTWFA.mjs +400 -0
  105. package/dist/cli/secrets-YNMNZNOM.js +400 -0
  106. package/dist/cli/sessions-CLEES4L6.mjs +15 -0
  107. package/dist/cli/sessions-J6VR47FG.js +15 -0
  108. package/dist/cli/settings-EEKZMTY4.js +846 -0
  109. package/dist/cli/settings-UVLOQZRH.mjs +846 -0
  110. package/dist/cli/setup-DLMDXPGJ.mjs +1257 -0
  111. package/dist/cli/setup-HC4Q7QM4.js +1257 -0
  112. package/dist/cli/skills-33PHFBSB.mjs +28 -0
  113. package/dist/cli/skills-LFRY3KPX.js +28 -0
  114. package/dist/cli/tools-3COEPKML.mjs +2955 -0
  115. package/dist/cli/tools-QXEVCN45.js +2955 -0
  116. package/dist/cli/update-LSAC4FF5.mjs +28 -0
  117. package/dist/cli/update-NXHSPYXA.js +28 -0
  118. package/dist/cli/workflow-7S44OECF.mjs +1936 -0
  119. package/dist/cli/workflow-QPZWYSZE.js +1936 -0
  120. package/dist/helpers.js +6 -32
  121. package/dist/helpers.mjs +2 -0
  122. package/dist/index.js +1972 -12825
  123. package/dist/index.mjs +1910 -12672
  124. package/dist/install-integrity.json +110 -0
  125. package/dist/monitor-fleet-contract.js +31 -730
  126. package/dist/monitor-fleet-contract.mjs +18 -667
  127. package/dist/monitor-input-contract.js +10 -390
  128. package/dist/monitor-input-contract.mjs +7 -348
  129. package/dist/plays/bundle-play-file.mjs +13 -5178
  130. package/dist/plays/csv-data-input.js +5 -3132
  131. package/dist/plays/csv-data-input.mjs +5 -3105
  132. package/dist/plays/run-request.js +56 -664
  133. package/dist/plays/run-request.mjs +30 -588
  134. package/dist/release.d.mts +1 -1
  135. package/dist/release.d.ts +1 -1
  136. package/dist/release.js +5 -212
  137. package/dist/release.mjs +5 -185
  138. package/package.json +1 -1
@@ -0,0 +1,2240 @@
1
+ import {
2
+ getActiveCliProgress
3
+ } from "./chunk-GAMFK2ZP.mjs";
4
+ import {
5
+ printCommandEnvelope
6
+ } from "./chunk-6O5YZOVN.mjs";
7
+ import {
8
+ DEFAULT_SKILL_AGENTS,
9
+ KNOWN_SKILL_AGENTS,
10
+ SDK_API_CONTRACT,
11
+ SDK_API_MAJOR,
12
+ SDK_VERSION,
13
+ autoDetectBaseUrl,
14
+ deploymentProtectionHeaders,
15
+ detectAgentRuntime,
16
+ ensureProjectPrivatePathsIgnored,
17
+ hasActivePluginSkills,
18
+ readSdkSkillsLocalVersion,
19
+ resolveApiKeyForBaseUrl,
20
+ resolveAutoSyncSkillAgents,
21
+ resolveProjectPinTarget,
22
+ sdkCliStateDirPath,
23
+ skillsAgentPathSegment,
24
+ writeSdkSkillsLocalVersion
25
+ } from "./chunk-EACVZI6N.mjs";
26
+ import {
27
+ DeeplineError
28
+ } from "./chunk-NXGCO4JB.mjs";
29
+
30
+ // src/cli/commands/skills.ts
31
+ import { spawn as spawn2 } from "child_process";
32
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
33
+ import { homedir as homedir3 } from "os";
34
+ import { dirname as dirname3, join as join3 } from "path";
35
+
36
+ // contracts/install-commands.json
37
+ var install_commands_default = {
38
+ skills: {
39
+ index_path: "/.well-known/skills/index.json",
40
+ default_agents: [
41
+ "codex",
42
+ "claude-code",
43
+ "cursor",
44
+ "gemini-cli",
45
+ "antigravity"
46
+ ],
47
+ npx_binary: "npx",
48
+ npx_add_args_template: [
49
+ "--yes",
50
+ "skills@1.5.15",
51
+ "add",
52
+ "{skills_source_url}",
53
+ "--agent",
54
+ "{agents}",
55
+ "--global",
56
+ "--yes",
57
+ "--skill",
58
+ "{skill_name}",
59
+ "--full-depth"
60
+ ]
61
+ },
62
+ cli: {
63
+ sdk_npm_global: "npm install -g deepline@latest"
64
+ }
65
+ };
66
+
67
+ // src/cli/install-commands.ts
68
+ var INSTALL_COMMANDS = install_commands_default;
69
+ var skillsNpxPackage = INSTALL_COMMANDS.skills.npx_add_args_template.find(
70
+ (arg) => /^skills@\d+\.\d+\.\d+$/.test(arg)
71
+ );
72
+ if (!skillsNpxPackage) {
73
+ throw new Error(
74
+ "shared_libs/cli/install-commands.json must pin an exact skills package version."
75
+ );
76
+ }
77
+ var SKILLS_NPX_PACKAGE = skillsNpxPackage;
78
+ var DEFAULT_V1_SKILL_NAMES = [
79
+ "build-tam",
80
+ "clay-to-deepline",
81
+ "deepline-analytics",
82
+ "deepline-engine",
83
+ "deepline-feedback",
84
+ "deepline-gtm",
85
+ "deepline-plays",
86
+ "deepline-quickstart",
87
+ "deepline-scoring",
88
+ "find-qualified-titles",
89
+ "linkedin-url-lookup",
90
+ // Deprecated alias for `deepline-scoring`, kept installable so saved prompts
91
+ // and scripts using `/niche-signal-discovery` keep routing to the renamed
92
+ // skill. Move to LEGACY_SKILL_NAMES_TO_REMOVE once the window closes.
93
+ "niche-signal-discovery",
94
+ "portfolio-prospecting"
95
+ ];
96
+ var DEFAULT_SDK_SKILL_NAMES = [...DEFAULT_V1_SKILL_NAMES];
97
+ var LEGACY_SKILL_NAMES_TO_REMOVE = [
98
+ "deepline-plays-feedback",
99
+ "deepline-plays-quickstart",
100
+ "workflow-hello-world"
101
+ ];
102
+ function normalizeBaseUrl(baseUrl) {
103
+ return baseUrl.replace(/\/$/, "");
104
+ }
105
+ function renderTemplate(template, values) {
106
+ return template.replace(/\{([a-z_]+)\}/g, (match, key) => {
107
+ return values[key] ?? match;
108
+ });
109
+ }
110
+ function skillsIndexUrl(baseUrl) {
111
+ return `${normalizeBaseUrl(baseUrl)}${INSTALL_COMMANDS.skills.index_path}`;
112
+ }
113
+ function skillsSourceUrl(baseUrl) {
114
+ return normalizeBaseUrl(baseUrl);
115
+ }
116
+ function buildSkillsAddArgs(baseUrl, skillName, options = {}) {
117
+ const skillNames = Array.isArray(skillName) ? skillName : [skillName];
118
+ const [firstSkillName, ...extraSkillNames] = skillNames;
119
+ const agents = options.agents ?? DEFAULT_SKILL_AGENTS;
120
+ const values = {
121
+ skills_index_url: skillsIndexUrl(baseUrl),
122
+ skills_source_url: skillsSourceUrl(baseUrl),
123
+ agents: agents.join(" "),
124
+ skill_name: firstSkillName ?? ""
125
+ };
126
+ const rendered = INSTALL_COMMANDS.skills.npx_add_args_template.flatMap(
127
+ (arg, index) => {
128
+ const next = index === 0 && options.firstArg ? options.firstArg : arg;
129
+ const value = renderTemplate(next, values);
130
+ if (arg === "{agents}") {
131
+ return agents;
132
+ }
133
+ if (arg === "{skill_name}") {
134
+ return [
135
+ value,
136
+ ...extraSkillNames.flatMap((name) => ["--skill", name])
137
+ ];
138
+ }
139
+ return value;
140
+ }
141
+ );
142
+ return rendered;
143
+ }
144
+
145
+ // src/cli/skills-sync.ts
146
+ import { spawn, spawnSync } from "child_process";
147
+ import { createHash, randomUUID as randomUUID2 } from "crypto";
148
+ import { homedir as homedir2 } from "os";
149
+ import { gunzipSync } from "zlib";
150
+ import {
151
+ appendFileSync,
152
+ existsSync as existsSync2,
153
+ mkdirSync as mkdirSync2,
154
+ readdirSync,
155
+ readFileSync as readFileSync2,
156
+ renameSync as renameSync2,
157
+ rmSync as rmSync2,
158
+ unlinkSync,
159
+ writeFileSync as writeFileSync2
160
+ } from "fs";
161
+ import { dirname as dirname2, join as join2 } from "path";
162
+
163
+ // src/compat.ts
164
+ import { randomUUID } from "crypto";
165
+ import {
166
+ existsSync,
167
+ mkdirSync,
168
+ readFileSync,
169
+ renameSync,
170
+ rmSync,
171
+ writeFileSync
172
+ } from "fs";
173
+ import { homedir } from "os";
174
+ import { dirname, join } from "path";
175
+ var CHECK_TIMEOUT_MS = 2e3;
176
+ var SDK_COMPATIBILITY_TRUST_MS = 10 * 60 * 1e3;
177
+ var SDK_COMPATIBILITY_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
178
+ var SDK_COMPATIBILITY_FAST_DEADLINE_MS = 400;
179
+ function writeCompatibilityCacheFile(path, cache) {
180
+ mkdirSync(dirname(path), { recursive: true });
181
+ const tempPath = `${path}.${process.pid}.${randomUUID()}.tmp`;
182
+ try {
183
+ writeFileSync(tempPath, `${JSON.stringify(cache, null, 2)}
184
+ `);
185
+ renameSync(tempPath, path);
186
+ } finally {
187
+ rmSync(tempPath, { force: true });
188
+ }
189
+ }
190
+ function shouldSkipCompatibilityCheck() {
191
+ const value = process.env.DEEPLINE_SKIP_SDK_COMPAT_CHECK?.trim().toLowerCase();
192
+ return value === "1" || value === "true" || value === "yes";
193
+ }
194
+ function sdkCompatibilityCachePath(baseUrl, homeDir = homedir()) {
195
+ return join(sdkCliStateDirPath(baseUrl, homeDir), "compat-cache.json");
196
+ }
197
+ function legacySdkCompatibilityCachePath(homeDir = homedir()) {
198
+ return join(homeDir, ".cache", "deepline", "sdk-compat-cache.json");
199
+ }
200
+ function compatibilityCacheKey(baseUrl, command, skillsVersion) {
201
+ return JSON.stringify({
202
+ baseUrl: baseUrl.replace(/\/$/, ""),
203
+ version: SDK_VERSION,
204
+ apiContract: SDK_API_CONTRACT,
205
+ apiMajor: SDK_API_MAJOR,
206
+ command: command?.trim() || null,
207
+ skillsVersion: skillsVersion ?? null
208
+ });
209
+ }
210
+ function legacyCompatibilityCacheKey(baseUrl, command) {
211
+ return JSON.stringify({
212
+ baseUrl: baseUrl.replace(/\/$/, ""),
213
+ version: SDK_VERSION,
214
+ apiContract: SDK_API_CONTRACT,
215
+ command: command?.trim() || null
216
+ });
217
+ }
218
+ function isCleanCompatibilityPolicy(response) {
219
+ if (!response) return false;
220
+ return response.ok === true && response.update_required !== true && response.status !== "unsupported" && !response.auto_update?.should_auto_update && !response.cli_family;
221
+ }
222
+ function readFreshCleanCompatibility(baseUrl, command, skillsVersion) {
223
+ const cached = readCachedCompatibility(baseUrl, command, skillsVersion);
224
+ return isCleanCompatibilityPolicy(cached) ? cached : null;
225
+ }
226
+ function readTrustedCleanCompatibility(baseUrl, command, skillsVersion, now = Date.now()) {
227
+ const entry = readCachedCompatibilityEntry(baseUrl, command, skillsVersion);
228
+ if (!entry || !isSavedWithin(entry.savedAt, SDK_COMPATIBILITY_TRUST_MS, now))
229
+ return null;
230
+ return isCleanCompatibilityPolicy(entry.response) ? entry.response : null;
231
+ }
232
+ function isSavedWithin(savedAt, windowMs, now) {
233
+ if (typeof savedAt !== "number") return false;
234
+ const age = now - savedAt;
235
+ return Number.isFinite(age) && age >= 0 && age <= windowMs;
236
+ }
237
+ function readCachedCompatibility(baseUrl, command, skillsVersion) {
238
+ return readCachedCompatibilityEntry(baseUrl, command, skillsVersion)?.response ?? null;
239
+ }
240
+ function readCachedCompatibilityEntry(baseUrl, command, skillsVersion) {
241
+ try {
242
+ const path = sdkCompatibilityCachePath(baseUrl);
243
+ const legacyPath = legacySdkCompatibilityCachePath();
244
+ const useLegacyCache = !existsSync(path) && existsSync(legacyPath);
245
+ const cachePath = useLegacyCache ? legacyPath : path;
246
+ if (!existsSync(cachePath)) {
247
+ return null;
248
+ }
249
+ const parsed = JSON.parse(
250
+ readFileSync(cachePath, "utf8")
251
+ );
252
+ const entry = parsed.entries?.[compatibilityCacheKey(baseUrl, command, skillsVersion)] ?? (useLegacyCache ? parsed.entries?.[legacyCompatibilityCacheKey(baseUrl, command)] : void 0);
253
+ if (!entry || !isSavedWithin(entry.savedAt, SDK_COMPATIBILITY_CACHE_TTL_MS, Date.now())) {
254
+ return null;
255
+ }
256
+ return entry;
257
+ } catch {
258
+ return null;
259
+ }
260
+ }
261
+ function invalidateCachedCompatibility(baseUrl, command, skillsVersion) {
262
+ try {
263
+ const path = sdkCompatibilityCachePath(baseUrl);
264
+ const legacyPath = legacySdkCompatibilityCachePath();
265
+ const cachePath = !existsSync(path) && existsSync(legacyPath) ? legacyPath : path;
266
+ if (!existsSync(cachePath)) return;
267
+ const existing = JSON.parse(
268
+ readFileSync(cachePath, "utf8")
269
+ );
270
+ const entries = existing.entries ?? {};
271
+ delete entries[compatibilityCacheKey(baseUrl, command, skillsVersion)];
272
+ delete entries[legacyCompatibilityCacheKey(baseUrl, command)];
273
+ writeCompatibilityCacheFile(cachePath, { entries });
274
+ } catch {
275
+ }
276
+ }
277
+ function writeCachedCompatibility(baseUrl, command, skillsVersion, response) {
278
+ try {
279
+ const path = sdkCompatibilityCachePath(baseUrl);
280
+ let existing = {};
281
+ if (existsSync(path)) {
282
+ try {
283
+ existing = JSON.parse(readFileSync(path, "utf8"));
284
+ } catch {
285
+ }
286
+ }
287
+ const entries = existing.entries ?? {};
288
+ entries[compatibilityCacheKey(baseUrl, command, skillsVersion)] = {
289
+ savedAt: Date.now(),
290
+ response
291
+ };
292
+ writeCompatibilityCacheFile(path, { entries });
293
+ } catch {
294
+ }
295
+ }
296
+ async function checkSdkCompatibility(baseUrl, options = {}) {
297
+ if (shouldSkipCompatibilityCheck()) {
298
+ return { response: null, error: null };
299
+ }
300
+ const controller = new AbortController();
301
+ const timeout = setTimeout(
302
+ () => controller.abort(),
303
+ options.timeoutMs ?? CHECK_TIMEOUT_MS
304
+ );
305
+ try {
306
+ const url = new URL("/api/v2/sdk/compat", baseUrl);
307
+ url.searchParams.set("version", SDK_VERSION);
308
+ if (options.command?.trim()) {
309
+ url.searchParams.set("command", options.command.trim());
310
+ }
311
+ if (options.skillsVersion !== void 0) {
312
+ url.searchParams.set("skills_version", options.skillsVersion ?? "");
313
+ }
314
+ const response = await fetch(url, {
315
+ method: "GET",
316
+ headers: {
317
+ ...deploymentProtectionHeaders(),
318
+ "User-Agent": `deepline-ts-sdk/${SDK_VERSION}`,
319
+ "X-Deepline-SDK-Version": SDK_VERSION,
320
+ "X-Deepline-API-Major": String(SDK_API_MAJOR),
321
+ "X-Deepline-API-Contract": SDK_API_CONTRACT,
322
+ "X-Deepline-CLI-Update-Preferences": "1"
323
+ },
324
+ signal: controller.signal
325
+ });
326
+ const data = await response.json().catch(() => null);
327
+ if (!data || typeof data.ok !== "boolean" || typeof data.status !== "string") {
328
+ throw new Error(
329
+ `SDK compatibility endpoint returned a non-policy response (HTTP ${response.status}).`
330
+ );
331
+ }
332
+ writeCachedCompatibility(
333
+ baseUrl,
334
+ options.command,
335
+ options.skillsVersion,
336
+ data
337
+ );
338
+ return { response: data, error: null };
339
+ } catch (error) {
340
+ const cached = readCachedCompatibility(
341
+ baseUrl,
342
+ options.command,
343
+ options.skillsVersion
344
+ );
345
+ if (cached) {
346
+ if (options.timeoutMs !== void 0 && options.timeoutMs < CHECK_TIMEOUT_MS) {
347
+ invalidateCachedCompatibility(
348
+ baseUrl,
349
+ options.command,
350
+ options.skillsVersion
351
+ );
352
+ }
353
+ return { response: cached, error: null };
354
+ }
355
+ return {
356
+ response: null,
357
+ error: error instanceof Error ? error : new Error(String(error))
358
+ };
359
+ } finally {
360
+ clearTimeout(timeout);
361
+ }
362
+ }
363
+ function isCiEnvironment() {
364
+ return ["CI", "GITHUB_ACTIONS"].some((name) => {
365
+ const value = process.env[name]?.trim().toLowerCase();
366
+ return value === "1" || value === "true" || value === "yes";
367
+ });
368
+ }
369
+ function enforceSdkCompatibilityResponse(response, options = {}) {
370
+ if (!response) {
371
+ return;
372
+ }
373
+ if (response.update_required) {
374
+ throw new Error(response.message);
375
+ }
376
+ if ((response.status === "deprecated" || response.status === "update_available") && !isCiEnvironment() && options.quietAdvisory !== true) {
377
+ process.stderr.write(`${response.message}
378
+ `);
379
+ }
380
+ }
381
+
382
+ // src/cli/windows-arg-escape.ts
383
+ var CMD_META_CHARS = /([()\][%!^"`<>&|;, *?])/g;
384
+ function escapeCmdArgument(arg, forBatchFile = true) {
385
+ let escaped = arg.replace(/(\\*)"/g, '$1$1\\"');
386
+ escaped = escaped.replace(/(\\*)$/, "$1$1");
387
+ escaped = `"${escaped}"`;
388
+ escaped = escaped.replace(CMD_META_CHARS, "^$1");
389
+ return forBatchFile ? escaped.replace(CMD_META_CHARS, "^$1") : escaped;
390
+ }
391
+ function escapeCmdCommand(command) {
392
+ return command.replace(CMD_META_CHARS, "^$1");
393
+ }
394
+ function resolveShellSpawn(command, args, platform2 = process.platform) {
395
+ if (platform2 !== "win32") {
396
+ return { command, args: [...args], shell: false };
397
+ }
398
+ return {
399
+ command: escapeCmdCommand(command),
400
+ args: args.map((arg) => escapeCmdArgument(arg)),
401
+ shell: true
402
+ };
403
+ }
404
+
405
+ // src/cli/failure-reporting.ts
406
+ import { hostname, platform, release } from "os";
407
+ var FAILURE_REPORT_DISABLE_ENV = "DEEPLINE_DISABLE_FAILURE_REPORTING";
408
+ var REPORT_FAILURE_TIMEOUT_MS = 1e4;
409
+ var BACKGROUND_REPORT_FAILURE_TIMEOUT_MS = 2e3;
410
+ var MAX_FAILURE_TEXT_CHARS = 4e3;
411
+ var MAX_COMMAND_TOKENS = 3;
412
+ var REPORTABLE_EXIT_CODES = /* @__PURE__ */ new Set([4, 5]);
413
+ var EMAIL_RE = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
414
+ var UNIX_PATH_RE = /(?:\/Users\/|\/home\/|\/var\/folders\/|\/tmp\/|\/sessions\/)[^"'\n\r`]+/g;
415
+ var WINDOWS_PATH_RE = /[A-Za-z]:(?:\\{1,2})(?:Users|Temp|tmp)(?:\\{1,2})[^"'\n\r`]+/g;
416
+ var ASSIGNMENT_SECRET_RE = /\b(access[_-]?token|api[_-]?key|apikey|auth(?:orization)?|bearer|password|secret|session)\b(\s*[:=]\s*)(?!Bearer\s+\[redacted-secret\])([^\s,;]+)/gi;
417
+ var BEARER_SECRET_RE = /\b(bearer)\s+([A-Za-z0-9._-]+)/gi;
418
+ var GENERIC_TOKEN_RE = /\b(?:dlp|sk|ghp|xox[baprs])[-_A-Za-z0-9]{8,}\b/g;
419
+ var SECRET_OPTION_RE = /^--?(?:access[-_]?token|api[-_]?key|apikey|auth(?:orization)?|bearer|password|secret|session|token)$/i;
420
+ function truthyEnv(name) {
421
+ return ["1", "true", "yes", "on"].includes(
422
+ String(process.env[name] ?? "").trim().toLowerCase()
423
+ );
424
+ }
425
+ function isFailureReportingDisabled() {
426
+ return truthyEnv(FAILURE_REPORT_DISABLE_ENV);
427
+ }
428
+ function redactFailureText(value, maxChars = MAX_FAILURE_TEXT_CHARS) {
429
+ const home = process.env.HOME?.trim();
430
+ let text = String(value ?? "");
431
+ if (!text) return "";
432
+ if (home && home !== "/") {
433
+ text = text.split(home).join("~");
434
+ }
435
+ return text.replace(EMAIL_RE, "[redacted-email]").replace(UNIX_PATH_RE, "[redacted-path]").replace(WINDOWS_PATH_RE, "[redacted-path]").replace(BEARER_SECRET_RE, "$1 [redacted-secret]").replace(ASSIGNMENT_SECRET_RE, "$1$2[redacted-secret]").replace(GENERIC_TOKEN_RE, "[redacted-secret]").slice(0, maxChars);
436
+ }
437
+ function sanitizeCommand(argv, prefix = "deepline") {
438
+ const tokens = [];
439
+ for (let index = 0; index < argv.length; index += 1) {
440
+ const arg = argv[index];
441
+ const value = String(arg ?? "").trim();
442
+ if (!value) continue;
443
+ if (value.startsWith("-")) {
444
+ if (!value.includes("=") && SECRET_OPTION_RE.test(value)) {
445
+ index += 1;
446
+ }
447
+ continue;
448
+ }
449
+ tokens.push(redactFailureText(value, 200));
450
+ if (tokens.length >= MAX_COMMAND_TOKENS) break;
451
+ }
452
+ return tokens.length > 0 ? [prefix, ...tokens].join(" ") : prefix;
453
+ }
454
+ function errorMessage(error) {
455
+ if (error instanceof Error) return `${error.name}: ${error.message}`;
456
+ return String(error ?? "");
457
+ }
458
+ function errorStack(error) {
459
+ if (error instanceof Error && error.stack) {
460
+ return redactFailureText(error.stack);
461
+ }
462
+ return null;
463
+ }
464
+ function classifyNetworkFailure(error) {
465
+ const seen = /* @__PURE__ */ new Set();
466
+ let current = error;
467
+ while (current && !seen.has(current)) {
468
+ seen.add(current);
469
+ const record = typeof current === "object" && current !== null ? current : {};
470
+ const code = String(record.code ?? "").toLowerCase();
471
+ const name = current instanceof Error ? current.name.toLowerCase() : "";
472
+ const text = String(
473
+ current instanceof Error ? current.message : current
474
+ ).toLowerCase();
475
+ const combined = `${code} ${name} ${text}`;
476
+ if (combined.includes("aborterror") || combined.includes("timeout") || combined.includes("timed out") || combined.includes("etimedout")) {
477
+ return "network_timeout";
478
+ }
479
+ if (combined.includes("enotfound") || combined.includes("eai_again") || combined.includes("getaddrinfo") || combined.includes("name or service not known") || combined.includes("temporary failure in name resolution")) {
480
+ return "network_dns_resolution_failed";
481
+ }
482
+ if (combined.includes("econnrefused") || combined.includes("connection refused")) {
483
+ return "network_connection_refused";
484
+ }
485
+ if (combined.includes("econnreset") || combined.includes("connection reset")) {
486
+ return "network_connection_reset";
487
+ }
488
+ if (combined.includes("incompleteread") || combined.includes("incomplete read")) {
489
+ return "network_incomplete_read";
490
+ }
491
+ if (combined.includes("remotedisconnected") || combined.includes("remote end closed connection") || combined.includes("other side closed") || combined.includes("socket hang up")) {
492
+ return "network_remote_disconnected";
493
+ }
494
+ if (combined.includes("ssl") || combined.includes("tls") || combined.includes("unexpected_eof_while_reading")) {
495
+ return "network_ssl_error";
496
+ }
497
+ current = record.cause ?? record.context;
498
+ }
499
+ return "network_error";
500
+ }
501
+ function classifyNetworkCauseCategory(error) {
502
+ const seen = /* @__PURE__ */ new Set();
503
+ let cause = error;
504
+ while (cause && typeof cause === "object" && !seen.has(cause)) {
505
+ seen.add(cause);
506
+ const record = cause;
507
+ if (record.code === "UND_ERR_CONNECT_TIMEOUT") return "connect_timeout";
508
+ cause = record.cause;
509
+ }
510
+ switch (classifyNetworkFailure(error)) {
511
+ case "network_dns_resolution_failed":
512
+ return "dns";
513
+ case "network_ssl_error":
514
+ return "tls";
515
+ case "network_connection_reset":
516
+ return "reset";
517
+ case "network_timeout":
518
+ return "timeout";
519
+ default:
520
+ return "unknown";
521
+ }
522
+ }
523
+ function isNetworkFailure(error) {
524
+ if (!(error instanceof Error)) return false;
525
+ if (error instanceof DeeplineError && error.statusCode) return false;
526
+ const code = classifyNetworkFailure(error);
527
+ return code !== "network_error" || /unable to connect|unable to stream/i.test(error.message);
528
+ }
529
+ function buildEnvironmentContext() {
530
+ const context = {
531
+ os: platform(),
532
+ os_release: release(),
533
+ platform: `${platform()}-${release()}-${process.arch}`,
534
+ node_version: process.version,
535
+ runtime: "Node.js",
536
+ hostname: hostname(),
537
+ agent_runtime: detectAgentRuntime()
538
+ };
539
+ for (const key of ["CLAUDE_CODE_REMOTE", "DEEPLINE_PLUGIN_MODE"]) {
540
+ const normalized = process.env[key]?.trim();
541
+ if (normalized) context[key.toLowerCase()] = normalized;
542
+ }
543
+ if (process.env.CLAUDE_PROJECT_DIR?.trim()) {
544
+ context.claude_project_dir_present = "true";
545
+ }
546
+ if (process.env.DEEPLINE_PLUGIN_ROOT?.trim()) {
547
+ context.deepline_plugin_root_present = "true";
548
+ }
549
+ if (process.env.DEEPLINE_PLUGIN_SKILLS_DIR?.trim()) {
550
+ context.deepline_plugin_skills_dir_present = "true";
551
+ }
552
+ if (process.env.HOME?.trim().startsWith("/sessions/")) {
553
+ context.home_scope = "sessions";
554
+ }
555
+ return context;
556
+ }
557
+ function failureReportHeaders(apiKey) {
558
+ return {
559
+ Authorization: `Bearer ${apiKey}`,
560
+ "Content-Type": "application/json",
561
+ "User-Agent": `deepline-ts-sdk/${SDK_VERSION}`,
562
+ "X-Deepline-Client-Family": "sdk",
563
+ "X-Deepline-CLI-Family": "sdk",
564
+ "X-Deepline-Agent-Runtime": detectAgentRuntime(),
565
+ "X-Deepline-CLI-Version": SDK_VERSION,
566
+ "X-Deepline-SDK-Version": SDK_VERSION
567
+ };
568
+ }
569
+ async function postFailureReport(input) {
570
+ const controller = new AbortController();
571
+ const timeout = setTimeout(() => controller.abort(), input.timeoutMs);
572
+ if (input.background) timeout.unref();
573
+ try {
574
+ await fetch(new URL("/api/v2/cli/report-failure", input.baseUrl), {
575
+ method: "POST",
576
+ headers: failureReportHeaders(input.apiKey),
577
+ body: JSON.stringify(input.body),
578
+ signal: controller.signal
579
+ });
580
+ return true;
581
+ } catch {
582
+ return false;
583
+ } finally {
584
+ clearTimeout(timeout);
585
+ }
586
+ }
587
+ function boundedTelemetryString(value, maxChars) {
588
+ return redactFailureText(value.trim(), maxChars);
589
+ }
590
+ async function maybeReportAutomaticSkillsSyncFailure(input) {
591
+ if (isFailureReportingDisabled()) return false;
592
+ let apiKey = "";
593
+ try {
594
+ apiKey = resolveApiKeyForBaseUrl(input.baseUrl);
595
+ } catch {
596
+ return false;
597
+ }
598
+ if (!apiKey) return false;
599
+ const attempts = input.attempts.slice(0, 2).map((attempt) => ({
600
+ installer: attempt.installer,
601
+ outcome: attempt.outcome,
602
+ exit_code: typeof attempt.exitCode === "number" && Number.isFinite(attempt.exitCode) ? Math.trunc(attempt.exitCode) : null
603
+ }));
604
+ const agents = input.agents.slice(0, 8).map((agent) => boundedTelemetryString(agent, 100)).filter(Boolean);
605
+ const baseUrl = input.baseUrl.replace(/\/$/, "");
606
+ const failureKind = "background_warning";
607
+ const failureCode2 = "SKILLS_AUTO_SYNC_FAILED";
608
+ const failureStage = "skills_install";
609
+ return postFailureReport({
610
+ baseUrl,
611
+ apiKey,
612
+ timeoutMs: BACKGROUND_REPORT_FAILURE_TIMEOUT_MS,
613
+ background: true,
614
+ body: {
615
+ command: "deepline skills",
616
+ subcommand: "skills",
617
+ failure_kind: failureKind,
618
+ failure_code: failureCode2,
619
+ failure_stage: failureStage,
620
+ error_body: "Automatic Deepline skills installation failed.",
621
+ cli_version: SDK_VERSION,
622
+ context: {
623
+ environment: buildEnvironmentContext(),
624
+ skills_sync: {
625
+ skills_package: boundedTelemetryString(input.skillsPackage, 100),
626
+ remote_skills_version: boundedTelemetryString(
627
+ input.remoteSkillsVersion,
628
+ 200
629
+ ),
630
+ agents,
631
+ target_skill_count: Math.max(
632
+ 0,
633
+ Math.trunc(
634
+ Number.isFinite(input.targetSkillCount) ? input.targetSkillCount : 0
635
+ )
636
+ ),
637
+ marker_written: input.markerWritten,
638
+ attempts
639
+ }
640
+ }
641
+ }
642
+ });
643
+ }
644
+ function subcommandFromArgv(argv) {
645
+ return argv.find((arg) => arg && !arg.startsWith("-")) ?? null;
646
+ }
647
+ function commandTokens(argv) {
648
+ return argv.map((arg) => String(arg ?? "").trim()).filter((arg) => arg && !arg.startsWith("-"));
649
+ }
650
+ function isServerLoggedPlayRunStartFailure(input) {
651
+ const [subcommand, command] = commandTokens(input.argv);
652
+ return subcommand === "plays" && command === "run" && input.error instanceof DeeplineError && typeof input.error.statusCode === "number";
653
+ }
654
+ function shouldReport(input) {
655
+ if (input.error !== void 0) return true;
656
+ return input.exitCode !== null && REPORTABLE_EXIT_CODES.has(input.exitCode);
657
+ }
658
+ function failureCode(input) {
659
+ if (input.error !== void 0) {
660
+ if (isNetworkFailure(input.error))
661
+ return classifyNetworkFailure(input.error);
662
+ if (input.error instanceof DeeplineError && input.error.code) {
663
+ return input.error.code;
664
+ }
665
+ if (input.error instanceof Error && input.error.name)
666
+ return input.error.name;
667
+ return "CLI_FAILURE";
668
+ }
669
+ return input.exitCode === 4 ? "network_error" : "command_exit";
670
+ }
671
+ function resolvedExitCode(input) {
672
+ if (typeof input.exitCode === "number" && Number.isFinite(input.exitCode)) {
673
+ return Math.trunc(input.exitCode);
674
+ }
675
+ if (input.error === void 0) return null;
676
+ return isNetworkFailure(input.error) ? 4 : 5;
677
+ }
678
+ function resolveSdkCliFailureExitCode(error) {
679
+ return isNetworkFailure(error) ? 4 : 1;
680
+ }
681
+ function resolvedFailureKind(input) {
682
+ return input.error === void 0 ? "command_exit" : "uncaught_exception";
683
+ }
684
+ function safeRequestDiagnostics(error) {
685
+ if (!(error instanceof DeeplineError)) return null;
686
+ const request = error.details?.request;
687
+ if (!request || typeof request !== "object" || Array.isArray(request))
688
+ return null;
689
+ const value = request;
690
+ const result = {};
691
+ if (typeof value.phase === "string" && [
692
+ "before_request",
693
+ "retry_wait",
694
+ "awaiting_headers",
695
+ "reading_body"
696
+ ].includes(value.phase))
697
+ result.phase = value.phase;
698
+ if (typeof value.method === "string" && /^(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS)$/.test(value.method))
699
+ result.method = value.method;
700
+ if (typeof value.url === "string") {
701
+ try {
702
+ const url = new URL(value.url);
703
+ if (url.protocol === "https:" || url.protocol === "http:") {
704
+ result.url = redactFailureText(`${url.origin}${url.pathname}`, 1e3);
705
+ }
706
+ } catch {
707
+ }
708
+ }
709
+ if (typeof value.failedAtUtc === "string" && Number.isFinite(Date.parse(value.failedAtUtc))) {
710
+ result.failedAtUtc = new Date(value.failedAtUtc).toISOString();
711
+ }
712
+ for (const key of ["elapsedMs", "attempts"]) {
713
+ const number = value[key];
714
+ if (typeof number === "number" && Number.isSafeInteger(number) && number >= 0)
715
+ result[key] = number;
716
+ }
717
+ return Object.keys(result).length > 0 ? result : null;
718
+ }
719
+ function buildFailureReport(input) {
720
+ const durationMs = Math.max(0, Date.now() - input.startedAtMs);
721
+ const request = safeRequestDiagnostics(input.error);
722
+ const failureKind = resolvedFailureKind({
723
+ exitCode: input.exitCode,
724
+ error: input.error
725
+ });
726
+ const code = failureCode({ exitCode: input.exitCode, error: input.error });
727
+ const errorBody = input.error === void 0 ? `SDK CLI command exited ${input.exitCode ?? "unknown"}` : errorMessage(input.error);
728
+ const networkCauseCategory = input.error !== void 0 && isNetworkFailure(input.error) ? classifyNetworkCauseCategory(input.error) : null;
729
+ return {
730
+ command: sanitizeCommand(input.argv),
731
+ subcommand: subcommandFromArgv(input.argv),
732
+ error_status: input.exitCode,
733
+ error_body: redactFailureText(errorBody),
734
+ exit_code: input.exitCode,
735
+ duration_ms: durationMs,
736
+ error_class: input.error instanceof Error ? input.error.name : null,
737
+ stack_trace: errorStack(input.error),
738
+ failure_kind: failureKind,
739
+ failure_code: code,
740
+ failure_stage: input.error === void 0 ? "command_exit" : "cli_main",
741
+ cli_version: SDK_VERSION,
742
+ context: {
743
+ base_url: redactFailureText(input.baseUrl, 400),
744
+ command_summary: sanitizeCommand(input.argv),
745
+ environment: buildEnvironmentContext(),
746
+ ...request ? { request } : {},
747
+ failure_kind: failureKind,
748
+ failure_code: code,
749
+ failure_stage: input.error === void 0 ? "command_exit" : "cli_main",
750
+ duration_ms: durationMs,
751
+ ...input.exitCode !== null ? { exit_code: input.exitCode } : {},
752
+ ...networkCauseCategory ? { network_cause_category: networkCauseCategory } : {}
753
+ }
754
+ };
755
+ }
756
+ async function maybeReportSdkCliFailure(input) {
757
+ if (isFailureReportingDisabled()) return false;
758
+ if (isServerLoggedPlayRunStartFailure(input)) return false;
759
+ const exitCode = resolvedExitCode(input);
760
+ if (!shouldReport({ exitCode, error: input.error })) return false;
761
+ const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
762
+ const apiKey = resolveApiKeyForBaseUrl(baseUrl);
763
+ if (!apiKey) return false;
764
+ return postFailureReport({
765
+ baseUrl,
766
+ apiKey,
767
+ timeoutMs: REPORT_FAILURE_TIMEOUT_MS,
768
+ body: buildFailureReport({
769
+ argv: input.argv,
770
+ startedAtMs: input.startedAtMs,
771
+ error: input.error,
772
+ exitCode,
773
+ baseUrl
774
+ })
775
+ });
776
+ }
777
+
778
+ // src/cli/skills-sync.ts
779
+ var CHECK_TIMEOUT_MS2 = 3e3;
780
+ var INSTALLER_PROBE_TIMEOUT_MS = 1500;
781
+ var PUBLISHED_VERIFICATION_RETRY_MS = 5 * 6e4;
782
+ function shouldSkipSkillsSync() {
783
+ if (detectAgentRuntime() === "claude_cowork" || isCiEnvironment()) {
784
+ return true;
785
+ }
786
+ const value = process.env.DEEPLINE_SKIP_SKILLS_SYNC?.trim().toLowerCase();
787
+ return value === "1" || value === "true" || value === "yes" || value === "on";
788
+ }
789
+ function unavailableSkillsNoticePath(baseUrl) {
790
+ return join2(sdkCliStateDirPath(baseUrl), "skills-sync-unavailable-version");
791
+ }
792
+ var SKILLS_SYNC_IN_PROGRESS_TTL_MS = 10 * 60 * 1e3;
793
+ var SKILLS_SYNC_IN_PROGRESS_PID_CAP_MS = 6 * 60 * 60 * 1e3;
794
+ var agentLeaseSegment = skillsAgentPathSegment;
795
+ function sortedLeaseAgents(agents) {
796
+ const expanded = agents.includes("*") ? [...agents, ...KNOWN_SKILL_AGENTS] : agents;
797
+ return [...new Set(expanded)].sort((a, b) => a.localeCompare(b));
798
+ }
799
+ function inProgressSkillsSyncPath(baseUrl, agent) {
800
+ return join2(
801
+ sdkCliStateDirPath(baseUrl),
802
+ `skills-sync-in-progress-${agentLeaseSegment(agent)}`
803
+ );
804
+ }
805
+ function readInProgressSkillsSyncLeaseFromDir(dir) {
806
+ const claim = readMarkedSkillsSyncVersion(join2(dir, "claim"));
807
+ const [version = "", startedAt = ""] = claim.split(" ");
808
+ const pidRaw = readMarkedSkillsSyncVersion(join2(dir, "pid"));
809
+ return {
810
+ claimToken: version && startedAt ? `${version} ${startedAt}` : "",
811
+ version,
812
+ startedAt: Number(startedAt),
813
+ pid: /^\d+$/.test(pidRaw) ? Number(pidRaw) : null,
814
+ owner: readMarkedSkillsSyncVersion(join2(dir, "owner"))
815
+ };
816
+ }
817
+ function readInProgressSkillsSyncLease(baseUrl, agent) {
818
+ return readInProgressSkillsSyncLeaseFromDir(
819
+ inProgressSkillsSyncPath(baseUrl, agent)
820
+ );
821
+ }
822
+ function readInProgressSkillsSyncLeases(baseUrl, agents) {
823
+ return new Map(
824
+ sortedLeaseAgents(agents).map((agent) => [
825
+ agent,
826
+ readInProgressSkillsSyncLease(baseUrl, agent)
827
+ ])
828
+ );
829
+ }
830
+ var UNRESOLVED_SKILLS_CLAIM_VERSION = "unresolved";
831
+ function claimedSkillsVersion(leases) {
832
+ for (const lease of leases.values()) {
833
+ if (lease.version && lease.version !== UNRESOLVED_SKILLS_CLAIM_VERSION) {
834
+ return lease.version;
835
+ }
836
+ }
837
+ return "";
838
+ }
839
+ function isProcessAlive(pid) {
840
+ try {
841
+ process.kill(pid, 0);
842
+ return true;
843
+ } catch (error) {
844
+ return error.code === "EPERM";
845
+ }
846
+ }
847
+ function isFreshInProgressSkillsSync(lease, now = Date.now()) {
848
+ if (!Number.isFinite(lease.startedAt)) return false;
849
+ const age = now - lease.startedAt;
850
+ if (age < 0) return false;
851
+ if (lease.pid !== null && isProcessAlive(lease.pid)) {
852
+ return age < SKILLS_SYNC_IN_PROGRESS_PID_CAP_MS;
853
+ }
854
+ return age < SKILLS_SYNC_IN_PROGRESS_TTL_MS;
855
+ }
856
+ function quarantine(path, tag) {
857
+ const holding = `${path}.${tag}-${process.pid}-${Date.now()}`;
858
+ try {
859
+ renameSync2(path, holding);
860
+ return holding;
861
+ } catch {
862
+ return null;
863
+ }
864
+ }
865
+ function claimOneInProgressSkillsSync(baseUrl, remoteVersion, agent, owner) {
866
+ const dir = inProgressSkillsSyncPath(baseUrl, agent);
867
+ const errorCode = (error) => error.code;
868
+ const create = () => {
869
+ const claimToken = `${remoteVersion} ${Date.now()}`;
870
+ const staging = `${dir}.staging-${process.pid}-${Date.now()}`;
871
+ mkdirSync2(staging);
872
+ writeFileSync2(join2(staging, "claim"), `${claimToken}
873
+ `, "utf-8");
874
+ writeFileSync2(join2(staging, "owner"), `${owner}
875
+ `, "utf-8");
876
+ try {
877
+ renameSync2(staging, dir);
878
+ } catch (error) {
879
+ rmSync2(staging, { recursive: true, force: true });
880
+ const code = errorCode(error);
881
+ const err = new Error("lease directory already exists");
882
+ err.code = code === "EEXIST" || code === "ENOTEMPTY" ? "EEXIST" : String(code ?? "");
883
+ throw err;
884
+ }
885
+ return { state: "claimed", claimToken };
886
+ };
887
+ try {
888
+ mkdirSync2(dirname2(dir), { recursive: true });
889
+ return create();
890
+ } catch (error) {
891
+ if (errorCode(error) !== "EEXIST") return { state: "unavailable" };
892
+ }
893
+ if (isFreshInProgressSkillsSync(readInProgressSkillsSyncLease(baseUrl, agent))) {
894
+ return { state: "held" };
895
+ }
896
+ const tombstone = quarantine(dir, "stale");
897
+ if (!tombstone) {
898
+ return { state: "held" };
899
+ }
900
+ rmSync2(tombstone, { recursive: true, force: true });
901
+ try {
902
+ return create();
903
+ } catch (error) {
904
+ return errorCode(error) === "EEXIST" ? { state: "held" } : { state: "unavailable" };
905
+ }
906
+ }
907
+ function claimInProgressSkillsSync(baseUrl, remoteVersion, agents) {
908
+ const claimTokens = /* @__PURE__ */ new Map();
909
+ const owner = randomUUID2();
910
+ for (const agent of sortedLeaseAgents(agents)) {
911
+ const claim = claimOneInProgressSkillsSync(
912
+ baseUrl,
913
+ remoteVersion,
914
+ agent,
915
+ owner
916
+ );
917
+ if (claim.state !== "claimed") {
918
+ releaseInProgressSkillsSync(baseUrl, claimTokens);
919
+ return { state: claim.state };
920
+ }
921
+ claimTokens.set(agent, claim.claimToken);
922
+ }
923
+ return { state: "claimed", claimTokens, owner };
924
+ }
925
+ function claimAndSpawnDetachedSkillsSync(baseUrl, agents, remoteVersion, spawnDetached) {
926
+ const claim = claimInProgressSkillsSync(baseUrl, remoteVersion, agents);
927
+ if (claim.state !== "claimed") return claim.state;
928
+ const { claimTokens, owner } = claim;
929
+ spawnDetached([SKILLS_AUTO_SYNC_COMMAND, "--claim", owner], () => {
930
+ releaseInProgressSkillsSync(baseUrl, claimTokens);
931
+ if (remoteVersion !== UNRESOLVED_SKILLS_CLAIM_VERSION) {
932
+ markFailedSkillsSync(baseUrl, remoteVersion, agents);
933
+ }
934
+ deferSkillsNotice(
935
+ baseUrl,
936
+ agents
937
+ )(
938
+ "Deepline skills refresh failed to start; run `deepline skills` to retry."
939
+ );
940
+ });
941
+ return "claimed";
942
+ }
943
+ function recordOwnPidOnInProgressSkillsSync(baseUrl, leases) {
944
+ for (const [agent, lease] of leases) {
945
+ if (!lease.claimToken) continue;
946
+ try {
947
+ writeFileSync2(
948
+ join2(inProgressSkillsSyncPath(baseUrl, agent), "pid"),
949
+ `${process.pid}
950
+ `,
951
+ "utf-8"
952
+ );
953
+ } catch {
954
+ }
955
+ }
956
+ }
957
+ function releaseInProgressSkillsSync(baseUrl, claimTokens) {
958
+ for (const [agent, claimToken] of claimTokens) {
959
+ const dir = inProgressSkillsSyncPath(baseUrl, agent);
960
+ if (!claimToken) {
961
+ try {
962
+ rmSync2(dir, { recursive: true, force: true });
963
+ } catch {
964
+ }
965
+ continue;
966
+ }
967
+ const tombstone = quarantine(dir, "release");
968
+ if (!tombstone) continue;
969
+ try {
970
+ if (readInProgressSkillsSyncLeaseFromDir(tombstone).claimToken !== claimToken) {
971
+ renameSync2(tombstone, dir);
972
+ continue;
973
+ }
974
+ } catch {
975
+ }
976
+ rmSync2(tombstone, { recursive: true, force: true });
977
+ }
978
+ }
979
+ function claimTokensOf(leases) {
980
+ return new Map(
981
+ [...leases].map(([agent, lease]) => [agent, lease.claimToken])
982
+ );
983
+ }
984
+ async function acquireSkillsInstallLease(baseUrl, agents, version, options = {
985
+ waitMs: 9e4
986
+ }) {
987
+ const pollMs = options.pollMs ?? 250;
988
+ const deadline = Date.now() + options.waitMs;
989
+ let waited = false;
990
+ for (; ; ) {
991
+ const claim = claimInProgressSkillsSync(baseUrl, version, agents);
992
+ if (claim.state === "unavailable") {
993
+ return { release: () => {
994
+ } };
995
+ }
996
+ if (claim.state === "claimed") {
997
+ recordOwnPidOnInProgressSkillsSync(
998
+ baseUrl,
999
+ readInProgressSkillsSyncLeases(baseUrl, agents)
1000
+ );
1001
+ return {
1002
+ release: () => releaseInProgressSkillsSync(baseUrl, claim.claimTokens)
1003
+ };
1004
+ }
1005
+ if (Date.now() >= deadline) return null;
1006
+ if (!waited) {
1007
+ waited = true;
1008
+ options.onWait?.();
1009
+ }
1010
+ await new Promise((resolve) => setTimeout(resolve, pollMs));
1011
+ }
1012
+ }
1013
+ function skillsInstallLeasePath(baseUrl, agents) {
1014
+ return sortedLeaseAgents(agents).map((agent) => inProgressSkillsSyncPath(baseUrl, agent)).join(", ");
1015
+ }
1016
+ function deferredSkillsNoticePath(baseUrl, agents) {
1017
+ return join2(
1018
+ sdkCliStateDirPath(baseUrl),
1019
+ `skills-sync-notice-${sortedLeaseAgents(agents).map(agentLeaseSegment).join("-")}`
1020
+ );
1021
+ }
1022
+ function deferSkillsNotice(baseUrl, agents) {
1023
+ const path = deferredSkillsNoticePath(baseUrl, agents);
1024
+ return (text) => {
1025
+ try {
1026
+ mkdirSync2(dirname2(path), { recursive: true });
1027
+ appendFileSync(path, `${text.trimEnd()}
1028
+ `, "utf-8");
1029
+ } catch {
1030
+ }
1031
+ };
1032
+ }
1033
+ function flushDeferredSkillsNotices(baseUrl, agents) {
1034
+ const path = deferredSkillsNoticePath(baseUrl, agents);
1035
+ if (!existsSync2(path)) return;
1036
+ const tombstone = quarantine(path, "consuming");
1037
+ if (!tombstone) return;
1038
+ let text = "";
1039
+ try {
1040
+ text = readFileSync2(tombstone, "utf-8");
1041
+ } catch {
1042
+ return;
1043
+ } finally {
1044
+ try {
1045
+ unlinkSync(tombstone);
1046
+ } catch {
1047
+ }
1048
+ }
1049
+ if (text.trim()) writeSdkSkillsStatusLine(text.trimEnd());
1050
+ }
1051
+ function flushDeferredSkillsSyncNotices(baseUrl) {
1052
+ if (shouldSkipSkillsSync() || hasActivePluginSkills()) return;
1053
+ const agents = resolveAutoSyncSkillAgents();
1054
+ if (agents.length === 0) return;
1055
+ flushDeferredSkillsNotices(baseUrl, agents);
1056
+ }
1057
+ var SKILLS_AUTO_SYNC_COMMAND = "skills-auto-sync";
1058
+ function spawnDetachedSkillsSyncChild(argv, onSpawnError) {
1059
+ const child = spawn(process.execPath, [process.argv[1] ?? "", ...argv], {
1060
+ detached: true,
1061
+ stdio: "ignore",
1062
+ env: process.env
1063
+ });
1064
+ child.on("error", () => onSpawnError?.());
1065
+ child.unref();
1066
+ }
1067
+ function failedSkillsSyncPath(baseUrl, agents) {
1068
+ return join2(
1069
+ sdkCliStateDirPath(baseUrl),
1070
+ `skills-sync-failed-${sortedLeaseAgents(agents).map(agentLeaseSegment).join("-")}-version`
1071
+ );
1072
+ }
1073
+ function hasMarkedSkillsSyncVersion(path, version) {
1074
+ return Boolean(version) && readMarkedSkillsSyncVersion(path) === version;
1075
+ }
1076
+ function readMarkedSkillsSyncVersion(path) {
1077
+ try {
1078
+ return existsSync2(path) ? readFileSync2(path, "utf-8").trim() : "";
1079
+ } catch {
1080
+ return "";
1081
+ }
1082
+ }
1083
+ function writeMarkedSkillsSyncVersion(path, version) {
1084
+ try {
1085
+ mkdirSync2(dirname2(path), { recursive: true });
1086
+ writeFileSync2(path, `${version}
1087
+ `, "utf-8");
1088
+ return true;
1089
+ } catch {
1090
+ return false;
1091
+ }
1092
+ }
1093
+ function writeUnavailableSkillsNotice(baseUrl, remoteVersion, skillNames, reason, notice = writeSdkSkillsStatusLine, knownProbes) {
1094
+ const path = unavailableSkillsNoticePath(baseUrl);
1095
+ if (hasMarkedSkillsSyncVersion(path, remoteVersion)) return;
1096
+ writeMarkedSkillsSyncVersion(path, remoteVersion);
1097
+ const probes = reason === "no-installer" ? knownProbes ?? probeInstallers() : null;
1098
+ const manualCommand = probes?.npx === "missing" && probes.bunx !== "missing" ? `bunx ${buildBunxSkillsInstallArgs(baseUrl, skillNames, DEFAULT_SKILL_AGENTS).map(shellQuote).join(" ")}` : `npx ${buildSkillsInstallArgs(baseUrl, skillNames).join(" ")}`;
1099
+ const explanation = reason === "unsupported-agent" ? "the detected agent has no automatic skills sync target. Automatic sync was skipped." : probes?.bunx === "missing" && probes.npx === "missing" ? "neither `bunx` nor `npx` was found on PATH. Make one available." : `installer version check failed for ${Object.entries(probes ?? {}).filter(([, result]) => result === "failed").map(([command]) => `\`${command}\``).join(" and ")}. Check those commands with --version.`;
1100
+ notice(
1101
+ `Deepline agent skills are out of date, but ${explanation} To install manually, run:
1102
+ ${manualCommand}`
1103
+ );
1104
+ }
1105
+ function clearUnavailableSkillsNotice(baseUrl) {
1106
+ try {
1107
+ unlinkSync(unavailableSkillsNoticePath(baseUrl));
1108
+ } catch {
1109
+ }
1110
+ }
1111
+ function hasFailedSkillsSync(baseUrl, remoteVersion, agents) {
1112
+ const marked = readMarkedSkillsSyncVersion(
1113
+ failedSkillsSyncPath(baseUrl, agents)
1114
+ );
1115
+ if (marked === remoteVersion) return true;
1116
+ try {
1117
+ const retry = JSON.parse(marked);
1118
+ const now = Date.now();
1119
+ return retry.version === remoteVersion && typeof retry.retryAfter === "number" && retry.retryAfter > now && retry.retryAfter <= now + PUBLISHED_VERIFICATION_RETRY_MS;
1120
+ } catch {
1121
+ return false;
1122
+ }
1123
+ }
1124
+ function hasFailedAutomaticSkillsSync(baseUrl, agents) {
1125
+ return existsSync2(failedSkillsSyncPath(baseUrl, agents));
1126
+ }
1127
+ function markFailedSkillsSync(baseUrl, remoteVersion, agents) {
1128
+ return writeMarkedSkillsSyncVersion(
1129
+ failedSkillsSyncPath(baseUrl, agents),
1130
+ remoteVersion
1131
+ );
1132
+ }
1133
+ function deferPublishedVerificationRetry(baseUrl, remoteVersion, agents) {
1134
+ return writeMarkedSkillsSyncVersion(
1135
+ failedSkillsSyncPath(baseUrl, agents),
1136
+ JSON.stringify({
1137
+ version: remoteVersion,
1138
+ retryAfter: Date.now() + PUBLISHED_VERIFICATION_RETRY_MS
1139
+ })
1140
+ );
1141
+ }
1142
+ function clearFailedSkillsSync(baseUrl, agents) {
1143
+ try {
1144
+ unlinkSync(failedSkillsSyncPath(baseUrl, agents));
1145
+ } catch {
1146
+ }
1147
+ }
1148
+ function clearFailedAutomaticSkillsSync(baseUrl, agents) {
1149
+ clearFailedSkillsSync(baseUrl, agents);
1150
+ }
1151
+ function sortedUniqueSkillNames(names) {
1152
+ return [...new Set(names.map((name) => name.trim()).filter(Boolean))].sort(
1153
+ (a, b) => a.localeCompare(b)
1154
+ );
1155
+ }
1156
+ async function fetchV1SkillIndex(baseUrl) {
1157
+ const controller = new AbortController();
1158
+ const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS2);
1159
+ try {
1160
+ const response = await fetch(
1161
+ new URL("/.well-known/skills/index.json", baseUrl),
1162
+ { signal: controller.signal }
1163
+ );
1164
+ if (!response.ok) return null;
1165
+ const data = await response.json().catch(() => null);
1166
+ return data;
1167
+ } catch {
1168
+ return null;
1169
+ } finally {
1170
+ clearTimeout(timeout);
1171
+ }
1172
+ }
1173
+ function v1SkillNames(index) {
1174
+ const skills = Array.isArray(index?.skills) ? index.skills : [];
1175
+ return sortedUniqueSkillNames(
1176
+ skills.filter((skill) => skill && skill.install_surface === "v1").map((skill) => skill.name).filter(
1177
+ (name) => typeof name === "string" && name.length > 0
1178
+ )
1179
+ );
1180
+ }
1181
+ function buildSdkSkillNames(v1SkillNames2) {
1182
+ return sortedUniqueSkillNames(v1SkillNames2);
1183
+ }
1184
+ function isDefaultAgentSet(agents) {
1185
+ return agents.length === DEFAULT_SKILL_AGENTS.length && agents.every((agent, index) => agent === DEFAULT_SKILL_AGENTS[index]);
1186
+ }
1187
+ function safeSkillFilePath(value) {
1188
+ if (typeof value !== "string" || !value) return false;
1189
+ return value.split("/").every(
1190
+ (part) => /^[a-zA-Z0-9._-]+$/.test(part) && part !== "." && part !== ".."
1191
+ );
1192
+ }
1193
+ function installedSkillFiles(root, prefix = "") {
1194
+ const files = [];
1195
+ for (const entry of readdirSync(join2(root, prefix), {
1196
+ withFileTypes: true
1197
+ })) {
1198
+ const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
1199
+ if (entry.isDirectory()) {
1200
+ files.push(...installedSkillFiles(root, relativePath));
1201
+ } else if (entry.isFile()) {
1202
+ files.push(relativePath);
1203
+ } else {
1204
+ throw new Error(`unexpected installed entry in ${root}`);
1205
+ }
1206
+ }
1207
+ return files.sort((a, b) => a.localeCompare(b));
1208
+ }
1209
+ function publishedArchiveFiles(bytes, expectedFiles, name) {
1210
+ const tar = gunzipSync(bytes, { maxOutputLength: 128 * 1024 * 1024 });
1211
+ const expected = new Set(expectedFiles);
1212
+ const files = /* @__PURE__ */ new Map();
1213
+ let offset = 0;
1214
+ while (offset + 512 <= tar.length) {
1215
+ const header = tar.subarray(offset, offset + 512);
1216
+ if (header.every((byte) => byte === 0)) {
1217
+ if (!tar.subarray(offset).every((byte) => byte === 0)) {
1218
+ throw new Error(`invalid published archive for ${name}`);
1219
+ }
1220
+ break;
1221
+ }
1222
+ const file = header.toString("utf8", 0, 100).split("\0", 1)[0] ?? "";
1223
+ const sizeText = header.toString("ascii", 124, 136).replace(/\0/g, "").trim();
1224
+ const size = /^[0-7]+$/.test(sizeText) ? Number.parseInt(sizeText, 8) : NaN;
1225
+ const type = header[156];
1226
+ if (!safeSkillFilePath(file) || !expected.has(file) || files.has(file) || !Number.isSafeInteger(size) || size < 0 || type !== 0 && type !== 48 || header.subarray(345, 500).some((byte) => byte !== 0)) {
1227
+ throw new Error(`invalid published archive entry for ${name}`);
1228
+ }
1229
+ const start = offset + 512;
1230
+ const end = start + size;
1231
+ if (end > tar.length) {
1232
+ throw new Error(`incomplete published archive for ${name}`);
1233
+ }
1234
+ files.set(file, tar.subarray(start, end));
1235
+ offset = start + Math.ceil(size / 512) * 512;
1236
+ }
1237
+ if (files.size !== expected.size) {
1238
+ throw new Error(`published archive file list differs for ${name}`);
1239
+ }
1240
+ return files;
1241
+ }
1242
+ var PublishedSkillsVerificationError = class extends Error {
1243
+ };
1244
+ async function verifyDefaultAgentSkillsInstall(baseUrl, remoteVersion, skillNames, index) {
1245
+ if (index?.version !== remoteVersion) {
1246
+ throw new PublishedSkillsVerificationError(
1247
+ "the published skill index version does not match the server version"
1248
+ );
1249
+ }
1250
+ const published = /* @__PURE__ */ new Map();
1251
+ const entries = Array.isArray(index.skills) ? index.skills : [];
1252
+ for (const name of skillNames) {
1253
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(name)) {
1254
+ throw new PublishedSkillsVerificationError(
1255
+ `unsafe published skill name: ${name}`
1256
+ );
1257
+ }
1258
+ const entry = entries.find(
1259
+ (skill) => skill && skill.name === name && skill.install_surface === "v1"
1260
+ );
1261
+ if (!Array.isArray(entry?.files) || !entry.files.length || !entry.files.every(safeSkillFilePath) || new Set(entry.files).size !== entry.files.length || !entry.files.includes("SKILL.md") || entry.type !== "archive" || entry.url !== `./archives/${name}.tar.gz` || typeof entry.digest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(entry.digest)) {
1262
+ throw new PublishedSkillsVerificationError(
1263
+ `invalid published file list for ${name}`
1264
+ );
1265
+ }
1266
+ published.set(name, {
1267
+ files: [...entry.files].sort((a, b) => a.localeCompare(b)),
1268
+ digest: entry.digest
1269
+ });
1270
+ }
1271
+ const expected = /* @__PURE__ */ new Map();
1272
+ const jobs = [...published];
1273
+ const controller = new AbortController();
1274
+ const timeout = setTimeout(() => controller.abort(), 12e4);
1275
+ let cursor = 0;
1276
+ try {
1277
+ await Promise.all(
1278
+ Array.from({ length: Math.min(8, jobs.length) }, async () => {
1279
+ while (cursor < jobs.length) {
1280
+ const job = jobs[cursor++];
1281
+ if (!job) break;
1282
+ const [name, publishedSkill] = job;
1283
+ const url = new URL(
1284
+ `/.well-known/skills/archives/${name}.tar.gz`,
1285
+ baseUrl
1286
+ );
1287
+ const response = await fetch(url, { signal: controller.signal });
1288
+ if (!response.ok) {
1289
+ throw new Error(`published archive is unavailable: ${name}`);
1290
+ }
1291
+ const bytes = Buffer.from(await response.arrayBuffer());
1292
+ const digest = `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
1293
+ if (digest !== publishedSkill.digest) {
1294
+ throw new Error(`published archive digest differs for ${name}`);
1295
+ }
1296
+ for (const [file, content] of publishedArchiveFiles(
1297
+ bytes,
1298
+ publishedSkill.files,
1299
+ name
1300
+ )) {
1301
+ expected.set(`${name}/${file}`, content);
1302
+ }
1303
+ }
1304
+ })
1305
+ );
1306
+ } catch (error) {
1307
+ throw new PublishedSkillsVerificationError(
1308
+ error instanceof Error ? error.message : String(error)
1309
+ );
1310
+ } finally {
1311
+ clearTimeout(timeout);
1312
+ controller.abort();
1313
+ }
1314
+ const home = homedir2();
1315
+ const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR?.trim() || join2(home, ".claude");
1316
+ for (const [name, { files }] of published) {
1317
+ for (const root of [
1318
+ join2(home, ".agents", "skills", name),
1319
+ join2(claudeConfigDir, "skills", name)
1320
+ ]) {
1321
+ const installed = installedSkillFiles(root);
1322
+ if (installed.length !== files.length || installed.some((file, index2) => file !== files[index2])) {
1323
+ throw new Error(`installed file tree differs for ${name}`);
1324
+ }
1325
+ for (const file of files) {
1326
+ const publishedContent = expected.get(`${name}/${file}`);
1327
+ if (!publishedContent || !readFileSync2(join2(root, file)).equals(publishedContent)) {
1328
+ throw new Error(`installed content differs for ${name}/${file}`);
1329
+ }
1330
+ }
1331
+ }
1332
+ }
1333
+ }
1334
+ async function fetchSkillsUpdate(baseUrl, localVersion) {
1335
+ const controller = new AbortController();
1336
+ const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS2);
1337
+ try {
1338
+ const response = await fetch(new URL("/api/v2/cli/update-check", baseUrl), {
1339
+ method: "POST",
1340
+ headers: { "Content-Type": "application/json" },
1341
+ body: JSON.stringify({
1342
+ skills: {
1343
+ version: localVersion
1344
+ }
1345
+ }),
1346
+ signal: controller.signal
1347
+ });
1348
+ if (!response.ok) return null;
1349
+ const data = await response.json().catch(() => null);
1350
+ const skills = data?.skills;
1351
+ if (!skills) return null;
1352
+ return {
1353
+ needsUpdate: skills.needs_update === true,
1354
+ remoteVersion: typeof skills.remote?.version === "string" ? skills.remote.version.trim() : ""
1355
+ };
1356
+ } catch {
1357
+ return null;
1358
+ } finally {
1359
+ clearTimeout(timeout);
1360
+ }
1361
+ }
1362
+ function buildSkillsInstallArgs(baseUrl, skillNames = DEFAULT_SDK_SKILL_NAMES, agents) {
1363
+ return buildSkillsAddArgs(baseUrl, sortedUniqueSkillNames(skillNames), {
1364
+ agents
1365
+ });
1366
+ }
1367
+ function buildBunxSkillsInstallArgs(baseUrl, skillNames, agents) {
1368
+ return buildSkillsAddArgs(baseUrl, sortedUniqueSkillNames(skillNames), {
1369
+ firstArg: "--bun",
1370
+ agents
1371
+ });
1372
+ }
1373
+ var NEGATIVE_INSTALLER_PROBE_TTL_MS = 5 * 60 * 1e3;
1374
+ function probeInstallers(timeoutMs) {
1375
+ return {
1376
+ bunx: probeCommand("bunx", timeoutMs),
1377
+ npx: probeCommand("npx", timeoutMs)
1378
+ };
1379
+ }
1380
+ function noInstallerProbePath(baseUrl) {
1381
+ return join2(
1382
+ sdkCliStateDirPath(baseUrl),
1383
+ "skills-sync-no-installer-unknown.json"
1384
+ );
1385
+ }
1386
+ function dueUnknownInstallerProbes(baseUrl, remoteVersion) {
1387
+ const path = noInstallerProbePath(baseUrl);
1388
+ try {
1389
+ const cached = JSON.parse(readFileSync2(path, "utf-8"));
1390
+ if (cached.version === remoteVersion && typeof cached.checkedAt === "number" && cached.checkedAt <= Date.now() && Date.now() - cached.checkedAt < NEGATIVE_INSTALLER_PROBE_TTL_MS && (cached.probes?.bunx === "missing" || cached.probes?.bunx === "failed") && (cached.probes?.npx === "missing" || cached.probes?.npx === "failed")) {
1391
+ return cached.probes;
1392
+ }
1393
+ } catch {
1394
+ }
1395
+ const probes = probeInstallers(INSTALLER_PROBE_TIMEOUT_MS);
1396
+ if (probes.bunx !== "available" && probes.npx !== "available") {
1397
+ try {
1398
+ mkdirSync2(dirname2(path), { recursive: true });
1399
+ writeFileSync2(
1400
+ path,
1401
+ JSON.stringify({
1402
+ version: remoteVersion,
1403
+ checkedAt: Date.now(),
1404
+ probes
1405
+ }),
1406
+ "utf-8"
1407
+ );
1408
+ } catch {
1409
+ }
1410
+ }
1411
+ return probes;
1412
+ }
1413
+ function probeCommand(command, timeoutMs) {
1414
+ const plan = resolveShellSpawn(command, ["--version"]);
1415
+ const result = spawnSync(plan.command, plan.args, {
1416
+ stdio: "ignore",
1417
+ shell: plan.shell,
1418
+ timeout: timeoutMs
1419
+ });
1420
+ if (result.status === 0) return "available";
1421
+ return result.error?.code === "ENOENT" ? "missing" : "failed";
1422
+ }
1423
+ function hasCommand(command) {
1424
+ return probeCommand(command) === "available";
1425
+ }
1426
+ function shellQuote(arg) {
1427
+ return `'${arg.replace(/'/g, `'\\''`)}'`;
1428
+ }
1429
+ function temporarySkillsSyncSkipCommand() {
1430
+ if (process.platform === "win32") {
1431
+ return "set DEEPLINE_SKIP_SKILLS_SYNC=1 && deepline <command> (cmd), or $env:DEEPLINE_SKIP_SKILLS_SYNC='1'; deepline <command> (PowerShell)";
1432
+ }
1433
+ return "DEEPLINE_SKIP_SKILLS_SYNC=1 deepline <command>";
1434
+ }
1435
+ function resolveSkillsInstallSpawn(install, platform2 = process.platform) {
1436
+ return resolveShellSpawn(install.command, install.args, platform2);
1437
+ }
1438
+ function resolveSkillsInstallCommands(baseUrl, skillNames = DEFAULT_SDK_SKILL_NAMES, agents = DEFAULT_SKILL_AGENTS) {
1439
+ const commands = [];
1440
+ if (hasCommand("bunx")) {
1441
+ const bunxArgs = buildBunxSkillsInstallArgs(baseUrl, skillNames, agents);
1442
+ commands.push({
1443
+ command: "bunx",
1444
+ args: bunxArgs,
1445
+ manualCommand: `bunx ${bunxArgs.map(shellQuote).join(" ")}`
1446
+ });
1447
+ }
1448
+ if (hasCommand("npx")) {
1449
+ const npxArgs = buildSkillsInstallArgs(baseUrl, skillNames, agents);
1450
+ commands.push({
1451
+ command: "npx",
1452
+ args: npxArgs,
1453
+ manualCommand: `npx ${npxArgs.map(shellQuote).join(" ")}`
1454
+ });
1455
+ }
1456
+ return commands;
1457
+ }
1458
+ function runOneSkillsInstall(install) {
1459
+ return new Promise((resolve) => {
1460
+ const plan = resolveSkillsInstallSpawn(install);
1461
+ const child = spawn(plan.command, plan.args, {
1462
+ stdio: ["ignore", "ignore", "pipe"],
1463
+ env: process.env,
1464
+ shell: plan.shell
1465
+ });
1466
+ let stderr = "";
1467
+ child.stderr.on("data", (chunk) => {
1468
+ stderr += chunk.toString("utf-8");
1469
+ });
1470
+ child.on("error", (error) => {
1471
+ resolve({
1472
+ ok: false,
1473
+ detail: `failed to start ${install.command}: ${error.message}`,
1474
+ manualCommand: install.manualCommand,
1475
+ attempt: {
1476
+ installer: install.command,
1477
+ outcome: "spawn_error",
1478
+ exitCode: null
1479
+ }
1480
+ });
1481
+ });
1482
+ child.on("close", (code) => {
1483
+ if (code === 0) {
1484
+ resolve({ ok: true });
1485
+ return;
1486
+ }
1487
+ const detail = stderr.trim();
1488
+ resolve({
1489
+ ok: false,
1490
+ detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
1491
+ manualCommand: install.manualCommand,
1492
+ attempt: {
1493
+ installer: install.command,
1494
+ outcome: code === null ? "terminated" : "exit",
1495
+ exitCode: code
1496
+ }
1497
+ });
1498
+ });
1499
+ });
1500
+ }
1501
+ async function runSkillsInstall(installs, agents, notice = writeSdkSkillsStatusLine) {
1502
+ const failures = [];
1503
+ for (const install of installs) {
1504
+ const result = await runOneSkillsInstall(install);
1505
+ if (result.ok) return { installed: true };
1506
+ failures.push(result);
1507
+ }
1508
+ const details = failures.map((failure) => failure.detail).filter(Boolean).join("\n");
1509
+ const attemptedCommands = failures.map((failure) => ` ${failure.manualCommand}`).join("\n");
1510
+ const retryAgent = agents.length === 1 ? agents[0] : void 0;
1511
+ notice(
1512
+ `Deepline agent-skills refresh failed for ${agents.join(", ")}. The Deepline command will continue; this only affects optional local agent instructions, not authentication, data, or billing.
1513
+ ` + (attemptedCommands ? `Attempted installer command${failures.length === 1 ? "" : "s"}:
1514
+ ${attemptedCommands}
1515
+ ` : "") + (details ? `Installer output:
1516
+ ${details}
1517
+ ` : "") + (retryAgent ? `To retry with full installer output: deepline skills --agent ${retryAgent} --json
1518
+ ` : "") + `To temporarily suppress automatic skills sync: ${temporarySkillsSyncSkipCommand()}`
1519
+ );
1520
+ return {
1521
+ installed: false,
1522
+ attempts: failures.map(({ attempt }) => attempt)
1523
+ };
1524
+ }
1525
+ function runLegacySkillsCleanup(agents) {
1526
+ const candidates = hasCommand("bunx") ? [
1527
+ {
1528
+ command: "bunx",
1529
+ args: [
1530
+ "--bun",
1531
+ SKILLS_NPX_PACKAGE,
1532
+ "remove",
1533
+ "--global",
1534
+ "--agent",
1535
+ ...agents,
1536
+ "-y",
1537
+ ...LEGACY_SKILL_NAMES_TO_REMOVE
1538
+ ]
1539
+ },
1540
+ {
1541
+ command: "npx",
1542
+ args: [
1543
+ "--yes",
1544
+ SKILLS_NPX_PACKAGE,
1545
+ "remove",
1546
+ "--global",
1547
+ "--agent",
1548
+ ...agents,
1549
+ "-y",
1550
+ ...LEGACY_SKILL_NAMES_TO_REMOVE
1551
+ ]
1552
+ }
1553
+ ] : [
1554
+ {
1555
+ command: "npx",
1556
+ args: [
1557
+ "--yes",
1558
+ SKILLS_NPX_PACKAGE,
1559
+ "remove",
1560
+ "--global",
1561
+ "--agent",
1562
+ ...agents,
1563
+ "-y",
1564
+ ...LEGACY_SKILL_NAMES_TO_REMOVE
1565
+ ]
1566
+ }
1567
+ ];
1568
+ for (const candidate of candidates) {
1569
+ const plan = resolveShellSpawn(candidate.command, candidate.args);
1570
+ const result = spawnSync(plan.command, plan.args, {
1571
+ stdio: "ignore",
1572
+ env: process.env,
1573
+ shell: plan.shell
1574
+ });
1575
+ if (result.status === 0) return;
1576
+ }
1577
+ }
1578
+ function writeSdkSkillsStatusLine(line) {
1579
+ const progress = getActiveCliProgress();
1580
+ if (progress) {
1581
+ progress.writeLine(line);
1582
+ return;
1583
+ }
1584
+ process.stderr.write(`${line}
1585
+ `);
1586
+ }
1587
+ async function syncSdkSkillsIfNeeded(baseUrl, options = {}) {
1588
+ if (shouldSkipSkillsSync()) return;
1589
+ const usingPluginSkills = hasActivePluginSkills();
1590
+ if (usingPluginSkills) {
1591
+ return;
1592
+ }
1593
+ const agents = resolveAutoSyncSkillAgents();
1594
+ if (options.mode === "detached" && agents.length > 0) {
1595
+ flushDeferredSkillsNotices(baseUrl, agents);
1596
+ }
1597
+ if (options.mode === "detached" && options.update === void 0 && isDefaultAgentSet(agents)) {
1598
+ return;
1599
+ }
1600
+ const ownsInProgressMarker = options.mode !== "detached" && agents.length > 0 && Boolean(options.claim);
1601
+ const leases = ownsInProgressMarker ? readInProgressSkillsSyncLeases(baseUrl, agents) : null;
1602
+ if (leases && [...leases.values()].some((lease) => lease.owner !== options.claim)) {
1603
+ return;
1604
+ }
1605
+ if (leases) recordOwnPidOnInProgressSkillsSync(baseUrl, leases);
1606
+ const claimedVersion = leases ? claimedSkillsVersion(leases) : "";
1607
+ if (options.mode === "detached" && agents.length > 0 && options.update === void 0 && !claimedVersion) {
1608
+ const outcome = claimAndSpawnDetachedSkillsSync(
1609
+ baseUrl,
1610
+ agents,
1611
+ UNRESOLVED_SKILLS_CLAIM_VERSION,
1612
+ options.spawnDetached ?? spawnDetachedSkillsSyncChild
1613
+ );
1614
+ if (outcome !== "unavailable") return;
1615
+ }
1616
+ try {
1617
+ const localVersion = readSdkSkillsLocalVersion(baseUrl);
1618
+ const update = options.update === void 0 ? claimedVersion ? { needsUpdate: true, remoteVersion: claimedVersion } : await fetchSkillsUpdate(baseUrl, localVersion) : options.update ? {
1619
+ needsUpdate: options.update.needs_update,
1620
+ remoteVersion: options.update.remote.version
1621
+ } : null;
1622
+ if (!update?.needsUpdate || !update.remoteVersion) {
1623
+ return;
1624
+ }
1625
+ if (agents.length > 0 && hasFailedSkillsSync(baseUrl, update.remoteVersion, agents)) {
1626
+ return;
1627
+ }
1628
+ if (options.mode === "detached" && agents.length === 0) {
1629
+ writeUnavailableSkillsNotice(
1630
+ baseUrl,
1631
+ update.remoteVersion,
1632
+ buildSdkSkillNames(DEFAULT_SDK_SKILL_NAMES),
1633
+ "unsupported-agent"
1634
+ );
1635
+ return;
1636
+ }
1637
+ if (options.mode === "detached" && agents.length > 0) {
1638
+ if (isDefaultAgentSet(agents)) {
1639
+ if (agents.some(
1640
+ (agent) => isFreshInProgressSkillsSync(
1641
+ readInProgressSkillsSyncLease(baseUrl, agent)
1642
+ )
1643
+ )) {
1644
+ return;
1645
+ }
1646
+ const probes = dueUnknownInstallerProbes(baseUrl, update.remoteVersion);
1647
+ if (probes.bunx !== "available" && probes.npx !== "available") {
1648
+ writeUnavailableSkillsNotice(
1649
+ baseUrl,
1650
+ update.remoteVersion,
1651
+ buildSdkSkillNames(DEFAULT_SDK_SKILL_NAMES),
1652
+ "no-installer",
1653
+ writeSdkSkillsStatusLine,
1654
+ probes
1655
+ );
1656
+ return;
1657
+ }
1658
+ }
1659
+ const outcome = claimAndSpawnDetachedSkillsSync(
1660
+ baseUrl,
1661
+ agents,
1662
+ update.remoteVersion,
1663
+ options.spawnDetached ?? spawnDetachedSkillsSyncChild
1664
+ );
1665
+ if (outcome === "held") return;
1666
+ if (outcome === "claimed") {
1667
+ writeSdkSkillsStatusLine(
1668
+ "Deepline skills are out of date; updating in the background."
1669
+ );
1670
+ return;
1671
+ }
1672
+ if (isDefaultAgentSet(agents)) return;
1673
+ }
1674
+ await runInlineSkillsSync(
1675
+ baseUrl,
1676
+ update.remoteVersion,
1677
+ agents,
1678
+ options.notices ?? "print"
1679
+ );
1680
+ } finally {
1681
+ if (leases) {
1682
+ releaseInProgressSkillsSync(baseUrl, claimTokensOf(leases));
1683
+ }
1684
+ }
1685
+ }
1686
+ async function runInlineSkillsSync(baseUrl, remoteVersion, agents, notices) {
1687
+ const update = { remoteVersion };
1688
+ const notice = notices === "defer" ? deferSkillsNotice(baseUrl, agents) : writeSdkSkillsStatusLine;
1689
+ const index = await fetchV1SkillIndex(baseUrl);
1690
+ const remoteSkillNames = v1SkillNames(index);
1691
+ const skillNames = buildSdkSkillNames(
1692
+ remoteSkillNames.length > 0 ? remoteSkillNames : DEFAULT_SDK_SKILL_NAMES
1693
+ );
1694
+ if (skillNames.length === 0) return;
1695
+ if (agents.length === 0) {
1696
+ writeUnavailableSkillsNotice(
1697
+ baseUrl,
1698
+ update.remoteVersion,
1699
+ skillNames,
1700
+ "unsupported-agent",
1701
+ notice
1702
+ );
1703
+ return;
1704
+ }
1705
+ const installs = resolveSkillsInstallCommands(baseUrl, skillNames, agents);
1706
+ if (installs.length === 0) {
1707
+ writeUnavailableSkillsNotice(
1708
+ baseUrl,
1709
+ update.remoteVersion,
1710
+ skillNames,
1711
+ "no-installer",
1712
+ notice
1713
+ );
1714
+ return;
1715
+ }
1716
+ if (notices === "print") {
1717
+ writeSdkSkillsStatusLine(
1718
+ "Deepline skills changed; syncing agent skills..."
1719
+ );
1720
+ }
1721
+ const installResult = await runSkillsInstall(installs, agents, notice);
1722
+ if (!installResult.installed) {
1723
+ const markerWritten = markFailedSkillsSync(
1724
+ baseUrl,
1725
+ update.remoteVersion,
1726
+ agents
1727
+ );
1728
+ void maybeReportAutomaticSkillsSyncFailure({
1729
+ baseUrl,
1730
+ remoteSkillsVersion: update.remoteVersion,
1731
+ agents,
1732
+ skillsPackage: SKILLS_NPX_PACKAGE,
1733
+ targetSkillCount: skillNames.length,
1734
+ markerWritten,
1735
+ attempts: installResult.attempts
1736
+ });
1737
+ if (isDefaultAgentSet(agents)) {
1738
+ notice(
1739
+ `After fixing the installer, remove ${failedSkillsSyncPath(baseUrl, agents)} to retry this skills version automatically.`
1740
+ );
1741
+ }
1742
+ return;
1743
+ }
1744
+ if (isDefaultAgentSet(agents)) {
1745
+ try {
1746
+ await verifyDefaultAgentSkillsInstall(
1747
+ baseUrl,
1748
+ update.remoteVersion,
1749
+ skillNames,
1750
+ index
1751
+ );
1752
+ } catch (error) {
1753
+ const detail = error instanceof Error ? error.message : String(error);
1754
+ const publishedFailure = error instanceof PublishedSkillsVerificationError;
1755
+ const markerWritten = publishedFailure ? deferPublishedVerificationRetry(baseUrl, update.remoteVersion, agents) : markFailedSkillsSync(baseUrl, update.remoteVersion, agents);
1756
+ notice(
1757
+ publishedFailure ? `Deepline could not verify the published skills: ${detail}. The local version remains stale; it will retry automatically on a later command.` : `Deepline agent-skills refresh could not verify the five installed targets: ${detail}. The local version remains stale. After fixing the install, remove ${failedSkillsSyncPath(baseUrl, agents)} to retry this version automatically.`
1758
+ );
1759
+ void maybeReportAutomaticSkillsSyncFailure({
1760
+ baseUrl,
1761
+ remoteSkillsVersion: update.remoteVersion,
1762
+ agents,
1763
+ skillsPackage: SKILLS_NPX_PACKAGE,
1764
+ targetSkillCount: skillNames.length,
1765
+ markerWritten,
1766
+ attempts: []
1767
+ });
1768
+ return;
1769
+ }
1770
+ }
1771
+ runLegacySkillsCleanup(agents);
1772
+ if (!writeSdkSkillsLocalVersion(baseUrl, update.remoteVersion, agents)) {
1773
+ notice(
1774
+ "Deepline agent skills installed, but the local skills version could not be recorded. A later command will retry the refresh."
1775
+ );
1776
+ return;
1777
+ }
1778
+ clearUnavailableSkillsNotice(baseUrl);
1779
+ clearFailedSkillsSync(baseUrl, agents);
1780
+ notice("Deepline agent skills are up to date.");
1781
+ }
1782
+
1783
+ // src/cli/commands/skills.ts
1784
+ var SKILLS_INSTALL_LEASE_WAIT_MS = 9e4;
1785
+ var SKILLS_PROCESS_TIMEOUT_MS = 45e3;
1786
+ var RUNTIME_TO_SKILLS_AGENT = {
1787
+ antigravity: "antigravity",
1788
+ claude_code: "claude-code",
1789
+ claude_cowork: "claude-code",
1790
+ cline: "cline",
1791
+ codex: "codex",
1792
+ cursor: "cursor",
1793
+ gemini: "gemini-cli",
1794
+ windsurf: "windsurf"
1795
+ };
1796
+ var AGENT_MARKERS = [
1797
+ { agent: "codex", paths: [".codex"] },
1798
+ { agent: "claude-code", paths: [".claude"] },
1799
+ { agent: "cursor", paths: [".cursor"] },
1800
+ { agent: "gemini-cli", paths: [".gemini", ".gemini-cli"] },
1801
+ { agent: "antigravity", paths: [".antigravity"] }
1802
+ ];
1803
+ var WORKSPACE_SKILL_ROOTS_BY_AGENT = {
1804
+ antigravity: [".agents"],
1805
+ "claude-code": [".claude"],
1806
+ cline: [".agents"],
1807
+ codex: [".agents"],
1808
+ cursor: [".agents"],
1809
+ "gemini-cli": [".agents"],
1810
+ windsurf: [".windsurf"],
1811
+ "*": [".agents", ".claude", ".windsurf"]
1812
+ };
1813
+ function workspaceSkillRootsForAgents(agents) {
1814
+ return [
1815
+ ...new Set(
1816
+ agents.flatMap((agent) => WORKSPACE_SKILL_ROOTS_BY_AGENT[agent] ?? [])
1817
+ )
1818
+ ].sort((a, b) => a.localeCompare(b));
1819
+ }
1820
+ function normalizeScope(value) {
1821
+ if (value === "global") return "global";
1822
+ if (value === "local") return "local";
1823
+ if (!value) return inferActiveSkillsScope();
1824
+ throw new Error("--scope must be one of: global, local");
1825
+ }
1826
+ function inferActiveSkillsScope(entrypoint = process.argv[1] ?? "") {
1827
+ return /[\\/]\.deepline[\\/]runtime[\\/]/.test(entrypoint) ? "local" : "global";
1828
+ }
1829
+ function resolveLocalRoot() {
1830
+ const target = resolveProjectPinTarget();
1831
+ if (!target.ok) {
1832
+ throw new Error(
1833
+ `Cowork project folder is ambiguous. Candidate folders: ${target.candidates.join(
1834
+ ", "
1835
+ )}. Set CLAUDE_PROJECT_DIR or cd into the intended project folder.`
1836
+ );
1837
+ }
1838
+ return target.dir;
1839
+ }
1840
+ function detectSkillsAgents(input) {
1841
+ const runtime = input.runtime ?? detectAgentRuntime();
1842
+ const knownAgent = RUNTIME_TO_SKILLS_AGENT[runtime];
1843
+ if (knownAgent) return [knownAgent];
1844
+ const roots = [
1845
+ ...input.scope === "local" && input.root ? [input.root] : [],
1846
+ input.homeDir ?? homedir3()
1847
+ ];
1848
+ const detected = AGENT_MARKERS.filter(
1849
+ (marker) => roots.some(
1850
+ (root) => marker.paths.some((path) => existsSync3(join3(root, path)))
1851
+ )
1852
+ ).map((marker) => marker.agent);
1853
+ return detected.length > 0 ? detected : ["*"];
1854
+ }
1855
+ function skillsStatePathForScope(baseUrl, scope, root) {
1856
+ return scope === "local" && root ? join3(root, ".deepline", "setup", "skills.json") : join3(sdkCliStateDirPath(baseUrl), "skills-install.json");
1857
+ }
1858
+ function buildSkillsPlan(input) {
1859
+ const scopeArgs = input.scope === "global" ? ["--global"] : [];
1860
+ const legacyNames = [...LEGACY_SKILL_NAMES_TO_REMOVE].sort(
1861
+ (a, b) => a.localeCompare(b)
1862
+ );
1863
+ return {
1864
+ scope: input.scope,
1865
+ root: input.root,
1866
+ agents: input.agents,
1867
+ skillNames: input.skillNames,
1868
+ version: input.version,
1869
+ remove: {
1870
+ command: "npm",
1871
+ args: [
1872
+ "exec",
1873
+ "--yes",
1874
+ `--package=${SKILLS_NPX_PACKAGE}`,
1875
+ "--",
1876
+ "skills",
1877
+ "remove",
1878
+ ...scopeArgs,
1879
+ "--agent",
1880
+ ...input.agents,
1881
+ "-y",
1882
+ ...legacyNames
1883
+ ]
1884
+ },
1885
+ install: {
1886
+ command: "npm",
1887
+ args: [
1888
+ "exec",
1889
+ "--yes",
1890
+ `--package=${SKILLS_NPX_PACKAGE}`,
1891
+ "--",
1892
+ "skills",
1893
+ "add",
1894
+ skillsSourceUrl(input.baseUrl),
1895
+ "--agent",
1896
+ ...input.agents,
1897
+ ...scopeArgs,
1898
+ "--yes",
1899
+ ...input.skillNames.flatMap((name) => ["--skill", name]),
1900
+ "--full-depth"
1901
+ ]
1902
+ },
1903
+ statePath: skillsStatePathForScope(input.baseUrl, input.scope, input.root)
1904
+ };
1905
+ }
1906
+ function sortedStrings(value) {
1907
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
1908
+ return null;
1909
+ }
1910
+ return [...value].sort((a, b) => a.localeCompare(b));
1911
+ }
1912
+ function isSkillsPlanCurrent(plan, state) {
1913
+ if (!state || state.scope !== plan.scope || state.skillsVersion !== plan.version) {
1914
+ return false;
1915
+ }
1916
+ const installedAgents = sortedStrings(state.agents);
1917
+ const installedSkillNames = sortedStrings(state.skillNames);
1918
+ if (!installedAgents || !installedSkillNames) return false;
1919
+ return installedAgents.join("\0") === [...plan.agents].sort((a, b) => a.localeCompare(b)).join("\0") && installedSkillNames.join("\0") === [...plan.skillNames].sort((a, b) => a.localeCompare(b)).join("\0");
1920
+ }
1921
+ function readSkillsInstallState(path) {
1922
+ try {
1923
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
1924
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
1925
+ } catch {
1926
+ return null;
1927
+ }
1928
+ }
1929
+ function runProcess(command, args, cwd) {
1930
+ return new Promise((resolve, reject) => {
1931
+ const plan = resolveShellSpawn(command, args);
1932
+ const child = spawn2(plan.command, plan.args, {
1933
+ cwd,
1934
+ env: process.env,
1935
+ stdio: ["ignore", "ignore", "pipe"],
1936
+ shell: plan.shell
1937
+ });
1938
+ let stderr = "";
1939
+ child.stderr.on("data", (chunk) => {
1940
+ stderr += chunk.toString("utf8");
1941
+ process.stderr.write(chunk);
1942
+ });
1943
+ const timeout = setTimeout(() => {
1944
+ child.kill("SIGTERM");
1945
+ reject(
1946
+ new Error(
1947
+ `${SKILLS_NPX_PACKAGE} timed out after ${SKILLS_PROCESS_TIMEOUT_MS}ms.`
1948
+ )
1949
+ );
1950
+ }, SKILLS_PROCESS_TIMEOUT_MS);
1951
+ child.once("error", (error) => {
1952
+ clearTimeout(timeout);
1953
+ reject(error);
1954
+ });
1955
+ child.once("close", (code) => {
1956
+ clearTimeout(timeout);
1957
+ if (code && stderr.trim()) {
1958
+ process.stderr.write(`${SKILLS_NPX_PACKAGE} exited ${code}.
1959
+ `);
1960
+ }
1961
+ resolve(code ?? 1);
1962
+ });
1963
+ });
1964
+ }
1965
+ async function runSkillsCommand(options, dependencies = {}) {
1966
+ let scope;
1967
+ let root;
1968
+ try {
1969
+ scope = normalizeScope(options.scope);
1970
+ root = scope === "local" ? resolveLocalRoot() : null;
1971
+ } catch (error) {
1972
+ printCommandEnvelope(
1973
+ {
1974
+ ok: false,
1975
+ status: "failed",
1976
+ code: "INVALID_SKILLS_SCOPE",
1977
+ exitCode: 2,
1978
+ message: error instanceof Error ? error.message : String(error)
1979
+ },
1980
+ { json: options.json }
1981
+ );
1982
+ return 2;
1983
+ }
1984
+ const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
1985
+ const catalog = {
1986
+ skillNames: [...DEFAULT_SDK_SKILL_NAMES],
1987
+ version: SDK_VERSION
1988
+ };
1989
+ const agents = options.agent ? [options.agent] : detectSkillsAgents({ scope, root });
1990
+ const plan = buildSkillsPlan({ baseUrl, scope, root, agents, ...catalog });
1991
+ if (options.dryRun) {
1992
+ printCommandEnvelope(
1993
+ { ok: true, status: "planned", dryRun: true, ...plan },
1994
+ { json: options.json }
1995
+ );
1996
+ return 0;
1997
+ }
1998
+ if (isSkillsPlanCurrent(plan, readSkillsInstallState(plan.statePath)) && !hasFailedAutomaticSkillsSync(baseUrl, agents)) {
1999
+ printCommandEnvelope(
2000
+ {
2001
+ ok: true,
2002
+ status: "current",
2003
+ complete: true,
2004
+ changed: false,
2005
+ skipped: true,
2006
+ skipReason: "skills_version_current",
2007
+ scope,
2008
+ agents,
2009
+ skillsVersion: plan.version,
2010
+ skillCount: plan.skillNames.length,
2011
+ statePath: plan.statePath,
2012
+ render: {
2013
+ sections: [
2014
+ {
2015
+ title: "skills",
2016
+ lines: [
2017
+ `Scope: ${scope}`,
2018
+ `Agents: ${agents.join(", ")}`,
2019
+ `Current: ${plan.skillNames.length}`
2020
+ ]
2021
+ }
2022
+ ]
2023
+ }
2024
+ },
2025
+ { json: options.json }
2026
+ );
2027
+ return 0;
2028
+ }
2029
+ if (scope === "local" && root) {
2030
+ const managedNames = [
2031
+ .../* @__PURE__ */ new Set([...plan.skillNames, ...LEGACY_SKILL_NAMES_TO_REMOVE])
2032
+ ];
2033
+ const agentRoots = workspaceSkillRootsForAgents(plan.agents);
2034
+ ensureProjectPrivatePathsIgnored(root, [
2035
+ ".deepline/",
2036
+ ...agentRoots.flatMap(
2037
+ (agentRoot) => managedNames.map((name) => `${agentRoot}/skills/${name}/`)
2038
+ )
2039
+ ]);
2040
+ }
2041
+ const lease = scope === "global" ? await acquireSkillsInstallLease(baseUrl, agents, plan.version, {
2042
+ waitMs: dependencies.installLeaseWaitMs ?? SKILLS_INSTALL_LEASE_WAIT_MS,
2043
+ onWait: () => process.stderr.write(
2044
+ "Waiting for a background Deepline skills refresh to finish...\n"
2045
+ )
2046
+ }) : { release: () => {
2047
+ } };
2048
+ if (!lease) {
2049
+ printCommandEnvelope(
2050
+ {
2051
+ ok: false,
2052
+ status: "failed",
2053
+ code: "SKILLS_SYNC_IN_PROGRESS",
2054
+ exitCode: 7,
2055
+ scope,
2056
+ agents,
2057
+ message: "A background Deepline skills refresh is still running for these agents.",
2058
+ next: `Retry in a moment: deepline skills --scope ${scope} --json. If no refresh is running, remove ${skillsInstallLeasePath(baseUrl, agents)}`
2059
+ },
2060
+ { json: options.json }
2061
+ );
2062
+ return 7;
2063
+ }
2064
+ try {
2065
+ return await runSkillsInstallWithLease({
2066
+ options,
2067
+ dependencies,
2068
+ baseUrl,
2069
+ scope,
2070
+ root,
2071
+ agents,
2072
+ plan
2073
+ });
2074
+ } finally {
2075
+ lease.release();
2076
+ }
2077
+ }
2078
+ async function runSkillsInstallWithLease(input) {
2079
+ const { options, dependencies, baseUrl, scope, root, agents, plan } = input;
2080
+ process.stderr.write(
2081
+ `Replacing Deepline skills for ${agents.join(", ")} (${scope})...
2082
+ `
2083
+ );
2084
+ const execute = dependencies.runProcess ?? runProcess;
2085
+ try {
2086
+ const installCode = await execute(
2087
+ plan.install.command,
2088
+ plan.install.args,
2089
+ root ?? void 0
2090
+ );
2091
+ if (installCode !== 0) {
2092
+ throw new Error("Could not install the current Deepline skills.");
2093
+ }
2094
+ } catch (error) {
2095
+ printCommandEnvelope(
2096
+ {
2097
+ ok: false,
2098
+ status: "failed",
2099
+ code: "SKILLS_INSTALL_FAILED",
2100
+ exitCode: 5,
2101
+ scope,
2102
+ agents,
2103
+ message: error instanceof Error ? error.message : String(error),
2104
+ next: `deepline skills --scope ${scope} --json`
2105
+ },
2106
+ { json: options.json }
2107
+ );
2108
+ return 5;
2109
+ }
2110
+ try {
2111
+ const removeCode = await execute(
2112
+ plan.remove.command,
2113
+ plan.remove.args,
2114
+ root ?? void 0
2115
+ );
2116
+ if (removeCode !== 0) {
2117
+ process.stderr.write(
2118
+ "Current Deepline skills installed, but legacy skill cleanup failed. The installed skills remain usable.\n"
2119
+ );
2120
+ }
2121
+ } catch (error) {
2122
+ process.stderr.write(
2123
+ `Current Deepline skills installed, but legacy skill cleanup failed: ${error instanceof Error ? error.message : String(error)}. The installed skills remain usable.
2124
+ `
2125
+ );
2126
+ }
2127
+ mkdirSync3(dirname3(plan.statePath), { recursive: true });
2128
+ writeFileSync3(
2129
+ plan.statePath,
2130
+ `${JSON.stringify(
2131
+ {
2132
+ schemaVersion: 1,
2133
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
2134
+ cliVersion: SDK_VERSION,
2135
+ scope,
2136
+ agents,
2137
+ skillsVersion: plan.version,
2138
+ skillNames: plan.skillNames
2139
+ },
2140
+ null,
2141
+ 2
2142
+ )}
2143
+ `,
2144
+ "utf8"
2145
+ );
2146
+ if (scope === "global") {
2147
+ clearFailedAutomaticSkillsSync(baseUrl, agents);
2148
+ }
2149
+ printCommandEnvelope(
2150
+ {
2151
+ ok: true,
2152
+ status: "complete",
2153
+ scope,
2154
+ agents,
2155
+ skillsVersion: plan.version,
2156
+ skillCount: plan.skillNames.length,
2157
+ statePath: plan.statePath,
2158
+ render: {
2159
+ sections: [
2160
+ {
2161
+ title: "skills",
2162
+ lines: [
2163
+ `Scope: ${scope}`,
2164
+ `Agents: ${agents.join(", ")}`,
2165
+ `Installed: ${plan.skillNames.length}`
2166
+ ]
2167
+ }
2168
+ ]
2169
+ }
2170
+ },
2171
+ { json: options.json }
2172
+ );
2173
+ return 0;
2174
+ }
2175
+ function registerSkillsCommand(program) {
2176
+ 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(
2177
+ "after",
2178
+ `
2179
+ Notes:
2180
+ This command reinstalls current Deepline-managed skills using ${SKILLS_NPX_PACKAGE},
2181
+ then removes legacy Deepline skill names. Local scope writes into the resolved
2182
+ persistent project.
2183
+
2184
+ Examples:
2185
+ deepline skills --json
2186
+ deepline skills --scope local --json
2187
+ deepline skills --agent codex --dry-run --json
2188
+ `
2189
+ ).action(async (options) => {
2190
+ process.exitCode = await runSkillsCommand(options);
2191
+ });
2192
+ program.command(SKILLS_AUTO_SYNC_COMMAND, { hidden: true }).description("Refresh Deepline agent skills in the background.").option(
2193
+ "--claim <token>",
2194
+ "Owner token of the leases this child was started for."
2195
+ ).action(async (options) => {
2196
+ const claim = options.claim?.trim();
2197
+ if (!claim) {
2198
+ printCommandEnvelope(
2199
+ {
2200
+ ok: false,
2201
+ status: "failed",
2202
+ code: "SKILLS_AUTO_SYNC_CLAIM_REQUIRED",
2203
+ exitCode: 2,
2204
+ message: "skills-auto-sync is started by ordinary deepline commands and requires the lease token they pass; run `deepline skills` to install skills yourself."
2205
+ },
2206
+ { json: true }
2207
+ );
2208
+ process.exitCode = 2;
2209
+ return;
2210
+ }
2211
+ await syncSdkSkillsIfNeeded(autoDetectBaseUrl().replace(/\/$/, ""), {
2212
+ mode: "inline",
2213
+ notices: "defer",
2214
+ claim
2215
+ });
2216
+ });
2217
+ }
2218
+
2219
+ export {
2220
+ SDK_COMPATIBILITY_FAST_DEADLINE_MS,
2221
+ readFreshCleanCompatibility,
2222
+ readTrustedCleanCompatibility,
2223
+ checkSdkCompatibility,
2224
+ isCiEnvironment,
2225
+ enforceSdkCompatibilityResponse,
2226
+ resolveShellSpawn,
2227
+ resolveSdkCliFailureExitCode,
2228
+ maybeReportSdkCliFailure,
2229
+ flushDeferredSkillsSyncNotices,
2230
+ SKILLS_AUTO_SYNC_COMMAND,
2231
+ syncSdkSkillsIfNeeded,
2232
+ workspaceSkillRootsForAgents,
2233
+ inferActiveSkillsScope,
2234
+ detectSkillsAgents,
2235
+ skillsStatePathForScope,
2236
+ buildSkillsPlan,
2237
+ isSkillsPlanCurrent,
2238
+ runSkillsCommand,
2239
+ registerSkillsCommand
2240
+ };