@cleocode/cleo 2026.5.126 → 2026.5.127

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
@@ -5170,7 +5170,7 @@ var init_operations_registry = __esm({
5170
5170
  }
5171
5171
  ]
5172
5172
  },
5173
- // === saga sub-domain (ADR-073above-epic grouping tier) ===
5173
+ // === saga sub-domain (ADR-088 PM-Core V2 canonical containment tier) ===
5174
5174
  {
5175
5175
  gateway: "mutate",
5176
5176
  domain: "tasks",
@@ -5319,8 +5319,10 @@ var init_operations_registry = __esm({
5319
5319
  ]
5320
5320
  },
5321
5321
  {
5322
- // T10117 detach I5-violating parentId and re-attach via task_relations
5322
+ // [LEGACY T10117] Detach I5-violating parentId and re-attach via task_relations
5323
5323
  // type=groups (ADR-073 §1.2 invariant I5). Idempotent.
5324
+ // PM-Core V2 (ADR-088): parent_id is canonical containment; this repair
5325
+ // direction is the inverse of the canonical model.
5324
5326
  gateway: "mutate",
5325
5327
  domain: "tasks",
5326
5328
  operation: "saga.repair",
@@ -5369,7 +5371,11 @@ var init_operations_registry = __esm({
5369
5371
  ]
5370
5372
  },
5371
5373
  {
5372
- // T10637 migrate parent_id-based Saga membership to groups relations.
5374
+ // [LEGACY T10637] Pre-ADR-088 migration: convert parent_id-based Saga
5375
+ // membership to groups relations. PM-Core V2 (ADR-088) canonicalizes
5376
+ // parent_id as the sole containment edge. This operation is retained
5377
+ // for historical reference; its migration direction is the opposite
5378
+ // of the PM-Core V2 canonical model.
5373
5379
  // Converts Epics whose parent_id points to a type='saga' task into proper
5374
5380
  // groups relations. Non-Epic tasks are documented as conflicts.
5375
5381
  gateway: "mutate",
@@ -9548,6 +9554,45 @@ var init_operations_registry = __esm({
9548
9554
  }
9549
9555
  ]
9550
9556
  },
9557
+ // ── docs.llm-output (T11137) — unified LLM output surface ──────────────────
9558
+ {
9559
+ gateway: "query",
9560
+ domain: "docs",
9561
+ operation: "llm-output",
9562
+ description: "docs.llm-output (query) \u2014 unified LLM output: task export (rich Markdown) or attachment-bundle (llms.txt). Replaces `docs export` and `docs generate`.",
9563
+ tier: 1,
9564
+ idempotent: true,
9565
+ sessionRequired: false,
9566
+ requiredParams: ["for"],
9567
+ params: [
9568
+ { name: "for", type: "string", required: true, description: "Target entity ID" },
9569
+ {
9570
+ name: "mode",
9571
+ type: "string",
9572
+ required: false,
9573
+ description: "Output mode: task-export|attachment-bundle (auto-detected)"
9574
+ },
9575
+ { name: "out", type: "string", required: false, description: "Output file path" },
9576
+ {
9577
+ name: "includeAttachments",
9578
+ type: "boolean",
9579
+ required: false,
9580
+ description: "Include attachment manifest (task-export)"
9581
+ },
9582
+ {
9583
+ name: "includeMemoryRefs",
9584
+ type: "boolean",
9585
+ required: false,
9586
+ description: "Include memory refs (task-export)"
9587
+ },
9588
+ {
9589
+ name: "attach",
9590
+ type: "boolean",
9591
+ required: false,
9592
+ description: "Save as llms-txt attachment (attachment-bundle)"
9593
+ }
9594
+ ]
9595
+ },
9551
9596
  // ── docs.update (T10161 — Epic T10157 / Saga T9855) ──────────────────────
9552
9597
  {
9553
9598
  gateway: "mutate",
@@ -13588,6 +13633,511 @@ var init_tasks2 = __esm({
13588
13633
  }
13589
13634
  });
13590
13635
 
13636
+ // packages/contracts/src/taxonomy.ts
13637
+ var BUILTIN_TAXONOMY_TAGS, CANONICAL_TAG_VALUES, _TAG_BY_ID, ADHOC_TO_CANONICAL, TAGS_BY_AXIS, CANONICAL_TYPE_TAGS, CANONICAL_DOMAIN_TAGS, CANONICAL_DOC_KIND_TAGS, TaxonomyError, TaxonomyRegistry;
13638
+ var init_taxonomy = __esm({
13639
+ "packages/contracts/src/taxonomy.ts"() {
13640
+ "use strict";
13641
+ BUILTIN_TAXONOMY_TAGS = [
13642
+ // ─── Domain tags ───────────────────────────────────────────────
13643
+ {
13644
+ tag: "architecture",
13645
+ label: "Architecture",
13646
+ description: "Cross-cutting architectural decisions, patterns, and subsystem-level concerns",
13647
+ axes: ["domain", "type"],
13648
+ adhocAliases: ["architecture"]
13649
+ },
13650
+ {
13651
+ tag: "cli",
13652
+ label: "CLI",
13653
+ description: "Command-line interface, dispatch, and command infrastructure",
13654
+ axes: ["domain"],
13655
+ adhocAliases: ["cli"]
13656
+ },
13657
+ {
13658
+ tag: "core",
13659
+ label: "Core",
13660
+ description: "Core engine, shared utilities, and foundational modules",
13661
+ axes: ["domain"],
13662
+ adhocAliases: ["core", "pm-core-v2", "foundation"]
13663
+ },
13664
+ {
13665
+ tag: "contracts",
13666
+ label: "Contracts",
13667
+ description: "Type contracts, schemas, and public API surface definitions",
13668
+ axes: ["domain"],
13669
+ adhocAliases: ["contracts", "schema"]
13670
+ },
13671
+ {
13672
+ tag: "caamp",
13673
+ label: "CAAMP",
13674
+ description: "Cross-Agent Adaptive Messaging Protocol \u2014 hooks, adapters, providers",
13675
+ axes: ["domain"],
13676
+ adhocAliases: ["caamp", "cant", "pi", "cant-dsl"]
13677
+ },
13678
+ {
13679
+ tag: "skills",
13680
+ label: "Skills",
13681
+ description: "Agent skill definitions, injection, dispatch, and guard patterns",
13682
+ axes: ["domain"],
13683
+ adhocAliases: ["skills"]
13684
+ },
13685
+ {
13686
+ tag: "brain",
13687
+ label: "BRAIN",
13688
+ description: "Cognitive memory system \u2014 decisions, patterns, observations, learnings",
13689
+ axes: ["domain"],
13690
+ adhocAliases: ["brain"]
13691
+ },
13692
+ {
13693
+ tag: "nexus",
13694
+ label: "Nexus",
13695
+ description: "Code intelligence surface \u2014 project indexing, impact analysis, cross-repo",
13696
+ axes: ["domain"],
13697
+ adhocAliases: ["nexus"]
13698
+ },
13699
+ {
13700
+ tag: "orchestration",
13701
+ label: "Orchestration",
13702
+ description: "Multi-agent orchestration, playbooks, LOOM lifecycle, spawn dispatch",
13703
+ axes: ["domain"],
13704
+ adhocAliases: ["orchestration", "orchestrate"]
13705
+ },
13706
+ {
13707
+ tag: "sessions",
13708
+ label: "Sessions",
13709
+ description: "Session lifecycle \u2014 handoff, debrief, briefing, snapshot, state",
13710
+ axes: ["domain"],
13711
+ adhocAliases: ["sessions"]
13712
+ },
13713
+ {
13714
+ tag: "tasks",
13715
+ label: "Tasks",
13716
+ description: "Task management \u2014 CRUD, gates, evidence, acceptance criteria, lifecycle",
13717
+ axes: ["domain"],
13718
+ adhocAliases: ["tasks"]
13719
+ },
13720
+ {
13721
+ tag: "docs",
13722
+ label: "Documents",
13723
+ description: "Document storage, retrieval, taxonomy, publish, and provenance",
13724
+ axes: ["domain"],
13725
+ adhocAliases: ["docs", "documentation"]
13726
+ },
13727
+ {
13728
+ tag: "cleoos",
13729
+ label: "CleoOS",
13730
+ description: "CleoOS runtime \u2014 gateway, daemon, injection facade, agents",
13731
+ axes: ["domain"],
13732
+ adhocAliases: ["cleoos", "facade", "sentient"]
13733
+ },
13734
+ {
13735
+ tag: "worktrunk",
13736
+ label: "Worktrunk",
13737
+ description: "Git worktree management \u2014 spawn, adopt, prune, lifecycle",
13738
+ axes: ["domain"],
13739
+ adhocAliases: ["worktrunk", "worktrunk-ssot"]
13740
+ },
13741
+ {
13742
+ tag: "agents",
13743
+ label: "Agents",
13744
+ description: "Agent profiles, execution learning, registry, dispatch",
13745
+ axes: ["domain"],
13746
+ adhocAliases: ["agents"]
13747
+ },
13748
+ {
13749
+ tag: "routing",
13750
+ label: "Routing",
13751
+ description: "Operation routing, capability matrix, dispatch path resolution",
13752
+ axes: ["domain"],
13753
+ adhocAliases: ["routing"]
13754
+ },
13755
+ {
13756
+ tag: "studio",
13757
+ label: "Studio",
13758
+ description: "Web UI, dashboard, and visual interfaces",
13759
+ axes: ["domain"],
13760
+ adhocAliases: ["studio"]
13761
+ },
13762
+ // ─── Type tags ─────────────────────────────────────────────────
13763
+ {
13764
+ tag: "architectural",
13765
+ label: "Architectural Decision",
13766
+ description: "Cross-cutting architectural decision with durable rationale",
13767
+ axes: ["type"],
13768
+ adhocAliases: ["architectural"]
13769
+ },
13770
+ {
13771
+ tag: "technical",
13772
+ label: "Technical Decision",
13773
+ description: "Implementation-scoped technical decision (library, pattern, algorithm)",
13774
+ axes: ["type"],
13775
+ adhocAliases: ["technical"]
13776
+ },
13777
+ {
13778
+ tag: "process",
13779
+ label: "Process Decision",
13780
+ description: "Workflow, methodology, or operational process decision",
13781
+ axes: ["type"],
13782
+ adhocAliases: ["process"]
13783
+ },
13784
+ {
13785
+ tag: "strategic",
13786
+ label: "Strategic Decision",
13787
+ description: "Long-horizon direction-setting decision spanning multiple epics",
13788
+ axes: ["type"],
13789
+ adhocAliases: ["strategic"]
13790
+ },
13791
+ {
13792
+ tag: "tactical",
13793
+ label: "Tactical Decision",
13794
+ description: "Short-horizon execution-level decision (AGT-* dispatch, within-sprint)",
13795
+ axes: ["type"],
13796
+ adhocAliases: ["tactical"]
13797
+ },
13798
+ {
13799
+ tag: "operational",
13800
+ label: "Operational Decision",
13801
+ description: "Infrastructure, deployment, or runtime configuration decision",
13802
+ axes: ["type"],
13803
+ adhocAliases: ["operational"]
13804
+ },
13805
+ {
13806
+ tag: "bugfix",
13807
+ label: "Bug Fix",
13808
+ description: "Defect correction or regression fix",
13809
+ axes: ["type"],
13810
+ adhocAliases: ["bugfix", "bug-fix", "hygiene"]
13811
+ },
13812
+ {
13813
+ tag: "refactor",
13814
+ label: "Refactor",
13815
+ description: "Internal restructuring without functional change",
13816
+ axes: ["type"],
13817
+ adhocAliases: ["refactor"]
13818
+ },
13819
+ {
13820
+ tag: "feature",
13821
+ label: "Feature",
13822
+ description: "New capability or user-facing enhancement",
13823
+ axes: ["type"],
13824
+ adhocAliases: ["feature"]
13825
+ },
13826
+ {
13827
+ tag: "discovery",
13828
+ label: "Discovery",
13829
+ description: "Exploratory research, investigation, or spike outcome",
13830
+ axes: ["type"],
13831
+ adhocAliases: ["discovery", "exploration", "research-type"]
13832
+ },
13833
+ {
13834
+ tag: "migration",
13835
+ label: "Migration",
13836
+ description: "Data or schema migration, backfill, or upgrade path",
13837
+ axes: ["type"],
13838
+ adhocAliases: ["migration", "migrations"]
13839
+ },
13840
+ {
13841
+ tag: "unification",
13842
+ label: "Unification",
13843
+ description: "Consolidation of fragmented systems into a single SSoT",
13844
+ axes: ["type"],
13845
+ adhocAliases: ["unification"]
13846
+ },
13847
+ {
13848
+ tag: "bootstrap",
13849
+ label: "Bootstrap",
13850
+ description: "Initial setup, scaffolding, or seed infrastructure",
13851
+ axes: ["type"],
13852
+ adhocAliases: ["bootstrap"]
13853
+ },
13854
+ // ─── Lifecycle tags ────────────────────────────────────────────
13855
+ {
13856
+ tag: "research",
13857
+ label: "Research",
13858
+ description: "LOOM stage 1: information gathering and domain understanding",
13859
+ axes: ["lifecycle"],
13860
+ adhocAliases: ["research", "wave-0"]
13861
+ },
13862
+ {
13863
+ tag: "consensus",
13864
+ label: "Consensus",
13865
+ description: "LOOM stage 2: multi-agent consensus and validation of findings",
13866
+ axes: ["lifecycle"],
13867
+ adhocAliases: ["consensus"]
13868
+ },
13869
+ {
13870
+ tag: "design",
13871
+ label: "Architecture Decision",
13872
+ description: "LOOM stage 3: architecture decision recording and validation",
13873
+ axes: ["lifecycle"],
13874
+ adhocAliases: ["design", "architecture_decision"]
13875
+ },
13876
+ {
13877
+ tag: "specification",
13878
+ label: "Specification",
13879
+ description: "LOOM stage 4: technical specification authoring",
13880
+ axes: ["lifecycle"],
13881
+ adhocAliases: ["specification", "rfc"]
13882
+ },
13883
+ {
13884
+ tag: "decomposition",
13885
+ label: "Decomposition",
13886
+ description: "LOOM stage 5: task decomposition and dependency modeling",
13887
+ axes: ["lifecycle"],
13888
+ adhocAliases: ["decomposition"]
13889
+ },
13890
+ {
13891
+ tag: "implementation",
13892
+ label: "Implementation",
13893
+ description: "LOOM stage 6: code authoring and unit-level verification",
13894
+ axes: ["lifecycle"],
13895
+ adhocAliases: [
13896
+ "implementation",
13897
+ "wave-1",
13898
+ "wave-2",
13899
+ "wave-3",
13900
+ "wave-4",
13901
+ "wave-5",
13902
+ "wave.1",
13903
+ "wave.2",
13904
+ "wave.3"
13905
+ ]
13906
+ },
13907
+ {
13908
+ tag: "validation",
13909
+ label: "Validation",
13910
+ description: "LOOM stage 7: integration testing and gate verification",
13911
+ axes: ["lifecycle"],
13912
+ adhocAliases: ["validation", "testing"]
13913
+ },
13914
+ {
13915
+ tag: "release",
13916
+ label: "Release",
13917
+ description: "LOOM stage 9: versioning, changelog, npm publish, tag push",
13918
+ axes: ["lifecycle", "type"],
13919
+ adhocAliases: ["release"]
13920
+ },
13921
+ // ─── Priority tags ─────────────────────────────────────────────
13922
+ {
13923
+ tag: "p0",
13924
+ label: "P0 \u2014 Critical",
13925
+ description: "Drop-everything critical: blocks release, data loss, security incident",
13926
+ axes: ["priority"],
13927
+ adhocAliases: ["p0", "critical", "prime-tier1"]
13928
+ },
13929
+ {
13930
+ tag: "p1",
13931
+ label: "P1 \u2014 High",
13932
+ description: "High priority: blocks dependent work, user-visible degradation",
13933
+ axes: ["priority"],
13934
+ adhocAliases: ["p1"]
13935
+ },
13936
+ {
13937
+ tag: "p2",
13938
+ label: "P2 \u2014 Medium",
13939
+ description: "Medium priority: should fix, not blocking current wave",
13940
+ axes: ["priority"],
13941
+ adhocAliases: ["p2"]
13942
+ },
13943
+ {
13944
+ tag: "p3",
13945
+ label: "P3 \u2014 Low",
13946
+ description: "Low priority: nice-to-have, cleanup, future consideration",
13947
+ axes: ["priority"],
13948
+ adhocAliases: ["p3"]
13949
+ },
13950
+ // ─── Doc-kind tags (absorb docs-taxonomy) ──────────────────────
13951
+ {
13952
+ tag: "adr",
13953
+ label: "Architecture Decision Record",
13954
+ description: "Formal architecture decision record with rationale and alternatives",
13955
+ axes: ["doc_kind"],
13956
+ adhocAliases: ["adr"]
13957
+ },
13958
+ {
13959
+ tag: "spec",
13960
+ label: "Specification",
13961
+ description: "Technical specification for a feature, protocol, or subsystem",
13962
+ axes: ["doc_kind"],
13963
+ adhocAliases: ["spec"]
13964
+ },
13965
+ {
13966
+ tag: "research",
13967
+ label: "Research Note",
13968
+ description: "Investigation findings, surveys, and domain analysis",
13969
+ axes: ["doc_kind"],
13970
+ adhocAliases: ["research"]
13971
+ },
13972
+ {
13973
+ tag: "handoff",
13974
+ label: "Handoff Note",
13975
+ description: "Cross-session handoff with state, blockers, and next actions",
13976
+ axes: ["doc_kind"],
13977
+ adhocAliases: ["handoff"]
13978
+ },
13979
+ {
13980
+ tag: "note",
13981
+ label: "Note",
13982
+ description: "General-purpose note, observation, or free-form documentation",
13983
+ axes: ["doc_kind"],
13984
+ adhocAliases: ["note"]
13985
+ },
13986
+ {
13987
+ tag: "llmreadme",
13988
+ label: "LLM README",
13989
+ description: "Agent-consumable project overview, conventions, and context",
13990
+ axes: ["doc_kind"],
13991
+ adhocAliases: ["llmreadme", "llm-readme"]
13992
+ },
13993
+ {
13994
+ tag: "designmd",
13995
+ label: "Design Doc",
13996
+ description: "Google DESIGN.md token spec for agent interface contracts",
13997
+ axes: ["doc_kind"],
13998
+ adhocAliases: ["designmd", "design-md"]
13999
+ },
14000
+ {
14001
+ tag: "changeset",
14002
+ label: "Changeset",
14003
+ description: "User-facing changelog entry for a single change",
14004
+ axes: ["doc_kind"],
14005
+ adhocAliases: ["changeset"]
14006
+ },
14007
+ {
14008
+ tag: "changelog",
14009
+ label: "Changelog",
14010
+ description: "Aggregated release changelog for a version",
14011
+ axes: ["doc_kind"],
14012
+ adhocAliases: ["changelog"]
14013
+ }
14014
+ ];
14015
+ CANONICAL_TAG_VALUES = BUILTIN_TAXONOMY_TAGS.map((t) => t.tag);
14016
+ _TAG_BY_ID = new Map(
14017
+ BUILTIN_TAXONOMY_TAGS.map((t) => [t.tag, t])
14018
+ );
14019
+ ADHOC_TO_CANONICAL = /* @__PURE__ */ new Map();
14020
+ for (const tag of BUILTIN_TAXONOMY_TAGS) {
14021
+ for (const alias2 of tag.adhocAliases) {
14022
+ ADHOC_TO_CANONICAL.set(alias2, tag.tag);
14023
+ }
14024
+ }
14025
+ TAGS_BY_AXIS = /* @__PURE__ */ new Map();
14026
+ for (const axis of ["domain", "type", "lifecycle", "priority", "doc_kind"]) {
14027
+ TAGS_BY_AXIS.set(
14028
+ axis,
14029
+ BUILTIN_TAXONOMY_TAGS.filter((t) => t.axes.includes(axis))
14030
+ );
14031
+ }
14032
+ CANONICAL_TYPE_TAGS = TAGS_BY_AXIS.get("type").map(
14033
+ (t) => t.tag
14034
+ );
14035
+ CANONICAL_DOMAIN_TAGS = TAGS_BY_AXIS.get("domain").map(
14036
+ (t) => t.tag
14037
+ );
14038
+ CANONICAL_DOC_KIND_TAGS = TAGS_BY_AXIS.get("doc_kind").map(
14039
+ (t) => t.tag
14040
+ );
14041
+ TaxonomyError = class extends Error {
14042
+ constructor(message, invalidTags) {
14043
+ super(message);
14044
+ this.invalidTags = invalidTags;
14045
+ this.name = "TaxonomyError";
14046
+ }
14047
+ invalidTags;
14048
+ };
14049
+ TaxonomyRegistry = class _TaxonomyRegistry {
14050
+ byId;
14051
+ adhocMap;
14052
+ constructor(tags) {
14053
+ this.byId = new Map(tags.map((t) => [t.tag, t]));
14054
+ this.adhocMap = /* @__PURE__ */ new Map();
14055
+ for (const tag of tags) {
14056
+ for (const alias2 of tag.adhocAliases) {
14057
+ const existingTarget = this.adhocMap.get(alias2);
14058
+ if (existingTarget !== void 0 && existingTarget !== tag.tag) {
14059
+ throw new TaxonomyError(
14060
+ `Ad-hoc alias '${alias2}' maps to both '${existingTarget}' and '${tag.tag}' \u2014 aliases must be unique across targets`,
14061
+ [alias2]
14062
+ );
14063
+ }
14064
+ this.adhocMap.set(alias2, tag.tag);
14065
+ }
14066
+ }
14067
+ }
14068
+ /** Default built-in registry singleton. */
14069
+ static default = new _TaxonomyRegistry(BUILTIN_TAXONOMY_TAGS);
14070
+ /** True when `tag` is a canonical tag. */
14071
+ isCanonical(tag) {
14072
+ return this.byId.has(tag);
14073
+ }
14074
+ /** Look up metadata for a canonical tag. */
14075
+ get(tag) {
14076
+ return this.byId.get(tag);
14077
+ }
14078
+ /** List all canonical tags. */
14079
+ list() {
14080
+ return [...this.byId.values()];
14081
+ }
14082
+ /** List tags for a specific axis. */
14083
+ listByAxis(axis) {
14084
+ return this.list().filter((t) => t.axes.includes(axis));
14085
+ }
14086
+ /**
14087
+ * Normalize an ad-hoc label to its canonical form.
14088
+ *
14089
+ * Returns the canonical tag id, or `undefined` if the label has no
14090
+ * known canonical mapping.
14091
+ */
14092
+ normalize(adhoc) {
14093
+ return this.adhocMap.get(adhoc);
14094
+ }
14095
+ /**
14096
+ * Validate that every tag in `tags` is canonical.
14097
+ *
14098
+ * @param tags - Tags to validate.
14099
+ * @returns An array of invalid tags. Empty array = all valid.
14100
+ */
14101
+ validate(tags) {
14102
+ return tags.filter((t) => !this.byId.has(t));
14103
+ }
14104
+ /**
14105
+ * Validate tags and throw on failure.
14106
+ *
14107
+ * @param tags - Tags to validate.
14108
+ * @param context - Human-readable context for the error message.
14109
+ * @throws TaxonomyError when any tag is not canonical.
14110
+ */
14111
+ validateOrThrow(tags, context) {
14112
+ const invalid = this.validate(tags);
14113
+ if (invalid.length > 0) {
14114
+ throw new TaxonomyError(
14115
+ `${context}: unknown tags [${invalid.join(", ")}]. Use canonical tags. Run 'cleo taxonomy list' to see all valid tags.`,
14116
+ invalid
14117
+ );
14118
+ }
14119
+ }
14120
+ /**
14121
+ * Normalize a set of ad-hoc labels to canonical form.
14122
+ *
14123
+ * Unknown labels are passed through unchanged (they may be
14124
+ * project-specific extensions or task-id references).
14125
+ *
14126
+ * @param labels - Ad-hoc labels to normalize.
14127
+ * @returns Normalized label set (deduplicated).
14128
+ */
14129
+ normalizeSet(labels) {
14130
+ const result = /* @__PURE__ */ new Set();
14131
+ for (const label of labels) {
14132
+ const canonical = this.normalize(label);
14133
+ result.add(canonical ?? label);
14134
+ }
14135
+ return [...result];
14136
+ }
14137
+ };
14138
+ }
14139
+ });
14140
+
13591
14141
  // packages/contracts/src/templates/manifest.ts
13592
14142
  import { z as z15 } from "zod";
13593
14143
  var TEMPLATE_KINDS, TEMPLATE_SUBSTITUTIONS, TEMPLATE_UPDATE_STRATEGIES, PLACEHOLDER_SOURCES, PlaceholderSpecSchema, TemplateManifestEntrySchema;
@@ -13740,6 +14290,7 @@ var init_src2 = __esm({
13740
14290
  init_task_evidence();
13741
14291
  init_archive();
13742
14292
  init_tasks2();
14293
+ init_taxonomy();
13743
14294
  init_manifest2();
13744
14295
  init_validator();
13745
14296
  init_workgraph();
@@ -18700,15 +19251,17 @@ var init_diagnostics = __esm({
18700
19251
  // packages/cleo/src/dispatch/domains/docs.ts
18701
19252
  import { readFile } from "node:fs/promises";
18702
19253
  import { resolve as resolve2 } from "node:path";
19254
+ import { LLM_OUTPUT_MODES } from "@cleocode/contracts/operations/docs";
18703
19255
  import { pushWarning } from "@cleocode/core";
18704
19256
  import {
18705
19257
  AUTO_TOKEN,
18706
19258
  allocateAdrSlug,
18707
19259
  allocateAutoSlugForDispatch,
18708
19260
  consumeReservedSlug,
19261
+ createAttachmentBlobStore,
18709
19262
  createAttachmentStore,
18710
19263
  createAttachmentStoreDocsAccessor,
18711
- createAttachmentStoreV2,
19264
+ createDocsReadModel,
18712
19265
  DOCS_UPDATE_LIFECYCLE_STATUS_LIST,
18713
19266
  exportDocument,
18714
19267
  findSimilarDocs,
@@ -18735,11 +19288,11 @@ import {
18735
19288
  SUPERSEDE_SAME_SLUG_CODE,
18736
19289
  searchAllProjectDocs,
18737
19290
  searchDocs,
18738
- statusDocs,
18739
19291
  supersedeDoc,
18740
19292
  syncFromGit,
18741
19293
  updateDocBySlug,
18742
19294
  validateDocBody,
19295
+ writeAuditEntry,
18743
19296
  writeChangesetEntry
18744
19297
  } from "@cleocode/core/internal";
18745
19298
  function getDocKindRegistry() {
@@ -18759,6 +19312,12 @@ function getDocKindRegistry() {
18759
19312
  function registeredKindValues() {
18760
19313
  return getDocKindRegistry().list().map((d) => d.kind);
18761
19314
  }
19315
+ async function currentAttachmentBackend() {
19316
+ return resolveAttachmentBackend();
19317
+ }
19318
+ function exportTaskDocument(options) {
19319
+ return exportDocument(options);
19320
+ }
18762
19321
  function inferOwnerType(ownerId) {
18763
19322
  if (/^T\d+$/i.test(ownerId)) return "task";
18764
19323
  if (ownerId.startsWith("ses_")) return "session";
@@ -18860,6 +19419,7 @@ function docsEnvelopeToResponse(envelope, gateway, operation, startTime) {
18860
19419
  error: {
18861
19420
  code: String(envelope.error?.code ?? "E_INTERNAL"),
18862
19421
  message: envelope.error?.message ?? "Unknown error",
19422
+ ...envelope.error?.fix !== void 0 ? { fix: envelope.error.fix } : {},
18863
19423
  // T9636 — preserve structured details (e.g. slug suggestions) so the
18864
19424
  // CLI can render alternative slugs without a separate API call.
18865
19425
  ...envelope.error?.details !== void 0 ? { details: envelope.error.details } : {}
@@ -18950,8 +19510,10 @@ async function dispatchDocsLegacyQuery(operation, params) {
18950
19510
  name: typeof params["name"] === "string" ? params["name"] : void 0,
18951
19511
  projectRoot
18952
19512
  });
18953
- case "status":
18954
- return statusDocs({ projectRoot });
19513
+ case "status": {
19514
+ const model = createDocsReadModel();
19515
+ return model.status(projectRoot);
19516
+ }
18955
19517
  default:
18956
19518
  throw new Error(`Unsupported docs query operation: ${operation}`);
18957
19519
  }
@@ -18976,10 +19538,19 @@ async function dispatchDocsLegacyMutate(operation, params) {
18976
19538
  });
18977
19539
  } catch {
18978
19540
  }
19541
+ try {
19542
+ writeAuditEntry(projectRoot, {
19543
+ op: "docs.publish",
19544
+ slug: result.blobName,
19545
+ attachmentId: result.blobSha256,
19546
+ summary: `Published doc '${result.blobName}' to ${result.relativePath}`
19547
+ });
19548
+ } catch {
19549
+ }
18979
19550
  return result;
18980
19551
  }
18981
- case "publish-pr":
18982
- return publishDocsAsPr({
19552
+ case "publish-pr": {
19553
+ const prResult = await publishDocsAsPr({
18983
19554
  slugOrId: String(params["slugOrId"]),
18984
19555
  ...typeof params["slug"] === "string" ? { slug: params["slug"] } : {},
18985
19556
  ...typeof params["type"] === "string" ? { type: params["type"] } : {},
@@ -18987,14 +19558,39 @@ async function dispatchDocsLegacyMutate(operation, params) {
18987
19558
  ...typeof params["body"] === "string" ? { body: params["body"] } : {},
18988
19559
  ...typeof params["base"] === "string" ? { base: params["base"] } : {}
18989
19560
  });
18990
- case "sync":
18991
- return syncFromGit({
19561
+ if (prResult.success) {
19562
+ try {
19563
+ writeAuditEntry(projectRoot, {
19564
+ op: "docs.publish-pr",
19565
+ slug: prResult.data.slug,
19566
+ type: prResult.data.type,
19567
+ attachmentId: prResult.data.blobSha,
19568
+ summary: `Published PR for doc '${prResult.data.slug}' (${prResult.data.action}) \u2014 ${prResult.data.prUrl}`
19569
+ });
19570
+ } catch {
19571
+ }
19572
+ }
19573
+ return prResult;
19574
+ }
19575
+ case "sync": {
19576
+ const syncResult = await syncFromGit({
18992
19577
  ownerId: String(params["ownerId"]),
18993
19578
  fromPath: String(params["fromPath"]),
18994
19579
  blobName: typeof params["blobName"] === "string" ? params["blobName"] : void 0,
18995
19580
  contentType: typeof params["contentType"] === "string" ? params["contentType"] : void 0,
18996
19581
  projectRoot
18997
19582
  });
19583
+ try {
19584
+ writeAuditEntry(projectRoot, {
19585
+ op: "docs.sync",
19586
+ slug: typeof params["blobName"] === "string" && params["blobName"] || void 0,
19587
+ ownerId: String(params["ownerId"]),
19588
+ summary: `Synced doc from '${String(params["fromPath"])}' for owner ${String(params["ownerId"])}`
19589
+ });
19590
+ } catch {
19591
+ }
19592
+ return syncResult;
19593
+ }
18998
19594
  case "import": {
18999
19595
  const scanRoot = String(params["scanRoot"]);
19000
19596
  const accessor = createAttachmentStoreDocsAccessor(projectRoot);
@@ -19008,6 +19604,13 @@ async function dispatchDocsLegacyMutate(operation, params) {
19008
19604
  auditDir: projectRoot,
19009
19605
  classify: makeClassifierForScanRoot(scanRoot, projectRoot)
19010
19606
  });
19607
+ try {
19608
+ writeAuditEntry(projectRoot, {
19609
+ op: "docs.import",
19610
+ summary: `Imported ${result.counters.importCount} created, ${result.counters.noopCount} skipped, ${result.counters.errorCount} errors from '${scanRoot}'`
19611
+ });
19612
+ } catch {
19613
+ }
19011
19614
  return {
19012
19615
  dryRun: result.dryRun,
19013
19616
  counters: result.counters,
@@ -19023,7 +19626,7 @@ async function dispatchDocsLegacyMutate(operation, params) {
19023
19626
  throw new Error(`Unsupported docs mutate operation: ${operation}`);
19024
19627
  }
19025
19628
  }
19026
- var DOCS_LIST_DEFAULT_LIMIT, _registryCache, SLUG_PATTERN, SLUG_MAX_LEN, _docsTypedHandler, QUERY_OPS3, MUTATE_OPS3, DocsHandler;
19629
+ var DOCS_LIST_DEFAULT_LIMIT, SLUG_COLLISION_GUIDANCE, _registryCache, SLUG_PATTERN, SLUG_MAX_LEN, _docsTypedHandler, QUERY_OPS3, MUTATE_OPS3, DocsHandler;
19027
19630
  var init_docs2 = __esm({
19028
19631
  "packages/cleo/src/dispatch/domains/docs.ts"() {
19029
19632
  "use strict";
@@ -19032,6 +19635,13 @@ var init_docs2 = __esm({
19032
19635
  init_base();
19033
19636
  init_meta2();
19034
19637
  DOCS_LIST_DEFAULT_LIMIT = 50;
19638
+ SLUG_COLLISION_GUIDANCE = `slug '{slug}' is already reserved in this project. Three ways to proceed:
19639
+
19640
+ 1. Update the existing document: cleo docs update {slug} [--file <path> | --content <text>]
19641
+ 2. Use a different slug: cleo docs add <owner> <file> --slug <alternative>
19642
+ 3. Supersede with a new slug: cleo docs supersede {slug} <new-slug>
19643
+
19644
+ Recovery command: cleo docs update {slug} --file <your-file>`;
19035
19645
  _registryCache = /* @__PURE__ */ new Map();
19036
19646
  SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
19037
19647
  SLUG_MAX_LEN = 80;
@@ -19062,30 +19672,29 @@ var init_docs2 = __esm({
19062
19672
  "no scope set \u2014 defaulted to --project. Pass --task <id>, --session <id>, or --observation <id> for a narrower listing."
19063
19673
  );
19064
19674
  }
19065
- const store = createAttachmentStore();
19066
- const backend = await resolveAttachmentBackend();
19675
+ const model = createDocsReadModel();
19676
+ const backend = await currentAttachmentBackend();
19067
19677
  if (isProjectScope) {
19068
- const rows = await store.listAllInProject(
19069
- void 0,
19070
- typeFilter !== void 0 ? { type: typeFilter } : void 0
19071
- );
19072
- const projected = rows.map(({ metadata: m, slug, type: type2, ownerId: oid, ownerType: ot }) => ({
19073
- id: m.id,
19074
- sha256: `${m.sha256.slice(0, 8)}\u2026`,
19075
- // T9792 — keep the full sha256 for stable cross-row sorting; the
19076
- // displayed truncated form is preserved on the wire field above.
19077
- _sortSha: m.sha256,
19078
- kind: m.attachment.kind,
19079
- mime: m.attachment.kind === "local-file" || m.attachment.kind === "blob" ? m.attachment.mime : m.attachment.mime ?? "\u2014",
19080
- size: m.attachment.kind === "local-file" || m.attachment.kind === "blob" ? m.attachment.size : void 0,
19081
- description: m.attachment.description,
19082
- labels: m.attachment.labels,
19083
- createdAt: m.createdAt,
19084
- refCount: m.refCount,
19085
- ...slug ? { slug } : {},
19086
- ...type2 ? { type: type2 } : {},
19087
- ownerId: oid,
19088
- ownerType: ot
19678
+ const docs = await model.listProjectDocs({
19679
+ kind: typeFilter,
19680
+ limit: Number.isFinite(effectiveLimit) ? effectiveLimit : void 0
19681
+ });
19682
+ const totalCount = docs.length;
19683
+ const projected = docs.map((doc) => ({
19684
+ id: doc.id,
19685
+ sha256: `${doc.sha256.slice(0, 8)}\u2026`,
19686
+ _sortSha: doc.sha256,
19687
+ kind: "blob",
19688
+ mime: doc.mimeType ?? "\u2014",
19689
+ size: doc.sizeBytes,
19690
+ description: void 0,
19691
+ labels: void 0,
19692
+ createdAt: doc.createdAt,
19693
+ refCount: 0,
19694
+ ...doc.slug ? { slug: doc.slug } : {},
19695
+ ...doc.kind ? { type: doc.kind } : {},
19696
+ ownerId: doc.ownerId,
19697
+ ownerType: doc.ownerType
19089
19698
  }));
19090
19699
  projected.sort(
19091
19700
  (a, b) => comparator(
@@ -19093,12 +19702,10 @@ var init_docs2 = __esm({
19093
19702
  { sha256: b._sortSha, slug: b.slug, createdAt: b.createdAt }
19094
19703
  )
19095
19704
  );
19096
- const totalCount = projected.length;
19097
- const sliced = Number.isFinite(effectiveLimit) ? projected.slice(0, effectiveLimit) : projected;
19098
- const truncated = totalCount > sliced.length;
19705
+ const truncated = totalCount > projected.length;
19099
19706
  if (truncated) {
19100
19707
  hintParts.push(
19101
- `showing ${sliced.length} of ${totalCount} \u2014 pass --limit <N> to widen or page further.`
19708
+ `showing ${projected.length} of ${totalCount} \u2014 pass --limit <N> to widen or page further.`
19102
19709
  );
19103
19710
  }
19104
19711
  return lafsSuccess(
@@ -19107,12 +19714,12 @@ var init_docs2 = __esm({
19107
19714
  ownerType: "task",
19108
19715
  project: true,
19109
19716
  ...typeFilter !== void 0 ? { type: typeFilter } : {},
19110
- count: sliced.length,
19717
+ count: projected.length,
19111
19718
  ...truncated ? { totalCount } : {},
19112
19719
  limit: Number.isFinite(effectiveLimit) ? effectiveLimit : 0,
19113
19720
  orderBy,
19114
19721
  ...hintParts.length > 0 ? { hint: hintParts.join(" ") } : {},
19115
- attachments: sliced.map(({ _sortSha: _drop, ...row }) => row),
19722
+ attachments: projected.map(({ _sortSha: _drop, ...row }) => row),
19116
19723
  attachmentBackend: backend
19117
19724
  },
19118
19725
  "list"
@@ -19120,28 +19727,20 @@ var init_docs2 = __esm({
19120
19727
  }
19121
19728
  const scopedOwner = ownerId;
19122
19729
  const ownerType = inferOwnerType(scopedOwner);
19123
- const ownerAttachments = await store.listByOwner(ownerType, scopedOwner);
19124
- const enriched = [];
19125
- for (const m of ownerAttachments) {
19126
- const extras = await store.getExtras(m.id);
19127
- const slug = extras?.slug ?? null;
19128
- const type2 = extras?.type ?? null;
19129
- if (typeFilter !== void 0 && type2 !== typeFilter) continue;
19130
- enriched.push({ meta: m, slug, type: type2 });
19131
- }
19132
- const projectedOwner = enriched.map(({ meta: m, slug, type: type2 }) => ({
19133
- id: m.id,
19134
- sha256: `${m.sha256.slice(0, 8)}\u2026`,
19135
- _sortSha: m.sha256,
19136
- kind: m.attachment.kind,
19137
- mime: m.attachment.kind === "local-file" || m.attachment.kind === "blob" ? m.attachment.mime : m.attachment.mime ?? "\u2014",
19138
- size: m.attachment.kind === "local-file" || m.attachment.kind === "blob" ? m.attachment.size : void 0,
19139
- description: m.attachment.description,
19140
- labels: m.attachment.labels,
19141
- createdAt: m.createdAt,
19142
- refCount: m.refCount,
19143
- ...slug ? { slug } : {},
19144
- ...type2 ? { type: type2 } : {}
19730
+ const ownerDocs = await model.resolveByOwner(scopedOwner, { ownerType, kind: typeFilter });
19731
+ const projectedOwner = ownerDocs.map((doc) => ({
19732
+ id: doc.id,
19733
+ sha256: `${doc.sha256.slice(0, 8)}\u2026`,
19734
+ _sortSha: doc.sha256,
19735
+ kind: "blob",
19736
+ mime: doc.mimeType ?? "\u2014",
19737
+ size: doc.sizeBytes,
19738
+ description: void 0,
19739
+ labels: void 0,
19740
+ createdAt: doc.createdAt,
19741
+ refCount: 0,
19742
+ ...doc.slug ? { slug: doc.slug } : {},
19743
+ ...doc.kind ? { type: doc.kind } : {}
19145
19744
  }));
19146
19745
  projectedOwner.sort(
19147
19746
  (a, b) => comparator(
@@ -19168,7 +19767,6 @@ var init_docs2 = __esm({
19168
19767
  orderBy,
19169
19768
  ...hintParts.length > 0 ? { hint: hintParts.join(" ") } : {},
19170
19769
  attachments: slicedOwner.map(({ _sortSha: _drop, ...row }) => row),
19171
- // Cast: core returns 'llmtxt'|'legacy'; contracts uses 'legacy'|'llmstxt-v2' (drift T1529)
19172
19770
  attachmentBackend: backend
19173
19771
  },
19174
19772
  "list"
@@ -19221,6 +19819,79 @@ var init_docs2 = __esm({
19221
19819
  "generate"
19222
19820
  );
19223
19821
  },
19822
+ // ── docs.llm-output (T11137) ──────────────────────────────────────────────
19823
+ "llm-output": async (params) => {
19824
+ const forId = params.for;
19825
+ if (!forId) {
19826
+ return lafsError("E_INVALID_INPUT", "--for <entityId> is required", "llm-output");
19827
+ }
19828
+ let mode;
19829
+ if (params.mode !== void 0) {
19830
+ const raw = String(params.mode);
19831
+ if (!LLM_OUTPUT_MODES.includes(raw)) {
19832
+ return lafsError(
19833
+ "E_INVALID_INPUT",
19834
+ `--mode must be one of: ${LLM_OUTPUT_MODES.join("|")}`,
19835
+ "llm-output"
19836
+ );
19837
+ }
19838
+ mode = raw;
19839
+ } else {
19840
+ mode = /^T\d+$/i.test(forId) ? "task-export" : "attachment-bundle";
19841
+ }
19842
+ const cwd = getProjectRoot5();
19843
+ if (mode === "task-export") {
19844
+ const result2 = await exportTaskDocument({
19845
+ taskId: forId,
19846
+ includeAttachments: params.includeAttachments !== false,
19847
+ includeMemoryRefs: params.includeMemoryRefs === true,
19848
+ projectRoot: cwd
19849
+ });
19850
+ return lafsSuccess(
19851
+ {
19852
+ forId,
19853
+ mode: "task-export",
19854
+ content: result2.markdown,
19855
+ sectionCount: result2.pages,
19856
+ usedLlmtxtPackage: true
19857
+ },
19858
+ "llm-output"
19859
+ );
19860
+ }
19861
+ const result = await generateDocsLlmsTxt({ ownerId: forId, cwd });
19862
+ let aid, asha;
19863
+ if (params.attach) {
19864
+ const store = createAttachmentStore();
19865
+ const desc = {
19866
+ kind: "llms-txt",
19867
+ source: "generated",
19868
+ content: result.content,
19869
+ description: `llms.txt for ${forId}`,
19870
+ labels: ["llms-txt", "generated"]
19871
+ };
19872
+ const meta = await store.put(
19873
+ Buffer.from(result.content, "utf-8"),
19874
+ desc,
19875
+ inferOwnerType(forId),
19876
+ forId,
19877
+ "cleo-docs-llm-output",
19878
+ cwd
19879
+ );
19880
+ aid = meta.id;
19881
+ asha = meta.sha256;
19882
+ }
19883
+ return lafsSuccess(
19884
+ {
19885
+ forId,
19886
+ mode: "attachment-bundle",
19887
+ content: result.content,
19888
+ sectionCount: result.attachmentCount,
19889
+ usedLlmtxtPackage: result.usedLlmtxtPackage,
19890
+ ...aid ? { attached: true, attachmentId: aid, attachmentSha256: asha } : { attached: false }
19891
+ },
19892
+ "llm-output"
19893
+ );
19894
+ },
19224
19895
  // ── docs.fetch ─────────────────────────────────────────────────────────────
19225
19896
  fetch: async (params) => {
19226
19897
  const ref = params.attachmentRef;
@@ -19231,103 +19902,53 @@ var init_docs2 = __esm({
19231
19902
  "fetch"
19232
19903
  );
19233
19904
  }
19234
- const store = createAttachmentStore();
19235
- const isSha256 = /^[0-9a-f]{64}$/i.test(ref);
19236
- const looksLikeAttId = /^(att_|[0-9a-f]{8}-)/i.test(ref);
19237
- const looksLikeSlug = SLUG_PATTERN.test(ref) && !/^[0-9a-f]+$/i.test(ref);
19238
- const isHexPrefix = /^[0-9a-f]{6,63}$/i.test(ref);
19239
- let fetchResult = null;
19240
- let metadata = isSha256 ? null : looksLikeAttId ? await store.getMetadata(ref) : null;
19241
- if (metadata) {
19242
- fetchResult = await store.get(metadata.sha256);
19243
- } else if (isSha256) {
19244
- fetchResult = await store.get(ref);
19245
- if (fetchResult) {
19246
- metadata = fetchResult.metadata;
19247
- }
19248
- }
19249
- if (!metadata && looksLikeSlug) {
19250
- const bySlug = await store.findBySlug(ref);
19251
- if (bySlug) {
19252
- metadata = bySlug.metadata;
19253
- fetchResult = await store.get(metadata.sha256);
19254
- }
19255
- }
19256
- if (!metadata && isHexPrefix) {
19257
- const projectList = await store.listAllInProject();
19258
- const seen = /* @__PURE__ */ new Map();
19259
- for (const row of projectList) {
19260
- if (row.metadata.sha256.toLowerCase().startsWith(ref.toLowerCase())) {
19261
- seen.set(row.metadata.id, row);
19262
- }
19263
- }
19264
- if (seen.size > 1) {
19265
- return lafsError(
19266
- "E_AMBIGUOUS",
19267
- `sha256 prefix '${ref}' matches ${seen.size} attachments \u2014 provide more hex digits`,
19268
- "fetch"
19269
- );
19270
- }
19271
- const hit = seen.values().next().value;
19272
- if (hit) {
19273
- metadata = hit.metadata;
19274
- fetchResult = await store.get(metadata.sha256);
19275
- }
19276
- }
19277
- if (!metadata && !looksLikeAttId) {
19278
- const direct = await store.getMetadata(ref);
19279
- if (direct) {
19280
- metadata = direct;
19281
- fetchResult = await store.get(direct.sha256);
19282
- }
19283
- }
19284
- if (!fetchResult || !metadata) {
19905
+ const model = createDocsReadModel();
19906
+ const doc = await model.resolveLatest(ref) ?? await model.resolveByAttachmentId(ref);
19907
+ if (!doc) {
19285
19908
  return lafsError("E_NOT_FOUND", `Attachment not found: ${ref}`, "fetch");
19286
19909
  }
19910
+ const content = await model.fetchContent(doc);
19911
+ if (content === null) {
19912
+ return lafsError("E_NOT_FOUND", `Content not retrievable: ${ref}`, "fetch");
19913
+ }
19287
19914
  const cwd = getProjectRoot5();
19288
19915
  const cleoDir = resolveCanonicalCleoDir(resolveProjectByCwd(cwd));
19289
19916
  let storagePath;
19290
- if (metadata.attachment.kind === "local-file") {
19291
- storagePath = metadata.attachment.path;
19292
- } else if (metadata.attachment.kind === "blob") {
19293
- const prefix = metadata.sha256.slice(0, 2);
19294
- const rest = metadata.sha256.slice(2);
19917
+ if (doc.sha256) {
19918
+ const prefix = doc.sha256.slice(0, 2);
19919
+ const rest = doc.sha256.slice(2);
19295
19920
  const extMap = {
19296
19921
  "text/markdown": ".md",
19297
19922
  "text/plain": ".txt",
19298
19923
  "application/json": ".json",
19299
19924
  "application/pdf": ".pdf"
19300
19925
  };
19301
- const mime = metadata.attachment.kind === "blob" ? metadata.attachment.mime : "application/octet-stream";
19302
- const ext = extMap[mime] ?? ".bin";
19926
+ const ext = extMap[doc.mimeType ?? ""] ?? ".bin";
19303
19927
  storagePath = resolve2(cleoDir, "attachments", "sha256", prefix, `${rest}${ext}`);
19304
19928
  }
19305
19929
  const MAX_INLINE = 1024 * 1024;
19306
- const bytesBase64 = fetchResult.bytes.length <= MAX_INLINE ? fetchResult.bytes.toString("base64") : void 0;
19307
- const backend = await resolveAttachmentBackend();
19308
- const extras = await store.getExtras(metadata.id);
19930
+ const contentBytes = Buffer.from(content, "utf-8");
19931
+ const bytesBase64 = contentBytes.length <= MAX_INLINE ? contentBytes.toString("base64") : void 0;
19932
+ const backend = await currentAttachmentBackend();
19309
19933
  return lafsSuccess(
19310
19934
  {
19311
- // Project the domain AttachmentMetadata (nested `attachment` object) into the
19312
- // flat DocsAttachmentRow wire format consumed by CLI + HTTP callers.
19313
19935
  metadata: {
19314
- id: metadata.id,
19315
- sha256: metadata.sha256,
19316
- kind: metadata.attachment.kind,
19317
- mime: metadata.attachment.kind === "local-file" || metadata.attachment.kind === "blob" ? metadata.attachment.mime : metadata.attachment.mime,
19318
- size: metadata.attachment.kind === "local-file" || metadata.attachment.kind === "blob" ? metadata.attachment.size : void 0,
19319
- description: metadata.attachment.description,
19320
- labels: metadata.attachment.labels,
19321
- createdAt: metadata.createdAt,
19322
- refCount: metadata.refCount,
19323
- ...extras?.slug ? { slug: extras.slug } : {},
19324
- ...extras?.type ? { type: extras.type } : {}
19936
+ id: doc.id,
19937
+ sha256: doc.sha256,
19938
+ kind: "blob",
19939
+ mime: doc.mimeType ?? "text/plain",
19940
+ size: doc.sizeBytes,
19941
+ description: doc.summary ?? void 0,
19942
+ labels: void 0,
19943
+ createdAt: doc.createdAt,
19944
+ refCount: 0,
19945
+ ...doc.slug ? { slug: doc.slug } : {},
19946
+ ...doc.kind ? { type: doc.kind } : {}
19325
19947
  },
19326
19948
  path: storagePath,
19327
- sizeBytes: fetchResult.bytes.length,
19949
+ sizeBytes: contentBytes.length,
19328
19950
  ...bytesBase64 !== void 0 ? { bytesBase64 } : {},
19329
19951
  inlined: bytesBase64 !== void 0,
19330
- // Cast: core returns 'llmtxt'|'legacy'; contracts uses 'legacy'|'llmstxt-v2' (T1529)
19331
19952
  attachmentBackend: backend
19332
19953
  },
19333
19954
  "fetch"
@@ -19475,6 +20096,17 @@ var init_docs2 = __esm({
19475
20096
  type: "changeset"
19476
20097
  };
19477
20098
  emitDocAttachmentObservation(changesetPayload, getProjectRoot5());
20099
+ try {
20100
+ writeAuditEntry(getProjectRoot5(), {
20101
+ op: "docs.add",
20102
+ slug: outcome.result.slug,
20103
+ type: "changeset",
20104
+ attachmentId: outcome.result.attachmentId,
20105
+ sha256: outcome.result.sha256,
20106
+ summary: `Added changeset '${outcome.result.slug}'`
20107
+ });
20108
+ } catch {
20109
+ }
19478
20110
  return lafsSuccess(
19479
20111
  {
19480
20112
  attachmentId: outcome.result.attachmentId,
@@ -19487,9 +20119,8 @@ var init_docs2 = __esm({
19487
20119
  kind: "blob",
19488
20120
  ownerId: outcome.result.ownerId,
19489
20121
  ownerType: "task",
19490
- // Changeset writes land in the legacy attachment store; v2 mirror
19491
- // is not applicable to the dual-write transaction.
19492
- attachmentBackend: "legacy",
20122
+ // Cast: core returns 'llmtxt' (Wave C — legacy backend retired for mirror store).
20123
+ attachmentBackend: await currentAttachmentBackend(),
19493
20124
  slug: outcome.result.slug,
19494
20125
  type: "changeset"
19495
20126
  },
@@ -19530,7 +20161,8 @@ var init_docs2 = __esm({
19530
20161
  success: false,
19531
20162
  error: {
19532
20163
  code: "E_SLUG_RESERVED",
19533
- message: `slug '${slug}' is already in use in this project`,
20164
+ message: SLUG_COLLISION_GUIDANCE.replaceAll("{slug}", slug ?? ""),
20165
+ fix: `cleo docs update ${slug ?? "<slug>"} --file <your-file>`,
19534
20166
  details: {
19535
20167
  suggestions: reservation.suggestions,
19536
20168
  aliases: ["E_SLUG_TAKEN"]
@@ -19611,7 +20243,8 @@ var init_docs2 = __esm({
19611
20243
  success: false,
19612
20244
  error: {
19613
20245
  code: "E_SLUG_RESERVED",
19614
- message: `slug '${err.slug}' is already in use in this project`,
20246
+ message: SLUG_COLLISION_GUIDANCE.replaceAll("{slug}", err.slug ?? ""),
20247
+ fix: `cleo docs update ${err.slug ?? "<slug>"} --file <your-file>`,
19615
20248
  details: {
19616
20249
  suggestions: err.suggestions,
19617
20250
  aliases: ["E_SLUG_TAKEN"]
@@ -19622,17 +20255,17 @@ var init_docs2 = __esm({
19622
20255
  throw err;
19623
20256
  }
19624
20257
  if (adrNumber !== void 0 && slug !== void 0) consumeReservedSlug(slug);
19625
- let backend = "legacy";
20258
+ let backend = "llmtxt";
19626
20259
  try {
19627
- const v2 = createAttachmentStoreV2(getProjectRoot5());
19628
- const v2Result = await v2.put(ownerId, {
20260
+ const blobMirror = createAttachmentBlobStore(getProjectRoot5());
20261
+ const mirrorResult = await blobMirror.put(ownerId, {
19629
20262
  name: absPath.split(/[\\/]/).pop() ?? meta.sha256.slice(0, 12),
19630
20263
  data: new Uint8Array(bytes),
19631
20264
  contentType: mime
19632
20265
  });
19633
- backend = v2Result.backend;
20266
+ backend = mirrorResult.backend;
19634
20267
  } catch {
19635
- backend = await resolveAttachmentBackend();
20268
+ backend = await currentAttachmentBackend();
19636
20269
  }
19637
20270
  import("@cleocode/core/internal").then(
19638
20271
  ({ ensureLlmtxtNode }) => ensureLlmtxtNode(
@@ -19652,6 +20285,18 @@ var init_docs2 = __esm({
19652
20285
  ...type2 !== void 0 ? { type: type2 } : {}
19653
20286
  };
19654
20287
  emitDocAttachmentObservation(filePayload, getProjectRoot5());
20288
+ try {
20289
+ writeAuditEntry(getProjectRoot5(), {
20290
+ op: "docs.add",
20291
+ slug,
20292
+ type: type2,
20293
+ attachmentId: meta.id,
20294
+ sha256: meta.sha256,
20295
+ ownerId,
20296
+ summary: `Added doc '${slug ?? meta.sha256.slice(0, 12)}'${type2 ? ` of type '${type2}'` : ""} for owner ${ownerId}`
20297
+ });
20298
+ } catch {
20299
+ }
19655
20300
  return lafsSuccess(
19656
20301
  {
19657
20302
  attachmentId: meta.id,
@@ -19695,7 +20340,8 @@ var init_docs2 = __esm({
19695
20340
  success: false,
19696
20341
  error: {
19697
20342
  code: "E_SLUG_RESERVED",
19698
- message: `slug '${err.slug}' is already in use in this project`,
20343
+ message: SLUG_COLLISION_GUIDANCE.replaceAll("{slug}", err.slug ?? ""),
20344
+ fix: `cleo docs update ${err.slug ?? "<slug>"} --file <your-file>`,
19699
20345
  details: {
19700
20346
  suggestions: err.suggestions,
19701
20347
  aliases: ["E_SLUG_TAKEN"]
@@ -19710,7 +20356,7 @@ var init_docs2 = __esm({
19710
20356
  ({ ensureLlmtxtNode }) => ensureLlmtxtNode(getProjectRoot5(), meta.sha256, `${ownerType}:${ownerId}`, url)
19711
20357
  ).catch(() => {
19712
20358
  });
19713
- const backend = "legacy";
20359
+ const backend = await currentAttachmentBackend();
19714
20360
  const urlPayload = {
19715
20361
  kind: "doc-attachment",
19716
20362
  attachmentId: meta.id,
@@ -19720,6 +20366,18 @@ var init_docs2 = __esm({
19720
20366
  ...type2 !== void 0 ? { type: type2 } : {}
19721
20367
  };
19722
20368
  emitDocAttachmentObservation(urlPayload, getProjectRoot5());
20369
+ try {
20370
+ writeAuditEntry(getProjectRoot5(), {
20371
+ op: "docs.add",
20372
+ slug,
20373
+ type: type2,
20374
+ attachmentId: meta.id,
20375
+ sha256: meta.sha256,
20376
+ ownerId,
20377
+ summary: `Added URL doc '${slug ?? url}'${type2 ? ` of type '${type2}'` : ""} for owner ${ownerId}`
20378
+ });
20379
+ } catch {
20380
+ }
19723
20381
  return lafsSuccess(
19724
20382
  {
19725
20383
  attachmentId: meta.id,
@@ -19774,11 +20432,20 @@ var init_docs2 = __esm({
19774
20432
  const blobPurged = derefResult.status === "removed";
19775
20433
  const refCountAfter = derefResult.status === "derefd" ? derefResult.refCountAfter : 0;
19776
20434
  try {
19777
- const v2 = createAttachmentStoreV2(getProjectRoot5());
19778
- await v2.remove(attachmentId, fromOwner);
20435
+ const blobMirror = createAttachmentBlobStore(getProjectRoot5());
20436
+ await blobMirror.remove(attachmentId, fromOwner);
20437
+ } catch {
20438
+ }
20439
+ const backend = await currentAttachmentBackend();
20440
+ try {
20441
+ writeAuditEntry(getProjectRoot5(), {
20442
+ op: "docs.remove",
20443
+ attachmentId,
20444
+ ownerId: fromOwner,
20445
+ summary: `Removed attachment ${attachmentId} from owner ${fromOwner}${blobPurged ? " (blob purged)" : ""}`
20446
+ });
19779
20447
  } catch {
19780
20448
  }
19781
- const backend = await resolveAttachmentBackend();
19782
20449
  return lafsSuccess(
19783
20450
  {
19784
20451
  removed: blobPurged,
@@ -19827,6 +20494,42 @@ var init_docs2 = __esm({
19827
20494
  "details" in outcome.error ? outcome.error.details : void 0
19828
20495
  );
19829
20496
  }
20497
+ let backend = "llmtxt";
20498
+ try {
20499
+ const blobMirror = createAttachmentBlobStore(getProjectRoot5());
20500
+ const updatedBytes = hasFile ? new Uint8Array(await readFile(resolve2(filePath))) : new Uint8Array(Buffer.from(inlineContent, "utf-8"));
20501
+ const contentType = hasFile ? mimeFromPath(resolve2(filePath)) : "text/plain";
20502
+ const store = createAttachmentStore();
20503
+ const rows = await store.listAllInProject(projectRoot);
20504
+ const ownerIds = Array.from(
20505
+ new Set(
20506
+ rows.filter((row) => row.metadata.id === outcome.result.attachmentId).map((row) => row.ownerId)
20507
+ )
20508
+ );
20509
+ const mirrorOwnerIds = ownerIds.length > 0 ? ownerIds : [`slug:${outcome.result.slug}`];
20510
+ for (const mirrorOwnerId of mirrorOwnerIds) {
20511
+ const mirrorResult = await blobMirror.put(mirrorOwnerId, {
20512
+ name: outcome.result.slug,
20513
+ data: updatedBytes,
20514
+ contentType
20515
+ });
20516
+ backend = mirrorResult.backend;
20517
+ }
20518
+ } catch {
20519
+ backend = await currentAttachmentBackend();
20520
+ }
20521
+ try {
20522
+ writeAuditEntry(getProjectRoot5(), {
20523
+ op: "docs.update",
20524
+ slug: outcome.result.slug,
20525
+ type: outcome.result.type ?? void 0,
20526
+ attachmentId: outcome.result.attachmentId,
20527
+ sha256: outcome.result.sha256,
20528
+ previousSha256: outcome.result.previousSha256 ?? void 0,
20529
+ summary: `Updated doc '${outcome.result.slug}'${outcome.result.lifecycleStatus ? ` (status: ${outcome.result.lifecycleStatus})` : ""}`
20530
+ });
20531
+ } catch {
20532
+ }
19830
20533
  return lafsSuccess(
19831
20534
  {
19832
20535
  slug: outcome.result.slug,
@@ -19839,7 +20542,15 @@ var init_docs2 = __esm({
19839
20542
  lifecycleStatus: outcome.result.lifecycleStatus,
19840
20543
  updatedAt: outcome.result.updatedAt,
19841
20544
  version: outcome.result.version,
20545
+ /** Version SSoT (T11181) — canonical version identifiers. */
20546
+ ownerVersion: outcome.result.ownerVersion,
20547
+ /** Sequential doc version counter (T11181). */
20548
+ docVersion: outcome.result.docVersion,
19842
20549
  squashed: outcome.result.squashed,
20550
+ summary: outcome.result.summary,
20551
+ // T11053 — surface which backend stored the updated blob so
20552
+ // observability consumers can confirm the V2 mirror succeeded.
20553
+ attachmentBackend: backend,
19843
20554
  ...outcome.result.dryRun === true ? { dryRun: true } : {},
19844
20555
  ...outcome.result.wouldWrite !== void 0 ? { wouldWrite: outcome.result.wouldWrite } : {},
19845
20556
  ...outcome.result.wouldChange !== void 0 ? { wouldChange: outcome.result.wouldChange } : {}
@@ -19871,6 +20582,15 @@ var init_docs2 = __esm({
19871
20582
  edgeId: result.edgeId,
19872
20583
  ...result.reason !== void 0 ? { reason: result.reason } : {}
19873
20584
  };
20585
+ try {
20586
+ writeAuditEntry(getProjectRoot5(), {
20587
+ op: "docs.supersede",
20588
+ slug: result.newSlug,
20589
+ attachmentId: result.newAttachmentId,
20590
+ summary: `Superseded '${result.oldSlug}' \u2192 '${result.newSlug}'${result.reason ? ` (reason: ${result.reason})` : ""}`
20591
+ });
20592
+ } catch {
20593
+ }
19874
20594
  return lafsSuccess(payload, "supersede");
19875
20595
  } catch (err) {
19876
20596
  const code = err && typeof err === "object" && "code" in err && typeof err.code === "number" ? (
@@ -46439,6 +47159,53 @@ var init_strict_args = __esm({
46439
47159
  }
46440
47160
  });
46441
47161
 
47162
+ // packages/cleo/src/cli/commands/docs/audit.ts
47163
+ var auditCommand3;
47164
+ var init_audit3 = __esm({
47165
+ "packages/cleo/src/cli/commands/docs/audit.ts"() {
47166
+ "use strict";
47167
+ init_src2();
47168
+ init_cli();
47169
+ init_define_cli_command();
47170
+ init_renderers();
47171
+ auditCommand3 = defineCommand({
47172
+ meta: {
47173
+ name: "audit",
47174
+ description: "Query the immutable docs audit trail (--slug <slug> or --verify for integrity check)"
47175
+ },
47176
+ args: {
47177
+ slug: {
47178
+ type: "string",
47179
+ description: "Show audit history for a specific document slug"
47180
+ },
47181
+ verify: {
47182
+ type: "boolean",
47183
+ description: "Verify checkpoint chain integrity across the full audit log"
47184
+ }
47185
+ },
47186
+ async run({ args }) {
47187
+ if (!args.slug && !args.verify) {
47188
+ cliError(
47189
+ "Pass --slug <slug> to view history or --verify to check integrity.",
47190
+ 1 /* GENERAL_ERROR */
47191
+ );
47192
+ return;
47193
+ }
47194
+ await dispatchFromCli(
47195
+ "query",
47196
+ "docs",
47197
+ "audit",
47198
+ {
47199
+ ...typeof args.slug === "string" ? { slug: args.slug } : {},
47200
+ ...args.verify === true ? { verify: true } : {}
47201
+ },
47202
+ { command: "docs audit" }
47203
+ );
47204
+ }
47205
+ });
47206
+ }
47207
+ });
47208
+
46442
47209
  // packages/cleo/src/cli/commands/docs/graph.ts
46443
47210
  import {
46444
47211
  buildDocProvenanceGraph,
@@ -46931,7 +47698,7 @@ async function spawnDetachedServer(opts) {
46931
47698
  if (handle) await handle.close();
46932
47699
  return child;
46933
47700
  }
46934
- var DETACHED_CHILD_ENV, serveCommand, openCommand, stopCommand4, viewerStatusCommand, docsViewerSubcommands;
47701
+ var DETACHED_CHILD_ENV, serveCommand, openCommand, stopCommand4, viewerStatusCommand, runViewerStatus, viewerCommand, docsViewerSubcommands;
46935
47702
  var init_docs_viewer = __esm({
46936
47703
  "packages/cleo/src/cli/commands/docs-viewer.ts"() {
46937
47704
  "use strict";
@@ -46944,7 +47711,7 @@ var init_docs_viewer = __esm({
46944
47711
  serveCommand = defineCommand({
46945
47712
  meta: {
46946
47713
  name: "serve",
46947
- description: "Run a local web viewer for published docs"
47714
+ description: "[legacy] Run docs viewer \u2014 prefer `cleo docs viewer start`"
46948
47715
  },
46949
47716
  args: {
46950
47717
  port: {
@@ -47115,7 +47882,7 @@ var init_docs_viewer = __esm({
47115
47882
  openCommand = defineCommand({
47116
47883
  meta: {
47117
47884
  name: "open",
47118
- description: "Open the docs viewer in the browser (auto-starts server if needed)"
47885
+ description: "[legacy] Open docs viewer in browser \u2014 prefer `cleo docs viewer open`"
47119
47886
  },
47120
47887
  args: {
47121
47888
  slug: {
@@ -47212,7 +47979,7 @@ var init_docs_viewer = __esm({
47212
47979
  stopCommand4 = defineCommand({
47213
47980
  meta: {
47214
47981
  name: "stop",
47215
- description: "Stop the running docs viewer (SIGTERM + cleanup pidfile)"
47982
+ description: "[legacy] Stop the docs viewer \u2014 prefer `cleo docs viewer stop`"
47216
47983
  },
47217
47984
  args: {
47218
47985
  timeout: {
@@ -47285,64 +48052,85 @@ var init_docs_viewer = __esm({
47285
48052
  viewerStatusCommand = defineCommand({
47286
48053
  meta: {
47287
48054
  name: "viewer-status",
47288
- description: "Report viewer running state (pid, port, url)"
48055
+ description: "[legacy] Report viewer state \u2014 prefer `cleo docs viewer status`"
47289
48056
  },
47290
48057
  async run() {
47291
- const record = await readViewerPidFile();
47292
- if (!record) {
47293
- cliOutput(
47294
- { running: false, pidFile: viewerPidFilePath() },
47295
- {
47296
- command: "docs viewer-status",
47297
- operation: "docs.viewer-status",
47298
- message: "viewer not running"
47299
- }
47300
- );
47301
- return;
47302
- }
47303
- const alive = isProcessAlive(record.pid);
47304
- if (!alive) {
47305
- await removeViewerPidFile();
47306
- cliOutput(
47307
- {
47308
- running: false,
47309
- reason: "stale pidfile",
47310
- pid: record.pid,
47311
- pidFile: viewerPidFilePath()
47312
- },
47313
- {
47314
- command: "docs viewer-status",
47315
- operation: "docs.viewer-status",
47316
- message: `stale pidfile removed (pid ${record.pid} not alive)`
47317
- }
47318
- );
47319
- return;
47320
- }
48058
+ await runViewerStatus();
48059
+ }
48060
+ });
48061
+ runViewerStatus = async () => {
48062
+ const record = await readViewerPidFile();
48063
+ if (!record) {
47321
48064
  cliOutput(
48065
+ { running: false, pidFile: viewerPidFilePath() },
47322
48066
  {
47323
- running: true,
48067
+ command: "docs viewer-status",
48068
+ operation: "docs.viewer-status",
48069
+ message: "viewer not running"
48070
+ }
48071
+ );
48072
+ return;
48073
+ }
48074
+ const alive = isProcessAlive(record.pid);
48075
+ if (!alive) {
48076
+ await removeViewerPidFile();
48077
+ cliOutput(
48078
+ {
48079
+ running: false,
48080
+ reason: "stale pidfile",
47324
48081
  pid: record.pid,
47325
- port: record.port,
47326
- host: record.host,
47327
- projectRoot: record.projectRoot,
47328
- startedAt: record.startedAt,
47329
- uptimeMs: Date.now() - record.startedAt,
47330
- url: `http://${record.host}:${record.port}`,
47331
48082
  pidFile: viewerPidFilePath()
47332
48083
  },
47333
48084
  {
47334
48085
  command: "docs viewer-status",
47335
48086
  operation: "docs.viewer-status",
47336
- message: `viewer running (pid ${record.pid})`
48087
+ message: `stale pidfile removed (pid ${record.pid} not alive)`
47337
48088
  }
47338
48089
  );
48090
+ return;
48091
+ }
48092
+ cliOutput(
48093
+ {
48094
+ running: true,
48095
+ pid: record.pid,
48096
+ port: record.port,
48097
+ host: record.host,
48098
+ projectRoot: record.projectRoot,
48099
+ startedAt: record.startedAt,
48100
+ uptimeMs: Date.now() - record.startedAt,
48101
+ url: `http://${record.host}:${record.port}`,
48102
+ pidFile: viewerPidFilePath()
48103
+ },
48104
+ {
48105
+ command: "docs viewer-status",
48106
+ operation: "docs.viewer-status",
48107
+ message: `viewer running (pid ${record.pid})`
48108
+ }
48109
+ );
48110
+ };
48111
+ viewerCommand = defineCommand({
48112
+ meta: {
48113
+ name: "viewer",
48114
+ description: "Manage the docs web viewer lifecycle (start/stop/open/status)"
48115
+ },
48116
+ subCommands: {
48117
+ start: serveCommand,
48118
+ stop: stopCommand4,
48119
+ open: openCommand,
48120
+ status: viewerStatusCommand
48121
+ },
48122
+ async run({ cmd, rawArgs }) {
48123
+ const firstArg = rawArgs?.find((a) => !a.startsWith("-"));
48124
+ if (firstArg && cmd.subCommands && firstArg in cmd.subCommands) return;
48125
+ await runViewerStatus();
47339
48126
  }
47340
48127
  });
47341
48128
  docsViewerSubcommands = {
47342
48129
  serve: serveCommand,
47343
48130
  open: openCommand,
47344
48131
  stop: stopCommand4,
47345
- "viewer-status": viewerStatusCommand
48132
+ "viewer-status": viewerStatusCommand,
48133
+ viewer: viewerCommand
47346
48134
  };
47347
48135
  }
47348
48136
  });
@@ -47350,10 +48138,12 @@ var init_docs_viewer = __esm({
47350
48138
  // packages/cleo/src/cli/commands/docs.ts
47351
48139
  var docs_exports = {};
47352
48140
  __export(docs_exports, {
48141
+ _llmOutputCommand: () => _llmOutputCommand,
47353
48142
  docsCommand: () => docsCommand
47354
48143
  });
47355
48144
  import { appendFile, mkdir as mkdir2, readdir, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
47356
48145
  import { dirname as dirname8, isAbsolute as isAbsolute2, join as join21, resolve as resolve5 } from "node:path";
48146
+ import { pushWarning as pushWarning3 } from "@cleocode/core";
47357
48147
  import {
47358
48148
  CleoError as CleoError3,
47359
48149
  CounterMismatchError,
@@ -47494,7 +48284,7 @@ function loadCliRegistry(projectRoot) {
47494
48284
  throw err;
47495
48285
  }
47496
48286
  }
47497
- var docsOutputFlagHelp, docsOutputArgs, addCommand5, listCommand8, fetchCommand, removeCommand2, supersedeCommandArgs, supersedeCommand, generateCommand, exportCommand4, searchCommand, findCommand3, mergeCommand, rankCommand, docsUpdateOperation, docsUpdateSchema, docsUpdateArgs, docsUpdateCliArgs, updateCommand, versionsCommand, publishCommand2, publishPrCommand, syncCommand3, statusCommand7, gapCheckCommand, importCommand2, schemaCommand, listTypesCommand, docsCommand;
48287
+ var docsOutputFlagHelp, docsOutputArgs, addCommand5, listCommand8, fetchCommand, removeCommand2, supersedeCommandArgs, supersedeCommand, generateCommand, exportCommand4, _llmOutputCommand, llmOutputCommand, searchCommand, findCommand3, mergeCommand, queryCommand, rankCommand, docsUpdateOperation, docsUpdateSchema, docsUpdateArgs, docsUpdateCliArgs, updateCommand, versionsCommand, publishCommand2, publishPrCommand, checkCommand6, syncCommand3, statusCommand7, gapCheckCommand, importCommand2, schemaCommand, listTypesCommand, docsCommand;
47498
48288
  var init_docs3 = __esm({
47499
48289
  "packages/cleo/src/cli/commands/docs.ts"() {
47500
48290
  "use strict";
@@ -47506,6 +48296,7 @@ var init_docs3 = __esm({
47506
48296
  init_registry_args();
47507
48297
  init_strict_args();
47508
48298
  init_renderers();
48299
+ init_audit3();
47509
48300
  init_graph2();
47510
48301
  init_docs_viewer();
47511
48302
  docsOutputFlagHelp = " --json Emit the canonical LAFS JSON envelope (also accepted as a global flag)\n --output <mode> Re-render the result as envelope|id|table|count|silent (also accepted as a global flag)";
@@ -47522,7 +48313,7 @@ var init_docs3 = __esm({
47522
48313
  addCommand5 = defineCommand({
47523
48314
  meta: {
47524
48315
  name: "add",
47525
- description: 'Attach a local file or remote URL to a CLEO entity (task, session, observation). Owner type is inferred from the ID prefix: T### \u2192 task, ses_* \u2192 session, O-* \u2192 observation. Use --slug to set a human-friendly alias (unique per project) (T9636).\n\nPositional arguments:\n <owner-id> Owner entity ID (T###, ses_*, O-*) \u2014 required\n [file] Local file path to attach \u2014 optional when --url is set\n\nNamed arguments:\n --url <url> Remote URL to attach (instead of a local file)\n --desc <text> Free-text description of this attachment\n --labels <csv> Comma-separated labels (e.g. rfc,spec)\n --attached-by <name> Agent identity that created the attachment (default: "human")\n --slug <kebab> Human-friendly alias, unique per project (T9636)\n --title <text> Human-readable title \u2014 REQUIRED for --type adr when --slug is omitted (T10360)\n --type <kind> Taxonomy classification \u2014 run `cleo docs list-types` for kinds\n --allow-similar Bypass the slug-similarity warn \u2014 every bypass is audited\n to .cleo/audit/similar-bypass.jsonl (T10361)\n --strict Enforce body-schema (requiredSections) \u2014 fail with\n E_DOC_SCHEMA_MISMATCH instead of warning (T10160)\n' + docsOutputFlagHelp + "\n\nValidation behaviors:\n \u2022 Unknown flags \u2192 E_UNKNOWN_FLAG with did-you-mean suggestions (T10359)\n \u2022 Slug collision \u2192 E_SLUG_RESERVED + 3 alternative slugs (T10386)\n \u2022 Near-duplicate slug \u2192 W_SLUG_SIMILAR warning unless --allow-similar (T10361)\n \u2022 For --type adr without --slug, slug auto-allocates as `adr-NNN-<kebab-title>` via the\n central allocator (T10360 \u2014 closes T10153). --title is required in this case."
48316
+ description: 'Attach a local file or remote URL to a CLEO entity (task, session, observation). Owner type is inferred from the ID prefix: T### \u2192 task, ses_* \u2192 session, O-* \u2192 observation. Use --slug to set a human-friendly alias (unique per project) (T9636).\n\nPositional arguments:\n <owner-id> Owner entity ID (T###, ses_*, O-*) \u2014 required\n [file] Local file path to attach \u2014 optional when --url is set\n\nNamed arguments:\n --url <url> Remote URL to attach (instead of a local file)\n --desc <text> Free-text description of this attachment\n --labels <csv> Comma-separated labels (e.g. rfc,spec)\n --attached-by <name> Agent identity that created the attachment (default: "human")\n --slug <kebab> Human-friendly alias, unique per project (T9636)\n --title <text> Human-readable title \u2014 REQUIRED for --type adr when --slug is omitted (T10360)\n --type <kind> Taxonomy classification \u2014 run `cleo docs schema` for kinds\n --allow-similar Bypass the slug-similarity warn \u2014 every bypass is audited\n to .cleo/audit/similar-bypass.jsonl (T10361)\n --strict Enforce body-schema (requiredSections) \u2014 fail with\n E_DOC_SCHEMA_MISMATCH instead of warning (T10160)\n' + docsOutputFlagHelp + "\n\nValidation behaviors:\n \u2022 Unknown flags \u2192 E_UNKNOWN_FLAG with did-you-mean suggestions (T10359)\n \u2022 Slug collision \u2192 E_SLUG_RESERVED + 3 alternative slugs (T10386)\n \u2022 Near-duplicate slug \u2192 W_SLUG_SIMILAR warning unless --allow-similar (T10361)\n \u2022 For --type adr without --slug, slug auto-allocates as `adr-NNN-<kebab-title>` via the\n central allocator (T10360 \u2014 closes T10153). --title is required in this case."
47526
48317
  },
47527
48318
  args: {
47528
48319
  "owner-id": {
@@ -47561,7 +48352,7 @@ var init_docs3 = __esm({
47561
48352
  },
47562
48353
  type: {
47563
48354
  type: "string",
47564
- description: "Taxonomy classification \u2014 run `cleo docs list-types` to enumerate registered kinds (T9637 / T9788)"
48355
+ description: "Taxonomy classification \u2014 run `cleo docs schema` to enumerate registered kinds (T9637 / T9788 / T11142)"
47565
48356
  },
47566
48357
  "allow-similar": {
47567
48358
  type: "boolean",
@@ -47916,6 +48707,12 @@ var init_docs3 = __esm({
47916
48707
  },
47917
48708
  args: supersedeCommandArgs,
47918
48709
  async run({ args, rawArgs }) {
48710
+ pushWarning3({
48711
+ code: "W_DEPRECATED_COMMAND",
48712
+ message: "cleo docs supersede is deprecated - use `cleo docs update` for new work (T11179)",
48713
+ deprecated: "docs supersede",
48714
+ replacement: "docs update"
48715
+ });
47919
48716
  try {
47920
48717
  assertKnownFlags(rawArgs, supersedeCommandArgs, "docs supersede");
47921
48718
  } catch (err) {
@@ -47958,6 +48755,9 @@ var init_docs3 = __esm({
47958
48755
  }
47959
48756
  },
47960
48757
  async run({ args }) {
48758
+ humanInfo(
48759
+ `cleo: docs generate is deprecated \u2014 use \`cleo docs llm-output --for ${args.for} --mode attachment-bundle\` (T11137)`
48760
+ );
47961
48761
  await dispatchFromCli(
47962
48762
  "query",
47963
48763
  "docs",
@@ -48006,7 +48806,8 @@ var init_docs3 = __esm({
48006
48806
  const includeMemoryRefs = args["include-memory-refs"] === true;
48007
48807
  const projectRoot = getProjectRoot38();
48008
48808
  try {
48009
- const result = await dispatchDocsRaw("query", "export", {
48809
+ const result = await dispatchDocsRaw("query", "llm-output", {
48810
+ mode: "task-export",
48010
48811
  taskId,
48011
48812
  includeAttachments,
48012
48813
  includeMemoryRefs
@@ -48015,20 +48816,20 @@ var init_docs3 = __esm({
48015
48816
  if (typeof args.out === "string" && args.out.length > 0) {
48016
48817
  const outPath = isAbsolute2(args.out) ? args.out : resolve5(projectRoot, args.out);
48017
48818
  await mkdir2(dirname8(outPath), { recursive: true });
48018
- await writeFile2(outPath, result.markdown, "utf8");
48819
+ await writeFile2(outPath, result.content, "utf8");
48019
48820
  writtenPath = outPath;
48020
48821
  }
48021
48822
  if (args.json) {
48022
48823
  cliOutput(
48023
- { markdown: result.markdown, pages: result.pages, path: writtenPath ?? null },
48824
+ { markdown: result.content, pages: result.sectionCount, path: writtenPath ?? null },
48024
48825
  { command: "docs export", operation: "docs.export" }
48025
48826
  );
48026
48827
  } else {
48027
48828
  if (writtenPath) {
48028
- humanInfo(`Wrote ${result.pages} page(s) to ${writtenPath}`);
48829
+ humanInfo(`Wrote ${result.sectionCount} page(s) to ${writtenPath}`);
48029
48830
  } else {
48030
- process.stdout.write(result.markdown);
48031
- if (!result.markdown.endsWith("\n")) process.stdout.write("\n");
48831
+ process.stdout.write(result.content);
48832
+ if (!result.content.endsWith("\n")) process.stdout.write("\n");
48032
48833
  }
48033
48834
  }
48034
48835
  } catch (err) {
@@ -48040,6 +48841,167 @@ var init_docs3 = __esm({
48040
48841
  }
48041
48842
  }
48042
48843
  });
48844
+ _llmOutputCommand = defineCommand({
48845
+ meta: {
48846
+ name: "llm-output",
48847
+ description: "Unified LLM output: task export (rich Markdown with frontmatter + body + attachments + memory refs) or attachment-bundle (llms.txt summarising all attachments). Replaces `docs export` and `docs generate`."
48848
+ },
48849
+ args: {
48850
+ for: {
48851
+ type: "string",
48852
+ description: "Target entity ID (T###, ses_*, O-*, D-*, L-*, P-*)",
48853
+ required: true
48854
+ },
48855
+ mode: {
48856
+ type: "string",
48857
+ description: "Output mode: 'task-export' or 'attachment-bundle' (auto-detected)"
48858
+ },
48859
+ out: { type: "string", description: "Output file path. Omit for stdout." },
48860
+ "include-attachments": {
48861
+ type: "boolean",
48862
+ default: true,
48863
+ description: "Append attachment manifest (task-export, default: true)"
48864
+ },
48865
+ "include-memory-refs": {
48866
+ type: "boolean",
48867
+ default: false,
48868
+ description: "Append BRAIN memory refs (task-export, default: false)"
48869
+ },
48870
+ attach: {
48871
+ type: "boolean",
48872
+ description: "Save as llms-txt attachment on target (attachment-bundle)"
48873
+ },
48874
+ ...docsOutputArgs
48875
+ },
48876
+ async run({ args }) {
48877
+ const forId = String(args.for);
48878
+ const projectRoot = getProjectRoot38();
48879
+ try {
48880
+ const result = await dispatchDocsRaw("query", "llm-output", {
48881
+ for: forId,
48882
+ ...typeof args.mode === "string" ? { mode: args.mode } : {},
48883
+ includeAttachments: args["include-attachments"] !== false,
48884
+ includeMemoryRefs: args["include-memory-refs"] === true,
48885
+ ...args.attach ? { attach: true } : {}
48886
+ });
48887
+ let writtenPath;
48888
+ if (typeof args.out === "string" && args.out.length > 0) {
48889
+ const outPath = isAbsolute2(args.out) ? args.out : resolve5(projectRoot, args.out);
48890
+ await mkdir2(dirname8(outPath), { recursive: true });
48891
+ await writeFile2(outPath, result.content, "utf8");
48892
+ writtenPath = outPath;
48893
+ }
48894
+ if (args.json) {
48895
+ cliOutput(
48896
+ {
48897
+ forId: result.forId,
48898
+ mode: result.mode,
48899
+ content: result.content,
48900
+ sectionCount: result.sectionCount,
48901
+ usedLlmtxtPackage: result.usedLlmtxtPackage,
48902
+ attached: result.attached,
48903
+ attachmentId: result.attachmentId,
48904
+ attachmentSha256: result.attachmentSha256,
48905
+ path: writtenPath ?? null
48906
+ },
48907
+ { command: "docs llm-output", operation: "docs.llm-output" }
48908
+ );
48909
+ } else {
48910
+ if (writtenPath) {
48911
+ humanInfo(
48912
+ `Wrote ${result.sectionCount} ${result.mode === "task-export" ? "page(s)" : "section(s)"} to ${writtenPath}`
48913
+ );
48914
+ } else {
48915
+ process.stdout.write(result.content);
48916
+ if (!result.content.endsWith("\n")) process.stdout.write("\n");
48917
+ }
48918
+ }
48919
+ } catch (err) {
48920
+ const message = err instanceof Error ? err.message : String(err);
48921
+ cliError(`docs llm-output failed: ${message}`, 1 /* GENERAL_ERROR */, {
48922
+ name: "E_DOCS_LLM_OUTPUT_FAILED"
48923
+ });
48924
+ process.exit(1 /* GENERAL_ERROR */);
48925
+ }
48926
+ }
48927
+ });
48928
+ llmOutputCommand = defineCommand({
48929
+ meta: {
48930
+ name: "llm-output",
48931
+ description: "Unified LLM output: task export (rich Markdown) or attachment-bundle (llms.txt). Replaces `docs export` and `docs generate`."
48932
+ },
48933
+ args: {
48934
+ for: { type: "string", description: "Target entity ID", required: true },
48935
+ mode: {
48936
+ type: "string",
48937
+ description: "Output mode: task-export|attachment-bundle (auto-detected)"
48938
+ },
48939
+ out: { type: "string", description: "Output file path" },
48940
+ "include-attachments": {
48941
+ type: "boolean",
48942
+ default: true,
48943
+ description: "Include attachment manifest (task-export)"
48944
+ },
48945
+ "include-memory-refs": {
48946
+ type: "boolean",
48947
+ default: false,
48948
+ description: "Include memory refs (task-export)"
48949
+ },
48950
+ attach: { type: "boolean", description: "Save as llms-txt attachment (attachment-bundle)" },
48951
+ ...docsOutputArgs
48952
+ },
48953
+ async run({ args }) {
48954
+ const forId = String(args.for);
48955
+ const projectRoot = getProjectRoot38();
48956
+ try {
48957
+ const result = await dispatchDocsRaw("query", "llm-output", {
48958
+ for: forId,
48959
+ ...typeof args.mode === "string" ? { mode: args.mode } : {},
48960
+ includeAttachments: args["include-attachments"] !== false,
48961
+ includeMemoryRefs: args["include-memory-refs"] === true,
48962
+ ...args.attach ? { attach: true } : {}
48963
+ });
48964
+ let writtenPath;
48965
+ if (typeof args.out === "string" && args.out.length > 0) {
48966
+ const outPath = isAbsolute2(args.out) ? args.out : resolve5(projectRoot, args.out);
48967
+ await mkdir2(dirname8(outPath), { recursive: true });
48968
+ await writeFile2(outPath, result.content, "utf8");
48969
+ writtenPath = outPath;
48970
+ }
48971
+ if (args.json) {
48972
+ cliOutput(
48973
+ {
48974
+ forId: result.forId,
48975
+ mode: result.mode,
48976
+ content: result.content,
48977
+ sectionCount: result.sectionCount,
48978
+ usedLlmtxtPackage: result.usedLlmtxtPackage,
48979
+ attached: result.attached,
48980
+ attachmentId: result.attachmentId,
48981
+ attachmentSha256: result.attachmentSha256,
48982
+ path: writtenPath ?? null
48983
+ },
48984
+ { command: "docs llm-output", operation: "docs.llm-output" }
48985
+ );
48986
+ } else {
48987
+ if (writtenPath) {
48988
+ humanInfo(
48989
+ `Wrote ${result.sectionCount} ${result.mode === "task-export" ? "page(s)" : "section(s)"} to ${writtenPath}`
48990
+ );
48991
+ } else {
48992
+ process.stdout.write(result.content);
48993
+ if (!result.content.endsWith("\n")) process.stdout.write("\n");
48994
+ }
48995
+ }
48996
+ } catch (err) {
48997
+ const message = err instanceof Error ? err.message : String(err);
48998
+ cliError(`docs llm-output failed: ${message}`, 1 /* GENERAL_ERROR */, {
48999
+ name: "E_DOCS_LLM_OUTPUT_FAILED"
49000
+ });
49001
+ process.exit(1 /* GENERAL_ERROR */);
49002
+ }
49003
+ }
49004
+ });
48043
49005
  searchCommand = defineCommand({
48044
49006
  meta: {
48045
49007
  name: "search",
@@ -48242,6 +49204,157 @@ var init_docs3 = __esm({
48242
49204
  }
48243
49205
  }
48244
49206
  });
49207
+ queryCommand = defineCommand({
49208
+ meta: {
49209
+ name: "query",
49210
+ description: 'Unified docs query surface: semantic search, similar-doc discovery, and entity ranking. Subsumes the legacy search, find, and rank subcommands into one consistent surface.\n\nModes (mutually exclusive):\n cleo docs query "<text>" Free-text search\n cleo docs query --similar <slug> Find similar to a slug\n cleo docs query --for <id> Rank for an entity\n\nCommon flags: --limit <n>, --type <kind>, --json\nFree-text search flags: --owner <id>\nSimilar-to-slug flags: --threshold <0..1>, --all-kinds\nEntity-ranking flags: --text "<query>"\n\n' + docsOutputFlagHelp
49211
+ },
49212
+ args: {
49213
+ query: {
49214
+ type: "positional",
49215
+ description: "Free-text query for semantic search",
49216
+ required: false
49217
+ },
49218
+ similar: {
49219
+ type: "string",
49220
+ description: "Slug of seed doc to find similar docs against (find mode)"
49221
+ },
49222
+ for: {
49223
+ type: "string",
49224
+ description: "Owner entity ID to rank attachments for (rank mode)"
49225
+ },
49226
+ text: {
49227
+ type: "string",
49228
+ description: "Custom query string for the rank mode (default: owner ID)"
49229
+ },
49230
+ type: {
49231
+ type: "string",
49232
+ description: "Filter by taxonomy type: spec|adr|research|handoff|note|llm-readme"
49233
+ },
49234
+ owner: {
49235
+ type: "string",
49236
+ description: "Scope free-text search to a specific owner entity ID"
49237
+ },
49238
+ limit: {
49239
+ type: "string",
49240
+ description: "Maximum number of results to return (default: 10)"
49241
+ },
49242
+ threshold: {
49243
+ type: "string",
49244
+ description: "Minimum cosine similarity score in [0, 1] (for --similar mode, default: 0.5)"
49245
+ },
49246
+ "all-kinds": {
49247
+ type: "boolean",
49248
+ description: "Disable the same-kind filter and rank across every DocKind (for --similar mode)"
49249
+ },
49250
+ ...docsOutputArgs
49251
+ },
49252
+ async run({ args, rawArgs }) {
49253
+ try {
49254
+ assertKnownFlags(rawArgs, queryCommand.args, "docs query");
49255
+ } catch (err) {
49256
+ if (err instanceof UnknownFlagError) {
49257
+ cliError(err.message, 6 /* VALIDATION_ERROR */, {
49258
+ name: err.code,
49259
+ fix: err.fix,
49260
+ alternatives: err.suggestions.map((s) => ({ action: s, command: s })),
49261
+ details: { flag: err.flag, knownFlags: err.knownFlags }
49262
+ });
49263
+ process.exit(6 /* VALIDATION_ERROR */);
49264
+ }
49265
+ throw err;
49266
+ }
49267
+ const similarSlug = typeof args.similar === "string" ? args.similar.trim() : "";
49268
+ const forId = typeof args.for === "string" ? args.for.trim() : "";
49269
+ const textQuery = typeof args.query === "string" ? String(args.query) : "";
49270
+ const customQuery = typeof args.text === "string" ? String(args.text) : void 0;
49271
+ const typeFilter = typeof args.type === "string" ? String(args.type) : void 0;
49272
+ const ownerScope = typeof args.owner === "string" ? String(args.owner) : void 0;
49273
+ const modes = [];
49274
+ if (similarSlug.length > 0) modes.push("--similar");
49275
+ if (forId.length > 0) modes.push("--for");
49276
+ if (textQuery.length > 0) modes.push("<query>");
49277
+ if (modes.length === 0) {
49278
+ cliError(
49279
+ "docs query requires a query mode: pass a free-text <query>, --similar <slug>, or --for <id>",
49280
+ 6 /* VALIDATION_ERROR */,
49281
+ {
49282
+ name: "E_VALIDATION",
49283
+ fix: 'Examples: cleo docs query "authentication flow" | cleo docs query --similar adr-073 | cleo docs query --for T123'
49284
+ }
49285
+ );
49286
+ process.exit(6 /* VALIDATION_ERROR */);
49287
+ }
49288
+ if (modes.length > 1) {
49289
+ cliError(
49290
+ `docs query modes are mutually exclusive \u2014 got ${modes.join(", ")}. Choose one.`,
49291
+ 6 /* VALIDATION_ERROR */,
49292
+ {
49293
+ name: "E_VALIDATION",
49294
+ fix: "Pass exactly one of: free-text <query>, --similar <slug>, or --for <id>"
49295
+ }
49296
+ );
49297
+ process.exit(6 /* VALIDATION_ERROR */);
49298
+ }
49299
+ let limit;
49300
+ if (typeof args.limit === "string") {
49301
+ const parsed = Number.parseInt(args.limit, 10);
49302
+ if (!Number.isFinite(parsed) || parsed <= 0) {
49303
+ cliError(
49304
+ `--limit must be a positive integer (got "${args.limit}")`,
49305
+ 6 /* VALIDATION_ERROR */,
49306
+ { name: "E_VALIDATION" }
49307
+ );
49308
+ process.exit(6 /* VALIDATION_ERROR */);
49309
+ }
49310
+ limit = parsed;
49311
+ }
49312
+ let threshold;
49313
+ if (typeof args.threshold === "string") {
49314
+ const parsed = Number.parseFloat(args.threshold);
49315
+ if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) {
49316
+ cliError(
49317
+ `--threshold must be a number in [0, 1] (got "${args.threshold}")`,
49318
+ 6 /* VALIDATION_ERROR */,
49319
+ { name: "E_VALIDATION" }
49320
+ );
49321
+ process.exit(6 /* VALIDATION_ERROR */);
49322
+ }
49323
+ threshold = parsed;
49324
+ }
49325
+ const allKinds = args["all-kinds"] === true;
49326
+ try {
49327
+ if (similarSlug.length > 0) {
49328
+ const result = await dispatchDocsRaw("query", "find", {
49329
+ similarSlug,
49330
+ ...limit !== void 0 ? { limit } : {},
49331
+ ...threshold !== void 0 ? { threshold } : {},
49332
+ allKinds
49333
+ });
49334
+ cliOutput(result, { command: "docs query", operation: "docs.find" });
49335
+ } else if (forId.length > 0) {
49336
+ const result = await dispatchDocsRaw("query", "rank", {
49337
+ ownerId: forId,
49338
+ query: customQuery ?? void 0
49339
+ });
49340
+ cliOutput(result, { command: "docs query", operation: "docs.rank" });
49341
+ } else {
49342
+ const result = await dispatchDocsRaw("query", "search", {
49343
+ query: textQuery,
49344
+ ...ownerScope ? { ownerId: ownerScope } : {},
49345
+ ...limit !== void 0 ? { limit } : {},
49346
+ ...typeFilter ? { type: typeFilter } : {}
49347
+ });
49348
+ cliOutput(result, { command: "docs query", operation: "docs.search" });
49349
+ }
49350
+ } catch (err) {
49351
+ const message = err instanceof Error ? err.message : String(err);
49352
+ const code = err instanceof Error && typeof err.code === "string" ? err.code : "E_DOCS_QUERY_FAILED";
49353
+ cliError(`docs query failed: ${message}`, 1 /* GENERAL_ERROR */, { name: code });
49354
+ process.exit(1 /* GENERAL_ERROR */);
49355
+ }
49356
+ }
49357
+ });
48245
49358
  rankCommand = defineCommand({
48246
49359
  meta: {
48247
49360
  name: "rank",
@@ -48392,34 +49505,125 @@ var init_docs3 = __esm({
48392
49505
  publishCommand2 = defineCommand({
48393
49506
  meta: {
48394
49507
  name: "publish",
48395
- description: "Atomically publish an attachment from the docs SSoT to a git-tracked file path. Uses tmp-then-rename for atomicity. The --to path may be absolute or relative to project root."
49508
+ description: "Publish a doc to a local file (--target file) or a GitHub PR (--target pr). Default target is file. Use --target pr with a slug-or-id to open/update a PR. Use --dry-run to preview without side effects."
48396
49509
  },
48397
49510
  args: {
49511
+ "slug-or-id": {
49512
+ type: "positional",
49513
+ description: "Slug, attachment id, or full sha256 hex of the doc to publish. Required for --target pr."
49514
+ },
49515
+ target: {
49516
+ type: "string",
49517
+ description: "Publish target: file (local git-tracked path) or pr (GitHub PR). Default: file.",
49518
+ default: "file"
49519
+ },
48398
49520
  for: {
48399
49521
  type: "string",
48400
- description: "Owner entity ID whose attachment to publish (T###, ses_*, O-*)",
48401
- required: true
49522
+ description: "Owner entity ID whose attachment to publish (T###, ses_*, O-*). Required for --target file."
48402
49523
  },
48403
49524
  to: {
48404
49525
  type: "string",
48405
- description: "Destination file path (absolute or relative to project root)",
48406
- required: true
49526
+ description: "Destination file path (absolute or relative to project root). Required for --target file."
48407
49527
  },
48408
49528
  attachment: {
48409
49529
  type: "string",
48410
49530
  description: "Specific attachment ID or SHA-256 to publish (default: latest)"
48411
49531
  },
48412
- json: {
49532
+ slug: {
49533
+ type: "string",
49534
+ description: "Override the slug used for the branch + filename. Required when <slug-or-id> is an attachment id or sha256 with no stored slug."
49535
+ },
49536
+ type: {
49537
+ type: "string",
49538
+ description: "Override the publish dir taxonomy (spec|adr|research|handoff|note|llm-readme)."
49539
+ },
49540
+ title: {
49541
+ type: "string",
49542
+ description: "Override the PR title. Default: `docs(<type>): publish <slug>`."
49543
+ },
49544
+ body: {
49545
+ type: "string",
49546
+ description: "Override the PR body. Default: an auto-generated summary."
49547
+ },
49548
+ base: {
49549
+ type: "string",
49550
+ description: "Base branch for the PR. Default: main."
49551
+ },
49552
+ "dry-run": {
48413
49553
  type: "boolean",
48414
- description: "Emit LAFS JSON envelope"
48415
- }
49554
+ description: "Preview what would happen without side effects. Reports resolved target, mode, and parameters. No files are written and no git/gh commands are invoked."
49555
+ },
49556
+ json: { type: "boolean", description: "Emit LAFS JSON envelope" }
48416
49557
  },
48417
49558
  async run({ args }) {
49559
+ const target = String(args.target ?? "file");
49560
+ const dryRun = args["dry-run"] === true;
49561
+ if (dryRun) {
49562
+ const details = { target, dryRun: true };
49563
+ if (target === "pr") {
49564
+ details.slugOrId = String(args["slug-or-id"] ?? "");
49565
+ if (args.slug) details.slug = String(args.slug);
49566
+ if (args.type) details.type = String(args.type);
49567
+ if (args.title) details.title = String(args.title);
49568
+ } else {
49569
+ details.for = args.for ? String(args.for) : null;
49570
+ details.to = args.to ? String(args.to) : null;
49571
+ if (args.attachment) details.attachment = String(args.attachment);
49572
+ }
49573
+ cliOutput(details, { command: "docs publish", operation: "docs.publish" });
49574
+ return;
49575
+ }
49576
+ if (target === "pr") {
49577
+ const slugOrId = String(args["slug-or-id"] ?? "");
49578
+ if (!slugOrId) {
49579
+ cliError(
49580
+ "docs publish --target pr requires a slug-or-id argument",
49581
+ 1 /* GENERAL_ERROR */,
49582
+ { name: "E_MISSING_ARG" }
49583
+ );
49584
+ process.exit(1 /* GENERAL_ERROR */);
49585
+ }
49586
+ const result = await dispatchDocsRaw("mutate", "publish", {
49587
+ slugOrId,
49588
+ target: "pr",
49589
+ ...typeof args.slug === "string" ? { slug: args.slug } : {},
49590
+ ...typeof args.type === "string" ? { type: args.type } : {},
49591
+ ...typeof args.title === "string" ? { title: args.title } : {},
49592
+ ...typeof args.body === "string" ? { body: args.body } : {},
49593
+ ...typeof args.base === "string" ? { base: args.base } : {}
49594
+ });
49595
+ if (result.success) {
49596
+ cliOutput(result.data, { command: "docs publish", operation: "docs.publish" });
49597
+ return;
49598
+ }
49599
+ const e = result.error;
49600
+ cliError(
49601
+ e.message,
49602
+ 1 /* GENERAL_ERROR */,
49603
+ {
49604
+ name: e.codeName,
49605
+ ...e.fix ? { fix: e.fix } : {},
49606
+ ...e.alternatives ? { alternatives: e.alternatives.map((alt) => ({ action: alt, command: alt })) } : {},
49607
+ ...e.details ? { details: e.details } : {}
49608
+ },
49609
+ { operation: "docs.publish" }
49610
+ );
49611
+ process.exit(1 /* GENERAL_ERROR */);
49612
+ }
49613
+ if (!args.for || !args.to) {
49614
+ cliError(
49615
+ "docs publish --target file requires --for <ownerId> and --to <path>",
49616
+ 1 /* GENERAL_ERROR */,
49617
+ { name: "E_MISSING_ARG" }
49618
+ );
49619
+ process.exit(1 /* GENERAL_ERROR */);
49620
+ }
48418
49621
  try {
48419
49622
  const result = await dispatchDocsRaw("mutate", "publish", {
48420
49623
  ownerId: String(args.for),
48421
49624
  toPath: String(args.to),
48422
- attachmentId: args.attachment ?? void 0
49625
+ attachmentId: args.attachment ?? void 0,
49626
+ target: "file"
48423
49627
  });
48424
49628
  cliOutput(result, { command: "docs publish", operation: "docs.publish" });
48425
49629
  } catch (err) {
@@ -48434,7 +49638,7 @@ var init_docs3 = __esm({
48434
49638
  publishPrCommand = defineCommand({
48435
49639
  meta: {
48436
49640
  name: "publish-pr",
48437
- description: "Publish an attachment to a GitHub PR. Opens a new PR on branch `docs/<slug>` with frontmatter, or atomically updates the existing open PR for the same slug."
49641
+ description: "[DEPRECATED] Use `docs publish --target pr` instead. Publish an attachment to a GitHub PR. Opens a new PR on branch `docs/<slug>` with frontmatter, or atomically updates the existing open PR for the same slug. Use --dry-run to preview without side effects."
48438
49642
  },
48439
49643
  args: {
48440
49644
  "slug-or-id": {
@@ -48442,10 +49646,7 @@ var init_docs3 = __esm({
48442
49646
  description: "Slug, attachment id, or full sha256 hex of the doc to publish",
48443
49647
  required: true
48444
49648
  },
48445
- slug: {
48446
- type: "string",
48447
- description: "Override the slug used for the branch + filename. Required when <slug-or-id> is an attachment id or sha256 with no stored slug."
48448
- },
49649
+ slug: { type: "string", description: "Override the slug used for the branch + filename." },
48449
49650
  type: {
48450
49651
  type: "string",
48451
49652
  description: "Override the publish dir taxonomy (spec|adr|research|handoff|note|llm-readme)."
@@ -48458,15 +49659,25 @@ var init_docs3 = __esm({
48458
49659
  type: "string",
48459
49660
  description: "Override the PR body. Default: an auto-generated summary."
48460
49661
  },
48461
- base: {
48462
- type: "string",
48463
- description: "Base branch for the PR. Default: main."
48464
- }
49662
+ base: { type: "string", description: "Base branch for the PR. Default: main." },
49663
+ "dry-run": { type: "boolean", description: "Preview what would happen without side effects." }
48465
49664
  },
48466
49665
  async run({ args }) {
49666
+ humanInfo(
49667
+ "[deprecated] `docs publish-pr` is deprecated. Use `docs publish --target pr <slug-or-id>` instead."
49668
+ );
48467
49669
  const slugOrId = String(args["slug-or-id"]);
48468
- const result = await dispatchDocsRaw("mutate", "publish-pr", {
49670
+ if (args["dry-run"] === true) {
49671
+ const details = { slugOrId, target: "pr", dryRun: true };
49672
+ if (args.slug) details.slug = String(args.slug);
49673
+ if (args.type) details.type = String(args.type);
49674
+ if (args.title) details.title = String(args.title);
49675
+ cliOutput(details, { command: "docs publish-pr", operation: "docs.publish" });
49676
+ return;
49677
+ }
49678
+ const result = await dispatchDocsRaw("mutate", "publish", {
48469
49679
  slugOrId,
49680
+ target: "pr",
48470
49681
  ...typeof args.slug === "string" ? { slug: args.slug } : {},
48471
49682
  ...typeof args.type === "string" ? { type: args.type } : {},
48472
49683
  ...typeof args.title === "string" ? { title: args.title } : {},
@@ -48474,7 +49685,7 @@ var init_docs3 = __esm({
48474
49685
  ...typeof args.base === "string" ? { base: args.base } : {}
48475
49686
  });
48476
49687
  if (result.success) {
48477
- cliOutput(result.data, { command: "docs publish-pr", operation: "docs.publish-pr" });
49688
+ cliOutput(result.data, { command: "docs publish-pr", operation: "docs.publish" });
48478
49689
  return;
48479
49690
  }
48480
49691
  const e = result.error;
@@ -48484,16 +49695,77 @@ var init_docs3 = __esm({
48484
49695
  {
48485
49696
  name: e.codeName,
48486
49697
  ...e.fix ? { fix: e.fix } : {},
48487
- ...e.alternatives ? {
48488
- alternatives: e.alternatives.map((alt) => ({ action: alt, command: alt }))
48489
- } : {},
49698
+ ...e.alternatives ? { alternatives: e.alternatives.map((alt) => ({ action: alt, command: alt })) } : {},
48490
49699
  ...e.details ? { details: e.details } : {}
48491
49700
  },
48492
- { operation: "docs.publish-pr" }
49701
+ { operation: "docs.publish" }
48493
49702
  );
48494
49703
  process.exit(1 /* GENERAL_ERROR */);
48495
49704
  }
48496
49705
  });
49706
+ checkCommand6 = defineCommand({
49707
+ meta: {
49708
+ name: "check",
49709
+ description: "Unified drift management: check documentation for drift, status, and gaps. Use --drift, --status, --gaps for specific checks; runs all by default."
49710
+ },
49711
+ args: {
49712
+ drift: {
49713
+ type: "boolean",
49714
+ description: "Run legacy drift check (scripts/ vs COMMANDS-INDEX.json)"
49715
+ },
49716
+ status: {
49717
+ type: "boolean",
49718
+ description: "Run git\u21C4llmtxt drift check (published files vs docs SSoT)"
49719
+ },
49720
+ gaps: { type: "boolean", description: "Run knowledge transfer gap check (review docs)" },
49721
+ all: {
49722
+ type: "boolean",
49723
+ description: "Run all three checks (default when no mode flag is set)"
49724
+ },
49725
+ quick: { type: "boolean", description: "Drift check only: quick mode (commands only)" },
49726
+ strict: { type: "boolean", description: "Exit with non-zero code on any drift detection" },
49727
+ epic: { type: "string", description: "Gap check only: filter by epic ID" },
49728
+ task: { type: "string", description: "Gap check only: filter by task ID" },
49729
+ json: { type: "boolean", description: "Emit LAFS JSON envelope" }
49730
+ },
49731
+ async run({ args }) {
49732
+ const hasExplicit = args.drift === true || args.status === true || args.gaps === true;
49733
+ const runDrift = args.drift === true || args.all === true || !hasExplicit;
49734
+ const runStatus = args.status === true || args.all === true || !hasExplicit;
49735
+ const runGaps = args.gaps === true || args.all === true || !hasExplicit;
49736
+ const projectRoot = process.cwd();
49737
+ const results = {};
49738
+ let anyDrift = false;
49739
+ try {
49740
+ if (runDrift) {
49741
+ const r = await detectDrift(projectRoot);
49742
+ results.drift = r;
49743
+ if (r.status !== "clean") anyDrift = true;
49744
+ }
49745
+ if (runStatus) {
49746
+ const r = await dispatchDocsRaw("query", "status", {});
49747
+ results.status = r;
49748
+ if (!r.allInSync) anyDrift = true;
49749
+ }
49750
+ if (runGaps) {
49751
+ const r = await runGapCheck(projectRoot, args.epic ?? args.task ?? void 0);
49752
+ results.gaps = r;
49753
+ if (r.length > 0) anyDrift = true;
49754
+ }
49755
+ cliOutput(results, {
49756
+ command: "docs check",
49757
+ message: anyDrift ? "Drift detected \u2014 see results for details" : "All checks passed \u2014 no drift detected"
49758
+ });
49759
+ if (args.strict && anyDrift) process.exit(2);
49760
+ } catch (err) {
49761
+ if (err instanceof CleoError3) {
49762
+ cliError(err.message, err.code, { name: "E_DOCS_CHECK_FAILED" });
49763
+ process.exit(err.code);
49764
+ }
49765
+ throw err;
49766
+ }
49767
+ }
49768
+ });
48497
49769
  syncCommand3 = defineCommand({
48498
49770
  meta: {
48499
49771
  name: "sync",
@@ -48725,12 +49997,16 @@ var init_docs3 = __esm({
48725
49997
  schemaCommand = defineCommand({
48726
49998
  meta: {
48727
49999
  name: "schema",
48728
- description: "Emit the canonical doc-kind taxonomy registry (built-ins + project extensions) as a LAFS envelope. The schema is the single source of truth for the --type values accepted by `cleo docs add` and the publish-dir layout used by `cleo docs publish-pr`. See `cleo docs list-types` for the human-readable form (T9788)."
50000
+ description: "Emit the canonical doc-kind taxonomy registry (built-ins + project extensions) as a LAFS envelope. The schema is the single source of truth for the --type values accepted by `cleo docs add` and the publish-dir layout used by `cleo docs publish-pr`. (T11142)."
48729
50001
  },
48730
50002
  args: {
48731
- "include-counts": {
50003
+ counts: {
48732
50004
  type: "boolean",
48733
50005
  description: "Include per-kind attachment counts from the project SSoT"
50006
+ },
50007
+ "include-counts": {
50008
+ type: "boolean",
50009
+ description: "DEPRECATED -- use --counts instead. Accepted for backward compatibility but will be removed in a future release."
48734
50010
  }
48735
50011
  },
48736
50012
  async run({ args }) {
@@ -48738,8 +50014,9 @@ var init_docs3 = __esm({
48738
50014
  const { registry, configError } = loadCliRegistry(projectRoot);
48739
50015
  const kinds = registry.list().map(toWireKind);
48740
50016
  const extensionsCount = kinds.filter((k) => k.isExtension).length;
50017
+ const wantCounts = args.counts === true || args["include-counts"] === true;
48741
50018
  let counts2;
48742
- if (args["include-counts"]) {
50019
+ if (wantCounts) {
48743
50020
  counts2 = {};
48744
50021
  const { createAttachmentStore: createAttachmentStore5 } = await import("@cleocode/core/internal");
48745
50022
  const store = createAttachmentStore5();
@@ -48769,52 +50046,57 @@ var init_docs3 = __esm({
48769
50046
  listTypesCommand = defineCommand({
48770
50047
  meta: {
48771
50048
  name: "list-types",
48772
- description: "List every registered doc kind with its label, publish directory, and slug-pattern requirement. Use --counts to include per-kind attachment counts from the project SSoT (T9788)."
50049
+ description: "DEPRECATED -- use `cleo docs schema` instead. Lists every registered doc kind (T11142)."
48773
50050
  },
48774
50051
  args: {
48775
50052
  counts: {
48776
50053
  type: "boolean",
48777
50054
  description: "Include per-kind attachment counts from the project SSoT"
50055
+ },
50056
+ "include-counts": {
50057
+ type: "boolean",
50058
+ description: "DEPRECATED -- use --counts instead."
48778
50059
  }
48779
50060
  },
48780
50061
  async run({ args }) {
50062
+ pushWarning3({
50063
+ code: "W_DEPRECATED_COMMAND",
50064
+ message: "cleo docs list-types is deprecated -- use `cleo docs schema` instead (T11142)",
50065
+ deprecated: "docs list-types",
50066
+ replacement: "docs schema"
50067
+ });
48781
50068
  const projectRoot = getProjectRoot38();
48782
50069
  const { registry, configError } = loadCliRegistry(projectRoot);
48783
50070
  const kinds = registry.list().map(toWireKind);
50071
+ const extensionsCount = kinds.filter((k) => k.isExtension).length;
50072
+ const wantCounts = args.counts === true || args["include-counts"] === true;
48784
50073
  let counts2;
48785
- if (args.counts) {
50074
+ if (wantCounts) {
48786
50075
  counts2 = {};
48787
50076
  const { createAttachmentStore: createAttachmentStore5 } = await import("@cleocode/core/internal");
48788
50077
  const store = createAttachmentStore5();
48789
50078
  for (const k of kinds) counts2[k.kind] = 0;
48790
50079
  try {
48791
- const rows2 = await store.listAllInProject(projectRoot);
48792
- for (const row of rows2) {
50080
+ const rows = await store.listAllInProject(projectRoot);
50081
+ for (const row of rows) {
48793
50082
  const key = row.type;
48794
50083
  if (key && key in counts2) counts2[key] = (counts2[key] ?? 0) + 1;
48795
50084
  }
48796
50085
  } catch {
48797
50086
  }
48798
50087
  }
48799
- const rows = kinds.map((k) => ({
48800
- kind: k.kind,
48801
- label: k.label,
48802
- ...counts2 ? { count: counts2[k.kind] ?? 0 } : {},
48803
- requiresEntityId: k.requiresEntityId,
48804
- publishDir: k.publishDir,
48805
- isExtension: k.isExtension
48806
- }));
48807
50088
  cliOutput(
48808
50089
  {
48809
50090
  version: 1,
48810
- total: rows.length,
48811
- rows,
50091
+ builtinsCount: kinds.length - extensionsCount,
50092
+ extensionsCount,
50093
+ kinds,
50094
+ ...counts2 ? { counts: counts2 } : {},
48812
50095
  ...configError ? { configError } : {}
48813
50096
  },
48814
50097
  {
48815
50098
  command: "docs list-types",
48816
- operation: "docs.list-types",
48817
- message: configError ? `loaded built-ins only \u2014 ${configError.message}` : void 0
50099
+ operation: "docs.list-types"
48818
50100
  }
48819
50101
  );
48820
50102
  }
@@ -48822,30 +50104,42 @@ var init_docs3 = __esm({
48822
50104
  docsCommand = defineCommand({
48823
50105
  meta: {
48824
50106
  name: "docs",
48825
- description: "Documentation attachment management (add/list/fetch/remove), llmtxt primitives (search/merge/graph/rank/versions/publish), PR publishing (publish-pr), drift detection (sync/status/gap-check), legacy .md migration (import), and a local web viewer (serve/open/stop/viewer-status)"
50107
+ description: "Canonical six-verb docs path: add, update, fetch, list, remove, publish. Unified query: query (subsumes search/find/rank). LLM output: llm-output (replaces generate + export). Advanced: supersede, merge, graph, versions. Audit: audit (query the immutable docs audit trail). Legacy/migration: search, find, rank, sync, status, gap-check, import (use query for new work). Viewer: viewer (start/stop/open/status). Utilities: schema (list-types \u2192 schema)."
48826
50108
  },
48827
50109
  subCommands: {
50110
+ // T11136 — Unified drift management (consolidates sync/status/gap-check)
50111
+ check: checkCommand6,
50112
+ // Canonical six-verb path (add, update, fetch, list, remove, publish)
48828
50113
  add: addCommand5,
48829
50114
  update: updateCommand,
48830
- list: listCommand8,
48831
50115
  fetch: fetchCommand,
50116
+ list: listCommand8,
48832
50117
  remove: removeCommand2,
50118
+ publish: publishCommand2,
50119
+ // T11176 — unified search/find/rank surface (preferred entry point)
50120
+ query: queryCommand,
50121
+ // Advanced primitives
48833
50122
  supersede: supersedeCommand,
48834
50123
  generate: generateCommand,
48835
50124
  export: exportCommand4,
48836
- find: findCommand3,
48837
- search: searchCommand,
50125
+ "llm-output": llmOutputCommand,
48838
50126
  merge: mergeCommand,
48839
50127
  // T10164 — DocProvenanceResponse-typed graph (`--root <slug>|<taskId>`).
48840
50128
  graph: graphCommand,
50129
+ // Legacy aliases (use `query` for new work — retained for backward compatibility)
50130
+ search: searchCommand,
50131
+ find: findCommand3,
48841
50132
  rank: rankCommand,
48842
50133
  versions: versionsCommand,
48843
- publish: publishCommand2,
48844
50134
  "publish-pr": publishPrCommand,
50135
+ // T11182 — unified docs audit trail
50136
+ audit: auditCommand3,
50137
+ // Legacy/migration (use canonical verbs for new work)
48845
50138
  sync: syncCommand3,
48846
50139
  status: statusCommand7,
48847
50140
  "gap-check": gapCheckCommand,
48848
50141
  import: importCommand2,
50142
+ // Utilities
48849
50143
  // T9788 — canonical doc-kind taxonomy discovery surface.
48850
50144
  schema: schemaCommand,
48851
50145
  "list-types": listTypesCommand,
@@ -48865,7 +50159,7 @@ var doctor_db_substrate_exports = {};
48865
50159
  __export(doctor_db_substrate_exports, {
48866
50160
  doctorDbSubstrateCommand: () => doctorDbSubstrateCommand
48867
50161
  });
48868
- import { getProjectRoot as getProjectRoot39, pushWarning as pushWarning3 } from "@cleocode/core";
50162
+ import { getProjectRoot as getProjectRoot39, pushWarning as pushWarning4 } from "@cleocode/core";
48869
50163
  import { surveyDbSubstrate, surveyFleetDbSubstrate } from "@cleocode/core/doctor/db-substrate.js";
48870
50164
  function pushSubstrateWarnings(result) {
48871
50165
  for (const warning of result.warnings) {
@@ -48877,7 +50171,7 @@ function pushSubstrateWarnings(result) {
48877
50171
  if (warning.kind === "orphan-project-root") {
48878
50172
  context["parentWorkspace"] = warning.parentWorkspace ?? null;
48879
50173
  }
48880
- pushWarning3({
50174
+ pushWarning4({
48881
50175
  code: warning.kind === "orphan-project-root" ? "W_DB_SUBSTRATE_ORPHAN_PROJECT_ROOT" : "W_DB_SUBSTRATE_NESTED_NEXUS_DUPLICATE",
48882
50176
  message: warning.kind === "orphan-project-root" ? `Orphan project-root .cleo/ at ${warning.path} (T9550 regression class \u2014 review then remove)` + (warning.parentWorkspace ? ` \u2014 attributed to workspace: ${warning.parentWorkspace}` : "") : `Nested-nexus duplicate at ${warning.path} (structural duplicate of the canonical flat layout)`,
48883
50177
  severity: "warn",
@@ -48888,7 +50182,7 @@ function pushSubstrateWarnings(result) {
48888
50182
  for (const [role, entry] of Object.entries(projectSurvey.dbs)) {
48889
50183
  if (entry.pragmaDrift === null || entry.pragmaDrift.length === 0) continue;
48890
50184
  for (const drift of entry.pragmaDrift) {
48891
- pushWarning3({
50185
+ pushWarning4({
48892
50186
  code: "W_DB_SUBSTRATE_PRAGMA_DRIFT",
48893
50187
  message: `Pragma drift on ${role} (${entry.filePath}): expected ${drift.pragma}=${drift.expected}, actual=${drift.actual ?? "<unmeasurable>"}`,
48894
50188
  severity: "warn",
@@ -48905,7 +50199,7 @@ function pushSubstrateWarnings(result) {
48905
50199
  }
48906
50200
  for (const report of result.crossDbOrphans) {
48907
50201
  if (report.skipped || report.orphanCount === 0) continue;
48908
- pushWarning3({
50202
+ pushWarning4({
48909
50203
  code: `W_DB_SUBSTRATE_CROSS_DB_${report.invariant}`,
48910
50204
  message: `${report.invariant}: ${report.orphanCount} orphan row${report.orphanCount === 1 ? "" : "s"} \u2014 ${report.description}. ${report.suggestedFix}`,
48911
50205
  severity: "warn",
@@ -48922,7 +50216,7 @@ function pushPerDbWarnings(result) {
48922
50216
  for (const projectSurvey of result.projects) {
48923
50217
  for (const [role, dbEntry] of Object.entries(projectSurvey.dbs)) {
48924
50218
  if (dbEntry.quarantinedTo !== null) {
48925
- pushWarning3({
50219
+ pushWarning4({
48926
50220
  code: "W_DB_SUBSTRATE_AUTO_QUARANTINED",
48927
50221
  message: `Auto-quarantined corrupt ${role} DB at ${dbEntry.filePath} \u2192 ${dbEntry.quarantinedTo}. Recover via: cleo backup recover ${role}`,
48928
50222
  severity: "warn",
@@ -48935,7 +50229,7 @@ function pushPerDbWarnings(result) {
48935
50229
  });
48936
50230
  }
48937
50231
  if (dbEntry.timedOut) {
48938
- pushWarning3({
50232
+ pushWarning4({
48939
50233
  code: "W_DB_SUBSTRATE_INTEGRITY_TIMEOUT",
48940
50234
  message: `integrity_check on ${role} DB at ${dbEntry.filePath} took ${dbEntry.integrityCheckMs}ms \u2014 slow substrate flagged for operator attention`,
48941
50235
  severity: "warn",
@@ -49860,7 +51154,7 @@ __export(doctor_exports, {
49860
51154
  });
49861
51155
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
49862
51156
  import { join as join24 } from "node:path";
49863
- import { getProjectRoot as getProjectRoot42, pushWarning as pushWarning4 } from "@cleocode/core";
51157
+ import { getProjectRoot as getProjectRoot42, pushWarning as pushWarning5 } from "@cleocode/core";
49864
51158
  import { renderInvariantAuditLines } from "@cleocode/core/doctor/invariant-audit-render.js";
49865
51159
  import {
49866
51160
  quarantineRogueCleoDir,
@@ -50394,7 +51688,7 @@ var init_doctor = __esm({
50394
51688
  const legacyOrphans = legacyScanResult.orphans;
50395
51689
  const totalAnomalies = comprehensive.count;
50396
51690
  if (legacyScanResult.softWarnMessage) {
50397
- pushWarning4({
51691
+ pushWarning5({
50398
51692
  code: "W_DOCTOR_SCAN_SOFT_WARN",
50399
51693
  message: legacyScanResult.softWarnMessage,
50400
51694
  severity: "warn"
@@ -50402,7 +51696,7 @@ var init_doctor = __esm({
50402
51696
  }
50403
51697
  if (legacyScanResult.isPartial) {
50404
51698
  const reason = legacyScanResult.partialReason === "timeout" ? `timed out after ${timeoutSecs}s (use --timeout <seconds> to adjust)` : `per-level entry cap of ${maxEntriesPerLevel} exceeded (use --max-entries-per-level <n> to adjust)`;
50405
- pushWarning4({
51699
+ pushWarning5({
50406
51700
  code: "W_DOCTOR_SCAN_PARTIAL",
50407
51701
  message: `legacy orphan scan is PARTIAL \u2014 ${reason}. Results may be incomplete.`,
50408
51702
  severity: "warn",
@@ -50446,7 +51740,7 @@ var init_doctor = __esm({
50446
51740
  maxEntriesPerLevel: Number.isFinite(maxEntriesPerLevel) ? maxEntriesPerLevel : 500
50447
51741
  });
50448
51742
  if (scanResult.softWarnMessage) {
50449
- pushWarning4({
51743
+ pushWarning5({
50450
51744
  code: "W_DOCTOR_SCAN_SOFT_WARN",
50451
51745
  message: scanResult.softWarnMessage,
50452
51746
  severity: "warn"
@@ -50454,7 +51748,7 @@ var init_doctor = __esm({
50454
51748
  }
50455
51749
  if (scanResult.isPartial) {
50456
51750
  const reason = scanResult.partialReason === "timeout" ? `timed out after ${timeoutSecs}s (use --timeout <seconds> to adjust)` : `per-level entry cap of ${maxEntriesPerLevel} exceeded (use --max-entries-per-level <n> to adjust)`;
50457
- pushWarning4({
51751
+ pushWarning5({
50458
51752
  code: "W_DOCTOR_SCAN_PARTIAL",
50459
51753
  message: `orphan scan is PARTIAL \u2014 ${reason}. Only orphans found before abort will be pruned.`,
50460
51754
  severity: "warn",
@@ -51788,7 +53082,7 @@ function applyJsonFlag(jsonFlag) {
51788
53082
  setFormatContext({ format: "json", source: "flag", quiet: false });
51789
53083
  }
51790
53084
  }
51791
- var statusCommand9, resolveCommand, depsCommand2, rawCommand, discoverCommand, searchCommand2, augmentCommand, contextCommand2, impactCommand2, impactFullCommand, clustersCommand, flowsCommand, diffCommand, routeMapCommand, shapeCheckCommand, searchCodeCommand, wikiCommand, hotPathsCommand, hotNodesCommand, coldSymbolsCommand, orphansCommand, queryCommand, initCommand, syncCommand4, reconcileCommand, livingFullContextCommand, livingTaskFootprintCommand, livingBrainAnchorsCommand, livingWhyCommand, livingConduitScanCommand, livingCommand, graphCommand2;
53085
+ var statusCommand9, resolveCommand, depsCommand2, rawCommand, discoverCommand, searchCommand2, augmentCommand, contextCommand2, impactCommand2, impactFullCommand, clustersCommand, flowsCommand, diffCommand, routeMapCommand, shapeCheckCommand, searchCodeCommand, wikiCommand, hotPathsCommand, hotNodesCommand, coldSymbolsCommand, orphansCommand, queryCommand2, initCommand, syncCommand4, reconcileCommand, livingFullContextCommand, livingTaskFootprintCommand, livingBrainAnchorsCommand, livingWhyCommand, livingConduitScanCommand, livingCommand, graphCommand2;
51792
53086
  var init_graph3 = __esm({
51793
53087
  "packages/cleo/src/cli/commands/graph.ts"() {
51794
53088
  "use strict";
@@ -52296,7 +53590,7 @@ var init_graph3 = __esm({
52296
53590
  await dispatchFromCli("query", "nexus", "orphans.list", {}, { command: "graph" });
52297
53591
  }
52298
53592
  });
52299
- queryCommand = defineCommand({
53593
+ queryCommand2 = defineCommand({
52300
53594
  meta: { name: "query", description: "Execute recursive CTE queries against nexus.db" },
52301
53595
  args: {
52302
53596
  cte: {
@@ -52604,7 +53898,7 @@ var init_graph3 = __esm({
52604
53898
  "hot-nodes": hotNodesCommand,
52605
53899
  "cold-symbols": coldSymbolsCommand,
52606
53900
  orphans: orphansCommand,
52607
- query: queryCommand,
53901
+ query: queryCommand2,
52608
53902
  init: initCommand,
52609
53903
  sync: syncCommand4,
52610
53904
  reconcile: reconcileCommand,
@@ -52899,7 +54193,7 @@ import {
52899
54193
  CleoError as CleoError4,
52900
54194
  getWorkflowTemplatesDir as getCoreWorkflowTemplatesDir,
52901
54195
  initProject as initProject2,
52902
- pushWarning as pushWarning5,
54196
+ pushWarning as pushWarning6,
52903
54197
  scaffoldWorkflows
52904
54198
  } from "@cleocode/core";
52905
54199
  import { getTemplatesByKind } from "@cleocode/core/templates/registry";
@@ -52979,7 +54273,7 @@ var init_init = __esm({
52979
54273
  async run({ args }) {
52980
54274
  try {
52981
54275
  if (args.workflows) {
52982
- pushWarning5({
54276
+ pushWarning6({
52983
54277
  code: "W_INIT_WORKFLOWS_DEPRECATED",
52984
54278
  message: "[deprecated] cleo init --workflows: use `cleo templates install --kind workflow` instead. This alias will be removed in v2026.7.0.",
52985
54279
  severity: "warn",
@@ -54597,7 +55891,7 @@ var llm_exports = {};
54597
55891
  __export(llm_exports, {
54598
55892
  llmCommand: () => llmCommand
54599
55893
  });
54600
- import { pushWarning as pushWarning6 } from "@cleocode/core";
55894
+ import { pushWarning as pushWarning7 } from "@cleocode/core";
54601
55895
  async function getListProviders() {
54602
55896
  const { listProviders } = await import(
54603
55897
  /* webpackIgnore: true */
@@ -54728,7 +56022,7 @@ var init_llm3 = __esm({
54728
56022
  apiKey = envValue;
54729
56023
  source = "env";
54730
56024
  } else if (typeof a["api-key"] === "string" && a["api-key"]) {
54731
- pushWarning6({
56025
+ pushWarning7({
54732
56026
  code: "W_DEPRECATED_FLAG",
54733
56027
  message: API_KEY_FLAG_DEPRECATION,
54734
56028
  deprecated: "--api-key=<value>",
@@ -55839,7 +57133,10 @@ var init_memory3 = __esm({
55839
57133
  }
55840
57134
  });
55841
57135
  decisionFindCommand = defineCommand({
55842
- meta: { name: "decision-find", description: "Search decisions stored in brain.db" },
57136
+ meta: {
57137
+ name: "decision-find",
57138
+ description: "Search architectural decisions (use BEFORE grep or ledger files)"
57139
+ },
55843
57140
  args: {
55844
57141
  query: {
55845
57142
  type: "positional",
@@ -55875,7 +57172,10 @@ var init_memory3 = __esm({
55875
57172
  }
55876
57173
  });
55877
57174
  decisionStoreCommand = defineCommand({
55878
- meta: { name: "decision-store", description: "Store a decision to brain.db" },
57175
+ meta: {
57176
+ name: "decision-store",
57177
+ description: "Store an architectural decision \u2014 supports --linked-task, --confirmation-state, --supersedes, --superseded-by, --decided-by"
57178
+ },
55879
57179
  args: {
55880
57180
  decision: {
55881
57181
  type: "string",
@@ -55943,7 +57243,10 @@ var init_memory3 = __esm({
55943
57243
  }
55944
57244
  });
55945
57245
  linkCommand = defineCommand({
55946
- meta: { name: "link", description: "Link a brain entry to a task" },
57246
+ meta: {
57247
+ name: "link",
57248
+ description: "Connect a BRAIN decision/observation to a task (example: cleo memory link T10520 D012)"
57249
+ },
55947
57250
  args: {
55948
57251
  taskId: {
55949
57252
  type: "positional",
@@ -57234,7 +58537,10 @@ data: ${JSON.stringify({ ts: item.ts })}
57234
58537
  }
57235
58538
  });
57236
58539
  memoryCommand = defineCommand({
57237
- meta: { name: "memory", description: "BRAIN memory operations (patterns, learnings)" },
58540
+ meta: {
58541
+ name: "memory",
58542
+ description: "BRAIN memory operations (patterns, learnings, decisions \u2014 use decision-store/-find for architectural decisions)"
58543
+ },
57238
58544
  subCommands: {
57239
58545
  store: storeCommand,
57240
58546
  find: findCommand6,
@@ -57530,7 +58836,7 @@ function applyJsonFlag2(jsonFlag) {
57530
58836
  setFormatContext({ format: "json", source: "flag", quiet: false });
57531
58837
  }
57532
58838
  }
57533
- var initCommand3, registerCommand2, unregisterCommand, listCommand14, statusCommand10, 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, projectsCommand, refreshBridgeCommand, exportCommand6, diffCommand2, queryCommand2, routeMapCommand2, shapeCheckCommand2, fullContextCommand, taskFootprintCommand, brainAnchorsCommand, whyCommand, impactFullCommand2, conduitScanCommand, taskSymbolsCommand, searchCodeCommand2, contractsSyncCommand, contractsShowCommand, contractsLinkTasksCommand, contractsCommand, groupCommand, wikiCommand2, hotPathsCommand2, hotNodesCommand2, coldSymbolsCommand2, sigilSyncCommand, sigilListCommand, sigilCommand, topEntriesCommand, nexusCommand;
58839
+ var initCommand3, registerCommand2, unregisterCommand, listCommand14, statusCommand10, 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, 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;
57534
58840
  var init_nexus3 = __esm({
57535
58841
  "packages/cleo/src/cli/commands/nexus.ts"() {
57536
58842
  "use strict";
@@ -58960,7 +60266,7 @@ var init_nexus3 = __esm({
58960
60266
  });
58961
60267
  }
58962
60268
  });
58963
- queryCommand2 = defineCommand({
60269
+ queryCommand3 = defineCommand({
58964
60270
  meta: {
58965
60271
  name: "query",
58966
60272
  description: "Execute recursive CTE queries against nexus.db"
@@ -59946,7 +61252,7 @@ var init_nexus3 = __esm({
59946
61252
  context: contextCommand4,
59947
61253
  impact: impactCommand3,
59948
61254
  analyze: analyzeCommand3,
59949
- query: queryCommand2,
61255
+ query: queryCommand3,
59950
61256
  projects: projectsCommand,
59951
61257
  "refresh-bridge": refreshBridgeCommand,
59952
61258
  export: exportCommand6,
@@ -64645,7 +65951,7 @@ var init_saga = __esm({
64645
65951
  createCommand3 = defineCommand({
64646
65952
  meta: {
64647
65953
  name: "create",
64648
- description: "Create a new Saga (labeled top-level Epic with label=saga)"
65954
+ description: "Create a new Saga (top-level task with type='saga')"
64649
65955
  },
64650
65956
  args: {
64651
65957
  title: {
@@ -64689,12 +65995,12 @@ var init_saga = __esm({
64689
65995
  addCommand11 = defineCommand({
64690
65996
  meta: {
64691
65997
  name: "add",
64692
- description: "Link a member Epic to a Saga (writes task_relations type=groups)"
65998
+ description: "Link a member Epic to a Saga via parent_id containment"
64693
65999
  },
64694
66000
  args: {
64695
66001
  sagaId: {
64696
66002
  type: "positional",
64697
- description: "Saga task ID (must have label=saga)",
66003
+ description: "Saga task ID (must have type='saga')",
64698
66004
  required: true
64699
66005
  },
64700
66006
  epicId: {
@@ -64716,7 +66022,7 @@ var init_saga = __esm({
64716
66022
  detachCommand2 = defineCommand({
64717
66023
  meta: {
64718
66024
  name: "detach",
64719
- description: "Remove a Saga member relation (task_relations type=groups) \u2014 idempotent, audit-logged"
66025
+ description: "Remove a Saga member via parent_id containment \u2014 idempotent, audit-logged"
64720
66026
  },
64721
66027
  args: {
64722
66028
  sagaId: {
@@ -64748,7 +66054,7 @@ var init_saga = __esm({
64748
66054
  listCommand22 = defineCommand({
64749
66055
  meta: {
64750
66056
  name: "list",
64751
- description: "List all Sagas (labeled top-level Epics)"
66057
+ description: "List all Sagas"
64752
66058
  },
64753
66059
  async run() {
64754
66060
  await dispatchFromCli("query", "tasks", "saga.list", {}, { command: "saga" });
@@ -64757,7 +66063,7 @@ var init_saga = __esm({
64757
66063
  membersCommand = defineCommand({
64758
66064
  meta: {
64759
66065
  name: "members",
64760
- description: "List all member Epics linked to a Saga via type=groups"
66066
+ description: "List all member Epics linked to a Saga via parent_id containment"
64761
66067
  },
64762
66068
  args: {
64763
66069
  sagaId: {
@@ -64779,12 +66085,12 @@ var init_saga = __esm({
64779
66085
  repairCommand = defineCommand({
64780
66086
  meta: {
64781
66087
  name: "repair",
64782
- description: "Detach an I5-violating parentId from a Saga and re-attach via task_relations.type='groups' (ADR-073 \xA71.2). Idempotent."
66088
+ description: "Detach an I5-violating parentId from a Saga. Idempotent."
64783
66089
  },
64784
66090
  args: {
64785
66091
  sagaId: {
64786
66092
  type: "positional",
64787
- description: "Saga task ID (must have label=saga)",
66093
+ description: "Saga task ID (must have type='saga')",
64788
66094
  required: true
64789
66095
  }
64790
66096
  },
@@ -66582,7 +67888,7 @@ __export(sequence_exports, {
66582
67888
  sequenceCommand: () => sequenceCommand
66583
67889
  });
66584
67890
  import { getProjectRoot as getProjectRoot51 } from "@cleocode/core/internal";
66585
- var showCommand12, checkCommand6, repairCommand2, sequenceCommand;
67891
+ var showCommand12, checkCommand7, repairCommand2, sequenceCommand;
66586
67892
  var init_sequence = __esm({
66587
67893
  "packages/cleo/src/cli/commands/sequence.ts"() {
66588
67894
  "use strict";
@@ -66601,7 +67907,7 @@ var init_sequence = __esm({
66601
67907
  );
66602
67908
  }
66603
67909
  });
66604
- checkCommand6 = defineCommand({
67910
+ checkCommand7 = defineCommand({
66605
67911
  meta: { name: "check", description: "Verify counter >= max(todo + archive)" },
66606
67912
  async run() {
66607
67913
  await dispatchFromCli(
@@ -66636,7 +67942,7 @@ var init_sequence = __esm({
66636
67942
  },
66637
67943
  subCommands: {
66638
67944
  show: showCommand12,
66639
- check: checkCommand6,
67945
+ check: checkCommand7,
66640
67946
  repair: repairCommand2
66641
67947
  },
66642
67948
  async run({ cmd, rawArgs }) {
@@ -70433,7 +71739,7 @@ var testing_exports = {};
70433
71739
  __export(testing_exports, {
70434
71740
  testingCommand: () => testingCommand
70435
71741
  });
70436
- var validateCommand9, checkCommand7, statusCommand16, coverageCommand, runCommand5, testingCommand;
71742
+ var validateCommand9, checkCommand8, statusCommand16, coverageCommand, runCommand5, testingCommand;
70437
71743
  var init_testing = __esm({
70438
71744
  "packages/cleo/src/cli/commands/testing.ts"() {
70439
71745
  "use strict";
@@ -70467,7 +71773,7 @@ var init_testing = __esm({
70467
71773
  );
70468
71774
  }
70469
71775
  });
70470
- checkCommand7 = defineCommand({
71776
+ checkCommand8 = defineCommand({
70471
71777
  meta: { name: "check", description: "Validate testing protocol from a manifest file" },
70472
71778
  args: {
70473
71779
  manifestFile: {
@@ -70540,7 +71846,7 @@ var init_testing = __esm({
70540
71846
  meta: { name: "testing", description: "Validate testing protocol compliance" },
70541
71847
  subCommands: {
70542
71848
  validate: validateCommand9,
70543
- check: checkCommand7,
71849
+ check: checkCommand8,
70544
71850
  status: statusCommand16,
70545
71851
  coverage: coverageCommand,
70546
71852
  run: runCommand5
@@ -72287,7 +73593,7 @@ var validateCommand10, applyCommand, planCommand4, structureCommand, workgraphCo
72287
73593
  var init_workgraph2 = __esm({
72288
73594
  "packages/cleo/src/cli/commands/workgraph.ts"() {
72289
73595
  "use strict";
72290
- init_dist();
73596
+ init_define_cli_command();
72291
73597
  validateCommand10 = defineCommand({
72292
73598
  meta: {
72293
73599
  name: "validate",
@@ -73096,7 +74402,7 @@ var COMMAND_MANIFEST = [
73096
74402
  {
73097
74403
  exportName: "docsCommand",
73098
74404
  name: "docs",
73099
- description: "Documentation attachment management (add/list/fetch/remove), ",
74405
+ description: "Canonical six-verb docs path: add, update, fetch, list, remove, publish. ",
73100
74406
  load: async () => (await Promise.resolve().then(() => (init_docs3(), docs_exports))).docsCommand
73101
74407
  },
73102
74408
  {
@@ -73312,7 +74618,7 @@ var COMMAND_MANIFEST = [
73312
74618
  {
73313
74619
  exportName: "memoryCommand",
73314
74620
  name: "memory",
73315
- description: "BRAIN memory operations (patterns, learnings)",
74621
+ description: "BRAIN memory operations (patterns, learnings, decisions \u2014 use decision-store/-find for architectural decisions)",
73316
74622
  load: async () => (await Promise.resolve().then(() => (init_memory3(), memory_exports))).memoryCommand
73317
74623
  },
73318
74624
  {