diff --git a/AGENTS.md b/AGENTS.md index 974cc0fc..6420f983 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -124,8 +124,9 @@ You should not need to bump the version by hand at all: release-please owns it. ## Code Conventions -- Linting: `very_good_analysis` ^11.0.0, with `public_member_api_docs` disabled. Public classes still carry `/// {@template name}` / `/// {@macro name}` doc comments. -- The codebase uses **Dart 3.13 syntax**. Unnamed constructors are declared with the `new` keyword — `new({required this._logger})`, not `UpdateCommand({...})`. Empty class bodies use a semicolon — `class _MockLogger extends Mock implements Logger;`. +- Linting: `very_good_analysis` ^11.0.0 with no rule overrides, the same as Very Good CLI. Generated files are excluded in the `analyzer` section rather than by turning rules off. +- Every public class carries `/// {@template name}` / `/// {@endtemplate}`, and declares its constructor in the body under `/// {@macro name}` — `this;`, or `this { ... }` when it has one. The declaration is not optional: `public_member_api_docs` reads a primary constructor as an undocumented member, and the class's own doc comment does not satisfy it. +- The codebase uses **Dart 3.13 syntax**. Unnamed constructors are declared with the `new` keyword — `new({required this._logger})`, not `UpdateCommand({...})`. Empty class bodies use a semicolon — `class _MockLogger extends Mock implements Logger;`. That is for private classes such as test doubles; a public class still needs a body to hold the documented constructor above. - Commits and PR titles follow [Conventional Commits](https://www.conventionalcommits.org/). ### Testing diff --git a/packages/plugin_source/analysis_options.yaml b/packages/plugin_source/analysis_options.yaml index ce42cd44..9df80aa4 100644 --- a/packages/plugin_source/analysis_options.yaml +++ b/packages/plugin_source/analysis_options.yaml @@ -1,4 +1 @@ include: package:very_good_analysis/analysis_options.yaml -linter: - rules: - public_member_api_docs: false diff --git a/packages/plugin_source/lib/src/format/plugin_parse_exception.dart b/packages/plugin_source/lib/src/format/plugin_parse_exception.dart index 2359c922..0e7dd7d6 100644 --- a/packages/plugin_source/lib/src/format/plugin_parse_exception.dart +++ b/packages/plugin_source/lib/src/format/plugin_parse_exception.dart @@ -7,6 +7,9 @@ /// {@endtemplate} class const PluginParseException(final String path, final String message) implements Exception { + /// {@macro plugin_parse_exception} + this; + @override String toString() => 'Could not parse $path: $message'; } diff --git a/packages/plugin_source/lib/src/model/agent_asset.dart b/packages/plugin_source/lib/src/model/agent_asset.dart index a086b284..144e1154 100644 --- a/packages/plugin_source/lib/src/model/agent_asset.dart +++ b/packages/plugin_source/lib/src/model/agent_asset.dart @@ -13,4 +13,7 @@ class const AgentAsset({ /// The skills the agent expects to have loaded. required final List skills, -}); +}) { + /// {@macro agent_asset} + this; +} diff --git a/packages/plugin_source/lib/src/model/hook_asset.dart b/packages/plugin_source/lib/src/model/hook_asset.dart index 58f13c9d..ffeadb5d 100644 --- a/packages/plugin_source/lib/src/model/hook_asset.dart +++ b/packages/plugin_source/lib/src/model/hook_asset.dart @@ -14,6 +14,9 @@ class const HookCommand({ required final String command, final int? timeout, }) { + /// {@macro hook_command} + this; + /// Stands in for the plugin's root directory inside [command]. /// /// Parsers rewrite their harness's own placeholder to this one, so @@ -38,4 +41,7 @@ class const HookAsset({ required final Directory pluginRoot, final String? matcher, final String? agent, -}); +}) { + /// {@macro hook_asset} + this; +} diff --git a/packages/plugin_source/lib/src/model/mcp_server.dart b/packages/plugin_source/lib/src/model/mcp_server.dart index a1406fe9..364ef194 100644 --- a/packages/plugin_source/lib/src/model/mcp_server.dart +++ b/packages/plugin_source/lib/src/model/mcp_server.dart @@ -12,6 +12,9 @@ sealed class const MCPServer({ /// The name the plugin declares the server under, unique across plugins. required final String name, }) { + /// {@macro mcp_server} + this; + /// The server as a `.mcp.json` file declares it under its [name], leaving /// out every empty list and map rather than writing it empty. Map toJson(); @@ -32,6 +35,9 @@ final class const LocalMCPServer({ /// Environment variables set for the subprocess. final Map env = const {}, }) extends MCPServer { + /// {@macro local_mcp_server} + this; + @override Map toJson() => { 'command': command, @@ -52,6 +58,9 @@ final class const RemoteMCPServer({ /// Headers sent with each request. final Map headers = const {}, }) extends MCPServer { + /// {@macro remote_mcp_server} + this; + @override Map toJson() => { 'url': url, diff --git a/packages/plugin_source/lib/src/model/plugin_assets.dart b/packages/plugin_source/lib/src/model/plugin_assets.dart index 48443aa8..6b17761a 100644 --- a/packages/plugin_source/lib/src/model/plugin_assets.dart +++ b/packages/plugin_source/lib/src/model/plugin_assets.dart @@ -24,6 +24,9 @@ class const PluginAssets({ /// itself names in its settings. final List plugins = const [], }) { + /// {@macro plugin_assets} + this; + /// Combines what the [plugins] contribute into one [PluginAssets]. /// /// Each skill, agent and MCP server is written to a path or key derived diff --git a/packages/plugin_source/lib/src/model/skill_asset.dart b/packages/plugin_source/lib/src/model/skill_asset.dart index a293f599..63509161 100644 --- a/packages/plugin_source/lib/src/model/skill_asset.dart +++ b/packages/plugin_source/lib/src/model/skill_asset.dart @@ -17,6 +17,9 @@ class const SkillAsset({ required final Map portableFrontmatter, required final Directory directory, }) { + /// {@macro skill_asset} + this; + /// The frontmatter keys every harness understands: the open Agent Skills /// specification's key set. Parsers keep exactly these and drop everything /// else, so a harness-specific key never reaches another harness's skill. diff --git a/packages/plugin_source/lib/src/parser/claude_plugin_parser.dart b/packages/plugin_source/lib/src/parser/claude_plugin_parser.dart index 2d34601d..fd8186fa 100644 --- a/packages/plugin_source/lib/src/parser/claude_plugin_parser.dart +++ b/packages/plugin_source/lib/src/parser/claude_plugin_parser.dart @@ -15,6 +15,9 @@ import 'package:plugin_source/src/parser/plugin_parser.dart'; /// inconsistent shapes the two plugins ship and throws on anything else. /// {@endtemplate} class const ClaudePluginParser() implements PluginParser { + /// {@macro claude_plugin_parser} + this; + @override PluginAssets parse(Directory root) { final agents = _readAgents(root.childDirectory('agents')); diff --git a/packages/plugin_source/lib/src/parser/claude_schemas.dart b/packages/plugin_source/lib/src/parser/claude_schemas.dart index cf73f171..bffe00af 100644 --- a/packages/plugin_source/lib/src/parser/claude_schemas.dart +++ b/packages/plugin_source/lib/src/parser/claude_schemas.dart @@ -21,6 +21,7 @@ extension type PluginManifestJson(Map _json) { 'name': Ack.string().optional(), }).passthrough(); + /// The plugin's name as the manifest declares it, or null when unset. String? get name => _json['name'] as String?; } @@ -53,14 +54,19 @@ extension type McpServerJson(Map _json) { 'env': Ack.map(Ack.string()).optional(), }).passthrough(); + /// The endpoint a remote server is reached at, or null for a local one. String? get url => _json['url'] as String?; + /// Headers sent with each request to [url]. Map get headers => _strings(_json['headers']); + /// The executable a local server launches, or null for a remote one. String? get command => _json['command'] as String?; + /// Arguments passed to [command]. List get args => [...?_json['args'] as List?]; + /// Environment variables set for the subprocess launched by [command]. Map get env => _strings(_json['env']); static Map _strings(Object? block) => @@ -105,8 +111,10 @@ extension type HookRegistrationJson(Map _json) { 'hooks': Ack.list(HookJson._schema), }).passthrough(); + /// Which tools this registration fires for, or null for every tool. String? get matcher => _json['matcher'] as String?; + /// The hooks this registration runs. List get hooks => [ for (final hook in _json['hooks']! as List) HookJson(hook! as Map), @@ -122,10 +130,14 @@ extension type HookJson(Map _json) { 'timeout': Ack.integer().optional(), }).passthrough(); + /// The hook's kind, such as `command`. String? get type => _json['type'] as String?; + /// The shell command this hook runs, or null when it runs nothing + /// translatable. String? get command => _json['command'] as String?; + /// How long the command may run, or null for no limit. int? get timeout => _json['timeout'] as int?; } diff --git a/packages/plugin_source/lib/src/plugin_ref.dart b/packages/plugin_source/lib/src/plugin_ref.dart index 8617b453..f937c68d 100644 --- a/packages/plugin_source/lib/src/plugin_ref.dart +++ b/packages/plugin_source/lib/src/plugin_ref.dart @@ -20,6 +20,9 @@ class const PluginRef({ required final String version, required final String gitRef, }) { + /// {@macro plugin_ref} + this; + @override String toString() => '$name@$version'; } diff --git a/packages/plugin_source/lib/src/resolution.dart b/packages/plugin_source/lib/src/resolution.dart index 9548e1ca..f60ae85e 100644 --- a/packages/plugin_source/lib/src/resolution.dart +++ b/packages/plugin_source/lib/src/resolution.dart @@ -7,13 +7,19 @@ import 'package:file/file.dart'; /// plugin, collapsing a cache miss, a missing tag, an absent git binary and an /// unwritable cache root into one indistinguishable `null`. /// {@endtemplate} -sealed class const PluginResolution(); +sealed class const PluginResolution() { + /// {@macro plugin_resolution} + this; +} /// {@template resolved_plugin} /// The plugin was found on disk at [directory]. /// {@endtemplate} final class const ResolvedPlugin(final Directory directory) - extends PluginResolution; + extends PluginResolution { + /// {@macro resolved_plugin} + this; +} /// {@template unavailable_plugin} /// No source could supply the plugin. @@ -24,6 +30,9 @@ final class const ResolvedPlugin(final Directory directory) /// {@endtemplate} final class const UnavailablePlugin(final List reasons) extends PluginResolution { + /// {@macro unavailable_plugin} + this; + /// {@macro unavailable_plugin} /// /// Convenience for a single source, which has exactly one reason. diff --git a/packages/vgv_ai_cli/analysis_options.yaml b/packages/vgv_ai_cli/analysis_options.yaml index ce42cd44..511661a0 100644 --- a/packages/vgv_ai_cli/analysis_options.yaml +++ b/packages/vgv_ai_cli/analysis_options.yaml @@ -1,4 +1,5 @@ include: package:very_good_analysis/analysis_options.yaml -linter: - rules: - public_member_api_docs: false +analyzer: + exclude: + - "**/*.g.dart" + - "**/version.dart" diff --git a/packages/vgv_ai_cli/lib/src/commands/doctor/model/fix_command.dart b/packages/vgv_ai_cli/lib/src/commands/doctor/model/fix_command.dart index c6bd7753..4e023768 100644 --- a/packages/vgv_ai_cli/lib/src/commands/doctor/model/fix_command.dart +++ b/packages/vgv_ai_cli/lib/src/commands/doctor/model/fix_command.dart @@ -18,6 +18,9 @@ class const FixCommand({ /// harness with `install --plugin`. final Set plugins = const {}, }) { + /// {@macro fix_command} + this; + /// One command that does what every one of [fixes] does, or null when /// there are none, or when they install different plugins and so cannot /// be said in one command. diff --git a/packages/vgv_ai_cli/lib/src/commands/doctor/model/report_section.dart b/packages/vgv_ai_cli/lib/src/commands/doctor/model/report_section.dart index 95741356..eb008b54 100644 --- a/packages/vgv_ai_cli/lib/src/commands/doctor/model/report_section.dart +++ b/packages/vgv_ai_cli/lib/src/commands/doctor/model/report_section.dart @@ -21,6 +21,9 @@ class const ReportSection({ /// fixes them all. final Set? plugins = const {}, }) { + /// {@macro report_section} + this; + /// Whether anything was found that is wrong. bool get hasProblems => findings.any((finding) => finding.isProblem); diff --git a/packages/vgv_ai_cli/lib/src/commands/update_command.dart b/packages/vgv_ai_cli/lib/src/commands/update_command.dart index 0882e674..236d439f 100644 --- a/packages/vgv_ai_cli/lib/src/commands/update_command.dart +++ b/packages/vgv_ai_cli/lib/src/commands/update_command.dart @@ -11,11 +11,16 @@ import 'package:vgv_ai_cli/src/version.dart'; /// {@endtemplate} class UpdateCommand({required final Logger _logger, PubUpdater? pubUpdater}) extends Command { + /// {@macro update_command} + this; + final PubUpdater _pubUpdater = pubUpdater ?? PubUpdater(); @override String get description => 'Update vgv_ai to the latest version.'; + /// The name the CLI registers `update` under, so other code can name it + /// without constructing one. The runner skips its update check for it. static const String commandName = 'update'; @override diff --git a/packages/vgv_ai_cli/lib/src/detect/harness_detector.dart b/packages/vgv_ai_cli/lib/src/detect/harness_detector.dart index efeed1d3..11291936 100644 --- a/packages/vgv_ai_cli/lib/src/detect/harness_detector.dart +++ b/packages/vgv_ai_cli/lib/src/detect/harness_detector.dart @@ -31,6 +31,9 @@ class const HarnessDetector({ final RunProcess _runProcess = Process.run, final Duration _timeout = const Duration(seconds: 5), }) { + /// {@macro harness_detector} + this; + /// Matches the first `x.y.z` in a version banner. static final _versionPattern = RegExp(r'\d+\.\d+\.\d+'); diff --git a/packages/vgv_ai_cli/lib/src/document/markdown_text.dart b/packages/vgv_ai_cli/lib/src/document/markdown_text.dart index 18c2c842..fd77b4d0 100644 --- a/packages/vgv_ai_cli/lib/src/document/markdown_text.dart +++ b/packages/vgv_ai_cli/lib/src/document/markdown_text.dart @@ -18,6 +18,9 @@ class const MarkdownText( /// The file's text, as written. final String source, ) { + /// {@macro markdown_text} + this; + /// Opens the section named [name]. static String sectionStart(String name) => ''; diff --git a/packages/vgv_ai_cli/lib/src/document/model/normalized_text.dart b/packages/vgv_ai_cli/lib/src/document/model/normalized_text.dart index e2695fdc..d63aec2d 100644 --- a/packages/vgv_ai_cli/lib/src/document/model/normalized_text.dart +++ b/packages/vgv_ai_cli/lib/src/document/model/normalized_text.dart @@ -9,6 +9,9 @@ class const NormalizedText({ /// The line ending of the text [text] was normalized from. required final String ending, }) { + /// {@macro normalized_text} + this; + /// {@macro normalized_text} /// /// Normalizes [original], so an edit of it can be turned back into diff --git a/packages/vgv_ai_cli/lib/src/document/model/section_span.dart b/packages/vgv_ai_cli/lib/src/document/model/section_span.dart index 0b2e6279..d567bed2 100644 --- a/packages/vgv_ai_cli/lib/src/document/model/section_span.dart +++ b/packages/vgv_ai_cli/lib/src/document/model/section_span.dart @@ -8,4 +8,7 @@ class const SectionSpan({ /// The offset just past the section's end marker. required final int end, -}); +}) { + /// {@macro section_span} + this; +} diff --git a/packages/vgv_ai_cli/lib/src/document/model/toml_region.dart b/packages/vgv_ai_cli/lib/src/document/model/toml_region.dart index a2753315..07abae2f 100644 --- a/packages/vgv_ai_cli/lib/src/document/model/toml_region.dart +++ b/packages/vgv_ai_cli/lib/src/document/model/toml_region.dart @@ -11,6 +11,9 @@ class const TomlRegion({ /// Every statement after [header] up to the next header. required final List body, }) { + /// {@macro toml_region} + this; + /// The statement that sets [key] in this table, if any. TomlStatement? statementOf(String key) => body.firstWhereOrNull( (s) => s.declares(TomlStatementKind.keyValue, [key]), diff --git a/packages/vgv_ai_cli/lib/src/document/model/toml_statement.dart b/packages/vgv_ai_cli/lib/src/document/model/toml_statement.dart index bb2e1fb5..af2a8399 100644 --- a/packages/vgv_ai_cli/lib/src/document/model/toml_statement.dart +++ b/packages/vgv_ai_cli/lib/src/document/model/toml_statement.dart @@ -41,6 +41,9 @@ class const TomlStatement({ /// inside a value that spans several lines. required final bool hasInnerComment, }) { + /// {@macro toml_statement} + this; + /// Whether the statement opens a table or an array of tables. bool get isHeader => kind == TomlStatementKind.table || kind == TomlStatementKind.arrayTable; diff --git a/packages/vgv_ai_cli/lib/src/document/toml_scanner.dart b/packages/vgv_ai_cli/lib/src/document/toml_scanner.dart index 422b9918..d2312ced 100644 --- a/packages/vgv_ai_cli/lib/src/document/toml_scanner.dart +++ b/packages/vgv_ai_cli/lib/src/document/toml_scanner.dart @@ -6,6 +6,9 @@ import 'package:vgv_ai_cli/src/document/model/model.dart'; /// or array is one statement however many lines it spans. /// {@endtemplate} class TomlScanner(final List lines) { + /// {@macro toml_scanner} + this; + /// The region of [lines] that holds [table], or null when the document /// has no `[table]` header for it. An empty [table] is the document's top /// level. diff --git a/packages/vgv_ai_cli/lib/src/document/toml_text.dart b/packages/vgv_ai_cli/lib/src/document/toml_text.dart index cfcf7c64..53f62297 100644 --- a/packages/vgv_ai_cli/lib/src/document/toml_text.dart +++ b/packages/vgv_ai_cli/lib/src/document/toml_text.dart @@ -18,6 +18,9 @@ class const TomlText( /// Compares what an edit decodes to with what it meant to produce. final DocumentCodec codec = const DocumentCodec(), }) { + /// {@macro toml_text} + this; + /// {@macro toml_text} /// /// [map] encoded as canonical TOML with a trailing newline. diff --git a/packages/vgv_ai_cli/lib/src/manifest/manifest_auditor.dart b/packages/vgv_ai_cli/lib/src/manifest/manifest_auditor.dart index 49da2137..1c87f823 100644 --- a/packages/vgv_ai_cli/lib/src/manifest/manifest_auditor.dart +++ b/packages/vgv_ai_cli/lib/src/manifest/manifest_auditor.dart @@ -18,6 +18,9 @@ class const ManifestAuditor({ /// Decodes and compares what is on disk with what VGV recorded. final DocumentCodec codec = const DocumentCodec(), }) { + /// {@macro manifest_auditor} + this; + /// Checks that what [entry] records is still on disk, as VGV wrote it. /// /// Never writes, and reports a file it cannot read rather than throwing. diff --git a/packages/vgv_ai_cli/lib/src/manifest/manifest_store.dart b/packages/vgv_ai_cli/lib/src/manifest/manifest_store.dart index 81dadcbe..600a2944 100644 --- a/packages/vgv_ai_cli/lib/src/manifest/manifest_store.dart +++ b/packages/vgv_ai_cli/lib/src/manifest/manifest_store.dart @@ -14,6 +14,9 @@ class const ManifestStore( /// The home directory the manifest records, and is kept under. final Directory home, ) { + /// {@macro manifest_store} + this; + /// Why a manifest this version cannot read is left as it is. static const unreadable = 'the manifest was written by a newer version of vgv_ai, so it was ' diff --git a/packages/vgv_ai_cli/lib/src/manifest/model/reversal.dart b/packages/vgv_ai_cli/lib/src/manifest/model/reversal.dart index 0527f3f0..0b50290f 100644 --- a/packages/vgv_ai_cli/lib/src/manifest/model/reversal.dart +++ b/packages/vgv_ai_cli/lib/src/manifest/model/reversal.dart @@ -13,4 +13,7 @@ class const Reversal({ /// longer hold anything of VGV's. On a dry run it records what would /// have been taken back. required final InstallManifest manifest, -}); +}) { + /// {@macro reversal} + this; +} diff --git a/packages/vgv_ai_cli/lib/src/manifest/model/reverse_decision.dart b/packages/vgv_ai_cli/lib/src/manifest/model/reverse_decision.dart index b529afc2..18e61d82 100644 --- a/packages/vgv_ai_cli/lib/src/manifest/model/reverse_decision.dart +++ b/packages/vgv_ai_cli/lib/src/manifest/model/reverse_decision.dart @@ -20,4 +20,7 @@ class const ReverseDecision({ /// Only a file that is still VGV's is deleted through a link, so forcing /// cannot reach a file the user put at the other end. final bool throughLink = true, -}); +}) { + /// {@macro reverse_decision} + this; +} diff --git a/packages/vgv_ai_cli/lib/src/model/document_codec.dart b/packages/vgv_ai_cli/lib/src/model/document_codec.dart index 926dceaf..a977f396 100644 --- a/packages/vgv_ai_cli/lib/src/model/document_codec.dart +++ b/packages/vgv_ai_cli/lib/src/model/document_codec.dart @@ -13,6 +13,9 @@ class const DocumentCodec({ /// Compares decoded documents, and the values inside them, structurally. final DeepCollectionEquality deepEquality = const DeepCollectionEquality(), }) { + /// {@macro document_codec} + this; + /// The keys whose list is an argv: a command and its arguments. static const Set _argvKeys = {'command', 'args'}; diff --git a/packages/vgv_ai_cli/lib/src/model/drift.dart b/packages/vgv_ai_cli/lib/src/model/drift.dart index 480b296f..19c6abac 100644 --- a/packages/vgv_ai_cli/lib/src/model/drift.dart +++ b/packages/vgv_ai_cli/lib/src/model/drift.dart @@ -9,6 +9,9 @@ class const Drift({ /// The key paths, or section names, that hold a different value now. required final List drifted, }) { + /// {@macro drift} + this; + /// Whether nothing is missing and nothing has drifted. bool get isEmpty => missing.isEmpty && drifted.isEmpty; } diff --git a/packages/vgv_ai_cli/lib/src/model/install_manifest.dart b/packages/vgv_ai_cli/lib/src/model/install_manifest.dart index 2ad537cc..985f4c81 100644 --- a/packages/vgv_ai_cli/lib/src/model/install_manifest.dart +++ b/packages/vgv_ai_cli/lib/src/model/install_manifest.dart @@ -18,6 +18,9 @@ sealed class const ManifestEntry({ /// The harness the file configures, or null for the shared skills. required final Harness? owner, }) extends Equatable { + /// {@macro manifest_entry} + this; + /// How [path] was written. ChangeMode get mode; @@ -105,6 +108,9 @@ final class const OwnedFile({ /// The SHA-256 of what VGV last wrote. required final String sha256, }) extends ManifestEntry { + /// {@macro owned_file} + this; + /// {@macro owned_file} /// /// Records [bytes] as what VGV wrote to [path]. @@ -137,6 +143,9 @@ final class const MergedDocument({ /// The part of the document that is VGV's. required final Contribution contribution, }) extends ManifestEntry { + /// {@macro merged_document} + this; + @override ChangeMode get mode => format.mode; @@ -154,6 +163,9 @@ final class const MarkdownSections({ /// Each section's name to the SHA-256 of the body VGV last wrote there. required final Map sections, }) extends ManifestEntry { + /// {@macro markdown_sections} + this; + /// This entry with [body] recorded as what VGV wrote into section [name]. MarkdownSections recording(String name, String body) => MarkdownSections( path: path, diff --git a/packages/vgv_ai_cli/lib/src/model/subtraction.dart b/packages/vgv_ai_cli/lib/src/model/subtraction.dart index aafa8be5..4b767b19 100644 --- a/packages/vgv_ai_cli/lib/src/model/subtraction.dart +++ b/packages/vgv_ai_cli/lib/src/model/subtraction.dart @@ -7,4 +7,7 @@ class const Subtraction({ /// Whether any of what VGV wrote was kept, because the user changed it. required final bool kept, -}); +}) { + /// {@macro subtraction} + this; +} diff --git a/packages/vgv_ai_cli/lib/src/reconcile/model/applied_change.dart b/packages/vgv_ai_cli/lib/src/reconcile/model/applied_change.dart index 36e88522..218a7941 100644 --- a/packages/vgv_ai_cli/lib/src/reconcile/model/applied_change.dart +++ b/packages/vgv_ai_cli/lib/src/reconcile/model/applied_change.dart @@ -43,4 +43,7 @@ class AppliedChange({ /// Why the change [ChangeOutcome.failed], and null otherwise. final String? error, -}); +}) { + /// {@macro applied_change} + this; +} diff --git a/packages/vgv_ai_cli/lib/src/reconcile/model/deep_merge.dart b/packages/vgv_ai_cli/lib/src/reconcile/model/deep_merge.dart index d349d10d..98a143e2 100644 --- a/packages/vgv_ai_cli/lib/src/reconcile/model/deep_merge.dart +++ b/packages/vgv_ai_cli/lib/src/reconcile/model/deep_merge.dart @@ -11,4 +11,7 @@ class const DeepMerge({ /// Whether the merge kept a value the user set in place of VGV's. required final bool declined, -}); +}) { + /// {@macro deep_merge} + this; +} diff --git a/packages/vgv_ai_cli/lib/src/reconcile/model/reconciliation.dart b/packages/vgv_ai_cli/lib/src/reconcile/model/reconciliation.dart index bbf0656a..4f5aa0c3 100644 --- a/packages/vgv_ai_cli/lib/src/reconcile/model/reconciliation.dart +++ b/packages/vgv_ai_cli/lib/src/reconcile/model/reconciliation.dart @@ -12,4 +12,7 @@ class const Reconciliation({ /// The manifest the changes were applied against, updated with what they /// wrote. On a dry run it records what they would have written. required final InstallManifest manifest, -}); +}) { + /// {@macro reconciliation} + this; +} diff --git a/packages/vgv_ai_cli/lib/src/reconcile/model/text_merge.dart b/packages/vgv_ai_cli/lib/src/reconcile/model/text_merge.dart index 6bd06ac9..288e9a8c 100644 --- a/packages/vgv_ai_cli/lib/src/reconcile/model/text_merge.dart +++ b/packages/vgv_ai_cli/lib/src/reconcile/model/text_merge.dart @@ -15,4 +15,7 @@ class const TextMerge({ /// What of [contents] VGV contributed, and null when nothing could be /// parsed. final Contribution? contribution, -}); +}) { + /// {@macro text_merge} + this; +} diff --git a/packages/vgv_ai_cli/lib/src/reconcile/model/write_decision.dart b/packages/vgv_ai_cli/lib/src/reconcile/model/write_decision.dart index b56e739f..d92950da 100644 --- a/packages/vgv_ai_cli/lib/src/reconcile/model/write_decision.dart +++ b/packages/vgv_ai_cli/lib/src/reconcile/model/write_decision.dart @@ -16,4 +16,7 @@ class const WriteDecision({ /// entry as it is, and a [MergedDocument] with an empty contribution drops /// it, since nothing in the file is VGV's. final ManifestEntry? entry, -}); +}) { + /// {@macro write_decision} + this; +} diff --git a/packages/vgv_ai_cli/lib/src/reconcile/reconciler.dart b/packages/vgv_ai_cli/lib/src/reconcile/reconciler.dart index cd4bc0bc..2c977e2d 100644 --- a/packages/vgv_ai_cli/lib/src/reconcile/reconciler.dart +++ b/packages/vgv_ai_cli/lib/src/reconcile/reconciler.dart @@ -18,6 +18,9 @@ class const Reconciler({ /// Decodes and compares what is on disk with what VGV writes. final DocumentCodec codec = const DocumentCodec(), }) { + /// {@macro reconciler} + this; + /// Applies every one of [changes] against [manifest], and returns what /// each did and the manifest updated with what they wrote. /// diff --git a/packages/vgv_ai_cli/lib/src/resolve/model/asset_resolution.dart b/packages/vgv_ai_cli/lib/src/resolve/model/asset_resolution.dart index 285a071d..07c0e697 100644 --- a/packages/vgv_ai_cli/lib/src/resolve/model/asset_resolution.dart +++ b/packages/vgv_ai_cli/lib/src/resolve/model/asset_resolution.dart @@ -3,7 +3,10 @@ import 'package:plugin_source/plugin_source.dart'; /// {@template asset_resolution} /// What resolving and parsing every pinned plugin produced. /// {@endtemplate} -sealed class const AssetResolution(); +sealed class const AssetResolution() { + /// {@macro asset_resolution} + this; +} /// {@template resolved_assets} /// Every plugin resolved and parsed, merged into [assets], which names them. @@ -11,7 +14,10 @@ sealed class const AssetResolution(); final class const ResolvedAssets({ /// What the plugins contribute, merged. required final PluginAssets assets, -}) extends AssetResolution; +}) extends AssetResolution { + /// {@macro resolved_assets} + this; +} /// {@template unavailable_assets} /// No source could supply [plugin], for each of [reasons]. @@ -22,7 +28,10 @@ final class const UnavailableAssets( /// Why, one reason per source tried, in the order they were tried. final List reasons, -) extends AssetResolution; +) extends AssetResolution { + /// {@macro unavailable_assets} + this; +} /// {@template unparseable_assets} /// [plugin] resolved, but could not be parsed because of [error]. @@ -33,4 +42,7 @@ final class const UnparseableAssets( /// Why it could not be parsed. final PluginParseException error, -) extends AssetResolution; +) extends AssetResolution { + /// {@macro unparseable_assets} + this; +} diff --git a/packages/vgv_ai_cli/lib/src/translate/agents_brick.dart b/packages/vgv_ai_cli/lib/src/translate/agents_brick.dart index 465e609c..5f133c6a 100644 --- a/packages/vgv_ai_cli/lib/src/translate/agents_brick.dart +++ b/packages/vgv_ai_cli/lib/src/translate/agents_brick.dart @@ -13,6 +13,9 @@ import 'package:vgv_ai_cli/src/templates/templates.dart'; /// back. The brick has no hooks, so the bundle's files are all it needs. /// {@endtemplate} class const AgentsBrick() { + /// {@macro agents_brick} + this; + /// Renders every file [vars] select, keyed by its path in the brick. Future> render(Map vars) async { final generator = MasonGenerator( diff --git a/packages/vgv_ai_cli/lib/src/translate/agents_vars.dart b/packages/vgv_ai_cli/lib/src/translate/agents_vars.dart index d34422d6..851fa9c7 100644 --- a/packages/vgv_ai_cli/lib/src/translate/agents_vars.dart +++ b/packages/vgv_ai_cli/lib/src/translate/agents_vars.dart @@ -21,6 +21,9 @@ class const AgentsVars({ /// The hooks fragment of each harness that has one. final Map hooks = const {}, }) { + /// {@macro agents_vars} + this; + /// The vars, keyed as the brick reads them. /// /// `agents` and `commands` are left out when empty: the brick loops a file diff --git a/packages/vgv_ai_cli/lib/src/translate/harness_translator.dart b/packages/vgv_ai_cli/lib/src/translate/harness_translator.dart index 1f697863..31aad5ac 100644 --- a/packages/vgv_ai_cli/lib/src/translate/harness_translator.dart +++ b/packages/vgv_ai_cli/lib/src/translate/harness_translator.dart @@ -20,6 +20,9 @@ import 'package:vgv_ai_cli/src/translate/model/model.dart'; /// See `Harness` for every step of adding a harness. /// {@endtemplate} abstract class const HarnessTranslator() { + /// {@macro harness_translator} + this; + /// Which harness this configures. Harness get harness; diff --git a/packages/vgv_ai_cli/lib/src/translate/hook_scripts.dart b/packages/vgv_ai_cli/lib/src/translate/hook_scripts.dart index 5ddf74f0..b8b445e4 100644 --- a/packages/vgv_ai_cli/lib/src/translate/hook_scripts.dart +++ b/packages/vgv_ai_cli/lib/src/translate/hook_scripts.dart @@ -25,6 +25,9 @@ class const HookScripts({ /// What goes below the shebang of a script a hook runs. required final String preamble, }) { + /// {@macro hook_scripts} + this; + /// The folder a Claude Code plugin keeps its hooks in. static const hooksFolder = 'hooks'; @@ -307,7 +310,10 @@ class const HookScripts({ class const HookCopy({ required final List files, required final List notes, -}); +}) { + /// {@macro hook_copy} + this; +} /// One file of a plugin's hooks folder as the copy holds it; [hook] is any /// hook of the plugin, [relative] its path from the plugin root, and diff --git a/packages/vgv_ai_cli/lib/src/translate/model/harness_fields.dart b/packages/vgv_ai_cli/lib/src/translate/model/harness_fields.dart index c6d6f9cd..76f223f8 100644 --- a/packages/vgv_ai_cli/lib/src/translate/model/harness_fields.dart +++ b/packages/vgv_ai_cli/lib/src/translate/model/harness_fields.dart @@ -18,6 +18,9 @@ class const HarnessFields({ /// frontmatter rather than through the shared pointer. final String? instructions, }) extends Equatable { + /// {@macro harness_fields} + this; + /// The fields keyed as the brick reads them, leaving out every null one. Map toMap() => { 'frontmatter': ?frontmatter, diff --git a/packages/vgv_ai_cli/lib/src/translate/model/hook_translation.dart b/packages/vgv_ai_cli/lib/src/translate/model/hook_translation.dart index 0ec2f2af..81e5c8ad 100644 --- a/packages/vgv_ai_cli/lib/src/translate/model/hook_translation.dart +++ b/packages/vgv_ai_cli/lib/src/translate/model/hook_translation.dart @@ -16,4 +16,7 @@ class const HookTranslation({ /// What could not be translated as intended, and what the user must do. final List notes = const [], -}); +}) { + /// {@macro hook_translation} + this; +} diff --git a/packages/vgv_ai_cli/lib/src/translate/model/translation_note.dart b/packages/vgv_ai_cli/lib/src/translate/model/translation_note.dart index b6c7512f..63564962 100644 --- a/packages/vgv_ai_cli/lib/src/translate/model/translation_note.dart +++ b/packages/vgv_ai_cli/lib/src/translate/model/translation_note.dart @@ -13,6 +13,9 @@ class const TranslationNote({ /// Why. required final String reason, }) extends Equatable { + /// {@macro translation_note} + this; + @override List get props => [subject, reason]; diff --git a/packages/vgv_ai_cli/lib/src/translate/model/translation_plan.dart b/packages/vgv_ai_cli/lib/src/translate/model/translation_plan.dart index 3c7fb972..d513905b 100644 --- a/packages/vgv_ai_cli/lib/src/translate/model/translation_plan.dart +++ b/packages/vgv_ai_cli/lib/src/translate/model/translation_plan.dart @@ -12,6 +12,9 @@ class const TranslationPlan({ /// Each selected harness's own result, in reporting order. final Map harnesses = const {}, }) { + /// {@macro translation_plan} + this; + /// Every planned change, the shared ones first. List get changes => [ ...?shared?.changes, diff --git a/packages/vgv_ai_cli/lib/src/translate/model/translation_result.dart b/packages/vgv_ai_cli/lib/src/translate/model/translation_result.dart index 405c9fed..9dff0eab 100644 --- a/packages/vgv_ai_cli/lib/src/translate/model/translation_result.dart +++ b/packages/vgv_ai_cli/lib/src/translate/model/translation_result.dart @@ -13,4 +13,7 @@ class const TranslationResult({ /// Everything this harness could not express. final List notes = const [], -}); +}) { + /// {@macro translation_result} + this; +} diff --git a/packages/vgv_ai_cli/lib/src/translate/translator.dart b/packages/vgv_ai_cli/lib/src/translate/translator.dart index 30fc69d9..a0b8f90c 100644 --- a/packages/vgv_ai_cli/lib/src/translate/translator.dart +++ b/packages/vgv_ai_cli/lib/src/translate/translator.dart @@ -18,6 +18,9 @@ class const Translator({ final AgentsBrick brick = const AgentsBrick(), final SkillsTranslator skills = const SkillsTranslator(), }) { + /// {@macro translator} + this; + /// The changes and notes for each of [selected], plus the shared skills /// when any of them reads them. Future translate({ diff --git a/packages/vgv_ai_cli/lib/src/translate/translators/antigravity_translator.dart b/packages/vgv_ai_cli/lib/src/translate/translators/antigravity_translator.dart index c1218848..e3970e48 100644 --- a/packages/vgv_ai_cli/lib/src/translate/translators/antigravity_translator.dart +++ b/packages/vgv_ai_cli/lib/src/translate/translators/antigravity_translator.dart @@ -20,6 +20,9 @@ import 'package:vgv_ai_cli/src/translate/translate.dart'; /// See https://antigravity.google/docs/plugins/. /// {@endtemplate} class const AntigravityTranslator() extends HarnessTranslator { + /// {@macro antigravity_translator} + this; + /// The plugin everything is installed as. static const plugin = 'vgv'; diff --git a/packages/vgv_ai_cli/lib/src/translate/translators/claude_translator.dart b/packages/vgv_ai_cli/lib/src/translate/translators/claude_translator.dart index 1dae2f81..975b1e84 100644 --- a/packages/vgv_ai_cli/lib/src/translate/translators/claude_translator.dart +++ b/packages/vgv_ai_cli/lib/src/translate/translators/claude_translator.dart @@ -14,6 +14,9 @@ import 'package:vgv_ai_cli/src/translate/translate.dart'; /// does, and the two copies would drift apart on the next plugin release. /// {@endtemplate} class const ClaudeTranslator() extends HarnessTranslator { + /// {@macro claude_translator} + this; + @override Harness get harness => Harness.claude; diff --git a/packages/vgv_ai_cli/lib/src/translate/translators/codex_translator.dart b/packages/vgv_ai_cli/lib/src/translate/translators/codex_translator.dart index 32f6528f..8cca22c7 100644 --- a/packages/vgv_ai_cli/lib/src/translate/translators/codex_translator.dart +++ b/packages/vgv_ai_cli/lib/src/translate/translators/codex_translator.dart @@ -12,6 +12,9 @@ import 'package:vgv_ai_cli/src/translate/translate.dart'; /// Codex reads `~/.agents/skills` natively. Its agents are TOML. /// {@endtemplate} class const CodexTranslator() extends HarnessTranslator { + /// {@macro codex_translator} + this; + @override Harness get harness => Harness.codex; diff --git a/packages/vgv_ai_cli/lib/src/translate/translators/copilot_translator.dart b/packages/vgv_ai_cli/lib/src/translate/translators/copilot_translator.dart index 04315508..aa104195 100644 --- a/packages/vgv_ai_cli/lib/src/translate/translators/copilot_translator.dart +++ b/packages/vgv_ai_cli/lib/src/translate/translators/copilot_translator.dart @@ -13,6 +13,9 @@ import 'package:vgv_ai_cli/src/translate/translate.dart'; /// it needs no commands. /// {@endtemplate} class const CopilotTranslator() extends HarnessTranslator { + /// {@macro copilot_translator} + this; + @override Harness get harness => Harness.copilot; diff --git a/packages/vgv_ai_cli/lib/src/translate/translators/gemini_translator.dart b/packages/vgv_ai_cli/lib/src/translate/translators/gemini_translator.dart index 7f3ab911..0e47506d 100644 --- a/packages/vgv_ai_cli/lib/src/translate/translators/gemini_translator.dart +++ b/packages/vgv_ai_cli/lib/src/translate/translators/gemini_translator.dart @@ -14,6 +14,9 @@ import 'package:vgv_ai_cli/src/translate/translate.dart'; /// argument placeholder. /// {@endtemplate} class const GeminiTranslator() extends HarnessTranslator { + /// {@macro gemini_translator} + this; + /// Gemini's argument placeholder. Codex, OpenCode and Pi all keep Claude's /// `$ARGUMENTS`, so this substitution belongs to Gemini alone. static const _arguments = r'$ARGUMENTS'; diff --git a/packages/vgv_ai_cli/lib/src/translate/translators/opencode_translator.dart b/packages/vgv_ai_cli/lib/src/translate/translators/opencode_translator.dart index 0981110f..09638bf2 100644 --- a/packages/vgv_ai_cli/lib/src/translate/translators/opencode_translator.dart +++ b/packages/vgv_ai_cli/lib/src/translate/translators/opencode_translator.dart @@ -11,6 +11,9 @@ import 'package:vgv_ai_cli/src/translate/translate.dart'; /// `mode: subagent` so it is delegated to rather than driven directly. /// {@endtemplate} class const OpenCodeTranslator() extends HarnessTranslator { + /// {@macro opencode_translator} + this; + @override Harness get harness => Harness.opencode; diff --git a/packages/vgv_ai_cli/lib/src/translate/translators/pi_translator.dart b/packages/vgv_ai_cli/lib/src/translate/translators/pi_translator.dart index cc417c24..b9b2bab9 100644 --- a/packages/vgv_ai_cli/lib/src/translate/translators/pi_translator.dart +++ b/packages/vgv_ai_cli/lib/src/translate/translators/pi_translator.dart @@ -11,6 +11,9 @@ import 'package:vgv_ai_cli/src/translate/translate.dart'; /// written, and nothing that would imitate the rest. /// {@endtemplate} class const PiTranslator() extends HarnessTranslator { + /// {@macro pi_translator} + this; + @override Harness get harness => Harness.pi; diff --git a/packages/vgv_ai_cli/lib/src/translate/translators/skills_translator.dart b/packages/vgv_ai_cli/lib/src/translate/translators/skills_translator.dart index 28293071..fc266b68 100644 --- a/packages/vgv_ai_cli/lib/src/translate/translators/skills_translator.dart +++ b/packages/vgv_ai_cli/lib/src/translate/translators/skills_translator.dart @@ -14,6 +14,9 @@ import 'package:vgv_ai_cli/src/translate/translate.dart'; /// harness. /// {@endtemplate} class const SkillsTranslator() { + /// {@macro skills_translator} + this; + /// Plans every skill under [root], plus the files each one ships alongside /// its `SKILL.md`, owned by [harness] or shared when null. TranslationResult translate({