deepline 0.1.105 → 0.1.107

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
@@ -241,12 +241,23 @@ var SDK_RELEASE = {
241
241
  // 0.1.104 ships postgres_fast suspension/billing parity and runtime worker hardening.
242
242
  // 0.1.105 ships the billing catalog surface: billing plans, subscribe,
243
243
  // subscription status/cancel, invoices, and the client.billing namespace.
244
- version: "0.1.105",
244
+ // 0.1.106 ships play cell provenance metadata and v2 preview retry hardening.
245
+ // 0.1.107 ships the v2 quickstart command, the deepline-plays-quickstart
246
+ // skill on the sdk sync surface, and the people-search-to-email prebuilt.
247
+ version: "0.1.107",
245
248
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
246
249
  supportPolicy: {
247
- latest: "0.1.105",
250
+ latest: "0.1.107",
248
251
  minimumSupported: "0.1.53",
249
- deprecatedBelow: "0.1.53"
252
+ deprecatedBelow: "0.1.53",
253
+ commandMinimumSupported: [
254
+ {
255
+ command: "enrich",
256
+ minimumSupported: "0.1.106",
257
+ reason: "Older SDK CLI enrich generated stale play source for the current dataset API."
258
+ }
259
+ ],
260
+ autoUpdatePatchLag: 2
250
261
  }
251
262
  };
252
263
 
@@ -3168,7 +3179,7 @@ function shouldSkipCompatibilityCheck() {
3168
3179
  const value = process.env.DEEPLINE_SKIP_SDK_COMPAT_CHECK?.trim().toLowerCase();
3169
3180
  return value === "1" || value === "true" || value === "yes";
3170
3181
  }
3171
- async function checkSdkCompatibility(baseUrl) {
3182
+ async function checkSdkCompatibility(baseUrl, options = {}) {
3172
3183
  if (shouldSkipCompatibilityCheck()) {
3173
3184
  return { response: null, error: null };
3174
3185
  }
@@ -3177,6 +3188,9 @@ async function checkSdkCompatibility(baseUrl) {
3177
3188
  try {
3178
3189
  const url = new URL("/api/v2/sdk/compat", baseUrl);
3179
3190
  url.searchParams.set("version", SDK_VERSION);
3191
+ if (options.command?.trim()) {
3192
+ url.searchParams.set("command", options.command.trim());
3193
+ }
3180
3194
  const response = await fetch(url, {
3181
3195
  method: "GET",
3182
3196
  headers: {
@@ -3197,9 +3211,8 @@ async function checkSdkCompatibility(baseUrl) {
3197
3211
  clearTimeout(timeout);
3198
3212
  }
3199
3213
  }
3200
- async function enforceSdkCompatibility(baseUrl) {
3201
- const { response, error } = await checkSdkCompatibility(baseUrl);
3202
- if (error || !response) {
3214
+ function enforceSdkCompatibilityResponse(response) {
3215
+ if (!response) {
3203
3216
  return;
3204
3217
  }
3205
3218
  if (response.update_required) {
@@ -12271,8 +12284,13 @@ function exportableSheetRow(row) {
12271
12284
  function mergeExportColumns(preferredColumns, rows) {
12272
12285
  const columns = [];
12273
12286
  const seen = /* @__PURE__ */ new Set();
12287
+ const hasActualColumn = (column) => rows.some((row) => Object.prototype.hasOwnProperty.call(row, column));
12288
+ const hasCanonicalReplacement = (column) => {
12289
+ const canonical = sqlSafeExportColumnName(column);
12290
+ return canonical !== column && hasActualColumn(canonical);
12291
+ };
12274
12292
  for (const column of preferredColumns) {
12275
- if (!column || seen.has(column)) {
12293
+ if (!column || seen.has(column) || hasCanonicalReplacement(column)) {
12276
12294
  continue;
12277
12295
  }
12278
12296
  seen.add(column);
@@ -12280,7 +12298,7 @@ function mergeExportColumns(preferredColumns, rows) {
12280
12298
  }
12281
12299
  for (const row of rows) {
12282
12300
  for (const column of Object.keys(row)) {
12283
- if (seen.has(column)) {
12301
+ if (seen.has(column) || hasCanonicalReplacement(column)) {
12284
12302
  continue;
12285
12303
  }
12286
12304
  seen.add(column);
@@ -12289,6 +12307,11 @@ function mergeExportColumns(preferredColumns, rows) {
12289
12307
  }
12290
12308
  return columns;
12291
12309
  }
12310
+ function sqlSafeExportColumnName(id) {
12311
+ const normalized = id.trim().replace(/\.+/g, "__").replace(/[^A-Za-z0-9_]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
12312
+ const safe = normalized || "column";
12313
+ return /^[A-Za-z_]/.test(safe) ? safe : `c_${safe}`;
12314
+ }
12292
12315
  async function fetchBackingDatasetRows(input2) {
12293
12316
  const playName = extractRunPlayName(input2.status);
12294
12317
  const tableNamespace = input2.rowsInfo.tableNamespace?.trim();
@@ -17940,6 +17963,244 @@ Examples:
17940
17963
  ).option("--org-id <id>", "Switch using an explicit organization id").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgSwitch);
17941
17964
  }
17942
17965
 
17966
+ // src/cli/commands/quickstart.ts
17967
+ var import_node_child_process = require("child_process");
17968
+ var import_node_crypto4 = require("crypto");
17969
+ var import_node_http = require("http");
17970
+ var EXIT_OK2 = 0;
17971
+ var EXIT_AUTH2 = 1;
17972
+ var EXIT_SERVER3 = 2;
17973
+ var MAX_PROMPT_LENGTH = 8e3;
17974
+ var MAX_BODY_BYTES = 64 * 1024;
17975
+ function shellQuote(arg) {
17976
+ return `'${arg.replace(/'/g, `'\\''`)}'`;
17977
+ }
17978
+ function hasClaudeBinary() {
17979
+ try {
17980
+ const result = (0, import_node_child_process.spawnSync)("claude", ["--version"], {
17981
+ stdio: "ignore",
17982
+ shell: process.platform === "win32"
17983
+ });
17984
+ return result.status === 0;
17985
+ } catch {
17986
+ return false;
17987
+ }
17988
+ }
17989
+ function launchClaude(prompt) {
17990
+ return new Promise((resolve16) => {
17991
+ const child = (0, import_node_child_process.spawn)("claude", [prompt], {
17992
+ stdio: "inherit",
17993
+ shell: process.platform === "win32"
17994
+ });
17995
+ child.on("error", () => resolve16(EXIT_SERVER3));
17996
+ child.on("close", (status) => resolve16(status ?? EXIT_OK2));
17997
+ });
17998
+ }
17999
+ function readBody(req) {
18000
+ return new Promise((resolve16, reject) => {
18001
+ let raw = "";
18002
+ req.setEncoding("utf8");
18003
+ req.on("data", (chunk) => {
18004
+ raw += chunk;
18005
+ if (raw.length > MAX_BODY_BYTES) {
18006
+ reject(new Error("Request body too large."));
18007
+ req.destroy();
18008
+ }
18009
+ });
18010
+ req.on("end", () => resolve16(raw));
18011
+ req.on("error", reject);
18012
+ });
18013
+ }
18014
+ function writeJson(res, status, payload) {
18015
+ res.writeHead(status, { "content-type": "application/json" });
18016
+ res.end(JSON.stringify(payload));
18017
+ }
18018
+ function startCallbackServer(input2) {
18019
+ const server = (0, import_node_http.createServer)((req, res) => {
18020
+ res.setHeader("Access-Control-Allow-Origin", req.headers.origin ?? "*");
18021
+ res.setHeader("Vary", "Origin");
18022
+ res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
18023
+ res.setHeader("Access-Control-Allow-Headers", "content-type");
18024
+ res.setHeader("Access-Control-Allow-Private-Network", "true");
18025
+ res.setHeader("Access-Control-Max-Age", "600");
18026
+ if (req.method === "OPTIONS") {
18027
+ res.writeHead(204);
18028
+ res.end();
18029
+ return;
18030
+ }
18031
+ if (req.method !== "POST" || req.url !== "/submit") {
18032
+ writeJson(res, 404, { error: "Not found." });
18033
+ return;
18034
+ }
18035
+ void readBody(req).then((raw) => {
18036
+ let body = null;
18037
+ try {
18038
+ body = JSON.parse(raw);
18039
+ } catch {
18040
+ body = null;
18041
+ }
18042
+ const state = typeof body?.state === "string" ? body.state : "";
18043
+ const prompt = typeof body?.prompt === "string" ? body.prompt.trim() : "";
18044
+ const workflowId = typeof body?.workflow_id === "string" && body.workflow_id.trim() ? body.workflow_id.trim() : null;
18045
+ if (!state || state !== input2.state) {
18046
+ writeJson(res, 403, { error: "Invalid quickstart state token." });
18047
+ return;
18048
+ }
18049
+ if (!prompt) {
18050
+ writeJson(res, 400, { error: "prompt is required." });
18051
+ return;
18052
+ }
18053
+ if (prompt.length > MAX_PROMPT_LENGTH) {
18054
+ writeJson(res, 400, {
18055
+ error: `prompt exceeds ${MAX_PROMPT_LENGTH} characters.`
18056
+ });
18057
+ return;
18058
+ }
18059
+ writeJson(res, 200, { ok: true });
18060
+ setImmediate(() => input2.onSelection({ prompt, workflowId }));
18061
+ }).catch(() => {
18062
+ writeJson(res, 400, { error: "Invalid request body." });
18063
+ });
18064
+ });
18065
+ return new Promise((resolve16, reject) => {
18066
+ server.once("error", reject);
18067
+ server.listen(0, "127.0.0.1", () => {
18068
+ const address = server.address();
18069
+ if (!address || typeof address === "string") {
18070
+ reject(new Error("Failed to bind quickstart callback server."));
18071
+ return;
18072
+ }
18073
+ resolve16({ server, port: address.port });
18074
+ });
18075
+ });
18076
+ }
18077
+ async function handleQuickstart(options) {
18078
+ const jsonOutput = shouldEmitJson(options.json === true);
18079
+ const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
18080
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
18081
+ let apiKey = resolveApiKeyForBaseUrl(baseUrl);
18082
+ if (!apiKey) {
18083
+ if (interactive) {
18084
+ console.error("Not connected yet \u2014 registering this device first.");
18085
+ const registerExit = await handleRegister([]);
18086
+ if (registerExit !== EXIT_OK2) return registerExit;
18087
+ apiKey = resolveApiKeyForBaseUrl(baseUrl);
18088
+ }
18089
+ if (!apiKey) {
18090
+ console.error("Not connected. Run: deepline auth register");
18091
+ return EXIT_AUTH2;
18092
+ }
18093
+ }
18094
+ const state = (0, import_node_crypto4.randomBytes)(32).toString("hex");
18095
+ let resolveSelection;
18096
+ const selectionPromise = new Promise((resolve16) => {
18097
+ resolveSelection = resolve16;
18098
+ });
18099
+ let callback;
18100
+ try {
18101
+ callback = await startCallbackServer({
18102
+ state,
18103
+ onSelection: (selection) => resolveSelection(selection)
18104
+ });
18105
+ } catch (error) {
18106
+ console.error(
18107
+ `Could not start the local quickstart listener: ${error instanceof Error ? error.message : String(error)}`
18108
+ );
18109
+ return EXIT_SERVER3;
18110
+ }
18111
+ const quickstartUrl = `${baseUrl}/cli/quickstart?cb=${callback.port}&state=${state}`;
18112
+ console.error(" Opening the recipe picker in your browser.");
18113
+ console.error(` If it didn't open, cmd+click: ${quickstartUrl}`);
18114
+ if (options.open !== false) {
18115
+ openInBrowser(quickstartUrl);
18116
+ }
18117
+ const timeoutSeconds = Number.parseInt(options.timeout ?? "300", 10);
18118
+ const timeoutMs = (Number.isFinite(timeoutSeconds) && timeoutSeconds > 0 ? timeoutSeconds : 300) * 1e3;
18119
+ const timeoutHandle = setTimeout(
18120
+ () => resolveSelection("timeout"),
18121
+ timeoutMs
18122
+ );
18123
+ const progress = createCliProgress(!jsonOutput);
18124
+ progress.phase("waiting for your pick in the browser (Ctrl+C to skip)");
18125
+ const onSigint = () => resolveSelection("sigint");
18126
+ process.once("SIGINT", onSigint);
18127
+ const outcome = await selectionPromise;
18128
+ clearTimeout(timeoutHandle);
18129
+ process.removeListener("SIGINT", onSigint);
18130
+ callback.server.close();
18131
+ if (outcome === "sigint") {
18132
+ progress.fail();
18133
+ console.error("\nSkipped \u2014 run `deepline quickstart` anytime.");
18134
+ return EXIT_OK2;
18135
+ }
18136
+ if (outcome === "timeout") {
18137
+ progress.fail();
18138
+ console.error(
18139
+ "Timed out waiting for a selection. Run: deepline quickstart"
18140
+ );
18141
+ return EXIT_AUTH2;
18142
+ }
18143
+ progress.complete();
18144
+ const { prompt, workflowId } = outcome;
18145
+ const claudeCommand = `claude ${shellQuote(prompt)}`;
18146
+ const claudeAvailable = hasClaudeBinary();
18147
+ const willLaunch = options.launch !== false && !jsonOutput && interactive && claudeAvailable;
18148
+ printCommandEnvelope(
18149
+ {
18150
+ status: "submitted",
18151
+ workflowId,
18152
+ prompt,
18153
+ claudeCommand,
18154
+ url: quickstartUrl,
18155
+ launched: willLaunch,
18156
+ render: {
18157
+ sections: [
18158
+ {
18159
+ title: "quickstart",
18160
+ lines: [
18161
+ ...workflowId ? [`Recipe: ${workflowId}`] : [],
18162
+ willLaunch ? "Launching Claude Code with your recipe..." : claudeAvailable ? "Run the command below to start your recipe." : "Claude Code not found. Install it (https://code.claude.com/docs/en/overview), then run the command below."
18163
+ ]
18164
+ }
18165
+ ],
18166
+ actions: [{ label: "Run", command: claudeCommand }]
18167
+ }
18168
+ },
18169
+ { json: jsonOutput }
18170
+ );
18171
+ if (willLaunch) {
18172
+ return launchClaude(prompt);
18173
+ }
18174
+ return EXIT_OK2;
18175
+ }
18176
+ function registerQuickstartCommands(program) {
18177
+ program.command("quickstart").description(
18178
+ "Pick a starter recipe in the browser and launch it with Claude Code."
18179
+ ).addHelpText(
18180
+ "after",
18181
+ `
18182
+ Notes:
18183
+ Opens the hosted recipe picker in your browser. The picker sends your
18184
+ selection back to a temporary listener on 127.0.0.1, so the browser must run
18185
+ on the same machine as the CLI. Once a recipe arrives, the CLI prints the
18186
+ matching claude command and, when Claude Code is installed and the shell is
18187
+ interactive, launches it directly. Press Ctrl+C while waiting to skip.
18188
+
18189
+ Examples:
18190
+ deepline quickstart
18191
+ deepline quickstart --no-open
18192
+ deepline quickstart --no-launch --json
18193
+ deepline quickstart --timeout 120
18194
+ `
18195
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--no-open", "Do not open the browser; print the URL only").option("--no-launch", "Print the claude command instead of launching it").option(
18196
+ "--timeout <seconds>",
18197
+ "Maximum seconds to wait for a selection",
18198
+ "300"
18199
+ ).action(async (options) => {
18200
+ process.exitCode = await handleQuickstart(options);
18201
+ });
18202
+ }
18203
+
17943
18204
  // src/cli/commands/secrets.ts
17944
18205
  var import_node_process = require("process");
17945
18206
  var hiddenInputBuffer = "";
@@ -18157,7 +18418,7 @@ var import_node_fs11 = require("fs");
18157
18418
  var import_node_os8 = require("os");
18158
18419
  var import_node_path14 = require("path");
18159
18420
  var import_node_zlib = require("zlib");
18160
- var import_node_crypto4 = require("crypto");
18421
+ var import_node_crypto5 = require("crypto");
18161
18422
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
18162
18423
  var MAX_SESSION_UPLOAD_BYTES = 35e5;
18163
18424
  var MAX_DIRECT_SESSION_DECODED_BYTES = 50 * 1024 * 1024;
@@ -18420,7 +18681,7 @@ async function uploadPayload(path, payload) {
18420
18681
  return await http.post(path, payload);
18421
18682
  }
18422
18683
  async function uploadChunkedSessions(sessions, options) {
18423
- const uploadId = (0, import_node_crypto4.randomUUID)();
18684
+ const uploadId = (0, import_node_crypto5.randomUUID)();
18424
18685
  for (const session of sessions) {
18425
18686
  const bytes = Buffer.from(session.encodedContent, "base64");
18426
18687
  const chunks = [];
@@ -19944,7 +20205,7 @@ function parseExecuteOptions(args) {
19944
20205
  function safeFileStem(value) {
19945
20206
  return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
19946
20207
  }
19947
- function shellQuote(value) {
20208
+ function shellQuote2(value) {
19948
20209
  return `'${value.replace(/'/g, `'\\''`)}'`;
19949
20210
  }
19950
20211
  function powerShellQuote(value) {
@@ -19996,7 +20257,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
19996
20257
  path: scriptPath,
19997
20258
  sourceCode: script,
19998
20259
  projectDir,
19999
- macCopyCommand: `mkdir -p ${shellQuote(projectDir)} && cp ${shellQuote(scriptPath)} ${shellQuote(`${projectDir}/${fileName}`)}`,
20260
+ macCopyCommand: `mkdir -p ${shellQuote2(projectDir)} && cp ${shellQuote2(scriptPath)} ${shellQuote2(`${projectDir}/${fileName}`)}`,
20000
20261
  windowsCopyCommand: `New-Item -ItemType Directory -Force -Path ${powerShellQuote(projectDir.replace(/\//g, "\\"))} | Out-Null; Copy-Item -LiteralPath ${powerShellQuote(scriptPath)} -Destination ${powerShellQuote(`${projectDir.replace(/\//g, "\\")}\\${fileName}`)}`
20001
20262
  };
20002
20263
  }
@@ -20015,7 +20276,7 @@ function buildToolExecuteBaseEnvelope(input2) {
20015
20276
  summary: input2.summary
20016
20277
  };
20017
20278
  const envelopeHasCanonicalOutput = isRecord7(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
20018
- const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote(JSON.stringify(input2.params))} --json`;
20279
+ const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote2(JSON.stringify(input2.params))} --json`;
20019
20280
  const actions = input2.listConversion ? [
20020
20281
  {
20021
20282
  label: "next",
@@ -20249,7 +20510,7 @@ var import_promises6 = require("fs/promises");
20249
20510
  var import_node_path17 = require("path");
20250
20511
 
20251
20512
  // src/cli/workflow-to-play.ts
20252
- var import_node_crypto5 = require("crypto");
20513
+ var import_node_crypto6 = require("crypto");
20253
20514
  var HITL_WAIT_FOR_SIGNAL_TOOL = "deepline_workflow_wait_for_signal";
20254
20515
  var HITL_SLACK_TOOL = "slack_message_with_hitl";
20255
20516
  var SUB_WORKFLOW_TOOL_PREFIX = "deepline_workflow_";
@@ -20355,7 +20616,7 @@ function sanitizePlayNameSegment(value) {
20355
20616
  }
20356
20617
  function deriveWorkflowPlayName(workflowName) {
20357
20618
  const base = sanitizePlayNameSegment(workflowName) || "workflow";
20358
- const suffix = (0, import_node_crypto5.createHash)("sha256").update(workflowName).digest("hex").slice(0, 8);
20619
+ const suffix = (0, import_node_crypto6.createHash)("sha256").update(workflowName).digest("hex").slice(0, 8);
20359
20620
  const reserved = suffix.length + 1;
20360
20621
  const allowedBase = Math.max(1, MAX_PLAY_NAME_LENGTH - reserved);
20361
20622
  let name = `${base.slice(0, allowedBase)}_${suffix}`;
@@ -20734,7 +20995,7 @@ Notes:
20734
20995
  }
20735
20996
 
20736
20997
  // src/cli/commands/update.ts
20737
- var import_node_child_process = require("child_process");
20998
+ var import_node_child_process2 = require("child_process");
20738
20999
  var import_node_fs14 = require("fs");
20739
21000
  var import_node_path18 = require("path");
20740
21001
  function posixShellQuote(value) {
@@ -20743,14 +21004,14 @@ function posixShellQuote(value) {
20743
21004
  function windowsCmdQuote(value) {
20744
21005
  return `"${value.replace(/"/g, '""')}"`;
20745
21006
  }
20746
- function shellQuote2(value) {
21007
+ function shellQuote3(value) {
20747
21008
  if (process.platform === "win32") {
20748
21009
  return /^[A-Za-z0-9_./:@%+=,-]+$/.test(value) ? value : windowsCmdQuote(value);
20749
21010
  }
20750
21011
  return posixShellQuote(value);
20751
21012
  }
20752
21013
  function buildSourceUpdateCommand(sourceRoot) {
20753
- const quotedRoot = shellQuote2(sourceRoot);
21014
+ const quotedRoot = shellQuote3(sourceRoot);
20754
21015
  const cdCommand = process.platform === "win32" ? `cd /d ${quotedRoot}` : `cd ${quotedRoot}`;
20755
21016
  return `${cdCommand} && git fetch origin main --tags && git merge --ff-only origin/main`;
20756
21017
  }
@@ -20781,12 +21042,12 @@ function resolveUpdatePlan() {
20781
21042
  kind: "npm-global",
20782
21043
  command,
20783
21044
  args,
20784
- manualCommand: `${command} ${args.map(shellQuote2).join(" ")}`
21045
+ manualCommand: `${command} ${args.map(shellQuote3).join(" ")}`
20785
21046
  };
20786
21047
  }
20787
21048
  function runCommand(command, args) {
20788
21049
  return new Promise((resolveExitCode) => {
20789
- const child = (0, import_node_child_process.spawn)(command, args, {
21050
+ const child = (0, import_node_child_process2.spawn)(command, args, {
20790
21051
  stdio: "inherit",
20791
21052
  shell: process.platform === "win32",
20792
21053
  env: process.env
@@ -20801,6 +21062,12 @@ function runCommand(command, args) {
20801
21062
  });
20802
21063
  });
20803
21064
  }
21065
+ async function runNpmGlobalUpdatePlan(plan) {
21066
+ if (plan.kind !== "npm-global") {
21067
+ return 1;
21068
+ }
21069
+ return runCommand(plan.command, plan.args);
21070
+ }
20804
21071
  async function handleUpdate(options) {
20805
21072
  const plan = resolveUpdatePlan();
20806
21073
  const render = {
@@ -20947,16 +21214,27 @@ function skillsIndexUrl(baseUrl) {
20947
21214
  return `${normalizeBaseUrl2(baseUrl)}${INSTALL_COMMANDS.skills.index_path}`;
20948
21215
  }
20949
21216
  function buildSkillsAddArgs(baseUrl, skillName, options = {}) {
21217
+ const skillNames = Array.isArray(skillName) ? skillName : [skillName];
21218
+ const [firstSkillName, ...extraSkillNames] = skillNames;
20950
21219
  const values = {
20951
21220
  skills_index_url: skillsIndexUrl(baseUrl),
20952
21221
  agents: INSTALL_COMMANDS.skills.default_agents.join(" "),
20953
- skill_name: skillName
21222
+ skill_name: firstSkillName ?? ""
20954
21223
  };
20955
21224
  const rendered = INSTALL_COMMANDS.skills.npx_add_args_template.flatMap(
20956
21225
  (arg, index) => {
20957
21226
  const next = index === 0 && options.firstArg ? options.firstArg : arg;
20958
21227
  const value = renderTemplate(next, values);
20959
- return arg === "{agents}" ? INSTALL_COMMANDS.skills.default_agents : value;
21228
+ if (arg === "{agents}") {
21229
+ return INSTALL_COMMANDS.skills.default_agents;
21230
+ }
21231
+ if (arg === "{skill_name}") {
21232
+ return [
21233
+ value,
21234
+ ...extraSkillNames.flatMap((name) => ["--skill", name])
21235
+ ];
21236
+ }
21237
+ return value;
20960
21238
  }
20961
21239
  );
20962
21240
  return rendered;
@@ -21027,13 +21305,78 @@ function unknownCommandNameFromMessage(message) {
21027
21305
  return command ? command : null;
21028
21306
  }
21029
21307
 
21308
+ // src/cli/self-update.ts
21309
+ var import_node_child_process3 = require("child_process");
21310
+ function envTruthy(name) {
21311
+ const value = process.env[name]?.trim().toLowerCase();
21312
+ return value === "1" || value === "true" || value === "yes";
21313
+ }
21314
+ function isCi() {
21315
+ return envTruthy("CI") || envTruthy("GITHUB_ACTIONS");
21316
+ }
21317
+ function shouldSkipSelfUpdate() {
21318
+ return envTruthy("DEEPLINE_SKIP_SELF_UPDATE") || envTruthy("DEEPLINE_NO_AUTO_UPDATE") || envTruthy("DEEPLINE_SKIP_SDK_AUTO_UPDATE") || envTruthy("DEEPLINE_DISABLE_AUTO_UPDATE") || isCi();
21319
+ }
21320
+ function relaunchCurrentCommand() {
21321
+ return new Promise((resolve16) => {
21322
+ const child = (0, import_node_child_process3.spawn)(process.execPath, process.argv.slice(1), {
21323
+ stdio: "inherit",
21324
+ env: {
21325
+ ...process.env,
21326
+ DEEPLINE_NO_AUTO_UPDATE: "1"
21327
+ }
21328
+ });
21329
+ child.on("error", (error) => {
21330
+ process.stderr.write(
21331
+ `Deepline SDK/CLI updated, but relaunch failed: ${error.message}
21332
+ `
21333
+ );
21334
+ resolve16(1);
21335
+ });
21336
+ child.on("close", (code) => resolve16(code ?? 1));
21337
+ });
21338
+ }
21339
+ async function maybeAutoUpdateAndRelaunch(response) {
21340
+ const autoUpdate = response?.auto_update;
21341
+ if (!response || !autoUpdate?.should_auto_update || shouldSkipSelfUpdate()) {
21342
+ return false;
21343
+ }
21344
+ const plan = resolveUpdatePlan();
21345
+ if (plan.kind !== "npm-global") {
21346
+ return false;
21347
+ }
21348
+ const label = autoUpdate.required ? "requires an update" : "is more than the supported auto-update lag behind";
21349
+ process.stderr.write(
21350
+ `Deepline SDK/CLI ${label}; running ${plan.manualCommand}
21351
+ `
21352
+ );
21353
+ const updateExitCode = await runNpmGlobalUpdatePlan(plan);
21354
+ if (updateExitCode !== 0) {
21355
+ if (autoUpdate.required) {
21356
+ throw new Error(
21357
+ `Automatic Deepline SDK/CLI update failed with exit code ${updateExitCode}. ${response.message}`
21358
+ );
21359
+ }
21360
+ process.stderr.write(
21361
+ `Deepline SDK/CLI auto-update failed with exit code ${updateExitCode}; continuing with ${response.current ?? "current version"}.
21362
+ `
21363
+ );
21364
+ return false;
21365
+ }
21366
+ process.stderr.write("Deepline SDK/CLI updated; rerunning command.\n");
21367
+ const exitCode = await relaunchCurrentCommand();
21368
+ process.exit(exitCode);
21369
+ return true;
21370
+ }
21371
+
21030
21372
  // src/cli/skills-sync.ts
21031
- var import_node_child_process2 = require("child_process");
21373
+ var import_node_child_process4 = require("child_process");
21032
21374
  var import_node_fs15 = require("fs");
21033
21375
  var import_node_os11 = require("os");
21034
21376
  var import_node_path19 = require("path");
21035
21377
  var CHECK_TIMEOUT_MS2 = 3e3;
21036
21378
  var SDK_SKILL_NAME = "deepline-plays";
21379
+ var SDK_SKILL_NAMES = [SDK_SKILL_NAME, "deepline-plays-quickstart"];
21037
21380
  var attemptedSync = false;
21038
21381
  function shouldSkipSkillsSync() {
21039
21382
  const value = process.env.DEEPLINE_SKIP_SKILLS_SYNC?.trim().toLowerCase();
@@ -21150,19 +21493,19 @@ async function fetchSkillsUpdate(baseUrl, localVersion) {
21150
21493
  }
21151
21494
  }
21152
21495
  function buildSkillsInstallArgs(baseUrl) {
21153
- return buildSkillsAddArgs(baseUrl, SDK_SKILL_NAME);
21496
+ return buildSkillsAddArgs(baseUrl, SDK_SKILL_NAMES);
21154
21497
  }
21155
21498
  function buildBunxSkillsInstallArgs(baseUrl) {
21156
- return buildSkillsAddArgs(baseUrl, SDK_SKILL_NAME, { firstArg: "--bun" });
21499
+ return buildSkillsAddArgs(baseUrl, SDK_SKILL_NAMES, { firstArg: "--bun" });
21157
21500
  }
21158
21501
  function hasCommand(command) {
21159
- const result = (0, import_node_child_process2.spawnSync)(command, ["--version"], {
21502
+ const result = (0, import_node_child_process4.spawnSync)(command, ["--version"], {
21160
21503
  stdio: "ignore",
21161
21504
  shell: process.platform === "win32"
21162
21505
  });
21163
21506
  return result.status === 0;
21164
21507
  }
21165
- function shellQuote3(arg) {
21508
+ function shellQuote4(arg) {
21166
21509
  return `'${arg.replace(/'/g, `'\\''`)}'`;
21167
21510
  }
21168
21511
  function resolveSkillsInstallCommands(baseUrl) {
@@ -21170,7 +21513,7 @@ function resolveSkillsInstallCommands(baseUrl) {
21170
21513
  const npxInstall = {
21171
21514
  command: "npx",
21172
21515
  args: npxArgs,
21173
- manualCommand: `npx ${npxArgs.map(shellQuote3).join(" ")}`
21516
+ manualCommand: `npx ${npxArgs.map(shellQuote4).join(" ")}`
21174
21517
  };
21175
21518
  if (hasCommand("bunx")) {
21176
21519
  const bunxArgs = buildBunxSkillsInstallArgs(baseUrl);
@@ -21178,7 +21521,7 @@ function resolveSkillsInstallCommands(baseUrl) {
21178
21521
  {
21179
21522
  command: "bunx",
21180
21523
  args: bunxArgs,
21181
- manualCommand: `bunx ${bunxArgs.map(shellQuote3).join(" ")}`
21524
+ manualCommand: `bunx ${bunxArgs.map(shellQuote4).join(" ")}`
21182
21525
  },
21183
21526
  npxInstall
21184
21527
  ];
@@ -21187,7 +21530,7 @@ function resolveSkillsInstallCommands(baseUrl) {
21187
21530
  }
21188
21531
  function runOneSkillsInstall(install) {
21189
21532
  return new Promise((resolve16) => {
21190
- const child = (0, import_node_child_process2.spawn)(install.command, install.args, {
21533
+ const child = (0, import_node_child_process4.spawn)(install.command, install.args, {
21191
21534
  stdio: ["ignore", "ignore", "pipe"],
21192
21535
  env: process.env
21193
21536
  });
@@ -21255,7 +21598,7 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
21255
21598
  if (!update?.needsUpdate && !hasStaleInstalledSkill || !update?.remoteVersion) {
21256
21599
  return;
21257
21600
  }
21258
- writeSdkSkillsStatusLine("SDK skills changed; syncing deepline-plays skill...");
21601
+ writeSdkSkillsStatusLine("SDK skills changed; syncing Deepline SDK skills...");
21259
21602
  const installed = await runSkillsInstall(baseUrl);
21260
21603
  if (!installed) return;
21261
21604
  if (installedSdkSkillHasStalePositionalExecuteExamples()) {
@@ -21478,7 +21821,9 @@ async function main() {
21478
21821
  progress?.phase("loading deepline cli");
21479
21822
  }
21480
21823
  const program = new import_commander3.Command();
21481
- program.name("deepline").description("Deepline CLI (TypeScript SDK)").version(SDK_VERSION, "-v, --version", "Show version").exitOverride().showHelpAfterError().showSuggestionAfterError(true).addHelpText(
21824
+ program.name("deepline").description(
21825
+ "Deepline CLI \u2014 GTM enrichment tools, plays, and runs from your terminal."
21826
+ ).version(SDK_VERSION, "-v, --version", "Show version").exitOverride().showHelpAfterError().showSuggestionAfterError(true).addHelpText(
21482
21827
  "after",
21483
21828
  `
21484
21829
  Common commands:
@@ -21519,11 +21864,23 @@ Exit codes:
21519
21864
  progress?.phase("checking sdk compatibility");
21520
21865
  }
21521
21866
  const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
21522
- await traceCliSpan(
21867
+ const compatibility = await traceCliSpan(
21523
21868
  "cli.sdk_compatibility",
21524
- { baseUrl },
21525
- () => enforceSdkCompatibility(baseUrl)
21869
+ { baseUrl, command: actionCommand.name() },
21870
+ () => checkSdkCompatibility(baseUrl, {
21871
+ command: actionCommand.name()
21872
+ })
21873
+ );
21874
+ if (compatibility.error) {
21875
+ return;
21876
+ }
21877
+ const relaunched = await maybeAutoUpdateAndRelaunch(
21878
+ compatibility.response
21526
21879
  );
21880
+ if (relaunched) {
21881
+ return;
21882
+ }
21883
+ enforceSdkCompatibilityResponse(compatibility.response);
21527
21884
  if (printStartupPhase) {
21528
21885
  progress?.phase("checking sdk skills");
21529
21886
  }
@@ -21548,6 +21905,7 @@ Exit codes:
21548
21905
  registerDbCommands(program);
21549
21906
  registerFeedbackCommands(program);
21550
21907
  registerUpdateCommand(program);
21908
+ registerQuickstartCommands(program);
21551
21909
  program.command("preflight").description("Run compact health, auth, and Deepline billing checks.").option("--json", "Force JSON output.").addHelpText(
21552
21910
  "after",
21553
21911
  `
@@ -21623,6 +21981,29 @@ Examples:
21623
21981
  process.stdout.write(`deepline ${SDK_VERSION}
21624
21982
  `);
21625
21983
  });
21984
+ if (process.argv.slice(2).length === 0) {
21985
+ if (!process.stdout.isTTY) {
21986
+ printJson({
21987
+ ok: false,
21988
+ exitCode: 2,
21989
+ code: "MISSING_COMMAND",
21990
+ message: "No command provided.",
21991
+ next: "deepline --help"
21992
+ });
21993
+ console.error("error: missing command (see: deepline --help)");
21994
+ } else {
21995
+ program.outputHelp({ error: true });
21996
+ console.error("\nerror: missing command (see usage above)");
21997
+ }
21998
+ recordCliTrace({
21999
+ phase: "cli.main_total",
22000
+ ms: Date.now() - mainStartedAt,
22001
+ ok: false,
22002
+ error: "missing command"
22003
+ });
22004
+ process.exitCode = 2;
22005
+ return;
22006
+ }
21626
22007
  try {
21627
22008
  await program.parseAsync(process.argv);
21628
22009
  recordCliTrace({