docs: improve docs of config and readme

This commit is contained in:
insleker 2026-04-13 11:20:06 +08:00
parent 4d2ced2b96
commit 81d56f999d
17 changed files with 111 additions and 114 deletions

BIN
README.assets/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

View File

@ -1,44 +1,42 @@
# code_work_spawner # code_work_spawner
Repository issue thread assistant with an Agent Orchestrator style YAML config. Repository issue thread assistant configured by YAML.
It consumes issue tracker events for configured GitHub, GitLab, Gitea, or OpenProject It watches GitHub, GitLab, Gitea, or OpenProject issue events, reads threads, and triggers CLI responders such as `codex`, `opencode`, or `copilot-cli`.
projects, reads issue threads, and uses CLI responders such as `codex`, `opencode`, or Supports multi-repo config, responder fallback chains, isolated worktrees, and optional Discord or desktop notifications.
`copilot-cli` to decide when to reply. It supports
multi-repo config, responder fallback chains, and optional Discord webhook and <img src="README.assets/image.png" style="width:50%;">
desktop notifications. Repository access is configured separately through each
project's `scm` block so tracker and checkout handling can evolve
independently.
## Highlights ## Highlights
- Consume GitHub, GitLab, Gitea, and OpenProject issue events across multiple projects - Track issue events across multiple repositories
- Trigger replies from mentions or meaningful thread updates - Reply on mentions or meaningful thread updates
- Run planning and bug-verification flows in isolated worktrees - Run planning and bug verification in isolated worktrees
- Send optional Discord or desktop notifications for assistant events - Send optional Discord and desktop notifications
## Requirements ## Requirements
- Dart SDK - Dart SDK
- `gh`, `glab`, `tea`, and/or OpenProject API credentials authenticated for the target projects - Authenticated `gh`, `glab`, `tea`, and/or OpenProject API credentials
- Local checkouts for configured repositories - Local checkouts for configured repositories
- Optional local responder CLIs such as `codex`, `opencode`, or `copilot-cli` - Optional responder CLIs (`codex`, `opencode`, `copilot-cli`)
## Config ## Config
Create a local `agent-orchestrator.yaml` and use Create `CWS_conf.yaml` using:
[`examples/README.md`](examples/README.md) plus - [examples/README.md](examples/README.md)
[`examples/simple-github.yaml`](examples/simple-github.yaml), - [examples/simple-github.yaml](examples/simple-github.yaml)
[`examples/simple-gitlab.yaml`](examples/simple-gitlab.yaml), or - [examples/simple-gitlab.yaml](examples/simple-gitlab.yaml)
[`examples/simple-gitea.yaml`](examples/simple-gitea.yaml) as the reference. - [examples/simple-gitea.yaml](examples/simple-gitea.yaml)
Environment placeholders such as `${CWS_DISCORD_WEBHOOK}` are resolved from the Environment placeholders (for example `${CWS_DISCORD_WEBHOOK}`) are resolved
process environment and from a `.env` file placed next to the selected config from process env vars and from a `.env` file next to your config.
file. Logging also supports `CWS_LOG_LEVEL` (or `LOG_LEVEL`) with values such
as `trace`, `debug`, `info`, `warning`, `error`, or `fatal`. See
[.env.example](.env.example) for a minimal starter file.
Generate a starter config: Logging level can be set with `CWS_LOG_LEVEL` (or `LOG_LEVEL`): `trace`,
`debug`, `info`, `warning`, `error`, `fatal`. Minimal example:
[.env.example](.env.example).
Generate starter config:
```bash ```bash
dart run bin/code_work_spawner.dart init-config dart run bin/code_work_spawner.dart init-config
@ -50,23 +48,25 @@ dart run bin/code_work_spawner.dart init-config --output agent-orchestrator.yaml
## Run ## Run
Generate code first:
```bash ```bash
dart run build_runner build --delete-conflicting-outputs dart run build_runner build --delete-conflicting-outputs
``` ```
Single event-source reconciliation cycle: Run once:
```bash ```bash
dart run bin/code_work_spawner.dart --once dart run bin/code_work_spawner.dart --once
``` ```
Long-running tracker runtime: Run continuously:
```bash ```bash
dart run bin/code_work_spawner.dart dart run bin/code_work_spawner.dart
``` ```
Override tracker CLI paths when needed: Override CLI paths if needed:
```bash ```bash
dart run bin/code_work_spawner.dart \ dart run bin/code_work_spawner.dart \
@ -79,7 +79,7 @@ dart run bin/code_work_spawner.dart \
## Install Binary ## Install Binary
Install the latest matching release binary from GitHub Releases: Install latest release binary:
```bash ```bash
# export GH_TOKEN="$TOKEN" # If got 404, `GITHUB_TOKEN` also work # export GH_TOKEN="$TOKEN" # If got 404, `GITHUB_TOKEN` also work

View File

@ -18,7 +18,7 @@ class _CodeWorkSpawnerCommandRunner extends CommandRunner<void> {
_CodeWorkSpawnerCommandRunner() _CodeWorkSpawnerCommandRunner()
: super( : super(
'code_work_spawner', 'code_work_spawner',
'Repository issue thread assistant with an Agent Orchestrator style YAML config.', 'Repository issue thread assistant with a YAML config.',
) { ) {
argParser argParser
..addFlag( ..addFlag(
@ -30,8 +30,8 @@ class _CodeWorkSpawnerCommandRunner extends CommandRunner<void> {
..addOption( ..addOption(
'config', 'config',
abbr: 'c', abbr: 'c',
help: 'Path to agent-orchestrator.yaml.', help: 'Path to the YAML config file.',
defaultsTo: 'agent-orchestrator.yaml', defaultsTo: 'CWS_conf.yaml',
) )
..addOption( ..addOption(
'db', 'db',

View File

@ -25,10 +25,15 @@ defaults:
# Replace this with a responder available on your machine. # Replace this with a responder available on your machine.
- id: codex - id: codex
command: codex command: codex
args: ["exec", "--json"] args: ["-s", "danger-full-access", "exec", "--json"]
timeout: 10m timeout: 10m
# - id: opencode # - id: opencode
# command: opencode # command: opencode
# args: ["run", "-f", "json"]
# timeout: 10m
# - id: copilot-cli
# command: copilot
# args: ["--allow-all", "--autopilot", "-p", "--output-format", "json"]
# timeout: 10m # timeout: 10m
# notifications: # notifications:
# # Optional Discord notifications for assistant activity. # # Optional Discord notifications for assistant activity.

View File

@ -1081,6 +1081,11 @@ ${_commentBlock(defaultIssueAssistantPrompt, indent: ' # ')}
# timeout: 10m # timeout: 10m
# - id: opencode # - id: opencode
# command: opencode # command: opencode
# args: ["run", "-f", "json"]
# timeout: 10m
# - id: copilot-cli
# command: copilot
# args: ["--allow-all", "--autopilot", "-p", "--output-format", "json"]
# timeout: 10m # timeout: 10m
# notifications: # notifications:
# - type: discordWebhook # - type: discordWebhook

View File

@ -46,6 +46,11 @@
"sourceType": "github", "sourceType": "github",
"computedHash": "8bfae087ef27eb37092d94caf1da3406b500aa908756772bb1349a1ac70823dd" "computedHash": "8bfae087ef27eb37092d94caf1da3406b500aa908756772bb1349a1ac70823dd"
}, },
"babysit-pr": {
"source": "openai/codex",
"sourceType": "github",
"computedHash": "8d952e21312ebf8573d9f5655c0e0eb3ad6e2ca14fcc6b95cbad046692eaf651"
},
"breakdown-epic-arch": { "breakdown-epic-arch": {
"source": "github/awesome-copilot", "source": "github/awesome-copilot",
"sourceType": "github", "sourceType": "github",
@ -101,6 +106,11 @@
"sourceType": "github", "sourceType": "github",
"computedHash": "a7d80450799b2141ddff9ec614477e237c1f50acb9eeade2dd869aeac3d004a9" "computedHash": "a7d80450799b2141ddff9ec614477e237c1f50acb9eeade2dd869aeac3d004a9"
}, },
"codex": {
"source": "softaworks/agent-toolkit",
"sourceType": "github",
"computedHash": "4b7e8695807b567a7a7d467ab15b3da330aa0a847827067d79c654c540a72fe9"
},
"comment-code-generate-a-tutorial": { "comment-code-generate-a-tutorial": {
"source": "github/awesome-copilot", "source": "github/awesome-copilot",
"sourceType": "github", "sourceType": "github",
@ -121,6 +131,11 @@
"sourceType": "github", "sourceType": "github",
"computedHash": "934c017d41118a44f4db0e80b526db175cadcfdf8c5354fc9eceb0b3cb6beb50" "computedHash": "934c017d41118a44f4db0e80b526db175cadcfdf8c5354fc9eceb0b3cb6beb50"
}, },
"copilot-cli-quickstart": {
"source": "github/awesome-copilot",
"sourceType": "github",
"computedHash": "720e848c508c9fccc0d9edbdb7a68ede333866e1e0a126b7d4b179e01d7ba5d7"
},
"create-agentsmd": { "create-agentsmd": {
"source": "github/awesome-copilot", "source": "github/awesome-copilot",
"sourceType": "github", "sourceType": "github",

View File

@ -60,7 +60,7 @@ void main() {
test('Merge defaults with project overrides', () async { test('Merge defaults with project overrides', () async {
// Given camelCase config with shared defaults and one project override. // Given camelCase config with shared defaults and one project override.
final tempDir = await Directory.systemTemp.createTemp('cws-config-'); final tempDir = await Directory.systemTemp.createTemp('cws-config-');
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -120,7 +120,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-default-templates-', 'cws-default-templates-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -163,7 +163,7 @@ projects:
test('Resolve config placeholders from a colocated dotenv file', () async { test('Resolve config placeholders from a colocated dotenv file', () async {
// Given an orchestrator config with environment placeholders and a colocated dotenv file. // Given an orchestrator config with environment placeholders and a colocated dotenv file.
final tempDir = await Directory.systemTemp.createTemp('cws-dotenv-'); final tempDir = await Directory.systemTemp.createTemp('cws-dotenv-');
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
final dotenvFile = File(p.join(tempDir.path, '.env')); final dotenvFile = File(p.join(tempDir.path, '.env'));
addTearDown(AppEnvironment.resetForTest); addTearDown(AppEnvironment.resetForTest);
await dotenvFile.writeAsString(''' await dotenvFile.writeAsString('''
@ -211,7 +211,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-comment-template-', 'cws-comment-template-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -249,7 +249,7 @@ projects:
test('Normalize legacy codex json flag', () async { test('Normalize legacy codex json flag', () async {
// Given an orchestrator config that still uses the legacy codex --json-output flag. // Given an orchestrator config that still uses the legacy codex --json-output flag.
final tempDir = await Directory.systemTemp.createTemp('cws-codex-args-'); final tempDir = await Directory.systemTemp.createTemp('cws-codex-args-');
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -285,7 +285,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-notification-config-', 'cws-notification-config-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -340,7 +340,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-desktop-notification-config-', 'cws-desktop-notification-config-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -379,7 +379,7 @@ projects:
test('Load camelCase config with shared worktree root', () async { test('Load camelCase config with shared worktree root', () async {
// Given camelCase config with dataDir, worktreeDir, and project settings. // Given camelCase config with dataDir, worktreeDir, and project settings.
final tempDir = await Directory.systemTemp.createTemp('cws-camel-case-'); final tempDir = await Directory.systemTemp.createTemp('cws-camel-case-');
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
dataDir: ~/.agent-orchestrator dataDir: ~/.agent-orchestrator
worktreeDir: ~/.worktrees worktreeDir: ~/.worktrees
@ -420,7 +420,7 @@ projects:
test('Infer a Gitea issue tracker provider from the event source', () async { test('Infer a Gitea issue tracker provider from the event source', () async {
// Given a config whose project sets tea/polling as the event source type. // Given a config whose project sets tea/polling as the event source type.
final tempDir = await Directory.systemTemp.createTemp('cws-gitea-'); final tempDir = await Directory.systemTemp.createTemp('cws-gitea-');
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -456,7 +456,7 @@ projects:
test('Infer a GitLab issue tracker provider from the event source', () async { test('Infer a GitLab issue tracker provider from the event source', () async {
// Given a config whose project sets glab/polling as the event source type. // Given a config whose project sets glab/polling as the event source type.
final tempDir = await Directory.systemTemp.createTemp('cws-gitlab-'); final tempDir = await Directory.systemTemp.createTemp('cws-gitlab-');
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -497,9 +497,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-openproject-', 'cws-openproject-',
); );
final configFile = File( final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
p.join(tempDir.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -542,7 +540,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-openproject-missing-scm-', 'cws-openproject-missing-scm-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -585,7 +583,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-openproject-project-name-', 'cws-openproject-project-name-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -621,7 +619,7 @@ projects:
test('Load gh/gosmee as the issue event source', () async { test('Load gh/gosmee as the issue event source', () async {
// Given a GitHub project config that opts into gh/gosmee. // Given a GitHub project config that opts into gh/gosmee.
final tempDir = await Directory.systemTemp.createTemp('cws-gh-gosmee-'); final tempDir = await Directory.systemTemp.createTemp('cws-gh-gosmee-');
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -655,7 +653,7 @@ projects:
test('Load glab/gosmee as the issue event source', () async { test('Load glab/gosmee as the issue event source', () async {
// Given a GitLab project config that opts into glab/gosmee. // Given a GitLab project config that opts into glab/gosmee.
final tempDir = await Directory.systemTemp.createTemp('cws-glab-gosmee-'); final tempDir = await Directory.systemTemp.createTemp('cws-glab-gosmee-');
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -693,9 +691,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-explicit-scm-', 'cws-explicit-scm-',
); );
final configFile = File( final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
p.join(tempDir.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -732,7 +728,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-legacy-provider-fallback-', 'cws-legacy-provider-fallback-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -769,7 +765,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-gh-gosmee-gitea-', 'cws-gh-gosmee-gitea-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -813,9 +809,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-glab-gosmee-github-', 'cws-glab-gosmee-github-',
); );
final configFile = File( final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
p.join(tempDir.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -860,7 +854,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-webhook-forward-', 'cws-webhook-forward-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -896,7 +890,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-channel-reconcile-', 'cws-channel-reconcile-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -941,7 +935,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-legacy-channel-poll-', 'cws-legacy-channel-poll-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -985,9 +979,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-webhook-forward-gitea-', 'cws-webhook-forward-gitea-',
); );
final configFile = File( final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
p.join(tempDir.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -1028,7 +1020,7 @@ projects:
test('Load tea/gosmee as the issue event source', () async { test('Load tea/gosmee as the issue event source', () async {
// Given a Gitea project config that opts into tea/gosmee. // Given a Gitea project config that opts into tea/gosmee.
final tempDir = await Directory.systemTemp.createTemp('cws-gosmee-'); final tempDir = await Directory.systemTemp.createTemp('cws-gosmee-');
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -1066,9 +1058,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-gosmee-github-', 'cws-gosmee-github-',
); );
final configFile = File( final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
p.join(tempDir.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -1110,7 +1100,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-snake-case-invalid-', 'cws-snake-case-invalid-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
worktree_dir: ~/.worktrees worktree_dir: ~/.worktrees
projects: projects:
@ -1146,7 +1136,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-invalid-repo-slug-', 'cws-invalid-repo-slug-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:
@ -1180,7 +1170,7 @@ projects:
test('Generate a starter config that parses as valid YAML', () async { test('Generate a starter config that parses as valid YAML', () async {
// Given the built-in initial config renderer. // Given the built-in initial config renderer.
final tempDir = await Directory.systemTemp.createTemp('cws-init-config-'); final tempDir = await Directory.systemTemp.createTemp('cws-init-config-');
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
final contents = AppConfig.renderInitialConfig(); final contents = AppConfig.renderInitialConfig();
// When writing the generated starter config to disk. // When writing the generated starter config to disk.
@ -1220,7 +1210,7 @@ projects:
final tempDir = await Directory.systemTemp.createTemp( final tempDir = await Directory.systemTemp.createTemp(
'cws-init-optional-', 'cws-init-optional-',
); );
final configFile = File(p.join(tempDir.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(tempDir.path, 'CWS_conf.yaml'));
final contents = AppConfig.renderInitialConfig() final contents = AppConfig.renderInitialConfig()
.replaceFirst( .replaceFirst(
' # capabilities: ["planning", "bug_verification"]', ' # capabilities: ["planning", "bug_verification"]',

View File

@ -115,7 +115,7 @@ void main() {
test('Print the starter config to stdout', () async { test('Print the starter config to stdout', () async {
// Given the CLI is executed with the init-config command. // Given the CLI is executed with the init-config command.
final sandbox = await Directory.systemTemp.createTemp('cws-cli-stdout-'); final sandbox = await Directory.systemTemp.createTemp('cws-cli-stdout-');
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
final result = await runCli(['init-config']); final result = await runCli(['init-config']);
// When the process completes successfully. // When the process completes successfully.
@ -164,7 +164,7 @@ void main() {
test('Refuse overwrite unless force is provided', () async { test('Refuse overwrite unless force is provided', () async {
// Given an existing output file for the init-config command. // Given an existing output file for the init-config command.
final sandbox = await Directory.systemTemp.createTemp('cws-cli-force-'); final sandbox = await Directory.systemTemp.createTemp('cws-cli-force-');
final outputFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final outputFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
await outputFile.writeAsString('original'); await outputFile.writeAsString('original');
// When the CLI writes without the force flag and then with the force flag. // When the CLI writes without the force flag and then with the force flag.

View File

@ -124,7 +124,7 @@ void registerIssueAssistantAppRunOnceConcurrencyTests() {
); );
// And an orchestrator-style config with maxConcurrentIssues set to three. // And an orchestrator-style config with maxConcurrentIssues set to three.
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -266,7 +266,7 @@ projects:
}); });
// And a fake gh command that fails the authenticated user lookup. // And a fake gh command that fails the authenticated user lookup.
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -360,7 +360,7 @@ projects:
); );
// And an AO subset config that enables the assistant without defining responders. // And an AO subset config that enables the assistant without defining responders.
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
projects: projects:
sample: sample:

View File

@ -79,7 +79,7 @@ void registerIssueAssistantAppRunOnceCoreTests() {
}); });
// And an orchestrator-style config pointing to the fake repo and responder. // And an orchestrator-style config pointing to the fake repo and responder.
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -189,9 +189,7 @@ projects:
}); });
// And an orchestrator-style config pointing to the fake repo and responder. // And an orchestrator-style config pointing to the fake repo and responder.
final configFile = File( final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
p.join(sandbox.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -291,7 +289,7 @@ projects:
}); });
// And an orchestrator-style config pointing to the fake repo and responder. // And an orchestrator-style config pointing to the fake repo and responder.
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -419,9 +417,7 @@ projects:
}); });
// And an orchestrator-style config limiting replies after a human comment to one. // And an orchestrator-style config limiting replies after a human comment to one.
final configFile = File( final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
p.join(sandbox.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -524,7 +520,7 @@ projects:
}, stderr: 'codex diagnostic trace'); }, stderr: 'codex diagnostic trace');
// And an orchestrator-style config pointing to the fake repo and responder. // And an orchestrator-style config pointing to the fake repo and responder.
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:

View File

@ -144,9 +144,7 @@ void registerIssueAssistantAppRunOnceMultiProjectTests() {
}); });
// And an orchestrator-style config with both repositories enabled. // And an orchestrator-style config with both repositories enabled.
final configFile = File( final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
p.join(sandbox.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -293,7 +291,7 @@ projects:
}); });
// And an orchestrator-style config that marks the project provider as gitea. // And an orchestrator-style config that marks the project provider as gitea.
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -392,9 +390,7 @@ CWS_GITEA_TOKEN=test-token
}); });
// And an orchestrator-style config that uses an OpenProject project name. // And an orchestrator-style config that uses an OpenProject project name.
final configFile = File( final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
p.join(sandbox.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:

View File

@ -85,9 +85,7 @@ void main() {
}); });
// And an orchestrator-style config that marks the project provider as gitea. // And an orchestrator-style config that marks the project provider as gitea.
final configFile = File( final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
p.join(sandbox.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -212,9 +210,7 @@ CWS_GITEA_TOKEN=test-token
}); });
// And an orchestrator-style config that marks the project provider as gitlab. // And an orchestrator-style config that marks the project provider as gitlab.
final configFile = File( final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
p.join(sandbox.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -315,9 +311,7 @@ projects:
}); });
// And an orchestrator-style config that uses OpenProject as the tracker provider. // And an orchestrator-style config that uses OpenProject as the tracker provider.
final configFile = File( final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
p.join(sandbox.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:

View File

@ -67,7 +67,7 @@ void registerIssueAssistantAppRunRetryTests() {
); );
// And an orchestrator-style config with a short poll interval. // And an orchestrator-style config with a short poll interval.
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:

View File

@ -22,7 +22,7 @@ Future<File> writeSingleProjectIssueAssistantConfig({
required String repoPath, required String repoPath,
required List<String> eventSourceLines, required List<String> eventSourceLines,
}) async { }) async {
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
final eventSourceYaml = eventSourceLines final eventSourceYaml = eventSourceLines
.map((line) => ' $line') .map((line) => ' $line')
.join('\n'); .join('\n');

View File

@ -168,7 +168,7 @@ void registerIssueAssistantAppRunWebhookForwardTests() {
}; };
// And an orchestrator-style config that uses gh/webhook-forward. // And an orchestrator-style config that uses gh/webhook-forward.
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:

View File

@ -99,7 +99,7 @@ void registerIssueAssistantAppRunOnceNotificationTests() {
}); });
// And an orchestrator-style config pointing to the fake repo and responder. // And an orchestrator-style config pointing to the fake repo and responder.
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -225,9 +225,7 @@ projects:
}()); }());
// And an orchestrator-style config with a discord webhook notifier. // And an orchestrator-style config with a discord webhook notifier.
final configFile = File( final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
p.join(sandbox.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:

View File

@ -88,9 +88,7 @@ void registerIssueAssistantAppRunOnceWorktreeTests() {
); );
// And an orchestrator-style config pointing to the fake repo and responder. // And an orchestrator-style config pointing to the fake repo and responder.
final configFile = File( final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
p.join(sandbox.path, 'agent-orchestrator.yaml'),
);
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant:
@ -212,7 +210,7 @@ projects:
}, cwdCapture: cwdCapture); }, cwdCapture: cwdCapture);
// And an orchestrator-style config pointing to the fake repo and responder. // And an orchestrator-style config pointing to the fake repo and responder.
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
worktreeDir: ${worktreeRoot.path} worktreeDir: ${worktreeRoot.path}
defaults: defaults:
@ -312,7 +310,7 @@ projects:
}); });
// And an orchestrator-style config pointing to the fake repo and responder. // And an orchestrator-style config pointing to the fake repo and responder.
final configFile = File(p.join(sandbox.path, 'agent-orchestrator.yaml')); final configFile = File(p.join(sandbox.path, 'CWS_conf.yaml'));
await configFile.writeAsString(''' await configFile.writeAsString('''
defaults: defaults:
issueAssistant: issueAssistant: