fix: init-config command not shown in help
This commit is contained in:
parent
86ca0fc4a6
commit
1f2a803a7f
|
|
@ -12,3 +12,4 @@ agent-orchestrator.yaml
|
||||||
/skills/*
|
/skills/*
|
||||||
!/skills/dart-gherkin-tests
|
!/skills/dart-gherkin-tests
|
||||||
*.g.dart
|
*.g.dart
|
||||||
|
**/*.exe
|
||||||
|
|
|
||||||
|
|
@ -1,123 +1,152 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:args/args.dart';
|
import 'package:args/args.dart';
|
||||||
|
import 'package:args/command_runner.dart';
|
||||||
import 'package:code_work_spawner/code_work_spawner.dart';
|
import 'package:code_work_spawner/code_work_spawner.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
Future<void> main(List<String> arguments) async {
|
Future<void> main(List<String> arguments) async {
|
||||||
_configureLogging();
|
_configureLogging();
|
||||||
|
|
||||||
final parser = ArgParser()
|
final runner = _CodeWorkSpawnerCommandRunner();
|
||||||
..addFlag(
|
|
||||||
'once',
|
|
||||||
abbr: '1',
|
|
||||||
help: 'Run a single poll cycle and exit.',
|
|
||||||
negatable: false,
|
|
||||||
)
|
|
||||||
..addOption(
|
|
||||||
'config',
|
|
||||||
abbr: 'c',
|
|
||||||
help: 'Path to agent-orchestrator.yaml.',
|
|
||||||
defaultsTo: 'agent-orchestrator.yaml',
|
|
||||||
)
|
|
||||||
..addOption(
|
|
||||||
'db',
|
|
||||||
help: 'Path to the SQLite database file.',
|
|
||||||
defaultsTo: '.code_work_spawner.sqlite3',
|
|
||||||
)
|
|
||||||
..addOption(
|
|
||||||
'gh-command',
|
|
||||||
help: 'Path to the gh executable.',
|
|
||||||
defaultsTo: 'gh',
|
|
||||||
)
|
|
||||||
..addFlag('help', abbr: 'h', negatable: false);
|
|
||||||
final initConfigParser = parser.addCommand('init-config')
|
|
||||||
..addOption(
|
|
||||||
'output',
|
|
||||||
abbr: 'o',
|
|
||||||
help: 'Write the starter config to a file.',
|
|
||||||
)
|
|
||||||
..addFlag(
|
|
||||||
'force',
|
|
||||||
abbr: 'f',
|
|
||||||
help: 'Overwrite the output file if it already exists.',
|
|
||||||
negatable: false,
|
|
||||||
)
|
|
||||||
..addFlag('help', abbr: 'h', negatable: false);
|
|
||||||
|
|
||||||
final results = parser.parse(arguments);
|
|
||||||
final command = results.command;
|
|
||||||
if (command?.name == 'init-config') {
|
|
||||||
if (command!['help'] as bool) {
|
|
||||||
stdout.writeln(initConfigParser.usage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await _runInitConfig(command);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results['help'] as bool) {
|
|
||||||
stdout.writeln(parser.usage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final config = await AppConfig.load(results['config'] as String);
|
|
||||||
final logger = Logger('code_work_spawner.cli');
|
|
||||||
logger.info(
|
|
||||||
'loaded config=${config.configPath} '
|
|
||||||
'projects=${config.projects.length} '
|
|
||||||
'worktreeDir=${config.worktreeDir ?? "(system temp)"}',
|
|
||||||
);
|
|
||||||
for (final project in config.projects.values) {
|
|
||||||
final repoDirectory = Directory(project.path);
|
|
||||||
logger.info(
|
|
||||||
'project=${project.key} '
|
|
||||||
'repo=${project.repo} '
|
|
||||||
'path=${project.path} '
|
|
||||||
'exists=${await repoDirectory.exists()} '
|
|
||||||
'enabled=${project.issueAssistant.enabled}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final app = await IssueAssistantApp.open(
|
|
||||||
config: config,
|
|
||||||
databasePath: results['db'] as String,
|
|
||||||
ghCommand: results['gh-command'] as String,
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (results['once'] as bool) {
|
await runner.run(arguments);
|
||||||
await app.runOnce();
|
} on UsageException catch (error) {
|
||||||
return;
|
stdout.writeln(error);
|
||||||
}
|
exitCode = 64;
|
||||||
|
|
||||||
await app.run();
|
|
||||||
} finally {
|
|
||||||
await app.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _runInitConfig(ArgResults command) async {
|
class _CodeWorkSpawnerCommandRunner extends CommandRunner<void> {
|
||||||
final outputPath = command['output'] as String?;
|
_CodeWorkSpawnerCommandRunner()
|
||||||
final force = command['force'] as bool;
|
: super(
|
||||||
final content = AppConfig.renderInitialConfig();
|
'code_work_spawner',
|
||||||
|
'GitHub issue thread assistant with an Agent Orchestrator style YAML config.',
|
||||||
|
) {
|
||||||
|
argParser
|
||||||
|
..addFlag(
|
||||||
|
'once',
|
||||||
|
abbr: '1',
|
||||||
|
help: 'Run a single poll cycle and exit.',
|
||||||
|
negatable: false,
|
||||||
|
)
|
||||||
|
..addOption(
|
||||||
|
'config',
|
||||||
|
abbr: 'c',
|
||||||
|
help: 'Path to agent-orchestrator.yaml.',
|
||||||
|
defaultsTo: 'agent-orchestrator.yaml',
|
||||||
|
)
|
||||||
|
..addOption(
|
||||||
|
'db',
|
||||||
|
help: 'Path to the SQLite database file.',
|
||||||
|
defaultsTo: '.code_work_spawner.sqlite3',
|
||||||
|
)
|
||||||
|
..addOption(
|
||||||
|
'gh-command',
|
||||||
|
help: 'Path to the gh executable.',
|
||||||
|
defaultsTo: 'gh',
|
||||||
|
);
|
||||||
|
|
||||||
if (outputPath == null) {
|
addCommand(_InitConfigCommand());
|
||||||
stdout.writeln(content);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final outputFile = File(outputPath);
|
@override
|
||||||
if (await outputFile.exists() && !force) {
|
Future<void> runCommand(ArgResults topLevelResults) async {
|
||||||
stderr.writeln(
|
if (topLevelResults['help'] as bool) {
|
||||||
'Refusing to overwrite existing file: $outputPath. Use --force to replace it.',
|
stdout.writeln(usage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topLevelResults.command != null) {
|
||||||
|
await super.runCommand(topLevelResults);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final config = await AppConfig.load(topLevelResults['config'] as String);
|
||||||
|
final logger = Logger('code_work_spawner.cli');
|
||||||
|
logger.info(
|
||||||
|
'loaded config=${config.configPath} '
|
||||||
|
'projects=${config.projects.length} '
|
||||||
|
'worktreeDir=${config.worktreeDir ?? "(system temp)"}',
|
||||||
);
|
);
|
||||||
exitCode = 2;
|
for (final project in config.projects.values) {
|
||||||
return;
|
final repoDirectory = Directory(project.path);
|
||||||
|
logger.info(
|
||||||
|
'project=${project.key} '
|
||||||
|
'repo=${project.repo} '
|
||||||
|
'path=${project.path} '
|
||||||
|
'exists=${await repoDirectory.exists()} '
|
||||||
|
'enabled=${project.issueAssistant.enabled}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final app = await IssueAssistantApp.open(
|
||||||
|
config: config,
|
||||||
|
databasePath: topLevelResults['db'] as String,
|
||||||
|
ghCommand: topLevelResults['gh-command'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (topLevelResults['once'] as bool) {
|
||||||
|
await app.runOnce();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await app.run();
|
||||||
|
} finally {
|
||||||
|
await app.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InitConfigCommand extends Command<void> {
|
||||||
|
@override
|
||||||
|
String get name => 'init-config';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get description => 'Print or write a starter config file.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get invocation => 'code_work_spawner init-config [arguments]';
|
||||||
|
|
||||||
|
_InitConfigCommand() {
|
||||||
|
argParser
|
||||||
|
..addOption(
|
||||||
|
'output',
|
||||||
|
abbr: 'o',
|
||||||
|
help: 'Write the starter config to a file.',
|
||||||
|
)
|
||||||
|
..addFlag(
|
||||||
|
'force',
|
||||||
|
abbr: 'f',
|
||||||
|
help: 'Overwrite the output file if it already exists.',
|
||||||
|
negatable: false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await outputFile.parent.create(recursive: true);
|
@override
|
||||||
await outputFile.writeAsString('$content\n');
|
Future<void> run() async {
|
||||||
stdout.writeln('Wrote starter config to ${outputFile.path}');
|
final outputPath = argResults!['output'] as String?;
|
||||||
|
final force = argResults!['force'] as bool;
|
||||||
|
final content = AppConfig.renderInitialConfig();
|
||||||
|
|
||||||
|
if (outputPath == null) {
|
||||||
|
stdout.writeln(content);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final outputFile = File(outputPath);
|
||||||
|
if (await outputFile.exists() && !force) {
|
||||||
|
stderr.writeln(
|
||||||
|
'Refusing to overwrite existing file: $outputPath. Use --force to replace it.',
|
||||||
|
);
|
||||||
|
exitCode = 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await outputFile.parent.create(recursive: true);
|
||||||
|
await outputFile.writeAsString('$content\n');
|
||||||
|
stdout.writeln('Wrote starter config to ${outputFile.path}');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _configureLogging() {
|
void _configureLogging() {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ dependencies:
|
||||||
meta: ^1.16.0
|
meta: ^1.16.0
|
||||||
github: ^9.25.0
|
github: ^9.25.0
|
||||||
git: ^2.3.2
|
git: ^2.3.2
|
||||||
|
nyxx: ^6.8.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.6.0
|
build_runner: ^2.6.0
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,29 @@ void main() {
|
||||||
/// So that I can bootstrap configuration without reading the code first
|
/// So that I can bootstrap configuration without reading the code first
|
||||||
/// ```
|
/// ```
|
||||||
group('code_work_spawner init-config', () {
|
group('code_work_spawner init-config', () {
|
||||||
|
/// ```gherkin
|
||||||
|
/// Scenario: Show init-config in top-level help
|
||||||
|
/// Given the CLI is executed with the top-level help flag
|
||||||
|
/// When the process completes successfully
|
||||||
|
/// Then stdout lists the init-config command in the available commands section
|
||||||
|
/// ```
|
||||||
|
test('Show init-config in top-level help', () async {
|
||||||
|
// Given the CLI is executed with the top-level help flag.
|
||||||
|
final result = await Process.run(Platform.resolvedExecutable, [
|
||||||
|
'run',
|
||||||
|
'bin/code_work_spawner.dart',
|
||||||
|
'--help',
|
||||||
|
], workingDirectory: Directory.current.path);
|
||||||
|
|
||||||
|
// When the process completes successfully.
|
||||||
|
expect(result.exitCode, 0, reason: '${result.stderr}');
|
||||||
|
|
||||||
|
// Then stdout lists the init-config command in the available commands section.
|
||||||
|
final stdoutText = result.stdout as String;
|
||||||
|
expect(stdoutText, contains('Available commands:'));
|
||||||
|
expect(stdoutText, contains('init-config'));
|
||||||
|
});
|
||||||
|
|
||||||
/// ```gherkin
|
/// ```gherkin
|
||||||
/// Scenario: Print the starter config to stdout
|
/// Scenario: Print the starter config to stdout
|
||||||
/// Given the CLI is executed with the init-config command
|
/// Given the CLI is executed with the init-config command
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue