deepline 0.1.260 → 0.1.261

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.
@@ -701,7 +701,7 @@ var SDK_RELEASE = {
701
701
  // Deepline-native radars. Older clients must update before discovering,
702
702
  // checking, or deploying an unlaunched monitor integration.
703
703
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
704
- version: "0.1.260",
704
+ version: "0.1.261",
705
705
  apiContract: "2026-07-native-monitor-launch-hard-cutover",
706
706
  supportPolicy: {
707
707
  minimumSupported: "0.1.53",
@@ -28940,7 +28940,7 @@ import {
28940
28940
  existsSync as existsSync12,
28941
28941
  mkdirSync as mkdirSync10,
28942
28942
  realpathSync as realpathSync3,
28943
- readFileSync as readFileSync12,
28943
+ readFileSync as readFileSync13,
28944
28944
  renameSync,
28945
28945
  rmSync as rmSync4,
28946
28946
  unlinkSync,
@@ -28951,7 +28951,7 @@ import { dirname as dirname13, isAbsolute as isAbsolute3, join as join15, relati
28951
28951
 
28952
28952
  // src/cli/commands/skills.ts
28953
28953
  import { spawn as spawn3 } from "child_process";
28954
- import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync14 } from "fs";
28954
+ import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync14 } from "fs";
28955
28955
  import { homedir as homedir11 } from "os";
28956
28956
  import { dirname as dirname12, join as join14 } from "path";
28957
28957
 
@@ -29163,10 +29163,13 @@ function buildSkillsPlan(input2) {
29163
29163
  skillNames: input2.skillNames,
29164
29164
  version: input2.version,
29165
29165
  remove: {
29166
- command: "npx",
29166
+ command: "npm",
29167
29167
  args: [
29168
+ "exec",
29168
29169
  "--yes",
29169
- "skills@latest",
29170
+ "--package=skills@latest",
29171
+ "--",
29172
+ "skills",
29170
29173
  "remove",
29171
29174
  ...scopeArgs,
29172
29175
  "--agent",
@@ -29176,10 +29179,13 @@ function buildSkillsPlan(input2) {
29176
29179
  ]
29177
29180
  },
29178
29181
  install: {
29179
- command: "npx",
29182
+ command: "npm",
29180
29183
  args: [
29184
+ "exec",
29181
29185
  "--yes",
29182
- "skills@latest",
29186
+ "--package=skills@latest",
29187
+ "--",
29188
+ "skills",
29183
29189
  "add",
29184
29190
  skillsIndexUrl(input2.baseUrl),
29185
29191
  "--agent",
@@ -29193,6 +29199,29 @@ function buildSkillsPlan(input2) {
29193
29199
  statePath: skillsStatePathForScope(input2.baseUrl, input2.scope, input2.root)
29194
29200
  };
29195
29201
  }
29202
+ function sortedStrings(value) {
29203
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
29204
+ return null;
29205
+ }
29206
+ return [...value].sort((a, b) => a.localeCompare(b));
29207
+ }
29208
+ function isSkillsPlanCurrent(plan, state) {
29209
+ if (!state || state.scope !== plan.scope || state.skillsVersion !== plan.version) {
29210
+ return false;
29211
+ }
29212
+ const installedAgents = sortedStrings(state.agents);
29213
+ const installedSkillNames = sortedStrings(state.skillNames);
29214
+ if (!installedAgents || !installedSkillNames) return false;
29215
+ 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");
29216
+ }
29217
+ function readSkillsInstallState(path) {
29218
+ try {
29219
+ const parsed = JSON.parse(readFileSync12(path, "utf8"));
29220
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
29221
+ } catch {
29222
+ return null;
29223
+ }
29224
+ }
29196
29225
  function runProcess(command, args, cwd) {
29197
29226
  return new Promise((resolve15, reject) => {
29198
29227
  const child = spawn3(command, args, {
@@ -29216,7 +29245,7 @@ function runProcess(command, args, cwd) {
29216
29245
  });
29217
29246
  });
29218
29247
  }
29219
- async function runSkillsCommand(options) {
29248
+ async function runSkillsCommand(options, dependencies = {}) {
29220
29249
  let scope;
29221
29250
  let root;
29222
29251
  try {
@@ -29238,7 +29267,7 @@ async function runSkillsCommand(options) {
29238
29267
  const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
29239
29268
  let catalog;
29240
29269
  try {
29241
- catalog = options.dryRun ? { skillNames: [...DEFAULT_SDK_SKILL_NAMES], version: "latest" } : await fetchSkillCatalog(baseUrl);
29270
+ catalog = options.dryRun ? { skillNames: [...DEFAULT_SDK_SKILL_NAMES], version: "latest" } : await (dependencies.fetchCatalog ?? fetchSkillCatalog)(baseUrl);
29242
29271
  } catch (error) {
29243
29272
  printCommandEnvelope(
29244
29273
  {
@@ -29262,6 +29291,37 @@ async function runSkillsCommand(options) {
29262
29291
  );
29263
29292
  return 0;
29264
29293
  }
29294
+ if (isSkillsPlanCurrent(plan, readSkillsInstallState(plan.statePath))) {
29295
+ printCommandEnvelope(
29296
+ {
29297
+ ok: true,
29298
+ status: "current",
29299
+ complete: true,
29300
+ changed: false,
29301
+ skipped: true,
29302
+ skipReason: "skills_version_current",
29303
+ scope,
29304
+ agents,
29305
+ skillsVersion: plan.version,
29306
+ skillCount: plan.skillNames.length,
29307
+ statePath: plan.statePath,
29308
+ render: {
29309
+ sections: [
29310
+ {
29311
+ title: "skills",
29312
+ lines: [
29313
+ `Scope: ${scope}`,
29314
+ `Agents: ${agents.join(", ")}`,
29315
+ `Current: ${plan.skillNames.length}`
29316
+ ]
29317
+ }
29318
+ ]
29319
+ }
29320
+ },
29321
+ { json: options.json }
29322
+ );
29323
+ return 0;
29324
+ }
29265
29325
  if (scope === "local" && root) {
29266
29326
  const managedNames = [
29267
29327
  .../* @__PURE__ */ new Set([...plan.skillNames, ...LEGACY_SKILL_NAMES_TO_REMOVE])
@@ -29279,7 +29339,8 @@ async function runSkillsCommand(options) {
29279
29339
  `
29280
29340
  );
29281
29341
  try {
29282
- const removeCode = await runProcess(
29342
+ const execute = dependencies.runProcess ?? runProcess;
29343
+ const removeCode = await execute(
29283
29344
  plan.remove.command,
29284
29345
  plan.remove.args,
29285
29346
  root ?? void 0
@@ -29287,7 +29348,7 @@ async function runSkillsCommand(options) {
29287
29348
  if (removeCode !== 0) {
29288
29349
  throw new Error("Could not remove the existing Deepline skills.");
29289
29350
  }
29290
- const installCode = await runProcess(
29351
+ const installCode = await execute(
29291
29352
  plan.install.command,
29292
29353
  plan.install.args,
29293
29354
  root ?? void 0
@@ -29456,7 +29517,7 @@ function publicNpmFallbackRegistryUrl(hostUrl) {
29456
29517
  }
29457
29518
  function readOptionalText(path) {
29458
29519
  try {
29459
- return readFileSync12(path, "utf8").trim();
29520
+ return readFileSync13(path, "utf8").trim();
29460
29521
  } catch {
29461
29522
  return "";
29462
29523
  }
@@ -29596,7 +29657,7 @@ function readAutoUpdateFailure(plan) {
29596
29657
  if (!path) return null;
29597
29658
  try {
29598
29659
  const parsed = JSON.parse(
29599
- readFileSync12(path, "utf8")
29660
+ readFileSync13(path, "utf8")
29600
29661
  );
29601
29662
  if ((parsed.kind === "npm-global" || parsed.kind === "python-sidecar") && typeof parsed.packageSpec === "string" && typeof parsed.failedAt === "string" && typeof parsed.exitCode === "number" && typeof parsed.manualCommand === "string") {
29602
29663
  return parsed;
@@ -29680,7 +29741,7 @@ function installedPackageVersion(versionDir) {
29680
29741
  "package.json"
29681
29742
  );
29682
29743
  try {
29683
- const parsed = JSON.parse(readFileSync12(packageJsonPath, "utf8"));
29744
+ const parsed = JSON.parse(readFileSync13(packageJsonPath, "utf8"));
29684
29745
  return typeof parsed.version === "string" ? safeVersionSegment(parsed.version) : "";
29685
29746
  } catch {
29686
29747
  return "";
@@ -29993,13 +30054,92 @@ import {
29993
30054
  existsSync as existsSync13,
29994
30055
  lstatSync,
29995
30056
  mkdirSync as mkdirSync11,
29996
- readFileSync as readFileSync13,
30057
+ readFileSync as readFileSync14,
29997
30058
  realpathSync as realpathSync4,
29998
30059
  rmSync as rmSync5,
29999
30060
  writeFileSync as writeFileSync16
30000
30061
  } from "fs";
30001
30062
  import { homedir as homedir13 } from "os";
30002
30063
  import { dirname as dirname14, join as join16, resolve as resolve14 } from "path";
30064
+ var SETUP_PHASE_NAMES = [
30065
+ "cli",
30066
+ "cleanup",
30067
+ "skills",
30068
+ "auth",
30069
+ "verify"
30070
+ ];
30071
+ function initialSetupPhases() {
30072
+ return {
30073
+ cli: { status: "pending" },
30074
+ cleanup: { status: "pending" },
30075
+ skills: { status: "pending" },
30076
+ auth: { status: "pending" },
30077
+ verify: { status: "pending" }
30078
+ };
30079
+ }
30080
+ function isSetupPhaseStatus(value) {
30081
+ return value === "pending" || value === "in_progress" || value === "complete" || value === "waiting" || value === "failed";
30082
+ }
30083
+ function parseSetupPhases(value) {
30084
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
30085
+ const source = value;
30086
+ const phases = initialSetupPhases();
30087
+ for (const name of SETUP_PHASE_NAMES) {
30088
+ const phase = source[name];
30089
+ if (!phase || typeof phase !== "object" || Array.isArray(phase)) {
30090
+ return null;
30091
+ }
30092
+ const record = phase;
30093
+ if (!isSetupPhaseStatus(record.status)) return null;
30094
+ phases[name] = {
30095
+ status: record.status,
30096
+ ...typeof record.outcome === "string" ? { outcome: record.outcome } : {},
30097
+ ...typeof record.code === "string" ? { code: record.code } : {}
30098
+ };
30099
+ }
30100
+ return phases;
30101
+ }
30102
+ function phasesFromLegacyStatus(status) {
30103
+ const phases = initialSetupPhases();
30104
+ if (status === "skills_installed" || status === "authorization_pending" || status === "complete") {
30105
+ phases.cli = { status: "complete" };
30106
+ phases.cleanup = { status: "complete" };
30107
+ phases.skills = { status: "complete" };
30108
+ }
30109
+ if (status === "authorization_pending") {
30110
+ phases.auth = { status: "waiting", outcome: "authorization_pending" };
30111
+ } else if (status === "complete") {
30112
+ phases.auth = { status: "complete" };
30113
+ phases.verify = { status: "complete" };
30114
+ }
30115
+ return phases;
30116
+ }
30117
+ function readSetupState(input2) {
30118
+ try {
30119
+ const parsed = JSON.parse(
30120
+ readFileSync14(
30121
+ setupStatePath(input2.baseUrl, input2.scope, input2.root),
30122
+ "utf8"
30123
+ )
30124
+ );
30125
+ if (parsed.host !== input2.baseUrl || parsed.scope !== input2.scope || parsed.root !== input2.root || typeof parsed.status !== "string") {
30126
+ return null;
30127
+ }
30128
+ return {
30129
+ status: parsed.status,
30130
+ phases: parseSetupPhases(parsed.phases) ?? phasesFromLegacyStatus(parsed.status)
30131
+ };
30132
+ } catch {
30133
+ return null;
30134
+ }
30135
+ }
30136
+ function selectSetupProgress(previousState) {
30137
+ const resumed = Boolean(previousState && previousState.status !== "complete");
30138
+ return {
30139
+ resumed,
30140
+ phases: resumed ? previousState.phases : initialSetupPhases()
30141
+ };
30142
+ }
30003
30143
  function normalizeScope2(value) {
30004
30144
  if (!value || value === "global") return "global";
30005
30145
  if (value === "local") return "local";
@@ -30059,7 +30199,7 @@ function printCapturedAuthorizationUrl(stdout) {
30059
30199
  }
30060
30200
  function safeRead(path) {
30061
30201
  try {
30062
- return readFileSync13(path, "utf8");
30202
+ return readFileSync14(path, "utf8");
30063
30203
  } catch {
30064
30204
  return "";
30065
30205
  }
@@ -30170,13 +30310,14 @@ function writeSetupState(input2) {
30170
30310
  path,
30171
30311
  `${JSON.stringify(
30172
30312
  {
30173
- schemaVersion: 1,
30313
+ schemaVersion: 2,
30174
30314
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
30175
30315
  cliVersion: SDK_VERSION,
30176
30316
  host: input2.baseUrl,
30177
30317
  scope: input2.scope,
30178
30318
  root: input2.root,
30179
- status: input2.status
30319
+ status: input2.status,
30320
+ phases: input2.phases
30180
30321
  },
30181
30322
  null,
30182
30323
  2
@@ -30186,6 +30327,24 @@ function writeSetupState(input2) {
30186
30327
  );
30187
30328
  return path;
30188
30329
  }
30330
+ function persistSetupProgress(input2) {
30331
+ return writeSetupState({
30332
+ ...input2,
30333
+ status: input2.status ?? "in_progress"
30334
+ });
30335
+ }
30336
+ function completeSetupPhase(phases, phase, outcome) {
30337
+ phases[phase] = {
30338
+ status: "complete",
30339
+ ...outcome ? { outcome } : {}
30340
+ };
30341
+ }
30342
+ function beginSetupPhase(phases, phase) {
30343
+ phases[phase] = { status: "in_progress" };
30344
+ }
30345
+ function failSetupPhase(phases, phase, code) {
30346
+ phases[phase] = { status: "failed", code };
30347
+ }
30189
30348
  function rollbackCommand(scope, root) {
30190
30349
  const prefix = scope === "local" && root ? ` --prefix ${JSON.stringify(join16(root, ".deepline", "runtime"))}` : "";
30191
30350
  return `npm install -g${prefix} --no-audit --no-fund --include=optional --allow-scripts=esbuild deepline@${SDK_VERSION}`;
@@ -30197,6 +30356,42 @@ function setupResumeCommand(baseUrl, scope) {
30197
30356
  const hostPrefix = baseUrl === "https://code.deepline.com" ? "" : `DEEPLINE_HOST_URL=${JSON.stringify(baseUrl)} `;
30198
30357
  return `${hostPrefix}deepline setup --scope ${scope} --json`;
30199
30358
  }
30359
+ function setupRetry(input2) {
30360
+ return {
30361
+ phase: input2.phase,
30362
+ command: setupResumeCommand(input2.baseUrl, input2.scope),
30363
+ automatic: true
30364
+ };
30365
+ }
30366
+ function reportSetupPhaseFailure(input2) {
30367
+ failSetupPhase(input2.phases, input2.phase, input2.code);
30368
+ const statePath = persistSetupProgress({
30369
+ baseUrl: input2.baseUrl,
30370
+ scope: input2.scope,
30371
+ root: input2.root,
30372
+ phases: input2.phases,
30373
+ status: "failed"
30374
+ });
30375
+ const retry = setupRetry(input2);
30376
+ printCommandEnvelope(
30377
+ {
30378
+ ok: false,
30379
+ status: "failed",
30380
+ code: input2.code,
30381
+ exitCode: input2.exitCode,
30382
+ scope: input2.scope,
30383
+ message: input2.message,
30384
+ phases: input2.phases,
30385
+ failedPhase: input2.phase,
30386
+ retry,
30387
+ statePath,
30388
+ next: retry.command,
30389
+ ...input2.extra ?? {}
30390
+ },
30391
+ { json: input2.json }
30392
+ );
30393
+ return input2.exitCode;
30394
+ }
30200
30395
  async function readAuthStatus(authScope) {
30201
30396
  try {
30202
30397
  const captured = await captureStdout2(
@@ -30216,63 +30411,44 @@ async function readAuthStatus(authScope) {
30216
30411
  }
30217
30412
  function reportSetupAuthStatusFailure(input2) {
30218
30413
  if (!input2.auth.error) return null;
30219
- printCommandEnvelope(
30220
- {
30221
- ok: false,
30222
- status: "failed",
30223
- code: "AUTH_STATUS_FAILED",
30224
- exitCode: 4,
30225
- scope: input2.scope,
30226
- message: "Deepline could not verify authorization with the configured host.",
30227
- next: `Check network access, then rerun: deepline setup --scope ${input2.scope} --json`,
30228
- authScope: input2.authScope
30229
- },
30230
- { json: input2.json }
30231
- );
30232
- return 4;
30414
+ return reportSetupPhaseFailure({
30415
+ baseUrl: input2.baseUrl,
30416
+ scope: input2.scope,
30417
+ root: input2.root,
30418
+ phases: input2.phases,
30419
+ phase: "auth",
30420
+ code: "AUTH_STATUS_FAILED",
30421
+ exitCode: 4,
30422
+ message: "Deepline could not verify authorization with the configured host.",
30423
+ json: input2.json,
30424
+ extra: { authScope: input2.authScope }
30425
+ });
30233
30426
  }
30234
- async function runDoctorCommand(options) {
30235
- let scope;
30236
- let root;
30237
- try {
30238
- scope = normalizeScope2(options.scope);
30239
- root = resolveScopeRoot(scope);
30240
- } catch (error) {
30241
- printCommandEnvelope(
30242
- {
30243
- ok: false,
30244
- status: "failed",
30245
- code: "INVALID_SETUP_SCOPE",
30246
- exitCode: 2,
30247
- message: error instanceof Error ? error.message : String(error)
30248
- },
30249
- { json: options.json }
30250
- );
30251
- return 2;
30252
- }
30253
- const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
30254
- const skillsStatePath = skillsStatePathForScope(baseUrl, scope, root);
30427
+ function buildDoctorAssessment(input2) {
30428
+ const skillsStatePath = skillsStatePathForScope(
30429
+ input2.baseUrl,
30430
+ input2.scope,
30431
+ input2.root
30432
+ );
30255
30433
  const skillsState = parseCapturedJson(safeRead(skillsStatePath));
30256
- const authScope = authScopeForSetup(scope);
30257
- const apiKey = scope === "local" ? resolveProjectApiKeyForBaseUrl(baseUrl) : resolveGlobalApiKeyForBaseUrl(baseUrl);
30258
- const projectAuth = scope === "local" && apiKey ? getResolvedProjectAuthSource(baseUrl, apiKey) : null;
30259
- const authStatus = await readAuthStatus(authScope);
30260
- const connected = authStatus.payload?.connected === true;
30261
- const authScopeOk = scope === "local" ? Boolean(projectAuth) : Boolean(apiKey && !projectAuth);
30262
- const skillsOk = skillsState?.scope === scope && Array.isArray(skillsState.agents) && skillsState.agents.length > 0;
30434
+ const apiKey = input2.scope === "local" ? resolveProjectApiKeyForBaseUrl(input2.baseUrl) : resolveGlobalApiKeyForBaseUrl(input2.baseUrl);
30435
+ const projectAuth = input2.scope === "local" && apiKey ? getResolvedProjectAuthSource(input2.baseUrl, apiKey) : null;
30436
+ const connected = input2.authStatus.payload?.connected === true;
30437
+ const authScopeOk = input2.scope === "local" ? Boolean(projectAuth) : Boolean(apiKey && !projectAuth);
30438
+ const skillsOk = skillsState?.scope === input2.scope && typeof skillsState.skillsVersion === "string" && Array.isArray(skillsState.agents) && skillsState.agents.length > 0;
30263
30439
  const runningCliPath = process.argv[1] ? resolve14(process.argv[1]) : null;
30264
- const globalCli = scope === "global" ? inspectGlobalCliAvailability() : null;
30440
+ const globalCli = input2.scope === "global" ? inspectGlobalCliAvailability() : null;
30265
30441
  const pathGlobalCli = globalCli?.path ?? null;
30266
- const cliPath = scope === "global" ? pathGlobalCli : runningCliPath;
30267
- const cliScopeOk = scope === "global" ? Boolean(pathGlobalCli) : Boolean(
30268
- root && runningCliPath?.includes(join16(root, ".deepline", "runtime"))
30442
+ const cliPath = input2.scope === "global" ? pathGlobalCli : runningCliPath;
30443
+ const cliScopeOk = input2.scope === "global" ? Boolean(pathGlobalCli) : Boolean(
30444
+ input2.root && runningCliPath?.includes(join16(input2.root, ".deepline", "runtime"))
30269
30445
  );
30270
30446
  const checks = {
30271
30447
  cli: {
30272
30448
  ok: Boolean(cliPath) && cliScopeOk,
30273
30449
  version: SDK_VERSION,
30274
30450
  path: cliPath,
30275
- scope
30451
+ scope: input2.scope
30276
30452
  },
30277
30453
  skills: {
30278
30454
  ok: skillsOk,
@@ -30283,16 +30459,48 @@ async function runDoctorCommand(options) {
30283
30459
  auth: {
30284
30460
  ok: connected && authScopeOk,
30285
30461
  scope: projectAuth ? "local" : apiKey ? "global" : null,
30286
- status: authStatus.payload?.status ?? "not_connected"
30462
+ status: input2.authStatus.payload?.status ?? "not_connected"
30287
30463
  },
30288
30464
  api: {
30289
- ok: connected && authStatus.exitCode === 0,
30290
- host: baseUrl,
30291
- workspace: authStatus.payload?.workspace ?? null,
30465
+ ok: connected && input2.authStatus.exitCode === 0,
30466
+ host: input2.baseUrl,
30467
+ workspace: input2.authStatus.payload?.workspace ?? null,
30292
30468
  providerSpend: false
30293
30469
  }
30294
30470
  };
30295
- const ok = Object.values(checks).every((check) => check.ok);
30471
+ return {
30472
+ ok: Object.values(checks).every((check) => check.ok),
30473
+ checks
30474
+ };
30475
+ }
30476
+ async function runDoctorCommand(options) {
30477
+ let scope;
30478
+ let root;
30479
+ try {
30480
+ scope = normalizeScope2(options.scope);
30481
+ root = resolveScopeRoot(scope);
30482
+ } catch (error) {
30483
+ printCommandEnvelope(
30484
+ {
30485
+ ok: false,
30486
+ status: "failed",
30487
+ code: "INVALID_SETUP_SCOPE",
30488
+ exitCode: 2,
30489
+ message: error instanceof Error ? error.message : String(error)
30490
+ },
30491
+ { json: options.json }
30492
+ );
30493
+ return 2;
30494
+ }
30495
+ const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
30496
+ const authScope = authScopeForSetup(scope);
30497
+ const authStatus = await readAuthStatus(authScope);
30498
+ const { ok, checks } = buildDoctorAssessment({
30499
+ baseUrl,
30500
+ scope,
30501
+ root,
30502
+ authStatus
30503
+ });
30296
30504
  const quickstart = setupQuickstartCommand(baseUrl);
30297
30505
  printCommandEnvelope(
30298
30506
  {
@@ -30318,11 +30526,16 @@ async function runDoctorCommand(options) {
30318
30526
  return ok ? 0 : 7;
30319
30527
  }
30320
30528
  function pendingResult(input2) {
30529
+ input2.phases.auth = {
30530
+ status: "waiting",
30531
+ outcome: "authorization_pending"
30532
+ };
30321
30533
  const statePath = writeSetupState({
30322
30534
  baseUrl: input2.baseUrl,
30323
30535
  scope: input2.scope,
30324
30536
  root: input2.root,
30325
- status: "authorization_pending"
30537
+ status: "authorization_pending",
30538
+ phases: input2.phases
30326
30539
  });
30327
30540
  if (input2.authorizationUrl) {
30328
30541
  process.stderr.write(`Authorize Deepline: ${input2.authorizationUrl}
@@ -30336,6 +30549,14 @@ function pendingResult(input2) {
30336
30549
  scope: input2.scope,
30337
30550
  authorizationUrl: input2.authorizationUrl || null,
30338
30551
  statePath,
30552
+ phases: input2.phases,
30553
+ currentPhase: "auth",
30554
+ resumed: input2.resumed,
30555
+ retry: setupRetry({
30556
+ baseUrl: input2.baseUrl,
30557
+ scope: input2.scope,
30558
+ phase: "auth"
30559
+ }),
30339
30560
  next: `Approve the link, then run: ${setupResumeCommand(input2.baseUrl, input2.scope)}`,
30340
30561
  render: {
30341
30562
  sections: [
@@ -30366,84 +30587,146 @@ async function runSetupCommand(options) {
30366
30587
  status: "failed",
30367
30588
  code: "INVALID_SETUP_SCOPE",
30368
30589
  exitCode: 2,
30369
- message: error instanceof Error ? error.message : String(error)
30590
+ message: error instanceof Error ? error.message : String(error),
30591
+ phases: {
30592
+ ...initialSetupPhases(),
30593
+ cli: { status: "failed", code: "INVALID_SETUP_SCOPE" }
30594
+ },
30595
+ failedPhase: "cli"
30370
30596
  },
30371
30597
  { json: options.json }
30372
30598
  );
30373
30599
  return 2;
30374
30600
  }
30375
30601
  const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
30376
- if (scope === "global") {
30377
- const globalCli = inspectGlobalCliAvailability();
30378
- if (!globalCli.ok) {
30379
- const installedButUnreachable = Boolean(globalCli.persistentPath);
30380
- printCommandEnvelope(
30381
- {
30382
- ok: false,
30383
- status: "failed",
30384
- code: installedButUnreachable ? "GLOBAL_CLI_NOT_ON_PATH" : "GLOBAL_CLI_NOT_INSTALLED",
30385
- exitCode: 7,
30602
+ const previousState = readSetupState({ baseUrl, scope, root });
30603
+ const { resumed, phases } = selectSetupProgress(previousState);
30604
+ if (phases.cli.status !== "complete") {
30605
+ beginSetupPhase(phases, "cli");
30606
+ persistSetupProgress({ baseUrl, scope, root, phases });
30607
+ if (scope === "global") {
30608
+ const globalCli = inspectGlobalCliAvailability();
30609
+ if (!globalCli.ok) {
30610
+ const installedButUnreachable = Boolean(globalCli.persistentPath);
30611
+ const code = installedButUnreachable ? "GLOBAL_CLI_NOT_ON_PATH" : "GLOBAL_CLI_NOT_INSTALLED";
30612
+ return reportSetupPhaseFailure({
30613
+ baseUrl,
30386
30614
  scope,
30387
- persistentPath: globalCli.persistentPath,
30615
+ root,
30616
+ phases,
30617
+ phase: "cli",
30618
+ code,
30619
+ exitCode: 7,
30388
30620
  message: installedButUnreachable ? `The global Deepline executable is not reachable on PATH: ${globalCli.persistentPath}` : "No persistent global Deepline executable was found.",
30389
- next: "Use the automatic project-local fallback in https://code.deepline.com/SKILL.md"
30390
- },
30391
- { json: options.json }
30392
- );
30393
- return 7;
30621
+ json: options.json,
30622
+ extra: {
30623
+ persistentPath: globalCli.persistentPath,
30624
+ fallback: "https://code.deepline.com/INSTALL.md"
30625
+ }
30626
+ });
30627
+ }
30394
30628
  }
30395
- }
30396
- const conflict = inspectPathConflict();
30397
- if (conflict) {
30398
- printCommandEnvelope(
30399
- {
30400
- ok: false,
30401
- status: "failed",
30629
+ const conflict = inspectPathConflict();
30630
+ if (conflict) {
30631
+ return reportSetupPhaseFailure({
30632
+ baseUrl,
30633
+ scope,
30634
+ root,
30635
+ phases,
30636
+ phase: "cli",
30402
30637
  code: "PATH_CONFLICT",
30403
30638
  exitCode: 7,
30404
- path: conflict,
30405
30639
  message: `An unknown executable named deepline is first on PATH: ${conflict}`,
30406
- next: "Move or rename that executable, then rerun: deepline setup --json"
30407
- },
30408
- { json: options.json }
30409
- );
30410
- return 7;
30640
+ json: options.json,
30641
+ extra: { path: conflict }
30642
+ });
30643
+ }
30644
+ completeSetupPhase(phases, "cli", "available");
30645
+ persistSetupProgress({ baseUrl, scope, root, phases });
30411
30646
  }
30412
- const removedLegacyPaths = removeKnownLegacyPaths(baseUrl);
30413
- if (removedLegacyPaths.length > 0) {
30414
- process.stderr.write(
30415
- `Removed ${removedLegacyPaths.length} known legacy Deepline path(s).
30647
+ if (phases.cleanup.status !== "complete") {
30648
+ beginSetupPhase(phases, "cleanup");
30649
+ persistSetupProgress({ baseUrl, scope, root, phases });
30650
+ try {
30651
+ const removedLegacyPaths = removeKnownLegacyPaths(baseUrl);
30652
+ if (removedLegacyPaths.length > 0) {
30653
+ process.stderr.write(
30654
+ `Removed ${removedLegacyPaths.length} known legacy Deepline path(s).
30416
30655
  `
30417
- );
30656
+ );
30657
+ }
30658
+ completeSetupPhase(
30659
+ phases,
30660
+ "cleanup",
30661
+ removedLegacyPaths.length > 0 ? "removed_legacy_paths" : "clean"
30662
+ );
30663
+ persistSetupProgress({ baseUrl, scope, root, phases });
30664
+ } catch (error) {
30665
+ return reportSetupPhaseFailure({
30666
+ baseUrl,
30667
+ scope,
30668
+ root,
30669
+ phases,
30670
+ phase: "cleanup",
30671
+ code: "LEGACY_CLEANUP_FAILED",
30672
+ exitCode: 5,
30673
+ message: error instanceof Error ? error.message : String(error),
30674
+ json: options.json
30675
+ });
30676
+ }
30418
30677
  }
30419
- process.stderr.write(`Installing Deepline skills (${scope})...
30678
+ let skillsPayload = null;
30679
+ if (phases.skills.status !== "complete") {
30680
+ beginSetupPhase(phases, "skills");
30681
+ persistSetupProgress({ baseUrl, scope, root, phases });
30682
+ process.stderr.write(`Installing Deepline skills (${scope})...
30420
30683
  `);
30421
- const skills = await captureStdout2(
30422
- () => runSkillsCommand({ scope, json: true })
30423
- );
30424
- const skillsPayload = parseCapturedJson(skills.stdout);
30425
- if (skills.exitCode !== 0) {
30426
- printCommandEnvelope(
30427
- {
30428
- ok: false,
30429
- status: "failed",
30684
+ const skills = await captureStdout2(
30685
+ () => runSkillsCommand({ scope, json: true })
30686
+ );
30687
+ skillsPayload = parseCapturedJson(skills.stdout);
30688
+ if (skills.exitCode !== 0) {
30689
+ return reportSetupPhaseFailure({
30690
+ baseUrl,
30691
+ scope,
30692
+ root,
30693
+ phases,
30694
+ phase: "skills",
30430
30695
  code: "SKILLS_INSTALL_FAILED",
30431
30696
  exitCode: skills.exitCode,
30432
- scope,
30433
- detail: skillsPayload,
30434
- next: `deepline skills --scope ${scope} --json`
30435
- },
30436
- { json: options.json }
30697
+ message: typeof skillsPayload?.message === "string" ? skillsPayload.message : "Deepline skills could not be installed.",
30698
+ json: options.json,
30699
+ extra: { detail: skillsPayload }
30700
+ });
30701
+ }
30702
+ completeSetupPhase(
30703
+ phases,
30704
+ "skills",
30705
+ skillsPayload?.status === "current" ? "current" : "installed"
30706
+ );
30707
+ writeSetupState({
30708
+ baseUrl,
30709
+ scope,
30710
+ root,
30711
+ status: "skills_installed",
30712
+ phases
30713
+ });
30714
+ } else {
30715
+ skillsPayload = parseCapturedJson(
30716
+ safeRead(skillsStatePathForScope(baseUrl, scope, root))
30437
30717
  );
30438
- return skills.exitCode;
30439
30718
  }
30440
- writeSetupState({ baseUrl, scope, root, status: "skills_installed" });
30441
30719
  const authScope = authScopeForSetup(scope);
30720
+ beginSetupPhase(phases, "auth");
30721
+ persistSetupProgress({ baseUrl, scope, root, phases });
30442
30722
  let auth = await readAuthStatus(authScope);
30443
30723
  const initialAuthFailure = reportSetupAuthStatusFailure({
30444
30724
  auth,
30725
+ baseUrl,
30445
30726
  authScope,
30446
30727
  scope,
30728
+ root,
30729
+ phases,
30447
30730
  json: options.json
30448
30731
  });
30449
30732
  if (initialAuthFailure !== null) return initialAuthFailure;
@@ -30458,8 +30741,11 @@ async function runSetupCommand(options) {
30458
30741
  auth = await readAuthStatus(authScope);
30459
30742
  const resumedAuthFailure = reportSetupAuthStatusFailure({
30460
30743
  auth,
30744
+ baseUrl,
30461
30745
  authScope,
30462
30746
  scope,
30747
+ root,
30748
+ phases,
30463
30749
  json: options.json
30464
30750
  });
30465
30751
  if (resumedAuthFailure !== null) return resumedAuthFailure;
@@ -30470,7 +30756,9 @@ async function runSetupCommand(options) {
30470
30756
  baseUrl,
30471
30757
  scope,
30472
30758
  root,
30759
+ phases,
30473
30760
  authorizationUrl: stillPending.claimUrl,
30761
+ resumed,
30474
30762
  json: options.json
30475
30763
  });
30476
30764
  }
@@ -30487,24 +30775,30 @@ async function runSetupCommand(options) {
30487
30775
  );
30488
30776
  printCapturedAuthorizationUrl(registered.stdout);
30489
30777
  if (registered.exitCode !== 0) {
30490
- printCommandEnvelope(
30491
- {
30492
- ok: false,
30493
- status: "failed",
30494
- code: "AUTH_REGISTER_FAILED",
30495
- exitCode: registered.exitCode,
30496
- scope,
30497
- next: `deepline auth register --auth-scope ${authScope}`
30498
- },
30499
- { json: options.json }
30500
- );
30501
- return registered.exitCode;
30778
+ return reportSetupPhaseFailure({
30779
+ baseUrl,
30780
+ scope,
30781
+ root,
30782
+ phases,
30783
+ phase: "auth",
30784
+ code: "AUTH_REGISTER_FAILED",
30785
+ exitCode: registered.exitCode,
30786
+ message: "Deepline browser authorization could not be started.",
30787
+ json: options.json,
30788
+ extra: {
30789
+ detail: parseCapturedJson(registered.stdout),
30790
+ authScope
30791
+ }
30792
+ });
30502
30793
  }
30503
30794
  auth = await readAuthStatus(authScope);
30504
30795
  const registeredAuthFailure = reportSetupAuthStatusFailure({
30505
30796
  auth,
30797
+ baseUrl,
30506
30798
  authScope,
30507
30799
  scope,
30800
+ root,
30801
+ phases,
30508
30802
  json: options.json
30509
30803
  });
30510
30804
  if (registeredAuthFailure !== null) return registeredAuthFailure;
@@ -30514,40 +30808,60 @@ async function runSetupCommand(options) {
30514
30808
  baseUrl,
30515
30809
  scope,
30516
30810
  root,
30811
+ phases,
30517
30812
  authorizationUrl: pending?.claimUrl ?? "",
30813
+ resumed,
30518
30814
  json: options.json
30519
30815
  });
30520
30816
  }
30521
30817
  }
30522
- process.stderr.write("Verifying Deepline setup...\n");
30523
- const doctor = await captureStdout2(
30524
- () => runDoctorCommand({ scope, json: true })
30525
- );
30526
- const doctorPayload = parseCapturedJson(doctor.stdout);
30527
- if (doctor.exitCode !== 0) {
30528
- printCommandEnvelope(
30529
- {
30530
- ok: false,
30531
- status: "failed",
30532
- code: "DOCTOR_FAILED",
30533
- exitCode: doctor.exitCode,
30534
- scope,
30818
+ completeSetupPhase(phases, "auth", "connected");
30819
+ beginSetupPhase(phases, "verify");
30820
+ persistSetupProgress({ baseUrl, scope, root, phases });
30821
+ const assessment = buildDoctorAssessment({
30822
+ baseUrl,
30823
+ scope,
30824
+ root,
30825
+ authStatus: auth
30826
+ });
30827
+ const quickstart = setupQuickstartCommand(baseUrl);
30828
+ const doctorPayload = {
30829
+ ok: assessment.ok,
30830
+ status: assessment.ok ? "complete" : "failed",
30831
+ code: assessment.ok ? "DOCTOR_OK" : "DOCTOR_FAILED",
30832
+ exitCode: assessment.ok ? 0 : 7,
30833
+ checks: assessment.checks,
30834
+ next: assessment.ok ? quickstart : `deepline doctor --scope ${scope} --json`
30835
+ };
30836
+ if (!assessment.ok) {
30837
+ return reportSetupPhaseFailure({
30838
+ baseUrl,
30839
+ scope,
30840
+ root,
30841
+ phases,
30842
+ phase: "verify",
30843
+ code: "DOCTOR_FAILED",
30844
+ exitCode: 7,
30845
+ message: "Deepline setup verification found one or more failed checks.",
30846
+ json: options.json,
30847
+ extra: {
30535
30848
  doctor: doctorPayload,
30536
- next: "Review the doctor result and choose a repair, then rerun: deepline setup --json"
30537
- },
30538
- { json: options.json }
30539
- );
30540
- return doctor.exitCode;
30849
+ diagnostic: {
30850
+ command: `deepline doctor --scope ${scope} --json`
30851
+ }
30852
+ }
30853
+ });
30541
30854
  }
30855
+ completeSetupPhase(phases, "verify", "verified");
30542
30856
  const statePath = writeSetupState({
30543
30857
  baseUrl,
30544
30858
  scope,
30545
30859
  root,
30546
- status: "complete"
30860
+ status: "complete",
30861
+ phases
30547
30862
  });
30548
30863
  const doctorChecks = asRecord2(doctorPayload?.checks);
30549
30864
  const apiCheck = asRecord2(doctorChecks?.api);
30550
- const quickstart = setupQuickstartCommand(baseUrl);
30551
30865
  printCommandEnvelope(
30552
30866
  {
30553
30867
  ok: true,
@@ -30560,6 +30874,10 @@ async function runSetupCommand(options) {
30560
30874
  rollbackCommand: rollbackCommand(scope, root),
30561
30875
  statePath,
30562
30876
  doctor: doctorPayload,
30877
+ phases,
30878
+ currentPhase: null,
30879
+ failedPhase: null,
30880
+ resumed,
30563
30881
  next: quickstart,
30564
30882
  render: {
30565
30883
  sections: [
@@ -30582,8 +30900,9 @@ function registerSetupCommands(program) {
30582
30900
  "after",
30583
30901
  `
30584
30902
  Notes:
30585
- Setup is idempotent. Bare setup resumes pending browser authorization.
30903
+ Setup is idempotent. Bare setup resumes the first incomplete or failed phase.
30586
30904
  It installs skills before auth and does not run quickstart.
30905
+ JSON output includes phase status and an exact retry command.
30587
30906
 
30588
30907
  Examples:
30589
30908
  deepline setup --json
@@ -30823,7 +31142,7 @@ import { spawn as spawn6, spawnSync as spawnSync3 } from "child_process";
30823
31142
  import {
30824
31143
  existsSync as existsSync14,
30825
31144
  mkdirSync as mkdirSync12,
30826
- readFileSync as readFileSync14,
31145
+ readFileSync as readFileSync15,
30827
31146
  unlinkSync as unlinkSync2,
30828
31147
  writeFileSync as writeFileSync17
30829
31148
  } from "fs";
@@ -30849,7 +31168,7 @@ function readPluginSkillsVersion() {
30849
31168
  const dir = activePluginSkillsDir();
30850
31169
  if (!dir) return "";
30851
31170
  try {
30852
- return readFileSync14(join17(dir, ".version"), "utf-8").trim();
31171
+ return readFileSync15(join17(dir, ".version"), "utf-8").trim();
30853
31172
  } catch {
30854
31173
  return "";
30855
31174
  }
@@ -30869,7 +31188,7 @@ function readSdkSkillsLocalVersion(baseUrl) {
30869
31188
  const path = existsSync14(sdkSkillsVersionPath(baseUrl)) ? sdkSkillsVersionPath(baseUrl) : legacySdkSkillsVersionPath(baseUrl);
30870
31189
  if (!existsSync14(path)) return "";
30871
31190
  try {
30872
- return readFileSync14(path, "utf-8").trim();
31191
+ return readFileSync15(path, "utf-8").trim();
30873
31192
  } catch {
30874
31193
  return "";
30875
31194
  }
@@ -30883,7 +31202,7 @@ function writeLocalSkillsVersion(baseUrl, version) {
30883
31202
  function writeUnavailableSkillsNotice(baseUrl, remoteVersion, skillNames) {
30884
31203
  const path = unavailableSkillsNoticePath(baseUrl);
30885
31204
  try {
30886
- if (existsSync14(path) && readFileSync14(path, "utf-8").trim() === remoteVersion) {
31205
+ if (existsSync14(path) && readFileSync15(path, "utf-8").trim() === remoteVersion) {
30887
31206
  return;
30888
31207
  }
30889
31208
  mkdirSync12(dirname15(path), { recursive: true });