@cleocode/cleo 2026.9.23 → 2026.9.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/cli/index.js +1385 -347
  2. package/package.json +13 -13
  3. package/studio-dist/client/_app/immutable/chunks/{VqMa-FR_.js → BeSHW-PI.js} +1 -1
  4. package/studio-dist/client/_app/immutable/chunks/BmmibeAn.js +1 -0
  5. package/studio-dist/client/_app/immutable/chunks/{DJ4ceMnt.js → CqxUYDwj.js} +1 -1
  6. package/studio-dist/client/_app/immutable/chunks/{DlnXUEcN.js → iXXfYtUd.js} +1 -1
  7. package/studio-dist/client/_app/immutable/entry/{app.CXLixB0X.js → app.D0MhB1gq.js} +2 -2
  8. package/studio-dist/client/_app/immutable/entry/start.Z2uJ5V0K.js +1 -0
  9. package/studio-dist/client/_app/immutable/nodes/{0.D34GfsBr.js → 0.CPm_3d_8.js} +1 -1
  10. package/studio-dist/client/_app/immutable/nodes/{1.COzO-K-Z.js → 1.kD8K_ebT.js} +1 -1
  11. package/studio-dist/client/_app/immutable/nodes/{15.CkLwS1y2.js → 15.WEaAYVEy.js} +1 -1
  12. package/studio-dist/client/_app/immutable/nodes/{16.C2MFh329.js → 16.SxplBCYx.js} +1 -1
  13. package/studio-dist/client/_app/immutable/nodes/{17.CLdLJ6ju.js → 17.HawOwgFr.js} +1 -1
  14. package/studio-dist/client/_app/immutable/nodes/{18.ClGsX0an.js → 18.CU1uc6aV.js} +1 -1
  15. package/studio-dist/client/_app/immutable/nodes/{20.vQtazQa0.js → 20.Dj3In1mD.js} +1 -1
  16. package/studio-dist/client/_app/immutable/nodes/{24.DLwR4-7S.js → 24.BABzil92.js} +1 -1
  17. package/studio-dist/client/_app/immutable/nodes/{25.DUuYls4x.js → 25.K9V1t9FS.js} +2 -2
  18. package/studio-dist/client/_app/immutable/nodes/{26.BZ-Sb3tl.js → 26.Ber4zDmK.js} +1 -1
  19. package/studio-dist/client/_app/immutable/nodes/{3.CYwLXdQ4.js → 3.CDH1-JKn.js} +1 -1
  20. package/studio-dist/client/_app/version.json +1 -1
  21. package/studio-dist/server/chunks/{0-BlM3YDpC.js → 0-CzucydBz.js} +5 -4
  22. package/studio-dist/server/chunks/1-BgfY0ktP.js +9 -0
  23. package/studio-dist/server/chunks/{15-D4Y9me2S.js → 15-B36yiClS.js} +3 -3
  24. package/studio-dist/server/chunks/{16-BNVAfz5X.js → 16-TEN2Xs7g.js} +3 -3
  25. package/studio-dist/server/chunks/{17-ByJg8VAI.js → 17-CDqPwV0A.js} +3 -3
  26. package/studio-dist/server/chunks/{18-mcGKywiH.js → 18-C9SojWQi.js} +3 -3
  27. package/studio-dist/server/chunks/{20-D4RXMRdA.js → 20-D2rDiJCy.js} +5 -4
  28. package/studio-dist/server/chunks/{23-D2H9OSJf.js → 23-D194DGVi.js} +3 -2
  29. package/studio-dist/server/chunks/{24-BZesRs_l.js → 24-DFin4XEU.js} +5 -4
  30. package/studio-dist/server/chunks/{25-BPtkcHWk.js → 25-byalahiX.js} +4 -4
  31. package/studio-dist/server/chunks/{26-D7qW4CZ3.js → 26-BctSc53D.js} +3 -3
  32. package/studio-dist/server/chunks/{3-Csrn9tNa.js → 3-u3g5-V70.js} +5 -5
  33. package/studio-dist/server/chunks/{_layout.svelte-DtqTR50-.js → _layout.svelte-BkWa8zMq.js} +4 -4
  34. package/studio-dist/server/chunks/{_page.svelte-CyGNoYGs.js → _page.svelte-Balyz5No.js} +3 -3
  35. package/studio-dist/server/chunks/{_page.svelte-DESqb1iT.js → _page.svelte-CEoJA3np.js} +3 -3
  36. package/studio-dist/server/chunks/{_page.svelte-C9lhMoUX.js → _page.svelte-ClYQv-J6.js} +5 -5
  37. package/studio-dist/server/chunks/{_page.svelte-Dj05GrDh.js → _page.svelte-D1GgG_0J.js} +3 -3
  38. package/studio-dist/server/chunks/{_page.svelte-DTqKOclg.js → _page.svelte-D1ImKwNa.js} +3 -3
  39. package/studio-dist/server/chunks/{_page.svelte-BkrxW4NS.js → _page.svelte-DdRs2eQU.js} +3 -3
  40. package/studio-dist/server/chunks/{_page.svelte-Ce2_fPzl.js → _page.svelte-DdieDDqt.js} +4 -4
  41. package/studio-dist/server/chunks/{_page.svelte-BlDOqWK_.js → _page.svelte-Nf_2u2sG.js} +3 -3
  42. package/studio-dist/server/chunks/{_page.svelte-DxctlP-t.js → _page.svelte-SpNaTPY3.js} +3 -3
  43. package/studio-dist/server/chunks/{_server.ts-B23PRUg6.js → _server.ts-10Z9UtEP.js} +3 -3
  44. package/studio-dist/server/chunks/{_server.ts-BPzV5a4O.js → _server.ts-BF8dYynr.js} +3 -3
  45. package/studio-dist/server/chunks/{_server.ts-BtgcmXG8.js → _server.ts-BFAKIrzM.js} +2 -2
  46. package/studio-dist/server/chunks/{_server.ts-BLEs7rDo.js → _server.ts-BNaQ_u-J.js} +3 -3
  47. package/studio-dist/server/chunks/{_server.ts-BhoAYR43.js → _server.ts-CFMss8Ec.js} +3 -3
  48. package/studio-dist/server/chunks/{_server.ts-CyXruf03.js → _server.ts-CUDHRtqK.js} +2 -2
  49. package/studio-dist/server/chunks/{_server.ts-BCPpAOST.js → _server.ts-CVQlKiss.js} +3 -2
  50. package/studio-dist/server/chunks/{_server.ts-DUgUi5r_.js → _server.ts-CmOf90SL.js} +2 -2
  51. package/studio-dist/server/chunks/{_server.ts-D7JwQTun.js → _server.ts-DRFQht0c.js} +3 -2
  52. package/studio-dist/server/chunks/{_server.ts-D64WqH4h.js → _server.ts-DfzvMeMr.js} +3 -3
  53. package/studio-dist/server/chunks/{_server.ts-D9JYb9Nh.js → _server.ts-DrRXPeYo.js} +3 -3
  54. package/studio-dist/server/chunks/{_server.ts-CjJYkLJH.js → _server.ts-YrmdROs2.js} +3 -2
  55. package/studio-dist/server/chunks/{_server.ts-ChoGyX5C.js → _server.ts-ZCA_Uz1x.js} +3 -2
  56. package/studio-dist/server/chunks/{_server.ts-5euivmMj.js → _server.ts-mGj6h26U.js} +3 -2
  57. package/studio-dist/server/chunks/{brain-DLXOLooR.js → brain-4-tWVd6k.js} +2 -2
  58. package/studio-dist/server/chunks/{client-D4Rhd5ed.js → client-NfsjQdjU.js} +2 -2
  59. package/studio-dist/server/chunks/{dist-DafBXQH9.js → dist-b2dqLZbk.js} +178 -23
  60. package/studio-dist/server/chunks/{error.svelte-BLd3ZRbJ.js → error.svelte-xf-NkEuU.js} +3 -3
  61. package/studio-dist/server/chunks/{hooks.server-CWBRp8NI.js → hooks.server-D2qbf3Zp.js} +3 -2
  62. package/studio-dist/server/chunks/{http-C-8-8Oq_.js → http-C8NM9yzE.js} +2 -2
  63. package/studio-dist/server/chunks/{internal-DxboZxyS.js → internal-OCcHamz2.js} +3 -3
  64. package/studio-dist/server/chunks/{project-context-BAXQmgch.js → project-context-CX-nfwL2.js} +7 -5
  65. package/studio-dist/server/chunks/{src-RK_h29uK.js → src-CeLZUrEE.js} +218 -24
  66. package/studio-dist/server/chunks/{stores-DFVE4LvP.js → stores-Dyo9kN1m.js} +2 -2
  67. package/studio-dist/server/index.js +1 -1
  68. package/studio-dist/server/manifest.js +27 -27
  69. package/studio-dist/client/_app/immutable/chunks/CyNL2FJl.js +0 -1
  70. package/studio-dist/client/_app/immutable/entry/start.C_VLyotS.js +0 -1
  71. package/studio-dist/server/chunks/1-DROdkBtZ.js +0 -9
package/dist/cli/index.js CHANGED
@@ -2668,11 +2668,16 @@ function provisionIsolatedShell(options) {
2668
2668
  };
2669
2669
  const exportBlock = ISOLATION_ENV_KEYS.filter((k) => env[k] !== "").map((k) => `export ${k}=${shellQuote(env[k])}`).join("\n");
2670
2670
  const quotedPath = shellQuote(worktreePath);
2671
+ const identityExports = IDENTITY_ENV_KEYS.filter((k) => env[k] !== "").map(
2672
+ (k) => `${k}=${shellQuote(env[k])}`
2673
+ );
2674
+ const perCallLine = identityExports.length > 0 ? `cd ${quotedPath} && export ${identityExports.join(" ")} || exit 1` : `cd ${quotedPath} || exit 1`;
2671
2675
  const preamble = [
2672
2676
  "## Worktree Isolation (REQUIRED \u2014 do not skip)",
2673
2677
  "",
2674
- "# Step 1: Enter the worktree (exits immediately if path is missing)",
2675
- `cd ${quotedPath} || exit 1`,
2678
+ "# Step 1: Enter the worktree and bind your session (exits if the path is missing).",
2679
+ "# Repeat this line at the start of EVERY Bash call: cwd and env do not persist.",
2680
+ perCallLine,
2676
2681
  "",
2677
2682
  "# Step 2: Verify working directory (guards against shell/symlink quirks)",
2678
2683
  'case "$PWD" in',
@@ -2698,7 +2703,7 @@ function provisionIsolatedShell(options) {
2698
2703
  envKeys: ISOLATION_ENV_KEYS,
2699
2704
  absolutePathRules
2700
2705
  };
2701
- return { cwd, env, preamble, boundaryContract };
2706
+ return { cwd, env, preamble, perCallLine, boundaryContract };
2702
2707
  }
2703
2708
  function validateAbsolutePath(absolutePath, contract) {
2704
2709
  const { absolutePathRules } = contract;
@@ -2718,7 +2723,7 @@ function validateAbsolutePath(absolutePath, contract) {
2718
2723
  message: `E_BOUNDARY_VIOLATION: absolute path "${absolutePath}" is outside the permitted boundary. worktreeRoot="${contract.worktreeRoot}", allowedPrefixes=[${absolutePathRules.allowedPrefixes.map((p) => `"${p}"`).join(", ")}]. Use a path within the worktree or update AbsolutePathRules.allowedPrefixes.`
2719
2724
  };
2720
2725
  }
