@cleocode/cleo 2026.4.153 → 2026.4.154

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -3783,6 +3783,17 @@ var init_registry = __esm({
3783
3783
  requiredParams: [],
3784
3784
  params: []
3785
3785
  },
3786
+ {
3787
+ gateway: "query",
3788
+ domain: "check",
3789
+ operation: "test.coverage",
3790
+ description: "check.test.coverage (query) \u2014 dedicated test coverage sub-op (T1434)",
3791
+ tier: 1,
3792
+ idempotent: true,
3793
+ sessionRequired: false,
3794
+ requiredParams: [],
3795
+ params: []
3796
+ },
3786
3797
  {
3787
3798
  gateway: "query",
3788
3799
  domain: "check",
@@ -17929,6 +17940,7 @@ var init_check = __esm({
17929
17940
  "compliance.summary",
17930
17941
  "workflow.compliance",
17931
17942
  "test",
17943
+ "test.coverage",
17932
17944
  "coherence",
17933
17945
  "gate.status",
17934
17946
  "archive.stats",
@@ -18701,197 +18713,375 @@ function mimeFromPath(filePath) {
18701
18713
  if (lower.endsWith(".css")) return "text/css";
18702
18714
  return "application/octet-stream";
18703
18715
  }
18704
- var DocsHandler;
18716
+ function docsEnvelopeToResponse(envelope, gateway, operation, startTime) {
18717
+ if (!envelope.success) {
18718
+ return {
18719
+ meta: dispatchMeta(gateway, "docs", operation, startTime),
18720
+ success: false,
18721
+ error: {
18722
+ code: String(envelope.error?.code ?? "E_INTERNAL"),
18723
+ message: envelope.error?.message ?? "Unknown error"
18724
+ }
18725
+ };
18726
+ }
18727
+ let attachmentBackend;
18728
+ let responseData = envelope.data;
18729
+ if (responseData !== null && responseData !== void 0 && typeof responseData === "object") {
18730
+ const dataObj = responseData;
18731
+ if ("attachmentBackend" in dataObj && dataObj["attachmentBackend"] !== void 0) {
18732
+ attachmentBackend = dataObj["attachmentBackend"];
18733
+ const { attachmentBackend: _lifted, ...cleanData } = dataObj;
18734
+ responseData = cleanData;
18735
+ }
18736
+ }
18737
+ return {
18738
+ meta: {
18739
+ ...dispatchMeta(gateway, "docs", operation, startTime),
18740
+ ...attachmentBackend !== void 0 ? { attachmentBackend } : {}
18741
+ },
18742
+ success: true,
18743
+ data: responseData
18744
+ };
18745
+ }
18746
+ var _docsTypedHandler, QUERY_OPS3, MUTATE_OPS3, DocsHandler;
18705
18747
  var init_docs = __esm({
18706
18748
  "packages/cleo/src/dispatch/domains/docs.ts"() {
18707
18749
  "use strict";
18750
+ init_typed();
18708
18751
  init_base();
18709
18752
  init_meta2();
18753
+ _docsTypedHandler = defineTypedHandler("docs", {
18754
+ // ── docs.list ──────────────────────────────────────────────────────────────
18755
+ list: async (params) => {
18756
+ const ownerId = params.task ?? params.session ?? params.observation;
18757
+ if (!ownerId) {
18758
+ return lafsError(
18759
+ "E_INVALID_INPUT",
18760
+ "Provide one of --task, --session, or --observation to scope the list.",
18761
+ "list"
18762
+ );
18763
+ }
18764
+ const ownerType = inferOwnerType(ownerId);
18765
+ const store = createAttachmentStore();
18766
+ const attachments = await store.listByOwner(ownerType, ownerId);
18767
+ const backend = await resolveAttachmentBackend();
18768
+ return lafsSuccess(
18769
+ {
18770
+ ownerId,
18771
+ ownerType,
18772
+ count: attachments.length,
18773
+ attachments: attachments.map((m) => ({
18774
+ id: m.id,
18775
+ sha256: `${m.sha256.slice(0, 8)}\u2026`,
18776
+ // Cast: contracts AttachmentKind doesn't include 'llmtxt-doc' (contracts gap T1529)
18777
+ kind: m.attachment.kind,
18778
+ mime: m.attachment.kind === "local-file" || m.attachment.kind === "blob" ? m.attachment.mime : m.attachment.mime ?? "\u2014",
18779
+ size: m.attachment.kind === "local-file" || m.attachment.kind === "blob" ? m.attachment.size : void 0,
18780
+ description: m.attachment.description,
18781
+ labels: m.attachment.labels,
18782
+ createdAt: m.createdAt,
18783
+ refCount: m.refCount
18784
+ })),
18785
+ // Cast: core returns 'llmtxt'|'legacy'; contracts uses 'legacy'|'llmstxt-v2' (drift T1529)
18786
+ attachmentBackend: backend
18787
+ },
18788
+ "list"
18789
+ );
18790
+ },
18791
+ // ── docs.generate ──────────────────────────────────────────────────────────
18792
+ generate: async (params) => {
18793
+ const forId = params.for;
18794
+ if (!forId) {
18795
+ return lafsError("E_INVALID_INPUT", "--for <taskId|epicId> is required", "generate");
18796
+ }
18797
+ const cwd = getProjectRoot4();
18798
+ const result = await generateDocsLlmsTxt({ ownerId: forId, cwd });
18799
+ let attachmentId;
18800
+ let attachmentSha256;
18801
+ if (params.attach) {
18802
+ const store = createAttachmentStore();
18803
+ const ownerType = inferOwnerType(forId);
18804
+ const contentBytes = Buffer.from(result.content, "utf-8");
18805
+ const llmsTxtDescriptor = {
18806
+ kind: "llms-txt",
18807
+ source: "generated",
18808
+ content: result.content,
18809
+ description: `llms.txt for ${forId} (${result.attachmentCount} docs)`,
18810
+ labels: ["llms-txt", "generated"]
18811
+ };
18812
+ const meta = await store.put(
18813
+ contentBytes,
18814
+ llmsTxtDescriptor,
18815
+ ownerType,
18816
+ forId,
18817
+ "cleo-docs-generate",
18818
+ cwd
18819
+ );
18820
+ attachmentId = meta.id;
18821
+ attachmentSha256 = meta.sha256;
18822
+ }
18823
+ return lafsSuccess(
18824
+ {
18825
+ forId,
18826
+ content: result.content,
18827
+ attachmentCount: result.attachmentCount,
18828
+ usedLlmtxtPackage: result.usedLlmtxtPackage,
18829
+ ...attachmentId !== void 0 ? {
18830
+ attached: true,
18831
+ attachmentId,
18832
+ attachmentSha256
18833
+ } : { attached: false }
18834
+ },
18835
+ "generate"
18836
+ );
18837
+ },
18838
+ // ── docs.fetch ─────────────────────────────────────────────────────────────
18839
+ fetch: async (params) => {
18840
+ const ref = params.attachmentRef;
18841
+ if (!ref) {
18842
+ return lafsError(
18843
+ "E_INVALID_INPUT",
18844
+ "attachmentRef is required (attachment ID or SHA-256 hex)",
18845
+ "fetch"
18846
+ );
18847
+ }
18848
+ const store = createAttachmentStore();
18849
+ const isSha256 = /^[0-9a-f]{64}$/i.test(ref);
18850
+ let fetchResult = null;
18851
+ let metadata = await store.getMetadata(ref);
18852
+ if (metadata) {
18853
+ fetchResult = await store.get(metadata.sha256);
18854
+ } else if (isSha256) {
18855
+ fetchResult = await store.get(ref);
18856
+ if (fetchResult) {
18857
+ metadata = fetchResult.metadata;
18858
+ }
18859
+ }
18860
+ if (!fetchResult || !metadata) {
18861
+ return lafsError("E_NOT_FOUND", `Attachment not found: ${ref}`, "fetch");
18862
+ }
18863
+ const cwd = getProjectRoot4();
18864
+ const cleoDir = getCleoDirAbsolute(cwd);
18865
+ let storagePath;
18866
+ if (metadata.attachment.kind === "local-file") {
18867
+ storagePath = metadata.attachment.path;
18868
+ } else if (metadata.attachment.kind === "blob") {
18869
+ const prefix = metadata.sha256.slice(0, 2);
18870
+ const rest = metadata.sha256.slice(2);
18871
+ const extMap = {
18872
+ "text/markdown": ".md",
18873
+ "text/plain": ".txt",
18874
+ "application/json": ".json",
18875
+ "application/pdf": ".pdf"
18876
+ };
18877
+ const mime = metadata.attachment.kind === "blob" ? metadata.attachment.mime : "application/octet-stream";
18878
+ const ext = extMap[mime] ?? ".bin";
18879
+ storagePath = resolve2(cleoDir, "attachments", "sha256", prefix, `${rest}${ext}`);
18880
+ }
18881
+ const MAX_INLINE = 1024 * 1024;
18882
+ const bytesBase64 = fetchResult.bytes.length <= MAX_INLINE ? fetchResult.bytes.toString("base64") : void 0;
18883
+ const backend = await resolveAttachmentBackend();
18884
+ return lafsSuccess(
18885
+ {
18886
+ // Cast: core's AttachmentMetadata vs contracts/operations/docs AttachmentMetadata differ
18887
+ // in their nested attachment field structure (T1529 contracts gap). The runtime
18888
+ // value is compatible for all callers; the two interfaces diverged at the type level.
18889
+ metadata,
18890
+ path: storagePath,
18891
+ sizeBytes: fetchResult.bytes.length,
18892
+ ...bytesBase64 !== void 0 ? { bytesBase64 } : {},
18893
+ inlined: bytesBase64 !== void 0,
18894
+ // Cast: core returns 'llmtxt'|'legacy'; contracts uses 'legacy'|'llmstxt-v2' (T1529)
18895
+ attachmentBackend: backend
18896
+ },
18897
+ "fetch"
18898
+ );
18899
+ },
18900
+ // ── docs.add ───────────────────────────────────────────────────────────────
18901
+ add: async (params) => {
18902
+ const {
18903
+ ownerId,
18904
+ file: filePath,
18905
+ url,
18906
+ desc: description,
18907
+ labels: rawLabels,
18908
+ attachedBy: rawAttachedBy
18909
+ } = params;
18910
+ if (!ownerId) {
18911
+ return lafsError("E_INVALID_INPUT", "ownerId is required", "add");
18912
+ }
18913
+ if (!filePath && !url) {
18914
+ return lafsError(
18915
+ "E_INVALID_INPUT",
18916
+ "Provide either a file path (positional or --file) or --url",
18917
+ "add"
18918
+ );
18919
+ }
18920
+ const labels = parseLabels(rawLabels);
18921
+ const attachedBy = rawAttachedBy ?? "human";
18922
+ const ownerType = inferOwnerType(ownerId);
18923
+ const store = createAttachmentStore();
18924
+ if (filePath) {
18925
+ const absPath = resolve2(filePath);
18926
+ let bytes;
18927
+ try {
18928
+ bytes = await readFile(absPath);
18929
+ } catch {
18930
+ return lafsError("E_FILE_ERROR", `Cannot read file: ${absPath}`, "add");
18931
+ }
18932
+ const mime = mimeFromPath(absPath);
18933
+ const attachment = {
18934
+ kind: "local-file",
18935
+ path: absPath,
18936
+ mime,
18937
+ size: bytes.length,
18938
+ ...description ? { description } : {},
18939
+ ...labels ? { labels } : {}
18940
+ };
18941
+ const meta = await store.put(bytes, attachment, ownerType, ownerId, attachedBy);
18942
+ let backend = "legacy";
18943
+ try {
18944
+ const v2 = createAttachmentStoreV2(getProjectRoot4());
18945
+ const v2Result = await v2.put(ownerId, {
18946
+ name: absPath.split(/[\\/]/).pop() ?? meta.sha256.slice(0, 12),
18947
+ data: new Uint8Array(bytes),
18948
+ contentType: mime
18949
+ });
18950
+ backend = v2Result.backend;
18951
+ } catch {
18952
+ backend = await resolveAttachmentBackend();
18953
+ }
18954
+ import("@cleocode/core/internal").then(
18955
+ ({ ensureLlmtxtNode }) => ensureLlmtxtNode(
18956
+ getProjectRoot4(),
18957
+ meta.sha256,
18958
+ `${ownerType}:${ownerId}`,
18959
+ absPath.split("/").pop() ?? meta.sha256.slice(0, 12)
18960
+ )
18961
+ ).catch(() => {
18962
+ });
18963
+ return lafsSuccess(
18964
+ {
18965
+ attachmentId: meta.id,
18966
+ sha256: meta.sha256,
18967
+ refCount: meta.refCount,
18968
+ kind: "local-file",
18969
+ ownerId,
18970
+ ownerType,
18971
+ // Cast: core returns 'llmtxt'|'legacy'; contracts uses 'legacy'|'llmstxt-v2' (T1529)
18972
+ attachmentBackend: backend
18973
+ },
18974
+ "add"
18975
+ );
18976
+ }
18977
+ if (url) {
18978
+ const attachment = {
18979
+ kind: "url",
18980
+ url,
18981
+ ...description ? { description } : {},
18982
+ ...labels ? { labels } : {}
18983
+ };
18984
+ const urlBytes = Buffer.from(url, "utf-8");
18985
+ const meta = await store.put(urlBytes, attachment, ownerType, ownerId, attachedBy);
18986
+ import("@cleocode/core/internal").then(
18987
+ ({ ensureLlmtxtNode }) => ensureLlmtxtNode(getProjectRoot4(), meta.sha256, `${ownerType}:${ownerId}`, url)
18988
+ ).catch(() => {
18989
+ });
18990
+ const backend = "legacy";
18991
+ return lafsSuccess(
18992
+ {
18993
+ attachmentId: meta.id,
18994
+ sha256: meta.sha256,
18995
+ refCount: meta.refCount,
18996
+ kind: "url",
18997
+ url,
18998
+ ownerId,
18999
+ ownerType,
19000
+ // Cast: core returns 'llmtxt'|'legacy'; contracts uses 'legacy'|'llmstxt-v2' (T1529)
19001
+ attachmentBackend: backend
19002
+ },
19003
+ "add"
19004
+ );
19005
+ }
19006
+ return lafsError("E_INVALID_INPUT", "Unreachable: no file or url", "add");
19007
+ },
19008
+ // ── docs.remove ────────────────────────────────────────────────────────────
19009
+ remove: async (params) => {
19010
+ const { attachmentRef: ref, from: fromOwner } = params;
19011
+ if (!ref) {
19012
+ return lafsError(
19013
+ "E_INVALID_INPUT",
19014
+ "attachmentRef is required (attachment ID or SHA-256 hex)",
19015
+ "remove"
19016
+ );
19017
+ }
19018
+ if (!fromOwner) {
19019
+ return lafsError("E_INVALID_INPUT", "--from <ownerId> is required", "remove");
19020
+ }
19021
+ const store = createAttachmentStore();
19022
+ const ownerType = inferOwnerType(fromOwner);
19023
+ let attachmentId = ref;
19024
+ if (/^[0-9a-f]{64}$/i.test(ref)) {
19025
+ const result = await store.get(ref);
19026
+ if (!result) {
19027
+ return lafsError("E_NOT_FOUND", `No attachment found with SHA-256: ${ref}`, "remove");
19028
+ }
19029
+ attachmentId = result.metadata.id;
19030
+ }
19031
+ const derefResult = await store.deref(attachmentId, ownerType, fromOwner);
19032
+ if (derefResult.status === "not-found") {
19033
+ return lafsError(
19034
+ "E_NOT_FOUND",
19035
+ `Attachment ref not found: ${attachmentId} on owner ${fromOwner}`,
19036
+ "remove"
19037
+ );
19038
+ }
19039
+ const blobPurged = derefResult.status === "removed";
19040
+ const refCountAfter = derefResult.status === "derefd" ? derefResult.refCountAfter : 0;
19041
+ try {
19042
+ const v2 = createAttachmentStoreV2(getProjectRoot4());
19043
+ await v2.remove(attachmentId, fromOwner);
19044
+ } catch {
19045
+ }
19046
+ const backend = await resolveAttachmentBackend();
19047
+ return lafsSuccess(
19048
+ {
19049
+ removed: blobPurged,
19050
+ attachmentId,
19051
+ from: fromOwner,
19052
+ refCountAfter,
19053
+ blobPurged,
19054
+ // Cast: core returns 'llmtxt'|'legacy'; contracts uses 'legacy'|'llmstxt-v2' (T1529)
19055
+ attachmentBackend: backend
19056
+ },
19057
+ "remove"
19058
+ );
19059
+ }
19060
+ });
19061
+ QUERY_OPS3 = /* @__PURE__ */ new Set(["list", "fetch", "generate"]);
19062
+ MUTATE_OPS3 = /* @__PURE__ */ new Set(["add", "remove"]);
18710
19063
  DocsHandler = class {
18711
19064
  // -----------------------------------------------------------------------
18712
19065
  // Query
18713
19066
  // -----------------------------------------------------------------------
19067
+ /**
19068
+ * Execute a read-only docs query operation.
19069
+ *
19070
+ * @param operation - The docs query op name (e.g. 'list', 'fetch', 'generate').
19071
+ * @param params - Raw params from the dispatcher (narrowed internally).
19072
+ */
18714
19073
  async query(operation, params) {
18715
19074
  const startTime = Date.now();
19075
+ if (!QUERY_OPS3.has(operation)) {
19076
+ return unsupportedOp("query", "docs", operation, startTime);
19077
+ }
18716
19078
  try {
18717
- switch (operation) {
18718
- // ── docs.list ────────────────────────────────────────────────────
18719
- case "list": {
18720
- const taskId = params?.task;
18721
- const sessionId = params?.session;
18722
- const observationId = params?.observation;
18723
- const ownerId = taskId ?? sessionId ?? observationId;
18724
- if (!ownerId) {
18725
- return errorResult(
18726
- "query",
18727
- "docs",
18728
- operation,
18729
- "E_INVALID_INPUT",
18730
- "Provide one of --task, --session, or --observation to scope the list.",
18731
- startTime
18732
- );
18733
- }
18734
- const ownerType = inferOwnerType(ownerId);
18735
- const store = createAttachmentStore();
18736
- const attachments = await store.listByOwner(ownerType, ownerId);
18737
- const backend = await resolveAttachmentBackend();
18738
- return {
18739
- meta: {
18740
- ...dispatchMeta("query", "docs", operation, startTime),
18741
- attachmentBackend: backend
18742
- },
18743
- success: true,
18744
- data: {
18745
- ownerId,
18746
- ownerType,
18747
- count: attachments.length,
18748
- attachments: attachments.map((m) => ({
18749
- id: m.id,
18750
- sha256: `${m.sha256.slice(0, 8)}\u2026`,
18751
- kind: m.attachment.kind,
18752
- mime: m.attachment.kind === "local-file" || m.attachment.kind === "blob" ? m.attachment.mime : m.attachment.mime ?? "\u2014",
18753
- size: m.attachment.kind === "local-file" || m.attachment.kind === "blob" ? m.attachment.size : void 0,
18754
- description: m.attachment.description,
18755
- labels: m.attachment.labels,
18756
- createdAt: m.createdAt,
18757
- refCount: m.refCount
18758
- }))
18759
- }
18760
- };
18761
- }
18762
- // ── docs.generate ────────────────────────────────────────────────
18763
- case "generate": {
18764
- const forId = params?.for;
18765
- if (!forId) {
18766
- return errorResult(
18767
- "query",
18768
- "docs",
18769
- operation,
18770
- "E_INVALID_INPUT",
18771
- "--for <taskId|epicId> is required",
18772
- startTime
18773
- );
18774
- }
18775
- const attach = params?.attach;
18776
- const cwd = getProjectRoot4();
18777
- const result = await generateDocsLlmsTxt({
18778
- ownerId: forId,
18779
- cwd
18780
- });
18781
- let attachmentId;
18782
- let attachmentSha256;
18783
- if (attach) {
18784
- const store = createAttachmentStore();
18785
- const ownerType = inferOwnerType(forId);
18786
- const contentBytes = Buffer.from(result.content, "utf-8");
18787
- const llmsTxtDescriptor = {
18788
- kind: "llms-txt",
18789
- source: "generated",
18790
- content: result.content,
18791
- description: `llms.txt for ${forId} (${result.attachmentCount} docs)`,
18792
- labels: ["llms-txt", "generated"]
18793
- };
18794
- const meta = await store.put(
18795
- contentBytes,
18796
- llmsTxtDescriptor,
18797
- ownerType,
18798
- forId,
18799
- "cleo-docs-generate",
18800
- cwd
18801
- );
18802
- attachmentId = meta.id;
18803
- attachmentSha256 = meta.sha256;
18804
- }
18805
- return {
18806
- meta: dispatchMeta("query", "docs", operation, startTime),
18807
- success: true,
18808
- data: {
18809
- forId,
18810
- content: result.content,
18811
- attachmentCount: result.attachmentCount,
18812
- usedLlmtxtPackage: result.usedLlmtxtPackage,
18813
- ...attachmentId !== void 0 ? {
18814
- attached: true,
18815
- attachmentId,
18816
- attachmentSha256
18817
- } : { attached: false }
18818
- }
18819
- };
18820
- }
18821
- // ── docs.fetch ───────────────────────────────────────────────────
18822
- case "fetch": {
18823
- const ref = params?.attachmentRef;
18824
- if (!ref) {
18825
- return errorResult(
18826
- "query",
18827
- "docs",
18828
- operation,
18829
- "E_INVALID_INPUT",
18830
- "attachmentRef is required (attachment ID or SHA-256 hex)",
18831
- startTime
18832
- );
18833
- }
18834
- const store = createAttachmentStore();
18835
- const isSha256 = /^[0-9a-f]{64}$/i.test(ref);
18836
- let result = null;
18837
- let metadata = await store.getMetadata(ref);
18838
- if (metadata) {
18839
- result = await store.get(metadata.sha256);
18840
- } else if (isSha256) {
18841
- result = await store.get(ref);
18842
- if (result) {
18843
- metadata = result.metadata;
18844
- }
18845
- }
18846
- if (!result || !metadata) {
18847
- return errorResult(
18848
- "query",
18849
- "docs",
18850
- operation,
18851
- "E_NOT_FOUND",
18852
- `Attachment not found: ${ref}`,
18853
- startTime
18854
- );
18855
- }
18856
- const cwd = getProjectRoot4();
18857
- const cleoDir = getCleoDirAbsolute(cwd);
18858
- let storagePath;
18859
- if (metadata.attachment.kind === "local-file") {
18860
- storagePath = metadata.attachment.path;
18861
- } else if (metadata.attachment.kind === "blob") {
18862
- const prefix = metadata.sha256.slice(0, 2);
18863
- const rest = metadata.sha256.slice(2);
18864
- const extMap = {
18865
- "text/markdown": ".md",
18866
- "text/plain": ".txt",
18867
- "application/json": ".json",
18868
- "application/pdf": ".pdf"
18869
- };
18870
- const mime = metadata.attachment.kind === "blob" ? metadata.attachment.mime : "application/octet-stream";
18871
- const ext = extMap[mime] ?? ".bin";
18872
- storagePath = resolve2(cleoDir, "attachments", "sha256", prefix, `${rest}${ext}`);
18873
- }
18874
- const MAX_INLINE = 1024 * 1024;
18875
- const bytesBase64 = result.bytes.length <= MAX_INLINE ? result.bytes.toString("base64") : void 0;
18876
- const backend = await resolveAttachmentBackend();
18877
- return {
18878
- meta: {
18879
- ...dispatchMeta("query", "docs", operation, startTime),
18880
- attachmentBackend: backend
18881
- },
18882
- success: true,
18883
- data: {
18884
- metadata,
18885
- path: storagePath,
18886
- sizeBytes: result.bytes.length,
18887
- ...bytesBase64 !== void 0 ? { bytesBase64 } : {},
18888
- inlined: bytesBase64 !== void 0
18889
- }
18890
- };
18891
- }
18892
- default:
18893
- return unsupportedOp("query", "docs", operation, startTime);
18894
- }
19079
+ const envelope = await typedDispatch(
19080
+ _docsTypedHandler,
19081
+ operation,
19082
+ params ?? {}
19083
+ );
19084
+ return docsEnvelopeToResponse(envelope, "query", operation, startTime);
18895
19085
  } catch (error) {
18896
19086
  return handleErrorResult("query", "docs", operation, error, startTime);
18897
19087
  }
@@ -18899,220 +19089,24 @@ var init_docs = __esm({
18899
19089
  // -----------------------------------------------------------------------
18900
19090
  // Mutate
18901
19091
  // -----------------------------------------------------------------------
19092
+ /**
19093
+ * Execute a state-modifying docs mutation operation.
19094
+ *
19095
+ * @param operation - The docs mutate op name (e.g. 'add', 'remove').
19096
+ * @param params - Raw params from the dispatcher (narrowed internally).
19097
+ */
18902
19098
  async mutate(operation, params) {
18903
19099
  const startTime = Date.now();
19100
+ if (!MUTATE_OPS3.has(operation)) {
19101
+ return unsupportedOp("mutate", "docs", operation, startTime);
19102
+ }
18904
19103
  try {
18905
- switch (operation) {
18906
- // ── docs.add ─────────────────────────────────────────────────────
18907
- case "add": {
18908
- const ownerId = params?.ownerId;
18909
- if (!ownerId) {
18910
- return errorResult(
18911
- "mutate",
18912
- "docs",
18913
- operation,
18914
- "E_INVALID_INPUT",
18915
- "ownerId is required",
18916
- startTime
18917
- );
18918
- }
18919
- const filePath = params?.file;
18920
- const url = params?.url;
18921
- if (!filePath && !url) {
18922
- return errorResult(
18923
- "mutate",
18924
- "docs",
18925
- operation,
18926
- "E_INVALID_INPUT",
18927
- "Provide either a file path (positional or --file) or --url",
18928
- startTime
18929
- );
18930
- }
18931
- const description = params?.desc;
18932
- const labels = parseLabels(params?.labels);
18933
- const attachedBy = params?.attachedBy ?? "human";
18934
- const ownerType = inferOwnerType(ownerId);
18935
- const store = createAttachmentStore();
18936
- if (filePath) {
18937
- const absPath = resolve2(filePath);
18938
- let bytes;
18939
- try {
18940
- bytes = await readFile(absPath);
18941
- } catch {
18942
- return errorResult(
18943
- "mutate",
18944
- "docs",
18945
- operation,
18946
- "E_FILE_ERROR",
18947
- `Cannot read file: ${absPath}`,
18948
- startTime
18949
- );
18950
- }
18951
- const mime = mimeFromPath(absPath);
18952
- const attachment = {
18953
- kind: "local-file",
18954
- path: absPath,
18955
- mime,
18956
- size: bytes.length,
18957
- ...description ? { description } : {},
18958
- ...labels ? { labels } : {}
18959
- };
18960
- const meta = await store.put(bytes, attachment, ownerType, ownerId, attachedBy);
18961
- let backend = "legacy";
18962
- try {
18963
- const v2 = createAttachmentStoreV2(getProjectRoot4());
18964
- const v2Result = await v2.put(ownerId, {
18965
- name: absPath.split(/[\\/]/).pop() ?? meta.sha256.slice(0, 12),
18966
- data: new Uint8Array(bytes),
18967
- contentType: mime
18968
- });
18969
- backend = v2Result.backend;
18970
- } catch {
18971
- backend = await resolveAttachmentBackend();
18972
- }
18973
- import("@cleocode/core/internal").then(
18974
- ({ ensureLlmtxtNode }) => ensureLlmtxtNode(
18975
- getProjectRoot4(),
18976
- meta.sha256,
18977
- `${ownerType}:${ownerId}`,
18978
- absPath.split("/").pop() ?? meta.sha256.slice(0, 12)
18979
- )
18980
- ).catch(() => {
18981
- });
18982
- return {
18983
- meta: {
18984
- ...dispatchMeta("mutate", "docs", operation, startTime),
18985
- attachmentBackend: backend
18986
- },
18987
- success: true,
18988
- data: {
18989
- attachmentId: meta.id,
18990
- sha256: meta.sha256,
18991
- refCount: meta.refCount,
18992
- kind: "local-file",
18993
- ownerId,
18994
- ownerType
18995
- }
18996
- };
18997
- }
18998
- if (url) {
18999
- const attachment = {
19000
- kind: "url",
19001
- url,
19002
- ...description ? { description } : {},
19003
- ...labels ? { labels } : {}
19004
- };
19005
- const urlBytes = Buffer.from(url, "utf-8");
19006
- const meta = await store.put(urlBytes, attachment, ownerType, ownerId, attachedBy);
19007
- import("@cleocode/core/internal").then(
19008
- ({ ensureLlmtxtNode }) => ensureLlmtxtNode(getProjectRoot4(), meta.sha256, `${ownerType}:${ownerId}`, url)
19009
- ).catch(() => {
19010
- });
19011
- const backend = "legacy";
19012
- return {
19013
- meta: {
19014
- ...dispatchMeta("mutate", "docs", operation, startTime),
19015
- attachmentBackend: backend
19016
- },
19017
- success: true,
19018
- data: {
19019
- attachmentId: meta.id,
19020
- sha256: meta.sha256,
19021
- refCount: meta.refCount,
19022
- kind: "url",
19023
- url,
19024
- ownerId,
19025
- ownerType
19026
- }
19027
- };
19028
- }
19029
- return errorResult(
19030
- "mutate",
19031
- "docs",
19032
- operation,
19033
- "E_INVALID_INPUT",
19034
- "Unreachable: no file or url",
19035
- startTime
19036
- );
19037
- }
19038
- // ── docs.remove ──────────────────────────────────────────────────
19039
- case "remove": {
19040
- const ref = params?.attachmentRef;
19041
- const fromOwner = params?.from;
19042
- if (!ref) {
19043
- return errorResult(
19044
- "mutate",
19045
- "docs",
19046
- operation,
19047
- "E_INVALID_INPUT",
19048
- "attachmentRef is required (attachment ID or SHA-256 hex)",
19049
- startTime
19050
- );
19051
- }
19052
- if (!fromOwner) {
19053
- return errorResult(
19054
- "mutate",
19055
- "docs",
19056
- operation,
19057
- "E_INVALID_INPUT",
19058
- "--from <ownerId> is required",
19059
- startTime
19060
- );
19061
- }
19062
- const store = createAttachmentStore();
19063
- const ownerType = inferOwnerType(fromOwner);
19064
- let attachmentId = ref;
19065
- if (/^[0-9a-f]{64}$/i.test(ref)) {
19066
- const result = await store.get(ref);
19067
- if (!result) {
19068
- return errorResult(
19069
- "mutate",
19070
- "docs",
19071
- operation,
19072
- "E_NOT_FOUND",
19073
- `No attachment found with SHA-256: ${ref}`,
19074
- startTime
19075
- );
19076
- }
19077
- attachmentId = result.metadata.id;
19078
- }
19079
- const derefResult = await store.deref(attachmentId, ownerType, fromOwner);
19080
- if (derefResult.status === "not-found") {
19081
- return errorResult(
19082
- "mutate",
19083
- "docs",
19084
- operation,
19085
- "E_NOT_FOUND",
19086
- `Attachment ref not found: ${attachmentId} on owner ${fromOwner}`,
19087
- startTime
19088
- );
19089
- }
19090
- const blobPurged = derefResult.status === "removed";
19091
- const refCountAfter = derefResult.status === "derefd" ? derefResult.refCountAfter : 0;
19092
- try {
19093
- const v2 = createAttachmentStoreV2(getProjectRoot4());
19094
- await v2.remove(attachmentId, fromOwner);
19095
- } catch {
19096
- }
19097
- const backend = await resolveAttachmentBackend();
19098
- return {
19099
- meta: {
19100
- ...dispatchMeta("mutate", "docs", operation, startTime),
19101
- attachmentBackend: backend
19102
- },
19103
- success: true,
19104
- data: {
19105
- removed: blobPurged,
19106
- attachmentId,
19107
- from: fromOwner,
19108
- refCountAfter,
19109
- blobPurged
19110
- }
19111
- };
19112
- }
19113
- default:
19114
- return unsupportedOp("mutate", "docs", operation, startTime);
19115
- }
19104
+ const envelope = await typedDispatch(
19105
+ _docsTypedHandler,
19106
+ operation,
19107
+ params ?? {}
19108
+ );
19109
+ return docsEnvelopeToResponse(envelope, "mutate", operation, startTime);
19116
19110
  } catch (error) {
19117
19111
  return handleErrorResult("mutate", "docs", operation, error, startTime);
19118
19112
  }
@@ -19120,6 +19114,7 @@ var init_docs = __esm({
19120
19114
  // -----------------------------------------------------------------------
19121
19115
  // Supported operations
19122
19116
  // -----------------------------------------------------------------------
19117
+ /** Declared operations for introspection and validation. */
19123
19118
  getSupportedOperations() {
19124
19119
  return {
19125
19120
  query: ["list", "fetch", "generate"],
@@ -22205,7 +22200,7 @@ async function handleImpact(operation, params, startTime) {
22205
22200
  return handleErrorResult("query", "nexus", operation, dbErr, startTime);
22206
22201
  }
22207
22202
  }
22208
- var _nexusTypedHandler, QUERY_OPS3, MUTATE_OPS3, NexusHandler, IMPACT_REVERSE_TYPES;
22203
+ var _nexusTypedHandler, QUERY_OPS4, MUTATE_OPS4, NexusHandler, IMPACT_REVERSE_TYPES;
22209
22204
  var init_nexus2 = __esm({
22210
22205
  "packages/cleo/src/dispatch/domains/nexus.ts"() {
22211
22206
  "use strict";
@@ -22521,7 +22516,7 @@ var init_nexus2 = __esm({
22521
22516
  },
22522
22517
  "sigil.sync": async (_params) => wrapCoreResult(await nexusSigilSync(), "sigil.sync")
22523
22518
  });
22524
- QUERY_OPS3 = /* @__PURE__ */ new Set([
22519
+ QUERY_OPS4 = /* @__PURE__ */ new Set([
22525
22520
  "share.status",
22526
22521
  "status",
22527
22522
  "list",
@@ -22553,7 +22548,7 @@ var init_nexus2 = __esm({
22553
22548
  "profile.get",
22554
22549
  "sigil.list"
22555
22550
  ]);
22556
- MUTATE_OPS3 = /* @__PURE__ */ new Set([
22551
+ MUTATE_OPS4 = /* @__PURE__ */ new Set([
22557
22552
  "share.snapshot.export",
22558
22553
  "share.snapshot.import",
22559
22554
  "init",
@@ -22582,7 +22577,7 @@ var init_nexus2 = __esm({
22582
22577
  */
22583
22578
  async query(operation, params) {
22584
22579
  const startTime = Date.now();
22585
- if (!QUERY_OPS3.has(operation)) {
22580
+ if (!QUERY_OPS4.has(operation)) {
22586
22581
  return unsupportedOp("query", "nexus", operation, startTime);
22587
22582
  }
22588
22583
  if (operation === "top-entries") {
@@ -22614,7 +22609,7 @@ var init_nexus2 = __esm({
22614
22609
  */
22615
22610
  async mutate(operation, params) {
22616
22611
  const startTime = Date.now();
22617
- if (!MUTATE_OPS3.has(operation)) {
22612
+ if (!MUTATE_OPS4.has(operation)) {
22618
22613
  return unsupportedOp("mutate", "nexus", operation, startTime);
22619
22614
  }
22620
22615
  try {
@@ -22635,8 +22630,8 @@ var init_nexus2 = __esm({
22635
22630
  /** Declared operations for introspection and validation. */
22636
22631
  getSupportedOperations() {
22637
22632
  return {
22638
- query: Array.from(QUERY_OPS3),
22639
- mutate: Array.from(MUTATE_OPS3)
22633
+ query: Array.from(QUERY_OPS4),
22634
+ mutate: Array.from(MUTATE_OPS4)
22640
22635
  };
22641
22636
  }
22642
22637
  };
@@ -27541,7 +27536,7 @@ async function lookupApprovalByTokenForDispatch(token) {
27541
27536
  const db = await acquireDb();
27542
27537
  return getPlaybookApprovalByToken(db, token);
27543
27538
  }
27544
- var __playbookRuntimeOverrides, _playbookTypedHandler, QUERY_OPS4, MUTATE_OPS4, PlaybookHandler;
27539
+ var __playbookRuntimeOverrides, _playbookTypedHandler, QUERY_OPS5, MUTATE_OPS5, PlaybookHandler;
27545
27540
  var init_playbook2 = __esm({
27546
27541
  "packages/cleo/src/dispatch/domains/playbook.ts"() {
27547
27542
  "use strict";
@@ -27807,15 +27802,15 @@ var init_playbook2 = __esm({
27807
27802
  }
27808
27803
  }
27809
27804
  });
27810
- QUERY_OPS4 = /* @__PURE__ */ new Set(["status", "list", "validate"]);
27811
- MUTATE_OPS4 = /* @__PURE__ */ new Set(["run", "resume"]);
27805
+ QUERY_OPS5 = /* @__PURE__ */ new Set(["status", "list", "validate"]);
27806
+ MUTATE_OPS5 = /* @__PURE__ */ new Set(["run", "resume"]);
27812
27807
  PlaybookHandler = class {
27813
27808
  /**
27814
27809
  * Query gateway — `status`, `list`, and `validate`.
27815
27810
  */
27816
27811
  async query(operation, params) {
27817
27812
  const startTime = Date.now();
27818
- if (!QUERY_OPS4.has(operation)) {
27813
+ if (!QUERY_OPS5.has(operation)) {
27819
27814
  return unsupportedOp("query", "playbook", operation, startTime);
27820
27815
  }
27821
27816
  try {
@@ -27843,7 +27838,7 @@ var init_playbook2 = __esm({
27843
27838
  */
27844
27839
  async mutate(operation, params) {
27845
27840
  const startTime = Date.now();
27846
- if (!MUTATE_OPS4.has(operation)) {
27841
+ if (!MUTATE_OPS5.has(operation)) {
27847
27842
  return unsupportedOp("mutate", "playbook", operation, startTime);
27848
27843
  }
27849
27844
  try {
@@ -29890,7 +29885,7 @@ function envelopeToEngineResult3(envelope) {
29890
29885
  }
29891
29886
  };
29892
29887
  }
29893
- var coreOps2, _sentientTypedHandler, QUERY_OPS5, MUTATE_OPS5, SentientHandler;
29888
+ var coreOps2, _sentientTypedHandler, QUERY_OPS6, MUTATE_OPS6, SentientHandler;
29894
29889
  var init_sentient2 = __esm({
29895
29890
  "packages/cleo/src/dispatch/domains/sentient.ts"() {
29896
29891
  "use strict";
@@ -30010,8 +30005,8 @@ var init_sentient2 = __esm({
30010
30005
  }
30011
30006
  }
30012
30007
  });
30013
- QUERY_OPS5 = /* @__PURE__ */ new Set(["propose.list", "propose.diff", "allowlist.list"]);
30014
- MUTATE_OPS5 = /* @__PURE__ */ new Set([
30008
+ QUERY_OPS6 = /* @__PURE__ */ new Set(["propose.list", "propose.diff", "allowlist.list"]);
30009
+ MUTATE_OPS6 = /* @__PURE__ */ new Set([
30015
30010
  "propose.accept",
30016
30011
  "propose.reject",
30017
30012
  "propose.run",
@@ -30044,7 +30039,7 @@ var init_sentient2 = __esm({
30044
30039
  */
30045
30040
  async query(operation, params) {
30046
30041
  const startTime = Date.now();
30047
- if (!QUERY_OPS5.has(operation)) {
30042
+ if (!QUERY_OPS6.has(operation)) {
30048
30043
  return unsupportedOp("query", "sentient", operation, startTime);
30049
30044
  }
30050
30045
  try {
@@ -30072,7 +30067,7 @@ var init_sentient2 = __esm({
30072
30067
  */
30073
30068
  async mutate(operation, params) {
30074
30069
  const startTime = Date.now();
30075
- if (!MUTATE_OPS5.has(operation)) {
30070
+ if (!MUTATE_OPS6.has(operation)) {
30076
30071
  return unsupportedOp("mutate", "sentient", operation, startTime);
30077
30072
  }
30078
30073
  try {
@@ -30212,7 +30207,7 @@ async function storeSessionOwnerAuthToken(projectRoot, sessionId, token) {
30212
30207
  const db = await getDb(projectRoot);
30213
30208
  db.update(sessions).set({ ownerAuthToken: token }).where(eq(sessions.id, sessionId)).run();
30214
30209
  }
30215
- var coreOps3, _sessionTypedHandler, QUERY_OPS6, MUTATE_OPS6, SessionHandler;
30210
+ var coreOps3, _sessionTypedHandler, QUERY_OPS7, MUTATE_OPS7, SessionHandler;
30216
30211
  var init_session3 = __esm({
30217
30212
  "packages/cleo/src/dispatch/domains/session.ts"() {
30218
30213
  "use strict";
@@ -30428,7 +30423,7 @@ var init_session3 = __esm({
30428
30423
  return wrapCoreResult(result, "record.assumption");
30429
30424
  }
30430
30425
  });
30431
- QUERY_OPS6 = /* @__PURE__ */ new Set([
30426
+ QUERY_OPS7 = /* @__PURE__ */ new Set([
30432
30427
  "status",
30433
30428
  "list",
30434
30429
  "show",
@@ -30438,7 +30433,7 @@ var init_session3 = __esm({
30438
30433
  "handoff.show",
30439
30434
  "briefing.show"
30440
30435
  ]);
30441
- MUTATE_OPS6 = /* @__PURE__ */ new Set([
30436
+ MUTATE_OPS7 = /* @__PURE__ */ new Set([
30442
30437
  "start",
30443
30438
  "end",
30444
30439
  "resume",
@@ -30459,7 +30454,7 @@ var init_session3 = __esm({
30459
30454
  */
30460
30455
  async query(operation, params) {
30461
30456
  const startTime = Date.now();
30462
- if (!QUERY_OPS6.has(operation)) {
30457
+ if (!QUERY_OPS7.has(operation)) {
30463
30458
  return unsupportedOp("query", "session", operation, startTime);
30464
30459
  }
30465
30460
  try {
@@ -30488,7 +30483,7 @@ var init_session3 = __esm({
30488
30483
  */
30489
30484
  async mutate(operation, params) {
30490
30485
  const startTime = Date.now();
30491
- if (!MUTATE_OPS6.has(operation)) {
30486
+ if (!MUTATE_OPS7.has(operation)) {
30492
30487
  return unsupportedOp("mutate", "session", operation, startTime);
30493
30488
  }
30494
30489
  try {
@@ -30870,7 +30865,7 @@ function envelopeToEngineResult5(envelope) {
30870
30865
  }
30871
30866
  };
30872
30867
  }
30873
- var _tasksTypedHandler, QUERY_OPS7, MUTATE_OPS7, TasksHandler;
30868
+ var _tasksTypedHandler, QUERY_OPS8, MUTATE_OPS8, TasksHandler;
30874
30869
  var init_tasks3 = __esm({
30875
30870
  "packages/cleo/src/dispatch/domains/tasks.ts"() {
30876
30871
  "use strict";
@@ -31209,7 +31204,7 @@ var init_tasks3 = __esm({
31209
31204
  return wrapCoreResult(await taskUnclaim(projectRoot, params.taskId), "unclaim");
31210
31205
  }
31211
31206
  });
31212
- QUERY_OPS7 = /* @__PURE__ */ new Set([
31207
+ QUERY_OPS8 = /* @__PURE__ */ new Set([
31213
31208
  "show",
31214
31209
  "list",
31215
31210
  "find",
@@ -31227,7 +31222,7 @@ var init_tasks3 = __esm({
31227
31222
  "label.list",
31228
31223
  "sync.links"
31229
31224
  ]);
31230
- MUTATE_OPS7 = /* @__PURE__ */ new Set([
31225
+ MUTATE_OPS8 = /* @__PURE__ */ new Set([
31231
31226
  "add",
31232
31227
  "update",
31233
31228
  "complete",
@@ -31257,7 +31252,7 @@ var init_tasks3 = __esm({
31257
31252
  */
31258
31253
  async query(operation, params) {
31259
31254
  const startTime = Date.now();
31260
- if (!QUERY_OPS7.has(operation)) {
31255
+ if (!QUERY_OPS8.has(operation)) {
31261
31256
  return unsupportedOp("query", "tasks", operation, startTime);
31262
31257
  }
31263
31258
  if (operation === "impact" && !params?.change) {
@@ -31296,7 +31291,7 @@ var init_tasks3 = __esm({
31296
31291
  */
31297
31292
  async mutate(operation, params) {
31298
31293
  const startTime = Date.now();
31299
- if (!MUTATE_OPS7.has(operation)) {
31294
+ if (!MUTATE_OPS8.has(operation)) {
31300
31295
  return unsupportedOp("mutate", "tasks", operation, startTime);
31301
31296
  }
31302
31297
  try {