@documentation-robotics/cli 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/README.md +10 -14
  2. package/dist/analyzers/active-analyzer.js +62 -0
  3. package/dist/analyzers/active-analyzer.js.map +7 -0
  4. package/dist/analyzers/cards/build.js +21 -1
  5. package/dist/analyzers/cards/build.js.map +2 -2
  6. package/dist/analyzers/cards/extract.js +138 -0
  7. package/dist/analyzers/cards/extract.js.map +7 -0
  8. package/dist/analyzers/cards/routes.js +96 -0
  9. package/dist/analyzers/cards/routes.js.map +7 -0
  10. package/dist/analyzers/cbm-analyzer.js +76 -4
  11. package/dist/analyzers/cbm-analyzer.js.map +2 -2
  12. package/dist/analyzers/cbm-responses.js +12 -6
  13. package/dist/analyzers/cbm-responses.js.map +2 -2
  14. package/dist/analyzers/code-changes.js +159 -0
  15. package/dist/analyzers/code-changes.js.map +7 -0
  16. package/dist/analyzers/edge-relationships.js +6 -2
  17. package/dist/analyzers/edge-relationships.js.map +2 -2
  18. package/dist/analyzers/extraction-plan.js +541 -0
  19. package/dist/analyzers/extraction-plan.js.map +7 -0
  20. package/dist/analyzers/index-job.js +229 -0
  21. package/dist/analyzers/index-job.js.map +7 -0
  22. package/dist/cli.js +12 -10
  23. package/dist/cli.js.map +2 -2
  24. package/dist/coding-agents/drbot-mcp.js +120 -18
  25. package/dist/coding-agents/drbot-mcp.js.map +2 -2
  26. package/dist/commands/add.js +4 -2
  27. package/dist/commands/add.js.map +2 -2
  28. package/dist/commands/analyzer.js +35 -35
  29. package/dist/commands/analyzer.js.map +2 -2
  30. package/dist/commands/changeset-verify.js +16 -8
  31. package/dist/commands/changeset-verify.js.map +2 -2
  32. package/dist/commands/changeset.js +41 -11
  33. package/dist/commands/changeset.js.map +2 -2
  34. package/dist/commands/init.js +9 -26
  35. package/dist/commands/init.js.map +2 -2
  36. package/dist/commands/mcp.js +35 -31
  37. package/dist/commands/mcp.js.map +3 -3
  38. package/dist/commands/relationship.js +2 -12
  39. package/dist/commands/relationship.js.map +2 -2
  40. package/dist/commands/reports.js +3 -3
  41. package/dist/commands/reports.js.map +2 -2
  42. package/dist/commands/update.js +14 -13
  43. package/dist/commands/update.js.map +2 -2
  44. package/dist/commands/upgrade.js +28 -120
  45. package/dist/commands/upgrade.js.map +2 -2
  46. package/dist/commands/validate.js +19 -3
  47. package/dist/commands/validate.js.map +2 -2
  48. package/dist/commands/version.js +1 -1
  49. package/dist/commands/visualize.js +30 -12
  50. package/dist/commands/visualize.js.map +2 -2
  51. package/dist/core/annotation-store.js +15 -5
  52. package/dist/core/annotation-store.js.map +2 -2
  53. package/dist/core/catalog-migration.js +2 -1
  54. package/dist/core/catalog-migration.js.map +2 -2
  55. package/dist/core/changeset-base-index.js +7 -0
  56. package/dist/core/changeset-base-index.js.map +2 -2
  57. package/dist/core/changeset-brief-compact.js +256 -0
  58. package/dist/core/changeset-brief-compact.js.map +7 -0
  59. package/dist/core/changeset-verify.js +356 -26
  60. package/dist/core/changeset-verify.js.map +3 -3
  61. package/dist/core/extraction-observability.js +161 -0
  62. package/dist/core/extraction-observability.js.map +7 -0
  63. package/dist/core/migration-registry.js +13 -1
  64. package/dist/core/migration-registry.js.map +2 -2
  65. package/dist/core/project-init.js +53 -0
  66. package/dist/core/project-init.js.map +7 -0
  67. package/dist/core/relationship-staging.js +94 -1
  68. package/dist/core/relationship-staging.js.map +2 -2
  69. package/dist/core/staged-changeset-storage.js +83 -3
  70. package/dist/core/staged-changeset-storage.js.map +2 -2
  71. package/dist/core/staging-area.js +85 -8
  72. package/dist/core/staging-area.js.map +2 -2
  73. package/dist/core/upgrade-plan.js +117 -0
  74. package/dist/core/upgrade-plan.js.map +7 -0
  75. package/dist/generated/relationship-index.js +124 -15
  76. package/dist/generated/relationship-index.js.map +2 -2
  77. package/dist/integrations/claude_code/.claude-plugin/plugin.json +24 -0
  78. package/dist/integrations/claude_code/agents/dr-architect.md +19 -5
  79. package/dist/integrations/claude_code/agents/dr-extractor.md +7 -1
  80. package/dist/integrations/claude_code/commands/dr-changeset.md +2 -1
  81. package/dist/integrations/claude_code/commands/dr-map.md +7 -1
  82. package/dist/integrations/claude_code/commands/dr-plan.md +42 -16
  83. package/dist/integrations/claude_code/docs/MCP_SERVER.md +28 -31
  84. package/dist/integrations/claude_code/docs/README.md +20 -1
  85. package/dist/integrations/claude_code/plugin-agents/dr-advisor.md +255 -0
  86. package/dist/integrations/claude_code/plugin-agents/dr-architect.md +306 -0
  87. package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +3 -0
  88. package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +90 -31
  89. package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +5 -0
  90. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +5 -0
  91. package/dist/integrations/github_copilot/agents/dr-architect.md +2 -0
  92. package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +3 -0
  93. package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +88 -30
  94. package/dist/mcp/changeset-only.js +14 -3
  95. package/dist/mcp/changeset-only.js.map +2 -2
  96. package/dist/mcp/project-root.js +85 -0
  97. package/dist/mcp/project-root.js.map +7 -0
  98. package/dist/mcp/server-instructions.js +27 -0
  99. package/dist/mcp/server-instructions.js.map +7 -0
  100. package/dist/mcp/strict-schema.js +52 -0
  101. package/dist/mcp/strict-schema.js.map +7 -0
  102. package/dist/mcp/tool-registry.js +39 -5
  103. package/dist/mcp/tool-registry.js.map +2 -2
  104. package/dist/mcp/tools/changeset-analysis-tools.js +25 -5
  105. package/dist/mcp/tools/changeset-analysis-tools.js.map +3 -3
  106. package/dist/mcp/tools/changeset-commit.js +224 -0
  107. package/dist/mcp/tools/changeset-commit.js.map +7 -0
  108. package/dist/mcp/tools/changeset-editing.js +133 -57
  109. package/dist/mcp/tools/changeset-editing.js.map +3 -3
  110. package/dist/mcp/tools/changeset-lifecycle.js +1 -1
  111. package/dist/mcp/tools/changeset-lifecycle.js.map +2 -2
  112. package/dist/mcp/tools/changeset-verify.js +12 -6
  113. package/dist/mcp/tools/changeset-verify.js.map +2 -2
  114. package/dist/mcp/tools/codebase-extract.js +836 -0
  115. package/dist/mcp/tools/codebase-extract.js.map +7 -0
  116. package/dist/mcp/tools/codebase-index.js +125 -0
  117. package/dist/mcp/tools/codebase-index.js.map +7 -0
  118. package/dist/mcp/tools/codebase-shared.js +265 -0
  119. package/dist/mcp/tools/codebase-shared.js.map +7 -0
  120. package/dist/mcp/tools/codebase-verify.js +48 -0
  121. package/dist/mcp/tools/codebase-verify.js.map +7 -0
  122. package/dist/mcp/tools/index.js +15 -0
  123. package/dist/mcp/tools/index.js.map +2 -2
  124. package/dist/mcp/tools/model-add.js +189 -73
  125. package/dist/mcp/tools/model-add.js.map +2 -2
  126. package/dist/mcp/tools/model-update.js +16 -12
  127. package/dist/mcp/tools/model-update.js.map +2 -2
  128. package/dist/mcp/tools/model-validate.js +46 -4
  129. package/dist/mcp/tools/model-validate.js.map +2 -2
  130. package/dist/mcp/tools/project-status-collectors.js +381 -0
  131. package/dist/mcp/tools/project-status-collectors.js.map +7 -0
  132. package/dist/mcp/tools/project-status.js +237 -0
  133. package/dist/mcp/tools/project-status.js.map +7 -0
  134. package/dist/mcp/tools/project-tools.js +505 -0
  135. package/dist/mcp/tools/project-tools.js.map +7 -0
  136. package/dist/mcp/tools/relationship-tools.js +89 -20
  137. package/dist/mcp/tools/relationship-tools.js.map +2 -2
  138. package/dist/mcp/tools/review-open.js +210 -0
  139. package/dist/mcp/tools/review-open.js.map +7 -0
  140. package/dist/mcp/tools/shared.js +10 -2
  141. package/dist/mcp/tools/shared.js.map +2 -2
  142. package/dist/reports/model-report-orchestrator.js +31 -2
  143. package/dist/reports/model-report-orchestrator.js.map +2 -2
  144. package/dist/schemas/bundled/analyzers/cbm.json +22 -10
  145. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  146. package/dist/schemas/bundled/api.json +1 -1
  147. package/dist/schemas/bundled/apm.json +1 -1
  148. package/dist/schemas/bundled/application.json +1 -1
  149. package/dist/schemas/bundled/base.json +1 -1
  150. package/dist/schemas/bundled/business.json +1 -1
  151. package/dist/schemas/bundled/data-model.json +1 -1
  152. package/dist/schemas/bundled/data-store.json +1 -1
  153. package/dist/schemas/bundled/manifest.json +3 -3
  154. package/dist/schemas/bundled/motivation.json +1 -1
  155. package/dist/schemas/bundled/navigation.json +1 -1
  156. package/dist/schemas/bundled/product.json +1 -1
  157. package/dist/schemas/bundled/security.json +1 -1
  158. package/dist/schemas/bundled/technology.json +1 -1
  159. package/dist/schemas/bundled/testing.json +145 -1
  160. package/dist/schemas/bundled/ux.json +1 -1
  161. package/dist/server/server.js +508 -237
  162. package/dist/server/server.js.map +3 -3
  163. package/dist/server/viewer-registry.js +264 -0
  164. package/dist/server/viewer-registry.js.map +7 -0
  165. package/dist/telemetry/index.js +1 -1
  166. package/dist/utils/id-generator.js +5 -2
  167. package/dist/utils/id-generator.js.map +2 -2
  168. package/dist/utils/source-reference.js +58 -27
  169. package/dist/utils/source-reference.js.map +2 -2
  170. package/dist/utils/spec-version.js +2 -2
  171. package/dist/utils/spec-version.js.map +1 -1
  172. package/dist/validators/model-warnings.js +28 -0
  173. package/dist/validators/model-warnings.js.map +7 -0
  174. package/dist/viewer/README.md +6 -6
  175. package/dist/viewer/assets/main-CUOQGGRA.css +1 -0
  176. package/dist/viewer/assets/main-DvH3gYrI.js +84 -0
  177. package/dist/viewer/index.html +2 -2
  178. package/dist/viewer/manifest.json +10 -10
  179. package/dist/viewer/version.json +1 -1
  180. package/package.json +1 -1
  181. package/dist/viewer/assets/main-CxzcuU_o.js +0 -84
  182. package/dist/viewer/assets/main-j-7ULYpm.css +0 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/core/changeset-base-index.ts"],
4
- "sourcesContent": ["/**\n * Changeset base index \u2014 which elements and relationships the base model had when a changeset was\n * created, so later readers can tell what predates the plan and what changed in the base under it.\n *\n * The changeset's `baseSnapshot` is a single hash of the whole model: it says the base changed, not\n * what changed. The index records each base element's id with a short content hash, and each base\n * relationship's key, in `documentation-robotics/changesets/{id}/base-index.json`. It is written\n * when the changeset is created. Changesets created before it existed (or imported) have none, and\n * every reader falls back to what it can tell without one and says so.\n *\n * Readers:\n * - `changeset_verify` excludes elements that existed before the plan from the candidates for a\n * planned add (they cannot be its built form).\n * - `changeset_brief` names what changed in the base since the plan was created (`describeDrift`).\n */\n\nimport { createHash } from \"node:crypto\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileExists } from \"../utils/file-io.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { Changeset } from \"./changeset.js\";\nimport { ChangesetGraph, relationshipKey, type GraphElement } from \"./changeset-graph.js\";\nimport type { Model } from \"./model.js\";\n\nexport const BASE_INDEX_FILE = \"base-index.json\";\n\nexport interface BaseIndex {\n version: 1;\n captured: string;\n /** Element id (dotted path) \u2192 short hash of its content. */\n elements: Record<string, string>;\n /** Relationship keys, `source::predicate::target`, endpoints as dotted paths. */\n relationships: string[];\n}\n\nexport interface RelationshipRef {\n source: string;\n predicate: string;\n target: string;\n}\n\n/** How many ids each drift list carries before it is cut short (the counts stay exact). */\nexport const DRIFT_LIST_LIMIT = 50;\n\nexport interface ChangesetDrift {\n /** Kept for compatibility: true when the base changed since the changeset was created. */\n isDrifted: boolean;\n warnings: string[];\n baseSnapshotId?: string;\n currentSnapshotId?: string;\n /** Whether a base index was recorded for the changeset; without one, `baseChanges` is absent. */\n baseIndex: \"recorded\" | \"not-recorded\" | \"unreadable\";\n /**\n * Staged changes that the base now overlaps: adds whose element or relationship the base already\n * has, deletes of what the base no longer has, and (with a base index) elements the plan updates\n * or deletes whose base content changed since the plan was created.\n */\n overlapping: {\n elements: Array<{ id: string; reason: \"already-in-base\" | \"already-removed\" | \"changed-in-base\" }>;\n relationships: Array<RelationshipRef & { reason: \"already-in-base\" | \"already-removed\" }>;\n };\n /** What changed in the base since the changeset was created; needs a base index. */\n baseChanges?: {\n counts: {\n elementsAdded: number;\n elementsRemoved: number;\n elementsChanged: number;\n relationshipsAdded: number;\n relationshipsRemoved: number;\n };\n elementsAdded: string[];\n elementsRemoved: string[];\n elementsChanged: string[];\n relationshipsAdded: RelationshipRef[];\n relationshipsRemoved: RelationshipRef[];\n /** True when a list was cut at DRIFT_LIST_LIMIT entries. */\n truncated: boolean;\n };\n}\n\nfunction indexPath(rootPath: string, changesetId: string): string {\n return path.join(rootPath, \"documentation-robotics\", \"changesets\", changesetId, BASE_INDEX_FILE);\n}\n\nfunction contentHash(element: GraphElement): string {\n const content = {\n type: element.type,\n name: element.name,\n description: element.description ?? null,\n attributes: element.attributes ?? {},\n sourceReference: element.sourceReference ?? null,\n };\n return createHash(\"sha256\").update(stableStringify(content)).digest(\"hex\").slice(0, 16);\n}\n\n/** The base model (never an active changeset's projection) as a graph with no changes. */\nasync function baseGraph(model: Model): Promise<ChangesetGraph> {\n const now = new Date().toISOString();\n const empty = new Changeset({\n id: \"base\",\n name: \"base model\",\n created: now,\n modified: now,\n status: \"committed\",\n baseSnapshot: \"\",\n changes: [],\n });\n return ChangesetGraph.build(model, empty);\n}\n\nfunction indexFromGraph(graph: ChangesetGraph): BaseIndex {\n const elements: Record<string, string> = {};\n for (const element of graph.elements.values()) {\n if (element.status !== \"deleted\") elements[element.id] = contentHash(element);\n }\n return {\n version: 1,\n captured: new Date().toISOString(),\n elements,\n relationships: graph\n .relationships()\n .filter((r) => r.status !== \"deleted\")\n .map((r) => relationshipKey(r))\n .sort(),\n };\n}\n\n/** Indexes the base model as it is now. */\nexport async function captureBaseIndex(model: Model): Promise<BaseIndex> {\n return indexFromGraph(await baseGraph(model));\n}\n\n/** Records the base index for a changeset that was just created. */\nexport async function writeBaseIndex(rootPath: string, changesetId: string, index: BaseIndex): Promise<void> {\n await writeFile(indexPath(rootPath, changesetId), JSON.stringify(index), \"utf-8\");\n}\n\n/**\n * Reads a changeset's base index. `undefined` when none was recorded; throws when the file exists\n * but cannot be read, so callers can report it rather than mistake it for \"not recorded\".\n */\nexport async function readBaseIndex(rootPath: string, changesetId: string): Promise<BaseIndex | undefined> {\n const file = indexPath(rootPath, changesetId);\n if (!(await fileExists(file))) return undefined;\n const parsed = JSON.parse(await readFile(file, \"utf-8\")) as Partial<BaseIndex>;\n if (parsed.version !== 1 || typeof parsed.elements !== \"object\" || !Array.isArray(parsed.relationships)) {\n throw new Error(`${file} is not a base index this version of dr can read`);\n }\n return parsed as BaseIndex;\n}\n\n/** Like `readBaseIndex`, but reports an unreadable index as a warning instead of throwing. */\nexport async function tryReadBaseIndex(\n rootPath: string,\n changesetId: string\n): Promise<{ index?: BaseIndex; status: ChangesetDrift[\"baseIndex\"]; warning?: string }> {\n try {\n const index = await readBaseIndex(rootPath, changesetId);\n return index ? { index, status: \"recorded\" } : { status: \"not-recorded\" };\n } catch (error) {\n return {\n status: \"unreadable\",\n warning: `The changeset's base index could not be read (${getErrorMessage(error)}); treating it as not recorded.`,\n };\n }\n}\n\nfunction parseKey(key: string): RelationshipRef {\n const [source, predicate, target] = key.split(\"::\");\n return { source, predicate, target };\n}\n\n/**\n * Names how the base model changed under a changeset: staged changes the base now overlaps and,\n * with a base index, what was added, removed or changed in the base since the changeset was created.\n *\n * `graph` is the changeset's projected graph; `hash` is the hash-based drift check\n * (`StagingAreaManager.detectDrift`), when it ran.\n */\nexport async function describeDrift(\n model: Model,\n graph: ChangesetGraph,\n hash?: { isDrifted: boolean; warnings: string[]; baseSnapshotId?: string; currentSnapshotId?: string }\n): Promise<ChangesetDrift> {\n const changeset = graph.changeset;\n const warnings = [...(hash?.warnings ?? [])];\n const overlapping: ChangesetDrift[\"overlapping\"] = { elements: [], relationships: [] };\n\n const inBase = graph.alreadyInBase();\n for (const element of inBase.elements) overlapping.elements.push({ id: element.id, reason: \"already-in-base\" });\n for (const rel of inBase.relationships) {\n overlapping.relationships.push({ source: rel.source, predicate: rel.predicate, target: rel.target, reason: \"already-in-base\" });\n }\n\n // Staged deletes of what the base no longer has.\n const net = new Map<string, \"add\" | \"delete\">();\n const elementOps = new Map<string, string>();\n const addedHere = new Set<string>();\n for (const change of changeset.changes) {\n if (change.type === \"relationship-add\" || change.type === \"relationship-delete\") {\n const data = (change.type === \"relationship-delete\" ? (change.before ?? change.after) : change.after) as\n | Partial<RelationshipRef>\n | undefined;\n const [source, predicate, target] =\n data?.source && data.predicate && data.target\n ? [data.source, data.predicate, data.target]\n : change.elementId.split(\"::\");\n if (!source || !predicate || !target) continue;\n const key = relationshipKey({ source: graph.resolve(source), predicate, target: graph.resolve(target) });\n if (change.type === \"relationship-add\" && !net.has(key)) addedHere.add(key);\n net.set(key, change.type === \"relationship-add\" ? \"add\" : \"delete\");\n } else {\n const id = graph.resolve(change.elementId);\n if (change.type === \"add\" && !elementOps.has(id)) addedHere.add(id);\n elementOps.set(id, change.type);\n }\n }\n for (const [key, op] of net) {\n if (op !== \"delete\" || addedHere.has(key)) continue;\n const ref = parseKey(key);\n if (!graph.getRelationship(ref.source, ref.predicate, ref.target)) {\n overlapping.relationships.push({ ...ref, reason: \"already-removed\" });\n }\n }\n for (const [id, op] of elementOps) {\n if (op === \"delete\" && !addedHere.has(id) && !graph.getElement(id)) overlapping.elements.push({ id, reason: \"already-removed\" });\n }\n\n const read = await tryReadBaseIndex(model.rootPath, changeset.id);\n if (read.warning) warnings.push(read.warning);\n\n let baseChanges: ChangesetDrift[\"baseChanges\"];\n if (read.index) {\n const then = read.index;\n const now = indexFromGraph(await baseGraph(model));\n const elementsAdded = Object.keys(now.elements).filter((id) => !(id in then.elements)).sort();\n const elementsRemoved = Object.keys(then.elements).filter((id) => !(id in now.elements)).sort();\n const elementsChanged = Object.keys(now.elements)\n .filter((id) => id in then.elements && then.elements[id] !== now.elements[id])\n .sort();\n const thenRels = new Set(then.relationships);\n const nowRels = new Set(now.relationships);\n const relationshipsAdded = now.relationships.filter((k) => !thenRels.has(k));\n const relationshipsRemoved = then.relationships.filter((k) => !nowRels.has(k));\n\n for (const id of elementsChanged) {\n const op = elementOps.get(id);\n if (op === \"update\" || op === \"delete\") overlapping.elements.push({ id, reason: \"changed-in-base\" });\n }\n\n const cut = <T>(list: T[]): T[] => list.slice(0, DRIFT_LIST_LIMIT);\n baseChanges = {\n counts: {\n elementsAdded: elementsAdded.length,\n elementsRemoved: elementsRemoved.length,\n elementsChanged: elementsChanged.length,\n relationshipsAdded: relationshipsAdded.length,\n relationshipsRemoved: relationshipsRemoved.length,\n },\n elementsAdded: cut(elementsAdded),\n elementsRemoved: cut(elementsRemoved),\n elementsChanged: cut(elementsChanged),\n relationshipsAdded: cut(relationshipsAdded).map(parseKey),\n relationshipsRemoved: cut(relationshipsRemoved).map(parseKey),\n truncated: [elementsAdded, elementsRemoved, elementsChanged, relationshipsAdded, relationshipsRemoved].some(\n (l) => l.length > DRIFT_LIST_LIMIT\n ),\n };\n } else if (read.status === \"not-recorded\" && (hash?.isDrifted || overlapping.elements.length + overlapping.relationships.length > 0)) {\n warnings.push(\n \"No base index was recorded for this changeset (it predates them), so what changed in the base cannot be \" +\n \"listed beyond the staged changes it overlaps.\"\n );\n }\n\n const baseChanged =\n !!baseChanges &&\n Object.values(baseChanges.counts).some((n) => n > 0);\n const overlapCount = overlapping.elements.length + overlapping.relationships.length;\n const isDrifted = (hash?.isDrifted ?? false) || baseChanged || overlapCount > 0;\n if (overlapCount > 0) {\n warnings.push(\n `The base now overlaps ${overlapCount} staged change(s): ` +\n [\n ...overlapping.elements.map((e) => `${e.id} (${e.reason})`),\n ...overlapping.relationships.map((r) => `${r.source} --${r.predicate}--> ${r.target} (${r.reason})`),\n ]\n .slice(0, 10)\n .join(\", \") +\n (overlapCount > 10 ? \", ...\" : \"\")\n );\n }\n\n return {\n isDrifted,\n warnings,\n ...(hash?.baseSnapshotId ? { baseSnapshotId: hash.baseSnapshotId } : {}),\n ...(hash?.currentSnapshotId ? { currentSnapshotId: hash.currentSnapshotId } : {}),\n baseIndex: read.status,\n overlapping,\n ...(baseChanges ? { baseChanges } : {}),\n };\n}\n\nfunction stableStringify(value: unknown): string {\n if (value === undefined) return \"null\";\n if (value === null || typeof value !== \"object\") return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const entries = Object.keys(value as Record<string, unknown>)\n .sort()\n .map((k) => `${JSON.stringify(k)}:${stableStringify((value as Record<string, unknown>)[k])}`);\n return `{${entries.join(\",\")}}`;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Changeset base index \u2014 which elements and relationships the base model had when a changeset was\n * created, so later readers can tell what predates the plan and what changed in the base under it.\n *\n * The changeset's `baseSnapshot` is a single hash of the whole model: it says the base changed, not\n * what changed. The index records each base element's id with a short content hash, and each base\n * relationship's key, in `documentation-robotics/changesets/{id}/base-index.json`. It is written\n * when the changeset is created. Changesets created before it existed (or imported) have none, and\n * every reader falls back to what it can tell without one and says so.\n *\n * Readers:\n * - `changeset_verify` excludes elements that existed before the plan from the candidates for a\n * planned add (they cannot be its built form).\n * - `changeset_brief` names what changed in the base since the plan was created (`describeDrift`).\n */\n\nimport { createHash } from \"node:crypto\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileExists } from \"../utils/file-io.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { Changeset } from \"./changeset.js\";\nimport { ChangesetGraph, relationshipKey, type GraphElement } from \"./changeset-graph.js\";\nimport type { Model } from \"./model.js\";\n\nexport const BASE_INDEX_FILE = \"base-index.json\";\n\nexport interface BaseIndex {\n version: 1;\n captured: string;\n /** Element id (dotted path) \u2192 short hash of its content. */\n elements: Record<string, string>;\n /** Relationship keys, `source::predicate::target`, endpoints as dotted paths. */\n relationships: string[];\n}\n\nexport interface RelationshipRef {\n source: string;\n predicate: string;\n target: string;\n}\n\n/** How many ids each drift list carries before it is cut short (the counts stay exact). */\nexport const DRIFT_LIST_LIMIT = 50;\n\nexport interface ChangesetDrift {\n /** Kept for compatibility: true when the base changed since the changeset was created. */\n isDrifted: boolean;\n warnings: string[];\n baseSnapshotId?: string;\n currentSnapshotId?: string;\n /** Whether a base index was recorded for the changeset; without one, `baseChanges` is absent. */\n baseIndex: \"recorded\" | \"not-recorded\" | \"unreadable\";\n /**\n * Staged changes that the base now overlaps: adds whose element or relationship the base already\n * has, deletes of what the base no longer has, and (with a base index) elements the plan updates\n * or deletes whose base content changed since the plan was created, and relationships the plan\n * adds or deletes with an end on a base element that changed or was removed since (`endpoints`\n * names those ends). The plan doesn't edit such an element, but what it links to is not what it was.\n */\n overlapping: {\n elements: Array<{ id: string; reason: \"already-in-base\" | \"already-removed\" | \"changed-in-base\" }>;\n relationships: Array<\n RelationshipRef & {\n reason: \"already-in-base\" | \"already-removed\" | \"endpoint-changed-in-base\" | \"endpoint-removed-in-base\";\n /** For the endpoint reasons: the ends the base changed or removed. */\n endpoints?: string[];\n }\n >;\n };\n /** What changed in the base since the changeset was created; needs a base index. */\n baseChanges?: {\n counts: {\n elementsAdded: number;\n elementsRemoved: number;\n elementsChanged: number;\n relationshipsAdded: number;\n relationshipsRemoved: number;\n };\n elementsAdded: string[];\n elementsRemoved: string[];\n elementsChanged: string[];\n relationshipsAdded: RelationshipRef[];\n relationshipsRemoved: RelationshipRef[];\n /** True when a list was cut at DRIFT_LIST_LIMIT entries. */\n truncated: boolean;\n };\n}\n\nfunction indexPath(rootPath: string, changesetId: string): string {\n return path.join(rootPath, \"documentation-robotics\", \"changesets\", changesetId, BASE_INDEX_FILE);\n}\n\nfunction contentHash(element: GraphElement): string {\n const content = {\n type: element.type,\n name: element.name,\n description: element.description ?? null,\n attributes: element.attributes ?? {},\n sourceReference: element.sourceReference ?? null,\n };\n return createHash(\"sha256\").update(stableStringify(content)).digest(\"hex\").slice(0, 16);\n}\n\n/** The base model (never an active changeset's projection) as a graph with no changes. */\nasync function baseGraph(model: Model): Promise<ChangesetGraph> {\n const now = new Date().toISOString();\n const empty = new Changeset({\n id: \"base\",\n name: \"base model\",\n created: now,\n modified: now,\n status: \"committed\",\n baseSnapshot: \"\",\n changes: [],\n });\n return ChangesetGraph.build(model, empty);\n}\n\nfunction indexFromGraph(graph: ChangesetGraph): BaseIndex {\n const elements: Record<string, string> = {};\n for (const element of graph.elements.values()) {\n if (element.status !== \"deleted\") elements[element.id] = contentHash(element);\n }\n return {\n version: 1,\n captured: new Date().toISOString(),\n elements,\n relationships: graph\n .relationships()\n .filter((r) => r.status !== \"deleted\")\n .map((r) => relationshipKey(r))\n .sort(),\n };\n}\n\n/** Indexes the base model as it is now. */\nexport async function captureBaseIndex(model: Model): Promise<BaseIndex> {\n return indexFromGraph(await baseGraph(model));\n}\n\n/** Records the base index for a changeset that was just created. */\nexport async function writeBaseIndex(rootPath: string, changesetId: string, index: BaseIndex): Promise<void> {\n await writeFile(indexPath(rootPath, changesetId), JSON.stringify(index), \"utf-8\");\n}\n\n/**\n * Reads a changeset's base index. `undefined` when none was recorded; throws when the file exists\n * but cannot be read, so callers can report it rather than mistake it for \"not recorded\".\n */\nexport async function readBaseIndex(rootPath: string, changesetId: string): Promise<BaseIndex | undefined> {\n const file = indexPath(rootPath, changesetId);\n if (!(await fileExists(file))) return undefined;\n const parsed = JSON.parse(await readFile(file, \"utf-8\")) as Partial<BaseIndex>;\n if (parsed.version !== 1 || typeof parsed.elements !== \"object\" || !Array.isArray(parsed.relationships)) {\n throw new Error(`${file} is not a base index this version of dr can read`);\n }\n return parsed as BaseIndex;\n}\n\n/** Like `readBaseIndex`, but reports an unreadable index as a warning instead of throwing. */\nexport async function tryReadBaseIndex(\n rootPath: string,\n changesetId: string\n): Promise<{ index?: BaseIndex; status: ChangesetDrift[\"baseIndex\"]; warning?: string }> {\n try {\n const index = await readBaseIndex(rootPath, changesetId);\n return index ? { index, status: \"recorded\" } : { status: \"not-recorded\" };\n } catch (error) {\n return {\n status: \"unreadable\",\n warning: `The changeset's base index could not be read (${getErrorMessage(error)}); treating it as not recorded.`,\n };\n }\n}\n\nfunction parseKey(key: string): RelationshipRef {\n const [source, predicate, target] = key.split(\"::\");\n return { source, predicate, target };\n}\n\n/**\n * Names how the base model changed under a changeset: staged changes the base now overlaps and,\n * with a base index, what was added, removed or changed in the base since the changeset was created.\n *\n * `graph` is the changeset's projected graph; `hash` is the hash-based drift check\n * (`StagingAreaManager.detectDrift`), when it ran.\n */\nexport async function describeDrift(\n model: Model,\n graph: ChangesetGraph,\n hash?: { isDrifted: boolean; warnings: string[]; baseSnapshotId?: string; currentSnapshotId?: string }\n): Promise<ChangesetDrift> {\n const changeset = graph.changeset;\n const warnings = [...(hash?.warnings ?? [])];\n const overlapping: ChangesetDrift[\"overlapping\"] = { elements: [], relationships: [] };\n\n const inBase = graph.alreadyInBase();\n for (const element of inBase.elements) overlapping.elements.push({ id: element.id, reason: \"already-in-base\" });\n for (const rel of inBase.relationships) {\n overlapping.relationships.push({ source: rel.source, predicate: rel.predicate, target: rel.target, reason: \"already-in-base\" });\n }\n\n // Staged deletes of what the base no longer has.\n const net = new Map<string, \"add\" | \"delete\">();\n const elementOps = new Map<string, string>();\n const addedHere = new Set<string>();\n for (const change of changeset.changes) {\n if (change.type === \"relationship-add\" || change.type === \"relationship-delete\") {\n const data = (change.type === \"relationship-delete\" ? (change.before ?? change.after) : change.after) as\n | Partial<RelationshipRef>\n | undefined;\n const [source, predicate, target] =\n data?.source && data.predicate && data.target\n ? [data.source, data.predicate, data.target]\n : change.elementId.split(\"::\");\n if (!source || !predicate || !target) continue;\n const key = relationshipKey({ source: graph.resolve(source), predicate, target: graph.resolve(target) });\n if (change.type === \"relationship-add\" && !net.has(key)) addedHere.add(key);\n net.set(key, change.type === \"relationship-add\" ? \"add\" : \"delete\");\n } else {\n const id = graph.resolve(change.elementId);\n if (change.type === \"add\" && !elementOps.has(id)) addedHere.add(id);\n elementOps.set(id, change.type);\n }\n }\n for (const [key, op] of net) {\n if (op !== \"delete\" || addedHere.has(key)) continue;\n const ref = parseKey(key);\n if (!graph.getRelationship(ref.source, ref.predicate, ref.target)) {\n overlapping.relationships.push({ ...ref, reason: \"already-removed\" });\n }\n }\n for (const [id, op] of elementOps) {\n if (op === \"delete\" && !addedHere.has(id) && !graph.getElement(id)) overlapping.elements.push({ id, reason: \"already-removed\" });\n }\n\n const read = await tryReadBaseIndex(model.rootPath, changeset.id);\n if (read.warning) warnings.push(read.warning);\n\n let baseChanges: ChangesetDrift[\"baseChanges\"];\n if (read.index) {\n const then = read.index;\n const now = indexFromGraph(await baseGraph(model));\n const elementsAdded = Object.keys(now.elements).filter((id) => !(id in then.elements)).sort();\n const elementsRemoved = Object.keys(then.elements).filter((id) => !(id in now.elements)).sort();\n const elementsChanged = Object.keys(now.elements)\n .filter((id) => id in then.elements && then.elements[id] !== now.elements[id])\n .sort();\n const thenRels = new Set(then.relationships);\n const nowRels = new Set(now.relationships);\n const relationshipsAdded = now.relationships.filter((k) => !thenRels.has(k));\n const relationshipsRemoved = then.relationships.filter((k) => !nowRels.has(k));\n\n for (const id of elementsChanged) {\n const op = elementOps.get(id);\n if (op === \"update\" || op === \"delete\") overlapping.elements.push({ id, reason: \"changed-in-base\" });\n }\n\n // The plan's links into base elements that changed or went away since: not an edit of theirs,\n // but what the link reaches is not what the plan saw.\n const changedSet = new Set(elementsChanged);\n const removedSet = new Set(elementsRemoved);\n const listed = new Set(overlapping.relationships.map((r) => relationshipKey(r)));\n for (const key of net.keys()) {\n if (listed.has(key)) continue;\n const ref = parseKey(key);\n const ends = [ref.source, ref.target].filter((id, i, all) => all.indexOf(id) === i);\n const removed = ends.filter((id) => removedSet.has(id) && !addedHere.has(id));\n const changed = ends.filter((id) => changedSet.has(id) && !elementOps.has(id));\n if (removed.length > 0) {\n overlapping.relationships.push({ ...ref, reason: \"endpoint-removed-in-base\", endpoints: removed });\n } else if (changed.length > 0) {\n overlapping.relationships.push({ ...ref, reason: \"endpoint-changed-in-base\", endpoints: changed });\n }\n }\n\n const cut = <T>(list: T[]): T[] => list.slice(0, DRIFT_LIST_LIMIT);\n baseChanges = {\n counts: {\n elementsAdded: elementsAdded.length,\n elementsRemoved: elementsRemoved.length,\n elementsChanged: elementsChanged.length,\n relationshipsAdded: relationshipsAdded.length,\n relationshipsRemoved: relationshipsRemoved.length,\n },\n elementsAdded: cut(elementsAdded),\n elementsRemoved: cut(elementsRemoved),\n elementsChanged: cut(elementsChanged),\n relationshipsAdded: cut(relationshipsAdded).map(parseKey),\n relationshipsRemoved: cut(relationshipsRemoved).map(parseKey),\n truncated: [elementsAdded, elementsRemoved, elementsChanged, relationshipsAdded, relationshipsRemoved].some(\n (l) => l.length > DRIFT_LIST_LIMIT\n ),\n };\n } else if (read.status === \"not-recorded\" && (hash?.isDrifted || overlapping.elements.length + overlapping.relationships.length > 0)) {\n warnings.push(\n \"No base index was recorded for this changeset (it predates them), so what changed in the base cannot be \" +\n \"listed beyond the staged changes it overlaps.\"\n );\n }\n\n const baseChanged =\n !!baseChanges &&\n Object.values(baseChanges.counts).some((n) => n > 0);\n const overlapCount = overlapping.elements.length + overlapping.relationships.length;\n const isDrifted = (hash?.isDrifted ?? false) || baseChanged || overlapCount > 0;\n if (overlapCount > 0) {\n warnings.push(\n `The base now overlaps ${overlapCount} staged change(s): ` +\n [\n ...overlapping.elements.map((e) => `${e.id} (${e.reason})`),\n ...overlapping.relationships.map((r) => `${r.source} --${r.predicate}--> ${r.target} (${r.reason})`),\n ]\n .slice(0, 10)\n .join(\", \") +\n (overlapCount > 10 ? \", ...\" : \"\")\n );\n }\n\n return {\n isDrifted,\n warnings,\n ...(hash?.baseSnapshotId ? { baseSnapshotId: hash.baseSnapshotId } : {}),\n ...(hash?.currentSnapshotId ? { currentSnapshotId: hash.currentSnapshotId } : {}),\n baseIndex: read.status,\n overlapping,\n ...(baseChanges ? { baseChanges } : {}),\n };\n}\n\nfunction stableStringify(value: unknown): string {\n if (value === undefined) return \"null\";\n if (value === null || typeof value !== \"object\") return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const entries = Object.keys(value as Record<string, unknown>)\n .sort()\n .map((k) => `${JSON.stringify(k)}:${stableStringify((value as Record<string, unknown>)[k])}`);\n return `{${entries.join(\",\")}}`;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,256 @@
1
+ import { getAllLayerIds } from "../generated/layer-registry.js";
2
+ const BRIEF_SECTIONS = [
3
+ "changes",
4
+ "changeset",
5
+ "drift",
6
+ "layers",
7
+ "relationshipChanges",
8
+ "alreadyInBase",
9
+ "gaps",
10
+ "unwired",
11
+ "intent",
12
+ "codeTouched",
13
+ "feedback",
14
+ "delta"
15
+ ], ELEMENT_DESCRIPTION_LIMIT = 160, FEEDBACK_LIMIT = 200, LARGE_PLAN = 60, IDS_PER_TYPE = 5, LIST_LIMIT = 30, rel = (r) => `${r.source} --${r.predicate}--> ${r.target}`, gapLine = (g) => `${g.element}: ${g.kind}: ${g.detail}`;
16
+ function gapLines(gaps) {
17
+ if (gaps.length <= LIST_LIMIT)
18
+ return gaps.map(gapLine);
19
+ const groups = /* @__PURE__ */ new Map();
20
+ for (const g of gaps) {
21
+ const key = `${g.kind}: ${g.detail}`;
22
+ groups.set(key, [...groups.get(key) ?? [], g.element]);
23
+ }
24
+ return [...groups].map(([key, elements]) => elements.length === 1 ? `${elements[0]}: ${key}` : `${key} (${elements.length} elements: ${elements.slice(0, IDS_PER_TYPE).join(", ")}${elements.length > IDS_PER_TYPE ? `, +${elements.length - IDS_PER_TYPE} more` : ""})`);
25
+ }
26
+ const cut = (text, limit) => text.length > limit ? `${text.slice(0, limit - 1)}\u2026` : text;
27
+ function unwiredCounts(unwired) {
28
+ const counts = /* @__PURE__ */ new Map();
29
+ let total = 0;
30
+ for (const element of unwired)
31
+ for (const { layer, entries } of element.byLayer)
32
+ counts.set(layer, (counts.get(layer) ?? 0) + entries.length), total += entries.length;
33
+ const order = getAllLayerIds(), rank = (layer) => order.includes(layer) ? order.indexOf(layer) : order.length, byLayer = Object.fromEntries([...counts].sort(([a], [b]) => rank(a) - rank(b)));
34
+ return { total, byLayer };
35
+ }
36
+ function compactDrift(drift) {
37
+ const overlapping = drift.overlapping ? [
38
+ ...drift.overlapping.elements.map((e) => `${e.id}: ${e.reason}`),
39
+ ...drift.overlapping.relationships.map((r) => `${rel(r)}: ${r.reason}${r.endpoints ? ` (${r.endpoints.join(", ")})` : ""}`)
40
+ ] : void 0;
41
+ return {
42
+ isDrifted: drift.isDrifted,
43
+ warnings: drift.warnings,
44
+ ...drift.baseIndex ? { baseIndex: drift.baseIndex } : {},
45
+ ...overlapping ? { overlapping } : {},
46
+ ...drift.baseChanges ? {
47
+ baseChanges: {
48
+ counts: drift.baseChanges.counts,
49
+ elementsChanged: drift.baseChanges.elementsChanged,
50
+ elementsRemoved: drift.baseChanges.elementsRemoved,
51
+ truncated: drift.baseChanges.truncated
52
+ }
53
+ } : {}
54
+ };
55
+ }
56
+ function capped(list, name, counts, limit = LIST_LIMIT) {
57
+ return list.length <= limit ? list : (counts[name] = list.length, list.slice(0, limit));
58
+ }
59
+ function compactDelta(delta) {
60
+ const counts = {}, out = {
61
+ since: delta.since,
62
+ revision: delta.revision,
63
+ elements: {
64
+ added: capped(delta.elements.added, "elements.added", counts),
65
+ removed: capped(delta.elements.removed, "elements.removed", counts),
66
+ updated: capped(delta.elements.updated, "elements.updated", counts).map((u) => `${u.element} (${u.fields.join(", ")})`)
67
+ },
68
+ relationships: {
69
+ added: capped(delta.relationships.added, "relationships.added", counts).map(rel),
70
+ removed: capped(delta.relationships.removed, "relationships.removed", counts).map(rel)
71
+ },
72
+ unstaged: delta.unstaged.map((u) => `#${u.sequence} ${u.operation} ${u.source ? rel({ source: u.source, predicate: u.predicate ?? "?", target: u.target ?? "?" }) : u.elementId ?? ""} (unstaged at ${u.removedAt})`),
73
+ gaps: { opened: gapLines(delta.gaps.opened), closed: gapLines(delta.gaps.closed) },
74
+ unwired: { wired: delta.unwired.wired.length, new: delta.unwired.new.length, newElements: delta.unwired.newElements.length },
75
+ reach: delta.reach,
76
+ intent: {
77
+ newlyTraced: capped(delta.intent.newlyTraced, "intent.newlyTraced", counts),
78
+ newlyUntraced: capped(delta.intent.newlyUntraced, "intent.newlyUntraced", counts)
79
+ },
80
+ ...delta.warnings ? { warnings: delta.warnings } : {}
81
+ };
82
+ return Object.keys(counts).length > 0 && (out.counts = counts), out;
83
+ }
84
+ function compactChanges(brief) {
85
+ const changedIds = new Set(brief.layers.flatMap((l) => l.changes.map((c) => c.id))), filesOf = /* @__PURE__ */ new Map(), files = /* @__PURE__ */ new Map(), neighborFiles = [], baseName = (file) => file.slice(file.lastIndexOf("/") + 1), nameCount = /* @__PURE__ */ new Map();
86
+ for (const { file } of brief.codeTouched)
87
+ nameCount.set(baseName(file), (nameCount.get(baseName(file)) ?? 0) + 1);
88
+ const shortName = (file) => nameCount.get(baseName(file)) === 1 ? baseName(file) : file;
89
+ for (const { file, elements } of brief.codeTouched) {
90
+ const changed = elements.filter((e) => e.role === "changed");
91
+ changed.length === 0 ? neighborFiles.push(file) : files.set(file, changed.length);
92
+ for (const e of changed)
93
+ filesOf.set(e.id, [...filesOf.get(e.id) ?? [], shortName(file)]);
94
+ }
95
+ const changes = [], build = [];
96
+ for (const c of brief.layers.flatMap((l) => l.changes)) {
97
+ const connections = [];
98
+ for (const n of c.connections) {
99
+ if (n.direction === "in" && changedIds.has(n.other))
100
+ continue;
101
+ const mark = n.status === "deleted" ? " (deleted)" : n.status === "unchanged" ? " (base)" : "";
102
+ connections.push(n.direction === "out" ? `${n.predicate} \u2192 ${n.other}${mark}` : `${n.predicate} \u2190 ${n.other}${mark}`);
103
+ }
104
+ const sourceFiles = filesOf.has(c.id) ? { sourceFiles: filesOf.get(c.id) } : {};
105
+ changes.push({
106
+ id: c.id,
107
+ op: c.op,
108
+ ...c.description ? { description: cut(c.description, ELEMENT_DESCRIPTION_LIMIT) } : {},
109
+ ...sourceFiles,
110
+ ...c.changedFields?.length ? { changedFields: c.changedFields.map((f) => f.field) } : {},
111
+ connections
112
+ }), build.push({
113
+ id: c.id,
114
+ op: c.op,
115
+ name: c.name,
116
+ ...c.description ? { description: c.description } : {},
117
+ ...c.attributes && Object.keys(c.attributes).length > 0 ? { attributes: c.attributes } : {},
118
+ ...c.changedFields?.length ? { changedFields: c.changedFields } : {},
119
+ ...sourceFiles,
120
+ connections
121
+ });
122
+ }
123
+ return { changes, build, files, neighborFiles };
124
+ }
125
+ function changesByType(brief, idsPerType = IDS_PER_TYPE) {
126
+ const groups = /* @__PURE__ */ new Map();
127
+ for (const layer of brief.layers)
128
+ for (const c of layer.changes) {
129
+ const parts = c.id.split("."), type = parts.length >= 3 ? `${parts[0]}.${parts[1]}` : layer.layer, group = groups.get(type) ?? { type, added: 0, updated: 0, deleted: 0, ids: [] };
130
+ group[c.op] += 1, group.ids.length < idsPerType ? group.ids.push(c.id) : group.more = (group.more ?? 0) + 1, groups.set(type, group);
131
+ }
132
+ return [...groups.values()];
133
+ }
134
+ function headerOf(brief) {
135
+ return { id: brief.changeset.id, name: brief.changeset.name, status: brief.changeset.status };
136
+ }
137
+ function compactBrief(brief, options = {}) {
138
+ const { changes, files, neighborFiles } = compactChanges(brief), changedCount = changes.length, large = changedCount > LARGE_PLAN, description = brief.changeset.description, header = { ...headerOf(brief), ...description ? { description } : {} };
139
+ brief.changeset.drift && (header.drift = compactDrift(brief.changeset.drift));
140
+ const cutLists = {}, unanchored = brief.intent.unanchored?.map((u) => u.element), intent = {
141
+ traced: capped(brief.intent.traced.map((t) => t.element), "traced", cutLists),
142
+ untraced: capped(brief.intent.untraced, "untraced", cutLists),
143
+ ...unanchored ? { unanchored: capped(unanchored, "unanchored", cutLists) } : {}
144
+ };
145
+ Object.keys(cutLists).length > 0 && (intent.counts = {
146
+ traced: brief.intent.traced.length,
147
+ untraced: brief.intent.untraced.length,
148
+ ...unanchored ? { unanchored: unanchored.length } : {}
149
+ });
150
+ const withDelta = !!options.delta, elementList = withDelta ? {} : large ? { changesByType: changesByType(brief) } : { changes }, sectionNames = `sections: [...] for only some sections, in full (${BRIEF_SECTIONS.join(", ")})`, more = (withDelta ? 'With since, the changed elements are left out: the delta says what changed (sections: ["changes"] lists them). ' : large ? `This plan changes ${changedCount} elements, so changesByType counts them per type with the first ${IDS_PER_TYPE} ids of each; sections: ["changes"] lists every one with its connections. ` : "Each changed element is listed with a short description, its files and its connections; attributes are in the full brief. ") + (brief.gaps.length > LIST_LIMIT ? 'Gaps that say the same thing share a line (sections: ["gaps"] lists each). ' : "") + (intent.counts ? `Intent lists are cut at ${LIST_LIMIT} ids (intent.counts has their lengths; sections: ["intent"] has them all). ` : "") + `Pass detail: "full" for the whole brief, or ${sectionNames}.`, out = {
151
+ detail: "compact",
152
+ revision: brief.revision,
153
+ revisionToken: brief.revisionToken,
154
+ changeset: header,
155
+ summary: brief.summary,
156
+ byLayer: brief.layers.map((l) => ({
157
+ layer: l.layer,
158
+ added: l.changes.filter((c) => c.op === "added").length,
159
+ updated: l.changes.filter((c) => c.op === "updated").length,
160
+ deleted: l.changes.filter((c) => c.op === "deleted").length
161
+ })),
162
+ reach: brief.reach,
163
+ gaps: gapLines(brief.gaps),
164
+ intent,
165
+ ...brief.unwired && !options.unwired ? { unwired: brief.unwired } : options.unwired ? { unwired: unwiredCounts(options.unwired) } : {},
166
+ ...elementList,
167
+ relationshipChanges: brief.relationshipChanges.map((r) => `${r.status} ${rel(r)}`),
168
+ codeTouched: [...files].map(([file, n]) => `${file} (${n})`),
169
+ neighborFiles,
170
+ feedback: brief.feedback.map(feedbackLine),
171
+ more
172
+ };
173
+ return brief.alreadyInBase.elements.length + brief.alreadyInBase.relationships.length > 0 && (out.alreadyInBase = brief.alreadyInBase), options.delta && (out.delta = compactDelta(options.delta)), out;
174
+ }
175
+ const feedbackLine = (f) => `${f.annotationId} on ${f.elementId} (${f.author}, ${f.replies} ${f.replies === 1 ? "reply" : "replies"}): ${cut(f.content, FEEDBACK_LIMIT)}`;
176
+ function briefSections(brief, sections, options = {}) {
177
+ const names = [...new Set(sections)], filter = options.filter && (options.filter.layers?.length || options.filter.ids?.length) ? options.filter : void 0, ids = filter?.ids ? new Set(filter.ids) : void 0, layers = filter?.layers ? new Set(filter.layers) : void 0, keep = (id) => filter ? (ids?.has(id) ?? !1) || (layers?.has(id.split(".")[0]) ?? !1) : !0, out = {
178
+ detail: "compact",
179
+ revision: brief.revision,
180
+ revisionToken: brief.revisionToken,
181
+ changeset: headerOf(brief),
182
+ summary: brief.summary,
183
+ sections: names,
184
+ ...filter ? { filter } : {},
185
+ more: ""
186
+ }, notes = [];
187
+ for (const name of names)
188
+ switch (name) {
189
+ case "changes":
190
+ out.changes = compactChanges(brief).build.filter((c) => keep(c.id));
191
+ break;
192
+ case "changeset": {
193
+ const description = brief.changeset.description;
194
+ out.changeset = {
195
+ ...out.changeset,
196
+ ...description ? { description } : {},
197
+ ...brief.changeset.drift ? { drift: brief.changeset.drift } : {}
198
+ };
199
+ break;
200
+ }
201
+ case "drift":
202
+ brief.changeset.drift ? out.drift = brief.changeset.drift : notes.push("No drift: the changeset is not staged.");
203
+ break;
204
+ case "layers":
205
+ out.layers = filter ? brief.layers.map((l) => ({ ...l, changes: l.changes.filter((c) => keep(c.id)) })).filter((l) => l.changes.length > 0) : brief.layers;
206
+ break;
207
+ case "relationshipChanges":
208
+ out.relationshipChanges = brief.relationshipChanges;
209
+ break;
210
+ case "alreadyInBase":
211
+ out.alreadyInBase = brief.alreadyInBase;
212
+ break;
213
+ case "gaps":
214
+ out.gaps = brief.gaps.filter((g) => keep(g.element));
215
+ break;
216
+ case "unwired":
217
+ brief.unwired && (out.unwired = filter ? { ...brief.unwired, elements: brief.unwired.elements.filter((e) => keep(e.element)) } : brief.unwired);
218
+ break;
219
+ case "intent":
220
+ out.intent = filter ? {
221
+ traced: brief.intent.traced.filter((t) => keep(t.element)),
222
+ untraced: brief.intent.untraced.filter(keep),
223
+ ...brief.intent.unanchored ? { unanchored: brief.intent.unanchored.filter((u) => keep(u.element)) } : {}
224
+ } : brief.intent;
225
+ break;
226
+ case "codeTouched":
227
+ out.codeTouched = brief.codeTouched;
228
+ break;
229
+ case "feedback":
230
+ out.feedback = brief.feedback.filter((f) => keep(f.elementId));
231
+ break;
232
+ case "delta":
233
+ options.delta || notes.push("delta needs since: pass the revisionToken of an earlier brief.");
234
+ break;
235
+ }
236
+ if (options.delta && (out.delta = names.includes("delta") ? options.delta : compactDelta(options.delta)), filter && ids) {
237
+ const known = new Set(brief.layers.flatMap((l) => l.changes.map((c) => c.id))), unknown = [...ids].filter((id) => !known.has(id));
238
+ unknown.length > 0 && notes.push(`Not changed by this changeset: ${unknown.join(", ")}.`);
239
+ }
240
+ return out.more = [
241
+ ...notes,
242
+ `Only the sections asked for are returned (${names.join(", ")})` + (filter ? ", for the elements the filter keeps" : "") + '. Call without sections for the compact brief, or with detail: "full" for everything.'
243
+ ].join(" "), out;
244
+ }
245
+ export {
246
+ BRIEF_SECTIONS,
247
+ LARGE_PLAN,
248
+ LIST_LIMIT,
249
+ briefSections,
250
+ changesByType,
251
+ compactBrief,
252
+ compactDelta,
253
+ gapLines,
254
+ unwiredCounts
255
+ };
256
+ //# sourceMappingURL=changeset-brief-compact.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/core/changeset-brief-compact.ts"],
4
+ "sourcesContent": ["/**\n * The compact brief \u2014 what `changeset_brief` returns by default.\n *\n * The full brief (`buildBrief`) lists every changed element with its attributes, every connection\n * with the other end's name and layer, every unwired connection, and the code each element touches.\n * On a real plan that is 55\u2013420 KB, more than an agent's client takes in one result. The compact\n * brief keeps what a planning pass reads: counts, reach, gaps (one line each), intent (ids), drift,\n * and each changed element with its connections as `predicate \u2192 id` lines. A large plan (more than\n * LARGE_PLAN changed elements, a bootstrap) has its elements summarised by type instead, and long id\n * lists cut, so the default stays well under 30 KB. `sections` returns only the sections it names (a\n * small header besides), and `detail: \"full\"` returns the full brief unchanged.\n *\n * See \"Iterating on a plan\" in docs/CHANGE_MODELING.md.\n */\n\nimport { getAllLayerIds } from \"../generated/layer-registry.js\";\nimport type { BriefDelta } from \"./changeset-brief-delta.js\";\nimport type { BriefDrift, BriefGap, ChangesetBrief, ElementUnwired } from \"./changeset-analysis.js\";\n\n/**\n * Sections available through `sections`: each full-brief section in full, and `changes`, the\n * compact element list of every changed element. Naming sections returns only those, with a small\n * header (revision, the changeset's id, name and status, summary counts).\n */\nexport const BRIEF_SECTIONS = [\n \"changes\",\n \"changeset\",\n \"drift\",\n \"layers\",\n \"relationshipChanges\",\n \"alreadyInBase\",\n \"gaps\",\n \"unwired\",\n \"intent\",\n \"codeTouched\",\n \"feedback\",\n \"delta\",\n] as const;\n\nexport type BriefSection = (typeof BRIEF_SECTIONS)[number];\n\n/** A changed element's description longer than this is cut (the changeset's own never is). */\nconst ELEMENT_DESCRIPTION_LIMIT = 160;\n\n/** A feedback comment longer than this is cut. */\nconst FEEDBACK_LIMIT = 200;\n\n/** A plan with more changed elements than this is summarised by type in the default brief. */\nexport const LARGE_PLAN = 60;\n\n/** Ids listed per type in a large plan's summary. */\nconst IDS_PER_TYPE = 5;\n\n/** An id list in the default brief (intent, the delta) longer than this is cut, with its count kept. */\nexport const LIST_LIMIT = 30;\n\n/**\n * A changed element as `sections: [\"changes\"]` returns it: the build view. What the build needs to\n * write the element (its name, description in full, attributes, the fields an update changes with\n * both values, its files) and its connections as `predicate \u2192 id` lines, without the full brief's\n * names and layers per connection.\n */\nexport interface BuildChange {\n id: string;\n op: \"added\" | \"updated\" | \"deleted\";\n name: string;\n description?: string;\n attributes?: Record<string, unknown>;\n /** Updates: each field that changes, before and after. */\n changedFields?: Array<{ field: string; before: unknown; after: unknown }>;\n sourceFiles?: string[];\n connections: string[];\n}\n\n/** Narrows `sections` output to some elements: those in these layers, or with these ids. */\nexport interface SectionFilter {\n layers?: string[];\n ids?: string[];\n}\n\n/** A large plan's changed elements, per `layer.type`. */\nexport interface ChangesByType {\n type: string;\n added: number;\n updated: number;\n deleted: number;\n /** The first ids of the type, in brief order. */\n ids: string[];\n /** How many more of the type there are. */\n more?: number;\n}\n\nexport interface CompactChange {\n id: string;\n // The type is the id's second segment (`layer.type.name`), so it is not repeated.\n op: \"added\" | \"updated\" | \"deleted\";\n /** The element's description, cut at 160 characters. */\n description?: string;\n /**\n * The files its source reference names: where the build edits. A file's name alone when no other\n * touched file has it; `codeTouched` has the path.\n */\n sourceFiles?: string[];\n /** Updates: the fields that change. */\n changedFields?: string[];\n /**\n * `predicate \u2192 id` for a relationship from this element, `predicate \u2190 id` for one into it from an\n * element the changeset does not change (one between two changed elements is listed once, on its\n * source). `(deleted)` marks a relationship the change removes, `(base)` one the base already has.\n */\n connections: string[];\n}\n\nexport interface CompactDrift {\n isDrifted: boolean;\n warnings: string[];\n baseIndex?: string;\n /** `id: reason` for elements, `source --predicate--> target: reason` for relationships. */\n overlapping?: string[];\n baseChanges?: {\n counts: NonNullable<BriefDrift[\"baseChanges\"]>[\"counts\"];\n elementsChanged: string[];\n elementsRemoved: string[];\n truncated: boolean;\n };\n}\n\nexport interface CompactDelta {\n since: number;\n revision: number;\n elements: { added: string[]; removed: string[]; updated: string[] };\n relationships: { added: string[]; removed: string[] };\n unstaged: string[];\n gaps: { opened: string[]; closed: string[] };\n unwired: { wired: number; new: number; newElements: number };\n reach: BriefDelta[\"reach\"];\n intent: BriefDelta[\"intent\"];\n /** Set when a list was cut at LIST_LIMIT: each list's full length. `sections: [\"delta\"]` has them all. */\n counts?: Record<string, number>;\n warnings?: string[];\n}\n\nexport interface CompactBrief {\n detail: \"compact\";\n revision: number;\n revisionToken: string;\n changeset: {\n id: string;\n name: string;\n status: string;\n description?: string;\n drift?: CompactDrift;\n };\n summary: ChangesetBrief[\"summary\"];\n /** Changed elements per layer, in layer order. */\n byLayer: Array<{ layer: string; added: number; updated: number; deleted: number }>;\n reach: ChangesetBrief[\"reach\"];\n /** `element: kind: detail`, one per gap (see `gapLines` for a long list). */\n gaps: string[];\n intent: {\n traced: string[];\n untraced: string[];\n unanchored?: string[];\n /** Set when a list was cut at LIST_LIMIT: each list's full length. */\n counts?: { traced: number; untraced: number; unanchored?: number };\n };\n /** Unwired connections of added and updated elements, counted per other-end layer (listed when asked for). */\n unwired?: { total: number; byLayer: Record<string, number> } | ChangesetBrief[\"unwired\"];\n /**\n * Every changed element. Left out of a brief with a delta, and of a large plan's (which has\n * `changesByType`); `sections: [\"changes\"]` lists them.\n */\n changes?: CompactChange[];\n /** A large plan's changed elements, counted per type with the first ids of each. */\n changesByType?: ChangesByType[];\n /** `status source --predicate--> target` for relationships between unchanged elements. */\n relationshipChanges: string[];\n alreadyInBase?: ChangesetBrief[\"alreadyInBase\"];\n /** Files of the changed elements, with how many changed elements each has. */\n codeTouched: string[];\n /** Files that hold only existing elements next to the change: the build must not break them. */\n neighborFiles: string[];\n /** `annotationId on elementId (author, n replies): content`. */\n feedback: string[];\n delta?: CompactDelta;\n /** Set when the call had arguments this form doesn't use (e.g. `layers` or `ids` without `sections`). */\n note?: string;\n /** How to get more. */\n more: string;\n}\n\nconst rel = (r: { source: string; predicate: string; target: string }) => `${r.source} --${r.predicate}--> ${r.target}`;\nconst gapLine = (g: BriefGap) => `${g.element}: ${g.kind}: ${g.detail}`;\n\n/**\n * Gaps one line each; past LIST_LIMIT, gaps that say the same thing (a bootstrap's unconnected\n * adds) share one line, `kind: detail (n elements: first ids, +m more)`.\n */\nexport function gapLines(gaps: BriefGap[]): string[] {\n if (gaps.length <= LIST_LIMIT) return gaps.map(gapLine);\n const groups = new Map<string, string[]>();\n for (const g of gaps) {\n const key = `${g.kind}: ${g.detail}`;\n groups.set(key, [...(groups.get(key) ?? []), g.element]);\n }\n return [...groups].map(([key, elements]) =>\n elements.length === 1\n ? `${elements[0]}: ${key}`\n : `${key} (${elements.length} elements: ${elements.slice(0, IDS_PER_TYPE).join(\", \")}` +\n `${elements.length > IDS_PER_TYPE ? `, +${elements.length - IDS_PER_TYPE} more` : \"\"})`\n );\n}\nconst cut = (text: string, limit: number) => (text.length > limit ? `${text.slice(0, limit - 1)}\u2026` : text);\n\n/** Unwired connections counted per other-end layer, across every added or updated element. */\nexport function unwiredCounts(unwired: ElementUnwired[]): { total: number; byLayer: Record<string, number> } {\n const counts = new Map<string, number>();\n let total = 0;\n for (const element of unwired) {\n for (const { layer, entries } of element.byLayer) {\n counts.set(layer, (counts.get(layer) ?? 0) + entries.length);\n total += entries.length;\n }\n }\n const order = getAllLayerIds();\n const rank = (layer: string) => (order.includes(layer) ? order.indexOf(layer) : order.length);\n const byLayer = Object.fromEntries([...counts].sort(([a], [b]) => rank(a) - rank(b)));\n return { total, byLayer };\n}\n\nfunction compactDrift(drift: BriefDrift): CompactDrift {\n const overlapping = drift.overlapping\n ? [\n ...drift.overlapping.elements.map((e) => `${e.id}: ${e.reason}`),\n ...drift.overlapping.relationships.map((r) => `${rel(r)}: ${r.reason}${r.endpoints ? ` (${r.endpoints.join(\", \")})` : \"\"}`),\n ]\n : undefined;\n return {\n isDrifted: drift.isDrifted,\n warnings: drift.warnings,\n ...(drift.baseIndex ? { baseIndex: drift.baseIndex } : {}),\n ...(overlapping ? { overlapping } : {}),\n ...(drift.baseChanges\n ? {\n baseChanges: {\n counts: drift.baseChanges.counts,\n elementsChanged: drift.baseChanges.elementsChanged,\n elementsRemoved: drift.baseChanges.elementsRemoved,\n truncated: drift.baseChanges.truncated,\n },\n }\n : {}),\n };\n}\n\n/** A list cut at `limit`, recording its full length in `counts` under `name` when it was cut. */\nfunction capped<T>(list: T[], name: string, counts: Record<string, number>, limit = LIST_LIMIT): T[] {\n if (list.length <= limit) return list;\n counts[name] = list.length;\n return list.slice(0, limit);\n}\n\nexport function compactDelta(delta: BriefDelta): CompactDelta {\n const counts: Record<string, number> = {};\n const out: CompactDelta = {\n since: delta.since,\n revision: delta.revision,\n elements: {\n added: capped(delta.elements.added, \"elements.added\", counts),\n removed: capped(delta.elements.removed, \"elements.removed\", counts),\n updated: capped(delta.elements.updated, \"elements.updated\", counts).map((u) => `${u.element} (${u.fields.join(\", \")})`),\n },\n relationships: {\n added: capped(delta.relationships.added, \"relationships.added\", counts).map(rel),\n removed: capped(delta.relationships.removed, \"relationships.removed\", counts).map(rel),\n },\n unstaged: delta.unstaged.map(\n (u) =>\n `#${u.sequence} ${u.operation} ${u.source ? rel({ source: u.source, predicate: u.predicate ?? \"?\", target: u.target ?? \"?\" }) : (u.elementId ?? \"\")} (unstaged at ${u.removedAt})`\n ),\n gaps: { opened: gapLines(delta.gaps.opened), closed: gapLines(delta.gaps.closed) },\n unwired: { wired: delta.unwired.wired.length, new: delta.unwired.new.length, newElements: delta.unwired.newElements.length },\n reach: delta.reach,\n intent: {\n newlyTraced: capped(delta.intent.newlyTraced, \"intent.newlyTraced\", counts),\n newlyUntraced: capped(delta.intent.newlyUntraced, \"intent.newlyUntraced\", counts),\n },\n ...(delta.warnings ? { warnings: delta.warnings } : {}),\n };\n if (Object.keys(counts).length > 0) out.counts = counts;\n return out;\n}\n\n/** Each changed element in the compact form, and the touched files (changed ones counted, neighbour-only ones apart). */\nfunction compactChanges(brief: ChangesetBrief): {\n changes: CompactChange[];\n build: BuildChange[];\n files: Map<string, number>;\n neighborFiles: string[];\n} {\n const changedIds = new Set(brief.layers.flatMap((l) => l.changes.map((c) => c.id)));\n\n // Files: per changed element, and those holding only neighbours.\n const filesOf = new Map<string, string[]>();\n const files = new Map<string, number>();\n const neighborFiles: string[] = [];\n // Per element, a file's name stands for its path when no other touched file has that name.\n const baseName = (file: string) => file.slice(file.lastIndexOf(\"/\") + 1);\n const nameCount = new Map<string, number>();\n for (const { file } of brief.codeTouched) nameCount.set(baseName(file), (nameCount.get(baseName(file)) ?? 0) + 1);\n const shortName = (file: string) => (nameCount.get(baseName(file)) === 1 ? baseName(file) : file);\n for (const { file, elements } of brief.codeTouched) {\n const changed = elements.filter((e) => e.role === \"changed\");\n if (changed.length === 0) neighborFiles.push(file);\n else files.set(file, changed.length);\n for (const e of changed) filesOf.set(e.id, [...(filesOf.get(e.id) ?? []), shortName(file)]);\n }\n\n const changes: CompactChange[] = [];\n const build: BuildChange[] = [];\n for (const c of brief.layers.flatMap((l) => l.changes)) {\n const connections: string[] = [];\n for (const n of c.connections) {\n if (n.direction === \"in\" && changedIds.has(n.other)) continue; // listed on its source\n const mark = n.status === \"deleted\" ? \" (deleted)\" : n.status === \"unchanged\" ? \" (base)\" : \"\";\n connections.push(n.direction === \"out\" ? `${n.predicate} \u2192 ${n.other}${mark}` : `${n.predicate} \u2190 ${n.other}${mark}`);\n }\n const sourceFiles = filesOf.has(c.id) ? { sourceFiles: filesOf.get(c.id) } : {};\n changes.push({\n id: c.id,\n op: c.op,\n ...(c.description ? { description: cut(c.description, ELEMENT_DESCRIPTION_LIMIT) } : {}),\n ...sourceFiles,\n ...(c.changedFields?.length ? { changedFields: c.changedFields.map((f) => f.field) } : {}),\n connections,\n });\n build.push({\n id: c.id,\n op: c.op,\n name: c.name,\n ...(c.description ? { description: c.description } : {}),\n ...(c.attributes && Object.keys(c.attributes).length > 0 ? { attributes: c.attributes } : {}),\n ...(c.changedFields?.length ? { changedFields: c.changedFields } : {}),\n ...sourceFiles,\n connections,\n });\n }\n return { changes, build, files, neighborFiles };\n}\n\n/** A large plan's changed elements per `layer.type`, in brief order, with the first ids of each. */\nexport function changesByType(brief: ChangesetBrief, idsPerType = IDS_PER_TYPE): ChangesByType[] {\n const groups = new Map<string, ChangesByType>();\n for (const layer of brief.layers) {\n for (const c of layer.changes) {\n const parts = c.id.split(\".\");\n const type = parts.length >= 3 ? `${parts[0]}.${parts[1]}` : layer.layer;\n const group = groups.get(type) ?? { type, added: 0, updated: 0, deleted: 0, ids: [] };\n group[c.op] += 1;\n if (group.ids.length < idsPerType) group.ids.push(c.id);\n else group.more = (group.more ?? 0) + 1;\n groups.set(type, group);\n }\n }\n return [...groups.values()];\n}\n\nfunction headerOf(brief: ChangesetBrief): { id: string; name: string; status: string } {\n return { id: brief.changeset.id, name: brief.changeset.name, status: brief.changeset.status };\n}\n\n/**\n * The compact form of a full brief. `unwired` is the per-element list, counted per layer (pass it\n * when the brief was built without its unwired listing). `briefSections` returns named sections only.\n */\nexport function compactBrief(brief: ChangesetBrief, options: { unwired?: ElementUnwired[]; delta?: BriefDelta } = {}): CompactBrief {\n const { changes, files, neighborFiles } = compactChanges(brief);\n const changedCount = changes.length;\n const large = changedCount > LARGE_PLAN;\n\n // The description in full: accepted gaps and decisions are recorded there.\n const description = brief.changeset.description;\n const header: CompactBrief[\"changeset\"] = { ...headerOf(brief), ...(description ? { description } : {}) };\n if (brief.changeset.drift) header.drift = compactDrift(brief.changeset.drift);\n\n // Intent as ids; a long list is cut (a bootstrap has every element untraced) with its count kept.\n const cutLists: Record<string, number> = {};\n const unanchored = brief.intent.unanchored?.map((u) => u.element);\n const intent: CompactBrief[\"intent\"] = {\n traced: capped(brief.intent.traced.map((t) => t.element), \"traced\", cutLists),\n untraced: capped(brief.intent.untraced, \"untraced\", cutLists),\n ...(unanchored ? { unanchored: capped(unanchored, \"unanchored\", cutLists) } : {}),\n };\n if (Object.keys(cutLists).length > 0) {\n intent.counts = {\n traced: brief.intent.traced.length,\n untraced: brief.intent.untraced.length,\n ...(unanchored ? { unanchored: unanchored.length } : {}),\n };\n }\n\n const withDelta = !!options.delta;\n const elementList = withDelta ? {} : large ? { changesByType: changesByType(brief) } : { changes };\n const sectionNames = `sections: [...] for only some sections, in full (${BRIEF_SECTIONS.join(\", \")})`;\n const more =\n (withDelta\n ? 'With since, the changed elements are left out: the delta says what changed (sections: [\"changes\"] lists them). '\n : large\n ? `This plan changes ${changedCount} elements, so changesByType counts them per type with the first ${IDS_PER_TYPE} ids ` +\n 'of each; sections: [\"changes\"] lists every one with its connections. '\n : \"Each changed element is listed with a short description, its files and its connections; attributes are in the full brief. \") +\n (brief.gaps.length > LIST_LIMIT ? 'Gaps that say the same thing share a line (sections: [\"gaps\"] lists each). ' : \"\") +\n (intent.counts ? `Intent lists are cut at ${LIST_LIMIT} ids (intent.counts has their lengths; sections: [\"intent\"] has them all). ` : \"\") +\n `Pass detail: \"full\" for the whole brief, or ${sectionNames}.`;\n\n const out: CompactBrief = {\n detail: \"compact\",\n revision: brief.revision,\n revisionToken: brief.revisionToken,\n changeset: header,\n summary: brief.summary,\n byLayer: brief.layers.map((l) => ({\n layer: l.layer,\n added: l.changes.filter((c) => c.op === \"added\").length,\n updated: l.changes.filter((c) => c.op === \"updated\").length,\n deleted: l.changes.filter((c) => c.op === \"deleted\").length,\n })),\n reach: brief.reach,\n gaps: gapLines(brief.gaps),\n intent,\n ...(brief.unwired && !options.unwired\n ? { unwired: brief.unwired }\n : options.unwired\n ? { unwired: unwiredCounts(options.unwired) }\n : {}),\n ...elementList,\n relationshipChanges: brief.relationshipChanges.map((r) => `${r.status} ${rel(r)}`),\n codeTouched: [...files].map(([file, n]) => `${file} (${n})`),\n neighborFiles,\n feedback: brief.feedback.map(feedbackLine),\n more,\n };\n if (brief.alreadyInBase.elements.length + brief.alreadyInBase.relationships.length > 0) {\n // Short, and commit skips them: listed whenever there are any.\n out.alreadyInBase = brief.alreadyInBase;\n }\n if (options.delta) out.delta = compactDelta(options.delta);\n return out;\n}\n\nconst feedbackLine = (f: ChangesetBrief[\"feedback\"][number]) =>\n `${f.annotationId} on ${f.elementId} (${f.author}, ${f.replies} ${f.replies === 1 ? \"reply\" : \"replies\"}): ${cut(f.content, FEEDBACK_LIMIT)}`;\n\n/** A brief of named sections only: a small header, then each section in full. */\nexport interface SectionsBrief {\n detail: \"compact\";\n revision: number;\n revisionToken: string;\n /** The changeset's id, name and status; its description and drift with the `changeset` section. */\n changeset: { id: string; name: string; status: string; description?: string; drift?: BriefDrift };\n summary: ChangesetBrief[\"summary\"];\n /** The sections returned, in the order asked. */\n sections: BriefSection[];\n /** The filter applied to the element sections, when one was given. */\n filter?: SectionFilter;\n /** The build view of each changed element (see BuildChange). */\n changes?: BuildChange[];\n drift?: BriefDrift;\n layers?: ChangesetBrief[\"layers\"];\n relationshipChanges?: ChangesetBrief[\"relationshipChanges\"];\n alreadyInBase?: ChangesetBrief[\"alreadyInBase\"];\n gaps?: BriefGap[];\n unwired?: ChangesetBrief[\"unwired\"];\n intent?: ChangesetBrief[\"intent\"];\n codeTouched?: ChangesetBrief[\"codeTouched\"];\n feedback?: ChangesetBrief[\"feedback\"];\n /** With since: the compact delta, or the full one when `delta` is named. */\n delta?: CompactDelta | BriefDelta;\n more: string;\n}\n\n/**\n * Only the named sections, each in full, under a small header. `changes` is the build view of every\n * changed element; with `since`, the delta comes too, compact unless `delta` is named. `filter`\n * narrows the element sections (changes, layers, gaps, unwired, intent, feedback) to the elements in\n * some layers or with some ids.\n */\nexport function briefSections(\n brief: ChangesetBrief,\n sections: readonly BriefSection[],\n options: { delta?: BriefDelta; filter?: SectionFilter } = {}\n): SectionsBrief {\n const names = [...new Set(sections)];\n const filter = options.filter && (options.filter.layers?.length || options.filter.ids?.length) ? options.filter : undefined;\n const ids = filter?.ids ? new Set(filter.ids) : undefined;\n const layers = filter?.layers ? new Set(filter.layers) : undefined;\n const keep = (id: string): boolean => {\n if (!filter) return true;\n return (ids?.has(id) ?? false) || (layers?.has(id.split(\".\")[0]) ?? false);\n };\n const out: SectionsBrief = {\n detail: \"compact\",\n revision: brief.revision,\n revisionToken: brief.revisionToken,\n changeset: headerOf(brief),\n summary: brief.summary,\n sections: names,\n ...(filter ? { filter } : {}),\n more: \"\",\n };\n const notes: string[] = [];\n for (const name of names) {\n switch (name) {\n case \"changes\":\n out.changes = compactChanges(brief).build.filter((c) => keep(c.id));\n break;\n case \"changeset\": {\n const description = brief.changeset.description;\n out.changeset = {\n ...out.changeset,\n ...(description ? { description } : {}),\n ...(brief.changeset.drift ? { drift: brief.changeset.drift } : {}),\n };\n break;\n }\n case \"drift\":\n if (brief.changeset.drift) out.drift = brief.changeset.drift;\n else notes.push(\"No drift: the changeset is not staged.\");\n break;\n case \"layers\":\n out.layers = filter\n ? brief.layers.map((l) => ({ ...l, changes: l.changes.filter((c) => keep(c.id)) })).filter((l) => l.changes.length > 0)\n : brief.layers;\n break;\n case \"relationshipChanges\":\n out.relationshipChanges = brief.relationshipChanges;\n break;\n case \"alreadyInBase\":\n out.alreadyInBase = brief.alreadyInBase;\n break;\n case \"gaps\":\n out.gaps = brief.gaps.filter((g) => keep(g.element));\n break;\n case \"unwired\":\n if (brief.unwired) out.unwired = filter ? { ...brief.unwired, elements: brief.unwired.elements.filter((e) => keep(e.element)) } : brief.unwired;\n break;\n case \"intent\":\n out.intent = filter\n ? {\n traced: brief.intent.traced.filter((t) => keep(t.element)),\n untraced: brief.intent.untraced.filter(keep),\n ...(brief.intent.unanchored ? { unanchored: brief.intent.unanchored.filter((u) => keep(u.element)) } : {}),\n }\n : brief.intent;\n break;\n case \"codeTouched\":\n out.codeTouched = brief.codeTouched;\n break;\n case \"feedback\":\n out.feedback = brief.feedback.filter((f) => keep(f.elementId));\n break;\n case \"delta\":\n if (!options.delta) notes.push(\"delta needs since: pass the revisionToken of an earlier brief.\");\n break;\n }\n }\n if (options.delta) out.delta = names.includes(\"delta\") ? options.delta : compactDelta(options.delta);\n if (filter && ids) {\n const known = new Set(brief.layers.flatMap((l) => l.changes.map((c) => c.id)));\n const unknown = [...ids].filter((id) => !known.has(id));\n if (unknown.length > 0) notes.push(`Not changed by this changeset: ${unknown.join(\", \")}.`);\n }\n out.more = [\n ...notes,\n `Only the sections asked for are returned (${names.join(\", \")})` +\n (filter ? \", for the elements the filter keeps\" : \"\") +\n `. Call without sections for the compact brief, or with detail: \"full\" for everything.`,\n ].join(\" \");\n return out;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }