Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 0 additions & 3 deletions packages/plugin_source/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
include: package:very_good_analysis/analysis_options.yaml
linter:
rules:
public_member_api_docs: false
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}
5 changes: 4 additions & 1 deletion packages/plugin_source/lib/src/model/agent_asset.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ class const AgentAsset({

/// The skills the agent expects to have loaded.
required final List<String> skills,
});
}) {
/// {@macro agent_asset}
this;
}
8 changes: 7 additions & 1 deletion packages/plugin_source/lib/src/model/hook_asset.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -38,4 +41,7 @@ class const HookAsset({
required final Directory pluginRoot,
final String? matcher,
final String? agent,
});
}) {
/// {@macro hook_asset}
this;
}
9 changes: 9 additions & 0 deletions packages/plugin_source/lib/src/model/mcp_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Object> toJson();
Expand All @@ -32,6 +35,9 @@ final class const LocalMCPServer({
/// Environment variables set for the subprocess.
final Map<String, String> env = const {},
}) extends MCPServer {
/// {@macro local_mcp_server}
this;

@override
Map<String, Object> toJson() => {
'command': command,
Expand All @@ -52,6 +58,9 @@ final class const RemoteMCPServer({
/// Headers sent with each request.
final Map<String, String> headers = const {},
}) extends MCPServer {
/// {@macro remote_mcp_server}
this;

@override
Map<String, Object> toJson() => {
'url': url,
Expand Down
3 changes: 3 additions & 0 deletions packages/plugin_source/lib/src/model/plugin_assets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class const PluginAssets({
/// itself names in its settings.
final List<PluginRef> 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
Expand Down
3 changes: 3 additions & 0 deletions packages/plugin_source/lib/src/model/skill_asset.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ class const SkillAsset({
required final Map<String, Object?> 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'));
Expand Down
12 changes: 12 additions & 0 deletions packages/plugin_source/lib/src/parser/claude_schemas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ extension type PluginManifestJson(Map<String, Object?> _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?;
}

Expand Down Expand Up @@ -53,14 +54,19 @@ extension type McpServerJson(Map<String, Object?> _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<String, String> 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<String> get args => [...?_json['args'] as List<String>?];

/// Environment variables set for the subprocess launched by [command].
Map<String, String> get env => _strings(_json['env']);

static Map<String, String> _strings(Object? block) =>
Expand Down Expand Up @@ -105,8 +111,10 @@ extension type HookRegistrationJson(Map<String, Object?> _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<HookJson> get hooks => [
for (final hook in _json['hooks']! as List<Object?>)
HookJson(hook! as Map<String, Object?>),
Expand All @@ -122,10 +130,14 @@ extension type HookJson(Map<String, Object?> _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?;
}

Expand Down
3 changes: 3 additions & 0 deletions packages/plugin_source/lib/src/plugin_ref.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class const PluginRef({
required final String version,
required final String gitRef,
}) {
/// {@macro plugin_ref}
this;

@override
String toString() => '$name@$version';
}
Expand Down
13 changes: 11 additions & 2 deletions packages/plugin_source/lib/src/resolution.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -24,6 +30,9 @@ final class const ResolvedPlugin(final Directory directory)
/// {@endtemplate}
final class const UnavailablePlugin(final List<String> reasons)
extends PluginResolution {
/// {@macro unavailable_plugin}
this;

/// {@macro unavailable_plugin}
///
/// Convenience for a single source, which has exactly one reason.
Expand Down
7 changes: 4 additions & 3 deletions packages/vgv_ai_cli/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class const FixCommand({
/// harness with `install --plugin`.
final Set<String> 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class const ReportSection({
/// fixes them all.
final Set<String>? plugins = const {},
}) {
/// {@macro report_section}
this;

/// Whether anything was found that is wrong.
bool get hasProblems => findings.any((finding) => finding.isProblem);

Expand Down
5 changes: 5 additions & 0 deletions packages/vgv_ai_cli/lib/src/commands/update_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ import 'package:vgv_ai_cli/src/version.dart';
/// {@endtemplate}
class UpdateCommand({required final Logger _logger, PubUpdater? pubUpdater})
extends Command<int> {
/// {@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
Expand Down
3 changes: 3 additions & 0 deletions packages/vgv_ai_cli/lib/src/detect/harness_detector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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+');

Expand Down
3 changes: 3 additions & 0 deletions packages/vgv_ai_cli/lib/src/document/markdown_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) => '<!-- vgv-ai-cli:$name -->';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ class const SectionSpan({

/// The offset just past the section's end marker.
required final int end,
});
}) {
/// {@macro section_span}
this;
}
3 changes: 3 additions & 0 deletions packages/vgv_ai_cli/lib/src/document/model/toml_region.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ class const TomlRegion({
/// Every statement after [header] up to the next header.
required final List<TomlStatement> 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]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions packages/vgv_ai_cli/lib/src/document/toml_scanner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> 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.
Expand Down
3 changes: 3 additions & 0 deletions packages/vgv_ai_cli/lib/src/document/toml_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions packages/vgv_ai_cli/lib/src/manifest/manifest_auditor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions packages/vgv_ai_cli/lib/src/manifest/manifest_store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 '
Expand Down
5 changes: 4 additions & 1 deletion packages/vgv_ai_cli/lib/src/manifest/model/reversal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
3 changes: 3 additions & 0 deletions packages/vgv_ai_cli/lib/src/model/document_codec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> _argvKeys = {'command', 'args'};

Expand Down
3 changes: 3 additions & 0 deletions packages/vgv_ai_cli/lib/src/model/drift.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ class const Drift({
/// The key paths, or section names, that hold a different value now.
required final List<String> drifted,
}) {
/// {@macro drift}
this;

/// Whether nothing is missing and nothing has drifted.
bool get isEmpty => missing.isEmpty && drifted.isEmpty;
}
Loading
Loading