@cleocode/cleo 2026.6.14 → 2026.6.15

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
@@ -32559,16 +32559,16 @@ async function orchestrateClassify(request, context, projectRoot, taskId) {
32559
32559
  }
32560
32560
  try {
32561
32561
  const { getCleoCantWorkflowsDir } = await import("@cleocode/core/internal");
32562
- const { readFileSync: readFileSync21, readdirSync: readdirSync3, existsSync: existsSync22 } = await import("node:fs");
32563
- const { join: join38 } = await import("node:path");
32562
+ const { readFileSync: readFileSync21, readdirSync: readdirSync3, existsSync: existsSync23 } = await import("node:fs");
32563
+ const { join: join39 } = await import("node:path");
32564
32564
  const workflowsDir = getCleoCantWorkflowsDir();
32565
32565
  const combined = `${request} ${context ?? ""}`.toLowerCase();
32566
32566
  const matches = [];
32567
- if (existsSync22(workflowsDir)) {
32567
+ if (existsSync23(workflowsDir)) {
32568
32568
  const files = readdirSync3(workflowsDir).filter((f) => f.endsWith(".cant"));
32569
32569
  for (const file of files) {
32570
32570
  try {
32571
- const src = readFileSync21(join38(workflowsDir, file), "utf-8");
32571
+ const src = readFileSync21(join39(workflowsDir, file), "utf-8");
32572
32572
  const teamMatch = /^team\s+(\S+):/m.exec(src);
32573
32573
  if (!teamMatch) continue;
32574
32574
  const teamName = teamMatch[1];
@@ -32583,12 +32583,12 @@ async function orchestrateClassify(request, context, projectRoot, taskId) {
32583
32583
  }
32584
32584
  }
32585
32585
  }
32586
- const localCantDir = join38(projectRoot, CLEO_DIR_NAME2, WORKFLOWS_SUBDIR);
32587
- if (existsSync22(localCantDir)) {
32586
+ const localCantDir = join39(projectRoot, CLEO_DIR_NAME2, WORKFLOWS_SUBDIR);
32587
+ if (existsSync23(localCantDir)) {
32588
32588
  const files = readdirSync3(localCantDir).filter((f) => f.endsWith(".cant"));
32589
32589
  for (const file of files) {
32590
32590
  try {
32591
- const src = readFileSync21(join38(localCantDir, file), "utf-8");
32591
+ const src = readFileSync21(join39(localCantDir, file), "utf-8");
32592
32592
  const teamMatch = /^team\s+(\S+):/m.exec(src);
32593
32593
  if (!teamMatch) continue;
32594
32594
  const teamName = teamMatch[1];
@@ -32783,11 +32783,11 @@ async function orchestrateAnalyzeParallelSafety(taskIds, projectRoot) {
32783
32783
  };
32784
32784
  }
32785
32785
  }
32786
- async function handleWorktreeComplete(taskId, projectRoot, resolve12) {
32786
+ async function handleWorktreeComplete(taskId, projectRoot, resolve13) {
32787
32787
  try {
32788
32788
  const { completeWorktreeForTask } = await import("@cleocode/core/internal");
32789
32789
  const result = completeWorktreeForTask(taskId, projectRoot, {
32790
- resolve: resolve12 ?? "auto"
32790
+ resolve: resolve13 ?? "auto"
32791
32791
  });
32792
32792
  if (result.outcome === "conflict") {
32793
32793
  return {
@@ -33447,10 +33447,10 @@ var init_orchestrate2 = __esm({
33447
33447
  startTime
33448
33448
  );
33449
33449
  const rawResolve = params.resolve;
33450
- const resolve12 = rawResolve === "manual" ? "manual" : rawResolve === "auto" ? "auto" : void 0;
33450
+ const resolve13 = rawResolve === "manual" ? "manual" : rawResolve === "auto" ? "auto" : void 0;
33451
33451
  const p = {
33452
33452
  taskId: params.taskId,
33453
- ...resolve12 !== void 0 ? { resolve: resolve12 } : {}
33453
+ ...resolve13 !== void 0 ? { resolve: resolve13 } : {}
33454
33454
  };
33455
33455
  return wrapResult(
33456
33456
  await coreOps2["worktree.complete"](p),
@@ -39833,7 +39833,7 @@ function wrapParseError(rawInput, parseErr, sourceLabel) {
39833
39833
  return new Error(`Invalid JSON in ${sourceLabel}: ${parseErr.message} (got: ${snippet2})`);
39834
39834
  }
39835
39835
  function readStdinJson(stdin2) {
39836
- return new Promise((resolve12, reject) => {
39836
+ return new Promise((resolve13, reject) => {
39837
39837
  const chunks = [];
39838
39838
  stdin2.on("data", (chunk) => {
39839
39839
  chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
@@ -39844,7 +39844,7 @@ function readStdinJson(stdin2) {
39844
39844
  stdin2.on("end", () => {
39845
39845
  const raw = Buffer.concat(chunks).toString("utf8");
39846
39846
  try {
39847
- resolve12(JSON.parse(raw));
39847
+ resolve13(JSON.parse(raw));
39848
39848
  } catch (err) {
39849
39849
  reject(wrapParseError(raw, err, "stdin"));
39850
39850
  }
@@ -41075,12 +41075,12 @@ var init_agent = __esm({
41075
41075
  transportConfig: {},
41076
41076
  isActive: true
41077
41077
  });
41078
- const { existsSync: existsSync22, mkdirSync: mkdirSync7, writeFileSync: writeFileSync7 } = await import("node:fs");
41079
- const { join: join38 } = await import("node:path");
41080
- const cantDir = join38(CLEO_DIR_NAME3, AGENTS_SUBDIR);
41081
- const cantPath = join38(cantDir, `${agentId}.cant`);
41078
+ const { existsSync: existsSync23, mkdirSync: mkdirSync7, writeFileSync: writeFileSync7 } = await import("node:fs");
41079
+ const { join: join39 } = await import("node:path");
41080
+ const cantDir = join39(CLEO_DIR_NAME3, AGENTS_SUBDIR);
41081
+ const cantPath = join39(cantDir, `${agentId}.cant`);
41082
41082
  let cantScaffolded = false;
41083
- if (!existsSync22(cantPath)) {
41083
+ if (!existsSync23(cantPath)) {
41084
41084
  mkdirSync7(cantDir, { recursive: true });
41085
41085
  const role = classification ?? "specialist";
41086
41086
  const cantContent = `---
@@ -41140,7 +41140,7 @@ agent ${agentId}:
41140
41140
  data: {
41141
41141
  agentId: credential.agentId,
41142
41142
  displayName: credential.displayName,
41143
- cantFile: cantScaffolded ? cantPath : existsSync22(cantPath) ? cantPath : null,
41143
+ cantFile: cantScaffolded ? cantPath : existsSync23(cantPath) ? cantPath : null,
41144
41144
  cantScaffolded
41145
41145
  }
41146
41146
  },
@@ -41259,8 +41259,8 @@ agent ${agentId}:
41259
41259
  try {
41260
41260
  const { AgentRegistryAccessor } = await import("@cleocode/core/agents");
41261
41261
  const { createRuntime } = await import("@cleocode/runtime");
41262
- const { existsSync: existsSync22, readFileSync: readFileSync21 } = await import("node:fs");
41263
- const { join: join38 } = await import("node:path");
41262
+ const { existsSync: existsSync23, readFileSync: readFileSync21 } = await import("node:fs");
41263
+ const { join: join39 } = await import("node:path");
41264
41264
  await openCleoDb("project");
41265
41265
  const registry = new AgentRegistryAccessor(getProjectRoot25());
41266
41266
  const credential = await registry.get(args.agentId);
@@ -41280,8 +41280,8 @@ agent ${agentId}:
41280
41280
  }
41281
41281
  let profile = null;
41282
41282
  let cantValidation = null;
41283
- const cantPath = args.cant ?? join38(CLEO_DIR_NAME3, AGENTS_SUBDIR, `${args.agentId}.cant`);
41284
- if (existsSync22(cantPath)) {
41283
+ const cantPath = args.cant ?? join39(CLEO_DIR_NAME3, AGENTS_SUBDIR, `${args.agentId}.cant`);
41284
+ if (existsSync23(cantPath)) {
41285
41285
  profile = readFileSync21(cantPath, "utf-8");
41286
41286
  try {
41287
41287
  const cantModule = await import("@cleocode/cant");
@@ -41805,8 +41805,8 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
41805
41805
  try {
41806
41806
  const { AgentRegistryAccessor } = await import("@cleocode/core/agents");
41807
41807
  const { createRuntime } = await import("@cleocode/runtime");
41808
- const { existsSync: existsSync22 } = await import("node:fs");
41809
- const { join: join38 } = await import("node:path");
41808
+ const { existsSync: existsSync23 } = await import("node:fs");
41809
+ const { join: join39 } = await import("node:path");
41810
41810
  await openCleoDb("project");
41811
41811
  const registry = new AgentRegistryAccessor(getProjectRoot25());
41812
41812
  const credential = await registry.get(args.agentId);
@@ -41823,8 +41823,8 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
41823
41823
  }
41824
41824
  await registry.update(args.agentId, { isActive: true });
41825
41825
  await registry.markUsed(args.agentId);
41826
- const cantPath = join38(CLEO_DIR_NAME3, AGENTS_SUBDIR, `${args.agentId}.cant`);
41827
- const hasProfile = existsSync22(cantPath);
41826
+ const cantPath = join39(CLEO_DIR_NAME3, AGENTS_SUBDIR, `${args.agentId}.cant`);
41827
+ const hasProfile = existsSync23(cantPath);
41828
41828
  const runtime = await createRuntime(registry, {
41829
41829
  agentId: args.agentId,
41830
41830
  pollIntervalMs: 5e3,
@@ -42617,10 +42617,10 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
42617
42617
  async run({ args }) {
42618
42618
  let tempDir = null;
42619
42619
  try {
42620
- const { existsSync: existsSync22 } = await import("node:fs");
42621
- const { basename, resolve: resolve12 } = await import("node:path");
42622
- const resolvedPath = resolve12(args.path);
42623
- if (!existsSync22(resolvedPath)) {
42620
+ const { existsSync: existsSync23 } = await import("node:fs");
42621
+ const { basename, resolve: resolve13 } = await import("node:path");
42622
+ const resolvedPath = resolve13(args.path);
42623
+ if (!existsSync23(resolvedPath)) {
42624
42624
  cliOutput(
42625
42625
  {
42626
42626
  success: false,
@@ -42745,11 +42745,11 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
42745
42745
  },
42746
42746
  async run({ args }) {
42747
42747
  try {
42748
- const { existsSync: existsSync22, statSync: statSync2 } = await import("node:fs");
42749
- const { resolve: resolve12, basename, dirname: dirname12 } = await import("node:path");
42748
+ const { existsSync: existsSync23, statSync: statSync2 } = await import("node:fs");
42749
+ const { resolve: resolve13, basename, dirname: dirname14 } = await import("node:path");
42750
42750
  const { execFileSync: execFileSync5 } = await import("node:child_process");
42751
- const resolvedDir = resolve12(args.dir);
42752
- if (!existsSync22(resolvedDir) || !statSync2(resolvedDir).isDirectory()) {
42751
+ const resolvedDir = resolve13(args.dir);
42752
+ if (!existsSync23(resolvedDir) || !statSync2(resolvedDir).isDirectory()) {
42753
42753
  cliOutput(
42754
42754
  {
42755
42755
  success: false,
@@ -42763,9 +42763,9 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
42763
42763
  process.exitCode = 4;
42764
42764
  return;
42765
42765
  }
42766
- const { join: join38 } = await import("node:path");
42767
- const personaPath = join38(resolvedDir, "persona.cant");
42768
- if (!existsSync22(personaPath)) {
42766
+ const { join: join39 } = await import("node:path");
42767
+ const personaPath = join39(resolvedDir, "persona.cant");
42768
+ if (!existsSync23(personaPath)) {
42769
42769
  cliOutput(
42770
42770
  {
42771
42771
  success: false,
@@ -42781,8 +42781,8 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
42781
42781
  }
42782
42782
  const agentName = basename(resolvedDir);
42783
42783
  const archiveName = `${agentName}.cantz`;
42784
- const archivePath = resolve12(archiveName);
42785
- const parentDir = dirname12(resolvedDir);
42784
+ const archivePath = resolve13(archiveName);
42785
+ const parentDir = dirname14(resolvedDir);
42786
42786
  try {
42787
42787
  execFileSync5("zip", ["-r", archivePath, agentName], {
42788
42788
  cwd: parentDir,
@@ -42812,7 +42812,7 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
42812
42812
  if (entry.isFile()) {
42813
42813
  fileCount++;
42814
42814
  } else if (entry.isDirectory()) {
42815
- countFiles(join38(dirPath, entry.name));
42815
+ countFiles(join39(dirPath, entry.name));
42816
42816
  }
42817
42817
  }
42818
42818
  };
@@ -43060,17 +43060,17 @@ Task ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${arg
43060
43060
  },
43061
43061
  async run({ args }) {
43062
43062
  try {
43063
- const { existsSync: existsSync22, readFileSync: readFileSync21, mkdirSync: mkdirSync7 } = await import("node:fs");
43064
- const { resolve: resolve12, join: join38 } = await import("node:path");
43065
- const specPath = resolve12(args.spec);
43066
- if (!existsSync22(specPath)) {
43063
+ const { existsSync: existsSync23, readFileSync: readFileSync21, mkdirSync: mkdirSync7 } = await import("node:fs");
43064
+ const { resolve: resolve13, join: join39 } = await import("node:path");
43065
+ const specPath = resolve13(args.spec);
43066
+ if (!existsSync23(specPath)) {
43067
43067
  cliError(`spec file not found: ${specPath}`, 4, { name: "E_NOT_FOUND" });
43068
43068
  process.exitCode = 4;
43069
43069
  return;
43070
43070
  }
43071
43071
  const specContent = readFileSync21(specPath, "utf-8");
43072
43072
  const projectRoot = getProjectRoot25();
43073
- const outputDir = args["output-dir"] ? resolve12(args["output-dir"]) : join38(projectRoot, ".cleo", "cant", "agents");
43073
+ const outputDir = args["output-dir"] ? resolve13(args["output-dir"]) : join39(projectRoot, ".cleo", "cant", "agents");
43074
43074
  mkdirSync7(outputDir, { recursive: true });
43075
43075
  if (args["dry-run"]) {
43076
43076
  cliOutput(
@@ -44255,7 +44255,7 @@ async function _headlessPkceFlow(provider, authUrl, expectedState) {
44255
44255
  After approving, paste the full redirect URL (\u2026?code=\u2026&state=\u2026) or the code shown:
44256
44256
  `
44257
44257
  );
44258
- return new Promise((resolve12, reject) => {
44258
+ return new Promise((resolve13, reject) => {
44259
44259
  process.stdin.setEncoding("utf8");
44260
44260
  process.stdin.once("data", (chunk) => {
44261
44261
  const parsed = parseAuthorizationInput(String(chunk));
@@ -44267,13 +44267,13 @@ async function _headlessPkceFlow(provider, authUrl, expectedState) {
44267
44267
  reject(new Error("OAuth state mismatch \u2014 pasted redirect does not match this login"));
44268
44268
  return;
44269
44269
  }
44270
- resolve12(parsed.code);
44270
+ resolve13(parsed.code);
44271
44271
  });
44272
44272
  process.stdin.resume();
44273
44273
  });
44274
44274
  }
44275
44275
  async function _localCallbackPkceFlow(provider, authUrl, expectedState, port) {
44276
- return new Promise((resolve12) => {
44276
+ return new Promise((resolve13) => {
44277
44277
  const server = createServer((req, res) => {
44278
44278
  const url = new URL(req.url ?? "/", `http://localhost:${port}`);
44279
44279
  const code = url.searchParams.get("code");
@@ -44283,7 +44283,7 @@ async function _localCallbackPkceFlow(provider, authUrl, expectedState, port) {
44283
44283
  if (error) {
44284
44284
  res.end(`<h1>Authorization failed</h1><p>${error}</p><p>You may close this tab.</p>`);
44285
44285
  server.close();
44286
- resolve12({
44286
+ resolve13({
44287
44287
  error: {
44288
44288
  code: "E_PKCE_AUTH_DENIED",
44289
44289
  codeName: "E_PKCE_AUTH_DENIED",
@@ -44295,7 +44295,7 @@ async function _localCallbackPkceFlow(provider, authUrl, expectedState, port) {
44295
44295
  if (!code || state !== expectedState) {
44296
44296
  res.end("<h1>Invalid callback</h1><p>You may close this tab.</p>");
44297
44297
  server.close();
44298
- resolve12({
44298
+ resolve13({
44299
44299
  error: {
44300
44300
  code: "E_PKCE_INVALID_CALLBACK",
44301
44301
  codeName: "E_PKCE_INVALID_CALLBACK",
@@ -44306,7 +44306,7 @@ async function _localCallbackPkceFlow(provider, authUrl, expectedState, port) {
44306
44306
  }
44307
44307
  res.end("<h1>Authorized</h1><p>You may close this tab and return to your terminal.</p>");
44308
44308
  server.close();
44309
- resolve12({ code });
44309
+ resolve13({ code });
44310
44310
  });
44311
44311
  server.listen(port, "localhost", () => {
44312
44312
  process.stderr.write("\n");
@@ -44411,7 +44411,7 @@ function _generateState() {
44411
44411
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
44412
44412
  }
44413
44413
  function _findFreePort() {
44414
- return new Promise((resolve12, reject) => {
44414
+ return new Promise((resolve13, reject) => {
44415
44415
  const srv = createServer();
44416
44416
  srv.listen(0, "localhost", () => {
44417
44417
  const addr = srv.address();
@@ -44421,7 +44421,7 @@ function _findFreePort() {
44421
44421
  return;
44422
44422
  }
44423
44423
  const port = addr.port;
44424
- srv.close(() => resolve12(port));
44424
+ srv.close(() => resolve13(port));
44425
44425
  });
44426
44426
  srv.on("error", reject);
44427
44427
  });
@@ -44756,8 +44756,8 @@ function detectAuthType(provider, token) {
44756
44756
  async function promptYesNo(question) {
44757
44757
  const rl = createInterface2({ input: process.stdin, output: process.stderr });
44758
44758
  try {
44759
- const answer = await new Promise((resolve12) => {
44760
- rl.question(question, (a) => resolve12(a));
44759
+ const answer = await new Promise((resolve13) => {
44760
+ rl.question(question, (a) => resolve13(a));
44761
44761
  });
44762
44762
  const clean = answer.trim().toLowerCase();
44763
44763
  return clean === "y" || clean === "yes";
@@ -45689,12 +45689,12 @@ async function promptPassphrase() {
45689
45689
  "Cannot prompt for passphrase: stdin is not a TTY. Set the CLEO_BACKUP_PASSPHRASE environment variable for non-interactive use."
45690
45690
  );
45691
45691
  }
45692
- return new Promise((resolve12) => {
45692
+ return new Promise((resolve13) => {
45693
45693
  process.stdout.write("Passphrase: ");
45694
45694
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
45695
45695
  rl.question("", (answer) => {
45696
45696
  rl.close();
45697
- resolve12(answer.trim());
45697
+ resolve13(answer.trim());
45698
45698
  });
45699
45699
  });
45700
45700
  }
@@ -46804,11 +46804,11 @@ var init_caamp = __esm({
46804
46804
  }
46805
46805
  if (args["dry-run"]) {
46806
46806
  const { parseCaampBlocks } = await import("@cleocode/caamp");
46807
- const { existsSync: existsSync22 } = await import("node:fs");
46807
+ const { existsSync: existsSync23 } = await import("node:fs");
46808
46808
  const { readFile: readFile9 } = await import("node:fs/promises");
46809
46809
  const dryResults = [];
46810
46810
  for (const filePath of filePaths) {
46811
- if (!existsSync22(filePath)) {
46811
+ if (!existsSync23(filePath)) {
46812
46812
  dryResults.push({ filePath, exists: false, blockCount: 0, wouldRemove: 0 });
46813
46813
  continue;
46814
46814
  }
@@ -47767,11 +47767,11 @@ var init_check2 = __esm({
47767
47767
  },
47768
47768
  async run({ args }) {
47769
47769
  const { spawnSync } = await import("node:child_process");
47770
- const { existsSync: existsSync22 } = await import("node:fs");
47771
- const { join: join38, resolve: resolve12 } = await import("node:path");
47770
+ const { existsSync: existsSync23 } = await import("node:fs");
47771
+ const { join: join39, resolve: resolve13 } = await import("node:path");
47772
47772
  const strict = Boolean(args.strict);
47773
47773
  const jsonOnly = Boolean(args.json);
47774
- const repoRoot = resolve12(process.cwd());
47774
+ const repoRoot = resolve13(process.cwd());
47775
47775
  const gates = [
47776
47776
  {
47777
47777
  id: "gate-1",
@@ -47815,8 +47815,8 @@ var init_check2 = __esm({
47815
47815
  const results = [];
47816
47816
  let anyFailed = false;
47817
47817
  for (const gate of gates) {
47818
- const scriptPath = join38(repoRoot, gate.script);
47819
- if (!existsSync22(scriptPath)) {
47818
+ const scriptPath = join39(repoRoot, gate.script);
47819
+ if (!existsSync23(scriptPath)) {
47820
47820
  results.push({
47821
47821
  id: gate.id,
47822
47822
  task: gate.task,
@@ -48270,9 +48270,9 @@ var init_code = __esm({
48270
48270
  async run({ args }) {
48271
48271
  await requireTreeSitter();
48272
48272
  const { smartOutline } = await import("@cleocode/core/internal");
48273
- const { join: join38 } = await import("node:path");
48273
+ const { join: join39 } = await import("node:path");
48274
48274
  const root = process.cwd();
48275
- const absPath = args.file.startsWith("/") ? args.file : join38(root, args.file);
48275
+ const absPath = args.file.startsWith("/") ? args.file : join39(root, args.file);
48276
48276
  const result = smartOutline(absPath, root);
48277
48277
  if (result.errors.length > 0 && result.symbols.length === 0) {
48278
48278
  cliError(result.errors.join(", "), 1, { name: "E_OUTLINE_FAILED" });
@@ -48363,9 +48363,9 @@ var init_code = __esm({
48363
48363
  async run({ args }) {
48364
48364
  await requireTreeSitter();
48365
48365
  const { smartUnfold } = await import("@cleocode/core/internal");
48366
- const { join: join38 } = await import("node:path");
48366
+ const { join: join39 } = await import("node:path");
48367
48367
  const root = process.cwd();
48368
- const absPath = args.file.startsWith("/") ? args.file : join38(root, args.file);
48368
+ const absPath = args.file.startsWith("/") ? args.file : join39(root, args.file);
48369
48369
  const result = smartUnfold(absPath, args.symbol, root);
48370
48370
  if (!result.found) {
48371
48371
  const errs = result.errors.length > 0 ? `: ${result.errors.join(", ")}` : "";
@@ -50070,14 +50070,246 @@ var init_current = __esm({
50070
50070
  }
50071
50071
  });
50072
50072
 
50073
+ // packages/cleo/src/cli/web-subsystem.ts
50074
+ import { execFileSync as execFileSync2, spawn as spawn2 } from "node:child_process";
50075
+ import { existsSync as existsSync11 } from "node:fs";
50076
+ import { mkdir, open, readFile as readFile3, rm, stat, writeFile } from "node:fs/promises";
50077
+ import { dirname as dirname5, join as join16, resolve as resolve4 } from "node:path";
50078
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
50079
+ import { getCleoHome as getCleoHome2 } from "@cleocode/core";
50080
+ import { defineSubsystem } from "@cleocode/runtime/daemon";
50081
+ function resolveStudioDir() {
50082
+ const envOverride = process.env["CLEO_STUDIO_DIR"];
50083
+ if (envOverride !== void 0 && envOverride.length > 0 && existsSync11(envOverride)) {
50084
+ return envOverride;
50085
+ }
50086
+ try {
50087
+ const pkgRoot = resolve4(dirname5(fileURLToPath3(import.meta.url)), "..", "..", "..");
50088
+ const bundled = join16(pkgRoot, "studio-dist");
50089
+ if (existsSync11(join16(bundled, "index.js"))) {
50090
+ return bundled;
50091
+ }
50092
+ } catch {
50093
+ }
50094
+ const projectRoot = process.env["CLEO_ROOT"] ?? process.cwd();
50095
+ const devPath = join16(projectRoot, "packages", "studio", "build");
50096
+ if (existsSync11(join16(devPath, "index.js"))) {
50097
+ return devPath;
50098
+ }
50099
+ return void 0;
50100
+ }
50101
+ function resolveStudioStaticDir() {
50102
+ const buildDir = resolveStudioDir();
50103
+ if (buildDir === void 0) return void 0;
50104
+ const clientDir = join16(buildDir, "client");
50105
+ return existsSync11(clientDir) ? clientDir : void 0;
50106
+ }
50107
+ function getWebPaths() {
50108
+ const cleoHome = getCleoHome2();
50109
+ return {
50110
+ pidFile: join16(cleoHome, "web-server.pid"),
50111
+ configFile: join16(cleoHome, "web-server.json"),
50112
+ logDir: join16(cleoHome, "logs"),
50113
+ logFile: join16(cleoHome, "logs", "web-server.log")
50114
+ };
50115
+ }
50116
+ function isWebProcessRunning(pid) {
50117
+ try {
50118
+ process.kill(pid, 0);
50119
+ return true;
50120
+ } catch {
50121
+ return false;
50122
+ }
50123
+ }
50124
+ async function getWebStatus() {
50125
+ const { pidFile, configFile } = getWebPaths();
50126
+ try {
50127
+ const pidStr = (await readFile3(pidFile, "utf-8")).trim();
50128
+ const pid = Number.parseInt(pidStr, 10);
50129
+ if (Number.isNaN(pid) || !isWebProcessRunning(pid)) {
50130
+ return { running: false, pid: null, port: null, host: null, url: null };
50131
+ }
50132
+ let port = WEB_DEFAULT_PORT;
50133
+ let host = WEB_DEFAULT_HOST;
50134
+ try {
50135
+ const config = JSON.parse(await readFile3(configFile, "utf-8"));
50136
+ port = config.port ?? WEB_DEFAULT_PORT;
50137
+ host = config.host ?? WEB_DEFAULT_HOST;
50138
+ } catch {
50139
+ }
50140
+ return { running: true, pid, port, host, url: `http://${host}:${port}` };
50141
+ } catch {
50142
+ return { running: false, pid: null, port: null, host: null, url: null };
50143
+ }
50144
+ }
50145
+ function createWebSubsystem(opts = {}) {
50146
+ const port = opts.port ?? WEB_DEFAULT_PORT;
50147
+ const host = opts.host ?? WEB_DEFAULT_HOST;
50148
+ let live;
50149
+ return defineSubsystem({
50150
+ name: WEB_SUBSYSTEM_NAME,
50151
+ async start() {
50152
+ const { pidFile, configFile, logFile, logDir } = getWebPaths();
50153
+ const existing = await getWebStatus();
50154
+ if (existing.running && existing.pid !== null) {
50155
+ const ctx2 = {
50156
+ pid: existing.pid,
50157
+ pidFile,
50158
+ logFile,
50159
+ port: existing.port ?? port,
50160
+ host: existing.host ?? host
50161
+ };
50162
+ live = ctx2;
50163
+ return ctx2;
50164
+ }
50165
+ const resolvedStudioDir = resolveStudioDir();
50166
+ const studioDir = resolvedStudioDir ?? join16(process.env["CLEO_ROOT"] ?? process.cwd(), "packages", "studio", "build");
50167
+ const webIndexPath = join16(studioDir, "index.js");
50168
+ await mkdir(logDir, { recursive: true });
50169
+ await writeFile(
50170
+ configFile,
50171
+ JSON.stringify({ port, host, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
50172
+ );
50173
+ try {
50174
+ await stat(webIndexPath);
50175
+ } catch {
50176
+ const projectRoot = process.env["CLEO_ROOT"] ?? process.cwd();
50177
+ try {
50178
+ execFileSync2("pnpm", ["--filter", "@cleocode/studio", "run", "build"], {
50179
+ cwd: projectRoot,
50180
+ stdio: "ignore"
50181
+ });
50182
+ } catch {
50183
+ throw new Error(
50184
+ `Studio build failed. Run: pnpm --filter @cleocode/studio run build
50185
+ Logs: ${logFile}`
50186
+ );
50187
+ }
50188
+ }
50189
+ const logFileHandle = await open(logFile, "a");
50190
+ const serverProcess = spawn2("node", [webIndexPath], {
50191
+ cwd: studioDir,
50192
+ env: {
50193
+ ...process.env,
50194
+ HOST: host,
50195
+ PORT: String(port),
50196
+ CLEO_ROOT: process.env["CLEO_ROOT"] ?? process.cwd()
50197
+ },
50198
+ detached: true,
50199
+ stdio: ["ignore", logFileHandle.fd, logFileHandle.fd]
50200
+ });
50201
+ serverProcess.unref();
50202
+ const pidFileTmp = `${pidFile}.tmp`;
50203
+ await writeFile(pidFileTmp, String(serverProcess.pid));
50204
+ await rm(pidFile, { force: true });
50205
+ await writeFile(pidFile, String(serverProcess.pid));
50206
+ await rm(pidFileTmp, { force: true });
50207
+ await logFileHandle.close();
50208
+ let started = false;
50209
+ for (let i = 0; i < STARTUP_POLL_ITERATIONS; i++) {
50210
+ try {
50211
+ const response = await fetch(`http://${host}:${port}/api/health`);
50212
+ if (response.ok) {
50213
+ started = true;
50214
+ break;
50215
+ }
50216
+ } catch {
50217
+ }
50218
+ await new Promise((resolve13) => setTimeout(resolve13, 500));
50219
+ }
50220
+ if (!started) {
50221
+ try {
50222
+ process.kill(serverProcess.pid, "SIGTERM");
50223
+ } catch {
50224
+ }
50225
+ await rm(pidFile, { force: true });
50226
+ throw new Error("Studio web server failed to start within 15 seconds");
50227
+ }
50228
+ const ctx = {
50229
+ pid: serverProcess.pid,
50230
+ pidFile,
50231
+ logFile,
50232
+ port,
50233
+ host
50234
+ };
50235
+ live = ctx;
50236
+ return ctx;
50237
+ },
50238
+ healthProbe() {
50239
+ if (live === void 0) {
50240
+ const stopped = "stopped";
50241
+ return {
50242
+ child_id: WEB_SUBSYSTEM_NAME,
50243
+ pid: 0,
50244
+ state: stopped,
50245
+ restart_count: 0,
50246
+ detail: "not started"
50247
+ };
50248
+ }
50249
+ const alive = isWebProcessRunning(live.pid);
50250
+ const state = alive ? "running" : "stopped";
50251
+ return {
50252
+ child_id: WEB_SUBSYSTEM_NAME,
50253
+ pid: alive ? live.pid : 0,
50254
+ state,
50255
+ restart_count: 0,
50256
+ detail: alive ? `url=http://${live.host}:${live.port} pid=${live.pid}` : `pid=${live.pid} exited`
50257
+ };
50258
+ },
50259
+ async shutdown(context) {
50260
+ const { pidFile } = context;
50261
+ if (!isWebProcessRunning(context.pid)) {
50262
+ await rm(pidFile, { force: true });
50263
+ live = void 0;
50264
+ return;
50265
+ }
50266
+ try {
50267
+ if (process.platform === "win32") {
50268
+ spawn2("taskkill", ["/PID", String(context.pid), "/T"], { stdio: "ignore" });
50269
+ } else {
50270
+ process.kill(context.pid, "SIGTERM");
50271
+ }
50272
+ } catch {
50273
+ }
50274
+ for (let i = 0; i < SIGTERM_GRACE_ITERATIONS; i++) {
50275
+ if (!isWebProcessRunning(context.pid)) break;
50276
+ await new Promise((resolve13) => setTimeout(resolve13, 500));
50277
+ }
50278
+ if (isWebProcessRunning(context.pid)) {
50279
+ try {
50280
+ if (process.platform === "win32") {
50281
+ spawn2("taskkill", ["/PID", String(context.pid), "/F", "/T"], { stdio: "ignore" });
50282
+ } else {
50283
+ process.kill(context.pid, "SIGKILL");
50284
+ }
50285
+ } catch {
50286
+ }
50287
+ }
50288
+ await rm(pidFile, { force: true });
50289
+ live = void 0;
50290
+ }
50291
+ });
50292
+ }
50293
+ var WEB_DEFAULT_PORT, WEB_DEFAULT_HOST, WEB_SUBSYSTEM_NAME, SIGTERM_GRACE_ITERATIONS, STARTUP_POLL_ITERATIONS;
50294
+ var init_web_subsystem = __esm({
50295
+ "packages/cleo/src/cli/web-subsystem.ts"() {
50296
+ "use strict";
50297
+ WEB_DEFAULT_PORT = 3456;
50298
+ WEB_DEFAULT_HOST = "127.0.0.1";
50299
+ WEB_SUBSYSTEM_NAME = "cleo-web";
50300
+ SIGTERM_GRACE_ITERATIONS = 60;
50301
+ STARTUP_POLL_ITERATIONS = 30;
50302
+ }
50303
+ });
50304
+
50073
50305
  // packages/cleo/src/cli/commands/daemon.ts
50074
50306
  var daemon_exports = {};
50075
50307
  __export(daemon_exports, {
50076
50308
  daemonCommand: () => daemonCommand
50077
50309
  });
50078
- import { existsSync as existsSync11 } from "node:fs";
50079
- import { join as join16 } from "node:path";
50080
- import { fileURLToPath as fileURLToPath3 } from "node:url";
50310
+ import { existsSync as existsSync12 } from "node:fs";
50311
+ import { join as join17 } from "node:path";
50312
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
50081
50313
  import { getGCDaemonStatus, spawnGCDaemon, stopGCDaemon } from "@cleocode/core/gc/daemon.js";
50082
50314
  import {
50083
50315
  bootstrapDaemon as bootstrapSentientDaemon,
@@ -50086,9 +50318,9 @@ import {
50086
50318
  import { resolveLegacyCleoDir as resolveLegacyCleoDir2 } from "@cleocode/paths";
50087
50319
  import { serveGateway } from "@cleocode/runtime/gateway";
50088
50320
  async function blockUntilSignal(handle) {
50089
- await new Promise((resolve12) => {
50321
+ await new Promise((resolve13) => {
50090
50322
  const onSignal = () => {
50091
- void handle.close().finally(resolve12);
50323
+ void handle.close().finally(resolve13);
50092
50324
  };
50093
50325
  process.once("SIGTERM", onSignal);
50094
50326
  process.once("SIGINT", onSignal);
@@ -50151,10 +50383,10 @@ async function showDaemonStatus(cleoDir, projectRoot) {
50151
50383
  }
50152
50384
  }
50153
50385
  function resolveDaemonInstallerScript() {
50154
- const filePath = fileURLToPath3(import.meta.url);
50155
- const candidate1 = join16(filePath, "..", "..", "..", "scripts", "install-daemon-service.mjs");
50156
- if (existsSync11(candidate1)) return candidate1;
50157
- const candidate2 = join16(
50386
+ const filePath = fileURLToPath4(import.meta.url);
50387
+ const candidate1 = join17(filePath, "..", "..", "..", "scripts", "install-daemon-service.mjs");
50388
+ if (existsSync12(candidate1)) return candidate1;
50389
+ const candidate2 = join17(
50158
50390
  filePath,
50159
50391
  "..",
50160
50392
  "..",
@@ -50173,6 +50405,7 @@ var init_daemon2 = __esm({
50173
50405
  init_cli();
50174
50406
  init_subcommand_guard();
50175
50407
  init_renderers();
50408
+ init_web_subsystem();
50176
50409
  DEFAULT_GATEWAY_HTTP_PORT = 7777;
50177
50410
  startCommand3 = defineCommand({
50178
50411
  meta: { name: "start", description: "Spawn the GC daemon as a detached background process" },
@@ -50228,13 +50461,13 @@ var init_daemon2 = __esm({
50228
50461
  {
50229
50462
  pid,
50230
50463
  cleoDir,
50231
- logs: join16(cleoDir, "logs", "gc.log"),
50464
+ logs: join17(cleoDir, "logs", "gc.log"),
50232
50465
  message: `GC daemon started (PID ${pid})`
50233
50466
  },
50234
50467
  {
50235
50468
  command: "daemon",
50236
50469
  operation: "daemon.start",
50237
- message: `GC daemon started (PID ${pid}) \u2014 Logs: ${join16(cleoDir, "logs", "gc.log")}`
50470
+ message: `GC daemon started (PID ${pid}) \u2014 Logs: ${join17(cleoDir, "logs", "gc.log")}`
50238
50471
  }
50239
50472
  );
50240
50473
  } catch (err) {
@@ -50427,7 +50660,13 @@ var init_daemon2 = __esm({
50427
50660
  process.exit(2);
50428
50661
  }
50429
50662
  const host = args.host ?? "127.0.0.1";
50430
- const handle = await serveGateway({ handler: createCliGatewayHandler(), port, host });
50663
+ const studioStaticDir = resolveStudioStaticDir();
50664
+ const handle = await serveGateway({
50665
+ handler: createCliGatewayHandler(),
50666
+ port,
50667
+ host,
50668
+ studioStaticDir
50669
+ });
50431
50670
  cliOutput(
50432
50671
  {
50433
50672
  pid: process.pid,
@@ -50435,12 +50674,13 @@ var init_daemon2 = __esm({
50435
50674
  port: handle.port,
50436
50675
  scope: handle.scope,
50437
50676
  facade: "/v1",
50677
+ studioUrl: studioStaticDir !== void 0 ? `http://${handle.host}:${handle.port}/studio/` : null,
50438
50678
  message: `HTTP gateway listening on http://${handle.host}:${handle.port}/v1`
50439
50679
  },
50440
50680
  {
50441
50681
  command: "daemon",
50442
50682
  operation: "daemon.serve",
50443
- message: `HTTP gateway listening on http://${handle.host}:${handle.port}/v1 (PID ${process.pid})`
50683
+ message: studioStaticDir !== void 0 ? `HTTP gateway listening on http://${handle.host}:${handle.port}/v1 (Studio: http://${handle.host}:${handle.port}/studio/, PID ${process.pid})` : `HTTP gateway listening on http://${handle.host}:${handle.port}/v1 (PID ${process.pid})`
50444
50684
  }
50445
50685
  );
50446
50686
  await blockUntilSignal(handle);
@@ -51157,15 +51397,15 @@ var detect_drift_exports = {};
51157
51397
  __export(detect_drift_exports, {
51158
51398
  detectDriftCommand: () => detectDriftCommand
51159
51399
  });
51160
- import { existsSync as existsSync12, readdirSync, readFileSync as readFileSync11 } from "node:fs";
51161
- import { dirname as dirname5, join as join17 } from "node:path";
51400
+ import { existsSync as existsSync13, readdirSync, readFileSync as readFileSync11 } from "node:fs";
51401
+ import { dirname as dirname6, join as join18 } from "node:path";
51162
51402
  function findProjectRoot() {
51163
51403
  let currentDir = process.cwd();
51164
51404
  while (currentDir !== "/") {
51165
- if (existsSync12(join17(currentDir, "package.json"))) {
51405
+ if (existsSync13(join18(currentDir, "package.json"))) {
51166
51406
  return currentDir;
51167
51407
  }
51168
- const parent = dirname5(currentDir);
51408
+ const parent = dirname6(currentDir);
51169
51409
  if (parent === currentDir) break;
51170
51410
  currentDir = parent;
51171
51411
  }
@@ -51186,8 +51426,8 @@ var init_detect_drift = __esm({
51186
51426
  },
51187
51427
  async run() {
51188
51428
  const projectRoot = findProjectRoot();
51189
- const isCleoRepo = existsSync12(join17(projectRoot, "packages", "cleo", "src"));
51190
- const cleoSrcRoot = isCleoRepo ? join17(projectRoot, "packages", "cleo", "src") : join17(projectRoot, "src");
51429
+ const isCleoRepo = existsSync13(join18(projectRoot, "packages", "cleo", "src"));
51430
+ const cleoSrcRoot = isCleoRepo ? join18(projectRoot, "packages", "cleo", "src") : join18(projectRoot, "src");
51191
51431
  const safeRead2 = (filePath) => {
51192
51432
  try {
51193
51433
  return readFileSync11(filePath, "utf-8");
@@ -51201,8 +51441,8 @@ var init_detect_drift = __esm({
51201
51441
  checks: [],
51202
51442
  recommendations: []
51203
51443
  };
51204
- const injPath = join17(projectRoot, CLEO_DIR_NAME3, TEMPLATES_SUBDIR, CLEO_INJECTION_MD);
51205
- if (existsSync12(injPath)) {
51444
+ const injPath = join18(projectRoot, CLEO_DIR_NAME3, TEMPLATES_SUBDIR, CLEO_INJECTION_MD);
51445
+ if (existsSync13(injPath)) {
51206
51446
  const content = safeRead2(injPath);
51207
51447
  userResult.checks.push({
51208
51448
  name: "Agent injection",
@@ -51253,10 +51493,10 @@ var init_detect_drift = __esm({
51253
51493
  }
51254
51494
  };
51255
51495
  try {
51256
- const specPath = join17(projectRoot, "docs", "specs", "CLEO-OPERATION-CONSTITUTION.md");
51257
- const registryPath = join17(cleoSrcRoot, "dispatch", "registry.ts");
51258
- const dispatchDomainsDir = join17(cleoSrcRoot, "dispatch", "domains");
51259
- if (!existsSync12(specPath)) {
51496
+ const specPath = join18(projectRoot, "docs", "specs", "CLEO-OPERATION-CONSTITUTION.md");
51497
+ const registryPath = join18(cleoSrcRoot, "dispatch", "registry.ts");
51498
+ const dispatchDomainsDir = join18(cleoSrcRoot, "dispatch", "domains");
51499
+ if (!existsSync13(specPath)) {
51260
51500
  addCheck("Gateway-to-spec sync", "fail", "CLEO-OPERATION-CONSTITUTION.md missing", [
51261
51501
  {
51262
51502
  severity: "error",
@@ -51266,7 +51506,7 @@ var init_detect_drift = __esm({
51266
51506
  recommendation: "Create docs/specs/CLEO-OPERATION-CONSTITUTION.md with canonical operation definitions"
51267
51507
  }
51268
51508
  ]);
51269
- } else if (!existsSync12(registryPath) || !existsSync12(dispatchDomainsDir)) {
51509
+ } else if (!existsSync13(registryPath) || !existsSync13(dispatchDomainsDir)) {
51270
51510
  addCheck("Gateway-to-spec sync", "fail", "Dispatch registry or domains missing", [
51271
51511
  {
51272
51512
  severity: "error",
@@ -51324,9 +51564,9 @@ var init_detect_drift = __esm({
51324
51564
  ]);
51325
51565
  }
51326
51566
  try {
51327
- const cliDir = join17(cleoSrcRoot, "cli", "commands");
51328
- const coreDir = isCleoRepo ? join17(projectRoot, "packages", "core", "src") : join17(projectRoot, "src", "core");
51329
- if (!existsSync12(cliDir)) {
51567
+ const cliDir = join18(cleoSrcRoot, "cli", "commands");
51568
+ const coreDir = isCleoRepo ? join18(projectRoot, "packages", "core", "src") : join18(projectRoot, "src", "core");
51569
+ if (!existsSync13(cliDir)) {
51330
51570
  addCheck("CLI-to-core sync", "fail", "CLI commands directory missing", [
51331
51571
  {
51332
51572
  severity: "error",
@@ -51335,7 +51575,7 @@ var init_detect_drift = __esm({
51335
51575
  recommendation: "Verify TypeScript source structure is intact"
51336
51576
  }
51337
51577
  ]);
51338
- } else if (!existsSync12(coreDir)) {
51578
+ } else if (!existsSync13(coreDir)) {
51339
51579
  addCheck("CLI-to-core sync", "fail", "Core directory missing", [
51340
51580
  {
51341
51581
  severity: "error",
@@ -51352,8 +51592,8 @@ var init_detect_drift = __esm({
51352
51592
  addCheck("CLI-to-core sync", "fail", `Error: ${getErrorMessage(e)}`);
51353
51593
  }
51354
51594
  try {
51355
- const domainsDir = join17(cleoSrcRoot, "dispatch", "domains");
51356
- if (!existsSync12(domainsDir)) {
51595
+ const domainsDir = join18(cleoSrcRoot, "dispatch", "domains");
51596
+ if (!existsSync13(domainsDir)) {
51357
51597
  addCheck("Domain handler coverage", "fail", "Dispatch domains directory missing", [
51358
51598
  {
51359
51599
  severity: "error",
@@ -51370,8 +51610,8 @@ var init_detect_drift = __esm({
51370
51610
  addCheck("Domain handler coverage", "fail", `Error: ${getErrorMessage(e)}`);
51371
51611
  }
51372
51612
  try {
51373
- const matrixPath = join17(cleoSrcRoot, "dispatch", "lib", "capability-matrix.ts");
51374
- if (!existsSync12(matrixPath)) {
51613
+ const matrixPath = join18(cleoSrcRoot, "dispatch", "lib", "capability-matrix.ts");
51614
+ if (!existsSync13(matrixPath)) {
51375
51615
  addCheck("Capability matrix", "fail", "Capability matrix missing", [
51376
51616
  {
51377
51617
  severity: "error",
@@ -51387,8 +51627,8 @@ var init_detect_drift = __esm({
51387
51627
  addCheck("Capability matrix", "fail", `Error: ${getErrorMessage(e)}`);
51388
51628
  }
51389
51629
  try {
51390
- const schemaPath = join17(projectRoot, "src", "store", "schema.ts");
51391
- if (!existsSync12(schemaPath)) {
51630
+ const schemaPath = join18(projectRoot, "src", "store", "schema.ts");
51631
+ if (!existsSync13(schemaPath)) {
51392
51632
  addCheck("Schema validation", "fail", "Schema definition missing", [
51393
51633
  {
51394
51634
  severity: "error",
@@ -51422,10 +51662,10 @@ var init_detect_drift = __esm({
51422
51662
  addCheck("Schema validation", "fail", `Error: ${getErrorMessage(e)}`);
51423
51663
  }
51424
51664
  try {
51425
- const visionPath = join17(projectRoot, "docs", "concepts", "CLEO-VISION.md");
51426
- const specPath = join17(projectRoot, "docs", "specs", "CLEO-PORTABLE-PROJECT-BRAIN-SPEC.md");
51665
+ const visionPath = join18(projectRoot, "docs", "concepts", "CLEO-VISION.md");
51666
+ const specPath = join18(projectRoot, "docs", "specs", "CLEO-PORTABLE-PROJECT-BRAIN-SPEC.md");
51427
51667
  const issues = [];
51428
- if (!existsSync12(visionPath)) {
51668
+ if (!existsSync13(visionPath)) {
51429
51669
  issues.push({
51430
51670
  severity: "error",
51431
51671
  category: "vision",
@@ -51434,7 +51674,7 @@ var init_detect_drift = __esm({
51434
51674
  recommendation: "Create docs/concepts/CLEO-VISION.md with project vision"
51435
51675
  });
51436
51676
  }
51437
- if (!existsSync12(specPath)) {
51677
+ if (!existsSync13(specPath)) {
51438
51678
  issues.push({
51439
51679
  severity: "error",
51440
51680
  category: "spec",
@@ -51478,8 +51718,8 @@ var init_detect_drift = __esm({
51478
51718
  addCheck("Canonical identity", "fail", `Error: ${getErrorMessage(e)}`);
51479
51719
  }
51480
51720
  try {
51481
- const injectionPath = join17(projectRoot, CLEO_DIR_NAME3, TEMPLATES_SUBDIR, CLEO_INJECTION_MD);
51482
- if (!existsSync12(injectionPath)) {
51721
+ const injectionPath = join18(projectRoot, CLEO_DIR_NAME3, TEMPLATES_SUBDIR, CLEO_INJECTION_MD);
51722
+ if (!existsSync13(injectionPath)) {
51483
51723
  addCheck("Agent injection", "fail", "Agent injection template missing", [
51484
51724
  {
51485
51725
  severity: "error",
@@ -51508,8 +51748,8 @@ var init_detect_drift = __esm({
51508
51748
  addCheck("Agent injection", "fail", `Error: ${getErrorMessage(e)}`);
51509
51749
  }
51510
51750
  try {
51511
- const exitCodesPath = join17(cleoSrcRoot, "dispatch", "lib", "exit-codes.ts");
51512
- if (!existsSync12(exitCodesPath)) {
51751
+ const exitCodesPath = join18(cleoSrcRoot, "dispatch", "lib", "exit-codes.ts");
51752
+ if (!existsSync13(exitCodesPath)) {
51513
51753
  addCheck("Exit codes", "fail", "Exit codes definition missing", [
51514
51754
  {
51515
51755
  severity: "error",
@@ -52079,21 +52319,21 @@ var init_set_alias = __esm({
52079
52319
  });
52080
52320
 
52081
52321
  // packages/cleo/src/viewer/pidfile.ts
52082
- import { mkdir, readFile as readFile3, unlink, writeFile } from "node:fs/promises";
52083
- import { dirname as dirname6, join as join18 } from "node:path";
52084
- import { getCleoHome as getCleoHome2 } from "@cleocode/core/internal";
52322
+ import { mkdir as mkdir2, readFile as readFile4, unlink, writeFile as writeFile2 } from "node:fs/promises";
52323
+ import { dirname as dirname7, join as join19 } from "node:path";
52324
+ import { getCleoHome as getCleoHome3 } from "@cleocode/core/internal";
52085
52325
  function viewerPidFilePath() {
52086
- return join18(getCleoHome2(), "viewer.pid");
52326
+ return join19(getCleoHome3(), "viewer.pid");
52087
52327
  }
52088
52328
  async function writeViewerPidFile(record) {
52089
52329
  const path6 = viewerPidFilePath();
52090
- await mkdir(dirname6(path6), { recursive: true });
52091
- await writeFile(path6, JSON.stringify(record, null, 2), "utf8");
52330
+ await mkdir2(dirname7(path6), { recursive: true });
52331
+ await writeFile2(path6, JSON.stringify(record, null, 2), "utf8");
52092
52332
  return path6;
52093
52333
  }
52094
52334
  async function readViewerPidFile() {
52095
52335
  try {
52096
- const raw = await readFile3(viewerPidFilePath(), "utf8");
52336
+ const raw = await readFile4(viewerPidFilePath(), "utf8");
52097
52337
  const parsed = JSON.parse(raw);
52098
52338
  if (typeof parsed.pid === "number" && typeof parsed.port === "number" && typeof parsed.host === "string" && typeof parsed.projectRoot === "string" && typeof parsed.startedAt === "number") {
52099
52339
  return parsed;
@@ -52182,17 +52422,17 @@ var init_port_allocator = __esm({
52182
52422
 
52183
52423
  // packages/cleo/src/viewer/server.ts
52184
52424
  import { createReadStream } from "node:fs";
52185
- import { stat } from "node:fs/promises";
52186
- import { dirname as dirname7, join as join19, normalize, resolve as resolve4 } from "node:path";
52187
- import { fileURLToPath as fileURLToPath4 } from "node:url";
52425
+ import { stat as stat2 } from "node:fs/promises";
52426
+ import { dirname as dirname8, join as join20, normalize, resolve as resolve5 } from "node:path";
52427
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
52188
52428
  import {
52189
52429
  createAttachmentStore as createAttachmentStore4,
52190
52430
  getProjectRoot as getProjectRoot39,
52191
52431
  searchAllProjectDocs as searchAllProjectDocs2
52192
52432
  } from "@cleocode/core/internal";
52193
52433
  function getViewerAssetsDir() {
52194
- const thisFile = fileURLToPath4(import.meta.url);
52195
- return resolve4(dirname7(thisFile), "..", "..", "assets", "viewer");
52434
+ const thisFile = fileURLToPath5(import.meta.url);
52435
+ return resolve5(dirname8(thisFile), "..", "..", "assets", "viewer");
52196
52436
  }
52197
52437
  function extOf(p) {
52198
52438
  const i = p.lastIndexOf(".");
@@ -52222,15 +52462,15 @@ function inferTitle(markdown, fallback) {
52222
52462
  }
52223
52463
  async function serveStatic(res, assetsDir, relPath) {
52224
52464
  const safeRel = normalize(relPath).replace(/^[\\/]+/, "");
52225
- const absPath = join19(assetsDir, safeRel);
52226
- const resolvedAssets = resolve4(assetsDir) + "/";
52227
- const resolvedAbs = resolve4(absPath);
52228
- if (`${resolvedAbs}/`.indexOf(resolvedAssets) !== 0 && resolvedAbs !== resolve4(assetsDir)) {
52465
+ const absPath = join20(assetsDir, safeRel);
52466
+ const resolvedAssets = resolve5(assetsDir) + "/";
52467
+ const resolvedAbs = resolve5(absPath);
52468
+ if (`${resolvedAbs}/`.indexOf(resolvedAssets) !== 0 && resolvedAbs !== resolve5(assetsDir)) {
52229
52469
  send(res, 404, "application/json", lafsErrorJson("E_NOT_FOUND", `not found: ${relPath}`));
52230
52470
  return;
52231
52471
  }
52232
52472
  try {
52233
- const s = await stat(absPath);
52473
+ const s = await stat2(absPath);
52234
52474
  if (!s.isFile()) {
52235
52475
  send(res, 404, "application/json", lafsErrorJson("E_NOT_FOUND", `not found: ${relPath}`));
52236
52476
  return;
@@ -52421,15 +52661,15 @@ var init_server = __esm({
52421
52661
  });
52422
52662
 
52423
52663
  // packages/cleo/src/cli/docs-viewer-subsystem.ts
52424
- import { mkdir as mkdir2 } from "node:fs/promises";
52425
- import { join as join20 } from "node:path";
52426
- import { getCleoHome as getCleoHome3 } from "@cleocode/core";
52427
- import { defineSubsystem } from "@cleocode/runtime/daemon";
52664
+ import { mkdir as mkdir3 } from "node:fs/promises";
52665
+ import { join as join21 } from "node:path";
52666
+ import { getCleoHome as getCleoHome4 } from "@cleocode/core";
52667
+ import { defineSubsystem as defineSubsystem2 } from "@cleocode/runtime/daemon";
52428
52668
  function getViewerPaths() {
52429
- const cleoHome = getCleoHome3();
52669
+ const cleoHome = getCleoHome4();
52430
52670
  return {
52431
52671
  pidFile: viewerPidFilePath(),
52432
- logFile: join20(cleoHome, "viewer.log"),
52672
+ logFile: join21(cleoHome, "viewer.log"),
52433
52673
  logDir: cleoHome
52434
52674
  };
52435
52675
  }
@@ -52457,7 +52697,7 @@ function createDocsViewerSubsystem(opts = {}) {
52457
52697
  const host = opts.host ?? VIEWER_DEFAULT_HOST;
52458
52698
  const autoIncrement = !(opts.noAutoPort ?? false);
52459
52699
  let live;
52460
- return defineSubsystem({
52700
+ return defineSubsystem2({
52461
52701
  name: VIEWER_SUBSYSTEM_NAME,
52462
52702
  async start() {
52463
52703
  const { pidFile, logDir } = getViewerPaths();
@@ -52472,7 +52712,7 @@ function createDocsViewerSubsystem(opts = {}) {
52472
52712
  live = ctx2;
52473
52713
  return ctx2;
52474
52714
  }
52475
- await mkdir2(logDir, { recursive: true });
52715
+ await mkdir3(logDir, { recursive: true });
52476
52716
  const handle = await startViewer({
52477
52717
  startPort,
52478
52718
  endPort,
@@ -52526,9 +52766,9 @@ function createDocsViewerSubsystem(opts = {}) {
52526
52766
  process.kill(context.pid, "SIGTERM");
52527
52767
  } catch {
52528
52768
  }
52529
- for (let i = 0; i < SIGTERM_GRACE_ITERATIONS; i++) {
52769
+ for (let i = 0; i < SIGTERM_GRACE_ITERATIONS2; i++) {
52530
52770
  if (!isViewerProcessRunning(context.pid)) break;
52531
- await new Promise((resolve12) => setTimeout(resolve12, 500));
52771
+ await new Promise((resolve13) => setTimeout(resolve13, 500));
52532
52772
  }
52533
52773
  if (isViewerProcessRunning(context.pid)) {
52534
52774
  try {
@@ -52541,7 +52781,7 @@ function createDocsViewerSubsystem(opts = {}) {
52541
52781
  }
52542
52782
  });
52543
52783
  }
52544
- var VIEWER_DEFAULT_PORT, VIEWER_DEFAULT_END_PORT, VIEWER_DEFAULT_HOST, VIEWER_SUBSYSTEM_NAME, SIGTERM_GRACE_ITERATIONS;
52784
+ var VIEWER_DEFAULT_PORT, VIEWER_DEFAULT_END_PORT, VIEWER_DEFAULT_HOST, VIEWER_SUBSYSTEM_NAME, SIGTERM_GRACE_ITERATIONS2;
52545
52785
  var init_docs_viewer_subsystem = __esm({
52546
52786
  "packages/cleo/src/cli/docs-viewer-subsystem.ts"() {
52547
52787
  "use strict";
@@ -52551,12 +52791,12 @@ var init_docs_viewer_subsystem = __esm({
52551
52791
  VIEWER_DEFAULT_END_PORT = 7800;
52552
52792
  VIEWER_DEFAULT_HOST = "127.0.0.1";
52553
52793
  VIEWER_SUBSYSTEM_NAME = "cleo-docs-viewer";
52554
- SIGTERM_GRACE_ITERATIONS = 20;
52794
+ SIGTERM_GRACE_ITERATIONS2 = 20;
52555
52795
  }
52556
52796
  });
52557
52797
 
52558
52798
  // packages/cleo/src/cli/commands/docs-viewer.ts
52559
- import { spawn as spawn2 } from "node:child_process";
52799
+ import { spawn as spawn3 } from "node:child_process";
52560
52800
  function openInBrowser(url) {
52561
52801
  try {
52562
52802
  let cmd;
@@ -52571,7 +52811,7 @@ function openInBrowser(url) {
52571
52811
  cmd = "xdg-open";
52572
52812
  args = [url];
52573
52813
  }
52574
- spawn2(cmd, args, { detached: true, stdio: "ignore" }).unref();
52814
+ spawn3(cmd, args, { detached: true, stdio: "ignore" }).unref();
52575
52815
  } catch {
52576
52816
  }
52577
52817
  }
@@ -52870,8 +53110,8 @@ __export(docs_exports, {
52870
53110
  _llmOutputCommand: () => _llmOutputCommand,
52871
53111
  docsCommand: () => docsCommand
52872
53112
  });
52873
- import { appendFile, mkdir as mkdir3, readdir, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
52874
- import { dirname as dirname8, isAbsolute as isAbsolute2, join as join21, resolve as resolve5 } from "node:path";
53113
+ import { appendFile, mkdir as mkdir4, readdir, readFile as readFile5, writeFile as writeFile3 } from "node:fs/promises";
53114
+ import { dirname as dirname9, isAbsolute as isAbsolute2, join as join22, resolve as resolve6 } from "node:path";
52875
53115
  import { pushWarning as pushWarning3 } from "@cleocode/core";
52876
53116
  import { createDocsReadModel as createDocsReadModel2 } from "@cleocode/core/docs/docs-read-model";
52877
53117
  import {
@@ -52903,7 +53143,7 @@ async function readDocBodyFromStdin() {
52903
53143
  return buf.replace(/\r?\n$/, "");
52904
53144
  }
52905
53145
  async function getScriptNames(projectRoot) {
52906
- const scriptsDir = join21(projectRoot, "scripts");
53146
+ const scriptsDir = join22(projectRoot, "scripts");
52907
53147
  try {
52908
53148
  const files = await readdir(scriptsDir);
52909
53149
  return files.filter((f) => f.endsWith(".sh")).map((f) => f.replace(".sh", "")).sort();
@@ -52912,7 +53152,7 @@ async function getScriptNames(projectRoot) {
52912
53152
  }
52913
53153
  }
52914
53154
  async function getIndexedCommands(projectRoot) {
52915
- const indexPath = join21(projectRoot, "docs", "commands", "COMMANDS-INDEX.json");
53155
+ const indexPath = join22(projectRoot, "docs", "commands", "COMMANDS-INDEX.json");
52916
53156
  const index = await readJson(indexPath);
52917
53157
  if (!index) return [];
52918
53158
  return index.commands.map((c) => c.name).sort();
@@ -52945,8 +53185,8 @@ async function runGapCheck(_projectRoot, filterId) {
52945
53185
  const reviewFiles = files.filter((f) => f.endsWith(".md"));
52946
53186
  for (const file of reviewFiles) {
52947
53187
  if (filterId && !file.includes(filterId)) continue;
52948
- const filePath = join21(reviewDir, file);
52949
- const content = await readFile4(filePath, "utf-8");
53188
+ const filePath = join22(reviewDir, file);
53189
+ const content = await readFile5(filePath, "utf-8");
52950
53190
  const taskMatch = file.match(/^(T\d+)/);
52951
53191
  const taskId = taskMatch ? taskMatch[1] : "UNKNOWN";
52952
53192
  const gaps = [];
@@ -53240,9 +53480,9 @@ var init_docs3 = __esm({
53240
53480
  })}
53241
53481
  `;
53242
53482
  try {
53243
- await mkdir3(join21(projectRoot, ".cleo", "audit"), { recursive: true });
53483
+ await mkdir4(join22(projectRoot, ".cleo", "audit"), { recursive: true });
53244
53484
  await appendFile(
53245
- join21(projectRoot, ".cleo", "audit", "similar-bypass.jsonl"),
53485
+ join22(projectRoot, ".cleo", "audit", "similar-bypass.jsonl"),
53246
53486
  auditLine,
53247
53487
  "utf-8"
53248
53488
  );
@@ -53608,9 +53848,9 @@ var init_docs3 = __esm({
53608
53848
  });
53609
53849
  let writtenPath;
53610
53850
  if (typeof args.out === "string" && args.out.length > 0) {
53611
- const outPath = isAbsolute2(args.out) ? args.out : resolve5(projectRoot, args.out);
53612
- await mkdir3(dirname8(outPath), { recursive: true });
53613
- await writeFile2(outPath, result.content, "utf8");
53851
+ const outPath = isAbsolute2(args.out) ? args.out : resolve6(projectRoot, args.out);
53852
+ await mkdir4(dirname9(outPath), { recursive: true });
53853
+ await writeFile3(outPath, result.content, "utf8");
53614
53854
  writtenPath = outPath;
53615
53855
  }
53616
53856
  if (args.json) {
@@ -53680,9 +53920,9 @@ var init_docs3 = __esm({
53680
53920
  });
53681
53921
  let writtenPath;
53682
53922
  if (typeof args.out === "string" && args.out.length > 0) {
53683
- const outPath = isAbsolute2(args.out) ? args.out : resolve5(projectRoot, args.out);
53684
- await mkdir3(dirname8(outPath), { recursive: true });
53685
- await writeFile2(outPath, result.content, "utf8");
53923
+ const outPath = isAbsolute2(args.out) ? args.out : resolve6(projectRoot, args.out);
53924
+ await mkdir4(dirname9(outPath), { recursive: true });
53925
+ await writeFile3(outPath, result.content, "utf8");
53686
53926
  writtenPath = outPath;
53687
53927
  }
53688
53928
  if (args.json) {
@@ -53757,9 +53997,9 @@ var init_docs3 = __esm({
53757
53997
  });
53758
53998
  let writtenPath;
53759
53999
  if (typeof args.out === "string" && args.out.length > 0) {
53760
- const outPath = isAbsolute2(args.out) ? args.out : resolve5(projectRoot, args.out);
53761
- await mkdir3(dirname8(outPath), { recursive: true });
53762
- await writeFile2(outPath, result.content, "utf8");
54000
+ const outPath = isAbsolute2(args.out) ? args.out : resolve6(projectRoot, args.out);
54001
+ await mkdir4(dirname9(outPath), { recursive: true });
54002
+ await writeFile3(outPath, result.content, "utf8");
53763
54003
  writtenPath = outPath;
53764
54004
  }
53765
54005
  if (args.json) {
@@ -53983,9 +54223,9 @@ var init_docs3 = __esm({
53983
54223
  base: args.base ?? void 0
53984
54224
  });
53985
54225
  if (typeof args.out === "string" && args.out.length > 0) {
53986
- const outPath = isAbsolute2(args.out) ? args.out : resolve5(projectRoot, args.out);
53987
- await mkdir3(dirname8(outPath), { recursive: true });
53988
- await writeFile2(outPath, result.merged, "utf8");
54226
+ const outPath = isAbsolute2(args.out) ? args.out : resolve6(projectRoot, args.out);
54227
+ await mkdir4(dirname9(outPath), { recursive: true });
54228
+ await writeFile3(outPath, result.merged, "utf8");
53989
54229
  humanInfo(`Wrote merged content to ${outPath}`);
53990
54230
  }
53991
54231
  cliOutput(result, { command: "docs merge", operation: "docs.merge" });
@@ -54744,10 +54984,10 @@ var init_docs3 = __esm({
54744
54984
  async run({ args }) {
54745
54985
  const projectRoot = getProjectRoot40();
54746
54986
  const dirArg = String(args.dir);
54747
- const scanRoot = isAbsolute2(dirArg) ? dirArg : resolve5(projectRoot, dirArg);
54987
+ const scanRoot = isAbsolute2(dirArg) ? dirArg : resolve6(projectRoot, dirArg);
54748
54988
  const dryRun = args["dry-run"] === true;
54749
54989
  const force = args.force === true;
54750
- const manifestPath = args["audit-manifest"] ? isAbsolute2(String(args["audit-manifest"])) ? String(args["audit-manifest"]) : resolve5(projectRoot, String(args["audit-manifest"])) : void 0;
54990
+ const manifestPath = args["audit-manifest"] ? isAbsolute2(String(args["audit-manifest"])) ? String(args["audit-manifest"]) : resolve6(projectRoot, String(args["audit-manifest"])) : void 0;
54751
54991
  if (force) {
54752
54992
  const auditLine = `${JSON.stringify({
54753
54993
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -54756,9 +54996,9 @@ var init_docs3 = __esm({
54756
54996
  })}
54757
54997
  `;
54758
54998
  try {
54759
- await mkdir3(join21(projectRoot, ".cleo", "audit"), { recursive: true });
54999
+ await mkdir4(join22(projectRoot, ".cleo", "audit"), { recursive: true });
54760
55000
  await appendFile(
54761
- join21(projectRoot, ".cleo", "audit", "import-force-bypass.jsonl"),
55001
+ join22(projectRoot, ".cleo", "audit", "import-force-bypass.jsonl"),
54762
55002
  auditLine,
54763
55003
  "utf-8"
54764
55004
  );
@@ -55479,8 +55719,8 @@ var doctor_release_readiness_exports = {};
55479
55719
  __export(doctor_release_readiness_exports, {
55480
55720
  doctorReleaseReadinessCommand: () => doctorReleaseReadinessCommand
55481
55721
  });
55482
- import { existsSync as existsSync13 } from "node:fs";
55483
- import { join as join22, resolve as resolve6 } from "node:path";
55722
+ import { existsSync as existsSync14 } from "node:fs";
55723
+ import { join as join23, resolve as resolve7 } from "node:path";
55484
55724
  async function runCheck(name, cmd, args, cwd) {
55485
55725
  const { spawnSync } = await import("node:child_process");
55486
55726
  const start = Date.now();
@@ -55518,11 +55758,11 @@ var init_doctor_release_readiness = __esm({
55518
55758
  },
55519
55759
  async run({ args }) {
55520
55760
  const isHuman = args.human === true || !!process.stdout.isTTY && args.json !== true;
55521
- const repoRoot = resolve6(process.cwd());
55761
+ const repoRoot = resolve7(process.cwd());
55522
55762
  const startTotal = Date.now();
55523
55763
  const checks = [];
55524
- const biomePath = join22(repoRoot, "node_modules", ".bin", "biome");
55525
- const biomeAvailable = existsSync13(biomePath);
55764
+ const biomePath = join23(repoRoot, "node_modules", ".bin", "biome");
55765
+ const biomeAvailable = existsSync14(biomePath);
55526
55766
  if (biomeAvailable) {
55527
55767
  checks.push(await runCheck("biome-lint", biomePath, ["ci", "."], repoRoot));
55528
55768
  } else {
@@ -55535,8 +55775,8 @@ var init_doctor_release_readiness = __esm({
55535
55775
  stderr: "biome not found in node_modules/.bin \u2014 skipping"
55536
55776
  });
55537
55777
  }
55538
- const changesetLintScript = join22(repoRoot, "scripts", "lint-changesets.mjs");
55539
- if (existsSync13(changesetLintScript)) {
55778
+ const changesetLintScript = join23(repoRoot, "scripts", "lint-changesets.mjs");
55779
+ if (existsSync14(changesetLintScript)) {
55540
55780
  checks.push(await runCheck("changeset-lint", "node", [changesetLintScript], repoRoot));
55541
55781
  } else {
55542
55782
  checks.push({
@@ -55548,8 +55788,8 @@ var init_doctor_release_readiness = __esm({
55548
55788
  stderr: "scripts/lint-changesets.mjs not found \u2014 skipping"
55549
55789
  });
55550
55790
  }
55551
- const changelogPath = join22(repoRoot, "CHANGELOG.md");
55552
- const changelogExists = existsSync13(changelogPath);
55791
+ const changelogPath = join23(repoRoot, "CHANGELOG.md");
55792
+ const changelogExists = existsSync14(changelogPath);
55553
55793
  const changelogCheck = {
55554
55794
  name: "changelog-exists",
55555
55795
  status: changelogExists ? "pass" : "fail",
@@ -55559,9 +55799,9 @@ var init_doctor_release_readiness = __esm({
55559
55799
  stderr: changelogExists ? "" : `CHANGELOG.md not found at ${changelogPath}`
55560
55800
  };
55561
55801
  checks.push(changelogCheck);
55562
- const packageJsonPath = join22(repoRoot, "package.json");
55802
+ const packageJsonPath = join23(repoRoot, "package.json");
55563
55803
  let oidcCheck;
55564
- if (existsSync13(packageJsonPath)) {
55804
+ if (existsSync14(packageJsonPath)) {
55565
55805
  try {
55566
55806
  const pkg = JSON.parse(
55567
55807
  await import("node:fs").then((m) => m.readFileSync(packageJsonPath, "utf8"))
@@ -55608,8 +55848,8 @@ var init_doctor_release_readiness = __esm({
55608
55848
  };
55609
55849
  }
55610
55850
  checks.push(oidcCheck);
55611
- const tagWorkflowPath = join22(repoRoot, ".github", "workflows", "auto-tag-on-release-merge.yml");
55612
- const tagWorkflowExists = existsSync13(tagWorkflowPath);
55851
+ const tagWorkflowPath = join23(repoRoot, ".github", "workflows", "auto-tag-on-release-merge.yml");
55852
+ const tagWorkflowExists = existsSync14(tagWorkflowPath);
55613
55853
  let tagTriggerCheck;
55614
55854
  if (tagWorkflowExists) {
55615
55855
  try {
@@ -55648,10 +55888,10 @@ var init_doctor_release_readiness = __esm({
55648
55888
  };
55649
55889
  }
55650
55890
  checks.push(tagTriggerCheck);
55651
- const tsconfigPath = join22(repoRoot, "tsconfig.json");
55652
- if (existsSync13(tsconfigPath)) {
55653
- const tscPath = join22(repoRoot, "node_modules", ".bin", "tsc");
55654
- if (existsSync13(tscPath)) {
55891
+ const tsconfigPath = join23(repoRoot, "tsconfig.json");
55892
+ if (existsSync14(tsconfigPath)) {
55893
+ const tscPath = join23(repoRoot, "node_modules", ".bin", "tsc");
55894
+ if (existsSync14(tscPath)) {
55655
55895
  checks.push(await runCheck("typecheck", tscPath, ["--noEmit"], repoRoot));
55656
55896
  } else {
55657
55897
  checks.push({
@@ -55679,8 +55919,8 @@ var init_doctor_release_readiness = __esm({
55679
55919
  { name: "json-stream-lint", script: "scripts/lint-json-stream-hygiene.mjs" }
55680
55920
  ];
55681
55921
  for (const lint of fastLintScripts) {
55682
- const scriptPath = join22(repoRoot, lint.script);
55683
- if (existsSync13(scriptPath)) {
55922
+ const scriptPath = join23(repoRoot, lint.script);
55923
+ if (existsSync14(scriptPath)) {
55684
55924
  checks.push(await runCheck(lint.name, "node", [scriptPath], repoRoot));
55685
55925
  } else {
55686
55926
  checks.push({
@@ -55941,8 +56181,8 @@ __export(migrate_agents_v2_exports, {
55941
56181
  walkAgentsDir: () => walkAgentsDir
55942
56182
  });
55943
56183
  import { createHash as createHash2 } from "node:crypto";
55944
- import { appendFileSync as appendFileSync2, existsSync as existsSync14, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync12 } from "node:fs";
55945
- import { join as join23 } from "node:path";
56184
+ import { appendFileSync as appendFileSync2, existsSync as existsSync15, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync12 } from "node:fs";
56185
+ import { join as join24 } from "node:path";
55946
56186
  import {
55947
56187
  ensureGlobalAgentRegistryDb,
55948
56188
  getGlobalAgentRegistryNativeDb,
@@ -55966,15 +56206,15 @@ function extractAgentName(source) {
55966
56206
  return headerMatch[1] ?? null;
55967
56207
  }
55968
56208
  function appendAuditLog(projectRoot, entry) {
55969
- const auditPath = join23(projectRoot, AUDIT_LOG_RELATIVE);
55970
- const auditDir = join23(auditPath, "..");
55971
- if (!existsSync14(auditDir)) {
56209
+ const auditPath = join24(projectRoot, AUDIT_LOG_RELATIVE);
56210
+ const auditDir = join24(auditPath, "..");
56211
+ if (!existsSync15(auditDir)) {
55972
56212
  mkdirSync3(auditDir, { recursive: true });
55973
56213
  }
55974
56214
  appendFileSync2(auditPath, JSON.stringify(entry) + "\n", "utf8");
55975
56215
  }
55976
56216
  function walkAgentsDir(db, scanDir, projectRoot, summary, verbose) {
55977
- if (!existsSync14(scanDir)) return;
56217
+ if (!existsSync15(scanDir)) return;
55978
56218
  let files;
55979
56219
  try {
55980
56220
  files = readdirSync2(scanDir).filter((f) => f.endsWith(".cant"));
@@ -55985,7 +56225,7 @@ function walkAgentsDir(db, scanDir, projectRoot, summary, verbose) {
55985
56225
  return;
55986
56226
  }
55987
56227
  for (const filename of files) {
55988
- const cantPath = join23(scanDir, filename);
56228
+ const cantPath = join24(scanDir, filename);
55989
56229
  const relPath = cantPath.replace(`${projectRoot}/`, "");
55990
56230
  let sourceBytes;
55991
56231
  let sourceText;
@@ -56082,9 +56322,9 @@ async function runMigrateAgentsV2(projectRoot, verbose = true) {
56082
56322
  await ensureGlobalAgentRegistryDb();
56083
56323
  const db = getGlobalAgentRegistryNativeDb();
56084
56324
  try {
56085
- const canonicalDir = join23(projectRoot, ".cleo", "cant", "agents");
56325
+ const canonicalDir = join24(projectRoot, ".cleo", "cant", "agents");
56086
56326
  walkAgentsDir(db, canonicalDir, projectRoot, summary, verbose);
56087
- const legacyDir = join23(projectRoot, ".cleo", "agents");
56327
+ const legacyDir = join24(projectRoot, ".cleo", "agents");
56088
56328
  walkAgentsDir(db, legacyDir, projectRoot, summary, verbose);
56089
56329
  } finally {
56090
56330
  db.close();
@@ -56092,8 +56332,8 @@ async function runMigrateAgentsV2(projectRoot, verbose = true) {
56092
56332
  return summary;
56093
56333
  }
56094
56334
  function readMigrationConflicts(projectRoot) {
56095
- const auditPath = join23(projectRoot, AUDIT_LOG_RELATIVE);
56096
- if (!existsSync14(auditPath)) return [];
56335
+ const auditPath = join24(projectRoot, AUDIT_LOG_RELATIVE);
56336
+ if (!existsSync15(auditPath)) return [];
56097
56337
  let raw;
56098
56338
  try {
56099
56339
  raw = readFileSync12(auditPath, "utf8");
@@ -56177,7 +56417,7 @@ __export(doctor_exports, {
56177
56417
  doctorCommand: () => doctorCommand2
56178
56418
  });
56179
56419
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
56180
- import { join as join24 } from "node:path";
56420
+ import { join as join25 } from "node:path";
56181
56421
  import { getProjectRoot as getProjectRoot45, pushWarning as pushWarning5 } from "@cleocode/core";
56182
56422
  import { renderInvariantAuditLines } from "@cleocode/core/doctor/invariant-audit-render.js";
56183
56423
  import {
@@ -56217,8 +56457,8 @@ async function scanTestFixturesInProd(projectRoot) {
56217
56457
  }
56218
56458
  async function quarantineTestFixtures(projectRoot, matches) {
56219
56459
  if (matches.length === 0) return 0;
56220
- const cleoDir = join24(projectRoot, ".cleo");
56221
- const quarantineDir = join24(
56460
+ const cleoDir = join25(projectRoot, ".cleo");
56461
+ const quarantineDir = join25(
56222
56462
  cleoDir,
56223
56463
  "quarantine",
56224
56464
  `fixture-scan-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`
@@ -56226,7 +56466,7 @@ async function quarantineTestFixtures(projectRoot, matches) {
56226
56466
  mkdirSync4(quarantineDir, { recursive: true });
56227
56467
  const manifest = matches.map((m) => ({ ...m, quarantinedAt: (/* @__PURE__ */ new Date()).toISOString() }));
56228
56468
  writeFileSync4(
56229
- join24(quarantineDir, "manifest.jsonl"),
56469
+ join25(quarantineDir, "manifest.jsonl"),
56230
56470
  manifest.map((m) => JSON.stringify(m)).join("\n") + "\n"
56231
56471
  );
56232
56472
  const { getNativeDb } = await import("@cleocode/core/store/sqlite.js");
@@ -56524,7 +56764,7 @@ var init_doctor = __esm({
56524
56764
  if (args["check-worktree-config"]) {
56525
56765
  const { execFileSync: execFile2 } = await import("node:child_process");
56526
56766
  const { join: pathJoin } = await import("node:path");
56527
- const { existsSync: existsSync22 } = await import("node:fs");
56767
+ const { existsSync: existsSync23 } = await import("node:fs");
56528
56768
  const { detectAndHealCoreWorktreeLeak } = await import("@cleocode/worktree");
56529
56769
  const projectRoot = getProjectRoot45();
56530
56770
  let gitRoot = projectRoot;
@@ -56541,7 +56781,7 @@ var init_doctor = __esm({
56541
56781
  if (isDryRun) {
56542
56782
  const gitConfigPath = pathJoin(gitRoot, ".git", "config");
56543
56783
  let leakedValue;
56544
- if (existsSync22(gitConfigPath)) {
56784
+ if (existsSync23(gitConfigPath)) {
56545
56785
  try {
56546
56786
  leakedValue = execFile2("git", ["config", "--file", gitConfigPath, "--get", "core.worktree"], {
56547
56787
  encoding: "utf-8",
@@ -56876,8 +57116,8 @@ var init_doctor = __esm({
56876
57116
  );
56877
57117
  return;
56878
57118
  }
56879
- const archiveDir = join24(projectRoot, ".cleo", "backups");
56880
- const auditLogPath = join24(projectRoot, ".cleo", "audit", "worktree-prune.jsonl");
57119
+ const archiveDir = join25(projectRoot, ".cleo", "backups");
57120
+ const auditLogPath = join25(projectRoot, ".cleo", "audit", "worktree-prune.jsonl");
56881
57121
  const result = await pruneWorktreeOrphans(orphans, {
56882
57122
  archiveDir,
56883
57123
  auditLogPath,
@@ -57041,7 +57281,7 @@ __export(event_exports, {
57041
57281
  orchestratorCommand: () => orchestratorCommand
57042
57282
  });
57043
57283
  import { readdir as readdir2 } from "node:fs/promises";
57044
- import { join as join25 } from "node:path";
57284
+ import { join as join26 } from "node:path";
57045
57285
  import { cwd as processCwd } from "node:process";
57046
57286
  import { appendEvent } from "@cleocode/core/events/event-bus.js";
57047
57287
  function resolveProjectRoot2(arg) {
@@ -57164,7 +57404,7 @@ var init_event = __esm({
57164
57404
  const agentIdFilter = args.agent;
57165
57405
  const showAll = args.all === true;
57166
57406
  const lines = parseInt(args.lines ?? "20", 10);
57167
- const eventsDir = join25(projectRoot, ".cleo", "agent-events");
57407
+ const eventsDir = join26(projectRoot, ".cleo", "agent-events");
57168
57408
  try {
57169
57409
  let files = [];
57170
57410
  try {
@@ -57202,7 +57442,7 @@ var init_event = __esm({
57202
57442
  for (const file of filesToTail) {
57203
57443
  const agentId = file.replace(".jsonl", "");
57204
57444
  const { readFileSync: readFileSync21 } = await import("node:fs");
57205
- const content = readFileSync21(join25(eventsDir, file), "utf-8");
57445
+ const content = readFileSync21(join26(eventsDir, file), "utf-8");
57206
57446
  const eventLines = content.trim().split("\n").filter(Boolean);
57207
57447
  const tail = eventLines.slice(-lines);
57208
57448
  if (jsonMode) {
@@ -57272,7 +57512,7 @@ var init_event = __esm({
57272
57512
  const epicFilter = args.epic;
57273
57513
  const follow = args.follow === true;
57274
57514
  const lines = parseInt(args.lines ?? "50", 10);
57275
- const eventsDir = join25(projectRoot, ".cleo", "agent-events");
57515
+ const eventsDir = join26(projectRoot, ".cleo", "agent-events");
57276
57516
  let files = [];
57277
57517
  try {
57278
57518
  const entries = await readdir2(eventsDir);
@@ -57298,7 +57538,7 @@ var init_event = __esm({
57298
57538
  const agentId = file.replace(".jsonl", "");
57299
57539
  try {
57300
57540
  const { readFileSync: readFileSync21 } = await import("node:fs");
57301
- const content = readFileSync21(join25(eventsDir, file), "utf-8");
57541
+ const content = readFileSync21(join26(eventsDir, file), "utf-8");
57302
57542
  const eventLines = content.trim().split("\n").filter(Boolean);
57303
57543
  const tail = eventLines.slice(-lines);
57304
57544
  if (jsonMode) {
@@ -57444,7 +57684,7 @@ var exodus_exports = {};
57444
57684
  __export(exodus_exports, {
57445
57685
  exodusCommand: () => exodusCommand
57446
57686
  });
57447
- import { existsSync as existsSync15 } from "node:fs";
57687
+ import { existsSync as existsSync16 } from "node:fs";
57448
57688
  import { resolveDualScopeDbPath } from "@cleocode/core/store/dual-scope-db.js";
57449
57689
  import {
57450
57690
  archiveMigratedSources,
@@ -57625,7 +57865,7 @@ var init_exodus = __esm({
57625
57865
  );
57626
57866
  let archived = [];
57627
57867
  if (isDataContinuityOk(verifyResult)) {
57628
- const consumed = plan.sources.filter((s) => existsSync15(s.path));
57868
+ const consumed = plan.sources.filter((s) => existsSync16(s.path));
57629
57869
  const archiveResult = archiveMigratedSources(consumed, process.cwd());
57630
57870
  archived = archiveResult.sources.filter((s) => s.action === "archived").map((s) => s.name);
57631
57871
  humanInfo(
@@ -58273,7 +58513,7 @@ __export(gateway_exports, {
58273
58513
  gatewayCommand: () => gatewayCommand,
58274
58514
  gatewayOpenapiSubCommand: () => gatewayOpenapiSubCommand
58275
58515
  });
58276
- import { resolve as resolve7 } from "node:path";
58516
+ import { resolve as resolve8 } from "node:path";
58277
58517
  import { generateOpenApi, getProjectRoot as getProjectRoot46 } from "@cleocode/core";
58278
58518
  var gatewayOpenapiSubCommand, gatewayCommand;
58279
58519
  var init_gateway = __esm({
@@ -58306,7 +58546,7 @@ var init_gateway = __esm({
58306
58546
  const pathCount = Object.keys(doc.paths).length;
58307
58547
  if (typeof args.out === "string" && args.out.length > 0) {
58308
58548
  const { writeFileSync: writeFileSync7 } = await import("node:fs");
58309
- const target = resolve7(getProjectRoot46(), args.out);
58549
+ const target = resolve8(getProjectRoot46(), args.out);
58310
58550
  writeFileSync7(target, json2, "utf8");
58311
58551
  cliOutput(
58312
58552
  { out: target, pathCount, version: doc.info.version, openapi: doc.openapi },
@@ -58387,7 +58627,7 @@ __export(gc_exports, {
58387
58627
  gcCommand: () => gcCommand
58388
58628
  });
58389
58629
  import { homedir as homedir4, tmpdir } from "node:os";
58390
- import { join as join26 } from "node:path";
58630
+ import { join as join27 } from "node:path";
58391
58631
  import { pruneOrphanTempDirs, pruneOrphanWorktrees } from "@cleocode/core/gc/cleanup.js";
58392
58632
  import { runGC } from "@cleocode/core/gc/runner.js";
58393
58633
  import { readGCState } from "@cleocode/core/gc/state.js";
@@ -58464,7 +58704,7 @@ var init_gc = __esm({
58464
58704
  },
58465
58705
  async run({ args }) {
58466
58706
  const cleoDir = resolveLegacyCleoDir3(args["cleo-dir"]);
58467
- const statePath = join26(cleoDir, "gc-state.json");
58707
+ const statePath = join27(cleoDir, "gc-state.json");
58468
58708
  try {
58469
58709
  const state = await readGCState(statePath);
58470
58710
  const diskStr = state.lastDiskUsedPct !== null ? `${state.lastDiskUsedPct.toFixed(1)}%` : "unknown";
@@ -58517,8 +58757,8 @@ var init_gc = __esm({
58517
58757
  }
58518
58758
  },
58519
58759
  async run({ args }) {
58520
- const xdgData = process.env["XDG_DATA_HOME"] ?? join26(homedir4(), ".local", "share");
58521
- const worktreesRoot = args["worktrees-root"] ?? join26(xdgData, "cleo", "worktrees");
58760
+ const xdgData = process.env["XDG_DATA_HOME"] ?? join27(homedir4(), ".local", "share");
58761
+ const worktreesRoot = args["worktrees-root"] ?? join27(xdgData, "cleo", "worktrees");
58522
58762
  const projectHash = args["project-hash"];
58523
58763
  const dryRun = args["dry-run"];
58524
58764
  const preserveRaw = args["preserve-tasks"];
@@ -58533,7 +58773,7 @@ var init_gc = __esm({
58533
58773
  dryRun
58534
58774
  });
58535
58775
  const dryLabel = dryRun ? " (dry run)" : "";
58536
- const humanMsg = `GC worktrees${dryLabel} \u2014 ${dryRun ? "Would remove" : "Removed"}: ${result.removed} director${result.removed === 1 ? "y" : "ies"}` + (result.errors.length > 0 ? `, ${result.errors.length} error(s)` : "");
58776
+ const humanMsg = `GC worktrees${dryLabel} \u2014 ${dryRun ? "Would remove" : "Removed"}: ${result.removed} director${result.removed === 1 ? "y" : "ies"}` + (result.quarantined > 0 ? `, ${result.quarantined} quarantined (dirty/unpushed)` : "") + (result.errors.length > 0 ? `, ${result.errors.length} error(s)` : "");
58537
58777
  cliOutput(result, {
58538
58778
  command: "gc",
58539
58779
  operation: "gc.worktrees",
@@ -60166,9 +60406,9 @@ __export(init_exports, {
60166
60406
  isCredentialPoolEmpty: () => isCredentialPoolEmpty,
60167
60407
  maybeNudgeFirstRunLogin: () => maybeNudgeFirstRunLogin
60168
60408
  });
60169
- import { existsSync as existsSync16, readFileSync as readFileSync13 } from "node:fs";
60170
- import { join as join27 } from "node:path";
60171
- import { fileURLToPath as fileURLToPath5 } from "node:url";
60409
+ import { existsSync as existsSync17, readFileSync as readFileSync13 } from "node:fs";
60410
+ import { join as join28 } from "node:path";
60411
+ import { fileURLToPath as fileURLToPath6 } from "node:url";
60172
60412
  import {
60173
60413
  CleoError as CleoError6,
60174
60414
  getWorkflowTemplatesDir as getCoreWorkflowTemplatesDir,
@@ -60180,12 +60420,12 @@ import { getCredentialPool } from "@cleocode/core/llm/credential-pool.js";
60180
60420
  import { getTemplatesByKind } from "@cleocode/core/templates/registry";
60181
60421
  function getGitignoreTemplate() {
60182
60422
  try {
60183
- const thisFile = fileURLToPath5(import.meta.url);
60184
- const packageRoot = join27(thisFile, "..", "..", "..", "..");
60185
- const localTemplatePath = join27(packageRoot, "templates", "cleo-gitignore");
60186
- const monorepoTemplatePath = join27(packageRoot, "..", "..", "templates", "cleo-gitignore");
60187
- const templatePath = existsSync16(localTemplatePath) ? localTemplatePath : monorepoTemplatePath;
60188
- if (existsSync16(templatePath)) {
60423
+ const thisFile = fileURLToPath6(import.meta.url);
60424
+ const packageRoot = join28(thisFile, "..", "..", "..", "..");
60425
+ const localTemplatePath = join28(packageRoot, "templates", "cleo-gitignore");
60426
+ const monorepoTemplatePath = join28(packageRoot, "..", "..", "templates", "cleo-gitignore");
60427
+ const templatePath = existsSync17(localTemplatePath) ? localTemplatePath : monorepoTemplatePath;
60428
+ if (existsSync17(templatePath)) {
60189
60429
  return readFileSync13(templatePath, "utf-8");
60190
60430
  }
60191
60431
  } catch {
@@ -60679,7 +60919,7 @@ var issue_exports = {};
60679
60919
  __export(issue_exports, {
60680
60920
  issueCommand: () => issueCommand
60681
60921
  });
60682
- import { execFileSync as execFileSync2 } from "node:child_process";
60922
+ import { execFileSync as execFileSync3 } from "node:child_process";
60683
60923
  import { addIssue, BUILD_CONFIG } from "@cleocode/core/internal";
60684
60924
  async function handleIssueType(issueType, opts) {
60685
60925
  let result;
@@ -60702,7 +60942,7 @@ async function handleIssueType(issueType, opts) {
60702
60942
  if (opts["open"] && typeof result.url === "string" && result.url.startsWith("https://")) {
60703
60943
  const issueNumber = result.url.match(/(\d+)$/)?.[1] ?? "unknown";
60704
60944
  try {
60705
- execFileSync2("gh", ["issue", "view", issueNumber, "--repo", CLEO_REPO, "--web"], {
60945
+ execFileSync3("gh", ["issue", "view", issueNumber, "--repo", CLEO_REPO, "--web"], {
60706
60946
  stdio: ["pipe", "pipe", "pipe"]
60707
60947
  });
60708
60948
  } catch {
@@ -61597,7 +61837,7 @@ function makeLlmSubcommand(opts) {
61597
61837
  }
61598
61838
  });
61599
61839
  }
61600
- var API_KEY_FLAG_DEPRECATION, addCommand7, listCommand11, removeCommand4, useCommand, profileCommand, testCommand, whoamiCommand, listProvidersCommand, contextEnginesListCommand, contextEnginesCommand, loginCommand2, refreshCatalogCommand, llmCommand;
61840
+ var API_KEY_FLAG_DEPRECATION, addCommand7, listCommand11, removeCommand4, useCommand, profileCommand, testCommand, whoamiCommand, listProvidersCommand, contextEnginesListCommand, contextEnginesCommand, loginCommand2, refreshCatalogCommand, providersCommand, healthCommand4, fitCommand, llmCommand;
61601
61841
  var init_llm3 = __esm({
61602
61842
  "packages/cleo/src/cli/commands/llm.ts"() {
61603
61843
  "use strict";
@@ -61995,6 +62235,170 @@ var init_llm3 = __esm({
61995
62235
  }
61996
62236
  }
61997
62237
  });
62238
+ providersCommand = defineCommand({
62239
+ meta: {
62240
+ name: "providers",
62241
+ description: "Show provisioning state, reachability, and resolver scores for all builtin LLM providers (DHQ-081)."
62242
+ },
62243
+ args: {
62244
+ json: {
62245
+ type: "boolean",
62246
+ description: "Output as JSON"
62247
+ },
62248
+ role: {
62249
+ type: "string",
62250
+ description: "Role to compute scores against (default: consolidation). One of: consolidation, judgement, hygiene, extraction, derivation."
62251
+ }
62252
+ },
62253
+ async run({ args }) {
62254
+ const { enumerateProvisionedProviders, roleTierFor } = await import(
62255
+ /* webpackIgnore: true */
62256
+ "@cleocode/core/llm/cross-provider-selector"
62257
+ );
62258
+ const typedArgs = args;
62259
+ const role = typeof typedArgs["role"] === "string" ? typedArgs["role"] : "consolidation";
62260
+ const taskTier = roleTierFor(role);
62261
+ const providers = await enumerateProvisionedProviders(taskTier);
62262
+ const provisioned = providers.filter((p) => p.provisioningState === "provisioned");
62263
+ const winner = provisioned.length > 0 ? provisioned.reduce(
62264
+ (best, p) => (p.resolverScore ?? -Infinity) > (best.resolverScore ?? -Infinity) ? p : best
62265
+ ) : null;
62266
+ const envelope = {
62267
+ providers,
62268
+ implicitFallbackWouldUse: "anthropic",
62269
+ explainCrossProvider: {
62270
+ winner: winner?.id ?? null,
62271
+ scores: Object.fromEntries(providers.map((p) => [p.id, p.resolverScore ?? 0])),
62272
+ reason: winner ? `${winner.id} (tier: ${winner.tier}, score: ${winner.resolverScore}) selected as best provisioned provider for role '${role}'` : `No provisioned provider found; falling back to anthropic implicit fallback`
62273
+ }
62274
+ };
62275
+ if (isHumanOutput() && !typedArgs["json"]) {
62276
+ const lines = ["", "LLM Providers \u2014 Provisioning State", "=".repeat(40)];
62277
+ for (const p of providers) {
62278
+ const star = p.id === winner?.id ? " \u2605" : "";
62279
+ const runnable = p.machineRunnable === null ? "" : ` [local:${p.machineRunnable ? "up" : "down"}]`;
62280
+ lines.push(
62281
+ ` ${p.id.padEnd(12)} ${p.provisioningState.padEnd(16)} score:${String(p.resolverScore ?? "-").padStart(4)} ${p.reachabilityState}${runnable}${star}`
62282
+ );
62283
+ }
62284
+ if (winner) {
62285
+ lines.push(
62286
+ "",
62287
+ `Winner: ${winner.id} \u2014 would pick model: ${winner.wouldPickModel ?? "unknown"}`
62288
+ );
62289
+ }
62290
+ humanLine(lines.join("\n"));
62291
+ } else {
62292
+ cliOutput(envelope, { command: "llm-providers", operation: "llm.providers" });
62293
+ }
62294
+ }
62295
+ });
62296
+ healthCommand4 = defineCommand({
62297
+ meta: {
62298
+ name: "health",
62299
+ description: "Show lightweight provisioning health for all builtin LLM providers (DHQ-081). Alias for cleo llm providers --brief."
62300
+ },
62301
+ args: {
62302
+ json: {
62303
+ type: "boolean",
62304
+ description: "Output as JSON"
62305
+ }
62306
+ },
62307
+ async run({ args }) {
62308
+ const { enumerateProvisionedProviders } = await import(
62309
+ /* webpackIgnore: true */
62310
+ "@cleocode/core/llm/cross-provider-selector"
62311
+ );
62312
+ const typedArgs = args;
62313
+ const providers = await enumerateProvisionedProviders("frontier");
62314
+ const healthEnvelope = {
62315
+ providers: providers.map((p) => ({
62316
+ id: p.id,
62317
+ displayName: p.displayName,
62318
+ provisioningState: p.provisioningState,
62319
+ reachabilityState: p.reachabilityState,
62320
+ machineRunnable: p.machineRunnable
62321
+ })),
62322
+ implicitFallbackWouldUse: "anthropic"
62323
+ };
62324
+ if (isHumanOutput() && !typedArgs["json"]) {
62325
+ const lines = ["", "LLM Health", "=".repeat(30)];
62326
+ for (const p of healthEnvelope.providers) {
62327
+ const icon = p.provisioningState === "provisioned" ? "\u2713" : "\u2717";
62328
+ const runnable = p.machineRunnable === null ? "" : ` [local:${p.machineRunnable ? "up" : "down"}]`;
62329
+ lines.push(
62330
+ ` ${icon} ${p.id.padEnd(12)} ${p.provisioningState.padEnd(16)} ${p.reachabilityState}${runnable}`
62331
+ );
62332
+ }
62333
+ humanLine(lines.join("\n"));
62334
+ } else {
62335
+ cliOutput(healthEnvelope, { command: "llm-health", operation: "llm.health" });
62336
+ }
62337
+ }
62338
+ });
62339
+ fitCommand = defineCommand({
62340
+ meta: {
62341
+ name: "fit",
62342
+ description: "Rank 2\u20133 best-fit local open-weight models for this machine (RAM/VRAM check). Wizard building block for local Ollama setup."
62343
+ },
62344
+ args: {
62345
+ json: {
62346
+ type: "boolean",
62347
+ description: "Output as JSON (LAFS envelope)"
62348
+ },
62349
+ "ollama-url": {
62350
+ type: "string",
62351
+ description: "Override Ollama base URL (default: http://localhost:11434)"
62352
+ }
62353
+ },
62354
+ async run({ args }) {
62355
+ const { rankLocalModelFit } = await import(
62356
+ /* webpackIgnore: true */
62357
+ "@cleocode/core/llm/local-model-fit"
62358
+ );
62359
+ const typedArgs = args;
62360
+ const ollamaBaseUrl = typeof typedArgs["ollama-url"] === "string" ? typedArgs["ollama-url"] : "http://localhost:11434";
62361
+ const envelope = await rankLocalModelFit({ ollamaBaseUrl });
62362
+ if (isHumanOutput() && !typedArgs["json"]) {
62363
+ const lines = ["", "Local Model Fit \u2014 Hardware Summary", "=".repeat(42)];
62364
+ const hw = envelope.hardware;
62365
+ lines.push(` RAM total : ${hw.totalRamGb.toFixed(1)} GB`);
62366
+ lines.push(` RAM avail : ${hw.availableRamGb.toFixed(1)} GB`);
62367
+ if (hw.vramTotalGb !== null) {
62368
+ lines.push(` VRAM total : ${hw.vramTotalGb.toFixed(1)} GB (${hw.vramMethod})`);
62369
+ lines.push(
62370
+ ` VRAM free : ${hw.vramFreeGb !== null ? hw.vramFreeGb.toFixed(1) + " GB" : "unknown"}`
62371
+ );
62372
+ } else {
62373
+ lines.push(` VRAM : not detected (${hw.vramMethod})`);
62374
+ }
62375
+ lines.push(` Ollama : ${envelope.ollamaRunning ? "running" : "not running"}`);
62376
+ if (envelope.pulledModels.length > 0) {
62377
+ lines.push(` Pulled : ${envelope.pulledModels.map((m) => m.name).join(", ")}`);
62378
+ }
62379
+ lines.push("");
62380
+ if (envelope.noRecommendationReason) {
62381
+ lines.push(" No local model recommendations:");
62382
+ lines.push(` ${envelope.noRecommendationReason}`);
62383
+ } else {
62384
+ lines.push("Recommended Models", "-".repeat(42));
62385
+ for (let i = 0; i < envelope.recommendations.length; i++) {
62386
+ const rec = envelope.recommendations[i];
62387
+ const pulled = rec.alreadyPulled ? " [already pulled]" : "";
62388
+ lines.push(` ${i + 1}. ${rec.candidate.displayName}${pulled} (fit: ${rec.fitTier})`);
62389
+ for (const reason of rec.reasons) {
62390
+ lines.push(` \u2022 ${reason}`);
62391
+ }
62392
+ lines.push(` pull: ${rec.pullCommand}`);
62393
+ lines.push("");
62394
+ }
62395
+ }
62396
+ humanLine(lines.join("\n"));
62397
+ } else {
62398
+ cliOutput(envelope, { command: "llm-fit", operation: "llm.fit" });
62399
+ }
62400
+ }
62401
+ });
61998
62402
  llmCommand = defineCommand({
61999
62403
  meta: {
62000
62404
  name: "llm",
@@ -62004,8 +62408,11 @@ var init_llm3 = __esm({
62004
62408
  add: addCommand7,
62005
62409
  "context-engines": contextEnginesCommand,
62006
62410
  cost: costCommand,
62411
+ fit: fitCommand,
62412
+ health: healthCommand4,
62007
62413
  list: listCommand11,
62008
62414
  login: loginCommand2,
62415
+ providers: providersCommand,
62009
62416
  remove: removeCommand4,
62010
62417
  stream: streamCommand,
62011
62418
  use: useCommand,
@@ -62090,14 +62497,14 @@ async function readStdin() {
62090
62497
  if (process.stdin.isTTY) {
62091
62498
  return "";
62092
62499
  }
62093
- return new Promise((resolve12, reject) => {
62500
+ return new Promise((resolve13, reject) => {
62094
62501
  let data = "";
62095
62502
  process.stdin.setEncoding("utf-8");
62096
62503
  process.stdin.on("data", (chunk) => {
62097
62504
  data += chunk;
62098
62505
  });
62099
62506
  process.stdin.on("end", () => {
62100
- resolve12(data.trim());
62507
+ resolve13(data.trim());
62101
62508
  });
62102
62509
  process.stdin.on("error", reject);
62103
62510
  });
@@ -64478,7 +64885,7 @@ var nexus_exports = {};
64478
64885
  __export(nexus_exports, {
64479
64886
  nexusCommand: () => nexusCommand
64480
64887
  });
64481
- import { appendFile as appendFile2, mkdir as mkdir4 } from "node:fs/promises";
64888
+ import { appendFile as appendFile2, mkdir as mkdir5 } from "node:fs/promises";
64482
64889
  import { homedir as homedir5 } from "node:os";
64483
64890
  import path4 from "node:path";
64484
64891
  import { getProjectRoot as getProjectRoot53 } from "@cleocode/core";
@@ -64490,7 +64897,7 @@ async function appendDeprecationTelemetry(op, replacement) {
64490
64897
  try {
64491
64898
  const dateStr = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
64492
64899
  const dir = path4.join(homedir5(), ".local", "state", "cleo", "nexus-deprecation");
64493
- await mkdir4(dir, { recursive: true });
64900
+ await mkdir5(dir, { recursive: true });
64494
64901
  const record = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), op, replacement }) + "\n";
64495
64902
  await appendFile2(path4.join(dir, `${dateStr}.jsonl`), record, "utf8");
64496
64903
  } catch {
@@ -65692,13 +66099,13 @@ var init_nexus3 = __esm({
65692
66099
  if (!skipPrompt) {
65693
66100
  const { createInterface: createInterface5 } = await import("node:readline");
65694
66101
  const rl = createInterface5({ input: process.stdin, output: process.stdout });
65695
- const confirmed = await new Promise((resolve12) => {
66102
+ const confirmed = await new Promise((resolve13) => {
65696
66103
  rl.question(
65697
66104
  `
65698
66105
  [nexus] Delete ${matchCount} project(s) from the registry? [y/N] `,
65699
66106
  (answer) => {
65700
66107
  rl.close();
65701
- resolve12(answer.trim().toLowerCase() === "y");
66108
+ resolve13(answer.trim().toLowerCase() === "y");
65702
66109
  }
65703
66110
  );
65704
66111
  });
@@ -66997,7 +67404,7 @@ __export(orchestrate_exports, {
66997
67404
  formatRollupTable: () => formatRollupTable,
66998
67405
  orchestrateCommand: () => orchestrateCommand
66999
67406
  });
67000
- import { execFileSync as execFileSync3 } from "node:child_process";
67407
+ import { execFileSync as execFileSync4 } from "node:child_process";
67001
67408
  import { orchestration } from "@cleocode/core";
67002
67409
  import { BUILD_CONFIG as BUILD_CONFIG2, getProjectRoot as getProjectRoot54 } from "@cleocode/core/internal";
67003
67410
  function formatRollupTable(rollup) {
@@ -67106,7 +67513,7 @@ var init_orchestrate3 = __esm({
67106
67513
  let blocked = false;
67107
67514
  let note = "merge queue not enabled";
67108
67515
  try {
67109
- const raw = execFileSync3(
67516
+ const raw = execFileSync4(
67110
67517
  "gh",
67111
67518
  ["api", `repos/${repo}/merge-queue`, "--jq", ".entries | length"],
67112
67519
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 15e3 }
@@ -68579,8 +68986,8 @@ var project_exports = {};
68579
68986
  __export(project_exports, {
68580
68987
  projectCommand: () => projectCommand
68581
68988
  });
68582
- import { existsSync as existsSync17, statSync } from "node:fs";
68583
- import { resolve as resolve8 } from "node:path";
68989
+ import { existsSync as existsSync18, statSync } from "node:fs";
68990
+ import { resolve as resolve9 } from "node:path";
68584
68991
  import { moveProject, projectLifecycle, renameProject } from "@cleocode/core";
68585
68992
  function formatSuccessSection(header, icon, items) {
68586
68993
  const prefix = icon ? `${icon} ` : "";
@@ -68621,8 +69028,8 @@ var init_project = __esm({
68621
69028
  async run({ args }) {
68622
69029
  const newPathRaw = args["newPath"];
68623
69030
  const dryRun = args["dry-run"] ?? false;
68624
- const newPath = resolve8(newPathRaw);
68625
- if (!dryRun && existsSync17(newPath) && statSync(newPath).isFile()) {
69031
+ const newPath = resolve9(newPathRaw);
69032
+ if (!dryRun && existsSync18(newPath) && statSync(newPath).isFile()) {
68626
69033
  cliOutput(formatErrorSection("E_INVALID_PATH", `newPath is not a directory: ${newPath}`), {
68627
69034
  command: "project",
68628
69035
  operation: "project.move"
@@ -69299,7 +69706,7 @@ var relates_exports = {};
69299
69706
  __export(relates_exports, {
69300
69707
  relatesCommand: () => relatesCommand
69301
69708
  });
69302
- import { readFile as readFile5 } from "node:fs/promises";
69709
+ import { readFile as readFile6 } from "node:fs/promises";
69303
69710
  var suggestCommand2, addCommand8, addBatchCommand2, discoverCommand3, removeCommand5, listCommand17, relatesCommand;
69304
69711
  var init_relates = __esm({
69305
69712
  "packages/cleo/src/cli/commands/relates.ts"() {
@@ -69394,15 +69801,15 @@ var init_relates = __esm({
69394
69801
  }
69395
69802
  },
69396
69803
  async run({ args }) {
69397
- const input2 = args.file === "-" ? await new Promise((resolve12, reject) => {
69804
+ const input2 = args.file === "-" ? await new Promise((resolve13, reject) => {
69398
69805
  let data = "";
69399
69806
  process.stdin.setEncoding("utf8");
69400
69807
  process.stdin.on("data", (chunk) => {
69401
69808
  data += chunk;
69402
69809
  });
69403
- process.stdin.on("end", () => resolve12(data));
69810
+ process.stdin.on("end", () => resolve13(data));
69404
69811
  process.stdin.on("error", reject);
69405
- }) : await readFile5(args.file, "utf8");
69812
+ }) : await readFile6(args.file, "utf8");
69406
69813
  const parsed = JSON.parse(input2);
69407
69814
  const payload = Array.isArray(parsed) ? { relations: parsed } : parsed;
69408
69815
  await dispatchFromCli(
@@ -69921,12 +70328,26 @@ var init_release3 = __esm({
69921
70328
  type: "boolean",
69922
70329
  description: "Reconcile a rollback rather than a publish (deferred to T9528)"
69923
70330
  },
70331
+ /**
70332
+ * T11977 / DHQ-080: tag-driven path dry-run.
70333
+ *
70334
+ * When the release plan file is absent (tag-driven publish with no prior
70335
+ * `cleo release plan`), `--dry-run` synthesises the minimal plan from the
70336
+ * git tag + CHANGELOG.md + merged-PR history and prints the derivation
70337
+ * WITHOUT writing to the DB. Safe to run against the live project — produces
70338
+ * no side effects.
70339
+ */
70340
+ "dry-run": {
70341
+ type: "boolean",
70342
+ description: "On tag-driven path (no plan file): synthesise and print the plan derivation without writing to DB"
70343
+ },
69924
70344
  json: { type: "boolean", description: "Emit LAFS envelope" }
69925
70345
  },
69926
70346
  async run({ args }) {
69927
70347
  const result = await release3.releaseReconcileV2(args.version, {
69928
70348
  fromWorkflow: args["from-workflow"] === true,
69929
- rollback: args.rollback === true
70349
+ rollback: args.rollback === true,
70350
+ dryRun: args["dry-run"] === true
69930
70351
  });
69931
70352
  if (result.success) {
69932
70353
  cliOutput(result.data, { command: "release", operation: "release.reconcile" });
@@ -71194,8 +71615,8 @@ var revert_exports = {};
71194
71615
  __export(revert_exports, {
71195
71616
  revertCommand: () => revertCommand
71196
71617
  });
71197
- import { readFile as readFile6 } from "node:fs/promises";
71198
- import { join as join28 } from "node:path";
71618
+ import { readFile as readFile7 } from "node:fs/promises";
71619
+ import { join as join29 } from "node:path";
71199
71620
  import { cwd as processCwd2 } from "node:process";
71200
71621
  import { E_RECEIPT_NOT_FOUND } from "@cleocode/core/sentient/chain-walker.js";
71201
71622
  import { SENTIENT_STATE_FILE } from "@cleocode/core/sentient/daemon.js";
@@ -71220,7 +71641,7 @@ async function loadOwnerAttestation(attestationFilePath) {
71220
71641
  let raw;
71221
71642
  if (attestationFilePath) {
71222
71643
  try {
71223
- raw = await readFile6(attestationFilePath, "utf-8");
71644
+ raw = await readFile7(attestationFilePath, "utf-8");
71224
71645
  } catch (err) {
71225
71646
  const message = err instanceof Error ? err.message : String(err);
71226
71647
  throw new Error(`Failed to read attestation file "${attestationFilePath}": ${message}`);
@@ -71248,9 +71669,9 @@ async function loadOwnerAttestation(attestationFilePath) {
71248
71669
  return obj;
71249
71670
  }
71250
71671
  async function loadOwnerPubkeys(projectRoot) {
71251
- const path6 = join28(projectRoot, OWNER_PUBKEYS_FILE);
71672
+ const path6 = join29(projectRoot, OWNER_PUBKEYS_FILE);
71252
71673
  try {
71253
- const raw = await readFile6(path6, "utf-8");
71674
+ const raw = await readFile7(path6, "utf-8");
71254
71675
  const parsed = JSON.parse(raw);
71255
71676
  if (Array.isArray(parsed)) {
71256
71677
  return new Set(parsed.filter((k) => typeof k === "string"));
@@ -71332,7 +71753,7 @@ var init_revert = __esm({
71332
71753
  if (attestation && allowedPubkeys.size > 0 && !allowedPubkeys.has(attestation.ownerPubkey)) {
71333
71754
  emitFailure2(
71334
71755
  E_OWNER_ATTESTATION_REQUIRED,
71335
- `Attestation pubkey "${attestation.ownerPubkey}" is not in the owner allowlist at ${join28(projectRoot, OWNER_PUBKEYS_FILE)}`,
71756
+ `Attestation pubkey "${attestation.ownerPubkey}" is not in the owner allowlist at ${join29(projectRoot, OWNER_PUBKEYS_FILE)}`,
71336
71757
  jsonMode
71337
71758
  );
71338
71759
  }
@@ -71385,7 +71806,7 @@ ${lines}`
71385
71806
  identity,
71386
71807
  includeHuman
71387
71808
  });
71388
- const statePath = join28(projectRoot, SENTIENT_STATE_FILE);
71809
+ const statePath = join29(projectRoot, SENTIENT_STATE_FILE);
71389
71810
  const state = await readSentientState(statePath);
71390
71811
  emitSuccess(
71391
71812
  {
@@ -71945,8 +72366,8 @@ __export(self_update_exports, {
71945
72366
  selfUpdateCommand: () => selfUpdateCommand
71946
72367
  });
71947
72368
  import { execFile } from "node:child_process";
71948
- import { readFile as readFile7 } from "node:fs/promises";
71949
- import { join as join29 } from "node:path";
72369
+ import { readFile as readFile8 } from "node:fs/promises";
72370
+ import { join as join30 } from "node:path";
71950
72371
  import * as readline3 from "node:readline";
71951
72372
  import { promisify } from "node:util";
71952
72373
  import {
@@ -71954,14 +72375,14 @@ import {
71954
72375
  CleoError as CleoError10,
71955
72376
  checkAllRegisteredProjects as checkAllRegisteredProjects2,
71956
72377
  checkStorageMigration,
71957
- getCleoHome as getCleoHome4,
72378
+ getCleoHome as getCleoHome5,
71958
72379
  getRuntimeDiagnostics as getRuntimeDiagnostics2,
71959
72380
  runUpgrade
71960
72381
  } from "@cleocode/core/internal";
71961
72382
  async function getCurrentVersion() {
71962
- const cleoHome = getCleoHome4();
72383
+ const cleoHome = getCleoHome5();
71963
72384
  try {
71964
- const content = await readFile7(join29(cleoHome, "VERSION"), "utf-8");
72385
+ const content = await readFile8(join30(cleoHome, "VERSION"), "utf-8");
71965
72386
  return (content.split("\n")[0] ?? "unknown").trim();
71966
72387
  } catch {
71967
72388
  return "unknown";
@@ -72006,7 +72427,7 @@ async function getDistTagVersion(tag) {
72006
72427
  }
72007
72428
  }
72008
72429
  async function writeRuntimeVersionMetadata(mode, source, version) {
72009
- const cleoHome = getCleoHome4();
72430
+ const cleoHome = getCleoHome5();
72010
72431
  const lines = [
72011
72432
  version,
72012
72433
  `mode=${mode}`,
@@ -72014,8 +72435,8 @@ async function writeRuntimeVersionMetadata(mode, source, version) {
72014
72435
  `installed=${(/* @__PURE__ */ new Date()).toISOString()}`
72015
72436
  ];
72016
72437
  await import("node:fs/promises").then(
72017
- ({ writeFile: writeFile4, mkdir: mkdir6 }) => mkdir6(cleoHome, { recursive: true }).then(
72018
- () => writeFile4(join29(cleoHome, "VERSION"), `${lines.join("\n")}
72438
+ ({ writeFile: writeFile4, mkdir: mkdir7 }) => mkdir7(cleoHome, { recursive: true }).then(
72439
+ () => writeFile4(join30(cleoHome, "VERSION"), `${lines.join("\n")}
72019
72440
  `, "utf-8")
72020
72441
  )
72021
72442
  );
@@ -72053,11 +72474,11 @@ async function runPostUpdateDiagnostics(opts) {
72053
72474
  input: process.stdin,
72054
72475
  output: process.stdout
72055
72476
  });
72056
- shouldMigrate = await new Promise((resolve12) => {
72477
+ shouldMigrate = await new Promise((resolve13) => {
72057
72478
  rl.question(" Do you want to run the upgrade now? [Y/n] ", (answer) => {
72058
72479
  rl.close();
72059
72480
  const clean = answer.trim().toLowerCase();
72060
- resolve12(clean === "" || clean === "y" || clean === "yes");
72481
+ resolve13(clean === "" || clean === "y" || clean === "yes");
72061
72482
  });
72062
72483
  });
72063
72484
  }
@@ -72495,7 +72916,7 @@ __export(sentient_exports, {
72495
72916
  promptAcceptExecution: () => promptAcceptExecution,
72496
72917
  sentientCommand: () => sentientCommand
72497
72918
  });
72498
- import { join as join30 } from "node:path";
72919
+ import { join as join31 } from "node:path";
72499
72920
  import { cwd as processCwd3, stdin, stdout } from "node:process";
72500
72921
  import { createInterface as createInterface4 } from "node:readline/promises";
72501
72922
  import {
@@ -72584,7 +73005,7 @@ var init_sentient3 = __esm({
72584
73005
  return;
72585
73006
  }
72586
73007
  if (dryRun) {
72587
- const statePath2 = join30(projectRoot, SENTIENT_STATE_FILE2);
73008
+ const statePath2 = join31(projectRoot, SENTIENT_STATE_FILE2);
72588
73009
  const outcome = await safeRunTick({ projectRoot, statePath: statePath2, dryRun: true });
72589
73010
  emitSuccess2(
72590
73011
  { dryRun: true, outcome },
@@ -72700,7 +73121,7 @@ Logs: ${logPath}`
72700
73121
  const jsonMode = args.json === true;
72701
73122
  const dryRun = args["dry-run"] === true;
72702
73123
  try {
72703
- const statePath = join30(projectRoot, SENTIENT_STATE_FILE2);
73124
+ const statePath = join31(projectRoot, SENTIENT_STATE_FILE2);
72704
73125
  const outcome = await safeRunTick({ projectRoot, statePath, dryRun });
72705
73126
  emitSuccess2(
72706
73127
  { outcome, dryRun },
@@ -72792,7 +73213,7 @@ Logs: ${logPath}`
72792
73213
  return;
72793
73214
  }
72794
73215
  await db.update(tasks).set({ status: "pending", updatedAt: now }).where(eq2(tasks.id, id)).run();
72795
- const statePath = join30(projectRoot, SENTIENT_STATE_FILE2);
73216
+ const statePath = join31(projectRoot, SENTIENT_STATE_FILE2);
72796
73217
  const state = await readSentientState2(statePath);
72797
73218
  await patchSentientState(statePath, {
72798
73219
  tier2Stats: {
@@ -72888,7 +73309,7 @@ Logs: ${logPath}`
72888
73309
  return;
72889
73310
  }
72890
73311
  await db.update(tasks).set({ status: "cancelled", cancellationReason: reason, cancelledAt: now, updatedAt: now }).where(eq2(tasks.id, id)).run();
72891
- const statePath = join30(projectRoot, SENTIENT_STATE_FILE2);
73312
+ const statePath = join31(projectRoot, SENTIENT_STATE_FILE2);
72892
73313
  const state = await readSentientState2(statePath);
72893
73314
  await patchSentientState(statePath, {
72894
73315
  tier2Stats: {
@@ -72933,7 +73354,7 @@ Logs: ${logPath}`
72933
73354
  const projectRoot = resolveProjectRoot5(args.project);
72934
73355
  const jsonMode = args.json === true;
72935
73356
  try {
72936
- const statePath = join30(projectRoot, SENTIENT_STATE_FILE2);
73357
+ const statePath = join31(projectRoot, SENTIENT_STATE_FILE2);
72937
73358
  const outcome = await safeRunProposeTick({ projectRoot, statePath });
72938
73359
  emitSuccess2(
72939
73360
  { outcome },
@@ -72953,7 +73374,7 @@ Logs: ${logPath}`
72953
73374
  const projectRoot = resolveProjectRoot5(args.project);
72954
73375
  const jsonMode = args.json === true;
72955
73376
  try {
72956
- const statePath = join30(projectRoot, SENTIENT_STATE_FILE2);
73377
+ const statePath = join31(projectRoot, SENTIENT_STATE_FILE2);
72957
73378
  const updated = await patchSentientState(statePath, { tier2Enabled: true });
72958
73379
  emitSuccess2({ tier2Enabled: updated.tier2Enabled }, jsonMode, "Tier-2 proposals enabled");
72959
73380
  } catch (err) {
@@ -72972,7 +73393,7 @@ Logs: ${logPath}`
72972
73393
  const projectRoot = resolveProjectRoot5(args.project);
72973
73394
  const jsonMode = args.json === true;
72974
73395
  try {
72975
- const statePath = join30(projectRoot, SENTIENT_STATE_FILE2);
73396
+ const statePath = join31(projectRoot, SENTIENT_STATE_FILE2);
72976
73397
  const updated = await patchSentientState(statePath, { tier2Enabled: false });
72977
73398
  emitSuccess2({ tier2Enabled: updated.tier2Enabled }, jsonMode, "Tier-2 proposals disabled");
72978
73399
  } catch (err) {
@@ -73003,7 +73424,7 @@ Logs: ${logPath}`
73003
73424
  const projectRoot = resolveProjectRoot5(args.project);
73004
73425
  const jsonMode = args.json === true;
73005
73426
  try {
73006
- const statePath = join30(projectRoot, SENTIENT_STATE_FILE2);
73427
+ const statePath = join31(projectRoot, SENTIENT_STATE_FILE2);
73007
73428
  const state = await readSentientState2(statePath);
73008
73429
  emitSuccess2(
73009
73430
  {
@@ -73785,7 +74206,7 @@ async function promptOwnerAuthPassword(sessionName) {
73785
74206
  terminal: true
73786
74207
  });
73787
74208
  process.stderr.write(`[cleo] Enter owner-auth password for session "${sessionName}": `);
73788
- const password = await new Promise((resolve12) => {
74209
+ const password = await new Promise((resolve13) => {
73789
74210
  if (process.stdin.setRawMode) {
73790
74211
  process.stdin.setRawMode(
73791
74212
  true
@@ -73803,10 +74224,10 @@ async function promptOwnerAuthPassword(sessionName) {
73803
74224
  );
73804
74225
  }
73805
74226
  process.stderr.write("\n");
73806
- resolve12(pw);
74227
+ resolve13(pw);
73807
74228
  } else if (ch === "") {
73808
74229
  process.stderr.write("\n[cleo] Cancelled.\n");
73809
- resolve12("");
74230
+ resolve13("");
73810
74231
  } else if (ch === "\x7F" || ch === "\b") {
73811
74232
  pw = pw.slice(0, -1);
73812
74233
  } else {
@@ -76726,12 +77147,12 @@ var init_telemetry2 = __esm({
76726
77147
 
76727
77148
  // packages/cleo/src/cli/commands/templates/lib.ts
76728
77149
  import { readFileSync as readFileSync14 } from "node:fs";
76729
- import { isAbsolute as isAbsolute3, resolve as resolve9 } from "node:path";
77150
+ import { isAbsolute as isAbsolute3, resolve as resolve10 } from "node:path";
76730
77151
  import { getProjectRoot as getProjectRoot58 } from "@cleocode/core";
76731
77152
  import { resolveSourcePathAbsolute } from "@cleocode/core/templates/registry";
76732
77153
  function resolveProjectRoot6(raw) {
76733
77154
  if (typeof raw === "string" && raw.length > 0) {
76734
- return isAbsolute3(raw) ? raw : resolve9(process.cwd(), raw);
77155
+ return isAbsolute3(raw) ? raw : resolve10(process.cwd(), raw);
76735
77156
  }
76736
77157
  return getProjectRoot58();
76737
77158
  }
@@ -76752,8 +77173,8 @@ var init_lib = __esm({
76752
77173
  });
76753
77174
 
76754
77175
  // packages/cleo/src/cli/commands/templates/diff.ts
76755
- import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
76756
- import { join as join31 } from "node:path";
77176
+ import { existsSync as existsSync19, readFileSync as readFileSync15 } from "node:fs";
77177
+ import { join as join32 } from "node:path";
76757
77178
  import { getTemplateById } from "@cleocode/core/templates/registry";
76758
77179
  function unifiedDiff(a, b) {
76759
77180
  const aLines = a.length === 0 ? [] : a.split("\n");
@@ -76823,7 +77244,7 @@ var init_diff = __esm({
76823
77244
  let rendered;
76824
77245
  try {
76825
77246
  projectRoot = resolveProjectRoot6(args["project"]);
76826
- installPath = join31(projectRoot, entry.installPath);
77247
+ installPath = join32(projectRoot, entry.installPath);
76827
77248
  const source = readTemplateSource(entry);
76828
77249
  rendered = applySubstitution(entry, source).rendered;
76829
77250
  } catch (err) {
@@ -76834,7 +77255,7 @@ var init_diff = __esm({
76834
77255
  process.exit(1 /* GENERAL_ERROR */);
76835
77256
  return;
76836
77257
  }
76837
- if (!existsSync18(installPath)) {
77258
+ if (!existsSync19(installPath)) {
76838
77259
  const missingResult = {
76839
77260
  id,
76840
77261
  installPath,
@@ -76873,8 +77294,8 @@ ${unifiedDiff("", rendered)}`
76873
77294
  });
76874
77295
 
76875
77296
  // packages/cleo/src/cli/commands/templates/install.ts
76876
- import { existsSync as existsSync19, mkdirSync as mkdirSync5, readFileSync as readFileSync16, writeFileSync as writeFileSync5 } from "node:fs";
76877
- import { dirname as dirname9, join as join32 } from "node:path";
77297
+ import { existsSync as existsSync20, mkdirSync as mkdirSync5, readFileSync as readFileSync16, writeFileSync as writeFileSync5 } from "node:fs";
77298
+ import { dirname as dirname10, join as join33 } from "node:path";
76878
77299
  import { getTemplateById as getTemplateById2 } from "@cleocode/core/templates/registry";
76879
77300
  var templatesInstallCommand;
76880
77301
  var init_install = __esm({
@@ -76928,7 +77349,7 @@ var init_install = __esm({
76928
77349
  let substituted;
76929
77350
  try {
76930
77351
  projectRoot = resolveProjectRoot6(args["project"]);
76931
- installPath = join32(projectRoot, entry.installPath);
77352
+ installPath = join33(projectRoot, entry.installPath);
76932
77353
  const source = readTemplateSource(entry);
76933
77354
  const sub = applySubstitution(entry, source);
76934
77355
  rendered = sub.rendered;
@@ -76941,7 +77362,7 @@ var init_install = __esm({
76941
77362
  process.exit(1 /* GENERAL_ERROR */);
76942
77363
  return;
76943
77364
  }
76944
- if (existsSync19(installPath)) {
77365
+ if (existsSync20(installPath)) {
76945
77366
  const current = readFileSync16(installPath, "utf8");
76946
77367
  if (current === rendered) {
76947
77368
  const noopResult = {
@@ -76959,7 +77380,7 @@ var init_install = __esm({
76959
77380
  }
76960
77381
  }
76961
77382
  try {
76962
- mkdirSync5(dirname9(installPath), { recursive: true });
77383
+ mkdirSync5(dirname10(installPath), { recursive: true });
76963
77384
  writeFileSync5(installPath, rendered, "utf8");
76964
77385
  } catch (err) {
76965
77386
  const message = err instanceof Error ? err.message : String(err);
@@ -77090,8 +77511,8 @@ var init_show3 = __esm({
77090
77511
  });
77091
77512
 
77092
77513
  // packages/cleo/src/cli/commands/templates/upgrade.ts
77093
- import { existsSync as existsSync20, mkdirSync as mkdirSync6, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "node:fs";
77094
- import { dirname as dirname10, join as join33 } from "node:path";
77514
+ import { existsSync as existsSync21, mkdirSync as mkdirSync6, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "node:fs";
77515
+ import { dirname as dirname11, join as join34 } from "node:path";
77095
77516
  import { getTemplateById as getTemplateById4 } from "@cleocode/core/templates/registry";
77096
77517
  var templatesUpgradeCommand;
77097
77518
  var init_upgrade2 = __esm({
@@ -77153,7 +77574,7 @@ var init_upgrade2 = __esm({
77153
77574
  let rendered;
77154
77575
  try {
77155
77576
  projectRoot = resolveProjectRoot6(args["project"]);
77156
- installPath = join33(projectRoot, entry.installPath);
77577
+ installPath = join34(projectRoot, entry.installPath);
77157
77578
  const source = readTemplateSource(entry);
77158
77579
  rendered = applySubstitution(entry, source).rendered;
77159
77580
  } catch (err) {
@@ -77166,7 +77587,7 @@ var init_upgrade2 = __esm({
77166
77587
  }
77167
77588
  const previewOnly = args["diff"] === true;
77168
77589
  const accept = args["accept"] === true;
77169
- const current = existsSync20(installPath) ? readFileSync17(installPath, "utf8") : null;
77590
+ const current = existsSync21(installPath) ? readFileSync17(installPath, "utf8") : null;
77170
77591
  const diffBody = current === null ? "" : current === rendered ? "" : unifiedDiff(current, rendered);
77171
77592
  let outcome;
77172
77593
  let reason;
@@ -77201,7 +77622,7 @@ var init_upgrade2 = __esm({
77201
77622
  }
77202
77623
  if (shouldWrite) {
77203
77624
  try {
77204
- mkdirSync6(dirname10(installPath), { recursive: true });
77625
+ mkdirSync6(dirname11(installPath), { recursive: true });
77205
77626
  writeFileSync6(installPath, rendered, "utf8");
77206
77627
  } catch (err) {
77207
77628
  const message = err instanceof Error ? err.message : String(err);
@@ -77716,7 +78137,7 @@ __export(transcript_exports, {
77716
78137
  transcriptCommand: () => transcriptCommand
77717
78138
  });
77718
78139
  import { homedir as homedir6 } from "node:os";
77719
- import { join as join34 } from "node:path";
78140
+ import { join as join35 } from "node:path";
77720
78141
  import { getProjectRoot as getProjectRoot60 } from "@cleocode/core";
77721
78142
  import {
77722
78143
  parseDurationMs,
@@ -77770,7 +78191,7 @@ var init_transcript = __esm({
77770
78191
  }
77771
78192
  return;
77772
78193
  }
77773
- const projectsDir = args["projects-dir"] ?? join34(homedir6(), ".claude", "projects");
78194
+ const projectsDir = args["projects-dir"] ?? join35(homedir6(), ".claude", "projects");
77774
78195
  try {
77775
78196
  const result = await scanTranscripts(projectsDir);
77776
78197
  cliOutput(
@@ -78076,7 +78497,7 @@ var init_transcript = __esm({
78076
78497
  process.exit(2);
78077
78498
  return;
78078
78499
  }
78079
- const projectsDir = args["projects-dir"] ?? join34(homedir6(), ".claude", "projects");
78500
+ const projectsDir = args["projects-dir"] ?? join35(homedir6(), ".claude", "projects");
78080
78501
  try {
78081
78502
  const pruneResult = await pruneTranscripts({
78082
78503
  olderThanMs,
@@ -78130,6 +78551,131 @@ var init_transcript = __esm({
78130
78551
  }
78131
78552
  });
78132
78553
 
78554
+ // packages/cleo/src/cli/lib/gateway-auto-start.ts
78555
+ var gateway_auto_start_exports = {};
78556
+ __export(gateway_auto_start_exports, {
78557
+ GATEWAY_DEFAULT_HOST: () => GATEWAY_DEFAULT_HOST,
78558
+ GATEWAY_DEFAULT_PORT: () => GATEWAY_DEFAULT_PORT,
78559
+ GATEWAY_WAIT_TIMEOUT_MS: () => GATEWAY_WAIT_TIMEOUT_MS,
78560
+ pollPort: () => pollPort,
78561
+ probePort: () => probePort,
78562
+ shouldAutoStartGateway: () => shouldAutoStartGateway,
78563
+ spawnGatewayIfDown: () => spawnGatewayIfDown
78564
+ });
78565
+ import { once } from "node:events";
78566
+ import { createWriteStream } from "node:fs";
78567
+ import { mkdir as mkdir6 } from "node:fs/promises";
78568
+ import * as net from "node:net";
78569
+ import { dirname as dirname12, join as join36 } from "node:path";
78570
+ import { fileURLToPath as fileURLToPath7 } from "node:url";
78571
+ import { spawnWrapped } from "@cleocode/core/resources/spawn-wrapper.js";
78572
+ function shouldAutoStartGateway(config) {
78573
+ if (config === null || config === void 0) return true;
78574
+ const daemon = config["daemon"];
78575
+ if (daemon === null || daemon === void 0) return true;
78576
+ if (typeof daemon !== "object") return true;
78577
+ const daemonObj = daemon;
78578
+ const autoStart = daemonObj["autoStart"];
78579
+ if (autoStart === false) return false;
78580
+ return true;
78581
+ }
78582
+ function probePort(port, host = GATEWAY_DEFAULT_HOST) {
78583
+ return new Promise((resolve13) => {
78584
+ const socket = net.connect({ port, host });
78585
+ socket.once("connect", () => {
78586
+ socket.destroy();
78587
+ resolve13(true);
78588
+ });
78589
+ socket.once("error", () => {
78590
+ socket.destroy();
78591
+ resolve13(false);
78592
+ });
78593
+ });
78594
+ }
78595
+ async function pollPort(port, host, timeoutMs) {
78596
+ const deadline = Date.now() + timeoutMs;
78597
+ let delay = POLL_INITIAL_DELAY_MS;
78598
+ while (Date.now() < deadline) {
78599
+ const ok = await probePort(port, host);
78600
+ if (ok) return true;
78601
+ const remaining = deadline - Date.now();
78602
+ const wait = Math.min(delay, remaining, POLL_MAX_DELAY_MS);
78603
+ if (wait <= 0) break;
78604
+ await new Promise((r) => setTimeout(r, wait));
78605
+ delay = Math.min(delay * 2, POLL_MAX_DELAY_MS);
78606
+ }
78607
+ return probePort(port, host);
78608
+ }
78609
+ function resolveCliEntryPath() {
78610
+ const thisDir = dirname12(fileURLToPath7(import.meta.url));
78611
+ const pkgRoot = join36(thisDir, "..", "..", "..", "..", "..");
78612
+ return join36(pkgRoot, "dist", "cli", "index.js");
78613
+ }
78614
+ async function spawnGatewayIfDown(opts = {}) {
78615
+ const port = opts.port ?? GATEWAY_DEFAULT_PORT;
78616
+ const host = opts.host ?? GATEWAY_DEFAULT_HOST;
78617
+ const waitTimeoutMs = opts.waitTimeoutMs ?? GATEWAY_WAIT_TIMEOUT_MS;
78618
+ if (await probePort(port, host)) {
78619
+ return { reachable: true, spawned: false };
78620
+ }
78621
+ let cliEntry;
78622
+ try {
78623
+ cliEntry = opts.cliEntryPath ?? resolveCliEntryPath();
78624
+ } catch (err) {
78625
+ const reason = `gateway auto-start: cannot resolve CLI entry path \u2014 ${err instanceof Error ? err.message : String(err)}`;
78626
+ return { reachable: false, spawned: false, reason };
78627
+ }
78628
+ let outFd = "ignore";
78629
+ let errFd = "ignore";
78630
+ try {
78631
+ const { getCleoLogDir } = await import("@cleocode/core");
78632
+ const logDir = getCleoLogDir();
78633
+ await mkdir6(logDir, { recursive: true });
78634
+ const outStream = createWriteStream(join36(logDir, "gateway.log"), { flags: "a" });
78635
+ const errStream = createWriteStream(join36(logDir, "gateway.err"), { flags: "a" });
78636
+ await Promise.all([once(outStream, "open"), once(errStream, "open")]);
78637
+ outFd = outStream;
78638
+ errFd = errStream;
78639
+ } catch {
78640
+ }
78641
+ const spawnArgs = ["daemon", "serve"];
78642
+ if (port !== GATEWAY_DEFAULT_PORT) spawnArgs.push("--port", String(port));
78643
+ if (host !== GATEWAY_DEFAULT_HOST) spawnArgs.push("--host", host);
78644
+ try {
78645
+ const { child } = spawnWrapped(
78646
+ process.execPath,
78647
+ [cliEntry, ...spawnArgs],
78648
+ {
78649
+ detached: true,
78650
+ stdio: ["ignore", outFd, errFd],
78651
+ env: { ...process.env, CLEO_GATEWAY_AUTO_STARTED: "1" }
78652
+ },
78653
+ { scopeClass: "daemon", scopeId: "gateway" }
78654
+ );
78655
+ child.unref();
78656
+ } catch (err) {
78657
+ const reason = `gateway auto-start: spawn failed \u2014 ${err instanceof Error ? err.message : String(err)}`;
78658
+ return { reachable: false, spawned: false, reason };
78659
+ }
78660
+ const reachable = await pollPort(port, host, waitTimeoutMs);
78661
+ return {
78662
+ reachable,
78663
+ spawned: true,
78664
+ reason: reachable ? void 0 : "gateway did not become reachable within the timeout"
78665
+ };
78666
+ }
78667
+ var GATEWAY_DEFAULT_PORT, GATEWAY_DEFAULT_HOST, GATEWAY_WAIT_TIMEOUT_MS, POLL_INITIAL_DELAY_MS, POLL_MAX_DELAY_MS;
78668
+ var init_gateway_auto_start = __esm({
78669
+ "packages/cleo/src/cli/lib/gateway-auto-start.ts"() {
78670
+ "use strict";
78671
+ GATEWAY_DEFAULT_PORT = 7777;
78672
+ GATEWAY_DEFAULT_HOST = "127.0.0.1";
78673
+ GATEWAY_WAIT_TIMEOUT_MS = 1e4;
78674
+ POLL_INITIAL_DELAY_MS = 100;
78675
+ POLL_MAX_DELAY_MS = 1e3;
78676
+ }
78677
+ });
78678
+
78133
78679
  // packages/cleo/src/cli/lib/pi-tui-loader.ts
78134
78680
  function shapeCheck(candidate) {
78135
78681
  if (candidate === null || typeof candidate !== "object") return null;
@@ -78558,7 +79104,7 @@ async function fetchTaskRows(baseUrl) {
78558
79104
  }
78559
79105
  }
78560
79106
  function runInteractiveLoop(piTui, component, refresh) {
78561
- return new Promise((resolve12) => {
79107
+ return new Promise((resolve13) => {
78562
79108
  const terminal = new piTui.ProcessTerminal();
78563
79109
  const tui = new piTui.TUI(terminal);
78564
79110
  tui.addChild(component);
@@ -78574,7 +79120,7 @@ function runInteractiveLoop(piTui, component, refresh) {
78574
79120
  component.teardownWorkerPanel();
78575
79121
  dispose();
78576
79122
  tui.stop();
78577
- resolve12();
79123
+ resolve13();
78578
79124
  return { consume: true };
78579
79125
  }
78580
79126
  if (data === "\x1B[C" || data === "l" || data === " ") {
@@ -78626,13 +79172,35 @@ async function runCockpit(options = {}, sink = (line) => process.stdout.write(`$
78626
79172
  const baseUrl = options.baseUrl ?? DEFAULT_TUI_BASE_URL;
78627
79173
  const dispatch = options.dispatch ?? dispatchWorker;
78628
79174
  const subscribe = options.subscribe ?? subscribeOrchestrateEvents;
78629
- const rows = await fetchTaskRows(baseUrl);
79175
+ let gatewayAutoStarted = false;
79176
+ const autoStart = options.autoStart !== false;
79177
+ let rows = await fetchTaskRows(baseUrl);
79178
+ if (rows === null && autoStart) {
79179
+ let port;
79180
+ let host;
79181
+ try {
79182
+ const u = new URL(baseUrl);
79183
+ const parsed = Number.parseInt(u.port, 10);
79184
+ port = Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
79185
+ host = u.hostname || void 0;
79186
+ } catch {
79187
+ }
79188
+ const spawnResult = await spawnGatewayIfDown({
79189
+ ...options.spawnOpts ?? {},
79190
+ ...port !== void 0 ? { port } : {},
79191
+ ...host !== void 0 ? { host } : {}
79192
+ });
79193
+ if (spawnResult.reachable) {
79194
+ gatewayAutoStarted = spawnResult.spawned;
79195
+ rows = await fetchTaskRows(baseUrl);
79196
+ }
79197
+ }
78630
79198
  if (rows === null) {
78631
79199
  sink("CLEO cockpit: the daemon gateway is not reachable.");
78632
79200
  sink(` Tried: ${baseUrl}/v1`);
78633
79201
  sink(" Start it with: cleo daemon serve");
78634
79202
  sink(" (override the target with: cleo tui --base-url <url>)");
78635
- return { outcome: "daemon-down", baseUrl, piTui: false };
79203
+ return { outcome: "daemon-down", baseUrl, piTui: false, gatewayAutoStarted };
78636
79204
  }
78637
79205
  const board = buildKanbanBoard(rows);
78638
79206
  const piTuiOk = await isPiTuiAvailable();
@@ -78642,16 +79210,16 @@ async function runCockpit(options = {}, sink = (line) => process.stdout.write(`$
78642
79210
  if (!piTuiOk) {
78643
79211
  sink("");
78644
79212
  sink(PI_TUI_INSTALL_HINT);
78645
- return { outcome: "degraded-pi", baseUrl, piTui: false };
79213
+ return { outcome: "degraded-pi", baseUrl, piTui: false, gatewayAutoStarted };
78646
79214
  }
78647
- return { outcome: "rendered", baseUrl, piTui: false };
79215
+ return { outcome: "rendered", baseUrl, piTui: false, gatewayAutoStarted };
78648
79216
  }
78649
79217
  const piTui = await loadPiTui();
78650
79218
  if (piTui === null) {
78651
79219
  for (const line of renderKanbanBoardText(board)) sink(line);
78652
79220
  sink("");
78653
79221
  sink(PI_TUI_INSTALL_HINT);
78654
- return { outcome: "degraded-pi", baseUrl, piTui: false };
79222
+ return { outcome: "degraded-pi", baseUrl, piTui: false, gatewayAutoStarted };
78655
79223
  }
78656
79224
  const component = new KanbanBoardComponent(board, { baseUrl, dispatch, subscribe });
78657
79225
  const refresh = async () => {
@@ -78659,12 +79227,13 @@ async function runCockpit(options = {}, sink = (line) => process.stdout.write(`$
78659
79227
  if (fresh !== null) component.setBoard(buildKanbanBoard(fresh));
78660
79228
  };
78661
79229
  await runInteractiveLoop(piTui, component, refresh);
78662
- return { outcome: "rendered", baseUrl, piTui: true };
79230
+ return { outcome: "rendered", baseUrl, piTui: true, gatewayAutoStarted };
78663
79231
  }
78664
79232
  var DEFAULT_TUI_BASE_URL, DISPATCHABLE_LANES, KanbanBoardComponent;
78665
79233
  var init_cockpit = __esm({
78666
79234
  "packages/cleo/src/cli/lib/tui/cockpit.ts"() {
78667
79235
  "use strict";
79236
+ init_gateway_auto_start();
78668
79237
  init_pi_tui_loader();
78669
79238
  init_dispatch();
78670
79239
  init_kanban_board();
@@ -79415,7 +79984,7 @@ var upgrade_exports = {};
79415
79984
  __export(upgrade_exports, {
79416
79985
  upgradeCommand: () => upgradeCommand
79417
79986
  });
79418
- import { resolve as resolve10 } from "node:path";
79987
+ import { resolve as resolve11 } from "node:path";
79419
79988
  import { CleoError as CleoError12, diagnoseUpgrade, runUpgrade as runUpgrade2, upgradeWorkflows as upgradeWorkflows2 } from "@cleocode/core/internal";
79420
79989
  var workflowsSubcommand, upgradeCommand;
79421
79990
  var init_upgrade3 = __esm({
@@ -79455,7 +80024,7 @@ var init_upgrade3 = __esm({
79455
80024
  const dryRun = args["dry-run"] === true || args.check === true;
79456
80025
  const force = args.force === true && !dryRun;
79457
80026
  const result = await upgradeWorkflows2({
79458
- projectRoot: resolve10(process.cwd()),
80027
+ projectRoot: resolve11(process.cwd()),
79459
80028
  templatesDir: getWorkflowTemplatesDir2(),
79460
80029
  dryRun,
79461
80030
  force
@@ -79709,223 +80278,38 @@ var init_verify = __esm({
79709
80278
  }
79710
80279
  });
79711
80280
 
79712
- // packages/cleo/src/cli/web-subsystem.ts
79713
- import { execFileSync as execFileSync4, spawn as spawn3 } from "node:child_process";
79714
- import { mkdir as mkdir5, open, readFile as readFile8, rm, stat as stat2, writeFile as writeFile3 } from "node:fs/promises";
79715
- import { join as join35 } from "node:path";
79716
- import { getCleoHome as getCleoHome5 } from "@cleocode/core";
79717
- import { defineSubsystem as defineSubsystem2 } from "@cleocode/runtime/daemon";
79718
- function getWebPaths() {
79719
- const cleoHome = getCleoHome5();
79720
- return {
79721
- pidFile: join35(cleoHome, "web-server.pid"),
79722
- configFile: join35(cleoHome, "web-server.json"),
79723
- logDir: join35(cleoHome, "logs"),
79724
- logFile: join35(cleoHome, "logs", "web-server.log")
79725
- };
79726
- }
79727
- function isWebProcessRunning(pid) {
79728
- try {
79729
- process.kill(pid, 0);
79730
- return true;
79731
- } catch {
79732
- return false;
79733
- }
79734
- }
79735
- async function getWebStatus() {
79736
- const { pidFile, configFile } = getWebPaths();
79737
- try {
79738
- const pidStr = (await readFile8(pidFile, "utf-8")).trim();
79739
- const pid = Number.parseInt(pidStr, 10);
79740
- if (Number.isNaN(pid) || !isWebProcessRunning(pid)) {
79741
- return { running: false, pid: null, port: null, host: null, url: null };
79742
- }
79743
- let port = WEB_DEFAULT_PORT;
79744
- let host = WEB_DEFAULT_HOST;
79745
- try {
79746
- const config = JSON.parse(await readFile8(configFile, "utf-8"));
79747
- port = config.port ?? WEB_DEFAULT_PORT;
79748
- host = config.host ?? WEB_DEFAULT_HOST;
79749
- } catch {
79750
- }
79751
- return { running: true, pid, port, host, url: `http://${host}:${port}` };
79752
- } catch {
79753
- return { running: false, pid: null, port: null, host: null, url: null };
79754
- }
79755
- }
79756
- function createWebSubsystem(opts = {}) {
79757
- const port = opts.port ?? WEB_DEFAULT_PORT;
79758
- const host = opts.host ?? WEB_DEFAULT_HOST;
79759
- let live;
79760
- return defineSubsystem2({
79761
- name: WEB_SUBSYSTEM_NAME,
79762
- async start() {
79763
- const { pidFile, configFile, logFile, logDir } = getWebPaths();
79764
- const existing = await getWebStatus();
79765
- if (existing.running && existing.pid !== null) {
79766
- const ctx2 = {
79767
- pid: existing.pid,
79768
- pidFile,
79769
- logFile,
79770
- port: existing.port ?? port,
79771
- host: existing.host ?? host
79772
- };
79773
- live = ctx2;
79774
- return ctx2;
79775
- }
79776
- const projectRoot = process.env["CLEO_ROOT"] ?? process.cwd();
79777
- const studioDir = process.env["CLEO_STUDIO_DIR"] ?? join35(projectRoot, "packages", "studio", "build");
79778
- const webIndexPath = join35(studioDir, "index.js");
79779
- await mkdir5(logDir, { recursive: true });
79780
- await writeFile3(
79781
- configFile,
79782
- JSON.stringify({ port, host, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
79783
- );
79784
- try {
79785
- await stat2(webIndexPath);
79786
- } catch {
79787
- try {
79788
- execFileSync4("pnpm", ["--filter", "@cleocode/studio", "run", "build"], {
79789
- cwd: projectRoot,
79790
- stdio: "ignore"
79791
- });
79792
- } catch {
79793
- throw new Error(
79794
- `Studio build failed. Run: pnpm --filter @cleocode/studio run build
79795
- Logs: ${logFile}`
79796
- );
79797
- }
79798
- }
79799
- const logFileHandle = await open(logFile, "a");
79800
- const serverProcess = spawn3("node", [webIndexPath], {
79801
- cwd: studioDir,
79802
- env: {
79803
- ...process.env,
79804
- HOST: host,
79805
- PORT: String(port),
79806
- CLEO_ROOT: projectRoot
79807
- },
79808
- detached: true,
79809
- stdio: ["ignore", logFileHandle.fd, logFileHandle.fd]
79810
- });
79811
- serverProcess.unref();
79812
- const pidFileTmp = `${pidFile}.tmp`;
79813
- await writeFile3(pidFileTmp, String(serverProcess.pid));
79814
- await rm(pidFile, { force: true });
79815
- await writeFile3(pidFile, String(serverProcess.pid));
79816
- await rm(pidFileTmp, { force: true });
79817
- await logFileHandle.close();
79818
- let started = false;
79819
- for (let i = 0; i < STARTUP_POLL_ITERATIONS; i++) {
79820
- try {
79821
- const response = await fetch(`http://${host}:${port}/api/health`);
79822
- if (response.ok) {
79823
- started = true;
79824
- break;
79825
- }
79826
- } catch {
79827
- }
79828
- await new Promise((resolve12) => setTimeout(resolve12, 500));
79829
- }
79830
- if (!started) {
79831
- try {
79832
- process.kill(serverProcess.pid, "SIGTERM");
79833
- } catch {
79834
- }
79835
- await rm(pidFile, { force: true });
79836
- throw new Error("Studio web server failed to start within 15 seconds");
79837
- }
79838
- const ctx = {
79839
- pid: serverProcess.pid,
79840
- pidFile,
79841
- logFile,
79842
- port,
79843
- host
79844
- };
79845
- live = ctx;
79846
- return ctx;
79847
- },
79848
- healthProbe() {
79849
- if (live === void 0) {
79850
- const stopped = "stopped";
79851
- return {
79852
- child_id: WEB_SUBSYSTEM_NAME,
79853
- pid: 0,
79854
- state: stopped,
79855
- restart_count: 0,
79856
- detail: "not started"
79857
- };
79858
- }
79859
- const alive = isWebProcessRunning(live.pid);
79860
- const state = alive ? "running" : "stopped";
79861
- return {
79862
- child_id: WEB_SUBSYSTEM_NAME,
79863
- pid: alive ? live.pid : 0,
79864
- state,
79865
- restart_count: 0,
79866
- detail: alive ? `url=http://${live.host}:${live.port} pid=${live.pid}` : `pid=${live.pid} exited`
79867
- };
79868
- },
79869
- async shutdown(context) {
79870
- const { pidFile } = context;
79871
- if (!isWebProcessRunning(context.pid)) {
79872
- await rm(pidFile, { force: true });
79873
- live = void 0;
79874
- return;
79875
- }
79876
- try {
79877
- if (process.platform === "win32") {
79878
- spawn3("taskkill", ["/PID", String(context.pid), "/T"], { stdio: "ignore" });
79879
- } else {
79880
- process.kill(context.pid, "SIGTERM");
79881
- }
79882
- } catch {
79883
- }
79884
- for (let i = 0; i < SIGTERM_GRACE_ITERATIONS2; i++) {
79885
- if (!isWebProcessRunning(context.pid)) break;
79886
- await new Promise((resolve12) => setTimeout(resolve12, 500));
79887
- }
79888
- if (isWebProcessRunning(context.pid)) {
79889
- try {
79890
- if (process.platform === "win32") {
79891
- spawn3("taskkill", ["/PID", String(context.pid), "/F", "/T"], { stdio: "ignore" });
79892
- } else {
79893
- process.kill(context.pid, "SIGKILL");
79894
- }
79895
- } catch {
79896
- }
79897
- }
79898
- await rm(pidFile, { force: true });
79899
- live = void 0;
79900
- }
79901
- });
79902
- }
79903
- var WEB_DEFAULT_PORT, WEB_DEFAULT_HOST, WEB_SUBSYSTEM_NAME, SIGTERM_GRACE_ITERATIONS2, STARTUP_POLL_ITERATIONS;
79904
- var init_web_subsystem = __esm({
79905
- "packages/cleo/src/cli/web-subsystem.ts"() {
79906
- "use strict";
79907
- WEB_DEFAULT_PORT = 3456;
79908
- WEB_DEFAULT_HOST = "127.0.0.1";
79909
- WEB_SUBSYSTEM_NAME = "cleo-web";
79910
- SIGTERM_GRACE_ITERATIONS2 = 60;
79911
- STARTUP_POLL_ITERATIONS = 30;
79912
- }
79913
- });
79914
-
79915
80281
  // packages/cleo/src/cli/commands/web.ts
79916
80282
  var web_exports = {};
79917
80283
  __export(web_exports, {
80284
+ buildStudioUrl: () => buildStudioUrl,
80285
+ openBrowser: () => openBrowser,
79918
80286
  webCommand: () => webCommand
79919
80287
  });
79920
80288
  import { spawn as spawn4 } from "node:child_process";
79921
80289
  import { rm as rm2 } from "node:fs/promises";
79922
80290
  import { CleoError as CleoError13, formatError as formatError5 } from "@cleocode/core";
80291
+ function buildStudioUrl(port, host) {
80292
+ return `http://${host}:${port}`;
80293
+ }
80294
+ function openBrowser(url) {
80295
+ try {
80296
+ if (process.platform === "linux") {
80297
+ spawn4("xdg-open", [url], { detached: true, stdio: "ignore" }).unref();
80298
+ } else if (process.platform === "darwin") {
80299
+ spawn4("open", [url], { detached: true, stdio: "ignore" }).unref();
80300
+ } else if (process.platform === "win32") {
80301
+ spawn4("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" }).unref();
80302
+ }
80303
+ } catch {
80304
+ }
80305
+ }
79923
80306
  var startCommand9, stopCommand6, restartCommand, statusCommand19, openCommand3, webCommand;
79924
80307
  var init_web = __esm({
79925
80308
  "packages/cleo/src/cli/commands/web.ts"() {
79926
80309
  "use strict";
79927
80310
  init_src2();
79928
80311
  init_dist();
80312
+ init_gateway_auto_start();
79929
80313
  init_renderers();
79930
80314
  init_web_subsystem();
79931
80315
  startCommand9 = defineCommand({
@@ -80081,17 +80465,7 @@ var init_web = __esm({
80081
80465
  );
80082
80466
  }
80083
80467
  const url = status.url;
80084
- const platform = process.platform;
80085
- try {
80086
- if (platform === "linux") {
80087
- spawn4("xdg-open", [url], { detached: true, stdio: "ignore" }).unref();
80088
- } else if (platform === "darwin") {
80089
- spawn4("open", [url], { detached: true, stdio: "ignore" }).unref();
80090
- } else if (platform === "win32") {
80091
- spawn4("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" }).unref();
80092
- }
80093
- } catch {
80094
- }
80468
+ openBrowser(url);
80095
80469
  cliOutput({ url }, { command: "web", message: `Open browser to: ${url}` });
80096
80470
  } catch (err) {
80097
80471
  if (err instanceof CleoError13) {
@@ -80103,7 +80477,17 @@ var init_web = __esm({
80103
80477
  }
80104
80478
  });
80105
80479
  webCommand = defineCommand({
80106
- meta: { name: "web", description: "Manage CLEO Web UI server" },
80480
+ meta: { name: "web", description: "Open CLEO Studio in the browser (starts gateway on demand)" },
80481
+ args: {
80482
+ port: {
80483
+ type: "string",
80484
+ description: `Gateway port (default ${GATEWAY_DEFAULT_PORT})`
80485
+ },
80486
+ host: {
80487
+ type: "string",
80488
+ description: `Gateway host (default ${GATEWAY_DEFAULT_HOST})`
80489
+ }
80490
+ },
80107
80491
  subCommands: {
80108
80492
  start: startCommand9,
80109
80493
  stop: stopCommand6,
@@ -80111,10 +80495,34 @@ var init_web = __esm({
80111
80495
  status: statusCommand19,
80112
80496
  open: openCommand3
80113
80497
  },
80114
- async run({ cmd, rawArgs }) {
80498
+ async run({ cmd, rawArgs, args }) {
80115
80499
  const firstArg = rawArgs?.find((a) => !a.startsWith("-"));
80116
80500
  if (firstArg && cmd.subCommands && firstArg in cmd.subCommands) return;
80117
- await showUsage(cmd);
80501
+ const portArg = args.port;
80502
+ const hostArg = args.host;
80503
+ const port = portArg !== void 0 && portArg.length > 0 ? Number.parseInt(portArg, 10) : GATEWAY_DEFAULT_PORT;
80504
+ const host = hostArg ?? GATEWAY_DEFAULT_HOST;
80505
+ let autoStart = true;
80506
+ try {
80507
+ const { getRawConfig } = await import("@cleocode/core");
80508
+ const cfg = await getRawConfig();
80509
+ autoStart = shouldAutoStartGateway(cfg);
80510
+ } catch {
80511
+ }
80512
+ const studioUrl = buildStudioUrl(port, host);
80513
+ if (autoStart) {
80514
+ await spawnGatewayIfDown({ port, host });
80515
+ }
80516
+ openBrowser(studioUrl);
80517
+ const urlNotice = `Opening Studio: ${studioUrl}
80518
+ `;
80519
+ process.stderr.write(urlNotice);
80520
+ const installNote = " Note: Studio static bundle ships in T11979 (parallel lane).\n If the page is blank, the bundle may not yet be deployed in this install.\n";
80521
+ process.stderr.write(installNote);
80522
+ cliOutput(
80523
+ { url: studioUrl, port, host, gatewayAutoStart: autoStart },
80524
+ { command: "web", message: `Studio URL: ${studioUrl}` }
80525
+ );
80118
80526
  }
80119
80527
  });
80120
80528
  }
@@ -80126,7 +80534,7 @@ __export(workgraph_exports, {
80126
80534
  workgraphCommand: () => workgraphCommand
80127
80535
  });
80128
80536
  import { readFileSync as readFileSync19 } from "node:fs";
80129
- import { resolve as resolve11 } from "node:path";
80537
+ import { resolve as resolve12 } from "node:path";
80130
80538
  var validateCommand10, applyCommand, planCommand4, structureCommand, statusCommand20, workgraphCommand;
80131
80539
  var init_workgraph2 = __esm({
80132
80540
  "packages/cleo/src/cli/commands/workgraph.ts"() {
@@ -80147,7 +80555,7 @@ var init_workgraph2 = __esm({
80147
80555
  async run({ args }) {
80148
80556
  const { validateWorkGraphScaffold } = await import("@cleocode/core/workgraph");
80149
80557
  const { cliOutput: cliOutput2 } = await Promise.resolve().then(() => (init_renderers(), renderers_exports));
80150
- const filePath = resolve11(String(args.file));
80558
+ const filePath = resolve12(String(args.file));
80151
80559
  let raw;
80152
80560
  try {
80153
80561
  raw = readFileSync19(filePath, "utf-8");
@@ -80192,7 +80600,7 @@ var init_workgraph2 = __esm({
80192
80600
  async run({ args }) {
80193
80601
  const { applyWorkGraphScaffold } = await import("@cleocode/core/workgraph");
80194
80602
  const { cliOutput: cliOutput2 } = await Promise.resolve().then(() => (init_renderers(), renderers_exports));
80195
- const filePath = resolve11(String(args.file));
80603
+ const filePath = resolve12(String(args.file));
80196
80604
  let raw;
80197
80605
  try {
80198
80606
  raw = readFileSync19(filePath, "utf-8");
@@ -80347,12 +80755,12 @@ __export(worktree_exports, {
80347
80755
  import readline4 from "node:readline";
80348
80756
  import { getProjectRoot as getProjectRoot61, listWorktrees as listWorktrees2 } from "@cleocode/core/internal";
80349
80757
  async function promptYesNo2(question) {
80350
- return new Promise((resolve12) => {
80758
+ return new Promise((resolve13) => {
80351
80759
  const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
80352
80760
  rl.question(`${question} [y/N]: `, (answer) => {
80353
80761
  rl.close();
80354
80762
  const trimmed = answer.trim().toLowerCase();
80355
- resolve12(trimmed === "y" || trimmed === "yes");
80763
+ resolve13(trimmed === "y" || trimmed === "yes");
80356
80764
  });
80357
80765
  });
80358
80766
  }
@@ -80693,8 +81101,8 @@ init_describe_context();
80693
81101
  init_field_context();
80694
81102
  init_format_context();
80695
81103
  import { readFileSync as readFileSync20 } from "node:fs";
80696
- import { dirname as dirname11, join as join37 } from "node:path";
80697
- import { fileURLToPath as fileURLToPath6 } from "node:url";
81104
+ import { dirname as dirname13, join as join38 } from "node:path";
81105
+ import { fileURLToPath as fileURLToPath8 } from "node:url";
80698
81106
  import { enforceNodeVersion } from "@cleocode/paths";
80699
81107
 
80700
81108
  // packages/cleo/src/cli/generated/command-manifest.ts
@@ -81638,7 +82046,7 @@ var COMMAND_MANIFEST = [
81638
82046
  {
81639
82047
  exportName: "webCommand",
81640
82048
  name: "web",
81641
- description: "Manage CLEO Web UI server",
82049
+ description: "Open CLEO Studio in the browser (starts gateway on demand)",
81642
82050
  load: async () => (await Promise.resolve().then(() => (init_web(), web_exports))).webCommand
81643
82051
  },
81644
82052
  {
@@ -81819,14 +82227,14 @@ function lazyCommand(meta, loader2) {
81819
82227
  init_did_you_mean();
81820
82228
 
81821
82229
  // packages/cleo/src/cli/lib/first-run-detection.ts
81822
- import { existsSync as existsSync21 } from "node:fs";
81823
- import { join as join36 } from "node:path";
82230
+ import { existsSync as existsSync22 } from "node:fs";
82231
+ import { join as join37 } from "node:path";
81824
82232
  async function detectFirstRun() {
81825
82233
  const envKey = process.env["ANTHROPIC_API_KEY"];
81826
82234
  if (typeof envKey === "string" && envKey.length > 0) return false;
81827
82235
  const { getCleoPlatformPaths } = await import("@cleocode/paths");
81828
- const configPath = join36(getCleoPlatformPaths().config, "config.json");
81829
- if (existsSync21(configPath)) return false;
82236
+ const configPath = join37(getCleoPlatformPaths().config, "config.json");
82237
+ if (existsSync22(configPath)) return false;
81830
82238
  try {
81831
82239
  const { getCredentialPool: getCredentialPool2 } = await import("@cleocode/core/llm/credential-pool.js");
81832
82240
  const pool = getCredentialPool2();
@@ -81837,7 +82245,7 @@ async function detectFirstRun() {
81837
82245
  return true;
81838
82246
  }
81839
82247
  function waitForEnterOrTimeout(timeoutMs) {
81840
- return new Promise((resolve12) => {
82248
+ return new Promise((resolve13) => {
81841
82249
  let resolved = false;
81842
82250
  const finish = () => {
81843
82251
  if (resolved) return;
@@ -81849,7 +82257,7 @@ function waitForEnterOrTimeout(timeoutMs) {
81849
82257
  process.stdin.pause();
81850
82258
  } catch {
81851
82259
  }
81852
- resolve12();
82260
+ resolve13();
81853
82261
  };
81854
82262
  const onData = (chunk) => {
81855
82263
  const s = typeof chunk === "string" ? chunk : chunk.toString("utf8");
@@ -81947,7 +82355,7 @@ async function resolveSubCommandForHelp(cmd, rawArgs) {
81947
82355
  init_summary_context();
81948
82356
  enforceNodeVersion();
81949
82357
  function getPackageVersion() {
81950
- const pkgPath = join37(dirname11(fileURLToPath6(import.meta.url)), "../../package.json");
82358
+ const pkgPath = join38(dirname13(fileURLToPath8(import.meta.url)), "../../package.json");
81951
82359
  const pkg = JSON.parse(readFileSync20(pkgPath, "utf-8"));
81952
82360
  return pkg.version;
81953
82361
  }
@@ -82030,6 +82438,19 @@ Valid modes: ${validModes.join(", ")}
82030
82438
  }
82031
82439
  setSummaryMode(summaryFlag);
82032
82440
  setDescribeMode(describeFlag);
82441
+ if (argv.length === 0 && process.stdout.isTTY === true) {
82442
+ const { runCockpit: runCockpit2 } = await Promise.resolve().then(() => (init_cockpit(), cockpit_exports));
82443
+ let autoStart = true;
82444
+ try {
82445
+ const { getRawConfig } = await import("@cleocode/core");
82446
+ const cfg = await getRawConfig();
82447
+ const { shouldAutoStartGateway: shouldAutoStartGateway2 } = await Promise.resolve().then(() => (init_gateway_auto_start(), gateway_auto_start_exports));
82448
+ autoStart = shouldAutoStartGateway2(cfg);
82449
+ } catch {
82450
+ }
82451
+ await runCockpit2({ autoStart });
82452
+ return;
82453
+ }
82033
82454
  const isHelpOrVersion = argv.length === 0 || argv[0] === "--help" || argv[0] === "-h" || argv[0] === "--version" || argv[0] === "-V" || argv[0] === "help";
82034
82455
  if (argv[0] === "--version" || argv[0] === "-V") {
82035
82456
  const { cliOutput: cliOutput2 } = await Promise.resolve().then(() => (init_renderers(), renderers_exports));