// SPDX-License-Identifier: LGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® export module Crafter.Build:Agents; import std; namespace fs = std::filesystem; // Dependency notes. A crafter-build project ships an AGENTS.consumer.md beside // its project.cpp, written for the agents of the projects that depend on it, // and crafter-build keeps those files in each consumer's AGENTS.md. Without // that, an agent working on a consumer knows nothing about the libraries it // builds against unless someone copied the knowledge in by hand, and a copy // goes stale the moment the library changes. export namespace Crafter { struct Configuration; // Which dependencies' notes a project's AGENTS.md carries: the value of // Configuration::dependencyNotes. enum class DependencyNotes { // The projects this one builds against, and the projects those build // against, all the way down. Transitive, // Only the projects this one's own configurations, tests and // `#pragma crafter dependency` lines name. Direct, // None. A section an earlier build wrote is removed. None, }; // One dependency's AGENTS.consumer.md, ready to merge. struct DependencyNote { // Where it came from, written into the comment above it: the git URL, // or for a local project the file's path relative to the consumer. std::string source; // The file's content, without leading blank lines or trailing // whitespace. std::string text; }; // `agentsMd` with its dependency notes section holding `notes`, in order. // A file without a section gets one appended after a blank line; one // with a section has it rewritten in place, wherever it was moved to; // and empty `notes` remove it together with that blank line, so a file // that only ever gained the section gets its own bytes back. // // The two marker lines count only as whole lines outside fenced code // blocks, which is what lets a document quote them. Throws, naming the // line, when the markers are unbalanced or the document ends inside a // code block: guessing which part of someone's file is generated would // overwrite their text, and missing the section would add a second one // on every build. Also throws for a note that would break the section // when it is read back: one with a marker line of its own, or a code // block it never closes. std::string MergeDependencyNotes(std::string_view agentsMd, std::span notes); // Refresh the dependency notes section of the AGENTS.md beside // `projectFile` from the AGENTS.consumer.md of every dependency // project.dependencyNotes selects. `project` is the Configuration that // file returned, after LoadProject: loading is what found the // dependencies. Does nothing when there is no AGENTS.md, which is never // created. Writes only when the text changes, and returns whether it // did. An unreadable file, a failed write, malformed markers and a // dependency's unusable notes are warnings: the notes never fail a build. bool UpdateAgentsMd(const Configuration& project, const fs::path& projectFile); }