feat(agents): generate capabilities and rule loading

This commit is contained in:
Julian lechner committed 2026-09-17 08:31:58 +02:00
1 parent 3fad860256
commit ea2c80817b
4 files changed
+46 -43

No files matched your search

+20 -20
View File
@@ -6,6 +6,7 @@ $shared = Join-Path $root 'shared'
$output = Join-Path $root 'generated'
$pluginManifest = Join-Path $root 'adapters/plugins.tsv'
$ruleSkillManifest = Join-Path $root 'adapters/claude/rule-skills.tsv'
$capabilityManifest = Join-Path $root 'adapters/claude/capabilities.tsv'
if (-not (Test-Path -LiteralPath $pluginManifest)) { throw "Missing plugin manifest: $pluginManifest" }
$pluginEntries = @(Import-Csv -LiteralPath $pluginManifest -Delimiter ([char]9))
@@ -20,6 +21,7 @@ foreach ($pluginEntry in $pluginEntries) {
}
if (-not (Test-Path -LiteralPath $ruleSkillManifest)) { throw "Missing rule-skill manifest: $ruleSkillManifest" }
if (-not (Test-Path -LiteralPath $capabilityManifest)) { throw "Missing capability manifest: $capabilityManifest" }
$ruleSkillEntries = @(Import-Csv -LiteralPath $ruleSkillManifest -Delimiter ([char]9) | Sort-Object skill_name)
if ($ruleSkillEntries.Count -eq 0) { throw 'Rule-skill manifest must define at least one entry.' }
$ruleSkillNames = @()
@@ -31,6 +33,18 @@ foreach ($entry in $ruleSkillEntries) {
if ($ruleSkillNames -contains $entry.skill_name) { throw "Duplicate rule-skill name in manifest: $($entry.skill_name)" }
$ruleSkillNames += $entry.skill_name
}
$agentTools = @{}
foreach ($entry in @(Import-Csv -LiteralPath $capabilityManifest -Delimiter ([char]9))) {
if ([string]::IsNullOrWhiteSpace($entry.role) -or [string]::IsNullOrWhiteSpace($entry.tools)) { throw 'Invalid capability manifest row.' }
$agentTools[$entry.role] = $entry.tools
}
$codexRuleLoadingLines = @()
foreach ($entry in $ruleSkillEntries) {
$rulePath = $entry.rule_file
if (Test-Path -LiteralPath (Join-Path $shared "rules/$($entry.rule_file)") -PathType Container) { $rulePath = "$($entry.rule_file)/index.md" }
$codexRuleLoadingLines += "- rules/$rulePath for $($entry.trigger)."
}
$codexRuleLoadingList = $codexRuleLoadingLines -join "`r`n"
Remove-Item $output -Recurse -Force -ErrorAction SilentlyContinue
New-Item $output -ItemType Directory -Force | Out-Null
@@ -102,26 +116,9 @@ Detect the languages, frameworks, tools, and change areas from the repository an
Load rule files when their subject applies:
- rules/security-auth.md for authentication, authorization, sessions, tokens, permissions, or tenant boundaries.
- rules/security-web.md for browser, frontend, cookie, redirect, XSS, or CSRF work.
- rules/security-api.md for HTTP APIs, request handling, serialization, or endpoints.
- rules/security-data.md for databases, persistence, sensitive data, or multi-tenancy.
- rules/security-files.md for files, uploads, archives, paths, processes, IPC, or deserialization.
- rules/security-network.md for network access, URLs, TLS, proxies, or SSRF.
- rules/security-crypto.md for cryptography, secrets, credentials, keys, or tokens.
- rules/security-supply-chain.md for dependencies, packages, plugins, builds, deployments, or CI.
- rules/angular/index.md for Angular work.
- rules/typescript.md for TypeScript work.
- rules/csharp.md for C# or .NET work.
- rules/wpf/index.md for WPF work.
- rules/ui-ux/index.md for UI or UX decisions.
- rules/microsoft.md for Microsoft 365, Azure DevOps, or Teams work.
- rules/orchestration.md for delegation, subagent work, or long-running task state.
- rules/git.md for Git operations.
- rules/refactoring.md for refactoring work.
- rules/definition-of-done.md when validating completion.
- rules/decision-rule.md when requirements are unresolved, behavior is ambiguous, or a meaningful technical choice remains.
__CODEX_RULE_LOADING__
'@
$codexRuleLoading = $codexRuleLoading.Replace('__CODEX_RULE_LOADING__', $codexRuleLoadingList)
foreach ($shell in @('powershell','bash')) {
New-Item (Join-Path $output "codex-$shell") -ItemType Directory -Force | Out-Null
@@ -180,7 +177,10 @@ foreach ($shell in @('powershell','bash')) {
if ($client -eq 'codex') {
Set-Content (Join-Path $agentsOutput "$name.toml") "name = $(Quote-Toml $name)`r`ndescription = $(Quote-Toml $description)`r`ndeveloper_instructions = $(Quote-Toml $instructions)" -Encoding UTF8
} else {
Set-Content (Join-Path $agentsOutput "$name.md") "---`r`nname: $name`r`ndescription: $description`r`n---`r`n`r`n$instructions" -Encoding UTF8
$tools = $agentTools[$name]
$frontmatter = "---`r`nname: $name`r`ndescription: $description`r`n"
if ($tools) { $frontmatter += "tools: $tools`r`n" }
Set-Content (Join-Path $agentsOutput "$name.md") ($frontmatter + "---`r`n`r`n$instructions") -Encoding UTF8
}
}
}
+19 -22
View File
@@ -13,6 +13,7 @@ shared="$root/shared"
output="$root/generated"
plugin_manifest="$root/adapters/plugins.tsv"
rule_skill_manifest="$root/adapters/claude/rule-skills.tsv"
capability_manifest="$root/adapters/claude/capabilities.tsv"
[ -f "$plugin_manifest" ] || { printf 'Missing plugin manifest: %s\n' "$plugin_manifest" >&2; exit 1; }
declare -A plugin_seen
@@ -29,6 +30,7 @@ done < "$plugin_manifest"
[ "$plugin_count" -gt 0 ] || { printf 'Plugin manifest must define at least one plugin.\n' >&2; exit 1; }
[ -f "$rule_skill_manifest" ] || { printf 'Missing rule-skill manifest: %s\n' "$rule_skill_manifest" >&2; exit 1; }
[ -f "$capability_manifest" ] || { printf 'Missing capability manifest: %s\n' "$capability_manifest" >&2; exit 1; }
declare -A rule_skill_seen
rule_skill_files=()
rule_skill_names=()
@@ -45,8 +47,21 @@ while IFS=$'\t' read -r rule_file skill_name trigger; do
rule_skill_files+=("$rule_file")
rule_skill_names+=("$skill_name")
rule_skill_triggers+=("$trigger")
done < "$rule_skill_manifest"
done < <(tail -n +2 "$rule_skill_manifest" | sort -t$'\t' -k2,2)
[ "${#rule_skill_names[@]}" -gt 0 ] || { printf 'Rule-skill manifest must define at least one entry.\n' >&2; exit 1; }
codex_rule_loading_list=""
for i in "${!rule_skill_names[@]}"; do
rule_file=${rule_skill_files[$i]}
rule_path="$rule_file"
[ -d "$shared/rules/$rule_file" ] && rule_path="$rule_file/index.md"
codex_rule_loading_list="${codex_rule_loading_list}- rules/${rule_path} for ${rule_skill_triggers[$i]}."$'\n'
done
declare -A agent_tools
while IFS=$'\t' read -r role tools; do
[ "$role" != role ] || continue
[ -n "$role" ] && [ -n "$tools" ] || { printf 'Invalid capability manifest row.\n' >&2; exit 1; }
agent_tools[$role]="$tools"
done < "$capability_manifest"
copy_directory() {
local source=$1 destination=$2
@@ -97,27 +112,8 @@ When programming, always load and apply `rules/security.md`. This includes imple
Detect the languages, frameworks, tools, and change areas from the repository and the requested work. Load every applicable rule file before editing. Load all matching files when multiple technologies apply.
Load rule files when their subject applies:
- rules/security-auth.md for authentication, authorization, sessions, tokens, permissions, or tenant boundaries.
- rules/security-web.md for browser, frontend, cookie, redirect, XSS, or CSRF work.
- rules/security-api.md for HTTP APIs, request handling, serialization, or endpoints.
- rules/security-data.md for databases, persistence, sensitive data, or multi-tenancy.
- rules/security-files.md for files, uploads, archives, paths, processes, IPC, or deserialization.
- rules/security-network.md for network access, URLs, TLS, proxies, or SSRF.
- rules/security-crypto.md for cryptography, secrets, credentials, keys, or tokens.
- rules/security-supply-chain.md for dependencies, packages, plugins, builds, deployments, or CI.
- rules/angular/index.md for Angular work.
- rules/typescript.md for TypeScript work.
- rules/csharp.md for C# or .NET work.
- rules/wpf/index.md for WPF work.
- rules/ui-ux/index.md for UI or UX decisions.
- rules/microsoft.md for Microsoft 365, Azure DevOps, or Teams work.
- rules/orchestration.md for delegation, subagent work, or long-running task state.
- rules/git.md for Git operations.
- rules/refactoring.md for refactoring work.
- rules/definition-of-done.md when validating completion.
- rules/decision-rule.md when requirements are unresolved, behavior is ambiguous, or a meaningful technical choice remains.
BLOCK
codex_rule_loading="${codex_rule_loading}"$'\n\n'"${codex_rule_loading_list%$'\n'}"
for shell in powershell bash; do
mkdir -p "$output/codex-$shell" "$output/claude-$shell"
@@ -178,7 +174,8 @@ for client in codex claude; do
if [ "$client" = codex ]; then
printf 'name = %s\ndescription = %s\ndeveloper_instructions = %s\n' "$(quote_toml "$name")" "$(quote_toml "$description")" "$(quote_toml "$instructions")" > "$agents_output/$name.toml"
else
printf '%s\nname: %s\ndescription: %s\n%s\n\n%s\n' '---' "$name" "$description" '---' "$instructions" > "$agents_output/$name.md"
tools=${agent_tools[$name]:-}
if [ -n "$tools" ]; then printf '%s\nname: %s\ndescription: %s\ntools: %s\n%s\n\n%s\n' '---' "$name" "$description" "$tools" '---' "$instructions" > "$agents_output/$name.md"; else printf '%s\nname: %s\ndescription: %s\n%s\n\n%s\n' '---' "$name" "$description" '---' "$instructions" > "$agents_output/$name.md"; fi
fi
done
done
+1 -1
View File
@@ -6,7 +6,7 @@ $cacheDir = Join-Path $root '.ai-session'
$cachePath = Join-Path $cacheDir 'repository-context.json'
$safeRoot = $root.Replace('\','/')
$head = (& git -c "safe.directory=$safeRoot" -C $root rev-parse HEAD 2>$null).Trim()
$inputs = @('AI-Instructions.md','adapters/plugins.tsv','adapters/claude/rule-skills.tsv','adapters/codex/config/config.toml','adapters/claude/config/settings.json')
$inputs = @('AI-Instructions.md','adapters/plugins.tsv','adapters/claude/rule-skills.tsv','adapters/claude/capabilities.tsv','adapters/prompt-budget-baseline.json','adapters/prompt-budget-baseline.tsv','adapters/codex/config/config.toml','adapters/claude/config/settings.json')
$inputs += @(Get-ChildItem $root -File -Recurse | Where-Object { $_.FullName -notmatch '\\(generated|\.git|node_modules|bin|obj|vendor|\.ai-session)\\' -and $_.Extension -in @('.sln','.csproj','.fsproj','.ts','.tsx','.json','.toml','.yaml','.yml','.cs','.fs','.py','.sh','.ps1') } | ForEach-Object { $_.FullName.Substring($root.Length + 1) } | Sort-Object)
$keyText = $head + "`n" + (($inputs | Sort-Object -Unique | ForEach-Object { $_ + ':' + (Get-FileHash (Join-Path $root $_) -Algorithm SHA256).Hash }) -join "`n")
$key = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($keyText)).TrimEnd('=')