Clean repo skills config and support custom ACP endpoints
This commit is contained in:
parent
068312394c
commit
6aa87e2ff5
@ -1,8 +0,0 @@
|
||||
developer_instructions = """
|
||||
For this repository, the default execution mode is worktree-first and concurrency-friendly.
|
||||
|
||||
- For non-trivial implementation or debugging tasks, start from `main`, create a temporary git worktree, and do the task in that isolated worktree.
|
||||
- When tasks have independent side work, split them into concurrent lanes while keeping the main agent on the critical path.
|
||||
- When the task is complete, verify the change, merge back to `main`, and clean up the temporary worktree.
|
||||
- Do not repeatedly ask the user to re-enable this workflow for this repository unless they explicitly override it.
|
||||
"""
|
||||
@ -1,43 +0,0 @@
|
||||
---
|
||||
name: xworkmate-worktree-task-mode
|
||||
description: Mandatory XWorkmate task execution mode: every task starts in an isolated git worktree, implementation is verified and committed there, then merged back into main and cleaned up.
|
||||
---
|
||||
|
||||
# XWorkmate Worktree Task Mode
|
||||
|
||||
Use this skill as the required execution path for work in this repository unless the user explicitly asks for a different flow.
|
||||
|
||||
## Goals
|
||||
|
||||
- Keep `main` clean at all times.
|
||||
- Start every task from a temporary worktree created from `main`.
|
||||
- Finish coding, verification, and commit(s) inside that worktree.
|
||||
- Return to `main`, merge the finished worktree branch, then remove the temporary worktree.
|
||||
|
||||
## Required Flow
|
||||
|
||||
Every task follows this sequence:
|
||||
|
||||
1. Inspect the current repo state from the main checkout.
|
||||
2. Create a temporary branch and `git worktree` from `main`.
|
||||
3. Do all coding work inside the worktree.
|
||||
4. Run the required verification inside the worktree.
|
||||
5. Commit the task result inside the worktree.
|
||||
6. Return to the main checkout and switch to `main`.
|
||||
7. Merge the worktree branch into `main`.
|
||||
8. If the task completed successfully, remove the temporary worktree and delete the temporary branch.
|
||||
|
||||
## Guardrails
|
||||
|
||||
- Do not skip the worktree step because the task seems small. The default is still to start in a worktree.
|
||||
- Do not ask the user to re-confirm this mode on each task.
|
||||
- Do not merge unverified work back into `main`.
|
||||
- Do not leave temporary worktrees behind after a successful task unless the user explicitly asks to keep them.
|
||||
- Preserve user changes and do not revert unrelated work.
|
||||
|
||||
## Operational Notes
|
||||
|
||||
- The worktree branch should be created from `main`, not from the current feature branch.
|
||||
- Verification should match the task scope, but it must happen before the final merge.
|
||||
- The final integration step is not complete until the worktree branch is merged into `main`.
|
||||
- Successful completion means the full lifecycle is closed: worktree created, changes implemented, verification run, commit created, merged into `main`, worktree cleaned up.
|
||||
@ -1,8 +1,8 @@
|
||||
## Skills
|
||||
|
||||
- Use `xworkmate-secure-development` for any change that touches gateway auth, `.env`, secure storage, tokens, passwords, TLS, file upload, native entitlements, packaging, or release-sensitive settings.
|
||||
- Use `xworkmate-acceptance` before claiming build, packaging, installation, or release readiness for this repo.
|
||||
- For non-trivial implementation work, default to the repo skill `xworkmate-worktree-task-mode` and follow its worktree-first execution flow without asking the user to restate that preference each time.
|
||||
- For any change that touches gateway auth, `.env`, secure storage, tokens, passwords, TLS, file upload, native entitlements, packaging, or release-sensitive settings, follow the security rules in this file and [docs/security/secure-development-rules.md](/Users/shenlan/workspaces/cloud-neutral-toolkit/XWorkmate.svc.plus/docs/security/secure-development-rules.md).
|
||||
- For non-trivial implementation work, default to the worktree-first execution flow in this file without asking the user to restate that preference each time.
|
||||
|
||||
## Default Task Mode
|
||||
|
||||
|
||||
@ -914,8 +914,8 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
appText(
|
||||
'预设保留 Codex、OpenCode;其余 provider 通过“自定义添加更多”扩展。每条记录可自定义显示名称和 ACP Server Endpoint,协议支持 ws / wss / http / https。',
|
||||
'Codex and OpenCode stay as presets. Add more custom providers as needed. Each entry can define its own display name and ACP server endpoint with ws / wss / http / https.',
|
||||
'这里仅保留 Codex、OpenCode 预设接入。历史上的 Claude / Gemini 预配置会迁移为自定义 ACP Server Endpoint。你可以继续添加多个自定义 Endpoint,协议支持 ws / wss / http / https。',
|
||||
'Only Codex and OpenCode stay as preset integrations here. Legacy Claude and Gemini entries are migrated into custom ACP server endpoints. You can add multiple custom endpoints with ws / wss / http / https.',
|
||||
),
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
@ -929,7 +929,12 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
_appendExternalAcpProvider(settings),
|
||||
),
|
||||
icon: const Icon(Icons.add_rounded),
|
||||
label: Text(appText('自定义添加更多', 'Add more custom providers')),
|
||||
label: Text(
|
||||
appText(
|
||||
'添加自定义 ACP Server Endpoint',
|
||||
'Add custom ACP server endpoint',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@ -1013,7 +1018,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
),
|
||||
),
|
||||
_EditableField(
|
||||
label: appText('ACP Endpoint', 'ACP Endpoint'),
|
||||
label: appText('ACP Server Endpoint', 'ACP Server Endpoint'),
|
||||
value: endpoint,
|
||||
onSubmitted: (value) => _saveSettings(
|
||||
controller,
|
||||
@ -1049,7 +1054,10 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
...settings.externalAcpEndpoints,
|
||||
ExternalAcpEndpointProfile(
|
||||
providerKey: providerKey(),
|
||||
label: appText('自定义添加更多 $suffix', 'Custom Add More $suffix'),
|
||||
label: appText(
|
||||
'自定义 ACP Endpoint $suffix',
|
||||
'Custom ACP Endpoint $suffix',
|
||||
),
|
||||
badge: '',
|
||||
endpoint: '',
|
||||
enabled: true,
|
||||
|
||||
@ -262,6 +262,8 @@ const List<SingleAgentProvider> kKnownSingleAgentProviders =
|
||||
SingleAgentProvider.gemini,
|
||||
];
|
||||
|
||||
const Set<String> kLegacyExternalAcpProviderIds = <String>{'claude', 'gemini'};
|
||||
|
||||
class ExternalAcpEndpointProfile {
|
||||
const ExternalAcpEndpointProfile({
|
||||
required this.providerKey,
|
||||
@ -369,11 +371,29 @@ List<ExternalAcpEndpointProfile> normalizeExternalAcpEndpoints({
|
||||
final incoming =
|
||||
profiles?.toList(growable: false) ?? const <ExternalAcpEndpointProfile>[];
|
||||
final byKey = <String, ExternalAcpEndpointProfile>{};
|
||||
final migratedCustomProfiles = <ExternalAcpEndpointProfile>[];
|
||||
var customSuffix = 1;
|
||||
|
||||
String nextCustomKey() {
|
||||
while (true) {
|
||||
final key = 'custom-agent-$customSuffix';
|
||||
customSuffix += 1;
|
||||
if (!byKey.containsKey(key) &&
|
||||
!migratedCustomProfiles.any((item) => item.providerKey == key)) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (final item in incoming) {
|
||||
final key = item.providerKey.trim().toLowerCase();
|
||||
if (key.isEmpty || byKey.containsKey(key)) {
|
||||
continue;
|
||||
}
|
||||
if (kLegacyExternalAcpProviderIds.contains(key)) {
|
||||
migratedCustomProfiles.add(item.copyWith(providerKey: nextCustomKey()));
|
||||
continue;
|
||||
}
|
||||
byKey[key] = item.copyWith(providerKey: key);
|
||||
}
|
||||
|
||||
@ -381,6 +401,7 @@ List<ExternalAcpEndpointProfile> normalizeExternalAcpEndpoints({
|
||||
for (final provider in kBuiltinExternalAcpProviders)
|
||||
byKey.remove(provider.providerId) ??
|
||||
ExternalAcpEndpointProfile.defaultsForProvider(provider),
|
||||
...migratedCustomProfiles,
|
||||
...byKey.values,
|
||||
];
|
||||
return List<ExternalAcpEndpointProfile>.unmodifiable(normalized);
|
||||
|
||||
@ -926,8 +926,8 @@ class _WebSettingsPageState extends State<WebSettingsPage> {
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
appText(
|
||||
'预设保留 Codex、OpenCode;其余 provider 通过“自定义添加更多”扩展。每条记录可自定义显示名称和 ACP Server Endpoint,协议支持 ws / wss / http / https。',
|
||||
'Codex and OpenCode stay as presets. Add more custom providers as needed. Each entry can define its own display name and ACP server endpoint with ws / wss / http / https.',
|
||||
'这里仅保留 Codex、OpenCode 预设接入。历史上的 Claude / Gemini 预配置会迁移为自定义 ACP Server Endpoint。你可以继续添加多个自定义 Endpoint,协议支持 ws / wss / http / https。',
|
||||
'Only Codex and OpenCode stay as preset integrations here. Legacy Claude and Gemini entries are migrated into custom ACP server endpoints. You can add multiple custom endpoints with ws / wss / http / https.',
|
||||
),
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
@ -944,7 +944,12 @@ class _WebSettingsPageState extends State<WebSettingsPage> {
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.add_rounded),
|
||||
label: Text(appText('自定义添加更多', 'Add more custom providers')),
|
||||
label: Text(
|
||||
appText(
|
||||
'添加自定义 ACP Server Endpoint',
|
||||
'Add custom ACP server endpoint',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@ -1029,7 +1034,7 @@ class _WebSettingsPageState extends State<WebSettingsPage> {
|
||||
TextField(
|
||||
controller: endpointController,
|
||||
decoration: InputDecoration(
|
||||
labelText: appText('ACP Endpoint', 'ACP Endpoint'),
|
||||
labelText: appText('ACP Server Endpoint', 'ACP Server Endpoint'),
|
||||
),
|
||||
onChanged: (_) => _stageExternalAcpDraft(controller),
|
||||
),
|
||||
@ -1092,7 +1097,10 @@ class _WebSettingsPageState extends State<WebSettingsPage> {
|
||||
...settings.externalAcpEndpoints,
|
||||
ExternalAcpEndpointProfile(
|
||||
providerKey: providerKey(),
|
||||
label: appText('自定义 Provider $suffix', 'Custom Provider $suffix'),
|
||||
label: appText(
|
||||
'自定义 ACP Endpoint $suffix',
|
||||
'Custom ACP Endpoint $suffix',
|
||||
),
|
||||
badge: '$suffix',
|
||||
endpoint: '',
|
||||
enabled: true,
|
||||
|
||||
@ -309,7 +309,7 @@ void main() {
|
||||
find.byKey(const ValueKey('external-acp-provider-add-button')),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.text('自定义添加更多'), findsOneWidget);
|
||||
expect(find.text('添加自定义 ACP Server Endpoint'), findsOneWidget);
|
||||
expect(find.textContaining('ws://127.0.0.1:9001'), findsWidgets);
|
||||
expect(find.text('标志'), findsNothing);
|
||||
expect(find.text('Badge'), findsNothing);
|
||||
|
||||
@ -62,5 +62,40 @@ void main() {
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
||||
test('legacy claude and gemini entries migrate into custom endpoints', () {
|
||||
final normalized = normalizeExternalAcpEndpoints(
|
||||
profiles: const <ExternalAcpEndpointProfile>[
|
||||
ExternalAcpEndpointProfile(
|
||||
providerKey: 'claude',
|
||||
label: 'Claude',
|
||||
badge: 'Cl',
|
||||
endpoint: 'ws://127.0.0.1:9011',
|
||||
enabled: true,
|
||||
),
|
||||
ExternalAcpEndpointProfile(
|
||||
providerKey: 'gemini',
|
||||
label: 'Gemini',
|
||||
badge: 'G',
|
||||
endpoint: 'ws://127.0.0.1:9012',
|
||||
enabled: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
expect(
|
||||
normalized.take(2).map((item) => item.providerKey).toList(),
|
||||
const <String>['codex', 'opencode'],
|
||||
);
|
||||
expect(
|
||||
normalized
|
||||
.where((item) => item.providerKey.startsWith('custom-agent-'))
|
||||
.map((item) => item.label)
|
||||
.toList(growable: false),
|
||||
const <String>['Claude', 'Gemini'],
|
||||
);
|
||||
expect(normalized.any((item) => item.providerKey == 'claude'), isFalse);
|
||||
expect(normalized.any((item) => item.providerKey == 'gemini'), isFalse);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -178,7 +178,7 @@ void main() {
|
||||
find.byKey(const ValueKey('web-external-acp-provider-add-button')),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.text('自定义添加更多'), findsOneWidget);
|
||||
expect(find.text('添加自定义 ACP Server Endpoint'), findsOneWidget);
|
||||
expect(find.text('标志'), findsNothing);
|
||||
expect(find.text('Badge'), findsNothing);
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user