2721
- var BRANCH_LOCK_ERROR_CODES, ISOLATION_ENV_KEYS;
2726
+ var BRANCH_LOCK_ERROR_CODES, ISOLATION_ENV_KEYS, IDENTITY_ENV_KEYS;
2722
2727
  var init_branch_lock = __esm({
2723
2728
  "packages/contracts/src/branch-lock.ts"() {
2724
2729
  "use strict";
@@ -2787,6 +2792,11 @@ var init_branch_lock = __esm({
2787
2792
  "CLEO_SESSION_ID",
2788
2793
  "CLEO_AGENT_ID"
2789
2794
  ];
2795
+ IDENTITY_ENV_KEYS = [
2796
+ "CLEO_SESSION_ID",
2797
+ "CLEO_AGENT_ID",
2798
+ "CLEO_AGENT_ROLE"
2799
+ ];
2790
2800
  }
2791
2801
  });
2792
2802
 
@@ -4915,16 +4925,30 @@ var init_service = __esm({
4915
4925
  });
4916
4926
 
4917
4927
  // packages/contracts/src/operations/output-contracts-data.ts
4918
- var tasksShowOutputContract, taskPopulationSchema, tasksListOutputContract, tasksFindOutputContract, TASK_MUTATION_DATA_SCHEMA, tasksAddOutputContract, tasksAddBatchOutputContract, tasksUpdateOutputContract, tasksCompleteOutputContract, tasksReorderRankOutputContract, tasksBulkMoveOutputContract, tasksAssigneeOutputContract, adminConfigGetOutputContract, adminConfigListOutputContract, adminConfigValidateOutputContract, adminConfigUnsetOutputContract, OUTPUT_CONTRACTS;
4928
+ var TASK_CLAIM_SCHEMA, tasksShowOutputContract, taskPopulationSchema, tasksListOutputContract, tasksFindOutputContract, TASK_MUTATION_DATA_SCHEMA, tasksAddOutputContract, tasksAddBatchOutputContract, tasksUpdateOutputContract, tasksCompleteOutputContract, tasksReorderRankOutputContract, tasksBulkMoveOutputContract, tasksAssigneeOutputContract, tasksStartOutputContract, tasksClaimOutputContract, tasksUnclaimOutputContract, adminConfigGetOutputContract, adminConfigListOutputContract, adminConfigValidateOutputContract, adminConfigUnsetOutputContract, OUTPUT_CONTRACTS;
4919
4929
  var init_output_contracts_data = __esm({
4920
4930
  "packages/contracts/src/operations/output-contracts-data.ts"() {
4921
4931
  "use strict";
4922
4932
  init_check();
4923
4933
  init_entities();
4924
4934
  init_service();
4935
+ TASK_CLAIM_SCHEMA = {
4936
+ type: "object",
4937
+ description: "Agent claim lease (T12502): the holder session, its agent, and when the lease lapses unless renewed. Absent when unclaimed.",
4938
+ required: ["sessionId", "agentId", "claimedAt", "leaseExpiresAt"],
4939
+ properties: {
4940
+ sessionId: { type: "string", description: "Session holding the lease." },
4941
+ agentId: { type: ["string", "null"], description: "Agent identity of the holder." },
4942
+ claimedAt: { type: "string", description: "When the lease was taken (ISO-8601 UTC)." },
4943
+ leaseExpiresAt: {
4944
+ type: "string",
4945
+ description: "When the lease lapses unless renewed (ISO-8601 UTC)."
4946
+ }
4947
+ }
4948
+ };
4925
4949
  tasksShowOutputContract = {
4926
4950
  operation: "tasks.show",
4927
- shapeNote: "The task record is nested under `task` \u2014 use /data/task/<field>, not /data/<field>. Mutation envelopes are FLAT by contrast (/data/created/0, /data/updated/0), which is the asymmetry that produced GH #1225/#1239/#1231. `view` may be null. `acRows` and `relations` are conditional. `description`, `acceptance` and `verification` are withheld by the default (MVI) projection but `--field` resolves them transparently (T12108) \u2014 no `--full` needed. Evidence is NOT a task field: it lives at /data/task/verification/evidence (keyed by gate), alongside /data/task/verification/gates. T12127 (GH #1231) \u2014 the previous wording advertised `/data/task/evidence`, which does not exist, in the very text shown to an agent whose pointer had just failed.",
4951
+ shapeNote: "The task record is nested under `task` \u2014 use /data/task/<field>, not /data/<field>. Mutation envelopes are FLAT by contrast (/data/created/0, /data/updated/0), which is the asymmetry that produced GH #1225/#1239/#1231. `view` may be null. `acRows` and `relations` are conditional. The default (MVI) projection withholds each `acRows` UUID and names it once in `/data/_withheld` as `acRows/*/id` (T12523); cite a criterion by `alias` (AC1..n). `description`, `acceptance` and `verification` are withheld by the default (MVI) projection but `--field` resolves them transparently (T12108) \u2014 no `--full` needed. Evidence is NOT a task field: it lives at /data/task/verification/evidence (keyed by gate), alongside /data/task/verification/gates. T12127 (GH #1231) \u2014 the previous wording advertised `/data/task/evidence`, which does not exist, in the very text shown to an agent whose pointer had just failed.",
4928
4952
  dataSchema: {
4929
4953
  type: "object",
4930
4954
  required: ["task", "view", "attachments"],
@@ -4940,7 +4964,18 @@ var init_output_contracts_data = __esm({
4940
4964
  status: { type: "string" },
4941
4965
  priority: { type: "string" },
4942
4966
  type: { type: "string" },
4943
- parentId: { type: ["string", "null"] }
4967
+ parentId: { type: ["string", "null"] },
4968
+ // T12503: the optimistic-concurrency version agents pass to --if-match.
4969
+ updatedAt: {
4970
+ type: ["string", "null"],
4971
+ description: "Task version for optimistic concurrency: pass it to `cleo update|complete --if-match`."
4972
+ },
4973
+ // T12502: the human assignee and the agent claim lease are separate.
4974
+ assignee: {
4975
+ type: ["string", "null"],
4976
+ description: "Human / owner assignee. Separate from `claim`."
4977
+ },
4978
+ claim: TASK_CLAIM_SCHEMA
4944
4979
  }
4945
4980
  },
4946
4981
  view: {
@@ -4953,7 +4988,11 @@ var init_output_contracts_data = __esm({
4953
4988
  },
4954
4989
  acRows: {
4955
4990
  type: "array",
4956
- description: "Acceptance-criterion rows (id, alias AC<n>, ordinal, text). Optional."
4991
+ description: "Acceptance-criterion rows (alias AC<n>, ordinal, text; the UUID id only with --full). Optional."
4992
+ },
4993
+ _withheld: {
4994
+ type: "object",
4995
+ description: "Fields the default projection withheld from list elements, e.g. `acRows/*/id` \u2192 UTF-8 byte total. Absent with --full."
4957
4996
  },
4958
4997
  relations: {
4959
4998
  type: "object",
@@ -4968,6 +5007,14 @@ var init_output_contracts_data = __esm({
4968
5007
  "/data/task/priority",
4969
5008
  "/data/task/type",
4970
5009
  "/data/task/parentId",
5010
+ // T12503 — the optimistic-concurrency version for `--if-match`; withheld
5011
+ // by the MVI projection, resolved from the full record by `--field`.
5012
+ "/data/task/updatedAt",
5013
+ // T12502 — the agent claim lease (withheld by MVI; resolved by --field).
5014
+ "/data/task/assignee",
5015
+ "/data/task/claim",
5016
+ "/data/task/claim/sessionId",
5017
+ "/data/task/claim/leaseExpiresAt",
4971
5018
  // T12127 (GH #1231) — withheld by the MVI projection but resolvable
4972
5019
  // through `--field` since T12108. They were absent from this list, so the
4973
5020
  // remediation an agent is shown on a failed pointer never mentioned the
@@ -5209,6 +5256,56 @@ var init_output_contracts_data = __esm({
5209
5256
  },
5210
5257
  fieldPointers: ["/data/taskId", "/data/assignee", "/data/assigned"]
5211
5258
  };
5259
+ tasksStartOutputContract = {
5260
+ operation: "tasks.start",
5261
+ shapeNote: "The claim lease this start holds is at /data/claim (null when the caller is not bound to a session). Another session holding the task fails with E_TASK_CLAIMED (exit 35); error.details.holder names it.",
5262
+ dataSchema: {
5263
+ type: "object",
5264
+ required: ["taskId", "previousTask"],
5265
+ additionalProperties: true,
5266
+ properties: {
5267
+ taskId: { type: "string", description: "The task now active." },
5268
+ previousTask: { type: ["string", "null"], description: "The task active before, if any." },
5269
+ claim: { ...TASK_CLAIM_SCHEMA, type: ["object", "null"] }
5270
+ }
5271
+ },
5272
+ fieldPointers: [
5273
+ "/data/taskId",
5274
+ "/data/previousTask",
5275
+ "/data/claim",
5276
+ "/data/claim/sessionId",
5277
+ "/data/claim/leaseExpiresAt"
5278
+ ]
5279
+ };
5280
+ tasksClaimOutputContract = {
5281
+ operation: "tasks.claim",
5282
+ shapeNote: "The lease now held by your session is at /data/claim; renew it with --renew. Another holder fails with E_TASK_CLAIMED (exit 35): --take-over (expired) or --force-claim (live).",
5283
+ dataSchema: {
5284
+ type: "object",
5285
+ required: ["taskId", "agentId", "claim"],
5286
+ additionalProperties: true,
5287
+ properties: {
5288
+ taskId: { type: "string", description: "The claimed task." },
5289
+ agentId: { type: ["string", "null"], description: "Agent recorded with the lease." },
5290
+ claim: TASK_CLAIM_SCHEMA
5291
+ }
5292
+ },
5293
+ fieldPointers: ["/data/taskId", "/data/agentId", "/data/claim", "/data/claim/leaseExpiresAt"]
5294
+ };
5295
+ tasksUnclaimOutputContract = {
5296
+ operation: "tasks.unclaim",
5297
+ shapeNote: "/data/released is true when a lease was cleared, false when the task was unclaimed.",
5298
+ dataSchema: {
5299
+ type: "object",
5300
+ required: ["taskId", "released"],
5301
+ additionalProperties: true,
5302
+ properties: {
5303
+ taskId: { type: "string", description: "The released task." },
5304
+ released: { type: "boolean", description: "Whether a lease was cleared." }
5305
+ }
5306
+ },
5307
+ fieldPointers: ["/data/taskId", "/data/released"]
5308
+ };
5212
5309
  adminConfigGetOutputContract = {
5213
5310
  operation: "admin.config.get",
5214
5311
  shapeNote: "The resolved value is at /data/value (may be null). /data/found is false when the key is absent.",
@@ -5315,6 +5412,10 @@ var init_output_contracts_data = __esm({
5315
5412
  "tasks.reorder-rank": tasksReorderRankOutputContract,
5316
5413
  "tasks.bulk-move": tasksBulkMoveOutputContract,
5317
5414
  "tasks.assignee": tasksAssigneeOutputContract,
5415
+ // T12502 — leased task claims.
5416
+ "tasks.start": tasksStartOutputContract,
5417
+ "tasks.claim": tasksClaimOutputContract,
5418
+ "tasks.unclaim": tasksUnclaimOutputContract,
5318
5419
  "admin.config.get": adminConfigGetOutputContract,
5319
5420
  "admin.config.list": adminConfigListOutputContract,
5320
5421
  "admin.config.validate": adminConfigValidateOutputContract,
@@ -8543,6 +8644,13 @@ var init_operations_registry = __esm({
8543
8644
  required: true,
8544
8645
  description: "taskId parameter",
8545
8646
  cli: { positional: true }
8647
+ },
8648
+ {
8649
+ name: "expectedUpdatedAt",
8650
+ type: "string",
8651
+ required: false,
8652
+ description: "Optimistic-concurrency guard: fail with E_CONFLICT unless the task updatedAt still equals this value",
8653
+ cli: { flag: "if-match" }
8546
8654
  }
8547
8655
  ]
8548
8656
  },
@@ -9203,7 +9311,7 @@ var init_operations_registry = __esm({
9203
9311
  gateway: "mutate",
9204
9312
  domain: "tasks",
9205
9313
  operation: "start",
9206
- description: "tasks.start (mutate)",
9314
+ description: "tasks.start (mutate) \u2014 start work and take the caller session's leased claim (E_TASK_CLAIMED when another session holds it)",
9207
9315
  tier: 0,
9208
9316
  idempotent: false,
9209
9317
  sessionRequired: false,
@@ -9215,6 +9323,20 @@ var init_operations_registry = __esm({
9215
9323
  required: true,
9216
9324
  description: "taskId parameter",
9217
9325
  cli: { positional: true }
9326
+ },
9327
+ {
9328
+ name: "takeOver",
9329
+ type: "boolean",
9330
+ required: false,
9331
+ description: "Take over another session's EXPIRED claim lease (audited)",
9332
+ cli: { flag: "take-over" }
9333
+ },
9334
+ {
9335
+ name: "forceClaim",
9336
+ type: "boolean",
9337
+ required: false,
9338
+ description: "Take over another session's LIVE claim lease (audited)",
9339
+ cli: { flag: "force-claim" }
9218
9340
  }
9219
9341
  ]
9220
9342
  },
@@ -9324,11 +9446,11 @@ var init_operations_registry = __esm({
9324
9446
  gateway: "mutate",
9325
9447
  domain: "tasks",
9326
9448
  operation: "claim",
9327
- description: "tasks.claim (mutate) \u2014 claim a task by assigning it to the current session",
9449
+ description: "tasks.claim (mutate) \u2014 take, renew or override the caller session's leased claim on a task (never the human assignee)",
9328
9450
  tier: 0,
9329
9451
  idempotent: false,
9330
9452
  sessionRequired: true,
9331
- requiredParams: ["taskId", "agentId"],
9453
+ requiredParams: ["taskId"],
9332
9454
  params: [
9333
9455
  {
9334
9456
  name: "taskId",
@@ -9339,8 +9461,26 @@ var init_operations_registry = __esm({
9339
9461
  {
9340
9462
  name: "agentId",
9341
9463
  type: "string",
9342
- required: true,
9343
- description: "Agent ID to assign the task to"
9464
+ required: false,
9465
+ description: "Agent ID recorded with the lease (default: the session's agent / CLEO_AGENT_ID)"
9466
+ },
9467
+ {
9468
+ name: "renew",
9469
+ type: "boolean",
9470
+ required: false,
9471
+ description: "Renew the caller's own lease"
9472
+ },
9473
+ {
9474
+ name: "takeOver",
9475
+ type: "boolean",
9476
+ required: false,
9477
+ description: "Take over another session's EXPIRED lease (audited)"
9478
+ },
9479
+ {
9480
+ name: "forceClaim",
9481
+ type: "boolean",
9482
+ required: false,
9483
+ description: "Take over another session's LIVE lease (audited)"
9344
9484
  }
9345
9485
  ]
9346
9486
  },
@@ -9348,7 +9488,7 @@ var init_operations_registry = __esm({
9348
9488
  gateway: "mutate",
9349
9489
  domain: "tasks",
9350
9490
  operation: "unclaim",
9351
- description: "tasks.unclaim (mutate) \u2014 unclaim a task by removing the current assignee",
9491
+ description: "tasks.unclaim (mutate) \u2014 release the caller session's claim lease (never the human assignee)",
9352
9492
  tier: 0,
9353
9493
  idempotent: false,
9354
9494
  sessionRequired: true,
@@ -9359,6 +9499,12 @@ var init_operations_registry = __esm({
9359
9499
  type: "string",
9360
9500
  required: true,
9361
9501
  description: "Task ID to unclaim"
9502
+ },
9503
+ {
9504
+ name: "forceClaim",
9505
+ type: "boolean",
9506
+ required: false,
9507
+ description: "Release another session's lease (audited)"
9362
9508
  }
9363
9509
  ]
9364
9510
  },
@@ -9613,6 +9759,20 @@ var init_operations_registry = __esm({
9613
9759
  type: "boolean",
9614
9760
  required: false,
9615
9761
  description: "When true (default), adds toTaskId as a dependency on fromTaskId so it cannot complete before the pivot resolves"
9762
+ },
9763
+ {
9764
+ name: "takeOver",
9765
+ type: "boolean",
9766
+ required: false,
9767
+ description: "Take over another session's EXPIRED claim on toTaskId (audited)",
9768
+ cli: { flag: "take-over" }
9769
+ },
9770
+ {
9771
+ name: "forceClaim",
9772
+ type: "boolean",
9773
+ required: false,
9774
+ description: "Take over another session's LIVE claim on toTaskId (audited)",
9775
+ cli: { flag: "force-claim" }
9616
9776
  }
9617
9777
  ]
9618
9778
  },
@@ -12251,6 +12411,78 @@ var init_operations_registry = __esm({
12251
12411
  requiredParams: [],
12252
12412
  params: []
12253
12413
  },
12414
+ {
12415
+ gateway: "query",
12416
+ domain: "nexus",
12417
+ operation: "projects.fleet",
12418
+ description: "nexus.projects.fleet (query) \u2014 fleet view: every project with its location on each device, last recorded git state (local, and remote as of the last fetch) and staleness; paged, counts first, read-only",
12419
+ tier: 1,
12420
+ idempotent: true,
12421
+ sessionRequired: false,
12422
+ requiredParams: [],
12423
+ params: [
12424
+ {
12425
+ name: "device",
12426
+ type: "string",
12427
+ required: false,
12428
+ description: "Only locations on this device (device id, hostname, or current)"
12429
+ },
12430
+ {
12431
+ name: "missing",
12432
+ type: "boolean",
12433
+ required: false,
12434
+ description: "Only projects with a missing location"
12435
+ },
12436
+ {
12437
+ name: "dirty",
12438
+ type: "boolean",
12439
+ required: false,
12440
+ description: "Only projects with a dirty location"
12441
+ },
12442
+ {
12443
+ name: "behind",
12444
+ type: "boolean",
12445
+ required: false,
12446
+ description: "Only projects with a location behind its upstream (as of the last fetch)"
12447
+ },
12448
+ {
12449
+ name: "ahead",
12450
+ type: "boolean",
12451
+ required: false,
12452
+ description: "Only projects with a location ahead of its upstream (as of the last fetch)"
12453
+ },
12454
+ {
12455
+ name: "stale",
12456
+ type: "boolean",
12457
+ required: false,
12458
+ description: "Only projects with a never-probed, old-probe or old-fetch location"
12459
+ },
12460
+ {
12461
+ name: "errored",
12462
+ type: "boolean",
12463
+ required: false,
12464
+ description: "Only projects whose last probe recorded an error"
12465
+ },
12466
+ {
12467
+ name: "staleAfterMs",
12468
+ type: "number",
12469
+ required: false,
12470
+ description: "Staleness window for probes, fetches and heartbeats (default 24h)"
12471
+ },
12472
+ {
12473
+ name: "limit",
12474
+ type: "number",
12475
+ required: false,
12476
+ description: "Page size in projects (default 50, max 500; 0 = all)"
12477
+ },
12478
+ {
12479
+ name: "offset",
12480
+ type: "number",
12481
+ required: false,
12482
+ description: "Projects skipped before the page"
12483
+ }
12484
+ ]
12485
+ },
12254
12486
  {
12255
12487
  gateway: "mutate",
12256
12488
  domain: "nexus",
@@ -19198,9 +19430,11 @@ var init_memory = __esm({
19198
19430
  });
19199
19431
 
19200
19432
  // packages/contracts/src/operations/nexus.ts
19433
+ var NEXUS_FLEET_SCHEMA_VERSION;
19201
19434
  var init_nexus = __esm({
19202
19435
  "packages/contracts/src/operations/nexus.ts"() {
19203
19436
  "use strict";
19437
+ NEXUS_FLEET_SCHEMA_VERSION = 1;
19204
19438
  }
19205
19439
  });
19206
19440
 
@@ -19454,6 +19688,7 @@ __export(operations_exports, {
19454
19688
  DOCS_PROJECTION_RECEIPT_SCHEMA: () => DOCS_PROJECTION_RECEIPT_SCHEMA,
19455
19689
  DOCS_TYPE_VALUES: () => DOCS_TYPE_VALUES,
19456
19690
  LLM_OUTPUT_MODES: () => LLM_OUTPUT_MODES,
19691
+ NEXUS_FLEET_SCHEMA_VERSION: () => NEXUS_FLEET_SCHEMA_VERSION,
19457
19692
  RELEASE_SHARED_COMMAND_SURFACES: () => RELEASE_SHARED_COMMAND_SURFACES,
19458
19693
  RELEASE_SHARED_SURFACE_CONTRACT: () => RELEASE_SHARED_SURFACE_CONTRACT,
19459
19694
  RELEASE_SHARED_TEMPLATE_SURFACES: () => RELEASE_SHARED_TEMPLATE_SURFACES,
@@ -19955,6 +20190,14 @@ var init_nexus_scope_map = __esm({
19955
20190
  stores: ["nexus-registry"],
19956
20191
  requiresProject: false
19957
20192
  },
20193
+ "projects.fleet": {
20194
+ op: "projects.fleet",
20195
+ description: "Fleet view: every project, where it lives on each device, and its last recorded git state (read-only).",
20196
+ scope: "global",
20197
+ effect: "read",
20198
+ stores: ["nexus-registry"],
20199
+ requiresProject: false
20200
+ },
19958
20201
  "projects.register": {
19959
20202
  op: "projects.register",
19960
20203
  description: "Register a project (alias: register).",
@@ -20461,7 +20704,14 @@ var init_session2 = __esm({
20461
20704
  get length() {
20462
20705
  return this._sessions.length;
20463
20706
  }
20464
- /** Find the currently active session (if any). */
20707
+ /**
20708
+ * First `active` session in THIS collection — a scan over the rows the
20709
+ * caller loaded, NOT the caller's own session (T12500). With several agents
20710
+ * active it names an arbitrary one of them.
20711
+ *
20712
+ * @deprecated For "my session" use `resolveBoundSession` (mutations) or
20713
+ * `resolveSessionForRead` (reads, labels an unbound guess).
20714
+ */
20465
20715
  findActive() {
20466
20716
  return this._sessions.find((s) => s.status === "active");
20467
20717
  }
@@ -22318,7 +22568,7 @@ var init_collection_keys = __esm({
22318
22568
 
22319
22569
  // packages/contracts/src/decide.ts
22320
22570
  import { z as z24 } from "zod";
22321
- var DECISION_QUESTION_TYPES, DECISION_OUTCOME_SOURCES, DECISION_REQUEST_LIMITS, DECISION_PROVIDER_KINDS, LAYAHOST_BASE_URL, LAYAHOST_DEFAULT_MODEL, JEV_MINIMUM_CAPABILITIES, decisionQuestionTypeSchema, decisionOutcomeSourceSchema, decisionInstructionsSchema, unitIntervalSchema, decisionStateSchema, noulDecisionQuestionSchema, choiceDecisionQuestionSchema, scoreDecisionQuestionSchema, decisionQuestionSchema, decisionRequestSchema, noulDecisionAnswerSchema, choiceDecisionAnswerSchema, scoreDecisionAnswerSchema, decisionAnswerSchema, decisionOutcomeSchema, decisionProviderKindSchema, decisionProviderConfigSchema;
22571
+ var DECISION_QUESTION_TYPES, DECISION_OUTCOME_SOURCES, DECISION_REQUEST_LIMITS, DECISION_PROVIDER_KINDS, LAYAHOST_BASE_URL, JEV_DEFAULT_BASE_URL, LAYAHOST_DEFAULT_MODEL, DECIDE_PROFILE_NAME_PATTERN, DECIDE_DEFAULT_PROFILE_NAME, DECIDE_PROFILE_DEFAULT_URL, JEV_MINIMUM_CAPABILITIES, decisionQuestionTypeSchema, decisionOutcomeSourceSchema, decisionInstructionsSchema, unitIntervalSchema, decisionStateSchema, noulDecisionQuestionSchema, choiceDecisionQuestionSchema, scoreDecisionQuestionSchema, decisionQuestionSchema, decisionRequestSchema, noulDecisionAnswerSchema, choiceDecisionAnswerSchema, scoreDecisionAnswerSchema, decisionAnswerSchema, decisionOutcomeSchema, decisionProviderKindSchema, decisionProviderConfigSchema;
22322
22572
  var init_decide = __esm({
22323
22573
  "packages/contracts/src/decide.ts"() {
22324
22574
  "use strict";
@@ -22344,7 +22594,11 @@ var init_decide = __esm({
22344
22594
  };
22345
22595
  DECISION_PROVIDER_KINDS = ["layahost", "jev"];
22346
22596
  LAYAHOST_BASE_URL = "https://layahost.com";
22597
+ JEV_DEFAULT_BASE_URL = "https://api.typesafe.ai";
22347
22598
  LAYAHOST_DEFAULT_MODEL = "laya-auto";
22599
+ DECIDE_PROFILE_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$/;
22600
+ DECIDE_DEFAULT_PROFILE_NAME = "default";
22601
+ DECIDE_PROFILE_DEFAULT_URL = "default";
22348
22602
  JEV_MINIMUM_CAPABILITIES = {
22349
22603
  wire: "jev-systemone/1",
22350
22604
  maxQuestionsPerRequest: 32,
@@ -22453,6 +22707,49 @@ var init_decide_sites = __esm({
22453
22707
  }
22454
22708
  });
22455
22709
 
22710
+ // packages/contracts/src/nexus-account.ts
22711
+ import { z as z25 } from "zod";
22712
+ var NEXUS_DEFAULT_API_URL, NEXUS_CLI_CLIENT_ID, NEXUS_LOGIN_TARGET, NEXUS_ACCOUNT_ERROR_CODES, nexusAccountUserSchema, nexusAccountOrganizationSchema, nexusAccountMeSchema;
22713
+ var init_nexus_account = __esm({
22714
+ "packages/contracts/src/nexus-account.ts"() {
22715
+ "use strict";
22716
+ NEXUS_DEFAULT_API_URL = "https://api.cleocode.dev";
22717
+ NEXUS_CLI_CLIENT_ID = "cleo-cli";
22718
+ NEXUS_LOGIN_TARGET = "nexus";
22719
+ NEXUS_ACCOUNT_ERROR_CODES = [
22720
+ "E_NEXUS_INVALID_API_URL",
22721
+ "E_NEXUS_NOT_SIGNED_IN",
22722
+ "E_NEXUS_SESSION_EXPIRED",
22723
+ "E_NEXUS_DEVICE_CODE_START_FAILED",
22724
+ "E_NEXUS_UNTRUSTED_VERIFICATION_URI",
22725
+ "E_NEXUS_DEVICE_CODE_EXPIRED",
22726
+ "E_NEXUS_ACCESS_DENIED",
22727
+ "E_NEXUS_DEVICE_CODE_FAILED",
22728
+ "E_NEXUS_REVOKE_FAILED",
22729
+ "E_NEXUS_REQUEST_FAILED",
22730
+ "E_NEXUS_NOT_A_PROJECT",
22731
+ "E_NEXUS_INVALID_LABEL"
22732
+ ];
22733
+ nexusAccountUserSchema = z25.object({
22734
+ id: z25.string(),
22735
+ email: z25.string(),
22736
+ name: z25.string().nullable().optional()
22737
+ });
22738
+ nexusAccountOrganizationSchema = z25.object({
22739
+ id: z25.string(),
22740
+ name: z25.string(),
22741
+ slug: z25.string().nullable().optional(),
22742
+ role: z25.string().optional(),
22743
+ /** `true` for the personal organization created at sign-up. */
22744
+ personal: z25.boolean().optional()
22745
+ });
22746
+ nexusAccountMeSchema = z25.object({
22747
+ user: nexusAccountUserSchema,
22748
+ organizations: z25.array(nexusAccountOrganizationSchema).default([])
22749
+ });
22750
+ }
22751
+ });
22752
+
22456
22753
  // packages/contracts/src/task-id.ts
22457
22754
  function isTaskId(value) {
22458
22755
  return typeof value === "string" && TASK_ID_REGEX.test(value);
@@ -22531,6 +22828,9 @@ __export(src_exports, {
22531
22828
  ClassifierUnregisteredAgentError: () => ClassifierUnregisteredAgentError,
22532
22829
  DAEMON_IPC_PROTOCOL_VERSION: () => SUPERVISOR_IPC_PROTOCOL_VERSION,
22533
22830
  DB_INVENTORY: () => DB_INVENTORY,
22831
+ DECIDE_DEFAULT_PROFILE_NAME: () => DECIDE_DEFAULT_PROFILE_NAME,
22832
+ DECIDE_PROFILE_DEFAULT_URL: () => DECIDE_PROFILE_DEFAULT_URL,
22833
+ DECIDE_PROFILE_NAME_PATTERN: () => DECIDE_PROFILE_NAME_PATTERN,
22534
22834
  DECISION_OUTCOME_SOURCES: () => DECISION_OUTCOME_SOURCES,
22535
22835
  DECISION_PROVIDER_KINDS: () => DECISION_PROVIDER_KINDS,
22536
22836
  DECISION_QUESTION_TYPES: () => DECISION_QUESTION_TYPES,
@@ -22594,6 +22894,7 @@ __export(src_exports, {
22594
22894
  INVARIANTS_REGISTRY: () => INVARIANTS_REGISTRY,
22595
22895
  ISOLATION_ENV_KEYS: () => ISOLATION_ENV_KEYS,
22596
22896
  ImpactSchema: () => ImpactSchema,
22897
+ JEV_DEFAULT_BASE_URL: () => JEV_DEFAULT_BASE_URL,
22597
22898
  JEV_MINIMUM_CAPABILITIES: () => JEV_MINIMUM_CAPABILITIES,
22598
22899
  KIND_LIFECYCLE_REQUIREMENTS: () => KIND_LIFECYCLE_REQUIREMENTS,
22599
22900
  LAYAHOST_BASE_URL: () => LAYAHOST_BASE_URL,
@@ -22628,6 +22929,11 @@ __export(src_exports, {
22628
22929
  MIGRATION_ENUM_DRIFT_SAMPLE_LIMIT: () => MIGRATION_ENUM_DRIFT_SAMPLE_LIMIT,
22629
22930
  MonitorRequestSchema: () => MonitorRequestSchema,
22630
22931
  MonitorResponseSchema: () => MonitorResponseSchema,
22932
+ NEXUS_ACCOUNT_ERROR_CODES: () => NEXUS_ACCOUNT_ERROR_CODES,
22933
+ NEXUS_CLI_CLIENT_ID: () => NEXUS_CLI_CLIENT_ID,
22934
+ NEXUS_DEFAULT_API_URL: () => NEXUS_DEFAULT_API_URL,
22935
+ NEXUS_FLEET_SCHEMA_VERSION: () => NEXUS_FLEET_SCHEMA_VERSION,
22936
+ NEXUS_LOGIN_TARGET: () => NEXUS_LOGIN_TARGET,
22631
22937
  NEXUS_SCOPE_MAP: () => NEXUS_SCOPE_MAP,
22632
22938
  OPEN_SYSTEM_KEY_PREFIXES: () => OPEN_SYSTEM_KEY_PREFIXES,
22633
22939
  OPERATIONS: () => OPERATIONS,
@@ -22905,6 +23211,9 @@ __export(src_exports, {
22905
23211
  modelShowOutputContract: () => modelShowOutputContract,
22906
23212
  modelsCatalogRowInsertSchema: () => modelsCatalogRowInsertSchema,
22907
23213
  modelsCatalogRowSelectSchema: () => modelsCatalogRowSelectSchema,
23214
+ nexusAccountMeSchema: () => nexusAccountMeSchema,
23215
+ nexusAccountOrganizationSchema: () => nexusAccountOrganizationSchema,
23216
+ nexusAccountUserSchema: () => nexusAccountUserSchema,
22908
23217
  normalizeError: () => normalizeError,
22909
23218
  noteAtomSchema: () => noteAtomSchema,
22910
23219
  noulDecisionAnswerSchema: () => noulDecisionAnswerSchema,
@@ -23054,6 +23363,7 @@ var init_src2 = __esm({
23054
23363
  init_ensures_schema_registry();
23055
23364
  init_entities();
23056
23365
  init_operations();
23366
+ init_nexus();
23057
23367
  init_nexus_scope_map();
23058
23368
  init_nexus_user_profile();
23059
23369
  init_output_contracts_data();
@@ -23083,6 +23393,7 @@ var init_src2 = __esm({
23083
23393
  init_collection_keys();
23084
23394
  init_decide();
23085
23395
  init_decide_sites();
23396
+ init_nexus_account();
23086
23397
  init_task_id();
23087
23398
  }
23088
23399
  });
@@ -23597,6 +23908,10 @@ import {
23597
23908
  renderBrainQuality,
23598
23909
  renderBriefing,
23599
23910
  renderCurrent,
23911
+ renderDecideAsk,
23912
+ renderDecideConfig,
23913
+ renderDecideProfiles,
23914
+ renderDecideStatus,
23600
23915
  renderDoctor,
23601
23916
  renderGeneric,
23602
23917
  renderNext,
@@ -23619,6 +23934,7 @@ import {
23619
23934
  renderNexusImpactFull,
23620
23935
  renderNexusProjectsClean,
23621
23936
  renderNexusProjectsCleanPreview,
23937
+ renderNexusProjectsFleet,
23622
23938
  renderNexusProjectsList,
23623
23939
  renderNexusProjectsRegister,
23624
23940
  renderNexusProjectsRemove,
@@ -24037,6 +24353,11 @@ var init_renderers = __esm({
24037
24353
  // Nexus subcommand renderers (T1720)
24038
24354
  "nexus-status": renderNexusStatus,
24039
24355
  "nexus-setup": renderNexusSetup,
24356
+ // System One (T12733): human blocks for config / status / ask / profiles.
24357
+ "decide-config": renderDecideConfig,
24358
+ "decide-status": renderDecideStatus,
24359
+ "decide-ask": renderDecideAsk,
24360
+ "decide-profiles": renderDecideProfiles,
24040
24361
  "nexus-clusters": renderNexusClusters,
24041
24362
  "nexus-flows": renderNexusFlows,
24042
24363
  "nexus-context": renderNexusContextResult,
@@ -24047,6 +24368,7 @@ var init_renderers = __esm({
24047
24368
  "nexus-projects-remove": renderNexusProjectsRemove,
24048
24369
  "nexus-projects-scan": renderNexusProjectsScan,
24049
24370
  "nexus-projects-status": renderNexusProjectsStatus,
24371
+ "nexus-projects-fleet": renderNexusProjectsFleet,
24050
24372
  "nexus-projects-clean": renderNexusProjectsClean,
24051
24373
  "nexus-projects-clean-preview": renderNexusProjectsCleanPreview,
24052
24374
  "nexus-refresh-bridge": renderNexusRefreshBridge,
@@ -29746,7 +30068,7 @@ async function buildFocusEnvelope(id, projectRoot) {
29746
30068
  ...brainContext != null ? { brainContext } : {},
29747
30069
  blockers,
29748
30070
  ...readyWave != null ? { readyWave } : {},
29749
- knowledgeHealth: compactKnowledgeHealth(knowledge.health),
30071
+ knowledgeHealth: compactKnowledgeHealth(knowledge.health, "/coverage"),
29750
30072
  sourceDiagnostics,
29751
30073
  ...members != null ? { members } : {},
29752
30074
  ...attachedDocs != null ? { attachedDocs } : {},
@@ -32024,6 +32346,7 @@ import {
32024
32346
  nexusProfileUpsert,
32025
32347
  nexusProfileView,
32026
32348
  nexusProjectsClean,
32349
+ nexusProjectsFleet,
32027
32350
  nexusProjectsList,
32028
32351
  nexusProjectsRegister,
32029
32352
  nexusProjectsRemove,
@@ -32549,6 +32872,21 @@ var init_nexus2 = __esm({
32549
32872
  }),
32550
32873
  "projects.status"
32551
32874
  ),
32875
+ "projects.fleet": async (params) => wrapCoreResult(
32876
+ await nexusProjectsFleet(getProjectRoot9(), {
32877
+ device: typeof params.device === "string" ? params.device : void 0,
32878
+ missing: params.missing === true,
32879
+ dirty: params.dirty === true,
32880
+ behind: params.behind === true,
32881
+ ahead: params.ahead === true,
32882
+ stale: params.stale === true,
32883
+ errored: params.errored === true,
32884
+ staleAfterMs: typeof params.staleAfterMs === "number" ? params.staleAfterMs : void 0,
32885
+ limit: typeof params.limit === "number" ? params.limit : void 0,
32886
+ offset: typeof params.offset === "number" ? params.offset : void 0
32887
+ }),
32888
+ "projects.fleet"
32889
+ ),
32552
32890
  "refresh-bridge": async (params) => {
32553
32891
  const projectRoot = getProjectRoot9();
32554
32892
  const repoPath = params.repoPath ?? projectRoot;
@@ -32749,6 +33087,7 @@ var init_nexus2 = __esm({
32749
33087
  "flows",
32750
33088
  "context",
32751
33089
  "projects.list",
33090
+ "projects.fleet",
32752
33091
  "diff",
32753
33092
  "query-cte",
32754
33093
  "hot-paths",
@@ -38236,7 +38575,7 @@ var init_playbook2 = __esm({
38236
38575
  });
38237
38576
 
38238
38577
  // packages/cleo/src/dispatch/domains/orchestrate.ts
38239
- import { getLogger as getLogger8, getProjectRoot as getProjectRoot11, pivotTask } from "@cleocode/core/internal";
38578
+ import { CleoError, getLogger as getLogger8, getProjectRoot as getProjectRoot11, pivotTask } from "@cleocode/core/internal";
38240
38579
  import {
38241
38580
  CLEO_DIR_NAME as CLEO_DIR_NAME2,
38242
38581
  orchestrateAnalyze,
@@ -38397,7 +38736,9 @@ async function orchestratePivotOp(params) {
38397
38736
  const result = await pivotTask(params.fromTaskId, params.toTaskId, {
38398
38737
  reason: params.reason,
38399
38738
  blocksFrom: params.blocksFrom,
38400
- projectRoot: getProjectRoot11()
38739
+ projectRoot: getProjectRoot11(),
38740
+ takeOver: params.takeOver === true,
38741
+ forceClaim: params.forceClaim === true
38401
38742
  });
38402
38743
  return { success: true, data: result };
38403
38744
  } catch (err) {
@@ -38408,9 +38749,18 @@ async function orchestratePivotOp(params) {
38408
38749
  else if (code === 4) errorCode = "E_NOT_FOUND";
38409
38750
  else if (code === 6) errorCode = "E_VALIDATION";
38410
38751
  else if (code === 38) errorCode = "E_NOT_ACTIVE";
38752
+ else if (code === 35) errorCode = "E_TASK_CLAIMED";
38753
+ else if (code === 24) errorCode = "E_SESSION_UNBOUND";
38754
+ const cleo = err instanceof CleoError ? err : void 0;
38411
38755
  return {
38412
38756
  success: false,
38413
- error: { code: errorCode, message }
38757
+ error: {
38758
+ code: errorCode,
38759
+ message,
38760
+ ...typeof code === "number" && code !== 0 ? { exitCode: code } : {},
38761
+ ...cleo?.fix ? { fix: cleo.fix } : {},
38762
+ ...cleo?.details ? { details: cleo.details } : {}
38763
+ }
38414
38764
  };
38415
38765
  }
38416
38766
  }
@@ -39371,7 +39721,9 @@ var init_orchestrate2 = __esm({
39371
39721
  fromTaskId: params.fromTaskId,
39372
39722
  toTaskId: params.toTaskId,
39373
39723
  reason: params.reason,
39374
- blocksFrom: params.blocksFrom
39724
+ blocksFrom: params.blocksFrom,
39725
+ takeOver: params.takeOver === true,
39726
+ forceClaim: params.forceClaim === true
39375
39727
  };
39376
39728
  return wrapResult(
39377
39729
  await coreOps2.pivot(p),
@@ -42600,7 +42952,9 @@ var init_tasks3 = __esm({
42600
42952
  // T10538 — cancelled-child waiver (PM-Core V2 agent-trust)
42601
42953
  cancelledChildWaiverReason: params.cancelledChildWaiverReason,
42602
42954
  // T11954 (DHQ-071) — depends-edge waiver for stale/over-specified deps
42603
- waiveDependsReason: params.waiveDependsReason
42955
+ waiveDependsReason: params.waiveDependsReason,
42956
+ // T12503 — optimistic concurrency (--if-match)
42957
+ expectedUpdatedAt: params.expectedUpdatedAt
42604
42958
  });
42605
42959
  setImmediate(async () => {
42606
42960
  try {
@@ -42775,7 +43129,13 @@ var init_tasks3 = __esm({
42775
43129
  },
42776
43130
  start: async (params) => {
42777
43131
  const projectRoot = getProjectRoot19();
42778
- return wrapCoreResult(await taskStart(projectRoot, params.taskId), "start");
43132
+ return wrapCoreResult(
43133
+ await taskStart(projectRoot, params.taskId, {
43134
+ takeOver: params.takeOver === true,
43135
+ forceClaim: params.forceClaim === true
43136
+ }),
43137
+ "start"
43138
+ );
42779
43139
  },
42780
43140
  stop: async (_params) => {
42781
43141
  const projectRoot = getProjectRoot19();
@@ -42804,11 +43164,11 @@ var init_tasks3 = __esm({
42804
43164
  },
42805
43165
  claim: async (params) => {
42806
43166
  const projectRoot = getProjectRoot19();
42807
- return wrapCoreResult(await taskClaim(projectRoot, params.taskId, params.agentId), "claim");
43167
+ return wrapCoreResult(await taskClaim(projectRoot, params), "claim");
42808
43168
  },
42809
43169
  unclaim: async (params) => {
42810
43170
  const projectRoot = getProjectRoot19();
42811
- return wrapCoreResult(await taskUnclaim(projectRoot, params.taskId), "unclaim");
43171
+ return wrapCoreResult(await taskUnclaim(projectRoot, params), "unclaim");
42812
43172
  }
42813
43173
  });
42814
43174
  QUERY_OPS12 = /* @__PURE__ */ new Set([
@@ -44869,6 +45229,32 @@ var init_budget_enforcement = __esm({
44869
45229
  }
44870
45230
  });
44871
45231
 
45232
+ // packages/cleo/src/dispatch/middleware/claim-heartbeat.ts
45233
+ function createClaimHeartbeat(renew, budgetMs = CLAIM_HEARTBEAT_BUDGET_MS) {
45234
+ return async (req, next) => {
45235
+ const response = await next();
45236
+ const sessionId = req.sessionId;
45237
+ if (req.gateway === "mutate" && response.success && sessionId) {
45238
+ const renewal = renew(req, sessionId).catch(() => void 0);
45239
+ let timer;
45240
+ const budget = new Promise((resolve13) => {
45241
+ timer = setTimeout(resolve13, budgetMs);
45242
+ timer.unref?.();
45243
+ });
45244
+ await Promise.race([renewal, budget]);
45245
+ if (timer) clearTimeout(timer);
45246
+ }
45247
+ return response;
45248
+ };
45249
+ }
45250
+ var CLAIM_HEARTBEAT_BUDGET_MS;
45251
+ var init_claim_heartbeat = __esm({
45252
+ "packages/cleo/src/dispatch/middleware/claim-heartbeat.ts"() {
45253
+ "use strict";
45254
+ CLAIM_HEARTBEAT_BUDGET_MS = 250;
45255
+ }
45256
+ });
45257
+
44872
45258
  // packages/cleo/src/dispatch/middleware/field-filter.ts
44873
45259
  import { isMVILevel, applyFieldFilter as sdkApplyFieldFilter } from "@cleocode/lafs";
44874
45260
  function createFieldFilter() {
@@ -45462,7 +45848,10 @@ function createStoreWriteGuard(getProjectRoot73) {
45462
45848
  return next();
45463
45849
  }
45464
45850
  const { storeWriteBlock } = await import("@cleocode/core/store/store-write-guard.js");
45465
- const blocked = await storeWriteBlock(projectRoot);
45851
+ const blocked = await storeWriteBlock(projectRoot, {
45852
+ domain: req.domain,
45853
+ operation: req.operation
45854
+ });
45466
45855
  if (blocked === null) return next();
45467
45856
  return {
45468
45857
  success: false,
@@ -45535,6 +45924,7 @@ __export(cli_exports, {
45535
45924
  dispatchRaw: () => dispatchRaw,
45536
45925
  getCliDispatcher: () => getCliDispatcher,
45537
45926
  handleRawError: () => handleRawError,
45927
+ heartbeatCliSession: () => heartbeatCliSession,
45538
45928
  lookupCliSession: () => lookupCliSession,
45539
45929
  maybeEmitDescribe: () => maybeEmitDescribe,
45540
45930
  resetCliDispatcher: () => resetCliDispatcher,
@@ -45599,6 +45989,10 @@ async function lookupCliSession() {
45599
45989
  return null;
45600
45990
  }
45601
45991
  }
45992
+ async function heartbeatCliSession(_req, sessionId) {
45993
+ const { heartbeatProjectSession } = await import("@cleocode/core/internal");
45994
+ await heartbeatProjectSession(getProjectRoot23(), sessionId);
45995
+ }
45602
45996
  async function warnUnboundMutation(req) {
45603
45997
  if (getFormatContext().quiet) return;
45604
45998
  const { hasActiveSession } = await import("@cleocode/core/internal");
@@ -45619,6 +46013,8 @@ function createCliDispatcher() {
45619
46013
  createStoreWriteGuard(() => getProjectRoot23()),
45620
46014
  createSessionResolver(lookupCliSession, warnUnboundMutation),
45621
46015
  // T4959: session identity first; T12500: warn when unbound
46016
+ createClaimHeartbeat(heartbeatCliSession),
46017
+ // T12502 · T12540: a bound session's mutation refreshes lastActivity + renews its leases
45622
46018
  createSanitizer(() => getProjectRoot23()),
45623
46019
  createFieldFilter(),
45624
46020
  // T9922 (Saga T9855 / E8.3): MVI record projection default for read ops.
@@ -45842,6 +46238,7 @@ var init_cli = __esm({
45842
46238
  init_domains();
45843
46239
  init_audit();
45844
46240
  init_budget_enforcement();
46241
+ init_claim_heartbeat();
45845
46242
  init_field_filter();
45846
46243
  init_idempotency();
45847
46244
  init_mutate_minimal_envelope();
@@ -49706,7 +50103,7 @@ var attention_exports = {};
49706
50103
  __export(attention_exports, {
49707
50104
  attentionCommand: () => attentionCommand
49708
50105
  });
49709
- import { CleoError } from "@cleocode/core";
50106
+ import { CleoError as CleoError2 } from "@cleocode/core";
49710
50107
  function parseTags(rawTag) {
49711
50108
  if (!rawTag) return void 0;
49712
50109
  const tags = rawTag.split(",").map((t) => t.trim()).filter(Boolean);
@@ -49758,7 +50155,7 @@ var init_attention3 = __esm({
49758
50155
  { command: "attention", operation: "attention.add" }
49759
50156
  );
49760
50157
  } catch (err) {
49761
- if (err instanceof CleoError) {
50158
+ if (err instanceof CleoError2) {
49762
50159
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
49763
50160
  process.exit(err.code);
49764
50161
  }
@@ -49814,7 +50211,7 @@ var init_attention3 = __esm({
49814
50211
  }
49815
50212
  cliOutput(data, { command: "attention show", operation: "attention.show" });
49816
50213
  } catch (err) {
49817
- if (err instanceof CleoError) {
50214
+ if (err instanceof CleoError2) {
49818
50215
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
49819
50216
  process.exit(err.code);
49820
50217
  }
@@ -50158,8 +50555,13 @@ var init_list2 = __esm({
50158
50555
  });
50159
50556
  const claudeCredsPath = join9(homedir(), ".claude", ".credentials.json");
50160
50557
  const hint = existsSync6(claudeCredsPath) && entries.every((e) => e.source !== "claude-code") ? "Hint: Claude Code OAuth detected at ~/.claude/.credentials.json but consent is off. Run `cleo auth consent --enable-claude-code` to seed it into the pool." : null;
50558
+ const { getNexusAccountStatus } = await import(
50559
+ /* webpackIgnore: true */
50560
+ "@cleocode/core/cloud/nexus-auth.js"
50561
+ );
50562
+ const nexus = providerFilter === null || providerFilter === "nexus" ? await getNexusAccountStatus().catch(() => []) : [];
50161
50563
  cliOutput(
50162
- { entries, ...hint !== null ? { hint } : {} },
50564
+ { entries, nexus, ...hint !== null ? { hint } : {} },
50163
50565
  {
50164
50566
  command: "auth-list",
50165
50567
  operation: "auth.list"
@@ -50170,6 +50572,116 @@ var init_list2 = __esm({
50170
50572
  }
50171
50573
  });
50172
50574
 
50575
+ // packages/cleo/src/cli/lib/device-code-prompt.ts
50576
+ function writeDeviceCodePrompt(info, serviceName) {
50577
+ process.stderr.write("\n");
50578
+ process.stderr.write(` Visit: ${info.verificationUriComplete ?? info.verificationUri}
50579
+ `);
50580
+ process.stderr.write(` Enter code: ${info.userCode}
50581
+ `);
50582
+ process.stderr.write("\n");
50583
+ process.stderr.write(
50584
+ ` Waiting for ${serviceName} authorization (up to ${Math.round(info.expiresIn / 60)} min)...
50585
+ `
50586
+ );
50587
+ }
50588
+ function writeDeviceCodePending(elapsed) {
50589
+ process.stderr.write(`\r Polling... ${elapsed}s elapsed`);
50590
+ }
50591
+ function writeDeviceCodeInterrupted() {
50592
+ process.stderr.write("\n");
50593
+ }
50594
+ function writeDeviceCodeApproved(serviceName) {
50595
+ process.stderr.write(`\r ${serviceName} authorization approved.
50596
+
50597
+ `);
50598
+ }
50599
+ var init_device_code_prompt = __esm({
50600
+ "packages/cleo/src/cli/lib/device-code-prompt.ts"() {
50601
+ "use strict";
50602
+ }
50603
+ });
50604
+
50605
+ // packages/cleo/src/cli/lib/negated-flag.ts
50606
+ function negatedFlag(args, name) {
50607
+ if (name.startsWith("no-")) {
50608
+ throw new Error(
50609
+ `negatedFlag: pass the positive flag name ('${name.slice(3)}'), not '${name}'.`
50610
+ );
50611
+ }
50612
+ return args[name] === false || args[`no-${name}`] === true;
50613
+ }
50614
+ var init_negated_flag = __esm({
50615
+ "packages/cleo/src/cli/lib/negated-flag.ts"() {
50616
+ "use strict";
50617
+ }
50618
+ });
50619
+
50620
+ // packages/cleo/src/cli/lib/nexus-account-cli.ts
50621
+ function nexusApiUrlArg(args) {
50622
+ const raw = args["api-url"];
50623
+ return typeof raw === "string" && raw !== "" ? raw : void 0;
50624
+ }
50625
+ function failNexus(err, operation) {
50626
+ const code = err instanceof Error && "code" in err && typeof err.code === "string" ? err.code : void 0;
50627
+ const fix = err instanceof Error && "fix" in err && typeof err.fix === "string" ? err.fix : void 0;
50628
+ const exitCode = code === "E_NEXUS_INVALID_API_URL" || code === "E_NEXUS_INVALID_LABEL" ? 6 : 1;
50629
+ cliError(
50630
+ err instanceof Error ? err.message : String(err),
50631
+ exitCode,
50632
+ { name: code ?? "E_NEXUS_REQUEST_FAILED", ...fix ? { fix } : {} },
50633
+ { operation }
50634
+ );
50635
+ process.exit(exitCode);
50636
+ }
50637
+ function emitNexusResult(data, summary, command, operation) {
50638
+ if (isHumanOutput()) humanLine(summary);
50639
+ else cliOutput(data, { command, operation });
50640
+ }
50641
+ async function runNexusLogin(args, openBrowser2) {
50642
+ const { loginToNexus } = await import(
50643
+ /* webpackIgnore: true */
50644
+ "@cleocode/core/cloud/nexus-auth.js"
50645
+ );
50646
+ const noBrowser = negatedFlag(args, "browser");
50647
+ try {
50648
+ const result = await loginToNexus({
50649
+ apiUrl: nexusApiUrlArg(args),
50650
+ onCode: (code) => {
50651
+ writeDeviceCodePrompt(code, SERVICE_NAME);
50652
+ if (!noBrowser) openBrowser2(code.verificationUriComplete ?? code.verificationUri);
50653
+ },
50654
+ onPending: writeDeviceCodePending
50655
+ });
50656
+ writeDeviceCodeApproved(SERVICE_NAME);
50657
+ for (const warning of result.warnings) process.stderr.write(`warning: ${warning}
50658
+ `);
50659
+ return result;
50660
+ } catch (err) {
50661
+ writeDeviceCodeInterrupted();
50662
+ throw err;
50663
+ }
50664
+ }
50665
+ function nexusLoginSummary(r) {
50666
+ const who = r.user?.email ?? "your account";
50667
+ const org = r.organization ? ` (${r.organization.name})` : "";
50668
+ return `Signed in to ${r.apiUrl} as ${who}${org}.`;
50669
+ }
50670
+ var SERVICE_NAME, NEXUS_API_URL_ARG;
50671
+ var init_nexus_account_cli = __esm({
50672
+ "packages/cleo/src/cli/lib/nexus-account-cli.ts"() {
50673
+ "use strict";
50674
+ init_renderers();
50675
+ init_device_code_prompt();
50676
+ init_negated_flag();
50677
+ SERVICE_NAME = "Cleo Nexus";
50678
+ NEXUS_API_URL_ARG = {
50679
+ type: "string",
50680
+ description: "Cleo Nexus API URL (default: $CLEO_NEXUS_API_URL, else https://api.cleocode.dev; staging: https://api.staging.cleocode.dev)."
50681
+ };
50682
+ }
50683
+ });
50684
+
50173
50685
  // packages/cleo/src/cli/lib/readline-wizard-io.ts
50174
50686
  import { stdin as input, stdout as output } from "node:process";
50175
50687
  import * as readline from "node:readline/promises";
@@ -50181,7 +50693,7 @@ function stripBracketedPaste(raw) {
50181
50693
  function streamIsTTY(stream) {
50182
50694
  return "isTTY" in stream && stream.isTTY === true;
50183
50695
  }
50184
- var SETUP_STDIN_CLOSED_CODE, StdinClosedError, SECRET_ECHO_REFUSED_MESSAGE, MutableOutput, ReadlineWizardIO;
50696
+ var SETUP_STDIN_CLOSED_CODE, StdinClosedError, SECRET_ECHO_REFUSED_MESSAGE, SECRET_MASK_CHAR, MutableOutput, ReadlineWizardIO;
50185
50697
  var init_readline_wizard_io = __esm({
50186
50698
  "packages/cleo/src/cli/lib/readline-wizard-io.ts"() {
50187
50699
  "use strict";
@@ -50202,6 +50714,7 @@ var init_readline_wizard_io = __esm({
50202
50714
  }
50203
50715
  };
50204
50716
  SECRET_ECHO_REFUSED_MESSAGE = 'Refusing to read a secret: stdin is a terminal but terminal mode is off, so the key would be echoed. Pipe it instead: printf %s "$KEY" | cleo decide config --provider layahost --key-stdin (or cleo llm add <provider> --api-key-stdin).';
50717
+ SECRET_MASK_CHAR = "\u2022";
50205
50718
  MutableOutput = class extends Writable {
50206
50719
  /** @param target - Stream that receives writes while unmuted. */
50207
50720
  constructor(target) {
@@ -50248,6 +50761,8 @@ var init_readline_wizard_io = __esm({
50248
50761
  output;
50249
50762
  /** Whether the input stream is a TTY (the terminal echoes unless readline owns it). */
50250
50763
  inputIsTTY;
50764
+ /** The input stream (its keypress events drive the secret mask). */
50765
+ inStream;
50251
50766
  /** AbortController aborted when stdin closes — surfaced as {@link StdinClosedError}. */
50252
50767
  eofController;
50253
50768
  /**
@@ -50271,6 +50786,7 @@ var init_readline_wizard_io = __esm({
50271
50786
  constructor(inStream = input, outStream = output, options = {}) {
50272
50787
  this.eofController = new AbortController();
50273
50788
  this.output = new MutableOutput(outStream);
50789
+ this.inStream = inStream;
50274
50790
  this.inputIsTTY = streamIsTTY(inStream);
50275
50791
  this.rl = readline.createInterface({
50276
50792
  input: inStream,
@@ -50345,6 +50861,7 @@ var init_readline_wizard_io = __esm({
50345
50861
  if (this.inputIsTTY && !this.rl.terminal) throw new Error(SECRET_ECHO_REFUSED_MESSAGE);
50346
50862
  this.output.writeUnmuted(`${question} `);
50347
50863
  this.output.muted = true;
50864
+ const stopMask = this.echoMask();
50348
50865
  let raw;
50349
50866
  try {
50350
50867
  raw = await this.rl.question("", { signal: this.eofController.signal });
@@ -50352,11 +50869,37 @@ var init_readline_wizard_io = __esm({
50352
50869
  } catch (err) {
50353
50870
  return this.rethrowEof(err);
50354
50871
  } finally {
50872
+ stopMask();
50355
50873
  this.output.muted = false;
50356
50874
  this.output.writeUnmuted("\n");
50357
50875
  if (raw !== void 0) this.scrubHistory(raw);
50358
50876
  }
50359
50877
  }
50878
+ /**
50879
+ * While a secret is typed (readline's echo muted), echo one
50880
+ * {@link SECRET_MASK_CHAR} per character in the line and erase one per
50881
+ * deleted character. Runs after readline's own keypress handler (registered
50882
+ * first), so `rl.line` is already updated; only its LENGTH is read. The
50883
+ * submitting key (Enter) is ignored, since readline clears the line then.
50884
+ *
50885
+ * @returns A function that stops the echo.
50886
+ */
50887
+ echoMask() {
50888
+ if (!this.rl.terminal) return () => {
50889
+ };
50890
+ let shown = 0;
50891
+ const onKeypress = (_str, key) => {
50892
+ if (key?.name === "return" || key?.name === "enter") return;
50893
+ const length = this.rl.line.length;
50894
+ if (length > shown) this.output.writeUnmuted(SECRET_MASK_CHAR.repeat(length - shown));
50895
+ else if (length < shown) this.output.writeUnmuted("\b \b".repeat(shown - length));
50896
+ shown = length;
50897
+ };
50898
+ this.inStream.on("keypress", onKeypress);
50899
+ return () => {
50900
+ this.inStream.off("keypress", onKeypress);
50901
+ };
50902
+ }
50360
50903
  /**
50361
50904
  * Remove every history entry equal to `line`. `history` is a runtime field
50362
50905
  * of `readline.Interface` that `@types/node` does not declare, so it is
@@ -50687,27 +51230,12 @@ async function _runKimiCodeLogin(opts, meta) {
50687
51230
  meta
50688
51231
  );
50689
51232
  }
50690
- process.stderr.write("\n");
50691
- process.stderr.write(
50692
- ` Visit: ${startResp.verificationUriComplete ?? startResp.verificationUri}
50693
- `
50694
- );
50695
- process.stderr.write(` Enter code: ${startResp.userCode}
50696
- `);
50697
- process.stderr.write("\n");
50698
- process.stderr.write(
50699
- ` Waiting for Kimi Code authorization (up to ${Math.round(startResp.expiresIn / 60)} min)...
50700
- `
50701
- );
51233
+ writeDeviceCodePrompt(startResp, "Kimi Code");
50702
51234
  let tokenResp;
50703
51235
  try {
50704
- tokenResp = await pollForToken(cfg, startResp, {
50705
- onPending: (elapsed) => {
50706
- process.stderr.write(`\r Polling... ${elapsed}s elapsed`);
50707
- }
50708
- });
51236
+ tokenResp = await pollForToken(cfg, startResp, { onPending: writeDeviceCodePending });
50709
51237
  } catch (err) {
50710
- process.stderr.write("\n");
51238
+ writeDeviceCodeInterrupted();
50711
51239
  if (err instanceof DeviceCodeTimeoutError) {
50712
51240
  return _errorResult("E_DEVICE_CODE_TIMEOUT", err.message, meta);
50713
51241
  }
@@ -50721,7 +51249,7 @@ async function _runKimiCodeLogin(opts, meta) {
50721
51249
  meta
50722
51250
  );
50723
51251
  }
50724
- process.stderr.write("\r Kimi Code authorization approved. \n\n");
51252
+ writeDeviceCodeApproved("Kimi Code");
50725
51253
  const label = opts.label ?? "oauth-login";
50726
51254
  const expiresAt = typeof tokenResp.expiresIn === "number" ? Date.now() + tokenResp.expiresIn * 1e3 : void 0;
50727
51255
  try {
@@ -50777,15 +51305,22 @@ function _findFreePort() {
50777
51305
  });
50778
51306
  }
50779
51307
  function _tryOpenBrowser(url) {
50780
- const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
51308
+ if (UNSAFE_OPEN_URL.test(url)) return;
51309
+ const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["rundll32", ["url.dll,FileProtocolHandler", url]] : ["xdg-open", [url]];
50781
51310
  try {
50782
- spawn(cmd, [url], { detached: true, stdio: "ignore" }).unref();
51311
+ const child = spawn(cmd, args, { detached: true, stdio: "ignore", windowsHide: true });
51312
+ child.on("error", () => {
51313
+ });
51314
+ child.unref();
50783
51315
  } catch {
50784
51316
  }
50785
51317
  }
51318
+ var UNSAFE_OPEN_URL;
50786
51319
  var init_llm_login = __esm({
50787
51320
  "packages/cleo/src/cli/commands/llm-login.ts"() {
50788
51321
  "use strict";
51322
+ init_device_code_prompt();
51323
+ UNSAFE_OPEN_URL = /^-|[\u0000-\u0020\u007f-\u009f\s]/;
50789
51324
  }
50790
51325
  });
50791
51326
 
@@ -50793,9 +51328,12 @@ var init_llm_login = __esm({
50793
51328
  var login_exports = {};
50794
51329
  __export(login_exports, {
50795
51330
  LOGIN_ARGS: () => LOGIN_ARGS,
51331
+ NEXUS_PICKER_LABEL: () => NEXUS_PICKER_LABEL,
50796
51332
  emitLoginResult: () => emitLoginResult,
50797
51333
  loginCommand: () => loginCommand,
51334
+ loginPickerOptions: () => loginPickerOptions,
50798
51335
  makeOAuthAcquirer: () => makeOAuthAcquirer,
51336
+ runLoginCommand: () => runLoginCommand,
50799
51337
  runLoginFrontDoor: () => runLoginFrontDoor
50800
51338
  });
50801
51339
  async function providerRegistry() {
@@ -50920,6 +51458,55 @@ async function resolveApiKey(args, io) {
50920
51458
  function hasApiKeyFlag(args) {
50921
51459
  return args["api-key-stdin"] === true || typeof args["api-key"] === "string" && args["api-key"] !== "";
50922
51460
  }
51461
+ function loginPickerOptions(providerNames) {
51462
+ return [NEXUS_PICKER_LABEL, ...[...providerNames].sort()];
51463
+ }
51464
+ async function resolveLoginTarget(args) {
51465
+ const given = typeof args["provider"] === "string" ? args["provider"] : "";
51466
+ if (given || !process.stdin.isTTY) return given || void 0;
51467
+ const names = (await (await providerRegistry()).listProviders()).map((p) => p.name);
51468
+ const io = new ReadlineWizardIO(process.stdin, process.stderr);
51469
+ try {
51470
+ const choice = await io.select("What do you want to log in to?", loginPickerOptions(names));
51471
+ return choice === NEXUS_PICKER_LABEL ? NEXUS_LOGIN_TARGET : choice;
51472
+ } finally {
51473
+ io.close();
51474
+ }
51475
+ }
51476
+ async function runLoginCommand(args, operation) {
51477
+ let target;
51478
+ try {
51479
+ target = await resolveLoginTarget(args);
51480
+ } catch (err) {
51481
+ failLogin(err, operation);
51482
+ }
51483
+ if (target === NEXUS_LOGIN_TARGET) {
51484
+ let nexus;
51485
+ try {
51486
+ nexus = await runNexusLogin(args, _tryOpenBrowser);
51487
+ } catch (err) {
51488
+ failNexus(err, operation);
51489
+ }
51490
+ emitNexusResult(nexus, nexusLoginSummary(nexus), "login", operation);
51491
+ return;
51492
+ }
51493
+ let result;
51494
+ try {
51495
+ result = await runLoginFrontDoor(target ? { ...args, provider: target } : args);
51496
+ } catch (err) {
51497
+ failLogin(err, operation);
51498
+ }
51499
+ emitLoginResult(result, operation);
51500
+ }
51501
+ function failLogin(err, operation) {
51502
+ cliError(
51503
+ err instanceof Error ? err.message : String(err),
51504
+ 1,
51505
+ { name: "E_LOGIN_FAILED" },
51506
+ { operation }
51507
+ );
51508
+ process.exit(1);
51509
+ }
50923
51510
  function emitLoginResult(result, operation) {
50924
51511
  if (!result.validated) {
50925
51512
  emitLoginFailure(result, operation);
@@ -50943,22 +51530,30 @@ function emitLoginFailure(result, operation) {
50943
51530
  );
50944
51531
  process.exit(1);
50945
51532
  }
50946
- var AUTH_METHODS, LOGIN_ARGS, loginCommand;
51533
+ var AUTH_METHODS, NEXUS_PICKER_LABEL, LOGIN_ARGS, loginCommand;
50947
51534
  var init_login = __esm({
50948
51535
  "packages/cleo/src/cli/commands/login.ts"() {
50949
51536
  "use strict";
50950
51537
  init_src2();
50951
51538
  init_define_cli_command();
51539
+ init_nexus_account_cli();
50952
51540
  init_readline_wizard_io();
50953
51541
  init_renderers();
50954
51542
  init_llm_login();
50955
51543
  AUTH_METHODS = ["oauth", "api_key"];
51544
+ NEXUS_PICKER_LABEL = "Cleo Nexus account";
50956
51545
  LOGIN_ARGS = {
50957
51546
  provider: {
50958
51547
  type: "positional",
50959
- description: "Provider to log in to (anthropic | openai | codex | gemini | kimi-code | \u2026).",
51548
+ description: "What to log in to: 'nexus' (Cleo Nexus account) or an LLM provider (anthropic | openai | codex | gemini | kimi-code | \u2026).",
50960
51549
  required: false
50961
51550
  },
51551
+ "api-url": NEXUS_API_URL_ARG,
51552
+ browser: {
51553
+ type: "boolean",
51554
+ description: "Open the verification URL in a browser (nexus). --no-browser only prints it (SSH, containers).",
51555
+ default: true
51556
+ },
50962
51557
  auth: {
50963
51558
  type: "string",
50964
51559
  description: "Auth method: 'oauth' (browser) or 'api_key'. Inferred from the provider when omitted."
@@ -50994,23 +51589,11 @@ var init_login = __esm({
50994
51589
  // ONE quoted literal, no backticks: the manifest generator's DESC_RE
50995
51590
  // captures only the first plain string literal (concatenations + backticks
50996
51591
  // truncate the `cleo --help` text mid-sentence).
50997
- description: "Log in to an LLM provider and bind a usable profile in one step. Picks a provider + auth method (browser OAuth or API key), selects a model, binds it, and validates the binding. cleo auth login and cleo llm login resolve to this same flow. Prompts/URLs go to stderr; the result is a human line on a terminal or a JSON envelope when piped / --json."
51592
+ description: "Log in to a Cleo Nexus account (cleo login nexus: device code, --api-url, --no-browser) or to an LLM provider, binding a usable profile in one step. The picker lists the Cleo Nexus account first, then the providers. For a provider it picks an auth method (browser OAuth or API key), selects a model, binds it, and validates the binding. cleo auth login and cleo llm login resolve to this same flow. Prompts/URLs go to stderr; the result is a human line on a terminal or a JSON envelope when piped / --json."
50998
51593
  },
50999
51594
  args: LOGIN_ARGS,
51000
51595
  async run({ args }) {
51001
- let result;
51002
- try {
51003
- result = await runLoginFrontDoor(args);
51004
- } catch (err) {
51005
- cliError(
51006
- err instanceof Error ? err.message : String(err),
51007
- 1,
51008
- { name: "E_LOGIN_FAILED" },
51009
- { operation: "login.run" }
51010
- );
51011
- process.exit(1);
51012
- }
51013
- emitLoginResult(result, "login.run");
51596
+ await runLoginCommand(args, "login.run");
51014
51597
  }
51015
51598
  });
51016
51599
  }
@@ -51022,28 +51605,15 @@ var init_login2 = __esm({
51022
51605
  "packages/cleo/src/cli/commands/auth/login.ts"() {
51023
51606
  "use strict";
51024
51607
  init_define_cli_command();
51025
- init_renderers();
51026
51608
  init_login();
51027
51609
  authLoginCommand = defineCommand({
51028
51610
  meta: {
51029
51611
  name: "login",
51030
- description: "Log in to an LLM provider and bind a usable profile (alias of `cleo login`). Picks a provider + auth method (browser OAuth or API key), selects a model, binds it, and validates the binding. Prompts/URLs go to stderr; the result is a human line on a terminal or a JSON envelope when piped/--json."
51612
+ description: "Log in to a Cleo Nexus account (auth login nexus) or to an LLM provider and bind a usable profile (alias of `cleo login`). Picks a provider + auth method (browser OAuth or API key), selects a model, binds it, and validates the binding. Prompts/URLs go to stderr; the result is a human line on a terminal or a JSON envelope when piped/--json."
51031
51613
  },
51032
51614
  args: LOGIN_ARGS,
51033
51615
  async run({ args }) {
51034
- let result;
51035
- try {
51036
- result = await runLoginFrontDoor(args);
51037
- } catch (err) {
51038
- cliError(
51039
- err instanceof Error ? err.message : String(err),
51040
- 1,
51041
- { name: "E_LOGIN_FAILED" },
51042
- { operation: "auth.login" }
51043
- );
51044
- process.exit(1);
51045
- }
51046
- emitLoginResult(result, "auth.login");
51616
+ await runLoginCommand(args, "auth.login");
51047
51617
  }
51048
51618
  });
51049
51619
  }
@@ -51263,6 +51833,17 @@ var init_migrate_project_secrets = __esm({
51263
51833
  });
51264
51834
 
51265
51835
  // packages/cleo/src/cli/commands/auth/remove.ts
51836
+ function emitLlmCredentialRemoval(outcome, command, operation) {
51837
+ if (!outcome.ok) {
51838
+ cliError(outcome.message, outcome.exitCode, { name: outcome.code, fix: outcome.fix });
51839
+ process.exit(outcome.exitCode);
51840
+ }
51841
+ for (const path6 of outcome.result.cleaned) process.stderr.write(`cleaned: ${path6}
51842
+ `);
51843
+ for (const hint of outcome.result.hints) process.stderr.write(`hint: ${hint}
51844
+ `);
51845
+ cliOutput(outcome.result, { command, operation });
51846
+ }
51266
51847
  var authRemoveCommand;
51267
51848
  var init_remove = __esm({
51268
51849
  "packages/cleo/src/cli/commands/auth/remove.ts"() {
@@ -51302,73 +51883,15 @@ var init_remove = __esm({
51302
51883
  cliError("label is required", 6, { name: "E_INVALID_INPUT" });
51303
51884
  process.exit(6);
51304
51885
  }
51305
- const { getCredentialPool: getCredentialPool2 } = await import(
51306
- /* webpackIgnore: true */
51307
- "@cleocode/core/llm/credential-pool.js"
51308
- );
51309
- const { REMOVAL_REGISTRY, addSuppression } = await import(
51886
+ const { removeLlmCredential } = await import(
51310
51887
  /* webpackIgnore: true */
51311
- "@cleocode/core/llm/credential-removal.js"
51888
+ "@cleocode/core/llm/credential-remove-entry.js"
51312
51889
  );
51313
- const { removeCredential } = await import(
51314
- /* webpackIgnore: true */
51315
- "@cleocode/core/llm/credentials-store.js"
51890
+ emitLlmCredentialRemoval(
51891
+ await removeLlmCredential(provider, label),
51892
+ "auth-remove",
51893
+ "auth.remove"
51316
51894
  );
51317
- const pool = getCredentialPool2();
51318
- const entries = await pool.list();
51319
- const entry = entries.find((c) => c.provider === provider && c.label === label);
51320
- if (!entry) {
51321
- cliError(`No credential found for provider='${provider}' label='${label}'`, 4, {
51322
- name: "E_NOT_FOUND",
51323
- fix: `Run 'cleo auth list' to see active credentials.`
51324
- });
51325
- process.exit(4);
51326
- }
51327
- const sourceId = entry.source ?? "manual";
51328
- const step = REMOVAL_REGISTRY.find(sourceId);
51329
- if (!step) {
51330
- cliError(
51331
- `No RemovalStep registered for source='${sourceId}' \u2014 cannot safely remove '${provider}/${label}'.`,
51332
- 2,
51333
- {
51334
- name: "E_REMOVAL_NOT_REGISTERED",
51335
- fix: `Open an issue: a credential was seeded from an unknown source.`
51336
- }
51337
- );
51338
- process.exit(2);
51339
- }
51340
- const stepResult = await step.remove({ provider, label });
51341
- for (const path6 of stepResult.cleaned) {
51342
- process.stderr.write(`cleaned: ${path6}
51343
- `);
51344
- }
51345
- for (const hint of stepResult.hints) {
51346
- process.stderr.write(`hint: ${hint}
51347
- `);
51348
- }
51349
- let suppressed = false;
51350
- if (stepResult.suppress) {
51351
- addSuppression(provider, sourceId);
51352
- suppressed = true;
51353
- }
51354
- const removed = await removeCredential(
51355
- entry.provider,
51356
- // ModelTransport
51357
- label
51358
- );
51359
- const result = {
51360
- provider,
51361
- label,
51362
- source: sourceId,
51363
- removed,
51364
- cleaned: stepResult.cleaned,
51365
- hints: stepResult.hints,
51366
- suppressed
51367
- };
51368
- cliOutput(result, {
51369
- command: "auth-remove",
51370
- operation: "auth.remove"
51371
- });
51372
51895
  }
51373
51896
  });
51374
51897
  }
@@ -51830,21 +52353,6 @@ var init_backup_inspect = __esm({
51830
52353
  }
51831
52354
  });
51832
52355
 
51833
- // packages/cleo/src/cli/lib/negated-flag.ts
51834
- function negatedFlag(args, name) {
51835
- if (name.startsWith("no-")) {
51836
- throw new Error(
51837
- `negatedFlag: pass the positive flag name ('${name.slice(3)}'), not '${name}'.`
51838
- );
51839
- }
51840
- return args[name] === false || args[`no-${name}`] === true;
51841
- }
51842
- var init_negated_flag = __esm({
51843
- "packages/cleo/src/cli/lib/negated-flag.ts"() {
51844
- "use strict";
51845
- }
51846
- });
51847
-
51848
52356
  // packages/cleo/src/cli/commands/backup-recover.ts
51849
52357
  var backup_recover_exports = {};
51850
52358
  __export(backup_recover_exports, {
@@ -51873,7 +52381,10 @@ function executeRecover(role, args) {
51873
52381
  logger: getLogger20(`backup-recover-${role}`),
51874
52382
  dryRun,
51875
52383
  fromSnapshot: fromSnapshot.length > 0 ? fromSnapshot : void 0,
51876
- noDelta
52384
+ noDelta,
52385
+ // T12708: the invocation directory; core never falls back to it.
52386
+ cwd: process.cwd(),
52387
+ confirmOwnerStore: readBoolFlag(args, "confirm-owner-store")
51877
52388
  });
51878
52389
  cliOutput(result, {
51879
52390
  command: "backup",
@@ -51955,6 +52466,11 @@ var init_backup_recover = __esm({
51955
52466
  type: "boolean",
51956
52467
  description: "Bypass any safety prompts (currently a no-op; reserved)",
51957
52468
  default: false
52469
+ },
52470
+ "confirm-owner-store": {
52471
+ type: "boolean",
52472
+ description: "From inside a git worktree: allow overwriting the owning project's LIVE store (refused without it)",
52473
+ default: false
51958
52474
  }
51959
52475
  },
51960
52476
  async run({ args }) {
@@ -51992,6 +52508,11 @@ var init_backup_recover = __esm({
51992
52508
  type: "boolean",
51993
52509
  description: "Bypass any safety prompts (currently a no-op; reserved)",
51994
52510
  default: false
52511
+ },
52512
+ "confirm-owner-store": {
52513
+ type: "boolean",
52514
+ description: "From inside a git worktree: allow overwriting the owning project's LIVE store (refused without it)",
52515
+ default: false
51995
52516
  }
51996
52517
  },
51997
52518
  subCommands: {
@@ -52166,7 +52687,7 @@ function collectFlagValues(rawArgs, flag) {
52166
52687
  }
52167
52688
  return values;
52168
52689
  }
52169
- async function portableImport(bundlePath, target, mapValues, force) {
52690
+ async function portableImport(bundlePath, target, mapValues, force, confirmOwnerStore) {
52170
52691
  const core2 = await import("@cleocode/core/store/portable-bundle-import.js");
52171
52692
  const { PortableBundleError } = await import("@cleocode/core/store/portable-bundle.js");
52172
52693
  try {
@@ -52180,7 +52701,10 @@ async function portableImport(bundlePath, target, mapValues, force) {
52180
52701
  maps: core2.parsePathMappings(mapValues),
52181
52702
  force: force === true,
52182
52703
  requireLossless: true,
52183
- registerProject: core2.registerRelocatedProject
52704
+ registerProject: core2.registerRelocatedProject,
52705
+ // T12708: the invocation directory; core never falls back to it.
52706
+ cwd: process.cwd(),
52707
+ confirmOwnerStore
52184
52708
  });
52185
52709
  cliOutput(result, { command: "backup", operation: "backup.import" });
52186
52710
  } catch (err) {
@@ -52377,13 +52901,25 @@ var init_backup = __esm({
52377
52901
  map: {
52378
52902
  type: "string",
52379
52903
  description: "Portable bundles: rewrite project paths <oldPrefix>=<newPrefix> (repeatable; longest prefix wins)"
52904
+ },
52905
+ "confirm-owner-store": {
52906
+ type: "boolean",
52907
+ description: "From inside a git worktree: allow overwriting the owning project's LIVE store (refused without it)",
52908
+ default: false
52380
52909
  }
52381
52910
  },
52382
52911
  async run({ args, rawArgs }) {
52383
52912
  const bundlePath = args.bundle;
52913
+ const confirmOwnerStore = args["confirm-owner-store"] === true;
52384
52914
  const { detectBundleFormat } = await import("@cleocode/core/store/portable-bundle-import.js");
52385
52915
  if (await detectBundleFormat(bundlePath) === "v2") {
52386
- await portableImport(bundlePath, args.target, collectFlagValues(rawArgs, "map"), args.force);
52916
+ await portableImport(
52917
+ bundlePath,
52918
+ args.target,
52919
+ collectFlagValues(rawArgs, "map"),
52920
+ args.force,
52921
+ confirmOwnerStore
52922
+ );
52387
52923
  return;
52388
52924
  }
52389
52925
  const { getProjectRoot: getProjectRoot73, getCleoHome: getCleoHome8, getCleoVersion } = await import("@cleocode/core");
@@ -52392,6 +52928,16 @@ var init_backup = __esm({
52392
52928
  const { regenerateAndCompare } = await import("@cleocode/core/store/restore-json-merge.js");
52393
52929
  const { buildConflictReport, writeConflictReport } = await import("@cleocode/core/store/restore-conflict-report.js");
52394
52930
  const projectRoot = getProjectRoot73();
52931
+ const { assertOwnerStoreRewriteConfirmed } = await import("@cleocode/core/store/worktree-isolation-guard.js");
52932
+ try {
52933
+ assertOwnerStoreRewriteConfirmed("backup import", path2.join(projectRoot, ".cleo"), {
52934
+ cwd: process.cwd(),
52935
+ confirmOwnerStore
52936
+ });
52937
+ } catch (err) {
52938
+ reportBundleError(err, null, "E_IMPORT_FAILED");
52939
+ return;
52940
+ }
52395
52941
  if (args.force !== true) {
52396
52942
  const existing = checkForExistingData(projectRoot, getCleoHome8());
52397
52943
  if (existing.length > 0) {
@@ -54740,7 +55286,7 @@ __export(checkpoint_exports, {
54740
55286
  checkpointCommand: () => checkpointCommand
54741
55287
  });
54742
55288
  import {
54743
- CleoError as CleoError2,
55289
+ CleoError as CleoError3,
54744
55290
  formatError as formatError3,
54745
55291
  getCleoDir,
54746
55292
  gitCheckpoint,
@@ -54780,7 +55326,7 @@ var init_checkpoint = __esm({
54780
55326
  async run({ args }) {
54781
55327
  try {
54782
55328
  if (!isGitRepo()) {
54783
- throw new CleoError2(1 /* GENERAL_ERROR */, ".cleo/.git not initialized. Run: cleo init");
55329
+ throw new CleoError3(1 /* GENERAL_ERROR */, ".cleo/.git not initialized. Run: cleo init");
54784
55330
  }
54785
55331
  if (args.status) {
54786
55332
  const status = await gitCheckpointStatus();
@@ -54836,7 +55382,7 @@ var init_checkpoint = __esm({
54836
55382
  { command: "checkpoint", message: "Checkpoint complete" }
54837
55383
  );
54838
55384
  } catch (err) {
54839
- if (err instanceof CleoError2) {
55385
+ if (err instanceof CleoError3) {
54840
55386
  cliError(formatError3(err), err.code, { name: "E_CHECKPOINT_FAILED" });
54841
55387
  process.exit(err.code);
54842
55388
  }
@@ -54860,30 +55406,63 @@ var init_claim = __esm({
54860
55406
  init_dist();
54861
55407
  init_cli();
54862
55408
  claimCommand = defineCommand({
54863
- meta: { name: "claim", description: "Claim a task by assigning it to an agent" },
55409
+ meta: {
55410
+ name: "claim",
55411
+ description: "Take, renew (--renew) or override (--take-over expired, --force-claim live) your session's leased claim on a task"
55412
+ },
54864
55413
  args: {
54865
55414
  taskId: { type: "positional", description: "Task ID to claim", required: true },
54866
- agent: { type: "string", description: "Agent ID to assign the task to", required: true }
55415
+ agent: {
55416
+ type: "string",
55417
+ description: "Agent ID recorded with the lease (default: the session's agent / CLEO_AGENT_ID)"
55418
+ },
55419
+ renew: { type: "boolean", description: "Renew your own lease on the task" },
55420
+ "take-over": {
55421
+ type: "boolean",
55422
+ description: "Take over another session's EXPIRED lease (audited)"
55423
+ },
55424
+ "force-claim": {
55425
+ type: "boolean",
55426
+ description: "Take over another session's LIVE lease (audited)"
55427
+ }
54867
55428
  },
54868
55429
  async run({ args }) {
54869
55430
  await dispatchFromCli(
54870
55431
  "mutate",
54871
55432
  "tasks",
54872
55433
  "claim",
54873
- { taskId: args.taskId, agentId: args.agent },
55434
+ {
55435
+ taskId: args.taskId,
55436
+ ...typeof args.agent === "string" && args.agent ? { agentId: args.agent } : {},
55437
+ ...args.renew === true ? { renew: true } : {},
55438
+ ...args["take-over"] === true ? { takeOver: true } : {},
55439
+ ...args["force-claim"] === true ? { forceClaim: true } : {}
55440
+ },
54874
55441
  { command: "claim", operation: "tasks.claim" }
54875
55442
  );
54876
55443
  }
54877
55444
  });
54878
55445
  unclaimCommand = defineCommand({
54879
- meta: { name: "unclaim", description: "Unclaim a task by removing its current assignee" },
54880
- args: { taskId: { type: "positional", description: "Task ID to unclaim", required: true } },
55446
+ meta: {
55447
+ name: "unclaim",
55448
+ description: "Release your session's claim lease on a task (--force-claim: another session's)"
55449
+ },
55450
+ args: {
55451
+ taskId: { type: "positional", description: "Task ID to unclaim", required: true },
55452
+ "force-claim": {
55453
+ type: "boolean",
55454
+ description: "Release another session's lease (audited)"
55455
+ }
55456
+ },
54881
55457
  async run({ args }) {
54882
55458
  await dispatchFromCli(
54883
55459
  "mutate",
54884
55460
  "tasks",
54885
55461
  "unclaim",
54886
- { taskId: args.taskId },
55462
+ {
55463
+ taskId: args.taskId,
55464
+ ...args["force-claim"] === true ? { forceClaim: true } : {}
55465
+ },
54887
55466
  { command: "unclaim", operation: "tasks.unclaim" }
54888
55467
  );
54889
55468
  }
@@ -55178,7 +55757,9 @@ function completeDispatchParams(args) {
55178
55757
  // T10538 — cancelled-child waiver (PM-Core V2 agent-trust)
55179
55758
  cancelledChildWaiverReason: str3("waive-cancelled-children"),
55180
55759
  // T11954 (DHQ-071) — depends-edge waiver for stale/over-specified deps
55181
- waiveDependsReason: str3("waive-depends")
55760
+ waiveDependsReason: str3("waive-depends"),
55761
+ // T12503 — optimistic concurrency
55762
+ expectedUpdatedAt: str3("if-match")
55182
55763
  };
55183
55764
  }
55184
55765
  var completeCommandArgs, completeCommand;
@@ -55232,6 +55813,11 @@ var init_complete = __esm({
55232
55813
  "waive-depends": {
55233
55814
  type: "string",
55234
55815
  description: "Reason for completing a task whose own work is done but whose depends edges point at not-yet-terminal tasks (stale/over-specified). Audited to .cleo/audit/depends-waiver.jsonl."
55816
+ },
55817
+ // T12503 — optimistic concurrency
55818
+ "if-match": {
55819
+ type: "string",
55820
+ description: "Fail with E_CONFLICT unless the task updatedAt still equals this version (from cleo show). Guards against completing over a concurrent edit."
55235
55821
  }
55236
55822
  };
55237
55823
  completeCommand = defineCommand({
@@ -56316,7 +56902,7 @@ var config_exports = {};
56316
56902
  __export(config_exports, {
56317
56903
  configCommand: () => configCommand
56318
56904
  });
56319
- import { CleoError as CleoError3, loadConfig as loadConfig2 } from "@cleocode/core";
56905
+ import { CleoError as CleoError4, loadConfig as loadConfig2 } from "@cleocode/core";
56320
56906
  var PRESET_DESCRIPTIONS, setPresetCommand, presetsCommand, listCommand6, configCommand;
56321
56907
  var init_config3 = __esm({
56322
56908
  "packages/cleo/src/cli/commands/config.ts"() {
@@ -56371,7 +56957,7 @@ var init_config3 = __esm({
56371
56957
  const resolved = await loadConfig2();
56372
56958
  cliOutput({ config: resolved }, { command: "config" });
56373
56959
  } catch (err) {
56374
- if (err instanceof CleoError3) {
56960
+ if (err instanceof CleoError4) {
56375
56961
  cliError(`config list failed: ${err.message}`, err.code, { name: "E_CONFIG_LOAD" });
56376
56962
  process.exit(err.code);
56377
56963
  }
@@ -56865,7 +57451,7 @@ import { existsSync as existsSync10, statSync } from "node:fs";
56865
57451
  import { mkdir, open, readFile as readFile3, rm, stat, writeFile } from "node:fs/promises";
56866
57452
  import { dirname as dirname4, join as join15, resolve as resolve4 } from "node:path";
56867
57453
  import { fileURLToPath as fileURLToPath2 } from "node:url";
56868
- import { CleoError as CleoError4, getCleoHome as getCleoHome2 } from "@cleocode/core";
57454
+ import { CleoError as CleoError5, getCleoHome as getCleoHome2 } from "@cleocode/core";
56869
57455
  import { defineSubsystem } from "@cleocode/runtime/daemon";
56870
57456
  function resolveStudioCheckout(moduleUrl) {
56871
57457
  try {
@@ -56972,7 +57558,7 @@ function createWebSubsystem(opts = {}, moduleUrl = import.meta.url) {
56972
57558
  const checkout = resolveStudioCheckout(moduleUrl);
56973
57559
  const studioDir = resolvedStudioDir ?? (checkout ? join15(checkout, "packages", "studio", "build") : void 0);
56974
57560
  if (!studioDir) {
56975
- throw new CleoError4(
57561
+ throw new CleoError5(
56976
57562
  4 /* NOT_FOUND */,
56977
57563
  "Packaged Studio bundle is missing its index.js entry. Reinstall a complete CLEO package or set CLEO_STUDIO_DIR to a built Studio bundle."
56978
57564
  );
@@ -56987,7 +57573,7 @@ function createWebSubsystem(opts = {}, moduleUrl = import.meta.url) {
56987
57573
  await stat(webIndexPath);
56988
57574
  } catch {
56989
57575
  if (!checkout || studioDir !== join15(checkout, "packages", "studio", "build")) {
56990
- throw new CleoError4(4 /* NOT_FOUND */, `Studio entry is missing: ${webIndexPath}`);
57576
+ throw new CleoError5(4 /* NOT_FOUND */, `Studio entry is missing: ${webIndexPath}`);
56991
57577
  }
56992
57578
  try {
56993
57579
  execFileSync3("pnpm", ["--filter", "@cleocode/studio", "run", "build"], {
@@ -57603,13 +58189,17 @@ import {
57603
58189
  askDecideDebug,
57604
58190
  clearDecideConfig,
57605
58191
  configureDecide,
58192
+ DecideCredentialsError,
57606
58193
  describeDecideCredentials,
58194
+ listDecideProfilesReport,
57607
58195
  listDecisionSites,
57608
58196
  parseDecisionProviderKind,
57609
58197
  probeDecideProvider,
58198
+ removeDecideProfile,
57610
58199
  resetDecideBudget,
57611
58200
  runDecideWizard,
57612
- SpendResetRefusedError
58201
+ SpendResetRefusedError,
58202
+ useDecideProfile
57613
58203
  } from "@cleocode/core/decide/index.js";
57614
58204
  import { WizardInterruptError as WizardInterruptError2 } from "@cleocode/core/setup";
57615
58205
  async function readSecretFromStdin() {
@@ -57626,7 +58216,7 @@ function failValidation(message, operation, fix) {
57626
58216
  async function runConfigWizard(op) {
57627
58217
  const io = new ReadlineWizardIO(process.stdin, process.stderr);
57628
58218
  try {
57629
- cliOutput(await runDecideWizard(io), { command: "decide", operation: op });
58219
+ cliOutput(await runDecideWizard(io), { command: "decide-config", operation: op });
57630
58220
  } catch (err) {
57631
58221
  if (err instanceof WizardInterruptError2) {
57632
58222
  process.stderr.write("System One setup cancelled.\n");
@@ -57641,7 +58231,13 @@ async function runConfigWizard(op) {
57641
58231
  function oneOf(value, allowed) {
57642
58232
  return allowed.find((a) => a === value);
57643
58233
  }
57644
- var CONFIG_FIX, decideConfigCommand, decideStatusCommand, decideAskCommand, decideSitesCommand, decideBudgetResetCommand, decideBudgetCommand, decideCommand;
58234
+ function commaList(value) {
58235
+ return value === void 0 ? void 0 : value.split(",").map((v) => v.trim()).filter(Boolean);
58236
+ }
58237
+ function numberFlag(value) {
58238
+ return value === void 0 ? void 0 : Number(value);
58239
+ }
58240
+ var CONFIG_FIX, PROFILES_FIX, ASK_FIX, decideConfigCommand, decideUseCommand, decideProfilesCommand, decideStatusCommand, decideAskCommand, decideSitesCommand, decideBenchCommand, decideBudgetResetCommand, decideBudgetCommand, decideCommand;
57645
58241
  var init_decide2 = __esm({
57646
58242
  "packages/cleo/src/cli/commands/decide.ts"() {
57647
58243
  "use strict";
@@ -57650,19 +58246,21 @@ var init_decide2 = __esm({
57650
58246
  init_readline_wizard_io();
57651
58247
  init_renderers();
57652
58248
  CONFIG_FIX = 'printf %s "$KEY" | cleo decide config --provider layahost --key-stdin';
58249
+ PROFILES_FIX = "cleo decide profiles";
58250
+ ASK_FIX = 'cleo decide ask --state "<text>" --question "<yes/no question>"';
57653
58251
  decideConfigCommand = defineCommand({
57654
58252
  meta: {
57655
58253
  name: "config",
57656
- description: "Store the decision provider, API key and model (0600 file in the CLEO home), then probe it and detect its capabilities. --provider layahost (default) needs only the key; --provider jev needs --url. Prefer --key-stdin so the key stays out of shell history. No flags on a terminal runs the setup wizard (the key is typed hidden); no flags without a terminal shows the current settings (key masked)."
58254
+ description: "Store a System One profile, <provider>/<name> (several per provider, each with its own key): API key, URL and model (0600 file in the CLEO home), then probe it and detect its capabilities. Each provider has a default URL (layahost https://layahost.com, jev https://api.typesafe.ai); --url overrides it. Other profiles are kept and exactly one is active. Prefer --key-stdin so the key stays out of shell history. No flags on a terminal runs the setup wizard (the key is masked); no flags without a terminal shows the active settings (key masked)."
57657
58255
  },
57658
58256
  args: {
57659
58257
  provider: {
57660
58258
  type: "string",
57661
- description: `Provider: ${DECISION_PROVIDER_KINDS.join("|")} (layahost: fixed URL, model laya-auto; jev: custom URL, required)`
58259
+ description: `Provider: ${DECISION_PROVIDER_KINDS.join("|")} (layahost: model laya-auto; jev: model from the provider listing)`
57662
58260
  },
57663
58261
  url: {
57664
58262
  type: "string",
57665
- description: "Provider API base URL: overrides the layahost default; required for jev"
58263
+ description: 'Override URL for the profile, or "default" for the provider default URL (resolved at call time)'
57666
58264
  },
57667
58265
  key: {
57668
58266
  type: "string",
@@ -57673,12 +58271,37 @@ var init_decide2 = __esm({
57673
58271
  type: "string",
57674
58272
  description: "Default model; omitted \u2192 the stored model, the preset (layahost: laya-auto) or the first listed (jev)"
57675
58273
  },
57676
- clear: { type: "boolean", description: "Remove the stored URL, key and model" }
58274
+ profile: {
58275
+ type: "string",
58276
+ description: "Profile to add or update: <provider>/<name> or a name (a-z, 0-9, -); default: the active profile of that provider, else <provider>/default"
58277
+ },
58278
+ activate: {
58279
+ type: "boolean",
58280
+ description: "Make the profile active (default: only for the first profile or one already active)"
58281
+ },
58282
+ remove: {
58283
+ type: "string",
58284
+ description: "Remove this profile (<provider>/<name>); the active one needs --use"
58285
+ },
58286
+ use: {
58287
+ type: "string",
58288
+ description: "With --remove: the profile (<provider>/<name>) to activate instead"
58289
+ },
58290
+ clear: { type: "boolean", description: "Remove every profile (URLs, keys and models)" }
57677
58291
  },
57678
58292
  async run({ args }) {
57679
58293
  const op = "decide.config";
57680
58294
  if (args.clear === true)
57681
- return cliOutput(await clearDecideConfig(), { command: "decide", operation: op });
58295
+ return cliOutput(await clearDecideConfig(), { command: "decide-config", operation: op });
58296
+ if (args.remove !== void 0) {
58297
+ try {
58298
+ const result = await removeDecideProfile(args.remove, args.use);
58299
+ return cliOutput(result, { command: "decide-config", operation: "decide.config.remove" });
58300
+ } catch (err) {
58301
+ if (!(err instanceof DecideCredentialsError)) throw err;
58302
+ return failValidation(err.message, op, PROFILES_FIX);
58303
+ }
58304
+ }
57682
58305
  const provider = parseDecisionProviderKind(args.provider);
57683
58306
  if (args.provider !== void 0 && provider === void 0) {
57684
58307
  return failValidation(`unknown provider '${args.provider}'`, op, CONFIG_FIX);
@@ -57686,24 +58309,66 @@ var init_decide2 = __esm({
57686
58309
  const apiKey = args["key-stdin"] === true ? await readSecretFromStdin() : args.key;
57687
58310
  if (args["key-stdin"] === true && !apiKey)
57688
58311
  return failValidation("--key-stdin set but stdin is empty or a TTY", op, CONFIG_FIX);
57689
- const noSettings = provider === void 0 && args.url === void 0 && apiKey === void 0 && args.model === void 0;
58312
+ const noSettings = provider === void 0 && args.url === void 0 && apiKey === void 0 && args.model === void 0 && args.profile === void 0 && args.activate === void 0;
57690
58313
  if (noSettings && process.stdin.isTTY && process.stderr.isTTY) return runConfigWizard(op);
57691
58314
  if (noSettings) {
57692
- return cliOutput(describeDecideCredentials(), { command: "decide", operation: op });
58315
+ return cliOutput(describeDecideCredentials(), { command: "decide-config", operation: op });
57693
58316
  }
57694
58317
  try {
57695
58318
  const result = await configureDecide({
57696
58319
  provider,
57697
58320
  baseUrl: args.url,
57698
58321
  apiKey,
57699
- model: args.model
58322
+ model: args.model,
58323
+ profile: args.profile,
58324
+ ...args.activate === true ? { activate: true } : {}
57700
58325
  });
57701
- cliOutput(result, { command: "decide", operation: op });
58326
+ cliOutput(result, { command: "decide-config", operation: op });
57702
58327
  } catch (err) {
57703
58328
  failValidation(err instanceof Error ? err.message : "invalid settings", op, CONFIG_FIX);
57704
58329
  }
57705
58330
  }
57706
58331
  });
58332
+ decideUseCommand = defineCommand({
58333
+ meta: {
58334
+ name: "use",
58335
+ description: "Switch the active System One profile (<provider>/<name>, e.g. layahost/work; a bare provider means <provider>/default): everyday decisions use it from now on. Other profiles stay stored and can still be addressed by id."
58336
+ },
58337
+ args: {
58338
+ profile: {
58339
+ type: "positional",
58340
+ description: "Profile to activate, <provider>/<name>",
58341
+ required: true
58342
+ }
58343
+ },
58344
+ async run({ args }) {
58345
+ const op = "decide.use";
58346
+ try {
58347
+ cliOutput(await useDecideProfile(args.profile), {
58348
+ command: "decide-profiles",
58349
+ operation: op
58350
+ });
58351
+ } catch (err) {
58352
+ if (!(err instanceof DecideCredentialsError)) throw err;
58353
+ failValidation(err.message, op, PROFILES_FIX);
58354
+ }
58355
+ }
58356
+ });
58357
+ decideProfilesCommand = defineCommand({
58358
+ meta: {
58359
+ name: "profiles",
58360
+ description: "List the stored System One profiles: the active one marked, keys masked. --probe also checks each profile (GET {url}/v1/models, in parallel)."
58361
+ },
58362
+ args: {
58363
+ probe: { type: "boolean", description: "Probe each profile's reachability" },
58364
+ "timeout-ms": { type: "string", description: "Probe timeout in ms (default 3000)" }
58365
+ },
58366
+ async run({ args }) {
58367
+ const timeoutMs = args["timeout-ms"] ? Number(args["timeout-ms"]) : void 0;
58368
+ const result = await listDecideProfilesReport({ probe: args.probe === true, timeoutMs });
58369
+ cliOutput(result, { command: "decide-profiles", operation: "decide.profiles" });
58370
+ }
58371
+ });
57707
58372
  decideStatusCommand = defineCommand({
57708
58373
  meta: {
57709
58374
  name: "status",
@@ -57715,7 +58380,7 @@ var init_decide2 = __esm({
57715
58380
  async run({ args }) {
57716
58381
  const timeoutMs = args["timeout-ms"] ? Number(args["timeout-ms"]) : void 0;
57717
58382
  const result = await probeDecideProvider({ timeoutMs });
57718
- cliOutput(result, { command: "decide", operation: "decide.status" });
58383
+ cliOutput(result, { command: "decide-status", operation: "decide.status" });
57719
58384
  if (result.state !== "reachable" && (process.exitCode ?? 0) === 0) process.exitCode = 1;
57720
58385
  }
57721
58386
  });
@@ -57725,14 +58390,32 @@ var init_decide2 = __esm({
57725
58390
  description: "Debug: ask one yes/no question about a state through the configured provider. Shows the typed answer, source (provider or fallback), latency and cost."
57726
58391
  },
57727
58392
  args: {
57728
- state: { type: "string", description: "The state (text) to judge", required: true },
57729
- noul: { type: "string", description: "The yes/no question", required: true },
57730
- "timeout-ms": { type: "string", description: "Deadline in ms (default 10000)" }
58393
+ state: { type: "string", description: "The state (text) to judge" },
58394
+ question: { type: "string", description: "The yes/no question" },
58395
+ noul: { type: "string", description: "Alias of --question" },
58396
+ "timeout-ms": { type: "string", description: "Deadline in ms (default 10000)" },
58397
+ profile: {
58398
+ type: "string",
58399
+ description: "Ask through this profile, <provider>/<name> (default: the active one)"
58400
+ }
57731
58401
  },
57732
58402
  async run({ args }) {
58403
+ const question = args.question ?? args.noul;
58404
+ if (!args.state || !question) {
58405
+ return failValidation(
58406
+ "cleo decide ask needs a state and a yes/no question",
58407
+ "decide.ask",
58408
+ ASK_FIX
58409
+ );
58410
+ }
57733
58411
  const timeoutMs = args["timeout-ms"] ? Number(args["timeout-ms"]) : void 0;
57734
- const result = await askDecideDebug({ state: args.state, question: args.noul, timeoutMs });
57735
- cliOutput(result, { command: "decide", operation: "decide.ask" });
58412
+ const result = await askDecideDebug({
58413
+ state: args.state,
58414
+ question,
58415
+ timeoutMs,
58416
+ ...args.profile ? { profile: args.profile } : {}
58417
+ });
58418
+ cliOutput(result, { command: "decide-ask", operation: "decide.ask" });
57736
58419
  }
57737
58420
  });
57738
58421
  decideSitesCommand = defineCommand({
@@ -57779,6 +58462,61 @@ var init_decide2 = __esm({
57779
58462
  cliOutput(result, { command: "decide", operation: op });
57780
58463
  }
57781
58464
  });
58465
+ decideBenchCommand = defineCommand({
58466
+ meta: {
58467
+ name: "bench",
58468
+ description: "System One accuracy benchmark (T12495): build a labelled dataset from this project's own history (duplicate task pairs, explicitly typed observations, superseding decisions; redacted), write a stratified spot-check sample for the owner, then compare providers against each site's heuristic with batched calls (provider cache off, 30s or longer deadline): accuracy, precision, recall, F1, false-positive rate, p50/p95 latency, cost and fallbacks, mean and spread over --runs. A hard total cap (--max-usd, default 5) is checked before every batch; the run stops cleanly at it. Profiles resolve from CLEO_DECIDE_PROFILE_<NAME>_KEY/_URL/_MODEL or the stored provider kind. --sample-only never contacts a provider."
58469
+ },
58470
+ args: {
58471
+ profiles: {
58472
+ type: "string",
58473
+ description: "Comma list of provider profiles to compare, e.g. layahost,jev"
58474
+ },
58475
+ sites: {
58476
+ type: "string",
58477
+ description: "Comma list: duplicateDetection,observationType,decisionContradiction (default all)"
58478
+ },
58479
+ "sample-only": {
58480
+ type: "boolean",
58481
+ description: "Build the dataset and the spot-check sample only (offline, no spend)"
58482
+ },
58483
+ corrections: {
58484
+ type: "string",
58485
+ description: 'Owner corrections file ({"corrections":[{"id","label"}|{"id","drop":true}]}) to apply'
58486
+ },
58487
+ "max-usd": { type: "string", description: "Hard total spend cap in USD (default 5)" },
58488
+ runs: { type: "string", description: "Repeated runs for mean and spread (default 1)" },
58489
+ out: { type: "string", description: "Output directory (default .cleo/decide-bench)" },
58490
+ "batch-size": { type: "string", description: "Rows per batch call, 1-64 (default 16)" },
58491
+ rebuild: {
58492
+ type: "boolean",
58493
+ description: "Rebuild the dataset from the stores (drops corrections)"
58494
+ },
58495
+ seed: { type: "string", description: "Seed for negatives and the sample (default 12495)" }
58496
+ },
58497
+ async run({ args }) {
58498
+ const op = "decide.bench";
58499
+ const { DecideBenchInputError, runDecideBenchOperation } = await import("@cleocode/core/decide/bench/index.js");
58500
+ try {
58501
+ const result = await runDecideBenchOperation({
58502
+ profiles: commaList(args.profiles),
58503
+ sites: commaList(args.sites),
58504
+ sampleOnly: args["sample-only"] === true,
58505
+ correctionsPath: args.corrections,
58506
+ maxUsd: numberFlag(args["max-usd"]),
58507
+ runs: numberFlag(args.runs),
58508
+ outDir: args.out,
58509
+ batchSize: numberFlag(args["batch-size"]),
58510
+ rebuild: args.rebuild === true,
58511
+ seed: numberFlag(args.seed)
58512
+ });
58513
+ cliOutput(result, { command: "decide", operation: op });
58514
+ } catch (err) {
58515
+ if (!(err instanceof DecideBenchInputError)) throw err;
58516
+ failValidation(err.message, op, err.fix);
58517
+ }
58518
+ }
58519
+ });
57782
58520
  decideBudgetResetCommand = defineCommand({
57783
58521
  meta: {
57784
58522
  name: "reset",
@@ -57816,14 +58554,17 @@ var init_decide2 = __esm({
57816
58554
  decideCommand = defineCommand({
57817
58555
  meta: {
57818
58556
  name: "decide",
57819
- description: "System One integration (typed decisions): decide config (provider + API key; wizard on a terminal), decide status (reachability probe), decide ask (one debug question), decide sites (the registered decision sites). Unconfigured means heuristics answer."
58557
+ description: "System One integration (typed decisions): decide config (named provider profile + API key; wizard on a terminal), decide use (switch the active profile), decide profiles (list them), decide status (reachability probe), decide ask (one debug question), decide sites (the registered decision sites), decide bench (accuracy benchmark). Unconfigured means heuristics answer."
57820
58558
  },
57821
58559
  subCommands: {
57822
58560
  config: decideConfigCommand,
58561
+ use: decideUseCommand,
58562
+ profiles: decideProfilesCommand,
57823
58563
  status: decideStatusCommand,
57824
58564
  ask: decideAskCommand,
57825
58565
  sites: decideSitesCommand,
57826
- budget: decideBudgetCommand
58566
+ budget: decideBudgetCommand,
58567
+ bench: decideBenchCommand
57827
58568
  },
57828
58569
  async run({ cmd, rawArgs }) {
57829
58570
  const firstArg = rawArgs?.find((a) => !a.startsWith("-"));
@@ -59408,7 +60149,7 @@ var init_graph2 = __esm({
59408
60149
  });
59409
60150
 
59410
60151
  // packages/cleo/src/cli/commands/docs/set-alias.ts
59411
- import { CleoError as CleoError5, getProjectRoot as getProjectRoot38, setDisplayAlias } from "@cleocode/core/internal";
60152
+ import { CleoError as CleoError6, getProjectRoot as getProjectRoot38, setDisplayAlias } from "@cleocode/core/internal";
59412
60153
  var setAliasCommand;
59413
60154
  var init_set_alias = __esm({
59414
60155
  "packages/cleo/src/cli/commands/docs/set-alias.ts"() {
@@ -59470,7 +60211,7 @@ var init_set_alias = __esm({
59470
60211
  const result = await setDisplayAlias(getProjectRoot38(), { slug, displayAlias });
59471
60212
  cliOutput(result, { command: "docs set-alias", operation: "docs.set-alias" });
59472
60213
  } catch (err) {
59473
- if (err instanceof CleoError5) {
60214
+ if (err instanceof CleoError6) {
59474
60215
  cliError(err.message, err.code, {
59475
60216
  name: typeof err.details?.["code"] === "string" ? err.details["code"] : "E_DOCS_SET_ALIAS",
59476
60217
  ...err.fix ? { fix: err.fix } : {},
@@ -60288,7 +61029,7 @@ import { dirname as dirname8, isAbsolute as isAbsolute2, join as join21, resolve
60288
61029
  import { pushWarning as pushWarning3 } from "@cleocode/core";
60289
61030
  import { createDocsReadModel as createDocsReadModel2 } from "@cleocode/core/docs/docs-read-model";
60290
61031
  import {
60291
- CleoError as CleoError6,
61032
+ CleoError as CleoError7,
60292
61033
  CounterMismatchError,
60293
61034
  checkSlugSimilarity,
60294
61035
  DEFAULT_SIMILARITY_MODE,
@@ -61981,7 +62722,7 @@ var init_docs3 = __esm({
61981
62722
  });
61982
62723
  if (args.strict && anyDrift) process.exit(2);
61983
62724
  } catch (err) {
61984
- if (err instanceof CleoError6) {
62725
+ if (err instanceof CleoError7) {
61985
62726
  cliError(err.message, err.code, { name: "E_DOCS_CHECK_FAILED" });
61986
62727
  process.exit(err.code);
61987
62728
  }
@@ -62067,7 +62808,7 @@ var init_docs3 = __esm({
62067
62808
  process.exit(result.status === "error" ? 2 : 1);
62068
62809
  }
62069
62810
  } catch (err) {
62070
- if (err instanceof CleoError6) {
62811
+ if (err instanceof CleoError7) {
62071
62812
  cliError(err.message, err.code, { name: "E_DOCS_SYNC_FAILED" });
62072
62813
  process.exit(err.code);
62073
62814
  }
@@ -62134,7 +62875,7 @@ var init_docs3 = __esm({
62134
62875
  );
62135
62876
  }
62136
62877
  } catch (err) {
62137
- if (err instanceof CleoError6) {
62878
+ if (err instanceof CleoError7) {
62138
62879
  cliError(err.message, err.code, { name: "E_DOCS_GAP_CHECK_FAILED" });
62139
62880
  process.exit(err.code);
62140
62881
  }
@@ -62618,6 +63359,11 @@ var init_doctor_db_substrate = __esm({
62618
63359
  type: "boolean",
62619
63360
  description: "Disable auto-quarantine of corrupt DBs (leaves them in place; report-only mode)."
62620
63361
  },
63362
+ "confirm-owner-store": {
63363
+ type: "boolean",
63364
+ description: "From inside a git worktree: allow quarantining the owning project's LIVE store (skipped without it)",
63365
+ default: false
63366
+ },
62621
63367
  json: { type: "boolean", description: "Output as JSON" },
62622
63368
  human: { type: "boolean", description: "Force human-readable output" },
62623
63369
  quiet: { type: "boolean", description: "Suppress non-essential output" }
@@ -62633,7 +63379,10 @@ var init_doctor_db_substrate = __esm({
62633
63379
  }
62634
63380
  const options = {
62635
63381
  ...parsedTimeoutMs !== void 0 ? { integrityCheckTimeoutMs: parsedTimeoutMs } : {},
62636
- autoQuarantine: !negatedFlag(args, "quarantine")
63382
+ autoQuarantine: !negatedFlag(args, "quarantine"),
63383
+ // T12708: the invocation directory; the quarantine guard checks it.
63384
+ cwd: process.cwd(),
63385
+ confirmOwnerStore: args["confirm-owner-store"] === true
62637
63386
  };
62638
63387
  const result = isFleet ? surveyFleetDbSubstrate(
62639
63388
  typeof args["fleet-root"] === "string" && args["fleet-root"].length > 0 ? args["fleet-root"] : await defaultFleetRoots(),
@@ -63300,6 +64049,7 @@ __export(doctor_project_identity_exports, {
63300
64049
  import { getProjectRoot as getProjectRoot48 } from "@cleocode/core";
63301
64050
  import {
63302
64051
  inspectProjectIdentity,
64052
+ inspectProjectNameDrift,
63303
64053
  resolveProjectIdentity
63304
64054
  } from "@cleocode/core/doctor/project-identity.js";
63305
64055
  var doctorProjectIdentityCommand;
@@ -63311,7 +64061,7 @@ var init_doctor_project_identity = __esm({
63311
64061
  doctorProjectIdentityCommand = defineCommand({
63312
64062
  meta: {
63313
64063
  name: "project-identity",
63314
- description: "Check .cleo/project-id against project-info.json (missing / conflict / invalid / untracked) and print the exact remedy. --resolve applies it (a conflict is re-keyed to the tracked id through the alias table, losing no registry rows); add --dry-run to see the plan first."
64064
+ description: "Check .cleo/project.json, its legacy .cleo/project-id mirror and the project-info.json cache (legacy / missing / conflict / invalid / untracked) plus registry-name drift, and print the exact remedy. --resolve applies it: it migrates a legacy project to .cleo/project.json (no id changes) and re-keys a conflict to the tracked id through the alias table, losing no registry rows; add --dry-run to see the plan first."
63315
64065
  },
63316
64066
  args: {
63317
64067
  resolve: { type: "boolean", description: "Apply the remedy for the reported state" },
@@ -63324,8 +64074,13 @@ var init_doctor_project_identity = __esm({
63324
64074
  const projectRoot = getProjectRoot48();
63325
64075
  if (!args.resolve) {
63326
64076
  const report = inspectProjectIdentity(projectRoot);
63327
- cliOutput(report, { command: "doctor", operation: "doctor.project-identity.inspect" });
63328
- if (report.state !== "ok" && (process.exitCode === void 0 || process.exitCode === 0))
64077
+ const nameDrift = await inspectProjectNameDrift(projectRoot);
64078
+ cliOutput(
64079
+ { ...report, nameDrift },
64080
+ { command: "doctor", operation: "doctor.project-identity.inspect" }
64081
+ );
64082
+ const drifted = nameDrift.state === "drift" || nameDrift.state === "taken";
64083
+ if ((report.state !== "ok" || drifted) && (process.exitCode === void 0 || process.exitCode === 0))
63329
64084
  process.exitCode = 1;
63330
64085
  return;
63331
64086
  }
@@ -63855,6 +64610,11 @@ var init_doctor_repair = __esm({
63855
64610
  type: "boolean",
63856
64611
  description: "Detect + plan only \u2014 report what would be repaired without quarantining/restoring",
63857
64612
  default: false
64613
+ },
64614
+ "confirm-owner-store": {
64615
+ type: "boolean",
64616
+ description: "From inside a git worktree: allow overwriting the owning project's LIVE store (refused without it)",
64617
+ default: false
63858
64618
  }
63859
64619
  },
63860
64620
  run({ args }) {
@@ -63878,7 +64638,10 @@ var init_doctor_repair = __esm({
63878
64638
  projectRoot: getProjectRoot49(),
63879
64639
  roles: roleArg.length > 0 ? [roleArg] : void 0,
63880
64640
  dryRun,
63881
- logger: getLogger21("doctor-repair")
64641
+ logger: getLogger21("doctor-repair"),
64642
+ // T12708: the invocation directory; core never falls back to it.
64643
+ cwd: process.cwd(),
64644
+ confirmOwnerStore: args["confirm-owner-store"] === true
63882
64645
  });
63883
64646
  for (const r of result.roles) {
63884
64647
  if (!r.healthy) {
@@ -64224,10 +64987,24 @@ __export(doctor_twin_collapse_exports, {
64224
64987
  });
64225
64988
  import {
64226
64989
  inspectProjectTwinCollapse,
64227
- retryTwinCollapse
64990
+ recoverTwinCollapse,
64991
+ retryTwinCollapse,
64992
+ rollbackTwinCollapse
64228
64993
  } from "@cleocode/core/doctor/twin-collapse.js";
64229
- import { CleoError as CleoError7 } from "@cleocode/core/errors";
64994
+ import { CleoError as CleoError8 } from "@cleocode/core/errors";
64230
64995
  import { getProjectRoot as getProjectRoot51 } from "@cleocode/core/paths.js";
64996
+ function reportTwinCollapseError(error, retry) {
64997
+ if (retry && !(error instanceof CleoError8)) throw error;
64998
+ const message = error instanceof Error ? error.message : String(error);
64999
+ const named = /^(E_[A-Z_]+):/.exec(message)?.[1];
65000
+ const code = named ?? (retry ? "E_TWIN_COLLAPSE_FAILED" : "E_TWIN_COLLAPSE_RECOVER");
65001
+ cliError(message, code, {
65002
+ name: error instanceof CleoError8 ? "CleoError" : "Error",
65003
+ fix: error instanceof CleoError8 ? error.fix : void 0,
65004
+ details: error instanceof CleoError8 ? error.details : void 0
65005
+ });
65006
+ process.exitCode = error instanceof CleoError8 ? error.code : 1;
65007
+ }
64231
65008
  var doctorTwinCollapseCommand;
64232
65009
  var init_doctor_twin_collapse = __esm({
64233
65010
  "packages/cleo/src/cli/commands/doctor-twin-collapse.ts"() {
@@ -64244,28 +65021,59 @@ var init_doctor_twin_collapse = __esm({
64244
65021
  type: "boolean",
64245
65022
  description: "Run the collapse once now (after clearing the reported cause)"
64246
65023
  },
65024
+ recover: {
65025
+ type: "boolean",
65026
+ description: "Restore twin values a 2026.9.21 collapse dropped or replaced, from its pinned pre-collapse snapshot, into twin_collapse_archive:* (T12727). Combine with --dry-run"
65027
+ },
65028
+ "dry-run": {
65029
+ type: "boolean",
65030
+ description: "With --recover: print the plan, write nothing"
65031
+ },
65032
+ rollback: {
65033
+ type: "string",
65034
+ description: "Undo one --recover apply by its receipt id (twin_collapse_recovery:<recoveredAt>)"
65035
+ },
65036
+ "confirm-owner-store": {
65037
+ type: "boolean",
65038
+ description: "From a git worktree: allow --recover, --rollback or --retry to write the owning project's live store"
65039
+ },
64247
65040
  json: { type: "boolean", description: "Output as JSON" },
64248
65041
  human: { type: "boolean", description: "Force human-readable output" },
64249
65042
  quiet: { type: "boolean", description: "Suppress non-essential output" }
64250
65043
  },
64251
65044
  async run({ args }) {
64252
65045
  const projectRoot = getProjectRoot51();
64253
- if (args.retry === true) {
64254
- try {
64255
- const receipts = await retryTwinCollapse(projectRoot);
65046
+ const guard = { cwd: process.cwd(), confirmOwnerStore: args["confirm-owner-store"] === true };
65047
+ try {
65048
+ if (typeof args.rollback === "string" && args.rollback.length > 0) {
65049
+ const result = await rollbackTwinCollapse(projectRoot, args.rollback, guard);
65050
+ cliOutput(
65051
+ { kind: "generic", ...result },
65052
+ { command: "doctor", operation: "doctor.twin-collapse.rollback" }
65053
+ );
65054
+ return;
65055
+ }
65056
+ if (args.recover === true) {
65057
+ const result = await recoverTwinCollapse(projectRoot, {
65058
+ ...guard,
65059
+ dryRun: args["dry-run"] === true
65060
+ });
65061
+ cliOutput(
65062
+ { kind: "generic", ...result },
65063
+ { command: "doctor", operation: "doctor.twin-collapse.recover" }
65064
+ );
65065
+ return;
65066
+ }
65067
+ if (args.retry === true) {
65068
+ const receipts = await retryTwinCollapse(projectRoot, guard);
64256
65069
  cliOutput(
64257
65070
  { kind: "generic", receipts },
64258
65071
  { command: "doctor", operation: "doctor.twin-collapse.retry" }
64259
65072
  );
64260
- } catch (error) {
64261
- if (!(error instanceof CleoError7)) throw error;
64262
- cliError(error.message, "E_TWIN_COLLAPSE_FAILED", {
64263
- name: "CleoError",
64264
- fix: error.fix,
64265
- details: error.details
64266
- });
64267
- process.exitCode = error.code;
65073
+ return;
64268
65074
  }
65075
+ } catch (error) {
65076
+ reportTwinCollapseError(error, args.retry === true);
64269
65077
  return;
64270
65078
  }
64271
65079
  const report = inspectProjectTwinCollapse(projectRoot);
@@ -65105,7 +65913,7 @@ var init_doctor = __esm({
65105
65913
  "worktree-stores": doctorWorktreeStoresCommand,
65106
65914
  // T12097 — machine-wide guard for test runs started OUTSIDE cleo verify
65107
65915
  "memory-guard": doctorMemoryGuardCommand,
65108
- // T12353 — tracked .cleo/project-id vs project-info.json (+ --resolve re-key)
65916
+ // T12353 · T12716 — tracked .cleo/project.json / project-id vs project-info.json (+ --resolve migrate / re-key)
65109
65917
  "project-identity": doctorProjectIdentityCommand,
65110
65918
  // T12596 · T12598 — ~/.cleo, the global hub and every harness skill install must resolve
65111
65919
  "global-delivery": doctorGlobalDeliveryCommand,
@@ -65835,7 +66643,7 @@ async function batchEntry(taskId, result, args) {
65835
66643
  const { code, message, fix } = result.error;
65836
66644
  return { taskId, completed: false, error: code, message, next: fix };
65837
66645
  }
65838
- const params = completeDispatchParams({ ...args, taskId });
66646
+ const params = completeDispatchParams({ ...args, taskId, "if-match": void 0 });
65839
66647
  const done = await dispatchRaw("mutate", "tasks", "complete", params);
65840
66648
  const recordedGates = result.data.recordedGates;
65841
66649
  if (done.success) return { taskId, recordedGates, completed: true };
@@ -65882,6 +66690,10 @@ var init_done = __esm({
65882
66690
  },
65883
66691
  args: {
65884
66692
  ...completeCommandArgs,
66693
+ "if-match": {
66694
+ type: "string",
66695
+ description: "Refuse with E_CONFLICT, before recording any gate, unless the task updatedAt still equals this version (from cleo show). One task id only."
66696
+ },
65885
66697
  plan: {
65886
66698
  type: "boolean",
65887
66699
  description: "Print the evidence plan instead: derived change set, required gates, tool runs (cache state), AC mapping, ordered blockers and runnable commands. Records and executes nothing."
@@ -65915,6 +66727,18 @@ var init_done = __esm({
65915
66727
  return;
65916
66728
  }
65917
66729
  const taskIds = requested;
66730
+ const ifMatch = typeof args["if-match"] === "string" ? args["if-match"] : void 0;
66731
+ if (ifMatch !== void 0 && taskIds.length > 1) {
66732
+ cliError(
66733
+ "--if-match applies to one task; it cannot be used with several task ids",
66734
+ "E_INVALID_INPUT",
66735
+ {
66736
+ fix: "Pass one task id per call: cleo done <id> --if-match <updatedAt>"
66737
+ }
66738
+ );
66739
+ process.exitCode = 2;
66740
+ return;
66741
+ }
65918
66742
  if (taskIds.length > 1) {
65919
66743
  await runBatchDone(taskIds, args, options, args.plan === true);
65920
66744
  return;
@@ -65930,7 +66754,10 @@ var init_done = __esm({
65930
66754
  return;
65931
66755
  }
65932
66756
  const { recordTaskDone } = await import("@cleocode/core/tasks/done-record.js");
65933
- const recorded = await recordTaskDone(args.taskId, options);
66757
+ const recorded = await recordTaskDone(args.taskId, {
66758
+ ...options,
66759
+ ...ifMatch !== void 0 ? { expectedUpdatedAt: ifMatch } : {}
66760
+ });
65934
66761
  if (!recorded.success) {
65935
66762
  cliError(recorded.error.message, recorded.error.code, {
65936
66763
  fix: recorded.error.fix,
@@ -65943,7 +66770,7 @@ var init_done = __esm({
65943
66770
  "mutate",
65944
66771
  "tasks",
65945
66772
  "complete",
65946
- completeDispatchParams(args)
66773
+ completeDispatchParams({ ...args, "if-match": void 0 })
65947
66774
  );
65948
66775
  if (!completed.success) {
65949
66776
  const details = {
@@ -69204,7 +70031,7 @@ import { existsSync as existsSync16, readFileSync as readFileSync12 } from "node
69204
70031
  import { join as join28 } from "node:path";
69205
70032
  import { fileURLToPath as fileURLToPath5 } from "node:url";
69206
70033
  import {
69207
- CleoError as CleoError8,
70034
+ CleoError as CleoError9,
69208
70035
  getWorkflowTemplatesDir as getCoreWorkflowTemplatesDir,
69209
70036
  initErrorCodeName,
69210
70037
  initProject as initProject2,
@@ -69408,7 +70235,7 @@ var init_init = __esm({
69408
70235
  { command: "init" }
69409
70236
  );
69410
70237
  } catch (err) {
69411
- if (err instanceof CleoError8) {
70238
+ if (err instanceof CleoError9) {
69412
70239
  cliError(`init failed: ${err.message}`, err.code, {
69413
70240
  name: initErrorCodeName(err) ?? err.toLAFSError().code,
69414
70241
  ...err.fix !== void 0 ? { fix: err.fix } : {},
@@ -71106,19 +71933,7 @@ var init_llm3 = __esm({
71106
71933
  },
71107
71934
  args: LOGIN_ARGS,
71108
71935
  async run({ args }) {
71109
- let result;
71110
- try {
71111
- result = await runLoginFrontDoor(args);
71112
- } catch (err) {
71113
- cliError(
71114
- err instanceof Error ? err.message : String(err),
71115
- 1,
71116
- { name: "E_LOGIN_FAILED" },
71117
- { operation: "llm.login" }
71118
- );
71119
- process.exit(1);
71120
- }
71121
- emitLoginResult(result, "llm.login");
71936
+ await runLoginCommand(args, "llm.login");
71122
71937
  }
71123
71938
  });
71124
71939
  refreshCatalogCommand = defineCommand({
@@ -71408,6 +72223,72 @@ var init_log = __esm({
71408
72223
  }
71409
72224
  });
71410
72225
 
72226
+ // packages/cleo/src/cli/commands/logout.ts
72227
+ var logout_exports = {};
72228
+ __export(logout_exports, {
72229
+ logoutCommand: () => logoutCommand,
72230
+ nexusLogoutSummary: () => nexusLogoutSummary
72231
+ });
72232
+ function nexusLogoutSummary(r) {
72233
+ if (!r.removedLocally) return `Not signed in to ${r.apiUrl}; nothing to do.`;
72234
+ const server = r.revocation === "revoked" ? "session revoked" : r.revocation === "already-invalid" ? "session was already invalid" : "server-side revocation FAILED (the session ends when it expires)";
72235
+ return `Signed out of ${r.apiUrl}: ${server}; local token deleted.`;
72236
+ }
72237
+ var logoutCommand;
72238
+ var init_logout = __esm({
72239
+ "packages/cleo/src/cli/commands/logout.ts"() {
72240
+ "use strict";
72241
+ init_src2();
72242
+ init_define_cli_command();
72243
+ init_nexus_account_cli();
72244
+ init_remove();
72245
+ logoutCommand = defineCommand({
72246
+ meta: {
72247
+ name: "logout",
72248
+ description: "Sign out. cleo logout [nexus] revokes the Cleo Nexus session server-side and deletes the stored token; cleo logout <provider> [label] removes an LLM credential exactly like cleo auth remove (the label may be omitted when the provider has one credential)."
72249
+ },
72250
+ args: {
72251
+ target: {
72252
+ type: "positional",
72253
+ description: "'nexus' (default) or an LLM provider id (e.g. anthropic).",
72254
+ required: false
72255
+ },
72256
+ label: {
72257
+ type: "positional",
72258
+ description: "LLM credential label (optional when the provider has one credential).",
72259
+ required: false
72260
+ },
72261
+ "api-url": NEXUS_API_URL_ARG,
72262
+ json: { type: "boolean", description: "Output as JSON envelope" }
72263
+ },
72264
+ async run({ args }) {
72265
+ const a = args;
72266
+ const target = typeof a["target"] === "string" && a["target"] ? a["target"] : NEXUS_LOGIN_TARGET;
72267
+ if (target !== NEXUS_LOGIN_TARGET) {
72268
+ const label = typeof a["label"] === "string" && a["label"] ? a["label"] : void 0;
72269
+ const { removeLlmCredential } = await import(
72270
+ /* webpackIgnore: true */
72271
+ "@cleocode/core/llm/credential-remove-entry.js"
72272
+ );
72273
+ emitLlmCredentialRemoval(await removeLlmCredential(target, label), "logout", "logout.run");
72274
+ return;
72275
+ }
72276
+ const { logoutFromNexus } = await import(
72277
+ /* webpackIgnore: true */
72278
+ "@cleocode/core/cloud/nexus-auth.js"
72279
+ );
72280
+ let result;
72281
+ try {
72282
+ result = await logoutFromNexus({ apiUrl: nexusApiUrlArg(a) });
72283
+ } catch (err) {
72284
+ failNexus(err, "logout.run");
72285
+ }
72286
+ emitNexusResult(result, nexusLogoutSummary(result), "logout", "logout.run");
72287
+ }
72288
+ });
72289
+ }
72290
+ });
72291
+
71411
72292
  // packages/cleo/src/cli/commands/manifest.ts
71412
72293
  var manifest_exports = {};
71413
72294
  __export(manifest_exports, {
@@ -73970,10 +74851,10 @@ function applyJsonFlag2(jsonFlag) {
73970
74851
  setFormatContext({ format: "json", source: "flag", quiet: false });
73971
74852
  }
73972
74853
  }
73973
- function positiveIntFlag(value) {
74854
+ function intFlag(value, min) {
73974
74855
  if (value === void 0 || value === "") return void 0;
73975
74856
  const n = Number(value);
73976
- return Number.isInteger(n) && n > 0 ? n : null;
74857
+ return Number.isInteger(n) && n >= min ? n : null;
73977
74858
  }
73978
74859
  var initCommand3, registerCommand2, unregisterCommand, listCommand13, statusCommand11, showCommand8, resolveCommand2, discoverCommand2, augmentCommand2, setupCommand, searchCommand3, depsCommand3, criticalPathCommand2, blockingCommand, orphansCommand2, syncCommand5, reconcileCommand2, graphCommand3, shareStatusCommand, transferPreviewCommand, transferCommand, permissionSetCommand, permissionCommand, shareExportCommand, shareImportCommand, shareCommand, clustersCommand2, flowsCommand2, contextCommand4, impactCommand3, analyzeCommand3, projectsListCommand, projectsRegisterCommand, projectsRemoveCommand, projectsScanCommand, projectsCleanCommand, projectsStatusCommand, projectsCommand, refreshBridgeCommand, exportCommand6, diffCommand2, queryCommand3, routeMapCommand2, shapeCheckCommand2, fullContextCommand, taskFootprintCommand, brainAnchorsCommand, whyCommand, impactFullCommand2, conduitScanCommand, taskSymbolsCommand, searchCodeCommand2, contractsSyncCommand, contractsShowCommand, contractsLinkTasksCommand, contractsCommand, groupCommand, wikiCommand2, hotPathsCommand2, hotNodesCommand2, coldSymbolsCommand2, sigilSyncCommand, sigilListCommand, sigilCommand, topEntriesCommand, nexusCommand;
73979
74860
  var init_nexus3 = __esm({
@@ -75460,66 +76341,133 @@ The code-intelligence graph is project-scoped (ADR-090 \xB7 T11648): one store p
75460
76341
  projectsStatusCommand = defineCommand({
75461
76342
  meta: {
75462
76343
  name: "status",
75463
- description: "Git state (branch, head, dirty/untracked, upstream, ahead/behind, last fetch) of every project location on this device; bounded and parallel, no network unless --fetch"
76344
+ description: "Fleet view: every project, its location on each device, and its last recorded git state (branch, head, dirty, ahead/behind as of the last fetch) with staleness; paged, counts first. Reads recorded probes; --refresh re-probes this device (bounded), --fetch also fetches"
75464
76345
  },
75465
76346
  args: {
76347
+ refresh: {
76348
+ type: "boolean",
76349
+ description: "Re-probe this device first (bounded concurrency, per-repo timeout; no network)"
76350
+ },
75466
76351
  fetch: {
75467
76352
  type: "boolean",
75468
- description: "Run git fetch per location first (network). Off by default"
76353
+ description: "Re-probe this device with git fetch per location first (network; implies --refresh)"
76354
+ },
76355
+ device: {
76356
+ type: "string",
76357
+ description: "Only locations on this device (device id, hostname, or current)"
76358
+ },
76359
+ missing: { type: "boolean", description: "Only projects with a missing location" },
76360
+ dirty: { type: "boolean", description: "Only projects with uncommitted or untracked changes" },
76361
+ behind: {
76362
+ type: "boolean",
76363
+ description: "Only projects behind their upstream (as of last fetch)"
76364
+ },
76365
+ ahead: {
76366
+ type: "boolean",
76367
+ description: "Only projects ahead of their upstream (as of last fetch)"
76368
+ },
76369
+ stale: {
76370
+ type: "boolean",
76371
+ description: "Only projects never probed, or probed/fetched longer ago than --stale-after-ms"
76372
+ },
76373
+ errored: { type: "boolean", description: "Only projects whose last probe recorded an error" },
76374
+ limit: { type: "string", description: "Projects per page (default 50, max 500; 0 = all)" },
76375
+ offset: { type: "string", description: "Projects skipped before the page (default 0)" },
76376
+ concurrency: {
76377
+ type: "string",
76378
+ description: "With --refresh: locations probed at once (default 8, max 64)"
75469
76379
  },
75470
- concurrency: { type: "string", description: "Locations probed at once (default 8, max 64)" },
75471
76380
  "timeout-ms": {
75472
76381
  type: "string",
75473
- description: "Per-location git budget in ms (default 10000; 30000 with --fetch)"
76382
+ description: "With --refresh: per-location git budget in ms (default 10000; 30000 with --fetch)"
75474
76383
  },
75475
76384
  "stale-after-ms": {
75476
76385
  type: "string",
75477
- description: "A last fetch older than this marks remote state stale (default 86400000)"
76386
+ description: "Staleness window for probes, fetches and heartbeats (default 86400000)"
75478
76387
  },
75479
76388
  json: { type: "boolean", description: "Output as JSON (LAFS envelope format)" }
75480
76389
  },
75481
76390
  async run({ args }) {
75482
76391
  applyJsonFlag2(args.json);
75483
76392
  const startTime = Date.now();
75484
- const concurrency = positiveIntFlag(args.concurrency);
75485
- const timeoutMs = positiveIntFlag(args["timeout-ms"]);
75486
- const staleAfterMs = positiveIntFlag(args["stale-after-ms"]);
75487
- if (concurrency === null || timeoutMs === null || staleAfterMs === null) {
76393
+ const concurrency = intFlag(args.concurrency, 1);
76394
+ const timeoutMs = intFlag(args["timeout-ms"], 1);
76395
+ const staleAfterMs = intFlag(args["stale-after-ms"], 1);
76396
+ const limit = intFlag(args.limit, 0);
76397
+ const offset = intFlag(args.offset, 0);
76398
+ if ([concurrency, timeoutMs, staleAfterMs, limit, offset].includes(null)) {
75488
76399
  cliError(
75489
- "--concurrency, --timeout-ms and --stale-after-ms take positive integers",
76400
+ "--concurrency, --timeout-ms and --stale-after-ms take positive integers; --limit and --offset take integers >= 0",
75490
76401
  6,
75491
76402
  { name: "E_VALIDATION" },
75492
- { operation: "nexus.projects.status", duration_ms: 0 }
76403
+ { operation: "nexus.projects.fleet", duration_ms: 0 }
75493
76404
  );
75494
76405
  process.exitCode = 6;
75495
76406
  return;
75496
76407
  }
75497
- const response = await dispatchRaw("mutate", "nexus", "projects.status", {
75498
- fetch: args.fetch === true,
75499
- concurrency,
75500
- timeoutMs,
75501
- staleAfterMs
76408
+ const fetch2 = args.fetch === true;
76409
+ let refresh;
76410
+ if (fetch2 || args.refresh === true) {
76411
+ const probe = await dispatchRaw("mutate", "nexus", "projects.status", {
76412
+ fetch: fetch2,
76413
+ concurrency: concurrency ?? void 0,
76414
+ timeoutMs: timeoutMs ?? void 0,
76415
+ staleAfterMs: staleAfterMs ?? void 0
76416
+ });
76417
+ if (!probe.success) {
76418
+ const probeExit = probe.error?.exitCode ?? 1;
76419
+ cliError(
76420
+ probe.error?.message ?? "Unknown error",
76421
+ probeExit,
76422
+ { name: probe.error?.code ?? "E_PROJECTS_STATUS_FAILED", details: probe.error?.details },
76423
+ { operation: "nexus.projects.status", duration_ms: Date.now() - startTime }
76424
+ );
76425
+ process.exitCode = probeExit;
76426
+ return;
76427
+ }
76428
+ const probed = probe.data;
76429
+ refresh = {
76430
+ deviceId: probed.deviceId,
76431
+ count: probed.count,
76432
+ fetched: probed.fetched,
76433
+ durationMs: probed.durationMs,
76434
+ summary: probed.summary
76435
+ };
76436
+ }
76437
+ const response = await dispatchRaw("query", "nexus", "projects.fleet", {
76438
+ device: typeof args.device === "string" ? args.device : void 0,
76439
+ missing: args.missing === true,
76440
+ dirty: args.dirty === true,
76441
+ behind: args.behind === true,
76442
+ ahead: args.ahead === true,
76443
+ stale: args.stale === true,
76444
+ errored: args.errored === true,
76445
+ staleAfterMs: staleAfterMs ?? void 0,
76446
+ limit: limit ?? void 0,
76447
+ offset: offset ?? void 0
75502
76448
  });
75503
76449
  const durationMs = Date.now() - startTime;
75504
76450
  if (!response.success) {
76451
+ const code = response.error?.code ?? "E_PROJECTS_FLEET_FAILED";
76452
+ const exitCode = response.error?.exitCode ?? 1;
75505
76453
  cliError(
75506
76454
  response.error?.message ?? "Unknown error",
75507
- 1,
75508
- {
75509
- name: response.error?.code ?? "E_PROJECTS_STATUS_FAILED",
75510
- details: response.error?.details
75511
- },
75512
- { operation: "nexus.projects.status", duration_ms: durationMs }
76455
+ exitCode,
76456
+ { name: code, details: response.error?.details },
76457
+ { operation: "nexus.projects.fleet", duration_ms: durationMs }
75513
76458
  );
75514
- process.exitCode = 1;
76459
+ process.exitCode = exitCode;
75515
76460
  return;
75516
76461
  }
75517
- cliOutput(response.data, {
75518
- command: "nexus-projects-status",
75519
- operation: "nexus.projects.status",
75520
- extensions: { duration_ms: durationMs },
75521
- responseMeta: response.meta
75522
- });
76462
+ cliOutput(
76463
+ { ...response.data, ...refresh ? { refresh } : {} },
76464
+ {
76465
+ command: "nexus-projects-fleet",
76466
+ operation: "nexus.projects.fleet",
76467
+ extensions: { duration_ms: durationMs },
76468
+ responseMeta: response.meta
76469
+ }
76470
+ );
75523
76471
  }
75524
76472
  });
75525
76473
  projectsCommand = defineCommand({
@@ -77702,7 +78650,7 @@ __export(otel_exports, {
77702
78650
  otelCommand: () => otelCommand
77703
78651
  });
77704
78652
  import {
77705
- CleoError as CleoError9,
78653
+ CleoError as CleoError10,
77706
78654
  clearOtelData,
77707
78655
  formatError as formatError4,
77708
78656
  getOtelSessions,
@@ -77724,7 +78672,7 @@ var init_otel = __esm({
77724
78672
  const result = await getOtelStatus();
77725
78673
  cliOutput(result, { command: "otel" });
77726
78674
  } catch (err) {
77727
- if (err instanceof CleoError9) {
78675
+ if (err instanceof CleoError10) {
77728
78676
  cliError(formatError4(err), err.code, { name: "E_INTERNAL" });
77729
78677
  process.exit(err.code);
77730
78678
  }
@@ -77739,7 +78687,7 @@ var init_otel = __esm({
77739
78687
  const result = await getOtelSummary();
77740
78688
  cliOutput(result, { command: "otel" });
77741
78689
  } catch (err) {
77742
- if (err instanceof CleoError9) {
78690
+ if (err instanceof CleoError10) {
77743
78691
  cliError(formatError4(err), err.code, { name: "E_INTERNAL" });
77744
78692
  process.exit(err.code);
77745
78693
  }
@@ -77767,7 +78715,7 @@ var init_otel = __esm({
77767
78715
  });
77768
78716
  cliOutput(result, { command: "otel" });
77769
78717
  } catch (err) {
77770
- if (err instanceof CleoError9) {
78718
+ if (err instanceof CleoError10) {
77771
78719
  cliError(formatError4(err), err.code, { name: "E_INTERNAL" });
77772
78720
  process.exit(err.code);
77773
78721
  }
@@ -77795,7 +78743,7 @@ var init_otel = __esm({
77795
78743
  });
77796
78744
  cliOutput(result, { command: "otel" });
77797
78745
  } catch (err) {
77798
- if (err instanceof CleoError9) {
78746
+ if (err instanceof CleoError10) {
77799
78747
  cliError(formatError4(err), err.code, { name: "E_INTERNAL" });
77800
78748
  process.exit(err.code);
77801
78749
  }
@@ -77823,7 +78771,7 @@ var init_otel = __esm({
77823
78771
  });
77824
78772
  cliOutput(result, { command: "otel" });
77825
78773
  } catch (err) {
77826
- if (err instanceof CleoError9) {
78774
+ if (err instanceof CleoError10) {
77827
78775
  cliError(formatError4(err), err.code, { name: "E_INTERNAL" });
77828
78776
  process.exit(err.code);
77829
78777
  }
@@ -77838,7 +78786,7 @@ var init_otel = __esm({
77838
78786
  const result = await clearOtelData();
77839
78787
  cliOutput(result, { command: "otel" });
77840
78788
  } catch (err) {
77841
- if (err instanceof CleoError9) {
78789
+ if (err instanceof CleoError10) {
77842
78790
  cliError(formatError4(err), err.code, { name: "E_INTERNAL" });
77843
78791
  process.exit(err.code);
77844
78792
  }
@@ -78104,6 +79052,14 @@ var init_pivot = __esm({
78104
79052
  "no-blocks-from": {
78105
79053
  type: "boolean",
78106
79054
  description: "Skip adding toTaskId as a dependency on fromTaskId (advisory pivot \u2014 default is to block)"
79055
+ },
79056
+ "take-over": {
79057
+ type: "boolean",
79058
+ description: "Take over another session's EXPIRED claim on toTaskId (audited)"
79059
+ },
79060
+ "force-claim": {
79061
+ type: "boolean",
79062
+ description: "Take over another session's LIVE claim on toTaskId (audited)"
78107
79063
  }
78108
79064
  },
78109
79065
  async run({ args }) {
@@ -78115,7 +79071,9 @@ var init_pivot = __esm({
78115
79071
  fromTaskId: args.fromTaskId,
78116
79072
  toTaskId: args.toTaskId,
78117
79073
  reason: args.reason,
78118
- blocksFrom: !negatedFlag(args, "blocks-from")
79074
+ blocksFrom: !negatedFlag(args, "blocks-from"),
79075
+ ...args["take-over"] === true ? { takeOver: true } : {},
79076
+ ...args["force-claim"] === true ? { forceClaim: true } : {}
78119
79077
  },
78120
79078
  { command: "pivot" }
78121
79079
  );
@@ -78369,7 +79327,7 @@ __export(project_exports, {
78369
79327
  });
78370
79328
  import { resolve as resolve9 } from "node:path";
78371
79329
  import {
78372
- CleoError as CleoError10,
79330
+ CleoError as CleoError11,
78373
79331
  getProjectRoot as getProjectRoot65,
78374
79332
  moveProject,
78375
79333
  projectLifecycle,
@@ -78394,13 +79352,13 @@ function resolveRootOrFail(operation) {
78394
79352
  try {
78395
79353
  return getProjectRoot65();
78396
79354
  } catch (err) {
78397
- const code = err instanceof CleoError10 && typeof err.details?.["code"] === "string" ? err.details["code"] : "E_NOT_CLEO_PROJECT";
79355
+ const code = err instanceof CleoError11 && typeof err.details?.["code"] === "string" ? err.details["code"] : "E_NOT_CLEO_PROJECT";
78398
79356
  emitEngineFailure(
78399
79357
  {
78400
79358
  code,
78401
79359
  message: err.message,
78402
- exitCode: err instanceof CleoError10 ? err.code : 8 /* CONFIG_ERROR */,
78403
- fix: err instanceof CleoError10 ? err.fix : void 0
79360
+ exitCode: err instanceof CleoError11 ? err.code : 8 /* CONFIG_ERROR */,
79361
+ fix: err instanceof CleoError11 ? err.fix : void 0
78404
79362
  },
78405
79363
  operation
78406
79364
  );
@@ -78452,13 +79410,14 @@ function emitRelocation(data, operation) {
78452
79410
  { command: "project", operation }
78453
79411
  );
78454
79412
  }
78455
- var moveSubCommand, rerootSubCommand, renameSubCommand, reregisterSubCommand, projectCommand;
79413
+ var moveSubCommand, rerootSubCommand, renameSubCommand, reregisterSubCommand, linkSubCommand, projectCommand;
78456
79414
  var init_project = __esm({
78457
79415
  "packages/cleo/src/cli/commands/project.ts"() {
78458
79416
  "use strict";
78459
79417
  init_src2();
78460
79418
  init_dist();
78461
79419
  init_format_context();
79420
+ init_nexus_account_cli();
78462
79421
  init_renderers();
78463
79422
  moveSubCommand = defineCommand({
78464
79423
  meta: {
@@ -78525,7 +79484,10 @@ var init_project = __esm({
78525
79484
  }
78526
79485
  });
78527
79486
  renameSubCommand = defineCommand({
78528
- meta: { name: "rename", description: "Rename this project." },
79487
+ meta: {
79488
+ name: "rename",
79489
+ description: "Rename this project: the committed .cleo/project.json name, the registry label, and a Cleo Nexus relink hint (the id never changes)."
79490
+ },
78529
79491
  args: {
78530
79492
  newName: { type: "positional", description: "New project name.", required: true },
78531
79493
  "dry-run": { type: "boolean", description: "Validate without applying.", default: false },
@@ -78565,7 +79527,11 @@ var init_project = __esm({
78565
79527
  `Project ID: ${r.projectId}`,
78566
79528
  `Old name: ${r.oldName}`,
78567
79529
  `New name: ${r.newName}`,
78568
- `Project hash: ${r.newProjectHash}`
79530
+ `Project hash: ${r.newProjectHash}`,
79531
+ ...r.recordedIn ? [`Recorded in: .cleo/${r.recordedIn}`] : [],
79532
+ ...r.registry ? [`Registry: ${r.registry}`] : [],
79533
+ ...r.nexusLabel ? [`Nexus label: ${r.nexusLabel}`] : [],
79534
+ ...r.hint ? [`Next: ${r.hint}`] : []
78569
79535
  ]),
78570
79536
  { command: "project", operation: "project.rename" }
78571
79537
  );
@@ -78605,12 +79571,46 @@ var init_project = __esm({
78605
79571
  }
78606
79572
  }
78607
79573
  });
79574
+ linkSubCommand = defineCommand({
79575
+ meta: {
79576
+ name: "link",
79577
+ description: "Register this project with Cleo Nexus under its project id (.cleo/project-id) with its name as a plaintext label (--label overrides; never a filesystem path). Idempotent: re-linking after a rename updates the label. Requires cleo login nexus."
79578
+ },
79579
+ args: {
79580
+ label: {
79581
+ type: "string",
79582
+ description: "Name shown in Nexus, sent in plaintext (default: the project name). A name, never a path."
79583
+ },
79584
+ "api-url": NEXUS_API_URL_ARG,
79585
+ json: { type: "boolean", description: "Output raw JSON envelope.", default: false }
79586
+ },
79587
+ async run({ args }) {
79588
+ const { linkProjectToNexus } = await import(
79589
+ /* webpackIgnore: true */
79590
+ "@cleocode/core/cloud/nexus-link.js"
79591
+ );
79592
+ let result;
79593
+ try {
79594
+ result = await linkProjectToNexus({
79595
+ apiUrl: nexusApiUrlArg(args),
79596
+ ...typeof args["label"] === "string" && args["label"] ? { label: args["label"] } : {}
79597
+ });
79598
+ } catch (err) {
79599
+ failNexus(err, "project.link");
79600
+ }
79601
+ const { link } = result;
79602
+ const verb = result.alreadyLinked ? "Already linked" : "Linked";
79603
+ const summary = `${verb}: project ${link.localProjectId} as "${link.label ?? ""}" on ${link.apiUrl}.`;
79604
+ emitNexusResult(result, summary, "project", "project.link");
79605
+ }
79606
+ });
78608
79607
  projectCommand = defineCommand({
78609
79608
  meta: {
78610
79609
  name: "project",
78611
- description: "Project lifecycle management (move, reroot, rename, re-register)."
79610
+ description: "Project lifecycle management (move, reroot, rename, re-register, link)."
78612
79611
  },
78613
79612
  subCommands: {
79613
+ link: linkSubCommand,
78614
79614
  move: moveSubCommand,
78615
79615
  reroot: rerootSubCommand,
78616
79616
  rename: renameSubCommand,
@@ -79956,7 +80956,7 @@ __export(remote_exports, {
79956
80956
  });
79957
80957
  import {
79958
80958
  addRemote,
79959
- CleoError as CleoError11,
80959
+ CleoError as CleoError12,
79960
80960
  getRemoteSyncStatus as getRemoteGitStatus,
79961
80961
  listRemotes,
79962
80962
  pull,
@@ -79994,7 +80994,7 @@ var init_remote = __esm({
79994
80994
  { command: "remote", message: `Remote '${name}' added: ${args.url}` }
79995
80995
  );
79996
80996
  } catch (err) {
79997
- if (err instanceof CleoError11) {
80997
+ if (err instanceof CleoError12) {
79998
80998
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
79999
80999
  process.exit(err.code);
80000
81000
  }
@@ -80023,7 +81023,7 @@ var init_remote = __esm({
80023
81023
  { command: "remote", message: `Remote '${args.name}' removed` }
80024
81024
  );
80025
81025
  } catch (err) {
80026
- if (err instanceof CleoError11) {
81026
+ if (err instanceof CleoError12) {
80027
81027
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
80028
81028
  process.exit(err.code);
80029
81029
  }
@@ -80048,7 +81048,7 @@ var init_remote = __esm({
80048
81048
  }
80049
81049
  );
80050
81050
  } catch (err) {
80051
- if (err instanceof CleoError11) {
81051
+ if (err instanceof CleoError12) {
80052
81052
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
80053
81053
  process.exit(err.code);
80054
81054
  }
@@ -80088,7 +81088,7 @@ var init_remote = __esm({
80088
81088
  }
80089
81089
  cliOutput({ ...status }, { command: "remote", message });
80090
81090
  } catch (err) {
80091
- if (err instanceof CleoError11) {
81091
+ if (err instanceof CleoError12) {
80092
81092
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
80093
81093
  process.exit(err.code);
80094
81094
  }
@@ -80152,7 +81152,7 @@ var init_remote = __esm({
80152
81152
  { command: "push", message: result.message }
80153
81153
  );
80154
81154
  } catch (err) {
80155
- if (err instanceof CleoError11) {
81155
+ if (err instanceof CleoError12) {
80156
81156
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
80157
81157
  process.exit(err.code);
80158
81158
  }
@@ -80195,7 +81195,7 @@ var init_remote = __esm({
80195
81195
  { command: "pull", message: result.message }
80196
81196
  );
80197
81197
  } catch (err) {
80198
- if (err instanceof CleoError11) {
81198
+ if (err instanceof CleoError12) {
80199
81199
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
80200
81200
  process.exit(err.code);
80201
81201
  }
@@ -80776,7 +81776,7 @@ __export(restore_exports, {
80776
81776
  import fs3 from "node:fs";
80777
81777
  import path5 from "node:path";
80778
81778
  import {
80779
- CleoError as CleoError12,
81779
+ CleoError as CleoError13,
80780
81780
  getProjectRoot as getProjectRoot67,
80781
81781
  getTaskAccessor as getTaskAccessor3,
80782
81782
  parseConflictReport,
@@ -80907,7 +81907,7 @@ var init_restore = __esm({
80907
81907
  });
80908
81908
  if (!response.success) {
80909
81909
  const code = ExitCode[response.error?.code] ?? 1 /* GENERAL_ERROR */;
80910
- throw new CleoError12(code, response.error?.message ?? "Backup restore failed");
81910
+ throw new CleoError13(code, response.error?.message ?? "Backup restore failed");
80911
81911
  }
80912
81912
  const data = response.data;
80913
81913
  if (args["dry-run"]) {
@@ -80936,7 +81936,7 @@ var init_restore = __esm({
80936
81936
  { command: "restore", operation: "admin.backup.restore" }
80937
81937
  );
80938
81938
  } catch (err) {
80939
- if (err instanceof CleoError12) {
81939
+ if (err instanceof CleoError13) {
80940
81940
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
80941
81941
  process.exit(err.code);
80942
81942
  }
@@ -80980,7 +81980,7 @@ var init_restore = __esm({
80980
81980
  const taskId = args.taskId;
80981
81981
  const idPattern = /^T\d{3,}$/;
80982
81982
  if (!idPattern.test(taskId)) {
80983
- throw new CleoError12(2 /* INVALID_INPUT */, `Invalid task ID: ${taskId}`);
81983
+ throw new CleoError13(2 /* INVALID_INPUT */, `Invalid task ID: ${taskId}`);
80984
81984
  }
80985
81985
  const accessor = await getTaskAccessor3();
80986
81986
  const activeTask = await accessor.loadSingleTask(taskId);
@@ -81007,7 +82007,7 @@ var init_restore = __esm({
81007
82007
  const response = await dispatchRaw("mutate", "tasks", "restore", { taskId });
81008
82008
  if (!response.success) {
81009
82009
  const code = ExitCode[response.error?.code] ?? 1 /* GENERAL_ERROR */;
81010
- throw new CleoError12(code, response.error?.message ?? "Task restore failed");
82010
+ throw new CleoError13(code, response.error?.message ?? "Task restore failed");
81011
82011
  }
81012
82012
  const resultData = response.data;
81013
82013
  cliOutput(
@@ -81049,7 +82049,7 @@ var init_restore = __esm({
81049
82049
  });
81050
82050
  if (!response.success) {
81051
82051
  const code = ExitCode[response.error?.code] ?? 1 /* GENERAL_ERROR */;
81052
- throw new CleoError12(code, response.error?.message ?? "Task restore failed");
82052
+ throw new CleoError13(code, response.error?.message ?? "Task restore failed");
81053
82053
  }
81054
82054
  const resultData = response.data;
81055
82055
  cliOutput(
@@ -81064,7 +82064,7 @@ var init_restore = __esm({
81064
82064
  );
81065
82065
  return;
81066
82066
  } else {
81067
- throw new CleoError12(
82067
+ throw new CleoError13(
81068
82068
  6 /* VALIDATION_ERROR */,
81069
82069
  `Task ${taskId} is already active with status: ${activeTask.status}`
81070
82070
  );
@@ -81096,7 +82096,7 @@ var init_restore = __esm({
81096
82096
  }
81097
82097
  }
81098
82098
  }
81099
- throw new CleoError12(
82099
+ throw new CleoError13(
81100
82100
  4 /* NOT_FOUND */,
81101
82101
  `Task ${taskId} not found in active tasks or archive`,
81102
82102
  {
@@ -81114,7 +82114,7 @@ var init_restore = __esm({
81114
82114
  });
81115
82115
  if (!response.success) {
81116
82116
  const code = ExitCode[response.error?.code] ?? 1 /* GENERAL_ERROR */;
81117
- throw new CleoError12(code, response.error?.message ?? "Task unarchive failed");
82117
+ throw new CleoError13(code, response.error?.message ?? "Task unarchive failed");
81118
82118
  }
81119
82119
  const resultData = response.data;
81120
82120
  cliOutput(
@@ -81128,7 +82128,7 @@ var init_restore = __esm({
81128
82128
  { command: "restore", operation: "tasks.restore" }
81129
82129
  );
81130
82130
  } catch {
81131
- throw new CleoError12(
82131
+ throw new CleoError13(
81132
82132
  4 /* NOT_FOUND */,
81133
82133
  `Task ${taskId} not found in active tasks or archive`,
81134
82134
  {
@@ -81137,7 +82137,7 @@ var init_restore = __esm({
81137
82137
  );
81138
82138
  }
81139
82139
  } catch (err) {
81140
- if (err instanceof CleoError12) {
82140
+ if (err instanceof CleoError13) {
81141
82141
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
81142
82142
  process.exit(err.code);
81143
82143
  }
@@ -81947,7 +82947,7 @@ import * as readline3 from "node:readline";
81947
82947
  import { promisify } from "node:util";
81948
82948
  import {
81949
82949
  BUILD_CONFIG as BUILD_CONFIG3,
81950
- CleoError as CleoError13,
82950
+ CleoError as CleoError14,
81951
82951
  checkAllRegisteredProjects as checkAllRegisteredProjects2,
81952
82952
  checkStorageMigration,
81953
82953
  getCleoHome as getCleoHome7,
@@ -82283,7 +83283,7 @@ var init_self_update = __esm({
82283
83283
  const currentVersion = isDev ? await getCurrentVersion() : await getNpmInstalledVersion() ?? await getCurrentVersion();
82284
83284
  const rawChannel = args.channel?.toLowerCase();
82285
83285
  if (rawChannel && rawChannel !== "stable" && rawChannel !== "beta") {
82286
- throw new CleoError13(
83286
+ throw new CleoError14(
82287
83287
  6 /* VALIDATION_ERROR */,
82288
83288
  `Invalid --channel '${rawChannel}'. Expected stable|beta`
82289
83289
  );
@@ -82325,7 +83325,7 @@ var init_self_update = __esm({
82325
83325
  progress.step(2, "Querying npm registry");
82326
83326
  const latest2 = await getDistTagVersion(requestedChannel === "beta" ? "beta" : "latest");
82327
83327
  if (!latest2) {
82328
- throw new CleoError13(
83328
+ throw new CleoError14(
82329
83329
  5 /* DEPENDENCY_ERROR */,
82330
83330
  "Failed to check latest version from GitHub"
82331
83331
  );
@@ -82355,7 +83355,7 @@ var init_self_update = __esm({
82355
83355
  progress.step(2, "Querying npm registry");
82356
83356
  const latest = args.version ?? await getDistTagVersion(requestedChannel === "beta" ? "beta" : "latest");
82357
83357
  if (!latest) {
82358
- throw new CleoError13(
83358
+ throw new CleoError14(
82359
83359
  5 /* DEPENDENCY_ERROR */,
82360
83360
  "Failed to check latest version from GitHub"
82361
83361
  );
@@ -82400,7 +83400,7 @@ var init_self_update = __esm({
82400
83400
  });
82401
83401
  progress.complete(`Updated to ${latest}`);
82402
83402
  } catch (err) {
82403
- if (err instanceof CleoError13) {
83403
+ if (err instanceof CleoError14) {
82404
83404
  progress.error(err.message);
82405
83405
  cliError(
82406
83406
  err.message,
@@ -84617,7 +85617,7 @@ var init_setup = __esm({
84617
85617
  args: {
84618
85618
  section: {
84619
85619
  type: "string",
84620
- description: "Run only one named section. Valid: llm | system-one | models-roles | identity | sentient | harness | brain | project-conventions | integrations | telemetry | verification"
85620
+ description: "Run only one named section. Valid: llm | system-one | models-roles | identity | sentient | harness | brain | project-conventions | integrations | nexus-account | telemetry | verification"
84621
85621
  },
84622
85622
  "non-interactive": {
84623
85623
  type: "boolean",
@@ -85923,13 +86923,21 @@ var init_start = __esm({
85923
86923
  startCommand8 = defineCommand({
85924
86924
  meta: {
85925
86925
  name: "start",
85926
- description: "Start working on a task (sets it as the current task in the active session)"
86926
+ description: "Start working on a task (sets it as the current task and takes your session's claim lease)"
85927
86927
  },
85928
86928
  args: {
85929
86929
  taskId: {
85930
86930
  type: "positional",
85931
86931
  description: "ID of the task to start",
85932
86932
  required: true
86933
+ },
86934
+ "take-over": {
86935
+ type: "boolean",
86936
+ description: "Take over another session's EXPIRED claim lease on the task (audited)"
86937
+ },
86938
+ "force-claim": {
86939
+ type: "boolean",
86940
+ description: "Take over another session's LIVE claim lease on the task (audited)"
85933
86941
  }
85934
86942
  },
85935
86943
  async run({ args }) {
@@ -85937,7 +86945,11 @@ var init_start = __esm({
85937
86945
  "mutate",
85938
86946
  "tasks",
85939
86947
  "start",
85940
- { taskId: args.taskId },
86948
+ {
86949
+ taskId: args.taskId,
86950
+ ...args["take-over"] === true ? { takeOver: true } : {},
86951
+ ...args["force-claim"] === true ? { forceClaim: true } : {}
86952
+ },
85941
86953
  { command: "start" }
85942
86954
  );
85943
86955
  }
@@ -86102,6 +87114,12 @@ function renderStatusHuman(status) {
86102
87114
  );
86103
87115
  const killSwitchBadge = status.daemon.killSwitchActive ? `${RED}${BOLD}ACTIVE${NC}` : `${DIM}inactive${NC}`;
86104
87116
  lines.push(` ${DIM}killSwitchActive:${NC} ${killSwitchBadge}`);
87117
+ lines.push("");
87118
+ lines.push(`${BOLD}Cleo Nexus account${NC}`);
87119
+ for (const n of status.nexusAccount) {
87120
+ const color = n.state === "signed-in" ? GREEN : n.state === "not-signed-in" ? DIM : YELLOW;
87121
+ lines.push(` ${DIM}${n.apiUrl}:${NC} ${color}${n.summary}${NC}`);
87122
+ }
86105
87123
  return lines.join("\n");
86106
87124
  }
86107
87125
  function computeExitCode(status) {
@@ -86152,7 +87170,7 @@ var sticky_exports = {};
86152
87170
  __export(sticky_exports, {
86153
87171
  stickyCommand: () => stickyCommand
86154
87172
  });
86155
- import { CleoError as CleoError14 } from "@cleocode/core";
87173
+ import { CleoError as CleoError15 } from "@cleocode/core";
86156
87174
  var addCommand12, listCommand25, showCommand15, convertCommand, archiveCommand4, purgeCommand2, stickyCommand;
86157
87175
  var init_sticky3 = __esm({
86158
87176
  "packages/cleo/src/cli/commands/sticky.ts"() {
@@ -86201,7 +87219,7 @@ var init_sticky3 = __esm({
86201
87219
  { command: "sticky", operation: "sticky.add" }
86202
87220
  );
86203
87221
  } catch (err) {
86204
- if (err instanceof CleoError14) {
87222
+ if (err instanceof CleoError15) {
86205
87223
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
86206
87224
  process.exit(err.code);
86207
87225
  }
@@ -86256,7 +87274,7 @@ var init_sticky3 = __esm({
86256
87274
  }
86257
87275
  cliOutput(data, { command: "sticky list", operation: "sticky.list", page: response.page });
86258
87276
  } catch (err) {
86259
- if (err instanceof CleoError14) {
87277
+ if (err instanceof CleoError15) {
86260
87278
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
86261
87279
  process.exit(err.code);
86262
87280
  }
@@ -86297,7 +87315,7 @@ var init_sticky3 = __esm({
86297
87315
  }
86298
87316
  cliOutput({ sticky: data }, { command: "sticky show", operation: "sticky.show" });
86299
87317
  } catch (err) {
86300
- if (err instanceof CleoError14) {
87318
+ if (err instanceof CleoError15) {
86301
87319
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
86302
87320
  process.exit(err.code);
86303
87321
  }
@@ -86375,7 +87393,7 @@ var init_sticky3 = __esm({
86375
87393
  operation: "sticky.convert"
86376
87394
  });
86377
87395
  } catch (err) {
86378
- if (err instanceof CleoError14) {
87396
+ if (err instanceof CleoError15) {
86379
87397
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
86380
87398
  process.exit(err.code);
86381
87399
  }
@@ -86402,7 +87420,7 @@ var init_sticky3 = __esm({
86402
87420
  { command: "sticky archive", operation: "sticky.archive" }
86403
87421
  );
86404
87422
  } catch (err) {
86405
- if (err instanceof CleoError14) {
87423
+ if (err instanceof CleoError15) {
86406
87424
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
86407
87425
  process.exit(err.code);
86408
87426
  }
@@ -86429,7 +87447,7 @@ var init_sticky3 = __esm({
86429
87447
  { command: "sticky purge", operation: "sticky.purge" }
86430
87448
  );
86431
87449
  } catch (err) {
86432
- if (err instanceof CleoError14) {
87450
+ if (err instanceof CleoError15) {
86433
87451
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
86434
87452
  process.exit(err.code);
86435
87453
  }
@@ -89513,6 +90531,10 @@ var init_update = __esm({
89513
90531
  type: "string",
89514
90532
  description: "Fail with E_CONFLICT unless the task updatedAt still equals this value (optimistic concurrency; T12503)"
89515
90533
  },
90534
+ "if-match": {
90535
+ type: "string",
90536
+ description: "Fail with E_CONFLICT unless the task updatedAt still equals this version (from cleo show). Same as --expected-updated-at."
90537
+ },
89516
90538
  "depends-waiver": {
89517
90539
  type: "string",
89518
90540
  description: "Non-empty justification to waive the dependency requirement for --priority critical. Stored verbatim in the transactional task update audit log. Rejected for non-critical updates. (gh-405 / T1856)"
@@ -89722,8 +90744,8 @@ var init_update = __esm({
89722
90744
  if (args.severity !== void 0) params["severity"] = args.severity;
89723
90745
  if (args.reason !== void 0) params["reason"] = args.reason;
89724
90746
  if (args["depends-waiver"] !== void 0) params["dependsWaiver"] = args["depends-waiver"];
89725
- if (args["expected-updated-at"] !== void 0)
89726
- params["expectedUpdatedAt"] = args["expected-updated-at"];
90747
+ const ifMatch = args["if-match"] ?? args["expected-updated-at"];
90748
+ if (ifMatch !== void 0) params["expectedUpdatedAt"] = ifMatch;
89727
90749
  await dispatchFromCli("mutate", "tasks", "update", params, { command: "update" });
89728
90750
  }
89729
90751
  });
@@ -89736,7 +90758,7 @@ __export(upgrade_exports, {
89736
90758
  upgradeCommand: () => upgradeCommand
89737
90759
  });
89738
90760
  import { resolve as resolve11 } from "node:path";
89739
- import { CleoError as CleoError15, diagnoseUpgrade, runUpgrade as runUpgrade2, upgradeWorkflows as upgradeWorkflows2 } from "@cleocode/core/internal";
90761
+ import { CleoError as CleoError16, diagnoseUpgrade, runUpgrade as runUpgrade2, upgradeWorkflows as upgradeWorkflows2 } from "@cleocode/core/internal";
89740
90762
  var workflowsSubcommand, upgradeCommand;
89741
90763
  var init_upgrade3 = __esm({
89742
90764
  "packages/cleo/src/cli/commands/upgrade.ts"() {
@@ -89804,7 +90826,7 @@ var init_upgrade3 = __esm({
89804
90826
  process.exit(1);
89805
90827
  }
89806
90828
  } catch (err) {
89807
- if (err instanceof CleoError15) {
90829
+ if (err instanceof CleoError16) {
89808
90830
  cliError(err.message, err.code, { name: "CleoError", fix: err.fix });
89809
90831
  process.exit(err.code);
89810
90832
  }
@@ -89930,7 +90952,7 @@ var init_upgrade3 = __esm({
89930
90952
  }
89931
90953
  progress.complete(isDryRun ? "Preview complete" : "Upgrade complete");
89932
90954
  } catch (err) {
89933
- if (err instanceof CleoError15) {
90955
+ if (err instanceof CleoError16) {
89934
90956
  progress.error(err.message);
89935
90957
  cliError(
89936
90958
  err.message,
@@ -90110,7 +91132,7 @@ __export(web_exports, {
90110
91132
  });
90111
91133
  import { spawn as spawn4 } from "node:child_process";
90112
91134
  import { rm as rm2 } from "node:fs/promises";
90113
- import { CleoError as CleoError16, formatError as formatError5 } from "@cleocode/core";
91135
+ import { CleoError as CleoError17, formatError as formatError5 } from "@cleocode/core";
90114
91136
  function buildStudioUrl(port, host) {
90115
91137
  return `http://${host}:${port}`;
90116
91138
  }
@@ -90167,7 +91189,7 @@ var init_web = __esm({
90167
91189
  { command: "web", message: `CLEO Web UI running on port ${ctx.port}` }
90168
91190
  );
90169
91191
  } catch (err) {
90170
- if (err instanceof CleoError16) {
91192
+ if (err instanceof CleoError17) {
90171
91193
  console.error(formatError5(err));
90172
91194
  process.exit(err.code);
90173
91195
  }
@@ -90199,7 +91221,7 @@ var init_web = __esm({
90199
91221
  });
90200
91222
  cliOutput({ stopped: true }, { command: "web", message: "CLEO Web UI stopped" });
90201
91223
  } catch (err) {
90202
- if (err instanceof CleoError16) {
91224
+ if (err instanceof CleoError17) {
90203
91225
  console.error(formatError5(err));
90204
91226
  process.exit(err.code);
90205
91227
  }
@@ -90253,7 +91275,7 @@ var init_web = __esm({
90253
91275
  { command: "web", message: `CLEO Web UI running on port ${ctx.port}` }
90254
91276
  );
90255
91277
  } catch (err) {
90256
- if (err instanceof CleoError16) {
91278
+ if (err instanceof CleoError17) {
90257
91279
  console.error(formatError5(err));
90258
91280
  process.exit(err.code);
90259
91281
  }
@@ -90268,7 +91290,7 @@ var init_web = __esm({
90268
91290
  const status = await getWebStatus();
90269
91291
  cliOutput(status, { command: "web" });
90270
91292
  } catch (err) {
90271
- if (err instanceof CleoError16) {
91293
+ if (err instanceof CleoError17) {
90272
91294
  console.error(formatError5(err));
90273
91295
  process.exit(err.code);
90274
91296
  }
@@ -90282,7 +91304,7 @@ var init_web = __esm({
90282
91304
  try {
90283
91305
  const status = await getWebStatus();
90284
91306
  if (!status.running || !status.url) {
90285
- throw new CleoError16(
91307
+ throw new CleoError17(
90286
91308
  1 /* GENERAL_ERROR */,
90287
91309
  "Web server is not running. Start with: cleo web start"
90288
91310
  );
@@ -90291,7 +91313,7 @@ var init_web = __esm({
90291
91313
  openBrowser(url);
90292
91314
  cliOutput({ url }, { command: "web", message: `Open browser to: ${url}` });
90293
91315
  } catch (err) {
90294
- if (err instanceof CleoError16) {
91316
+ if (err instanceof CleoError17) {
90295
91317
  console.error(formatError5(err));
90296
91318
  process.exit(err.code);
90297
91319
  }
@@ -91106,13 +92128,13 @@ var COMMAND_MANIFEST = [
91106
92128
  {
91107
92129
  exportName: "claimCommand",
91108
92130
  name: "claim",
91109
- description: "Claim a task by assigning it to an agent",
92131
+ description: "Take, renew (--renew) or override (--take-over expired, --force-claim live) your session's leased claim on a task",
91110
92132
  load: async () => (await Promise.resolve().then(() => (init_claim(), claim_exports))).claimCommand
91111
92133
  },
91112
92134
  {
91113
92135
  exportName: "unclaimCommand",
91114
92136
  name: "unclaim",
91115
- description: "Unclaim a task by removing its current assignee",
92137
+ description: "Release your session's claim lease on a task (--force-claim: another session's)",
91116
92138
  load: async () => (await Promise.resolve().then(() => (init_claim(), claim_exports))).unclaimCommand
91117
92139
  },
91118
92140
  {
@@ -91202,7 +92224,7 @@ var COMMAND_MANIFEST = [
91202
92224
  {
91203
92225
  exportName: "decideCommand",
91204
92226
  name: "decide",
91205
- description: "System One integration (typed decisions): decide config (provider + API key; wizard on a terminal), decide status (reachability probe), decide ask (one debug question), decide sites (the registered decision sites). Unconfigured means heuristics answer.",
92227
+ description: "System One integration (typed decisions): decide config (named provider profile + API key; wizard on a terminal), decide use (switch the active profile), decide profiles (list them), decide status (reachability probe), decide ask (one debug question), decide sites (the registered decision sites), decide bench (accuracy benchmark). Unconfigured means heuristics answer.",
91206
92228
  load: async () => (await Promise.resolve().then(() => (init_decide2(), decide_exports))).decideCommand
91207
92229
  },
91208
92230
  {
@@ -91346,7 +92368,7 @@ var COMMAND_MANIFEST = [
91346
92368
  {
91347
92369
  exportName: "doctorProjectIdentityCommand",
91348
92370
  name: "project-identity",
91349
- description: "Check .cleo/project-id against project-info.json (missing / conflict / invalid / untracked) ",
92371
+ description: "Check .cleo/project.json, its legacy .cleo/project-id mirror and the project-info.json cache ",
91350
92372
  load: async () => (await Promise.resolve().then(() => (init_doctor_project_identity(), doctor_project_identity_exports))).doctorProjectIdentityCommand
91351
92373
  },
91352
92374
  {
@@ -91628,9 +92650,15 @@ var COMMAND_MANIFEST = [
91628
92650
  {
91629
92651
  exportName: "loginCommand",
91630
92652
  name: "login",
91631
- description: "Log in to an LLM provider and bind a usable profile in one step. Picks a provider + auth method (browser OAuth or API key), selects a model, binds it, and validates the binding. cleo auth login and cleo llm login resolve to this same flow. Prompts/URLs go to stderr; the result is a human line on a terminal or a JSON envelope when piped / --json.",
92653
+ description: "Log in to a Cleo Nexus account (cleo login nexus: device code, --api-url, --no-browser) or to an LLM provider, binding a usable profile in one step. The picker lists the Cleo Nexus account first, then the providers. For a provider it picks an auth method (browser OAuth or API key), selects a model, binds it, and validates the binding. cleo auth login and cleo llm login resolve to this same flow. Prompts/URLs go to stderr; the result is a human line on a terminal or a JSON envelope when piped / --json.",
91632
92654
  load: async () => (await Promise.resolve().then(() => (init_login(), login_exports))).loginCommand
91633
92655
  },
92656
+ {
92657
+ exportName: "logoutCommand",
92658
+ name: "logout",
92659
+ description: "Sign out. cleo logout [nexus] revokes the Cleo Nexus session server-side and deletes the stored token; cleo logout <provider> [label] removes an LLM credential exactly like cleo auth remove (the label may be omitted when the provider has one credential).",
92660
+ load: async () => (await Promise.resolve().then(() => (init_logout(), logout_exports))).logoutCommand
92661
+ },
91634
92662
  {
91635
92663
  exportName: "manifestCommand",
91636
92664
  name: "manifest",
@@ -91718,7 +92746,7 @@ var COMMAND_MANIFEST = [
91718
92746
  {
91719
92747
  exportName: "projectCommand",
91720
92748
  name: "project",
91721
- description: "Project lifecycle management (move, reroot, rename, re-register).",
92749
+ description: "Project lifecycle management (move, reroot, rename, re-register, link).",
91722
92750
  load: async () => (await Promise.resolve().then(() => (init_project(), project_exports))).projectCommand
91723
92751
  },
91724
92752
  {
@@ -91916,7 +92944,7 @@ var COMMAND_MANIFEST = [
91916
92944
  {
91917
92945
  exportName: "startCommand",
91918
92946
  name: "start",
91919
- description: "Start working on a task (sets it as the current task in the active session)",
92947
+ description: "Start working on a task (sets it as the current task and takes your session's claim lease)",
91920
92948
  load: async () => (await Promise.resolve().then(() => (init_start(), start_exports))).startCommand
91921
92949
  },
91922
92950
  {
@@ -92305,7 +93333,14 @@ var INTERACTIVE_COMMAND_PATHS = [
92305
93333
  ["init"],
92306
93334
  // The `cleo tui` cockpit (T11933) is a real-time keyboard-first terminal
92307
93335
  // client — human-default on a TTY, JSON when piped/non-TTY.
92308
- ["tui"]
93336
+ ["tui"],
93337
+ // System One setup and debugging (T12733): the wizard, the reachability
93338
+ // probe, one debug question and the profile list are read by a person.
93339
+ ["decide", "config"],
93340
+ ["decide", "status"],
93341
+ ["decide", "ask"],
93342
+ ["decide", "profiles"],
93343
+ ["decide", "use"]
92309
93344
  ];
92310
93345
  function isInteractiveInvocation(argv) {
92311
93346
  const positionals = argv.filter((token) => !token.startsWith("-"));
@@ -92555,9 +93590,10 @@ async function runMainWithLafsEnvelope(cmd, rawArgs, showUsage2) {
92555
93590
  const collector = new WarningCollector();
92556
93591
  await withWarningCollector(collector, async () => {
92557
93592
  const { recordProjectEncounter, setProjectMovedRefusal } = await import("@cleocode/core/internal");
92558
- if (rawArgs.find((a) => !a.startsWith("-")) === "doctor") setProjectMovedRefusal(false);
93593
+ const isDoctor = rawArgs.find((a) => !a.startsWith("-")) === "doctor";
93594
+ if (isDoctor) setProjectMovedRefusal(false);
92559
93595
  try {
92560
- const outcome = await recordProjectEncounter();
93596
+ const outcome = await recordProjectEncounter(void 0, { markOpened: !isDoctor });
92561
93597
  if (process.env["CLEO_DEBUG"])
92562
93598
  process.stderr.write(`[cleo][debug] Project encounter: ${outcome}
92563
93599
  `);
@@ -92624,6 +93660,7 @@ async function runStartupMaintenance() {
92624
93660
  getLogger: getLogger22,
92625
93661
  getProjectRoot: getProjectRoot73,
92626
93662
  isCleanupMarkerSet,
93663
+ isMissingProjectError,
92627
93664
  migrateSignaldockToConduit,
92628
93665
  needsSignaldockToConduitMigration,
92629
93666
  setCleanupMarker,
@@ -92663,7 +93700,8 @@ async function runStartupMaintenance() {
92663
93700
  }
92664
93701
  } catch (err) {
92665
93702
  const msg = err instanceof Error ? err.message : String(err);
92666
- if (msg.includes("E_NO_PROJECT") || msg.startsWith("Run cleo init")) {
93703
+ if (isMissingProjectError(err)) {
93704
+ _startupLog.debug({ error: msg }, "T310 migration startup check: no project, skipped");
92667
93705
  } else {
92668
93706
  _startupLog.warn(
92669
93707
  { error: msg },