refactor(rules): move loading map to global instructions

This commit is contained in:
fraujulian committed 2026-09-24 18:48:17 +02:00
1 parent a50575dbec
commit aeec4d351c
14 files changed
+113 -192

No files matched your search

-21
View File
@@ -1,21 +0,0 @@
rule_file skill_name trigger
angular rules-angular Angular component, template, or RxJS/signal work
csharp.md rules-csharp C# or .NET implementation, review, or refactoring work
decision-rule.md rules-decision-rule unresolved requirements, ambiguous behavior, or a meaningful technical choice
definition-of-done.md rules-definition-of-done validating that a task is complete or ready for review
git.md rules-git Git operations such as commits, branches, or merges
microsoft.md rules-microsoft Microsoft 365, Azure DevOps, or Teams operations
orchestration.md rules-orchestration delegation, subagent work, or long-running task state
refactoring.md rules-refactoring refactoring existing code without changing behavior
security.md rules-security any software development task: implementing, modifying, debugging, reviewing, testing, or configuring code, scripts, hooks, or infrastructure
security-auth.md rules-security-auth authentication, authorization, sessions, tokens, permissions, or tenant-boundary work
security-web.md rules-security-web web, browser, frontend, cookie, redirect, or XSS/CSRF work
security-api.md rules-security-api HTTP API, request handling, serialization, or endpoint work
security-data.md rules-security-data database, query, persistence, sensitive-data, or multi-tenant work
security-files.md rules-security-files file, upload, archive, path, process, IPC, or deserialization work
security-network.md rules-security-network network, URL fetch, proxy, outbound request, TLS, or SSRF work
security-crypto.md rules-security-crypto cryptography, secrets, credentials, key, or token work
security-supply-chain.md rules-security-supply-chain dependency, package, plugin, build, deployment, or CI supply-chain work
typescript.md rules-typescript TypeScript or modern ECMAScript implementation work
ui-ux rules-ui-ux UI or UX design and interaction decisions
wpf rules-wpf WPF, XAML, or MVVM desktop UI work
1 rule_file skill_name trigger
2 angular rules-angular Angular component, template, or RxJS/signal work
3 csharp.md rules-csharp C# or .NET implementation, review, or refactoring work
4 decision-rule.md rules-decision-rule unresolved requirements, ambiguous behavior, or a meaningful technical choice
5 definition-of-done.md rules-definition-of-done validating that a task is complete or ready for review
6 git.md rules-git Git operations such as commits, branches, or merges
7 microsoft.md rules-microsoft Microsoft 365, Azure DevOps, or Teams operations
8 orchestration.md rules-orchestration delegation, subagent work, or long-running task state
9 refactoring.md rules-refactoring refactoring existing code without changing behavior
10 security.md rules-security any software development task: implementing, modifying, debugging, reviewing, testing, or configuring code, scripts, hooks, or infrastructure
11 security-auth.md rules-security-auth authentication, authorization, sessions, tokens, permissions, or tenant-boundary work
12 security-web.md rules-security-web web, browser, frontend, cookie, redirect, or XSS/CSRF work
13 security-api.md rules-security-api HTTP API, request handling, serialization, or endpoint work
14 security-data.md rules-security-data database, query, persistence, sensitive-data, or multi-tenant work
15 security-files.md rules-security-files file, upload, archive, path, process, IPC, or deserialization work
16 security-network.md rules-security-network network, URL fetch, proxy, outbound request, TLS, or SSRF work
17 security-crypto.md rules-security-crypto cryptography, secrets, credentials, key, or token work
18 security-supply-chain.md rules-security-supply-chain dependency, package, plugin, build, deployment, or CI supply-chain work
19 typescript.md rules-typescript TypeScript or modern ECMAScript implementation work
20 ui-ux rules-ui-ux UI or UX design and interaction decisions
21 wpf rules-wpf WPF, XAML, or MVVM desktop UI work
+1 -1
View File
@@ -2,7 +2,7 @@
Codex receives a generated global `AGENTS.md`, agent TOML files, skills, rule files, and a `config.toml` adapter. It keeps workspace sandboxing, automatic approval review, `agents.enabled = true`, `agents.max_concurrent_threads_per_session = 5`, and `agents.max_depth = 1`. Claude Code receives a generated global `CLAUDE.md`, agent Markdown files, skills, and `settings.json` with `auto` permissions, explicit `Agent` permission, and five-operation concurrency. Installation resolves user paths at runtime; the repository contains no machine-specific paths. Codex receives a generated global `AGENTS.md`, agent TOML files, skills, rule files, and a `config.toml` adapter. It keeps workspace sandboxing, automatic approval review, `agents.enabled = true`, `agents.max_concurrent_threads_per_session = 5`, and `agents.max_depth = 1`. Claude Code receives a generated global `CLAUDE.md`, agent Markdown files, skills, and `settings.json` with `auto` permissions, explicit `Agent` permission, and five-operation concurrency. Installation resolves user paths at runtime; the repository contains no machine-specific paths.
Both clients embed `shared/rules/general.md` in their global instructions. Codex loads applicable focused rules from `rules/`; Claude generates them as skills from the shared `adapters/rule-skills.tsv` manifest. Angular, WPF, and UI/UX rules have small entry points with references loaded by topic. `security.md` is the baseline for programming tasks; focused security rules cover authentication, web, API, data, files, network, cryptography, and supply-chain work. Both clients embed `shared/rules/general.md` and the rule-loading conditions from `shared/global-instructions.md`. Codex loads applicable focused rules from `rules/`; Claude generates a matching skill for each focused rule file or rule directory. Angular, WPF, and UI/UX rules have small entry points with references loaded by topic. `security.md` is the baseline for programming tasks; focused security rules cover authentication, web, API, data, files, network, cryptography, and supply-chain work.
The Codex adapter sets `agents.max_depth = 1` and does not enable `features.multi_agent_v2`. Shared instructions also prohibit subagents from creating further subagents. Agent roles are optional, not stages that every task must run. The Codex adapter sets `agents.max_depth = 1` and does not enable `features.multi_agent_v2`. Shared instructions also prohibit subagents from creating further subagents. Agent roles are optional, not stages that every task must run.
+1 -1
View File
@@ -4,7 +4,7 @@ Shared definitions are the source of truth. Rebuild after every semantic change.
## Add a rule ## Add a rule
Create a focused Markdown file in `shared/rules/`. For a rule that applies only to a language, framework, tool, or change area, add one row to `adapters/rule-skills.tsv`. The build uses that manifest to generate both Claude's rule skill and Codex's rule-loading condition. Put always-applicable guidance in `general.md`, which the build embeds for both clients; a new rule file is not automatically embedded. Create a focused Markdown file in `shared/rules/` and add its loading condition to `shared/global-instructions.md`. Claude rule skills are generated from the rule filenames; Codex loads the referenced rule files directly. Put always-applicable guidance in `general.md`, which the build embeds for both clients; a new rule file is not automatically embedded.
## Add a skill ## Add a skill
+4 -4
View File
@@ -14,10 +14,10 @@ The current structure is organized by purpose:
The files intentionally define only a small baseline. Detailed personal workflows and standards can be added later without changing the structure. The files intentionally define only a small baseline. Detailed personal workflows and standards can be added later without changing the structure.
Claude's generated package additionally has `skills/rules/`: one skill per technology- or situation-specific rule in Claude's generated package additionally has `skills/rules/`: one skill per focused rule file or directory in
`shared/rules/` (driven by the shared `adapters/rule-skills.tsv` manifest), so the full rule text loads only when Claude invokes the `shared/rules/`. The rule-to-skill loading conditions live in `shared/global-instructions.md`, so full rule text loads
matching skill instead of sitting permanently in context. This category does not exist for Codex, which keeps loading only when Claude invokes a matching skill instead of sitting permanently in context. Codex reads the matching rule
applicable focused rules as plain files instead. Both clients embed the general rules in their global instructions. files directly. Both clients embed the general rules in their global instructions.
Split Angular, WPF, and UI/UX rules load supporting references only for the topic being worked on. See [Configuration](configuration.md). Split Angular, WPF, and UI/UX rules load supporting references only for the topic being worked on. See [Configuration](configuration.md).
Skills guide a task when needed; their availability does not require a full review, build, or test suite after every Skills guide a task when needed; their availability does not require a full review, build, or test suite after every
+19 -54
View File
@@ -5,7 +5,6 @@ $root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
$shared = Join-Path $root 'shared' $shared = Join-Path $root 'shared'
$output = Join-Path $root 'generated' $output = Join-Path $root 'generated'
$pluginManifest = Join-Path $root 'adapters/plugins.tsv' $pluginManifest = Join-Path $root 'adapters/plugins.tsv'
$ruleSkillManifest = Join-Path $root 'adapters/rule-skills.tsv'
$capabilityManifest = Join-Path $root 'adapters/claude/capabilities.tsv' $capabilityManifest = Join-Path $root 'adapters/claude/capabilities.tsv'
if (-not (Test-Path -LiteralPath $pluginManifest)) { throw "Missing plugin manifest: $pluginManifest" } if (-not (Test-Path -LiteralPath $pluginManifest)) { throw "Missing plugin manifest: $pluginManifest" }
@@ -20,31 +19,12 @@ foreach ($pluginEntry in $pluginEntries) {
$pluginNames += $pluginEntry.name $pluginNames += $pluginEntry.name
} }
if (-not (Test-Path -LiteralPath $ruleSkillManifest)) { throw "Missing rule-skill manifest: $ruleSkillManifest" }
if (-not (Test-Path -LiteralPath $capabilityManifest)) { throw "Missing capability manifest: $capabilityManifest" } 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 = @()
foreach ($entry in $ruleSkillEntries) {
foreach ($field in @('rule_file','skill_name','trigger')) {
if ([string]::IsNullOrWhiteSpace($entry.$field)) { throw "Missing $field in rule-skill manifest row for $($entry.rule_file)." }
}
if (-not (Test-Path -LiteralPath (Join-Path $shared "rules/$($entry.rule_file)"))) { throw "Rule-skill manifest references a missing rule file: $($entry.rule_file)" }
if ($ruleSkillNames -contains $entry.skill_name) { throw "Duplicate rule-skill name in manifest: $($entry.skill_name)" }
$ruleSkillNames += $entry.skill_name
}
$agentTools = @{} $agentTools = @{}
foreach ($entry in @(Import-Csv -LiteralPath $capabilityManifest -Delimiter ([char]9))) { 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.' } if ([string]::IsNullOrWhiteSpace($entry.role) -or [string]::IsNullOrWhiteSpace($entry.tools)) { throw 'Invalid capability manifest row.' }
$agentTools[$entry.role] = $entry.tools $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 Remove-Item $output -Recurse -Force -ErrorAction SilentlyContinue
New-Item $output -ItemType Directory -Force | Out-Null New-Item $output -ItemType Directory -Force | Out-Null
@@ -104,22 +84,6 @@ foreach ($agentDir in $sourceAgentDirs) {
$agentNames += $name $agentNames += $name
} }
# Codex: general.md is embedded directly into AGENTS.md, the same way Claude embeds
# it into CLAUDE.md, instead of only being referenced by path — guaranteed present
# either way, and safe even if a subagent's AGENTS.md inheritance is not guaranteed.
$codexRuleLoading = @'
General rules are embedded below.
When programming, always load and apply `rules/security.md`. This includes implementing, modifying, debugging, reviewing, testing, and configuring software, scripts, hooks, infrastructure, and integrations.
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:
__CODEX_RULE_LOADING__
'@
$codexRuleLoading = $codexRuleLoading.Replace('__CODEX_RULE_LOADING__', $codexRuleLoadingList)
foreach ($shell in @('powershell','bash')) { foreach ($shell in @('powershell','bash')) {
New-Item (Join-Path $output "codex-$shell") -ItemType Directory -Force | Out-Null New-Item (Join-Path $output "codex-$shell") -ItemType Directory -Force | Out-Null
New-Item (Join-Path $output "claude-$shell") -ItemType Directory -Force | Out-Null New-Item (Join-Path $output "claude-$shell") -ItemType Directory -Force | Out-Null
@@ -130,36 +94,37 @@ foreach ($shell in @('powershell','bash')) {
Copy-Directory (Join-Path $shared 'hooks') (Join-Path $output "claude-$shell/hooks") Copy-Directory (Join-Path $shared 'hooks') (Join-Path $output "claude-$shell/hooks")
Copy-Directory (Join-Path $shared 'statusline') (Join-Path $output "claude-$shell/statusline") Copy-Directory (Join-Path $shared 'statusline') (Join-Path $output "claude-$shell/statusline")
$claudeRulesDir = Join-Path $output "claude-$shell/rules" $rulesRoot = Join-Path $shared 'rules'
New-Item $claudeRulesDir -ItemType Directory -Force | Out-Null $ruleSkills = @()
foreach ($ruleFile in Get-ChildItem $rulesRoot -File -Filter '*.md' | Where-Object Name -ne 'general.md') {
$claudeRuleSkillLines = @() $ruleSkills += [pscustomobject]@{ Name = "rules-$($ruleFile.BaseName)"; Body = $ruleFile.FullName; References = $null }
foreach ($entry in $ruleSkillEntries) { }
$ruleSourcePath = Join-Path $shared "rules/$($entry.rule_file)" foreach ($ruleDirectory in Get-ChildItem $rulesRoot -Directory) {
$isDirectory = Test-Path -LiteralPath $ruleSourcePath -PathType Container $ruleBody = Join-Path $ruleDirectory.FullName 'index.md'
$ruleBodyPath = if ($isDirectory) { Join-Path $ruleSourcePath 'index.md' } else { $ruleSourcePath } if (Test-Path -LiteralPath $ruleBody) {
$skillDir = Join-Path $output "claude-$shell/skills/rules/$($entry.skill_name)" $ruleSkills += [pscustomobject]@{ Name = "rules-$($ruleDirectory.Name)"; Body = $ruleBody; References = (Join-Path $ruleDirectory.FullName 'references') }
}
}
foreach ($ruleSkill in $ruleSkills | Sort-Object Name) {
$skillDir = Join-Path $output "claude-$shell/skills/rules/$($ruleSkill.Name)"
New-Item $skillDir -ItemType Directory -Force | Out-Null New-Item $skillDir -ItemType Directory -Force | Out-Null
$ruleContent = Get-Content $ruleBodyPath -Raw $ruleContent = Get-Content $ruleSkill.Body -Raw
$skillBody = "---`r`nname: $($entry.skill_name)`r`ndescription: $(Quote-Toml "Use for $($entry.trigger).")`r`n---`r`n`r`n$ruleContent" $skillBody = "---`r`nname: $($ruleSkill.Name)`r`ndescription: $(Quote-Toml 'Use when its matching rule condition in global instructions applies.')`r`n---`r`n`r`n$ruleContent"
Set-Content (Join-Path $skillDir 'SKILL.md') $skillBody -Encoding UTF8 Set-Content (Join-Path $skillDir 'SKILL.md') $skillBody -Encoding UTF8
if ($isDirectory) { if ($ruleSkill.References -and (Test-Path -LiteralPath $ruleSkill.References)) {
$referencesSource = Join-Path $ruleSourcePath 'references' Copy-Directory $ruleSkill.References (Join-Path $skillDir 'references')
if (Test-Path -LiteralPath $referencesSource) { Copy-Directory $referencesSource (Join-Path $skillDir 'references') }
} }
$claudeRuleSkillLines += "- $($entry.skill_name) for $($entry.trigger)."
} }
$claudeRuleLoading = "General rules are embedded below.`r`n`r`nDetect the languages, frameworks, tools, and change areas from the repository and the requested work. Invoke every matching rule skill before editing. Invoke all matching rule skills when multiple technologies apply.`r`n`r`nInvoke rule skills when their subject applies:`r`n`r`n" + ($claudeRuleSkillLines -join "`r`n")
$sharedTemplate = Get-Content (Join-Path $shared 'global-instructions.md') -Raw $sharedTemplate = Get-Content (Join-Path $shared 'global-instructions.md') -Raw
$generalContent = Get-Content (Join-Path $shared 'rules/general.md') -Raw $generalContent = Get-Content (Join-Path $shared 'rules/general.md') -Raw
$credentialHelperExtension = if ($shell -eq 'powershell') { 'ps1' } else { 'sh' } $credentialHelperExtension = if ($shell -eq 'powershell') { 'ps1' } else { 'sh' }
$agentsContent = $sharedTemplate.Replace('__RULE_LOADING__', $codexRuleLoading).Replace('__CLIENT__', 'codex').Replace('__SHELL__', $credentialHelperExtension) $agentsContent = $sharedTemplate.Replace('__CLIENT__', 'codex').Replace('__SHELL__', $credentialHelperExtension)
$agentsContent = $agentsContent.TrimEnd() + "`r`n`r`n---`r`n`r`n$generalContent" $agentsContent = $agentsContent.TrimEnd() + "`r`n`r`n---`r`n`r`n$generalContent"
Set-Content (Join-Path $output "codex-$shell/AGENTS.md") $agentsContent -Encoding UTF8 Set-Content (Join-Path $output "codex-$shell/AGENTS.md") $agentsContent -Encoding UTF8
$claudeContent = $sharedTemplate.Replace('__RULE_LOADING__', $claudeRuleLoading).Replace('__CLIENT__', 'claude').Replace('__SHELL__', $credentialHelperExtension) $claudeContent = $sharedTemplate.Replace('__CLIENT__', 'claude').Replace('__SHELL__', $credentialHelperExtension)
$claudeContent = $claudeContent.TrimEnd() + "`r`n`r`n---`r`n`r`n$generalContent" $claudeContent = $claudeContent.TrimEnd() + "`r`n`r`n---`r`n`r`n$generalContent"
Set-Content (Join-Path $output "claude-$shell/CLAUDE.md") $claudeContent -Encoding UTF8 Set-Content (Join-Path $output "claude-$shell/CLAUDE.md") $claudeContent -Encoding UTF8
+13 -82
View File
@@ -12,50 +12,9 @@ root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)
shared="$root/shared" shared="$root/shared"
output="$root/generated" output="$root/generated"
plugin_manifest="$root/adapters/plugins.tsv" plugin_manifest="$root/adapters/plugins.tsv"
rule_skill_manifest="$root/adapters/rule-skills.tsv"
capability_manifest="$root/adapters/claude/capabilities.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
plugin_count=0
while IFS=$'\t' read -r name _ claude_plugin _ codex_plugin; do
[ "$name" != name ] || continue
[ -n "$name" ] || continue
plugin_count=$((plugin_count + 1))
[ -n "$claude_plugin" ] || { printf 'Missing claude_plugin for plugin %s.\n' "$name" >&2; exit 1; }
[ -n "$codex_plugin" ] || { printf 'Missing codex_plugin for plugin %s.\n' "$name" >&2; exit 1; }
[ -z "${plugin_seen[$name]+x}" ] || { printf 'Duplicate plugin name in manifest: %s\n' "$name" >&2; exit 1; }
plugin_seen[$name]=1
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; } [ -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=()
rule_skill_triggers=()
while IFS=$'\t' read -r rule_file skill_name trigger; do
trigger=${trigger%$'\r'}
[ "$rule_file" != rule_file ] || continue
[ -n "$rule_file" ] || continue
[ -n "$skill_name" ] || { printf 'Missing skill_name in rule-skill manifest row for %s.\n' "$rule_file" >&2; exit 1; }
[ -n "$trigger" ] || { printf 'Missing trigger in rule-skill manifest row for %s.\n' "$rule_file" >&2; exit 1; }
[ -f "$shared/rules/$rule_file" ] || [ -d "$shared/rules/$rule_file" ] || { printf 'Rule-skill manifest references a missing rule file: %s\n' "$rule_file" >&2; exit 1; }
[ -z "${rule_skill_seen[$skill_name]+x}" ] || { printf 'Duplicate rule-skill name in manifest: %s\n' "$skill_name" >&2; exit 1; }
rule_skill_seen[$skill_name]=1
rule_skill_files+=("$rule_file")
rule_skill_names+=("$skill_name")
rule_skill_triggers+=("$trigger")
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 declare -A agent_tools
while IFS=$'\t' read -r role tools; do while IFS=$'\t' read -r role tools; do
[ "$role" != role ] || continue [ "$role" != role ] || continue
@@ -101,20 +60,6 @@ for agent_dir in "${agent_dirs[@]}"; do
agent_seen[$name]=1 agent_seen[$name]=1
done done
# Codex: general.md is embedded directly into AGENTS.md, the same way Claude embeds
# it into CLAUDE.md, instead of only being referenced by path — guaranteed present
# either way, and safe even if a subagent's AGENTS.md inheritance is not guaranteed.
read -r -d '' codex_rule_loading <<'BLOCK' || true
General rules are embedded below.
When programming, always load and apply `rules/security.md`. This includes implementing, modifying, debugging, reviewing, testing, and configuring software, scripts, hooks, infrastructure, and integrations.
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:
BLOCK
codex_rule_loading="${codex_rule_loading}"$'\n\n'"${codex_rule_loading_list%$'\n'}"
for shell in powershell bash; do for shell in powershell bash; do
mkdir -p "$output/codex-$shell" "$output/claude-$shell" mkdir -p "$output/codex-$shell" "$output/claude-$shell"
copy_directory "$shared/skills" "$output/codex-$shell/skills" copy_directory "$shared/skills" "$output/codex-$shell/skills"
@@ -124,45 +69,31 @@ copy_directory "$shared/hooks" "$output/codex-$shell/hooks"
copy_directory "$shared/hooks" "$output/claude-$shell/hooks" copy_directory "$shared/hooks" "$output/claude-$shell/hooks"
copy_directory "$shared/statusline" "$output/claude-$shell/statusline" copy_directory "$shared/statusline" "$output/claude-$shell/statusline"
mkdir -p "$output/claude-$shell/rules" while IFS= read -r rule_path; do
case "$rule_path" in
claude_rule_loading_list="" general.md) continue ;;
for i in "${!rule_skill_names[@]}"; do */index.md) skill_name="rules-${rule_path%/index.md}"; rule_body="$shared/rules/$rule_path"; references_dir="${rule_body%/index.md}/references" ;;
rule_file=${rule_skill_files[$i]} *.md) skill_name="rules-${rule_path%.md}"; rule_body="$shared/rules/$rule_path"; references_dir='' ;;
skill_name=${rule_skill_names[$i]} *) continue ;;
trigger=${rule_skill_triggers[$i]} esac
rule_source="$shared/rules/$rule_file"
rule_body="$rule_source"
[ -d "$rule_source" ] && rule_body="$rule_source/index.md"
skill_dir="$output/claude-$shell/skills/rules/$skill_name" skill_dir="$output/claude-$shell/skills/rules/$skill_name"
mkdir -p "$skill_dir" mkdir -p "$skill_dir"
{ printf -- '---\nname: %s\ndescription: %s\n---\n\n' "$skill_name" "$(quote_toml "Use for $trigger.")"; cat "$rule_body"; } > "$skill_dir/SKILL.md" { printf -- '---\nname: %s\ndescription: %s\n---\n\n' "$skill_name" "$(quote_toml 'Use when its matching rule condition in global instructions applies.')"; cat "$rule_body"; } > "$skill_dir/SKILL.md"
if [ -d "$rule_source/references" ]; then if [ -n "$references_dir" ] && [ -d "$references_dir" ]; then
copy_directory "$rule_source/references" "$skill_dir/references" copy_directory "$references_dir" "$skill_dir/references"
fi fi
claude_rule_loading_list="${claude_rule_loading_list}- ${skill_name} for ${trigger}. done < <(find "$shared/rules" -mindepth 1 -maxdepth 2 -type f -name '*.md' -printf '%P\n' | sort)
"
done
claude_rule_loading="General rules are embedded below.
Detect the languages, frameworks, tools, and change areas from the repository and the requested work. Invoke every matching rule skill before editing. Invoke all matching rule skills when multiple technologies apply.
Invoke rule skills when their subject applies:
${claude_rule_loading_list%$'\n'}"
shared_template=$(<"$shared/global-instructions.md") shared_template=$(<"$shared/global-instructions.md")
general_content=$(<"$shared/rules/general.md") general_content=$(<"$shared/rules/general.md")
credential_helper_extension='sh' credential_helper_extension='sh'
[ "$shell" != powershell ] || credential_helper_extension='ps1' [ "$shell" != powershell ] || credential_helper_extension='ps1'
agents_content=${shared_template//__RULE_LOADING__/$codex_rule_loading} agents_content=${shared_template//__CLIENT__/codex}
agents_content=${agents_content//__CLIENT__/codex}
agents_content=${agents_content//__SHELL__/$credential_helper_extension} agents_content=${agents_content//__SHELL__/$credential_helper_extension}
printf '%s\n\n---\n\n%s\n' "${agents_content%$'\n'}" "$general_content" > "$output/codex-$shell/AGENTS.md" printf '%s\n\n---\n\n%s\n' "${agents_content%$'\n'}" "$general_content" > "$output/codex-$shell/AGENTS.md"
claude_content=${shared_template//__RULE_LOADING__/$claude_rule_loading} claude_content=${shared_template//__CLIENT__/claude}
claude_content=${claude_content//__CLIENT__/claude}
claude_content=${claude_content//__SHELL__/$credential_helper_extension} claude_content=${claude_content//__SHELL__/$credential_helper_extension}
printf '%s\n\n---\n\n%s\n' "${claude_content%$'\n'}" "$general_content" > "$output/claude-$shell/CLAUDE.md" printf '%s\n\n---\n\n%s\n' "${claude_content%$'\n'}" "$general_content" > "$output/claude-$shell/CLAUDE.md"
+2 -2
View File
@@ -22,8 +22,8 @@ if (Get-Command jq -ErrorAction SilentlyContinue) { Result 'PASS' 'jq available
$sourceAgentCount = @(Get-ChildItem (Join-Path $root 'shared/agents') -Directory -ErrorAction SilentlyContinue).Count $sourceAgentCount = @(Get-ChildItem (Join-Path $root 'shared/agents') -Directory -ErrorAction SilentlyContinue).Count
$sourceSkillCount = @(Get-ChildItem (Join-Path $root 'shared/skills') -Filter 'SKILL.md' -Recurse -ErrorAction SilentlyContinue).Count $sourceSkillCount = @(Get-ChildItem (Join-Path $root 'shared/skills') -Filter 'SKILL.md' -Recurse -ErrorAction SilentlyContinue).Count
$ruleSkillManifestPath = Join-Path $root 'adapters/rule-skills.tsv' $rulesRoot = Join-Path $root 'shared/rules'
$ruleSkillCount = if (Test-Path -LiteralPath $ruleSkillManifestPath) { @(Import-Csv -LiteralPath $ruleSkillManifestPath -Delimiter ([char]9)).Count } else { 0 } $ruleSkillCount = @((Get-ChildItem $rulesRoot -File -Filter '*.md' | Where-Object Name -ne 'general.md')).Count + @((Get-ChildItem $rulesRoot -Directory | Where-Object { Test-Path (Join-Path $_.FullName 'index.md') })).Count
foreach ($shell in @('powershell','bash')) { foreach ($shell in @('powershell','bash')) {
if (Test-Path (Join-Path $root "generated/codex-$shell/AGENTS.md")) { Result 'PASS' "generated output present ($shell)" } else { Result 'FAIL' "generated output missing ($shell); run build" } if (Test-Path (Join-Path $root "generated/codex-$shell/AGENTS.md")) { Result 'PASS' "generated output present ($shell)" } else { Result 'FAIL' "generated output missing ($shell); run build" }
+2 -3
View File
@@ -33,9 +33,8 @@ command -v jq >/dev/null && result PASS 'jq available for Claude Bash status lin
source_agent_count=$(find "$root/shared/agents" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l) source_agent_count=$(find "$root/shared/agents" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l)
source_skill_count=$(find "$root/shared/skills" -name SKILL.md 2>/dev/null | wc -l) source_skill_count=$(find "$root/shared/skills" -name SKILL.md 2>/dev/null | wc -l)
rule_skill_manifest="$root/adapters/rule-skills.tsv" rule_skill_count=$(find "$root/shared/rules" -mindepth 1 -maxdepth 1 -type f -name '*.md' ! -name general.md | wc -l)
rule_skill_count=0 rule_skill_count=$((rule_skill_count + $(find "$root/shared/rules" -mindepth 2 -maxdepth 2 -type f -name index.md | wc -l)))
[ -f "$rule_skill_manifest" ] && rule_skill_count=$(($(wc -l < "$rule_skill_manifest") - 1))
for shell in powershell bash; do for shell in powershell bash; do
[ -f "$root/generated/codex-$shell/AGENTS.md" ] && result PASS "generated output present ($shell)" || result FAIL "generated output missing ($shell); run build" [ -f "$root/generated/codex-$shell/AGENTS.md" ] && result PASS "generated output present ($shell)" || result FAIL "generated output missing ($shell); run build"
+1 -2
View File
@@ -12,12 +12,11 @@ function Tokens([int64]$bytes) { return [int][Math]::Ceiling($bytes / 4.0) }
$claude = (Get-Item (Join-Path $generated 'claude-bash/CLAUDE.md')).Length $claude = (Get-Item (Join-Path $generated 'claude-bash/CLAUDE.md')).Length
$codex = (Get-Item (Join-Path $generated 'codex-bash/AGENTS.md')).Length $codex = (Get-Item (Join-Path $generated 'codex-bash/AGENTS.md')).Length
$skills = Measure-Tree (Join-Path $generated 'claude-bash/skills') $skills = Measure-Tree (Join-Path $generated 'claude-bash/skills')
$triggers = (Get-Item (Join-Path $root 'adapters/rule-skills.tsv')).Length
$lazyItem = Get-ChildItem (Join-Path $root 'shared/rules') -File -Recurse | Sort-Object Length -Descending | Select-Object -First 1 $lazyItem = Get-ChildItem (Join-Path $root 'shared/rules') -File -Recurse | Sort-Object Length -Descending | Select-Object -First 1
$lazy = if ($lazyItem) { [int64]$lazyItem.Length } else { 0 } $lazy = if ($lazyItem) { [int64]$lazyItem.Length } else { 0 }
$baseline = Join-Path $root 'adapters/prompt-budget-baseline.json' $baseline = Join-Path $root 'adapters/prompt-budget-baseline.json'
$old = if (Test-Path $baseline) { Get-Content $baseline -Raw | ConvertFrom-Json } else { $null } $old = if (Test-Path $baseline) { Get-Content $baseline -Raw | ConvertFrom-Json } else { $null }
$rows = @([pscustomobject]@{Name='Claude permanent context';Bytes=$claude;Tokens=(Tokens $claude)},[pscustomobject]@{Name='Codex permanent context';Bytes=$codex;Tokens=(Tokens $codex)},[pscustomobject]@{Name='Claude skill files (lazy)';Bytes=$skills;Tokens=(Tokens $skills)},[pscustomobject]@{Name='Rule trigger metadata';Bytes=$triggers;Tokens=(Tokens $triggers)},[pscustomobject]@{Name='Largest lazy rule';Bytes=$lazy;Tokens=(Tokens $lazy)}) $rows = @([pscustomobject]@{Name='Claude permanent context';Bytes=$claude;Tokens=(Tokens $claude)},[pscustomobject]@{Name='Codex permanent context';Bytes=$codex;Tokens=(Tokens $codex)},[pscustomobject]@{Name='Claude skill files (lazy)';Bytes=$skills;Tokens=(Tokens $skills)},[pscustomobject]@{Name='Largest lazy rule';Bytes=$lazy;Tokens=(Tokens $lazy)})
$fail = $false $fail = $false
$baselineValues = @{} $baselineValues = @{}
if ($old) { if ($old) {
+2 -2
View File
@@ -8,7 +8,7 @@ generated="$root/generated"
bytes() { wc -c < "$1" | tr -d ' '; } bytes() { wc -c < "$1" | tr -d ' '; }
tree_bytes() { find "$1" -type f -printf '%s\n' 2>/dev/null | awk '{s+=$1} END {print s+0}'; } tree_bytes() { find "$1" -type f -printf '%s\n' 2>/dev/null | awk '{s+=$1} END {print s+0}'; }
tokens() { awk -v b="$1" 'BEGIN { printf "%d", (b + 3) / 4 }'; } tokens() { awk -v b="$1" 'BEGIN { printf "%d", (b + 3) / 4 }'; }
claude=$(bytes "$generated/claude-bash/CLAUDE.md"); codex=$(bytes "$generated/codex-bash/AGENTS.md"); skills=$(tree_bytes "$generated/claude-bash/skills"); triggers=$(bytes "$root/adapters/rule-skills.tsv"); lazy=$(find "$root/shared/rules" -type f -printf '%s %p\n' | sort -nr | awk 'NR==1 {print $1+0}'); fail=false claude=$(bytes "$generated/claude-bash/CLAUDE.md"); codex=$(bytes "$generated/codex-bash/AGENTS.md"); skills=$(tree_bytes "$generated/claude-bash/skills"); lazy=$(find "$root/shared/rules" -type f -printf '%s %p\n' | sort -nr | awk 'NR==1 {print $1+0}'); fail=false
claude_baseline=0; codex_baseline=0; claude_absolute=0; codex_absolute=0 claude_baseline=0; codex_baseline=0; claude_absolute=0; codex_absolute=0
if [ -f "$root/adapters/prompt-budget-baseline.tsv" ]; then if [ -f "$root/adapters/prompt-budget-baseline.tsv" ]; then
while IFS=$'\t' read -r name old absolute; do while IFS=$'\t' read -r name old absolute; do
@@ -20,7 +20,7 @@ if [ -f "$root/adapters/prompt-budget-baseline.tsv" ]; then
if awk -v n="$current" -v a="$absolute" 'BEGIN { exit !(a > 0 && n > a) }'; then printf 'Prompt budget absolute limit failed: %s %s bytes (absolute limit %s).\n' "$name" "$current" "$absolute" >&2; fail=true; fi if awk -v n="$current" -v a="$absolute" 'BEGIN { exit !(a > 0 && n > a) }'; then printf 'Prompt budget absolute limit failed: %s %s bytes (absolute limit %s).\n' "$name" "$current" "$absolute" >&2; fail=true; fi
done < "$root/adapters/prompt-budget-baseline.tsv" done < "$root/adapters/prompt-budget-baseline.tsv"
fi fi
if [ "$summary" = true ]; then printf 'Prompt Budget\nClaude permanent context %8s bytes baseline %s relative 115%% absolute %s\nCodex permanent context %8s bytes baseline %s relative 115%% absolute %s\nClaude skill files (lazy) %8s bytes %6s tokens\nRule trigger metadata %8s bytes %6s tokens\nLargest lazy rule %8s bytes %6s tokens\n' "$claude" "$claude_baseline" "$claude_absolute" "$codex" "$codex_baseline" "$codex_absolute" "$skills" "$(tokens "$skills")" "$triggers" "$(tokens "$triggers")" "$lazy" "$(tokens "$lazy")"; else printf 'Prompt budget: Claude %s bytes, Codex %s bytes, skills %s bytes, triggers %s bytes, largest lazy rule %s bytes\n' "$claude" "$codex" "$skills" "$triggers" "$lazy"; fi if [ "$summary" = true ]; then printf 'Prompt Budget\nClaude permanent context %8s bytes baseline %s relative 115%% absolute %s\nCodex permanent context %8s bytes baseline %s relative 115%% absolute %s\nClaude skill files (lazy) %8s bytes %6s tokens\nLargest lazy rule %8s bytes %6s tokens\n' "$claude" "$claude_baseline" "$claude_absolute" "$codex" "$codex_baseline" "$codex_absolute" "$skills" "$(tokens "$skills")" "$lazy" "$(tokens "$lazy")"; else printf 'Prompt budget: Claude %s bytes, Codex %s bytes, skills %s bytes, largest lazy rule %s bytes\n' "$claude" "$codex" "$skills" "$lazy"; fi
python3 "$root/scripts/lib/prompt-inventory.py" --home "${HOME:?HOME is required}" python3 "$root/scripts/lib/prompt-inventory.py" --home "${HOME:?HOME is required}"
$fail && { printf 'Prompt budget: permanent context increased by more than 15 percent.\n' >&2; exit 1; } $fail && { printf 'Prompt budget: permanent context increased by more than 15 percent.\n' >&2; exit 1; }
exit 0 exit 0
+1 -1
View File
@@ -6,7 +6,7 @@ $cacheDir = Join-Path $root '.ai-session'
$cachePath = Join-Path $cacheDir 'repository-context.json' $cachePath = Join-Path $cacheDir 'repository-context.json'
$safeRoot = $root.Replace('\','/') $safeRoot = $root.Replace('\','/')
$head = (& git -c "safe.directory=$safeRoot" -C $root rev-parse HEAD 2>$null).Trim() $head = (& git -c "safe.directory=$safeRoot" -C $root rev-parse HEAD 2>$null).Trim()
$inputs = @('AI-Instructions.md','adapters/plugins.tsv','adapters/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 = @('AI-Instructions.md','shared/global-instructions.md','adapters/plugins.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) $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") $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('=') $key = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($keyText)).TrimEnd('=')
+21 -8
View File
@@ -4,19 +4,25 @@ $ErrorActionPreference = 'Stop'
$root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent $root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
$sourceAgentCount = @(Get-ChildItem (Join-Path $root 'shared/agents') -Directory).Count $sourceAgentCount = @(Get-ChildItem (Join-Path $root 'shared/agents') -Directory).Count
$sourceSkillCount = @(Get-ChildItem (Join-Path $root 'shared/skills') -Filter 'SKILL.md' -Recurse).Count $sourceSkillCount = @(Get-ChildItem (Join-Path $root 'shared/skills') -Filter 'SKILL.md' -Recurse).Count
$ruleSkillCount = @(Import-Csv -LiteralPath (Join-Path $root 'adapters/rule-skills.tsv') -Delimiter ([char]9)).Count $rulesRoot = Join-Path $root 'shared/rules'
$ruleSources = @()
foreach ($ruleFile in Get-ChildItem $rulesRoot -File -Filter '*.md' | Where-Object Name -ne 'general.md') {
$ruleSources += [pscustomobject]@{ Path = $ruleFile.Name; Skill = "rules-$($ruleFile.BaseName)" }
}
foreach ($ruleDirectory in Get-ChildItem $rulesRoot -Directory) {
if (Test-Path (Join-Path $ruleDirectory.FullName 'index.md')) { $ruleSources += [pscustomobject]@{ Path = "$($ruleDirectory.Name)/index.md"; Skill = "rules-$($ruleDirectory.Name)" } }
}
$ruleSkillCount = $ruleSources.Count
$globalInstructions = Get-Content (Join-Path $root 'shared/global-instructions.md') -Raw
foreach ($ruleSource in $ruleSources) {
$ruleInstruction = [char]96 + "rules/$($ruleSource.Path)" + [char]96 + ' / ' + [char]96 + $ruleSource.Skill + [char]96
if (-not $globalInstructions.Contains($ruleInstruction)) { throw "Global instructions are missing rule loading entry: $($ruleSource.Path)" }
}
foreach ($shell in @('powershell','bash')) { foreach ($shell in @('powershell','bash')) {
foreach ($client in @('codex','claude')) { foreach ($client in @('codex','claude')) {
$package = Join-Path $root "generated/$client-$shell" $package = Join-Path $root "generated/$client-$shell"
$file = if ($client -eq 'codex') { 'config.toml' } else { 'settings.json' } $file = if ($client -eq 'codex') { 'config.toml' } else { 'settings.json' }
$content = Get-Content -LiteralPath (Join-Path $package $file) -Raw $content = Get-Content -LiteralPath (Join-Path $package $file) -Raw
if ($client -eq 'codex') {
foreach ($entry in @(Import-Csv (Join-Path $root 'adapters/rule-skills.tsv') -Delimiter ([char]9))) {
$rulePath = $entry.rule_file
if (Test-Path (Join-Path $root "shared/rules/$($entry.rule_file)") -PathType Container) { $rulePath = "$($entry.rule_file)/index.md" }
if ((Get-Content (Join-Path $package 'AGENTS.md') -Raw) -notmatch [regex]::Escape("- rules/$rulePath for $($entry.trigger).")) { throw "Codex rule loading is missing $($entry.rule_file) in $package" }
}
}
if ($client -eq 'claude') { $settings = $content | ConvertFrom-Json } if ($client -eq 'claude') { $settings = $content | ConvertFrom-Json }
if (@(Get-ChildItem "$package/agents" -File).Count -ne $sourceAgentCount) { throw "Missing agents in $package" } if (@(Get-ChildItem "$package/agents" -File).Count -ne $sourceAgentCount) { throw "Missing agents in $package" }
$expectedReviewerShellTool = if ($shell -eq 'powershell') { 'PowerShell' } else { 'Bash' } $expectedReviewerShellTool = if ($shell -eq 'powershell') { 'PowerShell' } else { 'Bash' }
@@ -45,6 +51,13 @@ foreach ($shell in @('powershell','bash')) {
} }
$docFile = if ($client -eq 'codex') { 'AGENTS.md' } else { 'CLAUDE.md' } $docFile = if ($client -eq 'codex') { 'AGENTS.md' } else { 'CLAUDE.md' }
$docContent = Get-Content -LiteralPath (Join-Path $package $docFile) -Raw $docContent = Get-Content -LiteralPath (Join-Path $package $docFile) -Raw
if ($docContent -notmatch [regex]::Escape('rules/security.md')) { throw "$package is missing common rule loading instructions." }
if ($client -eq 'claude') {
foreach ($ruleSource in $ruleSources) {
$skillPath = Join-Path $package "skills/rules/$($ruleSource.Skill)/SKILL.md"
if (-not (Test-Path -LiteralPath $skillPath)) { throw "$package is missing generated skill $($ruleSource.Skill)." }
}
}
if ($docContent -notmatch [regex]::Escape('Use the `mcporter` CLI for MCP services by default.')) { throw "$package must set the MCPorter default" } if ($docContent -notmatch [regex]::Escape('Use the `mcporter` CLI for MCP services by default.')) { throw "$package must set the MCPorter default" }
if ($docContent -notmatch [regex]::Escape('Use a native MCP connection when MCPorter is not active, or when the user explicitly requests it or selects a native MCP plugin in this configuration.')) { throw "$package must allow the selected native MCP route" } if ($docContent -notmatch [regex]::Escape('Use a native MCP connection when MCPorter is not active, or when the user explicitly requests it or selects a native MCP plugin in this configuration.')) { throw "$package must allow the selected native MCP route" }
if (([regex]::Matches($docContent, [regex]::Escape('Apply instructions in this order'))).Count -ne 1) { throw "$client-$shell must embed the priority rule exactly once" } if (([regex]::Matches($docContent, [regex]::Escape('Apply instructions in this order'))).Count -ne 1) { throw "$client-$shell must embed the priority rule exactly once" }
+21 -10
View File
@@ -10,22 +10,23 @@ done
root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd) root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)
source_agent_count=$(find "$root/shared/agents" -mindepth 1 -maxdepth 1 -type d | wc -l) source_agent_count=$(find "$root/shared/agents" -mindepth 1 -maxdepth 1 -type d | wc -l)
source_skill_count=$(find "$root/shared/skills" -name SKILL.md | wc -l) source_skill_count=$(find "$root/shared/skills" -name SKILL.md | wc -l)
rule_skill_count=$(($(wc -l < "$root/adapters/rule-skills.tsv") - 1)) mapfile -t rule_paths < <(find "$root/shared/rules" -mindepth 1 -maxdepth 2 -type f -name '*.md' ! -path '*/references/*' ! -name general.md -printf '%P\n' | sort)
rule_skill_count=${#rule_paths[@]}
global_instructions=$(<"$root/shared/global-instructions.md")
for rule_path in "${rule_paths[@]}"; do
case "$rule_path" in
*/index.md) skill_name="rules-${rule_path%/index.md}" ;;
*.md) skill_name="rules-${rule_path%.md}" ;;
esac
expected_rule="\`rules/$rule_path\` / \`$skill_name\`"
printf '%s' "$global_instructions" | grep -Fq -- "$expected_rule"
done
for shell in powershell bash; do for shell in powershell bash; do
for client in codex claude; do for client in codex claude; do
package="$root/generated/$client-$shell" package="$root/generated/$client-$shell"
file=config.toml file=config.toml
[ "$client" != claude ] || file=settings.json [ "$client" != claude ] || file=settings.json
test -f "$package/$file" test -f "$package/$file"
if [ "$client" = codex ]; then
while IFS=$'\t' read -r rule_file _ trigger; do
[ "$rule_file" = rule_file ] && continue
[ -n "$rule_file" ] || continue
rule_path=$rule_file
[ -d "$root/shared/rules/$rule_file" ] && rule_path="$rule_file/index.md"
grep -Fq -- "- rules/$rule_path for $trigger." "$package/AGENTS.md"
done < "$root/adapters/rule-skills.tsv"
fi
test "$(find "$package/agents" -type f | wc -l)" -eq "$source_agent_count" test "$(find "$package/agents" -type f | wc -l)" -eq "$source_agent_count"
expected_reviewer_shell_tool=Bash expected_reviewer_shell_tool=Bash
[ "$shell" != powershell ] || expected_reviewer_shell_tool=PowerShell [ "$shell" != powershell ] || expected_reviewer_shell_tool=PowerShell
@@ -61,6 +62,16 @@ for shell in powershell bash; do
doc_file=AGENTS.md doc_file=AGENTS.md
[ "$client" != claude ] || doc_file=CLAUDE.md [ "$client" != claude ] || doc_file=CLAUDE.md
doc_content=$(cat "$package/$doc_file") doc_content=$(cat "$package/$doc_file")
printf '%s' "$doc_content" | grep -Fq 'rules/security.md'
if [ "$client" = claude ]; then
for rule_path in "${rule_paths[@]}"; do
case "$rule_path" in
*/index.md) skill_name="rules-${rule_path%/index.md}" ;;
*.md) skill_name="rules-${rule_path%.md}" ;;
esac
test -f "$package/skills/rules/$skill_name/SKILL.md"
done
fi
printf '%s' "$doc_content" | grep -Fq 'Use the `mcporter` CLI for MCP services by default.' printf '%s' "$doc_content" | grep -Fq 'Use the `mcporter` CLI for MCP services by default.'
printf '%s' "$doc_content" | grep -Fq 'Use a native MCP connection when MCPorter is not active, or when the user explicitly requests it or selects a native MCP plugin in this configuration.' printf '%s' "$doc_content" | grep -Fq 'Use a native MCP connection when MCPorter is not active, or when the user explicitly requests it or selects a native MCP plugin in this configuration.'
occurrences=$(printf '%s' "$doc_content" | grep -o 'Apply instructions in this order' | wc -l) occurrences=$(printf '%s' "$doc_content" | grep -o 'Apply instructions in this order' | wc -l)
+25 -1
View File
@@ -6,7 +6,31 @@ Before applying generated instructions, read `~/.my-ai-configuration/instruction
For a tool credential, invoke `~/.my-ai-configuration/bin/with-credential.__SHELL__` with the client, credential key, and child command. It exposes the credential only to that child process. Never print credentials or place them in commands. For a tool credential, invoke `~/.my-ai-configuration/bin/with-credential.__SHELL__` with the client, credential key, and child command. It exposes the credential only to that child process. Never print credentials or place them in commands.
__RULE_LOADING__ When programming, always apply the security baseline: `rules/security.md` / `rules-security` (Codex file / Claude skill). This applies to implementing, modifying, debugging, reviewing, testing, and configuring software, scripts, hooks, infrastructure, and integrations.
Detect languages, frameworks, tools, and change areas from the repository and requested work. Apply every matching rule before editing. For Codex, read the listed rule file. For Claude, invoke the matching `rules-*` skill. Apply all matching rules when multiple areas apply.
Load or invoke rules for these areas:
- Angular component, template, or RxJS/signal work: `rules/angular/index.md` / `rules-angular`.
- C# or .NET implementation, review, or refactoring: `rules/csharp.md` / `rules-csharp`.
- Unresolved requirements, ambiguous behavior, or meaningful technical choices: `rules/decision-rule.md` / `rules-decision-rule`.
- Validating completion or review readiness: `rules/definition-of-done.md` / `rules-definition-of-done`.
- Git operations such as commits, branches, or merges: `rules/git.md` / `rules-git`.
- Microsoft 365, Azure DevOps, or Teams operations: `rules/microsoft.md` / `rules-microsoft`.
- Delegation, subagents, or long-running task state: `rules/orchestration.md` / `rules-orchestration`.
- Refactoring without behavior changes: `rules/refactoring.md` / `rules-refactoring`.
- Authentication, authorization, sessions, tokens, permissions, or tenant boundaries: `rules/security-auth.md` / `rules-security-auth`.
- HTTP APIs, request handling, serialization, or endpoints: `rules/security-api.md` / `rules-security-api`.
- Browser, frontend, cookies, redirects, XSS, or CSRF: `rules/security-web.md` / `rules-security-web`.
- Databases, queries, persistence, sensitive data, or tenant boundaries: `rules/security-data.md` / `rules-security-data`.
- Files, uploads, archives, paths, processes, IPC, or deserialization: `rules/security-files.md` / `rules-security-files`.
- Network access, URLs, proxies, outbound requests, TLS, or SSRF: `rules/security-network.md` / `rules-security-network`.
- Cryptography, secrets, credentials, keys, or tokens: `rules/security-crypto.md` / `rules-security-crypto`.
- Dependencies, packages, plugins, builds, deployments, or CI supply chains: `rules/security-supply-chain.md` / `rules-security-supply-chain`.
- TypeScript or modern ECMAScript implementation: `rules/typescript.md` / `rules-typescript`.
- UI or UX design and interaction: `rules/ui-ux/index.md` / `rules-ui-ux`.
- WPF, XAML, or MVVM desktop UI: `rules/wpf/index.md` / `rules-wpf`.
Use the `mcporter` CLI for MCP services by default. Use a native MCP connection when MCPorter is not active, or when the user explicitly requests it or selects a native MCP plugin in this configuration. If the requested route is unavailable, report the limitation instead of silently switching routes. Use the `mcporter` CLI for MCP services by default. Use a native MCP connection when MCPorter is not active, or when the user explicitly requests it or selects a native MCP plugin in this configuration. If the requested route is unavailable, report the limitation instead of silently switching routes.