Compare commits

...
50 Commits
Author SHA1 Message Date
fraujulian d5c2ca556b docs(rules): clarify just-in-time loading
CI / Capabilities and shell quality (push) Failing after 11s
CI / Python tests and compilation (push) Failing after 8s
CI / Build and inspect generated packages (push) Skipped
CI / Bash integration and installation guards (push) Skipped
CI / Installed client configuration smoke tests (push) Skipped
CI / Qwen retrieval integration (push) Skipped
CI / PowerShell tests on Ubuntu (push) Failing after 15s
CI / CI configuration security checks (push) Successful in 4s
2026-09-24 22:59:17 +02:00
fraujulian 3fcc2aa709 docs(install): explain saved status line choice 2026-09-24 22:59:17 +02:00
fraujulian ee19c64beb feat(install): configure custom status line 2026-09-24 22:58:56 +02:00
fraujulian 0d034f4299 fix(retrieval): invalidate stale model caches 2026-09-24 22:20:30 +02:00
fraujulian 2efa43365e docs(instructions): condense global rules 2026-09-24 22:20:00 +02:00
fraujulian b54c3d61d0 docs(security): strengthen web guidance 2026-09-24 22:04:13 +02:00
fraujulian c086919866 docs(security): strengthen supply chain guidance 2026-09-24 22:04:01 +02:00
fraujulian 6a513de496 docs(security): strengthen network guidance 2026-09-24 22:03:28 +02:00
fraujulian 0041fbe777 docs(security): strengthen file guidance 2026-09-24 22:03:18 +02:00
fraujulian 2c60db173d docs(security): strengthen data guidance 2026-09-24 22:03:06 +02:00
fraujulian 605d0ef64a docs(security): strengthen crypto guidance 2026-09-24 22:02:41 +02:00
fraujulian a96b198e23 docs(security): strengthen auth guidance 2026-09-24 22:02:23 +02:00
fraujulian e5827e7ddb docs(security): strengthen API guidance 2026-09-24 22:02:12 +02:00
fraujulian 56a71a8f2c docs(security): define authorized testing scope 2026-09-24 22:00:53 +02:00
fraujulian 65f99d398e perf(java): add JVM performance guidance 2026-09-24 22:00:29 +02:00
fraujulian 9bf0d1b1e2 feat(java): add modern Java rules 2026-09-24 21:59:59 +02:00
fraujulian 0bdd5f46b3 refactor(rules): group focused security rules 2026-09-24 21:47:59 +02:00
fraujulian 646e552ff1 refactor(rules): split TypeScript guidance by topic 2026-09-24 21:47:29 +02:00
fraujulian 27db155b85 refactor(rules): split C# guidance by topic 2026-09-24 21:46:50 +02:00
fraujulian d4f45a3320 refactor(rules): move UI principles to topic rule 2026-09-24 21:45:33 +02:00
fraujulian ca9f6296d6 refactor(rules): keep WPF index focused 2026-09-24 21:45:25 +02:00
fraujulian c91505a568 refactor(rules): keep Angular index focused 2026-09-24 21:45:15 +02:00
fraujulian 5c25b4fe5a docs(rules): document focused rule directories 2026-09-24 21:44:29 +02:00
fraujulian bb03528337 refactor(rules): route loading by focused topic 2026-09-24 21:43:06 +02:00
fraujulian ce54b34092 test(rules): verify nested rule packages 2026-09-24 21:42:57 +02:00
fraujulian a7a7c3caba build(rules): generate skills from nested topics 2026-09-24 21:42:49 +02:00
fraujulian 67f7c9041e docs(skills): update skill catalog 2026-09-24 19:12:35 +02:00
fraujulian f87db70f78 docs(skill): clarify work item verification triggers 2026-09-24 19:12:30 +02:00
fraujulian 8ace616ea5 docs(skill): clarify regression verification triggers 2026-09-24 19:12:26 +02:00
fraujulian a8d1ea6554 docs(skill): clarify fact verification triggers 2026-09-24 19:12:20 +02:00
fraujulian 2646bfaa8b docs(skill): clarify WPF review triggers 2026-09-24 19:12:16 +02:00
fraujulian bc0fd83e14 docs(skill): clarify UI UX review triggers 2026-09-24 19:12:11 +02:00
fraujulian 7bac5a75c6 docs(skill): clarify security review triggers 2026-09-24 19:12:07 +02:00
fraujulian f3f02a393a docs(skill): clarify performance review triggers 2026-09-24 19:12:02 +02:00
fraujulian 9996eb64a6 docs(skill): clarify EF Core review triggers 2026-09-24 19:11:56 +02:00
fraujulian 2ded5617af docs(skill): clarify code review triggers 2026-09-24 19:11:52 +02:00
fraujulian 34a65d9019 docs(skill): clarify business review triggers 2026-09-24 19:11:46 +02:00
fraujulian f58cffd4d7 docs(skill): clarify API review triggers 2026-09-24 19:11:38 +02:00
fraujulian 2b6985c37f docs(skill): clarify technical research triggers 2026-09-24 19:11:34 +02:00
fraujulian 28fa850dcc docs(skill): clarify repository research triggers 2026-09-24 19:11:29 +02:00
fraujulian d42a70605d docs(skill): clarify work item skill triggers 2026-09-24 19:11:25 +02:00
fraujulian 2e18a66a77 docs(skill): clarify planning skill triggers 2026-09-24 19:11:21 +02:00
fraujulian e5205316ab docs(skill): clarify impact analysis triggers 2026-09-24 19:11:15 +02:00
fraujulian 3e1db3d6f2 docs(skill): clarify unit test triggers 2026-09-24 19:11:09 +02:00
fraujulian 4d06ceb223 docs(skill): clarify SQL skill triggers 2026-09-24 19:11:04 +02:00
fraujulian 3982f4c430 docs(skill): clarify integration test triggers 2026-09-24 19:11:00 +02:00
fraujulian afb9ea6521 docs(skill): clarify pull request review triggers 2026-09-24 19:10:56 +02:00
fraujulian e8fea95774 docs(skill): clarify debugging triggers 2026-09-24 19:10:47 +02:00
fraujulian e3f4e01fea chore(skill): remove answer-pull-request 2026-09-24 19:10:01 +02:00
fraujulian aeec4d351c refactor(rules): move loading map to global instructions 2026-09-24 18:48:17 +02:00
102 changed files with 919 additions and 1075 deletions

No files matched your search

+1 -2
View File
@@ -15,7 +15,7 @@ Keep the architecture as:
- Do not duplicate shared behavior in adapters. - Do not duplicate shared behavior in adapters.
- Treat `generated/` as reproducible build output. - Treat `generated/` as reproducible build output.
- Maintain equivalent support for both Codex and Claude Code. - Maintain equivalent support for both Codex and Claude Code.
- Keep technology-specific rules in separate files under `shared/rules/`. - Keep each language, framework, and library in its own directory under `shared/rules/`, with an index and focused Markdown rules. Keep security in `shared/rules/security/` and cross-cutting rules such as Git and Microsoft directly under `shared/rules/`.
- Generate those rule files beside `AGENTS.md` and `CLAUDE.md`; load them only when their subject applies. - Generate those rule files beside `AGENTS.md` and `CLAUDE.md`; load them only when their subject applies.
## Portability ## Portability
@@ -60,4 +60,3 @@ Write all repository content, generated instructions, scripts, prompts, document
- Never commit changes unless explicitly instructed. - Never commit changes unless explicitly instructed.
- Preserve unrelated user changes. - Preserve unrelated user changes.
+8 -16
View File
@@ -30,7 +30,7 @@ Bash:
## Quickstart ## Quickstart
With the [requirements](#requirements) installed, clone the repository, build it, then install it. With the [requirements](#requirements) installed, clone the repository, build it, then install it.
Stop if any command fails. Installation asks for the shell, client, CLI update, Flashbang, Qwen retrieval, and extension choices. Stop if any command fails. Installation asks for the shell, client, Flashbang, status line, Qwen retrieval, and extension choices.
PowerShell: PowerShell:
@@ -54,7 +54,7 @@ For an already-installed configuration, use Quickupdate instead.
## Quickupdate ## Quickupdate
Run these commands from the repository root. Quick reuses the shell, client, CLI update, Flashbang, retrieval, and extension choices from the last Run these commands from the repository root. Quick reuses the shell, client, Flashbang, status line, retrieval, and extension choices from the last
successful installation or update without opening selection menus. Stop if any command fails. successful installation or update without opening selection menus. Stop if any command fails.
PowerShell: PowerShell:
@@ -218,19 +218,11 @@ Local Codex `model`, `model_reasoning_effort`, `plugins`, and `marketplaces` set
other local settings are not generally merged. Dry runs still rebuild `generated/`, but do not modify installation destinations. other local settings are not generally merged. Dry runs still rebuild `generated/`, but do not modify installation destinations.
Selecting Codex also installs shared skills to `.agents/skills`. An interactive extension list includes plugins and Selecting Codex also installs shared skills to `.agents/skills`. An interactive extension list includes plugins and
Codex skills; unchecked extensions are skipped on installation. Normal runs also ask whether to update Codex skills; unchecked extensions are skipped on installation. Normal install and update ask whether to enable
the selected client CLIs, enable Flashbang, and enable Qwen3 semantic retrieval. These choices are saved for Quickupdate. Flashbang, apply the custom status line, and enable Qwen3 semantic retrieval. Choices are saved for Quickupdate.
Disabling retrieval on a later update removes its setup-owned runtime and index. Dry runs preview The status line defaults to enabled to preserve existing behavior. Disabling it removes the custom client setting and,
changes without updating CLIs, extensions, or retrieval. See [Plugins](docs/plugins.md) for client-specific behavior. for Claude, the setup-owned status line script. Disabling retrieval on update removes its setup-owned runtime and index.
Dry runs preview changes without updating extensions or retrieval. See [Plugins](docs/plugins.md) for client-specific behavior.
Install also always asks `Uninstall Codex/Claude/Both and reinstall via npm?` (default no), scoped to whatever client
was selected. This choice is independent of the "update selected agent CLIs" choice above, is never saved, and is not
asked again by Quickupdate or by the update script. Answering yes uninstalls and reinstalls, per selected client, the
matching CLI (`@openai/codex` for Codex, `@anthropic-ai/claude-code` for Claude) through `npm uninstall -g`
followed by `npm install -g <package>@latest`, when that CLI is currently found as a global npm package. When it is
not (for example, installed through a native installer or another package manager), that prior installation is left
in place with a warning, and only a fresh `npm install -g` is run alongside it. Codex is skipped with a warning while
`codex.exe` is running. A dry run never asks this question and never previews it, since the choice is not persisted.
## Update Script ## Update Script
@@ -249,7 +241,7 @@ Bash:
``` ```
It accepts the same `-DryRun`/`--dry-run`, `-Client`/`--client`, and `-Shell`/`--shell` parameters as the It accepts the same `-DryRun`/`--dry-run`, `-Client`/`--client`, and `-Shell`/`--shell` parameters as the
installer, re-syncs every managed file, and lets you revise the CLI update, Flashbang, retrieval, and extension choices. installer, re-syncs every managed file, and lets you revise the status line, Flashbang, retrieval, and extension choices.
Selected extensions are installed or updated. Deselection removes only extensions recorded as installed by this Selected extensions are installed or updated. Deselection removes only extensions recorded as installed by this
setup; pre-existing or manually installed extensions are retained. Quickupdate reuses the saved choices. setup; pre-existing or manually installed extensions are retained. Quickupdate reuses the saved choices.
-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`. Rules for languages and frameworks live in their own directories, with an `index.md` entry point and focused Markdown rules under `references/`. Security has its own directory with a baseline entry point and focused rules for authentication, web, APIs, data, files, network, cryptography, and supply chains. Git, Microsoft, and cross-cutting rules remain directly under `shared/rules/`. Codex receives the full directory tree; Claude generates a skill for each rule and copies supporting references alongside the index skill.
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 cross-cutting rules such as Git or Microsoft guidance as focused Markdown files directly in `shared/rules/`. Put each language, framework, or library in its own directory with an `index.md` and focused files under `references/`; put security rules in `shared/rules/security/`. Add the matching loading condition and generated Claude skill name to `shared/global-instructions.md` and the directory index. The build copies rule trees to Codex and generates Claude skills from each rule file. 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
+1 -1
View File
@@ -18,7 +18,7 @@ On Windows, installation and update repair the known Caveman 1.0.0 hook launcher
[QMD](https://github.com/tobi/qmd) requires Node.js 22 or newer and `npm`. Selecting it installs `@tobilu/qmd` globally. Claude receives QMD's official `qmd@qmd` plugin; Codex can use the `qmd` CLI. Deselecting QMD removes setup-owned integrations and uninstalls the global package when the Codex-owned QMD record is removed. [QMD](https://github.com/tobi/qmd) requires Node.js 22 or newer and `npm`. Selecting it installs `@tobilu/qmd` globally. Claude receives QMD's official `qmd@qmd` plugin; Codex can use the `qmd` CLI. Deselecting QMD removes setup-owned integrations and uninstalls the global package when the Codex-owned QMD record is removed.
Normal installation and update also ask whether to update the selected client CLIs, enable Flashbang, and enable the optional Qwen3-Embedding-0.6B semantic retrieval layer. Retrieval uses a per-user virtual environment and index and is invoked through the installed `server.py search` CLI, disabled by default. Quickupdate reuses the saved shell, client, CLI update, Flashbang, retrieval, and extension choices without opening menus. Disabling retrieval removes its setup-owned runtime and index. Only successful runs save selections; dry runs do not update CLIs, extensions, or retrieval. Normal installation and update ask whether to enable Flashbang, apply the custom status line, and enable the optional Qwen3-Embedding-0.6B semantic retrieval layer. Retrieval uses a per-user virtual environment and index and is invoked through the installed `server.py search` CLI, disabled by default. Quickupdate reuses the saved shell, client, status line, Flashbang, retrieval, and extension choices without opening menus. Disabling the status line removes its custom setting and setup-owned Claude script; disabling retrieval removes its setup-owned runtime and index. Only successful runs save selections; dry runs do not update extensions or retrieval. Install and update do not modify the Codex or Claude CLI installations.
The `server.py search`/`rebuild` CLI transparently reuses a background daemon (spawned automatically on first use per `--data-dir`) that keeps the embedding and reranking models loaded in memory, so only the first query after a cold start or an idle period pays the model-loading cost; every call falls back to loading the models directly in-process if the daemon cannot be reached or started. The daemon exits on its own after 30 minutes of inactivity. Indexing skips files larger than 512 KB and, per repository, stops after 2000 indexable files by default; set `SEMANTIC_RETRIEVAL_MAX_FILES` to raise that cap for larger monorepos. When the cap is reached, the CLI prints an explicit warning to stderr rather than silently omitting files. The `server.py search`/`rebuild` CLI transparently reuses a background daemon (spawned automatically on first use per `--data-dir`) that keeps the embedding and reranking models loaded in memory, so only the first query after a cold start or an idle period pays the model-loading cost; every call falls back to loading the models directly in-process if the daemon cannot be reached or started. The daemon exits on its own after 30 minutes of inactivity. Indexing skips files larger than 512 KB and, per repository, stops after 2000 indexable files by default; set `SEMANTIC_RETRIEVAL_MAX_FILES` to raise that cap for larger monorepos. When the cap is reached, the CLI prints an explicit warning to stderr rather than silently omitting files.
+7 -7
View File
@@ -10,15 +10,15 @@ The current structure is organized by purpose:
- `reviews/`: code, security, performance, WPF, UI/UX, business, API, and EF Core reviews - `reviews/`: code, security, performance, WPF, UI/UX, business, API, and EF Core reviews
- `implementation/`: SQL, unit-test, and integration-test writing - `implementation/`: SQL, unit-test, and integration-test writing
- `verification/`: facts, work-item, and regression verification - `verification/`: facts, work-item, and regression verification
- `git/`: pull request responses and reviews - `git/`: pull request reviews
The files intentionally define only a small baseline. Detailed personal workflows and standards can be added later without changing the structure. Each skill defines a focused workflow with the investigation, decision points, and output expected for that task. Keep guidance specific to the skill; shared repository constraints belong in the rules.
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). Language, framework, library, and security rules use directory indexes plus focused topic files. Their generated Claude skills expose each focused rule separately; the index skills also include the references for contextual lookup. 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
change. The shared Verification rules govern proportional checking, including when generic skill workflows call change. The shared Verification rules govern proportional checking, including when generic skill workflows call
+32 -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,50 @@ 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 foreach ($ruleFile in Get-ChildItem $ruleDirectory.FullName -File -Filter '*.md' -Recurse) {
$ruleBodyPath = if ($isDirectory) { Join-Path $ruleSourcePath 'index.md' } else { $ruleSourcePath } $relativeRulePath = $ruleFile.FullName.Substring($ruleDirectory.FullName.Length).TrimStart([char[]]@('\','/'))
$skillDir = Join-Path $output "claude-$shell/skills/rules/$($entry.skill_name)" if ($relativeRulePath -eq 'index.md') {
$ruleName = "rules-$($ruleDirectory.Name)"
$references = Join-Path $ruleDirectory.FullName 'references'
} else {
$ruleParts = $relativeRulePath.Substring(0, $relativeRulePath.Length - 3).Split([char[]]@('\','/'))
if ($ruleParts[0] -eq 'references') { $ruleParts = @($ruleParts | Select-Object -Skip 1) }
$ruleName = "rules-$($ruleDirectory.Name)-$($ruleParts -join '-')"
$references = $null
}
if ($ruleSkills.Name -contains $ruleName) { throw "Duplicate generated rule skill name: $ruleName" }
$ruleSkills += [pscustomobject]@{ Name = $ruleName; Body = $ruleFile.FullName; References = $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" $ruleTitle = (Get-Content $ruleSkill.Body | Where-Object { $_ -match '^#+\s+' } | Select-Object -First 1) -replace '^#+\s+', ''
if ([string]::IsNullOrWhiteSpace($ruleTitle)) { throw "Missing rule title in $($ruleSkill.Body)" }
$ruleDescription = "Use when the task concerns $ruleTitle."
$skillBody = "---`r`nname: $($ruleSkill.Name)`r`ndescription: $(Quote-Toml $ruleDescription)`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
+32 -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,50 @@ 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" declare -A rule_skill_names=()
while IFS= read -r rule_path; do
claude_rule_loading_list="" case "$rule_path" in
for i in "${!rule_skill_names[@]}"; do general.md) continue ;;
rule_file=${rule_skill_files[$i]} */index.md) skill_name="rules-${rule_path%/index.md}"; rule_body="$shared/rules/$rule_path"; references_dir="${rule_body%/index.md}/references" ;;
skill_name=${rule_skill_names[$i]} */*.md)
trigger=${rule_skill_triggers[$i]} rule_directory=${rule_path%%/*}
rule_source="$shared/rules/$rule_file" rule_relative=${rule_path#*/}
rule_body="$rule_source" rule_relative=${rule_relative%.md}
[ -d "$rule_source" ] && rule_body="$rule_source/index.md" rule_relative=${rule_relative#references/}
rule_relative=${rule_relative//\//-}
skill_name="rules-$rule_directory-$rule_relative"
rule_body="$shared/rules/$rule_path"
references_dir=''
;;
*.md) skill_name="rules-${rule_path%.md}"; rule_body="$shared/rules/$rule_path"; references_dir='' ;;
*) continue ;;
esac
if [[ -n "${rule_skill_names[$skill_name]:-}" ]]; then
printf 'Duplicate generated rule skill name: %s\n' "$skill_name" >&2
exit 1
fi
rule_skill_names[$skill_name]=1
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" rule_title=$(sed -n 's/^#\{1,6\} *//p' "$rule_body" | head -n 1)
if [ -d "$rule_source/references" ]; then if [ -z "$rule_title" ]; then printf 'Missing rule title in %s\n' "$rule_body" >&2; exit 1; fi
copy_directory "$rule_source/references" "$skill_dir/references" rule_description="Use when the task concerns $rule_title."
{ printf -- '---\nname: %s\ndescription: %s\n---\n\n' "$skill_name" "$(quote_toml "$rule_description")"; cat "$rule_body"; } > "$skill_dir/SKILL.md"
if [ -n "$references_dir" ] && [ -d "$references_dir" ]; then
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 -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"
+2 -5
View File
@@ -24,7 +24,6 @@ if (-not $DryRun -and (Test-AnyManifestPresent (Get-InstallDestinations -HomePat
} }
$options = Read-InstallOptions -HomePath $homePath -RepositoryRoot $root -Client $Client -DryRun:$DryRun $options = Read-InstallOptions -HomePath $homePath -RepositoryRoot $root -Client $Client -DryRun:$DryRun
$reinstallViaNpm = Read-CliReinstallOption -Client $Client -DryRun:$DryRun
$buildScript = Join-Path $root 'scripts/commands/build.ps1' $buildScript = Join-Path $root 'scripts/commands/build.ps1'
& $buildScript -Summary:$Summary & $buildScript -Summary:$Summary
@@ -39,16 +38,14 @@ $claudeConcurrency = if ($env:CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY) { $env:CLAUD
if ($claudeConcurrency -notmatch '^[1-9][0-9]?$' -or [int]$claudeConcurrency -gt 10) { throw 'CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY must be an integer from 1 to 10.' } if ($claudeConcurrency -notmatch '^[1-9][0-9]?$' -or [int]$claudeConcurrency -gt 10) { throw 'CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY must be an integer from 1 to 10.' }
$plugins = Select-ConfiguredPlugins -RepositoryRoot $root -HomePath $homePath -Client $Client -Mode 'Install' -DryRun:$DryRun $plugins = Select-ConfiguredPlugins -RepositoryRoot $root -HomePath $homePath -Client $Client -Mode 'Install' -DryRun:$DryRun
if ($options.update_agents) { Update-SelectedAgentClis -Client $Client -DryRun:$DryRun -Summary:$Summary }
if ($reinstallViaNpm) { Invoke-CliReinstall -Client $Client -DryRun:$DryRun -Summary:$Summary }
foreach ($item in (Get-InstallTargets -Generated $generated -HomePath $homePath -Shell $shell -Client $Client)) { foreach ($item in (Get-InstallTargets -Generated $generated -HomePath $homePath -Shell $shell -Client $Client)) {
Sync-ManagedDestination -Source $item.Source -Destination $item.Destination -Stamp $stamp ` Sync-ManagedDestination -Source $item.Source -Destination $item.Destination -Stamp $stamp `
-AiConfigRoot $item.Destination.Replace('\','/') -ShellCommand $shellCommand -PowerShellCommand $powerShellCommand -ClaudeConcurrency $claudeConcurrency -FlashbangEnabled $options.flashbang -DryRun:$DryRun -Summary:$Summary -AiConfigRoot $item.Destination.Replace('\','/') -ShellCommand $shellCommand -PowerShellCommand $powerShellCommand -ClaudeConcurrency $claudeConcurrency -FlashbangEnabled $options.flashbang -StatusLineEnabled $options.statusline -DryRun:$DryRun -Summary:$Summary
} }
Sync-ConfiguredPlugins -RepositoryRoot $root -HomePath $homePath -Client $Client -DryRun:$DryRun -Summary:$Summary -Entries $plugins.Selected Sync-ConfiguredPlugins -RepositoryRoot $root -HomePath $homePath -Client $Client -DryRun:$DryRun -Summary:$Summary -Entries $plugins.Selected
Sync-SemanticRetrieval -RepositoryRoot $root -HomePath $homePath -Client $Client -Enabled $options.semantic_retrieval -DryRun:$DryRun -Summary:$Summary Sync-SemanticRetrieval -RepositoryRoot $root -HomePath $homePath -Client $Client -Enabled $options.semantic_retrieval -DryRun:$DryRun -Summary:$Summary
if (-not $DryRun) { Save-UpdateSelection -HomePath $homePath -RepositoryRoot $root -Shell $Shell -Client $Client -Plugins $plugins -UpdateAgents $options.update_agents -Flashbang $options.flashbang -SemanticRetrieval $options.semantic_retrieval } if (-not $DryRun) { Save-UpdateSelection -HomePath $homePath -RepositoryRoot $root -Shell $Shell -Client $Client -Plugins $plugins -Flashbang $options.flashbang -StatusLine $options.statusline -SemanticRetrieval $options.semantic_retrieval }
if ($Summary) { Write-Output "Install: PASS | $Client, $Shell$(if ($DryRun) { ', dry-run' })" } else { Write-Output ($(if ($DryRun) { 'PASS install dry-run' } else { 'PASS install' })) } if ($Summary) { Write-Output "Install: PASS | $Client, $Shell$(if ($DryRun) { ', dry-run' })" } else { Write-Output ($(if ($DryRun) { 'PASS install dry-run' } else { 'PASS install' })) }
exit 0 exit 0
+1 -4
View File
@@ -36,7 +36,6 @@ if [ "$dry_run" = false ]; then
fi fi
read_install_options read_install_options
reinstall_via_npm=$(read_cli_reinstall_option "$client" "$dry_run")
build_args=() build_args=()
[ "$summary" = false ] || build_args+=(--summary) [ "$summary" = false ] || build_args+=(--summary)
@@ -55,13 +54,11 @@ claude_concurrency=${CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY:-5}
selected_plugins=() selected_plugins=()
deselected_plugins=() deselected_plugins=()
select_configured_plugins "$root" "$client" Install "$dry_run" selected_plugins deselected_plugins select_configured_plugins "$root" "$client" Install "$dry_run" selected_plugins deselected_plugins
[ "$update_agents" = false ] || update_selected_agent_clis
[ "$reinstall_via_npm" = false ] || invoke_cli_reinstall "$client" "$dry_run" "$summary"
while IFS='|' read -r source destination; do while IFS='|' read -r source destination; do
ai_config_root=$destination ai_config_root=$destination
command -v cygpath >/dev/null && ai_config_root=$(cygpath -m "$destination") command -v cygpath >/dev/null && ai_config_root=$(cygpath -m "$destination")
sync_managed_destination "$source" "$destination" "$stamp" "$ai_config_root" "$shell_command" "$powershell_command" "$dry_run" "$summary" "$flashbang" "$claude_concurrency" sync_managed_destination "$source" "$destination" "$stamp" "$ai_config_root" "$shell_command" "$powershell_command" "$dry_run" "$summary" "$flashbang" "$claude_concurrency" "$statusline"
done < <(get_install_targets "$generated" "$home_path" "$shell" "$client") done < <(get_install_targets "$generated" "$home_path" "$shell" "$client")
sync_configured_plugins "$root" "$client" "$home_path" "$dry_run" false selected_plugins "$summary" sync_configured_plugins "$root" "$client" "$home_path" "$dry_run" false selected_plugins "$summary"
+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('=')
+2 -4
View File
@@ -49,11 +49,9 @@ $plugins = if ($Quick) {
Select-ConfiguredPlugins -RepositoryRoot $root -HomePath $homePath -Client $Client -Mode 'Update' -DryRun:$DryRun Select-ConfiguredPlugins -RepositoryRoot $root -HomePath $homePath -Client $Client -Mode 'Update' -DryRun:$DryRun
} }
if ($options.update_agents) { Update-SelectedAgentClis -Client $Client -DryRun:$DryRun -Summary:$Summary }
foreach ($item in (Get-InstallTargets -Generated $generated -HomePath $homePath -Shell $shell -Client $Client)) { foreach ($item in (Get-InstallTargets -Generated $generated -HomePath $homePath -Shell $shell -Client $Client)) {
Sync-ManagedDestination -Source $item.Source -Destination $item.Destination -Stamp $stamp ` Sync-ManagedDestination -Source $item.Source -Destination $item.Destination -Stamp $stamp `
-AiConfigRoot $item.Destination.Replace('\','/') -ShellCommand $shellCommand -PowerShellCommand $powerShellCommand -FlashbangEnabled $options.flashbang -DryRun:$DryRun -Summary:$Summary -AiConfigRoot $item.Destination.Replace('\','/') -ShellCommand $shellCommand -PowerShellCommand $powerShellCommand -FlashbangEnabled $options.flashbang -StatusLineEnabled $options.statusline -DryRun:$DryRun -Summary:$Summary
} }
$previousPluginNonInteractive = $script:PluginNonInteractive $previousPluginNonInteractive = $script:PluginNonInteractive
@@ -67,6 +65,6 @@ try {
$env:CI = $previousCI $env:CI = $previousCI
} }
Sync-SemanticRetrieval -RepositoryRoot $root -HomePath $homePath -Client $Client -Enabled $options.semantic_retrieval -DryRun:$DryRun -Update -Summary:$Summary Sync-SemanticRetrieval -RepositoryRoot $root -HomePath $homePath -Client $Client -Enabled $options.semantic_retrieval -DryRun:$DryRun -Update -Summary:$Summary
if (-not $DryRun) { Save-UpdateSelection -HomePath $homePath -RepositoryRoot $root -Shell $Shell -Client $Client -Plugins $plugins -UpdateAgents $options.update_agents -Flashbang $options.flashbang -SemanticRetrieval $options.semantic_retrieval } if (-not $DryRun) { Save-UpdateSelection -HomePath $homePath -RepositoryRoot $root -Shell $Shell -Client $Client -Plugins $plugins -Flashbang $options.flashbang -StatusLine $options.statusline -SemanticRetrieval $options.semantic_retrieval }
if ($Summary) { Write-Output "Update: PASS | $Client, $Shell$(if ($DryRun) { ', dry-run' })" } else { Write-Output ($(if ($DryRun) { 'PASS update dry-run' } else { 'PASS update' })) } if ($Summary) { Write-Output "Update: PASS | $Client, $Shell$(if ($DryRun) { ', dry-run' })" } else { Write-Output ($(if ($DryRun) { 'PASS update dry-run' } else { 'PASS update' })) }
exit 0 exit 0
+1 -2
View File
@@ -59,12 +59,11 @@ if [ "$quick" = false ]; then
select_configured_plugins "$root" "$client" Update "$dry_run" selected_plugins deselected_plugins select_configured_plugins "$root" "$client" Update "$dry_run" selected_plugins deselected_plugins
fi fi
[ "$update_agents" = false ] || update_selected_agent_clis
while IFS='|' read -r source destination; do while IFS='|' read -r source destination; do
ai_config_root=$destination ai_config_root=$destination
command -v cygpath >/dev/null && ai_config_root=$(cygpath -m "$destination") command -v cygpath >/dev/null && ai_config_root=$(cygpath -m "$destination")
sync_managed_destination "$source" "$destination" "$stamp" "$ai_config_root" "$shell_command" "$powershell_command" "$dry_run" "$summary" "$flashbang" sync_managed_destination "$source" "$destination" "$stamp" "$ai_config_root" "$shell_command" "$powershell_command" "$dry_run" "$summary" "$flashbang" 5 "$statusline"
done < <(get_install_targets "$generated" "$home_path" "$shell" "$client") done < <(get_install_targets "$generated" "$home_path" "$shell" "$client")
if [ "$quick" = true ]; then if [ "$quick" = true ]; then
+2 -101
View File
@@ -15,15 +15,15 @@ function Read-InstallBoolean {
function Read-InstallOptions { function Read-InstallOptions {
param([string]$HomePath, [string]$RepositoryRoot, [string]$Client, [switch]$DryRun) param([string]$HomePath, [string]$RepositoryRoot, [string]$Client, [switch]$DryRun)
$defaults = @{ update_agents = $false; flashbang = $true; semantic_retrieval = $false } $defaults = @{ flashbang = $true; statusline = $true; semantic_retrieval = $false }
if (Test-Path -LiteralPath (Join-Path $HomePath '.my-ai-configuration/selection.json')) { if (Test-Path -LiteralPath (Join-Path $HomePath '.my-ai-configuration/selection.json')) {
try { $defaults = Read-UpdateSelection -HomePath $HomePath -RepositoryRoot $RepositoryRoot -AllowLegacy } try { $defaults = Read-UpdateSelection -HomePath $HomePath -RepositoryRoot $RepositoryRoot -AllowLegacy }
catch { Write-Warning 'Saved options could not be read; confirm new options below.' } catch { Write-Warning 'Saved options could not be read; confirm new options below.' }
} }
if ($DryRun) { return $defaults } if ($DryRun) { return $defaults }
return @{ return @{
update_agents = Read-InstallBoolean -Prompt "Update selected agent CLIs ($Client)?" -Default $defaults.update_agents
flashbang = Read-InstallBoolean -Prompt 'Enable the Flashbang notification hook?' -Default $defaults.flashbang flashbang = Read-InstallBoolean -Prompt 'Enable the Flashbang notification hook?' -Default $defaults.flashbang
statusline = Read-InstallBoolean -Prompt 'Apply the custom status line?' -Default $defaults.statusline
semantic_retrieval = Read-SemanticRetrievalOption -Default $defaults.semantic_retrieval -RepositoryRoot $RepositoryRoot -HomePath $HomePath semantic_retrieval = Read-SemanticRetrievalOption -Default $defaults.semantic_retrieval -RepositoryRoot $RepositoryRoot -HomePath $HomePath
} }
} }
@@ -43,102 +43,3 @@ function Read-SemanticRetrievalOption {
} }
} }
} }
function Test-CodexProcessActive {
if ([Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT) { return $false }
return [bool](Get-Process -Name 'codex' -ErrorAction SilentlyContinue)
}
function Test-GlobalNpmCodex {
$npm = Get-Command npm -ErrorAction SilentlyContinue
if (-not $npm) { return $false }
$previousPreference = $ErrorActionPreference
try {
$ErrorActionPreference = 'Continue'
& $npm list -g '@openai/codex' --depth=0 --json 1>$null 2>$null
return $LASTEXITCODE -eq 0
} finally {
$ErrorActionPreference = $previousPreference
}
}
function Test-GlobalNpmPackage {
param([Parameter(Mandatory=$true)][string]$Package)
$npm = Get-Command npm -ErrorAction SilentlyContinue
if (-not $npm) { return $false }
$previousPreference = $ErrorActionPreference
try {
$ErrorActionPreference = 'Continue'
& $npm list -g $Package --depth=0 --json 1>$null 2>$null
return $LASTEXITCODE -eq 0
} finally {
$ErrorActionPreference = $previousPreference
}
}
function Get-CliNpmPackage {
param([ValidateSet('codex','claude')][Parameter(Mandatory=$true)][string]$Client)
if ($Client -eq 'codex') { return '@openai/codex' }
return '@anthropic-ai/claude-code'
}
function Read-CliReinstallOption {
<#
.SYNOPSIS
Asked once per install run, never persisted; a dry run never prompts and never previews it.
#>
param([string]$Client, [switch]$DryRun)
if ($DryRun) { return $false }
return Read-InstallBoolean -Prompt "Uninstall $Client and reinstall via npm?" -Default $false
}
function Invoke-CliReinstall {
param([string]$Client, [switch]$DryRun, [switch]$Summary)
$clients = if ($Client -eq 'Both') { @('codex', 'claude') } else { @($Client.ToLowerInvariant()) }
$previousAutoUpdate = $env:CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE
try {
$env:CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE = '1'
foreach ($selectedClient in $clients) {
if ($selectedClient -eq 'codex' -and -not $DryRun -and (Test-CodexProcessActive)) {
Write-Warning 'Codex CLI reinstall skipped because codex.exe is running. Close all Codex sessions and run install again to reinstall the CLI.'
continue
}
$package = Get-CliNpmPackage -Client $selectedClient
if (Test-GlobalNpmPackage -Package $package) {
Invoke-PluginCommand -Command 'npm' -Arguments @('uninstall', '-g', $package) -DryRun:$DryRun -Summary:$Summary
} else {
Write-Warning "$selectedClient CLI was not found as a global npm package; a non-npm installation cannot be removed automatically. Installing $package via npm alongside it."
}
Invoke-PluginCommand -Command 'npm' -Arguments @('install', '-g', "$package@latest") -DryRun:$DryRun -Summary:$Summary
if ($Summary) { Write-Output "CLI $selectedClient reinstall: $(if ($DryRun) { 'dry-run' } else { 'PASS' })" }
}
} finally {
$env:CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE = $previousAutoUpdate
}
}
function Update-SelectedAgentClis {
param([string]$Client, [switch]$DryRun, [switch]$Summary)
$clients = if ($Client -eq 'Both') { @('codex', 'claude') } else { @($Client.ToLowerInvariant()) }
$previousNonInteractive = $script:PluginNonInteractive
$previousAutoUpdate = $env:CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE
try {
$script:PluginNonInteractive = $true
$env:CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE = '1'
foreach ($selectedClient in $clients) {
if ($selectedClient -eq 'codex' -and -not $DryRun -and (Test-CodexProcessActive)) {
Write-Warning 'Codex CLI update skipped because codex.exe is running. Close all Codex sessions and run the update again to update the CLI.'
continue
}
if ($selectedClient -eq 'codex' -and (Test-GlobalNpmCodex)) {
Invoke-PluginCommand -Command 'npm' -Arguments @('install','-g','@openai/codex@latest') -DryRun:$DryRun -Summary:$Summary
} else {
Invoke-PluginCommand -Command $selectedClient -Arguments @('update') -DryRun:$DryRun -Summary:$Summary
}
if ($Summary) { Write-Output "CLI $selectedClient update: $(if ($DryRun) { 'dry-run' } else { 'PASS' })" }
}
} finally {
$script:PluginNonInteractive = $previousNonInteractive
$env:CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE = $previousAutoUpdate
}
}
+22 -4
View File
@@ -11,10 +11,11 @@ def is_flashbang(hook):
for key in ('command', 'command_windows')) for key in ('command', 'command_windows'))
def filter_flashbang(path): def filter_options(path, flashbang_enabled=True, statusline_enabled=True):
content = path.read_text(encoding='utf-8-sig') content = path.read_text(encoding='utf-8-sig')
if path.name == 'settings.json': if path.name == 'settings.json':
settings = json.loads(content) settings = json.loads(content)
if not flashbang_enabled:
groups = settings.get('hooks', {}).get('Stop', []) groups = settings.get('hooks', {}).get('Stop', [])
remaining = [] remaining = []
for group in groups: for group in groups:
@@ -25,11 +26,15 @@ def filter_flashbang(path):
settings['hooks']['Stop'] = remaining settings['hooks']['Stop'] = remaining
elif 'Stop' in settings.get('hooks', {}): elif 'Stop' in settings.get('hooks', {}):
del settings['hooks']['Stop'] del settings['hooks']['Stop']
if not statusline_enabled:
settings.pop('statusLine', None)
return json.dumps(settings, indent=2, ensure_ascii=False) + '\n' return json.dumps(settings, indent=2, ensure_ascii=False) + '\n'
if path.name != 'config.toml': if path.name != 'config.toml':
raise ValueError('Flashbang filtering only supports client configuration files.') raise ValueError('Install option filtering only supports client configuration files.')
tomllib.loads(content) tomllib.loads(content)
sections = re.split(r'(?m)(?=^\s*\[\[?[^\r\n]+\]\]?\s*$)', content) result = content
if not flashbang_enabled:
sections = re.split(r'(?m)(?=^\s*\[\[?[^\r\n]+\]\]?\s*$)', result)
filtered = [] filtered = []
for section in sections: for section in sections:
if re.match(r'\s*\[\[hooks\.Stop\.hooks\]\]', section): if re.match(r'\s*\[\[hooks\.Stop\.hooks\]\]', section):
@@ -39,19 +44,32 @@ def filter_flashbang(path):
filtered.append(section) filtered.append(section)
result = ''.join(filtered) result = ''.join(filtered)
result = re.sub(r'(?m)^\[\[hooks\.Stop\]\][ \t]*\r?\n\s*(?=\[|\Z)(?!\[\[hooks\.Stop\.hooks\]\])', '', result) result = re.sub(r'(?m)^\[\[hooks\.Stop\]\][ \t]*\r?\n\s*(?=\[|\Z)(?!\[\[hooks\.Stop\.hooks\]\])', '', result)
if not statusline_enabled:
sections = re.split(r'(?m)(?=^\s*\[\[?[^\r\n]+\]\]?\s*$)', result)
for index, section in enumerate(sections):
header = re.match(r'\s*\[\[?([^\]\r\n]+)\]\]?\s*(?:#.*)?(?:\r?\n|$)', section)
if header and header.group(1).strip() == 'tui':
sections[index] = re.sub(r'(?m)^[ \t]*status_line[ \t]*=.*(?:\r?\n|$)', '', section)
break
result = ''.join(sections)
tomllib.loads(result) tomllib.loads(result)
return result return result
def filter_flashbang(path):
return filter_options(path, flashbang_enabled=False)
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('action', choices=('filter',)) parser.add_argument('action', choices=('filter',))
parser.add_argument('--path', type=Path, required=True) parser.add_argument('--path', type=Path, required=True)
parser.add_argument('--flashbang', choices=('true', 'false'), required=True) parser.add_argument('--flashbang', choices=('true', 'false'), required=True)
parser.add_argument('--statusline', choices=('true', 'false'), default='true')
args = parser.parse_args() args = parser.parse_args()
try: try:
sys.stdout.reconfigure(encoding='utf-8') sys.stdout.reconfigure(encoding='utf-8')
print(filter_flashbang(args.path) if args.flashbang == 'false' else args.path.read_text(encoding='utf-8-sig'), end='') print(filter_options(args.path, args.flashbang == 'true', args.statusline == 'true'), end='')
except (OSError, ValueError, KeyError) as error: except (OSError, ValueError, KeyError) as error:
print(f'Install options: {error}', file=sys.stderr) print(f'Install options: {error}', file=sys.stderr)
sys.exit(1) sys.exit(1)
+3 -78
View File
@@ -17,23 +17,23 @@ read_install_boolean() {
} }
read_install_options() { read_install_options() {
update_agents=false
flashbang=true flashbang=true
statusline=true
semantic_retrieval=false semantic_retrieval=false
local saved key value local saved key value
if [ -f "$home_path/.my-ai-configuration/selection.json" ]; then if [ -f "$home_path/.my-ai-configuration/selection.json" ]; then
if saved=$(python3 "$root/scripts/lib/selection-state.py" read --home "$home_path" --manifest "$root/adapters/plugins.tsv" --format tsv --allow-legacy); then if saved=$(python3 "$root/scripts/lib/selection-state.py" read --home "$home_path" --manifest "$root/adapters/plugins.tsv" --format tsv --allow-legacy); then
while IFS=$'\t' read -r key value; do while IFS=$'\t' read -r key value; do
value=${value%$'\r'} value=${value%$'\r'}
case "$key" in update_agents) update_agents=$value ;; flashbang) flashbang=$value ;; semantic_retrieval) semantic_retrieval=$value ;; esac case "$key" in flashbang) flashbang=$value ;; statusline) statusline=$value ;; semantic_retrieval) semantic_retrieval=$value ;; esac
done <<< "$saved" done <<< "$saved"
else else
printf 'WARN Saved options could not be read; confirm new options below.\n' >&2 printf 'WARN Saved options could not be read; confirm new options below.\n' >&2
fi fi
fi fi
[ "$dry_run" = false ] || return 0 [ "$dry_run" = false ] || return 0
update_agents=$(read_install_boolean "Update selected agent CLIs ($client)?" "$update_agents") || return
flashbang=$(read_install_boolean 'Enable the Flashbang notification hook?' "$flashbang") || return flashbang=$(read_install_boolean 'Enable the Flashbang notification hook?' "$flashbang") || return
statusline=$(read_install_boolean 'Apply the custom status line?' "$statusline") || return
semantic_retrieval=$(read_semantic_retrieval_option "$semantic_retrieval") || return semantic_retrieval=$(read_semantic_retrieval_option "$semantic_retrieval") || return
} }
@@ -56,78 +56,3 @@ read_semantic_retrieval_option() {
esac esac
done done
} }
global_npm_package() {
command -v npm >/dev/null 2>&1 || return 1
npm list -g "$1" --depth=0 --json >/dev/null 2>&1
}
cli_npm_package() {
case "$1" in
codex) printf '@openai/codex\n' ;;
claude) printf '@anthropic-ai/claude-code\n' ;;
esac
}
# read_cli_reinstall_option <client> <dry_run> -- asked once per install run, never persisted;
# a dry run never prompts and never previews it.
read_cli_reinstall_option() {
local client=$1 dry_run=$2
if [ "$dry_run" = true ]; then printf 'false\n'; return; fi
read_install_boolean "Uninstall $client and reinstall via npm?" false
}
invoke_cli_reinstall() {
local client=$1 dry_run=$2 summary=${3:-false}
local selected_client clients=() package
case "$client" in codex|both) clients+=(codex) ;; esac
case "$client" in claude|both) clients+=(claude) ;; esac
for selected_client in "${clients[@]}"; do
if [ "$selected_client" = codex ] && [ "$dry_run" = false ] && codex_process_active; then
printf 'WARN Codex CLI reinstall skipped because codex.exe is running. Close all Codex sessions and run install again to reinstall the CLI.\n' >&2
continue
fi
package=$(cli_npm_package "$selected_client")
if global_npm_package "$package"; then
CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE=1 run_plugin_command "$dry_run" npm uninstall -g "$package" </dev/null || return
else
printf 'WARN %s CLI was not found as a global npm package; a non-npm installation cannot be removed automatically. Installing %s via npm alongside it.\n' "$selected_client" "$package" >&2
fi
CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE=1 run_plugin_command "$dry_run" npm install -g "${package}@latest" </dev/null || return
if [ "$summary" = true ]; then
if [ "$dry_run" = true ]; then printf 'CLI %s reinstall: dry-run\n' "$selected_client"; else printf 'CLI %s reinstall: PASS\n' "$selected_client"; fi
fi
done
}
is_windows_host() {
case "${OSTYPE:-}" in msys*|cygwin*|win32*) return 0 ;; *) return 1 ;; esac
}
codex_process_active() {
is_windows_host || return 1
command -v powershell.exe >/dev/null 2>&1 || return 1
powershell.exe -NoProfile -Command "if (Get-Process -Name codex -ErrorAction SilentlyContinue) { exit 0 }; exit 1" >/dev/null 2>&1
}
global_npm_codex() {
command -v npm >/dev/null 2>&1 || return 1
npm list -g '@openai/codex' --depth=0 --json >/dev/null 2>&1
}
update_selected_agent_clis() {
local selected_client clients=()
case "$client" in codex|both) clients+=(codex) ;; esac
case "$client" in claude|both) clients+=(claude) ;; esac
for selected_client in "${clients[@]}"; do
if [ "$selected_client" = codex ] && [ "$dry_run" = false ] && codex_process_active; then
printf 'WARN Codex CLI update skipped because codex.exe is running. Close all Codex sessions and run the update again to update the CLI.\n' >&2
continue
fi
if [ "$selected_client" = codex ] && global_npm_codex; then
CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE=1 CI=true run_plugin_command "$dry_run" npm install -g '@openai/codex@latest' </dev/null || return
else
CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE=1 CI=true run_plugin_command "$dry_run" "$selected_client" update </dev/null || return
fi
done
}
+9 -5
View File
@@ -79,6 +79,7 @@ function Sync-ManagedDestination {
[string]$PowerShellCommand, [string]$PowerShellCommand,
[string]$ClaudeConcurrency = '5', [string]$ClaudeConcurrency = '5',
[bool]$FlashbangEnabled = $true, [bool]$FlashbangEnabled = $true,
[bool]$StatusLineEnabled = $true,
[switch]$DryRun, [switch]$DryRun,
[switch]$Summary [switch]$Summary
) )
@@ -93,14 +94,17 @@ function Sync-ManagedDestination {
Get-ChildItem $Source -File -Recurse | ForEach-Object { Get-ChildItem $Source -File -Recurse | ForEach-Object {
$relative = $_.FullName.Substring($Source.Length).TrimStart([char[]]@('\','/')).Replace('\','/') $relative = $_.FullName.Substring($Source.Length).TrimStart([char[]]@('\','/')).Replace('\','/')
if ((Split-Path $Destination -Leaf) -eq '.codex' -and $relative.StartsWith('skills/')) { return } if ((Split-Path $Destination -Leaf) -eq '.codex' -and $relative.StartsWith('skills/')) { return }
if ((Split-Path $Destination -Leaf) -eq '.claude' -and -not $StatusLineEnabled -and $relative.StartsWith('statusline/')) { return }
$target = Resolve-ManagedPath -Destination $Destination -Relative $relative $target = Resolve-ManagedPath -Destination $Destination -Relative $relative
$rawContent = Get-Content -LiteralPath $_.FullName -Raw -Encoding UTF8 $rawContent = Get-Content -LiteralPath $_.FullName -Raw -Encoding UTF8
$filterFlashbang = -not $FlashbangEnabled -and $relative -in @('settings.json', 'config.toml') $filterOptions = ((-not $FlashbangEnabled) -or (-not $StatusLineEnabled)) -and $relative -in @('settings.json', 'config.toml')
if ($filterFlashbang) { if ($filterOptions) {
$rawContent = (& python (Join-Path $PSScriptRoot 'install-options.py') filter --path $_.FullName --flashbang false | Out-String) $flashbangOption = if ($FlashbangEnabled) { 'true' } else { 'false' }
if ($LASTEXITCODE -ne 0) { throw 'Could not configure Flashbang.' } $statusLineOption = if ($StatusLineEnabled) { 'true' } else { 'false' }
$rawContent = (& python (Join-Path $PSScriptRoot 'install-options.py') filter --path $_.FullName --flashbang $flashbangOption --statusline $statusLineOption | Out-String)
if ($LASTEXITCODE -ne 0) { throw 'Could not configure install options.' }
} }
$needsSubstitution = $filterFlashbang -or $rawContent.Contains('__AI_CONFIG_ROOT__') -or $rawContent.Contains('__HOOK_COMMAND__') -or $rawContent.Contains('__POWERSHELL_HOOK_COMMAND__') -or $rawContent.Contains('__POWERSHELL_COMMAND__') -or $rawContent.Contains('__CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY__') $needsSubstitution = $filterOptions -or $rawContent.Contains('__AI_CONFIG_ROOT__') -or $rawContent.Contains('__HOOK_COMMAND__') -or $rawContent.Contains('__POWERSHELL_HOOK_COMMAND__') -or $rawContent.Contains('__POWERSHELL_COMMAND__') -or $rawContent.Contains('__CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY__')
if ($needsSubstitution) { if ($needsSubstitution) {
$finalContent = $rawContent.Replace('__AI_CONFIG_ROOT__', $AiConfigRoot).Replace('__HOOK_COMMAND__', $ShellCommand).Replace('__POWERSHELL_HOOK_COMMAND__', $PowerShellCommand).Replace('__POWERSHELL_COMMAND__', $PowerShellCommand).Replace('__CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY__', $ClaudeConcurrency).Replace('__HOOK_SCRIPT__', 'flashbang.ps1').Replace('__POWERSHELL_HOOK_SCRIPT__', 'flashbang.ps1') $finalContent = $rawContent.Replace('__AI_CONFIG_ROOT__', $AiConfigRoot).Replace('__HOOK_COMMAND__', $ShellCommand).Replace('__POWERSHELL_HOOK_COMMAND__', $PowerShellCommand).Replace('__POWERSHELL_COMMAND__', $PowerShellCommand).Replace('__CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY__', $ClaudeConcurrency).Replace('__HOOK_SCRIPT__', 'flashbang.ps1').Replace('__POWERSHELL_HOOK_SCRIPT__', 'flashbang.ps1')
$newBytes = [System.Text.Encoding]::UTF8.GetBytes($finalContent) $newBytes = [System.Text.Encoding]::UTF8.GetBytes($finalContent)
+5 -4
View File
@@ -70,9 +70,9 @@ write_managed_manifest() {
} > "$path" } > "$path"
} }
# sync_managed_destination <source-dir> <destination-dir> <stamp> <ai-config-root> <shell-command> <powershell-command> <dry-run: true|false> [summary: true|false] [flashbang] [claude-concurrency] # sync_managed_destination <source-dir> <destination-dir> <stamp> <ai-config-root> <shell-command> <powershell-command> <dry-run: true|false> [summary: true|false] [flashbang] [claude-concurrency] [statusline]
sync_managed_destination() { sync_managed_destination() {
local source=$1 destination=$2 stamp=$3 ai_config_root=$4 shell_command=$5 powershell_command=$6 dry_run=$7 summary=${8:-false} flashbang_enabled=${9:-true} claude_concurrency=${10:-5} local source=$1 destination=$2 stamp=$3 ai_config_root=$4 shell_command=$5 powershell_command=$6 dry_run=$7 summary=${8:-false} flashbang_enabled=${9:-true} claude_concurrency=${10:-5} statusline_enabled=${11:-true}
[ "$summary" = true ] || printf 'SOURCE %s -> %s\n' "$source" "$destination" [ "$summary" = true ] || printf 'SOURCE %s -> %s\n' "$source" "$destination"
local manifest local manifest
manifest=$(manifest_path "$destination") manifest=$(manifest_path "$destination")
@@ -87,12 +87,13 @@ sync_managed_destination() {
local relative target content needs_sub new_hash exists current_hash action backup local relative target content needs_sub new_hash exists current_hash action backup
relative=${source_file#"$source/"} relative=${source_file#"$source/"}
if [ "${destination##*/}" = .codex ] && [[ "$relative" = skills/* ]]; then continue; fi if [ "${destination##*/}" = .codex ] && [[ "$relative" = skills/* ]]; then continue; fi
if [ "${destination##*/}" = .claude ] && [ "$statusline_enabled" = false ] && [[ "$relative" = statusline/* ]]; then continue; fi
target=$(managed_target "$destination" "$relative") || return 1 target=$(managed_target "$destination" "$relative") || return 1
content=$(cat -- "$source_file" && printf '\034') || return content=$(cat -- "$source_file" && printf '\034') || return
content=${content%$'\034'} content=${content%$'\034'}
needs_sub=false needs_sub=false
if [ "$flashbang_enabled" = false ] && [[ "$relative" = settings.json || "$relative" = config.toml ]]; then if { [ "$flashbang_enabled" = false ] || [ "$statusline_enabled" = false ]; } && [[ "$relative" = settings.json || "$relative" = config.toml ]]; then
content=$(python3 "$(dirname -- "${BASH_SOURCE[0]}")/install-options.py" filter --path "$source_file" --flashbang false && printf '\034') || return content=$(python3 "$(dirname -- "${BASH_SOURCE[0]}")/install-options.py" filter --path "$source_file" --flashbang "$flashbang_enabled" --statusline "$statusline_enabled" && printf '\034') || return
content=${content%$'\034'} content=${content%$'\034'}
needs_sub=true needs_sub=true
fi fi
+2 -2
View File
@@ -9,8 +9,8 @@ function Read-UpdateSelection {
function Save-UpdateSelection { function Save-UpdateSelection {
param([string]$HomePath, [string]$RepositoryRoot, [string]$Shell, [string]$Client, [hashtable]$Plugins, param([string]$HomePath, [string]$RepositoryRoot, [string]$Shell, [string]$Client, [hashtable]$Plugins,
[Parameter(Mandatory=$true)][bool]$UpdateAgents, [Parameter(Mandatory=$true)][bool]$Flashbang, [bool]$SemanticRetrieval = $false) [Parameter(Mandatory=$true)][bool]$Flashbang, [bool]$StatusLine = $true, [bool]$SemanticRetrieval = $false)
$arguments = @((Join-Path $PSScriptRoot 'selection-state.py'), 'write', '--home', $HomePath, '--manifest', (Join-Path $RepositoryRoot 'adapters/plugins.tsv'), '--shell', $Shell.ToLowerInvariant(), '--client', $Client.ToLowerInvariant(), '--update-agents', $UpdateAgents.ToString().ToLowerInvariant(), '--flashbang', $Flashbang.ToString().ToLowerInvariant(), '--semantic-retrieval', $SemanticRetrieval.ToString().ToLowerInvariant(), '--selected') $arguments = @((Join-Path $PSScriptRoot 'selection-state.py'), 'write', '--home', $HomePath, '--manifest', (Join-Path $RepositoryRoot 'adapters/plugins.tsv'), '--shell', $Shell.ToLowerInvariant(), '--client', $Client.ToLowerInvariant(), '--flashbang', $Flashbang.ToString().ToLowerInvariant(), '--statusline', $StatusLine.ToString().ToLowerInvariant(), '--semantic-retrieval', $SemanticRetrieval.ToString().ToLowerInvariant(), '--selected')
$arguments += @($Plugins.Selected | ForEach-Object { $_.name }) $arguments += @($Plugins.Selected | ForEach-Object { $_.name })
$arguments += '--deselected' $arguments += '--deselected'
$arguments += @($Plugins.Deselected | ForEach-Object { $_.name }) $arguments += @($Plugins.Deselected | ForEach-Object { $_.name })
+9 -6
View File
@@ -12,7 +12,7 @@ def validate(state, allow_legacy=False):
if not allow_legacy: if not allow_legacy:
raise ValueError('Saved selection predates install options. Run update once without Quick.') raise ValueError('Saved selection predates install options. Run update once without Quick.')
state = dict(state, version=2, shell={'windows': 'powershell', 'linux': 'bash'}.get(state.get('platform')), state = dict(state, version=2, shell={'windows': 'powershell', 'linux': 'bash'}.get(state.get('platform')),
update_agents=False, flashbang=True) flashbang=True, statusline=True)
state.pop('platform', None) state.pop('platform', None)
if not isinstance(state, dict) or state.get('version') != 2: if not isinstance(state, dict) or state.get('version') != 2:
raise ValueError('Unsupported saved selection.') raise ValueError('Unsupported saved selection.')
@@ -20,7 +20,10 @@ def validate(state, allow_legacy=False):
raise ValueError('Invalid saved shell or client.') raise ValueError('Invalid saved shell or client.')
if 'semantic_retrieval' not in state: if 'semantic_retrieval' not in state:
state['semantic_retrieval'] = False state['semantic_retrieval'] = False
if any(type(state.get(key)) is not bool for key in ('update_agents', 'flashbang', 'semantic_retrieval')): if 'statusline' not in state:
state['statusline'] = True
state.pop('update_agents', None)
if any(type(state.get(key)) is not bool for key in ('flashbang', 'statusline', 'semantic_retrieval')):
raise ValueError('Invalid saved install options. Run update once without Quick.') raise ValueError('Invalid saved install options. Run update once without Quick.')
for key in ('selected', 'deselected'): for key in ('selected', 'deselected'):
values = state.get(key) values = state.get(key)
@@ -41,8 +44,8 @@ def main():
parser.add_argument('--format', choices=('json', 'tsv'), default='json') parser.add_argument('--format', choices=('json', 'tsv'), default='json')
parser.add_argument('--shell') parser.add_argument('--shell')
parser.add_argument('--allow-legacy', action='store_true') parser.add_argument('--allow-legacy', action='store_true')
parser.add_argument('--update-agents', choices=('true', 'false'))
parser.add_argument('--flashbang', choices=('true', 'false')) parser.add_argument('--flashbang', choices=('true', 'false'))
parser.add_argument('--statusline', choices=('true', 'false'))
parser.add_argument('--semantic-retrieval', choices=('true', 'false')) parser.add_argument('--semantic-retrieval', choices=('true', 'false'))
parser.add_argument('--client') parser.add_argument('--client')
parser.add_argument('--selected', nargs='*', default=[]) parser.add_argument('--selected', nargs='*', default=[])
@@ -62,15 +65,15 @@ def main():
else: else:
print('shell\t' + state['shell']) print('shell\t' + state['shell'])
print('client\t' + state['client']) print('client\t' + state['client'])
for key in ('update_agents', 'flashbang', 'semantic_retrieval'): for key in ('flashbang', 'statusline', 'semantic_retrieval'):
print(key + '\t' + str(state[key]).lower()) print(key + '\t' + str(state[key]).lower())
for key in ('selected', 'deselected'): for key in ('selected', 'deselected'):
for name in state[key]: for name in state[key]:
print(key + '\t' + name) print(key + '\t' + name)
return return
state = validate(dict(version=2, shell=args.shell, client=args.client, state = validate(dict(version=2, shell=args.shell, client=args.client,
update_agents=None if args.update_agents is None else args.update_agents == 'true', flashbang=True if args.flashbang is None else args.flashbang == 'true',
flashbang=None if args.flashbang is None else args.flashbang == 'true', statusline=True if args.statusline is None else args.statusline == 'true',
semantic_retrieval=False if args.semantic_retrieval is None else args.semantic_retrieval == 'true', semantic_retrieval=False if args.semantic_retrieval is None else args.semantic_retrieval == 'true',
selected=args.selected, deselected=args.deselected)) selected=args.selected, deselected=args.deselected))
if (set(state['selected']) | set(state['deselected'])) - names: if (set(state['selected']) | set(state['deselected'])) - names:
+2 -2
View File
@@ -11,8 +11,8 @@ read_update_selection() {
case "$key" in case "$key" in
shell) [ -n "$shell" ] || shell=$value ;; shell) [ -n "$shell" ] || shell=$value ;;
client) [ -n "$client" ] || client=$value ;; client) [ -n "$client" ] || client=$value ;;
update_agents) update_agents=$value ;;
flashbang) flashbang=$value ;; flashbang) flashbang=$value ;;
statusline) statusline=$value ;;
semantic_retrieval) semantic_retrieval=$value ;; semantic_retrieval) semantic_retrieval=$value ;;
selected) selected_plugins+=("$value") ;; selected) selected_plugins+=("$value") ;;
deselected) deselected_plugins+=("$value") ;; deselected) deselected_plugins+=("$value") ;;
@@ -22,6 +22,6 @@ read_update_selection() {
save_update_selection() { save_update_selection() {
python3 "$root/scripts/lib/selection-state.py" write --home "$home_path" --manifest "$root/adapters/plugins.tsv" \ python3 "$root/scripts/lib/selection-state.py" write --home "$home_path" --manifest "$root/adapters/plugins.tsv" \
--shell "$shell" --client "$client" --update-agents "$update_agents" --flashbang "$flashbang" --semantic-retrieval "${semantic_retrieval:-false}" \ --shell "$shell" --client "$client" --flashbang "$flashbang" --statusline "${statusline:-true}" --semantic-retrieval "${semantic_retrieval:-false}" \
--selected "${selected_plugins[@]}" --deselected "${deselected_plugins[@]}" --selected "${selected_plugins[@]}" --deselected "${deselected_plugins[@]}"
} }
+13 -71
View File
@@ -40,71 +40,6 @@ class InstallOptionsTests(unittest.TestCase):
self.assertEqual(result['hooks']['Stop'][0]['hooks'], [{'command': 'echo keep'}]) self.assertEqual(result['hooks']['Stop'][0]['hooks'], [{'command': 'echo keep'}])
self.assertEqual(result['tui']['status_line'], ['model']) self.assertEqual(result['tui']['status_line'], ['model'])
def test_native_option_helpers_select_clients_and_restore_environment(self):
shells = [('powershell', shutil.which('powershell') or shutil.which('pwsh')),
('bash', str(Path(os.environ.get('ProgramFiles', 'C:/Program Files')) / 'Git/bin/bash.exe')
if os.name == 'nt' else shutil.which('bash'))]
for kind, executable in shells:
if not executable or not Path(executable).exists():
continue
with self.subTest(shell=kind):
if kind == 'powershell':
command = [executable, '-NoProfile', '-Command',
"$ErrorActionPreference='Stop'; . ./scripts/lib/install-options.ps1; "
"function Test-CodexProcessActive { $false }; function Test-GlobalNpmCodex { $false }; "
"function Invoke-PluginCommand { param($Command,$Arguments,[switch]$DryRun,[switch]$Summary) "
"Write-Output ($Command + ':' + ($Arguments -join ' ')) }; "
"$env:CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE='original'; "
"Update-SelectedAgentClis -Client Both; "
"if ($env:CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE -ne 'original') { throw 'Environment leaked' }"]
else:
command = [executable, '-c',
'. ./scripts/lib/install-options.sh; '
'codex_process_active() { return 1; }; global_npm_codex() { return 1; }; '
'run_plugin_command() { shift; printf "%s:%s\\n" "$1" "$2"; }; '
'client=both; dry_run=false; summary=false; '
'CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE=original; update_selected_agent_clis; '
'[ "$CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE" = original ]']
result = subprocess.run(command, cwd=ROOT, capture_output=True, text=True, timeout=20)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertEqual(result.stdout.splitlines(), ['codex:update', 'claude:update'])
def test_running_codex_is_skipped_and_npm_install_is_used_when_available(self):
shell = shutil.which('powershell') or shutil.which('pwsh')
if shell:
skipped = subprocess.run([
shell, '-NoProfile', '-Command',
"$ErrorActionPreference='Stop'; . ./scripts/lib/install-options.ps1; "
"function Test-CodexProcessActive { $true }; function Test-GlobalNpmCodex { $true }; "
"function Invoke-PluginCommand { param($Command,$Arguments,[switch]$DryRun,[switch]$Summary) Write-Output $Command }; "
"Update-SelectedAgentClis -Client Both"
], cwd=ROOT, capture_output=True, text=True, timeout=20)
self.assertEqual(skipped.returncode, 0, skipped.stdout + skipped.stderr)
self.assertEqual(skipped.stdout.splitlines()[-1], 'claude')
self.assertIn('Codex CLI update skipped', skipped.stdout + skipped.stderr)
npm = subprocess.run([
shell, '-NoProfile', '-Command',
"$ErrorActionPreference='Stop'; . ./scripts/lib/install-options.ps1; "
"function Test-CodexProcessActive { $false }; function Test-GlobalNpmCodex { $true }; "
"function Invoke-PluginCommand { param($Command,$Arguments,[switch]$DryRun,[switch]$Summary) "
"Write-Output ($Command + ':' + ($Arguments -join ' ')) }; Update-SelectedAgentClis -Client Codex"
], cwd=ROOT, capture_output=True, text=True, timeout=20)
self.assertEqual(npm.returncode, 0, npm.stdout + npm.stderr)
self.assertEqual(npm.stdout.strip(), 'npm:install -g @openai/codex@latest')
bash = (str(Path(os.environ.get('ProgramFiles', 'C:/Program Files')) / 'Git/bin/bash.exe')
if os.name == 'nt' else shutil.which('bash'))
if bash and Path(bash).exists():
result = subprocess.run([
bash, '-c',
'. ./scripts/lib/install-options.sh; codex_process_active() { return 0; }; '
'global_npm_codex() { return 0; }; run_plugin_command() { shift; printf "%s\\n" "$1"; }; '
'client=both; dry_run=false; summary=false; update_selected_agent_clis'
], cwd=ROOT, capture_output=True, text=True, timeout=20)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertEqual(result.stdout.splitlines(), ['claude'])
self.assertIn('Codex CLI update skipped', result.stderr)
def test_flashbang_toggle_is_applied_by_both_manifest_writers(self): def test_flashbang_toggle_is_applied_by_both_manifest_writers(self):
shells = [('powershell', shutil.which('powershell') or shutil.which('pwsh')), shells = [('powershell', shutil.which('powershell') or shutil.which('pwsh')),
('bash', str(Path(os.environ.get('ProgramFiles', 'C:/Program Files')) / 'Git/bin/bash.exe') ('bash', str(Path(os.environ.get('ProgramFiles', 'C:/Program Files')) / 'Git/bin/bash.exe')
@@ -165,8 +100,13 @@ class InstallOptionsTests(unittest.TestCase):
package = root / 'generated' / (client + '-bash') package = root / 'generated' / (client + '-bash')
(package / 'skills').mkdir(parents=True) (package / 'skills').mkdir(parents=True)
(package / 'skills' / 'test.txt').write_text('managed') (package / 'skills' / 'test.txt').write_text('managed')
if client == 'claude':
(package / 'statusline').mkdir()
(package / 'statusline' / 'statusline.sh').write_text('managed status line')
config = ('[[hooks.Stop]]\n[[hooks.Stop.hooks]]\ncommand = \'bash "__AI_CONFIG_ROOT__/flashbang.sh"\'\n' config = ('[[hooks.Stop]]\n[[hooks.Stop.hooks]]\ncommand = \'bash "__AI_CONFIG_ROOT__/flashbang.sh"\'\n'
if client == 'codex' else '{"hooks":{"Stop":[{"hooks":[{"command":"bash flashbang.sh"}]}]}}') '[tui]\nstatus_line = ["model"]\n' if client == 'codex' else
'{"hooks":{"Stop":[{"hooks":[{"command":"bash flashbang.sh"}]}]},'
'"statusLine":{"type":"command","command":"bash statusline.sh"}}')
(package / ('config.toml' if client == 'codex' else 'settings.json')).write_text(config) (package / ('config.toml' if client == 'codex' else 'settings.json')).write_text(config)
suffix = 'ps1' if kind == 'powershell' else 'sh' suffix = 'ps1' if kind == 'powershell' else 'sh'
for name in ('install', 'update'): for name in ('install', 'update'):
@@ -186,7 +126,7 @@ class InstallOptionsTests(unittest.TestCase):
'select_configured_plugins() { :; }\nsync_configured_plugins() { :; }\n' 'select_configured_plugins() { :; }\nsync_configured_plugins() { :; }\n'
'run_plugin_command() { printf "CLI-MOCK %s\\n" "$2"; }\n') 'run_plugin_command() { printf "CLI-MOCK %s\\n" "$2"; }\n')
environment = dict(os.environ, HOME=home.as_posix(), TEST_INSTALL_HOME=str(home)) environment = dict(os.environ, HOME=home.as_posix(), TEST_INSTALL_HOME=str(home))
for name, quick, answers in [('install', False, 'y\nn\nn\nn\n'), ('update', True, '')]: for name, quick, answers in [('install', False, 'n\nn\nn\n'), ('update', True, '')]:
entry = str(commands / (name + '.' + suffix)) entry = str(commands / (name + '.' + suffix))
command = ([executable, '-NoProfile', '-File', entry, '-Shell', 'Bash', '-Client', 'Both', '-Summary'] command = ([executable, '-NoProfile', '-File', entry, '-Shell', 'Bash', '-Client', 'Both', '-Summary']
if kind == 'powershell' else [executable, entry, '--shell', 'bash', '--client', 'both', '--summary']) if kind == 'powershell' else [executable, entry, '--shell', 'bash', '--client', 'both', '--summary'])
@@ -194,16 +134,18 @@ class InstallOptionsTests(unittest.TestCase):
command.append('-Quick' if kind == 'powershell' else '--quick') command.append('-Quick' if kind == 'powershell' else '--quick')
result = subprocess.run(command, env=environment, input=answers, capture_output=True, text=True, timeout=25) result = subprocess.run(command, env=environment, input=answers, capture_output=True, text=True, timeout=25)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr) self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertTrue('CLI-MOCK codex' in result.stdout or self.assertNotIn('CLI-MOCK', result.stdout)
'Codex CLI update skipped' in result.stdout + result.stderr)
self.assertIn('CLI-MOCK claude', result.stdout)
if quick: if quick:
self.assertNotIn('Enable the Flashbang', result.stdout + result.stderr) self.assertNotIn('Enable the Flashbang', result.stdout + result.stderr)
self.assertNotIn('Apply the custom status line', result.stdout + result.stderr)
state = json.loads((home / '.my-ai-configuration/selection.json').read_text()) state = json.loads((home / '.my-ai-configuration/selection.json').read_text())
self.assertTrue(state['update_agents'])
self.assertFalse(state['flashbang']) self.assertFalse(state['flashbang'])
self.assertFalse(state['statusline'])
self.assertEqual(state['shell'], 'bash') self.assertEqual(state['shell'], 'bash')
self.assertNotIn('Stop', json.loads((home / '.claude/settings.json').read_text()).get('hooks', {})) self.assertNotIn('Stop', json.loads((home / '.claude/settings.json').read_text()).get('hooks', {}))
self.assertNotIn('statusLine', json.loads((home / '.claude/settings.json').read_text()))
self.assertFalse((home / '.claude/statusline/statusline.sh').exists())
self.assertNotIn('status_line', tomllib.loads((home / '.codex/config.toml').read_text()).get('tui', {}))
if __name__ == '__main__': if __name__ == '__main__':
+9 -8
View File
@@ -25,7 +25,7 @@ class SelectionTests(unittest.TestCase):
capture_output=True, text=True, timeout=10) capture_output=True, text=True, timeout=10)
def save(self, *arguments): def save(self, *arguments):
result = self.run_state('write', '--shell', 'powershell', '--update-agents', 'false', '--flashbang', 'false', '--client', 'both', *arguments) result = self.run_state('write', '--shell', 'powershell', '--flashbang', 'false', '--statusline', 'false', '--client', 'both', *arguments)
self.assertEqual(result.returncode, 0, result.stderr) self.assertEqual(result.returncode, 0, result.stderr)
def test_missing_selection_fails_without_creating_state(self): def test_missing_selection_fails_without_creating_state(self):
@@ -59,30 +59,31 @@ class SelectionTests(unittest.TestCase):
self.assertIn('without Quick', result.stderr) self.assertIn('without Quick', result.stderr)
migrated = json.loads(self.run_state('read', '--allow-legacy').stdout) migrated = json.loads(self.run_state('read', '--allow-legacy').stdout)
self.assertEqual(migrated['shell'], 'powershell') self.assertEqual(migrated['shell'], 'powershell')
self.assertFalse(migrated['update_agents'])
self.assertTrue(migrated['flashbang']) self.assertTrue(migrated['flashbang'])
self.assertTrue(migrated['statusline'])
def test_boolean_options_are_required_and_preserved(self): def test_boolean_options_are_required_and_preserved(self):
self.save() self.save()
state = json.loads(self.run_state('read').stdout) state = json.loads(self.run_state('read').stdout)
self.assertFalse(state['update_agents'])
self.assertFalse(state['flashbang']) self.assertFalse(state['flashbang'])
self.assertFalse(state['statusline'])
state['flashbang'] = 'false' state['flashbang'] = 'false'
self.path.write_text(json.dumps(state)) self.path.write_text(json.dumps(state))
self.assertNotEqual(self.run_state('read').returncode, 0) self.assertNotEqual(self.run_state('read').returncode, 0)
def test_semantic_retrieval_option_round_trips(self): def test_semantic_retrieval_option_round_trips(self):
result = self.run_state('write', '--shell', 'bash', '--client', 'both', '--update-agents', 'false', result = self.run_state('write', '--shell', 'bash', '--client', 'both',
'--flashbang', 'true', '--semantic-retrieval', 'true') '--flashbang', 'true', '--statusline', 'true', '--semantic-retrieval', 'true')
self.assertEqual(result.returncode, 0, result.stderr) self.assertEqual(result.returncode, 0, result.stderr)
state = json.loads(self.run_state('read').stdout) state = json.loads(self.run_state('read').stdout)
self.assertTrue(state['semantic_retrieval']) self.assertTrue(state['semantic_retrieval'])
self.assertTrue(state['statusline'])
self.assertIn('semantic_retrieval\ttrue', self.run_state('read', '--format', 'tsv').stdout) self.assertIn('semantic_retrieval\ttrue', self.run_state('read', '--format', 'tsv').stdout)
def test_invalid_write_preserves_last_selection(self): def test_invalid_write_preserves_last_selection(self):
self.save('--selected', 'Superpowers') self.save('--selected', 'Superpowers')
original = self.path.read_bytes() original = self.path.read_bytes()
result = self.run_state('write', '--shell', 'bash', '--update-agents', 'false', '--flashbang', 'true', '--client', 'codex', '--selected', 'Unknown') result = self.run_state('write', '--shell', 'bash', '--flashbang', 'true', '--client', 'codex', '--selected', 'Unknown')
self.assertNotEqual(result.returncode, 0) self.assertNotEqual(result.returncode, 0)
self.assertEqual(self.path.read_bytes(), original) self.assertEqual(self.path.read_bytes(), original)
@@ -108,7 +109,7 @@ class SelectionTests(unittest.TestCase):
save = subprocess.run([shell, '-NoProfile', '-Command', save = subprocess.run([shell, '-NoProfile', '-Command',
"$ErrorActionPreference = 'Stop'; . (Join-Path $env:TEST_REPOSITORY_ROOT 'scripts/lib/selection-state.ps1'); " "$ErrorActionPreference = 'Stop'; . (Join-Path $env:TEST_REPOSITORY_ROOT 'scripts/lib/selection-state.ps1'); "
"Save-UpdateSelection -HomePath $env:TEST_SELECTION_HOME -RepositoryRoot $env:TEST_REPOSITORY_ROOT " "Save-UpdateSelection -HomePath $env:TEST_SELECTION_HOME -RepositoryRoot $env:TEST_REPOSITORY_ROOT "
"-Shell PowerShell -Client Both -UpdateAgents $false -Flashbang $false -Plugins @{ Selected = @([pscustomobject]@{name='Superpowers'}); Deselected = @() }"], "-Shell PowerShell -Client Both -Flashbang $false -StatusLine $false -Plugins @{ Selected = @([pscustomobject]@{name='Superpowers'}); Deselected = @() }"],
env=environment, capture_output=True, text=True, timeout=10) env=environment, capture_output=True, text=True, timeout=10)
self.assertEqual(save.returncode, 0, save.stdout + save.stderr) self.assertEqual(save.returncode, 0, save.stdout + save.stderr)
original = self.path.read_bytes() original = self.path.read_bytes()
@@ -140,7 +141,7 @@ class SelectionTests(unittest.TestCase):
if not shell: if not shell:
self.skipTest('Bash unavailable') self.skipTest('Bash unavailable')
save = subprocess.run([shell, '-c', save = subprocess.run([shell, '-c',
'set -euo pipefail; root=$1; home_path=$2; shell=powershell; client=both; update_agents=false; flashbang=false; ' 'set -euo pipefail; root=$1; home_path=$2; shell=powershell; client=both; flashbang=false; statusline=false; '
'selected_plugins=(Superpowers); deselected_plugins=(); ' 'selected_plugins=(Superpowers); deselected_plugins=(); '
'. "$root/scripts/lib/selection-state.sh"; save_update_selection', '. "$root/scripts/lib/selection-state.sh"; save_update_selection',
'test', ROOT.as_posix(), self.home.as_posix()], 'test', ROOT.as_posix(), self.home.as_posix()],
+35 -8
View File
@@ -4,19 +4,39 @@ $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 = @()
$ruleLoadEntries = @()
foreach ($ruleFile in Get-ChildItem $rulesRoot -File -Filter '*.md' | Where-Object Name -ne 'general.md') {
$ruleSources += [pscustomobject]@{ Path = $ruleFile.Name; Skill = "rules-$($ruleFile.BaseName)" }
$ruleLoadEntries += [pscustomobject]@{ Path = $ruleFile.Name; Skill = "rules-$($ruleFile.BaseName)" }
}
foreach ($ruleDirectory in Get-ChildItem $rulesRoot -Directory) {
foreach ($ruleFile in Get-ChildItem $ruleDirectory.FullName -File -Filter '*.md' -Recurse) {
$relative = $ruleFile.FullName.Substring($ruleDirectory.FullName.Length).TrimStart([char[]]@('\','/')).Replace('\','/')
if ($relative -eq 'index.md') {
$skill = "rules-$($ruleDirectory.Name)"
$ruleLoadEntries += [pscustomobject]@{ Path = "$($ruleDirectory.Name)/index.md"; Skill = $skill }
} else {
$parts = $relative.Substring(0, $relative.Length - 3).Split([char[]]@('/'))
$parts = @($parts | Where-Object { $_ -ne 'references' })
$skill = "rules-$($ruleDirectory.Name)-$($parts -join '-')"
}
if ($ruleSources.Skill -contains $skill) { throw "Duplicate generated rule skill name: $skill" }
$ruleSources += [pscustomobject]@{ Path = "$($ruleDirectory.Name)/$relative"; Skill = $skill }
}
}
$ruleSkillCount = $ruleSources.Count
$globalInstructions = Get-Content (Join-Path $root 'shared/global-instructions.md') -Raw
foreach ($ruleSource in $ruleLoadEntries) {
$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 +65,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/index.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" }
+28 -10
View File
@@ -10,22 +10,33 @@ 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 -type f -name '*.md' ! -name general.md -printf '%P\n' | sort)
rule_skill_count=${#rule_paths[@]}
global_instructions=$(<"$root/shared/global-instructions.md")
rule_skill_name() {
local path=$1 directory relative
if [[ "$path" != */* ]]; then printf 'rules-%s' "${path%.md}"; return; fi
directory=${path%%/*}
relative=${path#*/}
if [[ "$relative" == index.md ]]; then printf 'rules-%s' "$directory"; return; fi
relative=${relative#references/}
relative=${relative%.md}
relative=${relative//\//-}
printf 'rules-%s-%s' "$directory" "$relative"
}
for rule_path in "${rule_paths[@]}"; do
if [[ "$rule_path" != */* || "$rule_path" == */index.md ]]; then
skill_name=$(rule_skill_name "$rule_path")
expected_rule="\`rules/$rule_path\` / \`$skill_name\`"
printf '%s' "$global_instructions" | grep -Fq -- "$expected_rule"
fi
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 +72,13 @@ 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/index.md'
if [ "$client" = claude ]; then
for rule_path in "${rule_paths[@]}"; do
skill_name=$(rule_skill_name "$rule_path")
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)
+18 -6
View File
@@ -1,13 +1,25 @@
# Personal global instructions # Global Instructions
This file is generated into client-specific instruction files. Do not edit generated copies. Read `~/.my-ai-configuration/instructions/__CLIENT__.md` first when present; it is the user's direct instruction. System and developer requirements still apply.
Before applying generated instructions, read `~/.my-ai-configuration/instructions/__CLIENT__.md` when it exists. Treat its contents as direct user instructions that override generated rules, skills, and plugins. System and developer safety requirements remain non-overridable. For credentials, run the child command through `~/.my-ai-configuration/bin/with-credential.__SHELL__` with the client and credential key. Never expose credentials in commands, output, logs, or files.
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. ## Loading rules
__RULE_LOADING__ Load rules just in time. First identify the changed area, languages, frameworks, tools, and security or workflow concerns from the task and nearby files. Then load the mandatory baseline and only rules whose triggers match; never preload or recursively read the complete rules tree.
For Codex, read the listed rule path directly. For Claude, invoke the matching `rules-*` skill by its exact name. When a technology directory applies, load its index skill/file first, then load only the topic skills/files that match the work. An index is a small routing map, not a request to load every linked topic. Load cross-cutting rules independently when their stated trigger applies. If a needed rule is not listed or installed, continue with applicable rules and repository conventions.
Every code, configuration, infrastructure, review, test, script, hook, or integration task: `rules/security/index.md` / `rules-security`.
Focused security: authentication, authorization, sessions, tokens, permissions, tenants => `rules/security/references/auth.md` / `rules-security-auth`; HTTP, requests, serialization, endpoints => `api.md` / `rules-security-api`; browser, cookies, redirects, XSS, CSRF => `web.md` / `rules-security-web`; databases, queries, persistence, sensitive data => `data.md` / `rules-security-data`; files, uploads, archives, paths, processes, IPC, deserialization => `files.md` / `rules-security-files`; network, URLs, proxies, TLS, SSRF => `network.md` / `rules-security-network`; cryptography, secrets, keys, credentials => `crypto.md` / `rules-security-crypto`; dependencies, packages, plugins, builds, deployment, CI => `supply-chain.md` / `rules-security-supply-chain` (remaining paths under `rules/security/references/`).
Technology indexes: Angular => `rules/angular/index.md` / `rules-angular`; C#/.NET => `rules/csharp/index.md` / `rules-csharp`; Java/JVM => `rules/java/index.md` / `rules-java`; TypeScript/ECMAScript => `rules/typescript/index.md` / `rules-typescript`; UI/UX => `rules/ui-ux/index.md` / `rules-ui-ux`; WPF/XAML/MVVM => `rules/wpf/index.md` / `rules-wpf`.
Cross-cutting: unclear requirements or significant choices => `rules/decision-rule.md` / `rules-decision-rule`; completion or review readiness => `rules/definition-of-done.md` / `rules-definition-of-done`; Git operations => `rules/git.md` / `rules-git`; Microsoft 365, Azure DevOps, Teams => `rules/microsoft.md` / `rules-microsoft`; delegation or long-running work => `rules/orchestration.md` / `rules-orchestration`; behavior-preserving refactors => `rules/refactoring.md` / `rules-refactoring`.
## Tools and delegation
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.
Main agent owns state and decisions. Delegate only for independent work where benefit exceeds context/tool cost. Use one implementer for contained work; subagents never spawn agents. Pass minimal context and verify handoffs. The main agent owns state and decisions. Delegate only independent work when it saves effort; use one implementer for contained work. Subagents do not spawn agents; pass minimal context and verify handoffs.
+9 -4
View File
@@ -371,8 +371,9 @@ class Index:
self.root = root.resolve() self.root = root.resolve()
self.data = data.resolve() self.data = data.resolve()
self.data.mkdir(parents=True, exist_ok=True) self.data.mkdir(parents=True, exist_ok=True)
identity = json.dumps([os.path.normcase(str(self.root)), MODEL, EMBEDDING_DIMENSIONS, identity = json.dumps([os.path.normcase(str(self.root)), MODEL, MODEL_REVISION,
INDEX_VERSION, CHUNK_TOKENS, OVERLAP_TOKENS]) EMBEDDING_DIMENSIONS, RETRIEVAL_INSTRUCTION, INDEX_VERSION,
CHUNK_TOKENS, OVERLAP_TOKENS])
database = hashlib.sha256(identity.encode()).hexdigest() + ".sqlite3" database = hashlib.sha256(identity.encode()).hexdigest() + ".sqlite3"
self.db = sqlite3.connect(self.data / database, check_same_thread=False, timeout=30) self.db = sqlite3.connect(self.data / database, check_same_thread=False, timeout=30)
self.db.execute("create table if not exists chunks (path text, symbol text, text text, vector blob)") self.db.execute("create table if not exists chunks (path text, symbol text, text text, vector blob)")
@@ -468,8 +469,12 @@ class Index:
def cache_key(self, query: str, top_k: int): def cache_key(self, query: str, top_k: int):
files = self.db.execute("select path, digest from files order by path").fetchall() files = self.db.execute("select path, digest from files order by path").fetchall()
state = json.dumps(files, separators=(",", ":")) identity = json.dumps([
return hashlib.sha256(f"{top_k}\0{query}\0{state}".encode("utf-8")).hexdigest() query, top_k, files,
RERANKER_MODEL, RERANKER_MODEL_REVISION, RETRIEVAL_INSTRUCTION,
DENSE_CANDIDATES, LEXICAL_CANDIDATES, RERANK_CANDIDATES, FINAL_RESULTS, RRF_K,
], ensure_ascii=False, separators=(",", ":"))
return hashlib.sha256(identity.encode("utf-8")).hexdigest()
def search(self, query: str, top_k: int = FINAL_RESULTS): def search(self, query: str, top_k: int = FINAL_RESULTS):
if not query.strip(): if not query.strip():
+12 -36
View File
@@ -1,38 +1,14 @@
# Angular Code Guidelines # Angular Rules
* Use modern Angular patterns and current framework conventions. For Angular work, open the focused topic rule that matches the change. Apply the security and accessibility rule whenever browser-facing behavior is affected.
* Prefer standalone components, directives and pipes for new code.
* Avoid introducing `NgModule` unless the existing project architecture requires it.
* Keep components small and focused.
* Keep templates simple.
* Move complex logic out of templates.
* Keep business logic out of components where practical.
* Prefer services, facades or dedicated domain logic for non-trivial behavior.
* Avoid god components and god services.
* Keep dependencies explicit.
* Prefer composition over inheritance.
* Avoid unnecessary abstractions.
* Avoid unnecessary wrapper components.
* Follow the existing project architecture unless there is a strong reason to change it.
## Safety Rule | Topic | Codex rule | Claude skill |
| --- | --- | --- |
* Angular frontend code is never a security boundary. | Components and state | `references/components-and-state.md` | `rules-angular-components-and-state` |
* Client-side validation, guards, hidden controls and disabled UI elements do not replace backend enforcement. | Templates and styling | `references/templates-and-styling.md` | `rules-angular-templates-and-styling` |
* Do not bypass Angular's built-in security mechanisms without a verified and explicit reason. | RxJS | `references/rxjs.md` | `rules-angular-rxjs` |
* Prefer safe framework defaults over custom low-level behavior. | Services and HTTP | `references/services-and-http.md` | `rules-angular-services-and-http` |
| Forms and routing | `references/forms-and-routing.md` | `rules-angular-forms-and-routing` |
## Detailed guidance | Security and accessibility | `references/security-and-accessibility.md` | `rules-angular-security-and-accessibility` |
| Performance | `references/performance.md` | `rules-angular-performance` |
Read the matching reference file before working in that area: | Testing and architecture | `references/testing-and-architecture.md` | `rules-angular-testing-and-architecture` |
| Read this reference for... | ...this kind of work |
| --- | --- |
| `references/components-and-state.md` | Component structure, signal-based state |
| `references/templates-and-styling.md` | Template markup, CSS/styling |
| `references/rxjs.md` | RxJS observables and subscriptions |
| `references/services-and-http.md` | Dependency injection, services, HTTP/API access |
| `references/forms-and-routing.md` | Reactive forms, routing |
| `references/security-and-accessibility.md` | Security, DOM/browser APIs, accessibility |
| `references/performance.md` | Performance work and its priority order |
| `references/testing-and-architecture.md` | Testing, architecture, code quality, dependency rules |
@@ -1,5 +1,8 @@
## Components ## Components
* Prefer standalone components, directives, and pipes for new work. Avoid introducing `NgModule` unless the existing architecture requires it.
* Keep components small; move substantial business logic to a focused service or domain layer.
* Keep dependencies explicit and follow the feature architecture. Avoid god components, god services, unnecessary wrappers, and abstractions without a concrete need.
* Use `ChangeDetectionStrategy.OnPush` unless there is a concrete reason not to. * Use `ChangeDetectionStrategy.OnPush` unless there is a concrete reason not to.
* Prefer signal-based state for local component state. * Prefer signal-based state for local component state.
* Prefer `computed` for derived state. * Prefer `computed` for derived state.
-117
View File
@@ -1,117 +0,0 @@
# C# Code Guidelines
* Write clear, readable, maintainable code first.
* Optimize only based on measurements and profiling.
* Prefer simple solutions over clever abstractions.
* Follow current C# and .NET conventions.
* Prefer immutable data where practical.
* Keep methods small and focused.
* Keep classes focused on a single responsibility.
* Avoid unnecessary inheritance.
* Prefer composition over inheritance.
* Use `sealed` when inheritance is not intended.
* Avoid unnecessary abstractions and wrapper layers.
* Avoid premature generalization.
* Prefer explicit code over hidden magic.
* Avoid unnecessary reflection.
* Avoid unnecessary runtime type checks.
* Avoid unnecessary allocations in hot paths.
* Avoid unnecessary collection copies.
* Avoid unnecessary string allocations.
* Avoid unnecessary LINQ in performance-critical paths.
* Use LINQ where it improves readability and performance is irrelevant.
* Choose collections based on access patterns.
* Preallocate collection capacity when the expected size is known.
* Avoid large mutable structs.
* Use `Span<T>` and related APIs only when they provide a measurable benefit.
* Prefer `Task` over `ValueTask` unless `ValueTask` is justified by profiling.
* Use async only for genuinely asynchronous operations.
* Keep async code async throughout the call chain.
* Avoid `.Result`, `.Wait()` and synchronous blocking of async operations.
* Propagate `CancellationToken` through asynchronous APIs.
* Do not create unnecessary background tasks.
* Limit concurrency when processing large workloads.
* Avoid uncontrolled `Task.WhenAll` over large collections.
* Avoid exceptions for normal control flow.
* Fail fast on invalid arguments and invalid state.
* Avoid swallowing exceptions.
* Preserve meaningful exception context.
* Use dependency injection where dependencies require substitution or lifecycle management.
* Avoid service locator patterns.
* Avoid excessive constructor dependencies.
* Keep application, domain and infrastructure concerns separated.
* Avoid unnecessary cross-layer coupling.
* Prefer explicit dependencies.
* Minimize global and static mutable state.
* Design APIs with clear contracts.
* Prefer strongly typed models over loosely typed dictionaries or dynamic objects.
* Validate input at system boundaries.
* Avoid duplicate validation deep inside trusted internal code.
* Treat external input as untrusted.
* Do not expose internal implementation details through public APIs.
* Avoid leaking sensitive information through logs or exceptions.
* Use structured logging.
* Avoid excessive logging in hot paths.
* Avoid logging sensitive data.
* Prefer database-side filtering, projection and aggregation.
* Do not load data that is not needed.
* Avoid N+1 database queries.
* Use no-tracking queries for read-only EF Core operations where appropriate.
* Keep database round trips minimal.
* Avoid unnecessary serialization and deserialization.
* Reuse expensive resources such as HTTP clients and database infrastructure correctly.
* Do not optimize micro-level CPU operations before fixing architectural, database or I/O bottlenecks.
* Prefer algorithms and data structures with appropriate complexity.
* Measure allocations, GC pressure, CPU usage and latency when investigating performance.
* Write code that is easy to test.
* Avoid tests coupled to implementation details.
* Prefer deterministic behavior.
* Avoid hidden side effects.
* Keep naming consistent and descriptive.
* Avoid abbreviations unless they are established domain terminology.
* Remove dead code instead of keeping it as commented-out code.
* Add comments or API documentation when they clarify non-obvious decisions, public contracts, or project conventions require them; otherwise prefer self-explanatory code.
* Follow existing project conventions unless there is a strong reason to change them.
* Do not introduce new libraries when the BCL or existing dependencies already solve the problem adequately.
* Prefer fewer dependencies.
* Keep dependency versions current and supported.
* Treat warnings seriously.
* Do not suppress analyzers without a concrete reason.
* Keep performance improvements readable unless the hot path clearly justifies additional complexity.
* Prefer correctness, maintainability and predictable behavior over theoretical micro-optimizations.
## C# Style & Safety
* Prefer modern, readable C# syntax and current .NET conventions.
* Prefer simple, explicit code over outdated or unnecessarily verbose patterns.
* Treat collection-type guidance as a default; follow established project conventions and public API contracts when they require a different choice.
* Prefer concrete collection types for internal parameters, properties, and local APIs when they express the intended contract without reducing needed flexibility.
* Do not introduce collection interfaces solely for abstraction. Use `IReadOnlyList<T>`, `IEnumerable<T>`, `IDictionary<TKey, TValue>`, `IList<T>`, or `ICollection<T>` only when a public API or boundary intentionally exposes that capability.
* Prefer `Dictionary<TKey, TValue>`, `List<T>`, arrays, or other concrete types when the implementation type is known and intentional.
* Prefer strongly typed models over `object`, `dynamic`, loosely typed dictionaries or string-based contracts.
* Validate external input at system boundaries.
* Fail fast on invalid arguments and impossible states.
* Avoid unsafe code unless explicitly required.
* Avoid reflection when a strongly typed solution is practical.
* Avoid hidden side effects and implicit global state.
* Prefer immutable or readonly data where practical.
* Avoid exposing mutable internal collections directly.
* Prefer safe framework APIs over custom low-level implementations.
* Never weaken validation, authorization, certificate checks, encryption or other security controls for convenience.
* Do not log secrets, tokens, credentials or sensitive data.
* Treat all external data as untrusted.
* Prefer correctness and security over minor convenience or micro-optimizations.
## Performance Priority
Prioritize performance work in this order:
* Architecture
* Database access
* Network and I/O
* Algorithms and data structures
* Concurrency
* Allocations and GC pressure
* CPU micro-optimizations
Never optimize based purely on assumptions.
+14
View File
@@ -0,0 +1,14 @@
# C# Rules
For C# or .NET work, open only the topic rules relevant to the task. Follow repository conventions and load applicable shared security rules as well.
| Topic | Codex rule | Claude skill |
| --- | --- | --- |
| Language style and code organization | `references/coding-style.md` | `rules-csharp-coding-style` |
| Types, collections, and public contracts | `references/types-and-collections.md` | `rules-csharp-types-and-collections` |
| Architecture and API boundaries | `references/architecture-and-apis.md` | `rules-csharp-architecture-and-apis` |
| Async work and concurrency | `references/async-and-concurrency.md` | `rules-csharp-async-and-concurrency` |
| Performance | `references/performance.md` | `rules-csharp-performance` |
| Security | `references/security.md` | `rules-csharp-security` |
| Validation, errors, and logging | `references/safety-and-errors.md` | `rules-csharp-safety-and-errors` |
| Tests, dependencies, and analyzers | `references/testing-and-dependencies.md` | `rules-csharp-testing-and-dependencies` |
@@ -0,0 +1,15 @@
# C# Architecture and APIs
Apply these rules when designing public APIs, service boundaries, or application architecture.
* Give APIs explicit, strongly typed contracts. Validate external input at the boundary and keep trusted internal paths free of redundant checks.
* Separate request models from persistence and domain models. Bind only fields the caller may set, and project only fields the caller may read.
* Authenticate and authorize before accessing protected resources. Enforce resource ownership and tenant scope in the data access path, not only in the UI or route layer.
* Bound page size, payload size, query complexity, and operation cost. Use explicit pagination for potentially large collections.
* Keep application, domain, and infrastructure concerns separate. Avoid cross-layer coupling and service-locator patterns.
* Use dependency injection when substitution or lifecycle management is needed. Keep dependencies explicit and avoid excessive constructor dependencies.
* Prefer composition over inheritance; use inheritance only when the contract requires it. Seal types when extension is not intended.
* Keep classes and methods focused. Avoid wrappers, abstractions, reflection, and global mutable state without a concrete need.
* Preserve established project and public API conventions unless a concrete compatibility or correctness issue requires a change.
* Keep authorization decisions close to the protected operation and fail closed when identity, ownership, or policy evaluation is unavailable.
* Avoid returning persistence entities, internal exception details, or fields that are not part of the public contract.
@@ -0,0 +1,13 @@
# C# Async and Concurrency
Apply these rules when adding or changing asynchronous APIs, background work, or concurrent processing.
* Use asynchronous APIs for genuinely asynchronous I/O; do not wrap synchronous work in unnecessary tasks.
* Keep asynchronous operations async through the call chain. Avoid `.Result`, `.Wait()`, and other synchronous waits that can block threads or deadlock.
* Accept and propagate `CancellationToken` through work that can take meaningful time or be cancelled by its caller.
* Bound concurrency for large or unbounded workloads. Avoid creating a task per item or using uncontrolled `Task.WhenAll`; use an appropriate queue or concurrency limiter when work must be throttled.
* Use `ValueTask` only when profiling supports it and its usage contract is clear; otherwise prefer `Task`.
* Keep shared state safe under concurrent access and preserve cancellation and exception behavior when coordinating tasks. Prefer immutable messages or synchronization primitives with clear ownership over unsynchronized shared mutation.
* Avoid holding locks across `await`, blocking thread-pool threads, and starting background work whose lifetime is detached from its owner.
* For producer/consumer work, apply backpressure and bound buffered items so a fast producer cannot exhaust memory.
* Retry only transient failures, with bounded attempts and delay. Preserve idempotency and cancellation, and avoid multiplying retries across nested layers.
@@ -0,0 +1,13 @@
# C# Coding Style
Apply these rules when editing C# syntax, class structure, and everyday code organization.
* Follow current C# and .NET conventions and the style already used in the project.
* Prefer direct, readable code over clever control flow, hidden magic, or outdated verbosity.
* Keep classes, methods, and responsibilities focused. Use descriptive names and established domain terms; avoid unexplained abbreviations.
* Prefer composition over inheritance. Use inheritance only for a real substitutable contract, and seal types when extension is not intended.
* Avoid unnecessary wrappers, abstractions, reflection, runtime type checks, and global mutable state.
* Prefer immutable or readonly state when it simplifies ownership and reduces accidental mutation.
* Keep side effects visible. Make behavior deterministic and easy to test where practical.
* Add comments or API documentation only for non-obvious decisions, public contracts, or documented project requirements.
* Remove dead code instead of leaving commented-out implementations.
@@ -0,0 +1,14 @@
# C# Performance
Apply these rules when optimizing code or reviewing runtime and resource costs.
* Establish a representative baseline before changing code. Measure the affected workload, input size, concurrency, and environment; distinguish average latency from tail latency and throughput.
* Find the dominant cost before optimizing: architecture, database access, network and I/O, algorithms, concurrency, allocations and GC, then CPU-level work.
* Choose collections and algorithms for actual access patterns. Check asymptotic cost and data growth; avoid repeated scans, unnecessary sorting, copies, and unbounded result sets.
* Keep data close to where it is processed. Filter and project in the database, avoid N+1 queries, bound page sizes, and avoid materializing rows or columns the caller does not use.
* Stream large inputs and outputs when buffering the whole value would waste memory. Bound queues, batches, and parallel work to match downstream capacity.
* Reduce allocations in measured hot paths: avoid repeated string construction, iterator/delegate capture, boxing, and collection resizing when profiling identifies them as material costs.
* Consider pooling only for expensive reusable objects with clear ownership and reset rules. Pooling can retain memory or leak state between users; measure it and cap retained capacity.
* Use `Span<T>`, `ValueTask`, preallocation, or specialized low-level APIs only when their benefit is demonstrated and they preserve readable, safe ownership.
* Cache only when repeated work is measurable and the key, lifetime, size bound, invalidation, and sensitive-data handling are explicit.
* After an optimization, run the same workload and compare correctness, allocations, GC, CPU, latency, and throughput against the baseline. Keep the simplest change that meets the target.
@@ -0,0 +1,14 @@
# C# Safety and Error Handling
Apply these rules when handling external input, failures, logs, or sensitive information.
* Treat external data as untrusted and validate it at system boundaries. Do not rely on static types to validate runtime input.
* Validate size, range, format, and allowed values before expensive parsing or allocation. Keep validation consistent with downstream business and security constraints.
* Fail fast for invalid arguments or impossible state. Do not use exceptions for routine control flow or swallow unexpected exceptions.
* Preserve useful exception context without exposing secrets, personal data, or internal details in logs, APIs, or user-facing errors.
* Use structured logging and avoid excessive logging, especially in hot paths. Never log credentials, tokens, or sensitive payloads.
* Keep authorization, certificate validation, encryption, and other security controls intact. Follow the focused security rules for the affected boundary.
* Keep side effects explicit and deterministic where practical; propagate cancellation and dispose owned resources correctly.
* Use narrow exception handling around the operation that can fail. Do not catch broadly merely to retry, continue with partial state, or convert failures into success.
* Avoid returning different error detail to unauthenticated callers when it would disclose whether protected accounts, records, or resources exist.
* Treat resource limits as part of safe error handling: cap request bodies, decompressed data, collection sizes, parser depth, and other attacker-controlled work.
@@ -0,0 +1,14 @@
# C# Security
Apply these rules when implementing or reviewing security-sensitive .NET code. Load the matching shared security rule for the affected API, identity, data, file, network, cryptographic, or dependency boundary as well.
* Validate untrusted input at the boundary and enforce authorization for the specific resource and operation. Static types and client-side checks do not establish trust.
* Use parameterized database APIs. If a query identifier or sort expression cannot be parameterized, select it from a fixed allowlist; never concatenate untrusted input into SQL.
* For file access, canonicalize the resolved path and verify it remains inside the authorized root. Account for reparse points and symlinks when the attacker can affect directory contents.
* For process execution, avoid a shell. Pass executable and arguments separately using APIs supported by the target framework, validate each argument, and restrict executable paths.
* Deserialize into explicit DTOs or allowlisted types. Avoid unsafe polymorphic type activation and unbounded object graphs for untrusted data.
* Bound request and parser work before allocating or processing large values. Apply timeouts to regular expressions that evaluate untrusted input and prefer patterns with predictable complexity.
* Keep secrets in an approved secret store or protected configuration source. Never embed them in assemblies, client bundles, source, diagnostics, or exception messages.
* Use platform TLS and established cryptographic APIs. Do not create custom cryptography or disable certificate validation; use cryptographically secure randomness for security tokens.
* Use framework output encoding and safe APIs for HTML, XML, and other interpreters. Do not build executable markup or queries from untrusted strings.
* Review security-sensitive changes with tests for rejected, malformed, oversized, unauthenticated, and cross-tenant inputs, as applicable.
@@ -0,0 +1,10 @@
# C# Testing and Dependencies
Apply these rules when changing tests, package references, analyzers, or build diagnostics.
* Write deterministic behavior-based tests. Avoid coupling assertions to private call order, implementation details, current time, or uncontrolled external services.
* Make code testable through clear contracts and dependencies; do not add production hooks or abstractions solely for tests without a concrete need.
* Prefer the .NET BCL and existing supported packages over new libraries when they solve the problem adequately.
* Keep dependencies minimal, maintained, compatible, and updated through the repository's normal package-management process.
* Treat compiler, analyzer, and nullable warnings seriously. Suppress a diagnostic only for a specific, documented reason and narrow the suppression to the affected code.
* Run focused tests and analyzers for the changed path; broaden verification when shared behavior or compatibility could be affected.
@@ -0,0 +1,11 @@
# C# Types and Collections
Apply these rules when designing type contracts, choosing collection shapes, or exposing data through APIs.
* Prefer strongly typed models over `object`, `dynamic`, loosely typed dictionaries, or string-based contracts.
* Prefer concrete collection types for internal parameters, properties, and local APIs when the contract does not need abstraction.
* Expose collection interfaces such as `IReadOnlyList<T>`, `IEnumerable<T>`, `IDictionary<TKey, TValue>`, or `ICollection<T>` only when their capability is part of an intentional boundary or public contract.
* Choose collection types based on lookup, ordering, mutation, and iteration needs. Pre-size collections only when expected capacity is known and material.
* Avoid large mutable structs and direct exposure of mutable internal collections.
* Prefer immutable data where practical, while preserving established API and serialization contracts.
* Use `Span<T>` and related low-level types only when the performance rule's measurement justifies their lifetime and ownership constraints.
+8
View File
@@ -0,0 +1,8 @@
# Java Rules
For Java or JVM work, open only the topic rules relevant to the task. Follow the project's target Java release and conventions, and load applicable shared security rules as well.
| Topic | Codex rule | Claude skill |
| --- | --- | --- |
| Modern Java and coding style | `references/modern-java.md` | `rules-java-modern-java` |
| Performance | `references/performance.md` | `rules-java-performance` |
@@ -0,0 +1,12 @@
# Modern Java
Apply these rules when writing or reviewing Java code.
* Match the project's configured Java release, compiler flags, libraries, and style. Use newer language or library features only when that release supports them and they improve clarity.
* Prefer clear, immutable value types. Use records for transparent data carriers and sealed types when a closed set of variants is part of the domain; do not force these forms onto mutable entities or framework-bound types.
* Use pattern matching, switch expressions, and text blocks where supported and clearer than older forms. Keep control flow explicit; streams are not automatically clearer or faster than loops.
* Prefer local `var` only when the initializer makes the type obvious. Keep public APIs explicitly typed and avoid raw types and unchecked casts.
* Keep classes and methods focused, use descriptive names, and make state and side effects easy to see. Prefer composition and small interfaces at real boundaries over speculative abstractions.
* Use try-with-resources for `AutoCloseable` resources. Preserve interruption, exception causes, and precise failure context; do not swallow exceptions or use exceptions for ordinary control flow.
* Define nullability and mutability expectations at API boundaries. Prefer standard collection and `Optional` conventions without using `Optional` for fields or parameters by default.
* Avoid deprecated APIs and obsolete date/time or concurrency utilities when supported standard replacements fit the target release. Remove dead code and unused dependencies.
@@ -0,0 +1,12 @@
# Java Performance
Apply these rules when optimizing Java runtime, memory, or throughput.
* Measure a representative workload on the target JDK and deployment configuration before optimizing. Profile first; distinguish startup, steady-state latency, tail latency, throughput, allocation, GC, and memory costs.
* Prioritize algorithmic complexity, database and network round trips, I/O, contention, allocation and GC, then instruction-level work. Fix the dominant cost rather than tuning by intuition.
* Choose collections and algorithms for actual access patterns and data growth. Avoid repeated scans, needless sorting or copying, unbounded results, and N+1 data access.
* Stream or process data in bounded chunks when whole-input buffering can exhaust memory. Bound queues, caches, thread pools, and parallel work to the capacity of downstream systems.
* Reduce allocation in profiled hot paths. Avoid premature object pooling, custom caches, and low-level tricks; pooling can retain memory or leak mutable state.
* Use concurrency only when it improves measured end-to-end throughput or latency. Account for ordering, cancellation, backpressure, contention, and failure handling.
* Use JMH for JVM microbenchmarks when isolating small operations; prevent dead-code elimination and account for warmup, forks, and measurement variance. Use production-like profiling for system behavior.
* Re-run the same workload after a change and compare correctness, CPU, allocation, GC, memory, latency, and throughput. Keep the simplest implementation that meets the measured goal.
-7
View File
@@ -1,7 +0,0 @@
# API security
* Authenticate and authorize protected endpoints; validate every request payload and identifier.
* Bound request and response sizes, pagination, query results, and expensive operations.
* Apply rate limits where abuse is plausible and keep administrative endpoints separately protected.
* Return only authorized data and never expose stack traces, internal models, or implementation details in API errors.
* Prefer explicit DTOs and schemas over deserializing arbitrary types or executable objects.
-8
View File
@@ -1,8 +0,0 @@
# Authentication and authorization security
* Use established authentication standards and password hashing; never store plaintext or reversibly encrypted passwords.
* Enforce authorization server-side for every sensitive operation and verify resource ownership and tenant boundaries.
* Use least-privilege roles, short-lived sessions or tokens where supported, and invalidate credentials after sensitive account changes.
* Validate token issuer, audience, signature, and expiry before trusting claims.
* Protect authentication and privileged actions against brute force and privilege escalation.
* Test unauthenticated, unauthorized, cross-user, and cross-tenant access paths.
-6
View File
@@ -1,6 +0,0 @@
# Cryptography and secrets security
* Never implement custom cryptographic primitives or use obsolete algorithms.
* Use established libraries, authenticated encryption, secure randomness, and verified signatures and certificates.
* Treat tokens, private keys, connection strings, and credentials as secrets; keep them out of source, URLs, command lines, logs, fixtures, and frontend code.
* Use environment-appropriate secret storage, separate environments, and rotate compromised credentials.
-7
View File
@@ -1,7 +0,0 @@
# Database and data security
* Use parameterized queries or ORM parameter binding; never concatenate untrusted input into SQL.
* Use least-privilege database accounts and protect connection strings.
* Scope tenant-owned queries explicitly and prevent cross-tenant cache, logging, and diagnostic leakage.
* Collect and retain only necessary sensitive data; encrypt it in transit and at rest where appropriate.
* Avoid raw database errors and use transactions where integrity requires atomicity.
-6
View File
@@ -1,6 +0,0 @@
# File and process security
* Validate filenames, content, sizes, and paths; prevent traversal, zip-slip, and unsafe symbolic-link use.
* Restrict access to intended directories and do not execute uploaded or untrusted files.
* Do not concatenate untrusted input into shell commands; prefer direct APIs and validate process arguments when execution is required.
* Treat local files, IPC, and deserialized input as untrusted at relevant boundaries.
-6
View File
@@ -1,6 +0,0 @@
# Network and SSRF security
* Use HTTPS and never disable certificate or TLS validation.
* Restrict inbound and outbound access to required ports, interfaces, hosts, and protocols.
* Treat user-controlled URLs as dangerous: allowlist schemes and hosts, block localhost, metadata, and internal ranges, and revalidate redirects.
* Set timeouts, response-size limits, bounded retries, and cancellation for remote operations.
-6
View File
@@ -1,6 +0,0 @@
# Supply-chain security
* Keep dependencies minimal, maintained, and from verified sources; respect lockfiles and review identity and compatibility before adding or upgrading packages.
* Pin versions where appropriate and remove unused dependencies.
* Treat plugins, build scripts, CI actions, and generated artifacts as part of the attack surface.
* Use least privilege for build and deployment identities and never expose credentials in pipelines, logs, or artifacts.
-7
View File
@@ -1,7 +0,0 @@
# Web and frontend security
* Escape or sanitize untrusted content; never inject untrusted HTML or bypass framework sanitization.
* Protect state-changing browser requests from CSRF where applicable.
* Use `HttpOnly`, `Secure`, and appropriate `SameSite` cookie settings for sensitive sessions.
* Validate redirect targets, avoid sensitive data in URLs, and use CSP and clickjacking protections where applicable.
* Treat frontend code and browser storage as visible and never rely on client-side checks for authorization.
-11
View File
@@ -1,11 +0,0 @@
# Security baseline
Apply this baseline to every code, configuration, infrastructure, or review task.
* Treat external input as untrusted and validate at trust boundaries.
* Prefer secure defaults, least privilege, explicit authorization, and fail-closed behavior.
* Never hardcode, commit, log, expose, or weaken protections for secrets and credentials.
* Use established framework and platform security mechanisms; do not invent cryptography or bypass validation, authentication, authorization, or TLS checks.
* Keep dependencies minimal and from trusted sources.
* Do not disclose sensitive data or internal implementation details in user-facing errors or diagnostics.
* Load the applicable focused security rule before changing authentication, web/UI, APIs, databases, files, network access, cryptography, secrets, or supply-chain configuration.
+13
View File
@@ -0,0 +1,13 @@
# Security Baseline
Apply this short baseline to every code, configuration, infrastructure, and review task. Load only the focused security rules that match the affected boundary; their triggers are listed in the global instructions.
* Treat external input as untrusted and validate it at the relevant trust boundary.
* Use secure defaults, least privilege, explicit authorization, and fail-closed behavior.
* Never hardcode, commit, log, expose, or weaken protections for secrets and credentials.
* Use established platform security mechanisms. Do not invent cryptography or bypass validation, authentication, authorization, or TLS checks.
* Keep dependencies minimal and trusted; avoid disclosing sensitive data or internal details in errors and diagnostics.
## Authorized security testing
The user states they are authorized to assess the software explicitly in scope for the task, including vulnerability discovery and writing proof-of-concept exploits. Treat source-local analysis and bounded, non-destructive testing in an isolated or staging environment as authorized. Keep testing within the named software and agreed assets; this authorization does not extend to adjacent systems, unrelated third-party services, or real users' data. Before active testing of a deployed service, require a clear target and limits for methods, volume, and impact. Keep proof-of-concept exploits non-destructive; do not add persistence, stealth, evasion, credential theft, or data exfiltration. System and platform safety requirements still apply.
+12
View File
@@ -0,0 +1,12 @@
# API security
* Authenticate and authorize protected endpoints; validate every request payload and identifier.
* Enforce object-level authorization on every read and write, using the authenticated principal and resource ownership at the operation that accesses the data.
* Bind only explicitly permitted request fields. Do not allow mass assignment of ownership, role, state, or other privileged properties.
* Bound request and response sizes, pagination, query results, and expensive operations.
* Apply rate limits and quotas to authentication, search, uploads, expensive operations, and other abuse-prone endpoints. Keep administrative routes separately protected.
* Define safe retry and idempotency behavior for state-changing requests. Protect against replay and duplicate side effects where relevant.
* Return only authorized fields. Do not expose stack traces, internal models, secrets, or implementation details in API responses or errors.
* Prefer explicit DTOs and schemas; reject unknown or ambiguous fields when accepting them could alter privileged state.
* Keep authorization, validation, serialization, and error responses consistent with the published schema and supported clients.
* Test malformed, oversized, unauthenticated, unauthorized, cross-user, cross-tenant, and duplicate requests within the approved scope.
+11
View File
@@ -0,0 +1,11 @@
# Authentication and authorization security
* Use established authentication standards and password hashing; never store plaintext or reversibly encrypted passwords.
* Use a maintained password-hashing scheme configured for the deployment environment. Never create password schemes or reduce work factors merely to improve latency.
* Enforce authorization server-side for every sensitive operation and verify resource ownership and tenant boundaries.
* Deny by default and check authorization on every request, including background jobs and alternate API paths. Do not trust role or identity values supplied by clients.
* Use least-privilege roles, short-lived sessions or tokens, secure renewal and revocation, and invalidate credentials after password, recovery, or privilege changes.
* Validate token algorithm, issuer, audience, signature, expiry, and any required not-before or key identifier claims before trusting it. Reject unexpected algorithms and malformed claims.
* Protect login, recovery, reset, MFA, and privileged actions against brute force, enumeration, replay, CSRF, and privilege escalation.
* Store session identifiers securely; rotate them after authentication or privilege changes and invalidate them on logout or account recovery.
* Test unauthenticated, unauthorized, cross-user, cross-tenant, expired-token, revoked-session, and recovery-flow paths.
@@ -0,0 +1,9 @@
# Cryptography and secrets security
* Never implement custom cryptographic primitives or use obsolete algorithms.
* Use maintained platform cryptography with authenticated encryption, secure randomness, and verified signatures and certificates. Use safe, documented parameter choices rather than inventing formats or defaults.
* Keep key generation, storage, access, rotation, revocation, and backup separate from application data where practical. Restrict each key to its intended purpose.
* Treat tokens, private keys, connection strings, and credentials as secrets; keep them out of source, URLs, command lines, logs, fixtures, error messages, and frontend code.
* Use environment-appropriate secret storage, separate environments, and rotate compromised or overexposed credentials. Do not silently fall back to a weaker key or algorithm when configuration is missing.
* Compare secret-derived values with established constant-time APIs when the comparison is security-sensitive.
* Do not log plaintext or retain decrypted sensitive data longer than required. Apply secure disposal only where the runtime and data type provide meaningful guarantees.
+11
View File
@@ -0,0 +1,11 @@
# Database and data security
* Use parameterized queries or ORM parameter binding; never concatenate untrusted input into SQL.
* Parameterize values in every query path, including raw SQL and stored procedures. Allowlist dynamic table names, columns, and sort directions.
* Use least-privilege database identities, separate application and migration privileges, and protect connection strings and backup credentials.
* Scope tenant- and user-owned reads, writes, joins, aggregates, background tasks, and cache keys explicitly. Do not rely on a caller having applied a filter elsewhere.
* Collect and retain only necessary sensitive data. Set retention and deletion behavior and protect backups, exports, replicas, logs, and analytics copies.
* Encrypt sensitive data in transit and at rest where appropriate; keep key access separate from database access.
* Use transactions and suitable constraints where integrity requires atomicity. Check concurrency and isolation around security-sensitive state changes.
* Avoid raw database errors and sensitive query parameters in responses, logs, traces, and metrics.
* Test injection, cross-tenant access, unauthorized mutation, stale updates, and cache isolation within the approved scope.
+10
View File
@@ -0,0 +1,10 @@
# File and process security
* Validate filenames, content, sizes, and paths; prevent traversal, zip-slip, and unsafe symbolic-link use.
* Resolve paths against a trusted root and verify the canonical result remains inside it. Account for symlink or reparse-point changes where an attacker can modify directories.
* Restrict filesystem permissions to the intended identity and directory. Use safe temporary-file creation and avoid predictable names or permissive shared locations.
* Validate upload content using limits and format-aware parsing; do not trust names or client-supplied MIME types. Store untrusted uploads outside executable or public paths.
* Limit archive entry count, nesting, total expanded size, and path destinations to prevent traversal and resource exhaustion.
* Do not execute uploaded or untrusted files. Avoid shell execution; when a process is required, fix the executable and pass validated arguments through structured process APIs.
* Treat local files, IPC, and deserialized input as untrusted at relevant boundaries. Use explicit deserialization types and bound depth, size, and processing time.
* Avoid insecure temporary-file handoff and check permissions and ownership before consuming files from shared or attacker-writable locations.
@@ -0,0 +1,9 @@
# Network and SSRF security
* Use HTTPS and never disable certificate or TLS validation.
* Restrict inbound and outbound access to required ports, interfaces, hosts, and protocols.
* Treat user-controlled URLs as dangerous. Allowlist schemes and destinations, reject loopback, link-local, private, and cloud metadata ranges unless explicitly needed, and validate the resolved destination.
* Revalidate every redirect and DNS resolution to prevent redirects, rebinding, or mixed-address answers from bypassing destination restrictions. Disable automatic redirects when they cannot be safely checked.
* Set connection and overall timeouts, response-size limits, bounded concurrency, bounded retries, and cancellation for remote operations.
* Avoid forwarding ambient credentials, cookies, or authorization headers to a new host or redirect target.
* Use controlled egress where possible and avoid exposing internal error details or response bodies from remote services.
@@ -0,0 +1,10 @@
# Supply-chain security
* Keep dependencies minimal, maintained, and from verified sources; respect lockfiles and review identity and compatibility before adding or upgrading packages.
* Verify package identity, source, maintainer, release history, transitive dependencies, and install-time behavior. Treat typosquats and unexpected ownership changes as risks.
* Pin or constrain versions according to project policy, commit lockfiles, and remove unused dependencies. Review lockfile changes instead of hand-editing generated entries.
* Review dependency and build-tool updates for breaking behavior and security advisories; upgrade deliberately and verify the resulting artifact.
* Treat plugins, build scripts, CI actions, compiler plugins, generated artifacts, and caches as part of the attack surface. Pin CI actions to reviewed immutable revisions where supported.
* Use least-privilege build and deployment identities. Separate untrusted pull-request jobs from secret-bearing jobs and protect release credentials from untrusted code.
* Never expose credentials in pipelines, logs, caches, artifacts, command output, or generated packages. Limit secret lifetime and scope.
* Verify artifact provenance and integrity before release or installation; avoid downloading and executing unverified content dynamically.
+11
View File
@@ -0,0 +1,11 @@
# Web and frontend security
* Escape or sanitize untrusted content; never inject untrusted HTML or bypass framework sanitization.
* Encode output for its exact context (HTML, attributes, JavaScript, CSS, or URL). Prefer text APIs and framework-safe templates; use a maintained sanitizer only when rich HTML is required.
* Protect state-changing browser requests from CSRF where applicable; validate origin or anti-CSRF tokens and do not treat CORS as authorization.
* Use `HttpOnly`, `Secure`, and appropriate `SameSite` cookie settings for sensitive sessions. Rotate session IDs after authentication and privilege changes.
* Validate redirect targets against an allowlist. Avoid sensitive data in URLs, referrers, browser history, analytics, and error reports.
* Apply restrictive Content Security Policy and clickjacking protections where applicable. Do not weaken security headers to accommodate unsafe inline content without a reviewed design.
* Treat frontend code, source maps, and browser storage as visible. Never place secrets there or rely on client-side checks for authorization.
* Protect state-changing operations against clickjacking and unintended form submissions when relevant; require explicit confirmation for high-impact actions.
* Keep dependencies and third-party scripts minimal. Apply integrity and origin controls to externally hosted resources where supported.
-160
View File
@@ -1,160 +0,0 @@
# TypeScript Code Guidelines
* Write clear, readable, maintainable code first.
* Prefer modern TypeScript and current ECMAScript syntax.
* Keep code simple and explicit.
* Avoid clever or overly abstract solutions.
* Follow the existing project style and architecture.
* Enable strict TypeScript settings where possible.
* Prefer `strict: true`.
* Avoid `any`.
* Prefer precise types over broad types.
* Prefer `unknown` over `any` for untrusted or unknown values.
* Narrow types explicitly before use.
* Prefer strongly typed models over loose objects.
* Avoid excessive type assertions.
* Avoid non-null assertions unless safety is guaranteed.
* Prefer discriminated unions for state and variant modeling.
* Prefer literal unions over arbitrary strings.
* Prefer enums only when they provide a clear benefit.
* Prefer `readonly` where mutation is not required.
* Prefer immutable data where practical.
* Avoid unnecessary mutation.
* Avoid global mutable state.
* Keep functions small and focused.
* Keep modules focused on a clear responsibility.
* Prefer composition over inheritance.
* Avoid unnecessary classes when functions and plain objects are sufficient.
* Avoid unnecessary interfaces and abstractions.
* Prefer concrete object types when no abstraction is required.
* Use interfaces primarily when extension, implementation contracts, or declaration merging is actually useful.
* Prefer `type` aliases for unions, compositions, mapped types, and local data models.
* Avoid unnecessary wrapper types.
* Avoid excessive generic complexity.
* Keep generic constraints explicit and meaningful.
* Avoid deeply nested conditional or mapped types unless they provide clear value.
* Prefer readable types over type-system cleverness.
* Avoid duplicated type definitions.
* Derive types from existing sources when practical.
* Do not duplicate backend contracts manually when generated or shared types are available.
* Prefer explicit return types for public APIs and non-trivial functions.
* Prefer inference for simple local variables.
* Avoid unnecessary annotations when inference is obvious.
* Prefer `const` by default.
* Use `let` only when reassignment is required.
* Avoid `var`.
* Prefer optional chaining and nullish coalescing where appropriate.
* Do not use `||` when `0`, `false`, or empty strings are valid values.
* Handle `null` and `undefined` intentionally.
* Avoid mixing `null` and `undefined` without a clear convention.
* Prefer early returns over excessive nesting.
* Avoid deeply nested control flow.
* Avoid overly large functions.
* Avoid hidden side effects.
* Keep pure logic pure where practical.
* Prefer deterministic behavior.
* Avoid implicit runtime coercion.
* Use strict equality.
* Avoid unnecessary object spreading in hot paths.
* Avoid unnecessary array copies.
* Avoid unnecessary intermediate arrays.
* Avoid repeated `.map()`, `.filter()`, or `.reduce()` chains in performance-critical paths.
* Use array methods when they improve readability and performance is not critical.
* Prefer appropriate data structures such as `Map` and `Set` when lookup behavior requires them.
* Avoid O(n²) patterns when a better structure is practical.
* Measure performance before micro-optimizing.
* Avoid unnecessary JSON serialization and parsing.
* Avoid unnecessary deep cloning.
* Avoid `JSON.parse(JSON.stringify(...))` as a cloning strategy.
* Prefer platform APIs such as `structuredClone` when deep cloning is actually required.
* Avoid synchronous blocking operations in server-side hot paths.
* Prefer asynchronous APIs for I/O.
* Always handle rejected promises.
* Avoid floating promises.
* Use `await` when sequencing is required.
* Use `Promise.all` for independent operations when safe.
* Limit concurrency for large workloads.
* Avoid uncontrolled `Promise.all` over very large collections.
* Support cancellation with `AbortSignal` where applicable.
* Clean up event listeners, timers, subscriptions, streams, and resources.
* Avoid memory leaks caused by retained closures or listeners.
* Do not swallow exceptions.
* Preserve useful error context.
* Use typed domain errors where they improve handling.
* Avoid exceptions for normal control flow.
* Validate external input at system boundaries.
* Never trust API payloads, query parameters, environment variables, storage data, or user input based solely on TypeScript types.
* Use runtime validation for untrusted external data.
* Do not assume compile-time types provide runtime safety.
* Prefer schema validation when external contracts are complex.
* Never expose secrets, tokens, credentials, or sensitive data.
* Do not log sensitive information.
* Avoid unsafe dynamic property access.
* Avoid `eval`, `Function`, and dynamic code execution.
* Avoid unsafe HTML injection.
* Sanitize untrusted HTML when rendering HTML is required.
* Avoid weakening TLS, authentication, authorization, CSP, validation, or other security controls.
* Treat all external data as untrusted.
* Prefer secure defaults.
* Do not disable security checks for convenience.
* Avoid prototype pollution risks when merging untrusted objects.
* Prefer allowlists over denylists for security-sensitive validation.
* Keep dependencies minimal.
* Prefer built-in platform APIs over adding small utility dependencies.
* Do not add dependencies without a clear reason.
* Avoid abandoned or unnecessary packages.
* Keep dependencies current and supported.
* Respect lockfiles.
* Do not manually modify generated lockfile content.
* Avoid broad dependency upgrades unrelated to the task.
* Use ESM or the project's existing module system consistently.
* Avoid mixing module systems without necessity.
* Prefer named exports unless the project convention favors default exports.
* Keep import paths consistent.
* Remove unused imports and exports.
* Avoid circular dependencies.
* Keep frontend, domain, application, and infrastructure concerns separated where applicable.
* Keep API and transport models separate from domain logic when necessary.
* Do not leak implementation details through public APIs.
* Avoid unnecessary public exports.
* Keep module boundaries intentional.
* Write code that is easy to test.
* Prefer behavior-based tests.
* Avoid tests coupled to implementation details.
* Mock only external boundaries or expensive dependencies where practical.
* Keep tests deterministic.
* Avoid time-, network-, and environment-dependent tests without proper isolation.
* Remove dead code instead of keeping it as commented-out code.
* Do not add comments unless the user explicitly requests them.
* Use descriptive naming.
* Avoid vague names such as `data`, `obj`, `item`, `temp`, or `value` when more precise names are available.
* Avoid abbreviations unless they are established domain terminology.
* Keep naming conventions consistent.
* Treat lint and TypeScript errors seriously.
* Do not suppress ESLint or TypeScript diagnostics without a concrete reason.
* Avoid `@ts-ignore`.
* Prefer `@ts-expect-error` only when the error is intentional and documented.
* Do not disable lint rules globally for local problems.
* Prefer correctness, security, maintainability, and predictable behavior over micro-optimizations.
## Performance Priority
Prioritize performance work in this order:
* Architecture
* Network and API usage
* Database access
* Algorithms and data structures
* Rendering and state updates
* Concurrency
* Memory allocations
* CPU micro-optimizations
Never optimize based purely on assumptions.
## Safety Rule
* Compile-time type safety is not runtime validation.
* Validate all external and untrusted data.
* Do not use `any`, type assertions, non-null assertions, or disabled compiler checks to bypass real type problems.
* Prefer making invalid states unrepresentable where practical.
+14
View File
@@ -0,0 +1,14 @@
# TypeScript Rules
For TypeScript or modern ECMAScript work, open only the topic rules relevant to the task. Load applicable framework and shared security rules as well.
| Topic | Codex rule | Claude skill |
| --- | --- | --- |
| Types and data modeling | `references/types-and-modeling.md` | `rules-typescript-types-and-modeling` |
| Coding style and control flow | `references/coding-style.md` | `rules-typescript-coding-style` |
| Runtime validation and browser safety | `references/runtime-safety.md` | `rules-typescript-runtime-safety` |
| Security-specific implementation | `references/security.md` | `rules-typescript-security` |
| Async work and resource lifetime | `references/async-and-resources.md` | `rules-typescript-async-and-resources` |
| Performance | `references/performance.md` | `rules-typescript-performance` |
| Modules, APIs, and dependencies | `references/modules-and-dependencies.md` | `rules-typescript-modules-and-dependencies` |
| Tests, diagnostics, and maintenance | `references/testing-and-quality.md` | `rules-typescript-testing-and-quality` |
@@ -0,0 +1,12 @@
# TypeScript Async Work and Resources
Apply these rules when implementing promises, I/O, streams, subscriptions, timers, or other long-lived work.
* Handle every rejected promise. Do not leave floating promises; for intentional fire-and-forget work, attach error handling and make its owner and lifetime explicit.
* Use `await` when sequencing or error handling depends on completion. Use `Promise.all` only for independent, bounded work that can safely run together.
* Limit concurrency for large workloads with a queue or limiter. Apply backpressure to streams and producer/consumer flows instead of buffering without a bound.
* Support cancellation with `AbortSignal` when callers need to stop I/O or long-running work. Propagate it through nested calls and release resources when cancellation occurs.
* Avoid synchronous blocking and long CPU-bound work on the Node.js event loop or browser main thread. Move substantial CPU work to a worker or split it into bounded tasks when measurements justify it.
* Remove event listeners, timers, subscriptions, streams, and abort handlers when their owner is done. Avoid retaining closures or resources beyond their intended lifetime.
* Keep shared mutable state safe and deterministic when asynchronous operations can overlap. Prevent stale responses from overwriting newer state when request ordering matters.
* Retry only failures known to be transient, with bounded attempts and delay. Preserve idempotency and cancellation; avoid multiplying retries across layers.
@@ -0,0 +1,12 @@
# TypeScript Coding Style
Apply these rules when changing TypeScript syntax, control flow, or everyday code organization.
* Follow the project's established architecture, formatter, lint rules, and module conventions.
* Prefer `const`; use `let` only when reassignment is needed and avoid `var`.
* Keep functions and modules focused. Prefer early returns and shallow control flow over deeply nested branches or oversized functions.
* Prefer explicit, predictable behavior over implicit coercion, hidden side effects, or overly abstract helpers.
* Use strict equality. Handle `null` and `undefined` intentionally and do not use `||` when `0`, `false`, or an empty string is a valid value.
* Use optional chaining and nullish coalescing where they clarify behavior; avoid mixing nullish conventions without a project reason.
* Keep naming descriptive and consistent. Avoid vague names and abbreviations unless they are established domain terms.
* Remove dead code and unused imports. Do not add comments unless the user asks or a project rule requires them.
@@ -0,0 +1,12 @@
# TypeScript Modules and Dependencies
Apply these rules when changing package dependencies, module structure, imports, or public exports.
* Follow the project's existing module system consistently; avoid mixing ESM and CommonJS without a compatibility need.
* Keep modules focused and boundaries intentional. Avoid circular dependencies and unnecessary public exports.
* Separate transport models from domain logic where that boundary exists. Do not leak implementation details through public APIs.
* Prefer built-in platform APIs or installed packages before adding a dependency. Keep dependencies minimal, supported, and compatible with the lockfile.
* Review package identity, maintenance, transitive dependencies, permissions, lifecycle scripts, and update behavior before adopting a package. Pin or constrain versions according to project policy and commit the package manager's lockfile.
* Avoid broad dependency upgrades unrelated to the task. Do not hand-edit generated lockfile content; regenerate it with the intended package manager and verify the resulting diff.
* Do not treat bundled frontend configuration as secret storage. Anything shipped to a browser is observable by users.
* Remove unused imports and exports, and keep path and export conventions consistent with the surrounding code.
@@ -0,0 +1,13 @@
# TypeScript Performance
Apply these rules when optimizing TypeScript runtime, rendering, or memory use.
* Establish a baseline using representative data, devices, concurrency, and runtime versions. Measure the user-visible path and its latency distribution, throughput, memory, and CPU cost as relevant.
* Prioritize architecture, network and API usage, database access, algorithms, rendering, concurrency, allocations, then CPU micro-optimizations. Confirm that the suspected layer dominates before changing it.
* Avoid quadratic scans and repeated full collection passes for large inputs. Choose `Map`, `Set`, or an indexed lookup when it reduces measured repeated work and its memory cost is acceptable.
* Avoid unnecessary object spreading, intermediate arrays, copies, deep clones, and serialization in hot paths. Do not replace clear code with opaque loops without measured benefit.
* In browsers, reduce unnecessary component updates and DOM work; virtualize large lists when rendering all rows is costly. Avoid memoization that adds stale-state or invalidation bugs.
* In Node.js, keep CPU-heavy synchronous work from blocking the event loop. Stream large payloads and bound queues, buffers, and concurrent requests.
* Prefer platform APIs over custom low-level code or dependencies. Use `structuredClone` only when a deep clone is required and its data constraints are met.
* Cache only repeated measurable work, with bounded size, explicit keys, expiry/invalidation, and safe treatment of user- or tenant-specific data.
* Do not add batching or parallelism without preserving ordering, cancellation, rate limits, and failure behavior. Measure end-to-end results after the change.
@@ -0,0 +1,13 @@
# TypeScript Runtime Safety
Apply these rules when handling API payloads, user input, browser data, or other values outside the type system.
* Treat external values as `unknown` until validated. TypeScript annotations and assertions do not validate runtime data.
* Validate untrusted data at system boundaries before storing it, using it for authorization, or passing it to sensitive operations. Use schema validation when external contracts are complex.
* Handle `null` and `undefined` intentionally, and narrow values before use. Avoid implicit coercion and unsafe dynamic property access.
* Prefer allowlists for security-sensitive input. Prevent prototype pollution when merging untrusted objects; avoid unsafe recursive merges and dynamic writes to ordinary object prototypes.
* Treat URL, path, header, query, and redirect destinations as untrusted. Server-side fetches must restrict schemes and destinations, validate every redirect, and enforce response size and timeout limits.
* Use framework-safe text binding and automatic output encoding. Avoid `innerHTML`, `dangerouslySetInnerHTML`, and equivalent escape hatches unless sanitized content is required and a maintained sanitizer is applied.
* For cookie-based authentication, preserve secure cookie attributes and the application's CSRF protections. Do not store authentication secrets in browser-accessible storage without an explicit threat model and security design.
* Never expose secrets or sensitive data in output, logs, or errors. Client-side validation and route guards do not replace server-side authentication, authorization, or validation.
* Bound input size, nesting, parsing work, and response size before processing untrusted JSON or other structured data.
@@ -0,0 +1,13 @@
# TypeScript Security
Apply these rules when implementing or reviewing security-sensitive TypeScript code. Load the matching shared security rule for the affected API, identity, data, browser, file, network, cryptographic, or dependency boundary as well.
* Validate external data at runtime before using it in authorization decisions, database operations, filesystem access, or privileged actions. A type assertion is not validation.
* Enforce authentication, authorization, tenant scoping, and input validation on the server. Browser checks, hidden controls, route guards, and TypeScript types do not create a security boundary.
* Keep credentials and private keys on trusted servers. Never embed secrets in frontend bundles, source maps, browser storage, or public runtime configuration.
* Use cryptographically secure platform randomness for tokens and security-sensitive identifiers. Do not use `Math.random()` for secrets, session values, or authorization codes.
* Use parameterized database APIs and allowlists for dynamic identifiers. Avoid constructing SQL, shell commands, HTML, or other interpreted input by string concatenation.
* For server-side URL fetching, restrict protocols and destinations, validate redirects, and bound time, response size, and concurrency to reduce SSRF and resource-exhaustion risk.
* Use the framework's safe output encoding and preserve CSP, TLS, cookie, and CSRF protections. Do not disable these controls to make a request or test pass.
* Review untrusted object merges for prototype pollution, unsafe deserialization, and mass assignment. Copy only fields the caller is allowed to control.
* Review dependency additions and upgrades for source, package identity, install scripts, transitive risk, and lockfile changes.
@@ -0,0 +1,11 @@
# TypeScript Testing and Quality
Apply these rules when changing tests, lint configuration, compiler diagnostics, or maintainability checks.
* Prefer behavior-based tests that assert externally observable results. Avoid coupling tests to private implementation details.
* Keep tests deterministic. Control time, randomness, filesystem state, and external services when they affect the result.
* Mock external boundaries or expensive dependencies only when needed; avoid mocks that merely restate implementation details.
* Treat TypeScript, compiler, and lint diagnostics seriously. Do not suppress errors globally or use `@ts-ignore` to hide a real type problem.
* Use `@ts-expect-error` only for an intentional, narrow case whose expected error is part of the test or contract.
* Keep code easy to test through clear module boundaries without adding test-only production exports or hooks without a concrete reason.
* Run focused tests, type checks, and lint for changed paths. Expand checks when shared contracts or generated types are affected.
@@ -0,0 +1,12 @@
# TypeScript Types and Modeling
Apply these rules when defining TypeScript types, public function contracts, or domain data models.
* Prefer `strict: true`, precise types, and `unknown` for values whose shape has not been validated. Avoid `any`, broad assertions, and non-null assertions that hide uncertainty.
* Model variants with discriminated unions and use literal unions for constrained values. Make invalid states difficult to represent.
* Prefer `readonly` and immutable data when mutation is unnecessary. Use interfaces for implementation contracts or declaration merging; use type aliases for unions and compositions.
* Give exported APIs, callbacks, and non-trivial functions explicit contracts; rely on inference for simple local values where it keeps code clearer.
* Derive types from trusted existing contracts or generated sources instead of duplicating backend schemas manually. Regenerate generated contracts from their source rather than hand-editing them.
* Keep generic constraints meaningful and types readable. Avoid deep conditional or mapped types when a simpler model expresses the contract.
* Keep compile-time types aligned with runtime schemas. If a schema changes, update its inferred types and tests so the two do not silently drift.
* Avoid representing authorization state, validation success, or trusted input using a type assertion alone. Construct trusted values only after the required checks succeed.
+11 -62
View File
@@ -1,64 +1,13 @@
# UI & UX Guidelines # UI and UX Rules
* Design for clarity first. For interface design or interaction changes, load the principles and only the focused topic rules relevant to the user flow.
* Prefer simple, modern and predictable interfaces.
* Optimize for fast understanding and low cognitive load.
* Every screen should have a clear primary purpose.
* Every important action should be easy to discover.
* Prefer familiar interaction patterns over novel ones.
* Do not require users to learn unnecessary custom behavior.
* Keep layouts visually calm and structured.
* Use consistent spacing, typography, colors and interaction patterns.
* Avoid visual noise.
* Avoid decorative elements that do not improve usability.
* Avoid unnecessary complexity in navigation and workflows.
* Minimize the number of steps required to complete common tasks.
* Keep related information and actions close together.
* Group content logically.
* Use clear visual hierarchy.
* Make primary actions visually distinct from secondary actions.
* Avoid giving multiple actions equal visual priority when one is clearly more important.
* Use whitespace intentionally.
* Prefer readable line lengths and sufficient spacing.
* Avoid dense walls of content.
* Avoid oversized empty areas that reduce information efficiency.
* Keep important information visible without unnecessary scrolling where practical.
## Design Decision Rule | Topic | Codex rule | Claude skill |
| --- | --- | --- |
* Every UI element should have a clear purpose. | General design principles and trade-offs | `references/principles-and-priorities.md` | `rules-ui-ux-principles-and-priorities` |
* Every additional interaction adds cognitive cost. | Navigation and actions | `references/navigation-and-actions.md` | `rules-ui-ux-navigation-and-actions` |
* Prefer removing complexity over explaining unnecessary complexity. | Forms and validation | `references/forms.md` | `rules-ui-ux-forms` |
* Prefer familiar patterns over clever ones. | Content and feedback | `references/content-and-feedback.md` | `rules-ui-ux-content-and-feedback` |
* Prefer fewer clear choices over many ambiguous choices. | Visual design and accessibility | `references/visual-and-accessibility.md` | `rules-ui-ux-visual-and-accessibility` |
* Prefer predictable behavior over surprising automation. | Tables, search, and dialogs | `references/tables-search-dialogs.md` | `rules-ui-ux-tables-search-dialogs` |
* If multiple valid UX directions remain and the choice materially affects user workflow or safety, ask the user. Make minor reversible choices independently. | Destructive actions and error prevention | `references/destructive-and-error-prevention.md` | `rules-ui-ux-destructive-and-error-prevention` |
## Priority Order
Prioritize UI/UX decisions in this order:
* Correctness
* Clarity
* Safety
* Accessibility
* User control
* Task efficiency
* Consistency
* Responsiveness
* Visual polish
Visual polish must never reduce usability.
## Detailed guidance
Read the matching reference file before working in that area:
| Read this reference for... | ...this kind of work |
| --- | --- |
| `references/navigation-and-actions.md` | Navigation structure, action labeling and placement |
| `references/forms.md` | Form design and validation UX |
| `references/content-and-feedback.md` | Copy, loading/empty/error states, responsiveness |
| `references/visual-and-accessibility.md` | Visual design, accessibility, responsive layout |
| `references/tables-search-dialogs.md` | Data-dense views, search/filtering, dialogs/modals |
| `references/destructive-and-error-prevention.md` | Destructive actions, error prevention, consistency, user control, progressive disclosure, perceived performance, defaults |
@@ -0,0 +1,12 @@
# UI and UX Principles
Apply these principles across interface work. Use the other UI/UX rules only for the affected screen or interaction.
* Design for clarity and low cognitive load. Give each screen a clear purpose and make important actions easy to discover.
* Prefer familiar, predictable patterns. Keep layouts calm, group related content, and use hierarchy, spacing, typography, and color consistently.
* Minimize steps for common tasks. Remove decorative elements, choices, and interactions that do not help users complete a task.
* Make primary actions distinct from secondary actions. Keep related information and actions close, and keep important information visible without unnecessary scrolling.
* Every interaction adds cognitive cost. Prefer fewer clear choices and predictable behavior over clever or surprising automation.
* Prioritize correctness, clarity, safety, accessibility, user control, task efficiency, consistency, and responsiveness before visual polish.
* Do not let visual polish reduce readability, accessibility, or task completion.
* If multiple UX directions remain and the choice materially changes a user's workflow or safety, ask for the decision. Make minor reversible choices independently.
+10 -32
View File
@@ -1,34 +1,12 @@
# WPF Code Guidelines # WPF Rules
* Use modern C# and current .NET/WPF conventions. For WPF, XAML, or MVVM work, open only the focused topic rules relevant to the change.
* Keep UI, application logic and domain logic clearly separated.
* Prefer MVVM for non-trivial views.
* Keep code-behind minimal.
* Use code-behind only for view-specific behavior that does not belong in the ViewModel.
* Avoid business logic in views.
* Avoid direct service access from controls.
* Keep dependencies explicit and testable.
* Prefer composition over inheritance.
* Avoid unnecessary abstractions and framework wrappers.
* Follow the existing project architecture unless there is a strong reason to change it.
## Safety Rule | Topic | Codex rule | Claude skill |
| --- | --- | --- |
* The UI must remain responsive. | MVVM and binding | `references/mvvm-and-binding.md` | `rules-wpf-mvvm-and-binding` |
* Do not block the UI thread with I/O or expensive CPU work. | Commands, async, and threading | `references/commands-and-async.md` | `rules-wpf-commands-and-async` |
* Do not bypass validation or security controls for convenience. | Collections and XAML | `references/collections-and-xaml.md` | `rules-wpf-collections-and-xaml` |
* Do not introduce hidden cross-thread access. | Resources and lifecycle | `references/resources-and-lifecycle.md` | `rules-wpf-resources-and-lifecycle` |
* Prefer predictable, explicit and testable UI behavior. | Security and errors | `references/security-and-errors.md` | `rules-wpf-security-and-errors` |
| Architecture, testing, and performance | `references/architecture-and-quality.md` | `rules-wpf-architecture-and-quality` |
## Detailed guidance
Read the matching reference file before working in that area:
| Read this reference for... | ...this kind of work |
| --- | --- |
| `references/mvvm-and-binding.md` | MVVM, data binding, property change notifications, dependency/attached properties |
| `references/commands-and-async.md` | Commands, async/threading, UI-thread work, events |
| `references/collections-and-xaml.md` | Collections, virtualization, layout, XAML |
| `references/resources-and-lifecycle.md` | Resources/styles, windows/dialogs, navigation, memory, images/media |
| `references/security-and-errors.md` | Security, file/process access, error handling, logging, validation |
| `references/architecture-and-quality.md` | Architecture, DI, testing, code quality, performance |
@@ -1,5 +1,8 @@
## Architecture ## Architecture
* Use modern C# and current WPF conventions. Prefer MVVM for non-trivial views and keep code-behind limited to view-specific behavior.
* Keep UI, application, and domain logic separate. Avoid business logic in views and direct service access from controls.
* Keep dependencies explicit and testable; prefer composition over inheritance and avoid unnecessary abstractions or framework wrappers.
* Keep presentation, application, domain and infrastructure concerns separated. * Keep presentation, application, domain and infrastructure concerns separated.
* Avoid direct database access from views or ViewModels. * Avoid direct database access from views or ViewModels.
* Avoid direct HTTP access from controls. * Avoid direct HTTP access from controls.
@@ -28,6 +28,7 @@
## UI Thread ## UI Thread
* Treat the UI thread as a scarce resource. * Treat the UI thread as a scarce resource.
* Keep the interface responsive and do not block the UI thread with I/O or expensive CPU work. Move CPU work off-thread only when needed and marshal UI changes back explicitly.
* Avoid expensive loops, parsing, serialization, database access or network calls on the UI thread. * Avoid expensive loops, parsing, serialization, database access or network calls on the UI thread.
* Avoid excessive Dispatcher invocations. * Avoid excessive Dispatcher invocations.
* Batch UI updates where practical. * Batch UI updates where practical.
+14 -8
View File
@@ -1,17 +1,23 @@
--- ---
name: debugging name: debugging
description: Investigate and resolve software defects using evidence. description: Use when code, tests, commands, integrations, or user-visible behavior fail unexpectedly; reproduce the failure, trace the cause, and verify a focused fix.
--- ---
# Debugging # Debugging
Use when behavior is incorrect, failing, or unexpectedly slow. Use when a command, test, integration, or user-visible behavior fails or behaves unexpectedly.
1. Reproduce or isolate the problem. ## Workflow
2. Gather evidence and identify the likely cause.
3. Apply the smallest complete fix.
4. Run relevant verification.
Return the cause, change, and verification result. 1. State expected and observed behavior. Capture the exact error, input, environment, and reproduction steps that matter.
2. Reproduce the failure with the smallest relevant command or scenario. If it cannot be reproduced, inspect logs, recent changes, callers, configuration, and environment differences before changing code.
3. Trace the failing path through its callers and dependencies. Compare a failing case with a successful one and identify the earliest point where behavior diverges.
4. Form a root-cause explanation supported by evidence. If several causes remain plausible, gather the cheapest evidence that distinguishes them.
5. Fix the cause at the narrowest shared boundary that covers affected callers. Preserve unrelated behavior and keep error handling intact.
6. Run the smallest check that would fail if the defect returned, then add broader regression checks when the change affects connected paths or data.
Do not guess when evidence can be collected. Do not hide the failure with retries, broad exception handling, suppressed diagnostics, or caller-specific guards unless evidence shows that is the actual cause.
## Report
Summarize the cause, evidence, change, checks run, and any remaining uncertainty. Separate environment failures from product failures. Never claim a check passed if it did not run.
@@ -1,10 +0,0 @@
---
name: answer-pull-request
description: Prepare a clear response to pull request feedback.
---
# Answer Pull Request
Understand each comment, make authorized changes, and respond with evidence.
Separate completed actions, explanations, and unresolved questions.
+15 -3
View File
@@ -1,10 +1,22 @@
--- ---
name: review-pull-request name: review-pull-request
description: Review a pull request for correctness, risk, and readiness. description: Use when reviewing a pull request or proposed diff for actionable defects, compatibility problems, security risks, and merge readiness. Do not implement review feedback.
--- ---
# Review Pull Request # Review Pull Request
Inspect the change and its context independently. Review requested pull requests or proposed diffs for correctness, compatibility, and merge risk.
Report actionable findings first, followed by verification status and open risks. ## Workflow
1. Identify the target branch and exact diff. Read the relevant surrounding code, callers, tests, and configuration before judging a change.
2. Trace changed behavior through its entry points and consumers. Check error paths, boundary conditions, data handling, and security-sensitive flows when affected.
3. Compare the implementation with the stated requirement and established repository patterns. Distinguish a concrete defect from a preference or speculative concern.
4. Check compatibility of public APIs, file formats, migrations, configuration, and generated output where relevant.
5. Inspect available verification evidence. Run checks only when the review request and environment call for them; report unrun checks accurately.
## Findings
List actionable findings first, ordered by severity. For each, give the location, the triggering condition, the user or system impact, and a practical correction. Do not report issues already fixed in the diff. If no concrete finding remains, say so and note meaningful verification gaps.
Do not modify the reviewed change unless asked to implement the feedback.
@@ -1,10 +1,20 @@
--- ---
name: integration-test-write name: integration-test-write
description: Add focused integration tests for connected system behavior. description: Use when adding or changing tests for behavior across a real boundary, such as an API and database, filesystem command, or cooperating services. Not for isolated unit behavior.
--- ---
# Integration Test Write # Integration Test Write
Identify the integration boundary, required setup, and meaningful success and failure cases. Use when verifying behavior across a real application boundary, such as an API and database, a command and filesystem, or cooperating services.
Keep the test deterministic and limited to the behavior under change. ## Workflow
1. Name the boundary under test and the production path the test must exercise. Keep unit-level details covered by unit tests.
2. Reuse the repository's integration-test setup. Start only the services and data stores required for this behavior.
3. Prepare minimal isolated data and control external inputs. Prevent test runs from touching user data, shared environments, or live services.
4. Cover a successful interaction and the failure or rejection path that matters to the change. Include transaction, retry, or cleanup behavior only when it is part of the contract.
5. Assert persisted or externally observable outcomes, including relevant status, response, and side effects.
6. Clean up created resources even when assertions fail. Make repeated runs safe and independent.
7. Run the focused integration test and report required services, results, and any unavailable environment.
Avoid turning an integration test into a full end-to-end suite when a narrower boundary proves the contract.
@@ -1,12 +1,19 @@
--- ---
name: sql-write name: sql-write
description: Implement a focused SQL change safely. description: Use when writing or modifying SQL queries, schema, database migrations, or data updates; account for dialect, compatibility, authorization, and data safety.
--- ---
# SQL Write # SQL Write
Understand the schema and affected data before writing SQL. Use when writing or changing SQL statements, schema definitions, or database migrations.
Use the smallest complete change, preserve data safety, and run relevant verification. ## Workflow
Do not execute destructive operations without explicit authorization. 1. Identify the database engine and version, target schema, affected callers, and expected data shape. Inspect existing migrations and SQL style first.
2. Parameterize values and validate any dynamic identifiers against an allowlist. Preserve tenant boundaries and authorization filters in every query path.
3. For schema changes, consider existing rows, nullability, defaults, constraints, indexes, and deployment order. Prefer additive, backward-compatible steps when old and new application versions may overlap.
4. Use transactions and explicit rollback or recovery steps where supported and appropriate. Estimate affected rows before broad updates or deletes.
5. Do not execute destructive or production SQL without explicit authorization. A request to write SQL does not authorize running it against live data.
6. Verify syntax and behavior against the intended engine, then inspect query results, migration ordering, and relevant application tests.
Report the database dialect, files or migration changed, verification, and any operational prerequisite.
@@ -1,10 +1,19 @@
--- ---
name: unit-test-write name: unit-test-write
description: Add focused unit tests for isolated behavior. description: Use when adding or changing tests for one isolated function, component, or decision. Use integration-test-write when behavior must cross an application or service boundary.
--- ---
# Unit Test Write # Unit Test Write
Identify the behavior and important boundary cases, then add focused deterministic tests. Use when adding or updating tests for one isolated function, component, or decision.
Prefer observable behavior over implementation details. Report the checks that were run. ## Workflow
1. Read the requirement and implementation. Identify the observable result that defines success and the boundary conditions most likely to fail.
2. Follow the repository's existing test framework, naming, setup, and assertion style. Avoid new dependencies or fixtures when a small test can express the behavior.
3. Cover the normal case and the smallest meaningful set of boundary or failure cases. Include a regression case for a reported defect.
4. Assert public behavior and useful error outcomes rather than private call order, incidental wording, or internal data layout.
5. Keep each test deterministic. Control time, randomness, filesystem state, and external services when they affect the result.
6. Run the focused test and report its result. Broaden verification only when shared behavior or connected components are affected.
Do not add tests that duplicate existing coverage or require production code to expose test-only hooks without a clear reason.
@@ -1,12 +1,19 @@
--- ---
name: change-impact-analysis name: change-impact-analysis
description: Determine what a proposed change may affect. description: Use before implementation when a proposed code, configuration, data, or workflow change has uncertain callers, compatibility effects, dependencies, or blast radius. Return an impact analysis without implementing it.
--- ---
# Change Impact Analysis # Change Impact Analysis
Use when the blast radius or compatibility impact is uncertain. Use before a change when its consumers, compatibility effects, or blast radius are uncertain.
Inspect callers, dependencies, configuration, tests, and affected workflows. ## Workflow
Return affected areas, risks, and recommended verification. Separate facts from assumptions. 1. Define the proposed change and the behavior or contract it affects.
2. Locate direct callers, related implementations, shared helpers, configuration, generated output, and relevant tests. Start with exact-path and symbol searches; broaden only when the flow is unclear.
3. Trace likely impact through runtime paths, persisted data, client integrations, deployment, and documentation as applicable.
4. Identify compatibility concerns, failure modes, security or data-loss risks, and dependencies on version or environment.
5. Separate confirmed impacts from assumptions and unknowns. Do not treat a possible downstream effect as a fact without evidence.
6. Recommend proportionate verification tied to the risks found.
Return a concise impact map: affected areas, evidence, risks, unknowns, and checks. Do not implement the proposal unless asked.
@@ -1,12 +1,19 @@
--- ---
name: implementation-planner name: implementation-planner
description: Create a concise plan for implementing an understood change. description: Use when requirements are understood but implementation needs multiple dependent steps, affected areas, risk handling, or coordinated verification. Produce a plan without making the changes.
--- ---
# Implementation Planner # Implementation Planner
Use before implementation when a change has multiple meaningful steps. Use when an understood change has multiple dependent steps or meaningful coordination risk.
Identify the goal, affected areas, sequence, risks, and verification. ## Workflow
Return a short actionable plan. Do not implement the change. 1. Restate the intended outcome and acceptance conditions without expanding the request.
2. Identify the files, components, data contracts, or workflows likely to change. Confirm the repository's existing patterns before proposing new structure.
3. Order work by dependency. Keep steps small enough to review and verify independently where possible.
4. Call out decisions that affect compatibility, data, permissions, or external systems. Resolve routine reversible choices using repository conventions.
5. Pair each substantial step with the cheapest useful verification.
6. Keep the plan concise and omit speculative scaffolding, unrelated cleanup, and work that can safely wait.
Return an actionable sequence with affected areas, risks, and checks. This skill plans only; do not implement the change.
@@ -1,12 +1,19 @@
--- ---
name: work-item-definition name: work-item-definition
description: Turn an unclear request into a bounded technical work item. description: Use when a request is too vague to scope, estimate, or verify. Define the objective, scope, acceptance criteria, assumptions, dependencies, and only material open questions; do not implement it.
--- ---
# Work Item Definition # Work Item Definition
Use when requirements, scope, or acceptance criteria are incomplete. Use when a request lacks enough detail to estimate or verify the intended technical outcome.
Clarify the objective, boundaries, assumptions, acceptance criteria, and open questions. ## Workflow
Do not invent business requirements. 1. State the user or system outcome in concrete terms.
2. Define what is in scope and what is explicitly out of scope based on the request and current behavior.
3. Write observable acceptance criteria, including relevant failure or edge cases.
4. Record constraints, dependencies, compatibility requirements, and assumptions separately.
5. Ask only questions whose answers would materially change behavior, contracts, permissions, or acceptance. Choose safe reversible defaults for minor implementation details.
6. Identify unresolved risks and a proportionate verification approach.
Return a bounded work item with objective, scope, criteria, assumptions, dependencies, and open questions. Do not invent business rules or start implementation unless asked.
@@ -1,12 +1,18 @@
--- ---
name: repository-research name: repository-research
description: Investigate an existing repository to find relevant code and established patterns. description: Use for codebase-specific questions or before changing an unfamiliar area; trace existing files, callers, behavior, tests, and conventions. Do not use for general external technology research.
--- ---
# Repository Research # Repository Research
Use before changing an unfamiliar repository area. Use to understand an existing repository area before changing it or answering a codebase-specific question.
Inspect relevant files, callers, tests, configuration, and established patterns. ## Workflow
Return evidence, affected areas, unknowns, and useful implementation context. Avoid modifying implementation code. 1. Start from known paths, symbols, exact strings, or configuration keys. Read relevant files directly and search callers with repository-native tools.
2. Trace the flow from entry point through shared helpers to observable behavior. Check related implementations, tests, configuration, and documentation.
3. Identify established conventions and note where they differ across clients, shells, or platforms.
4. Use semantic search only when terminology is unclear, the flow spans components, or direct search has not found the relevant code. Confirm any retrieved result against current files.
5. Report file paths and symbols as evidence. Separate confirmed behavior, inference, and unresolved questions.
Return the relevant architecture, existing patterns, affected areas, and unknowns. Avoid changing implementation code during research unless asked.
@@ -1,12 +1,18 @@
--- ---
name: technical-research name: technical-research
description: Investigate technical options and relevant evidence before implementation. description: Use before implementation when an unfamiliar or changing technology, API, standard, or technical trade-off needs current evidence. Prefer primary sources; do not use for repository-only code discovery.
--- ---
# Technical Research # Technical Research
Use when an implementation depends on unfamiliar technology, APIs, tools, or trade-offs. Use before implementation when an unfamiliar technology, API, current product behavior, or technical trade-off could change the solution.
Gather relevant evidence, compare viable options, and identify unknowns. ## Workflow
Return concise findings with sources, assumptions, and a recommendation when appropriate. Do not implement the change. 1. Turn the implementation question into specific facts or decisions that need evidence.
2. Prefer current primary sources: official product documentation, standards, release notes, or the original research. Check version and date when behavior may have changed.
3. Compare only viable options against the user's constraints, compatibility needs, security properties, and maintenance cost.
4. Separate source-backed facts from interpretation. Record assumptions and any material evidence that is missing or conflicting.
5. Recommend an option only when evidence supports it. Explain the deciding trade-off briefly.
Return concise findings with direct sources and an implementation-relevant conclusion. Do not implement the change.
+14 -3
View File
@@ -1,10 +1,21 @@
--- ---
name: api-review name: api-review
description: Review API changes for compatibility and contract clarity. description: Use when reviewing HTTP or RPC endpoint, request handling, serialization, schema, or API contract changes for correctness, security, and client compatibility.
--- ---
# API Review # API Review
Inspect request and response contracts, validation, errors, authorization, compatibility, and documentation. Use when reviewing an HTTP or RPC API contract, endpoint, serializer, or request-handling change.
Return concrete findings and affected consumers. Keep detailed API standards for future customization. ## Review
- Trace the request from routing through authentication, authorization, validation, business logic, and persistence. Check tenant or resource ownership at the point data is accessed.
- Compare request and response schemas, status codes, error shapes, pagination, identifiers, and nullability with existing consumers and published contracts.
- Check malformed, missing, oversized, duplicated, and unauthorized input where relevant. Ensure validation errors do not disclose sensitive data.
- Consider compatibility for older clients, retries, idempotency, concurrency, and versioning when those affect the endpoint.
- Review serialization defaults, content types, limits, and generated client or schema updates.
- Confirm documentation and examples match actual behavior.
## Findings
Report concrete contract breaks, security weaknesses, or incorrect behavior first. Give location, triggering request, impact on consumers, and a practical correction. Distinguish optional design suggestions from defects.
+11 -3
View File
@@ -1,10 +1,18 @@
--- ---
name: business-review name: business-review
description: Review a change against stated business intent and workflows. description: Use when assessing whether a product or code change meets stated business requirements and user workflows, including actors, domain rules, acceptance criteria, and unmet cases.
--- ---
# Business Review # Business Review
Check actors, workflows, domain concepts, invariants, edge cases, and acceptance criteria. Use when reviewing whether a change supports the stated business goal and real user workflows.
Separate stated requirements, evidence, and assumptions. Do not invent business requirements. ## Review
1. Identify the intended actors, goals, and acceptance criteria from the request or product evidence.
2. Trace the affected workflow across its relevant states, permissions, handoffs, and failure paths.
3. Check domain terms, invariants, eligibility rules, and calculations against existing behavior and authoritative product definitions.
4. Look for unmet acceptance criteria, inconsistent outcomes between related workflows, and impacts on existing users or data.
5. Separate stated requirements, observed behavior, assumptions, and recommendations. Ask about material ambiguity rather than inventing business policy.
Report actionable gaps with the affected actor and workflow, evidence, impact, and the requirement or decision needed to resolve them. Do not substitute implementation preference for business intent.
+12 -4
View File
@@ -1,12 +1,20 @@
--- ---
name: code-review name: code-review
description: Review code for correctness, maintainability, and regressions. description: Use for a general review of a code diff when looking for concrete defects, regressions, compatibility issues, and maintainability risks. For focused API, security, performance, UI, WPF, or EF Core reviews, use the matching review skill.
--- ---
# Code Review # Code Review
Inspect the change independently and report concrete findings with severity and evidence. Review the requested diff independently for defects and material regression risk.
Check behavior, regressions, maintainability, unnecessary complexity, and verification. ## Workflow
Do not modify code unless explicitly asked. 1. Read the diff and the surrounding code, then identify the behavior and contracts the change intends to affect.
2. Trace callers and data flow through relevant shared helpers. Check normal, boundary, and failure paths rather than reviewing changed lines in isolation.
3. Look for incorrect behavior, compatibility breaks, unsafe input or data handling, resource leaks, concurrency problems, and avoidable complexity where relevant.
4. Check whether the change has focused verification and whether stated results match the evidence. Do not run broad checks by default.
5. Ignore style preferences unless they make the code error-prone or conflict with a repository rule.
## Findings
List only actionable issues, ordered by severity. Include file and line, condition, concrete impact, and the smallest practical fix. Do not modify code unless explicitly asked. If no defect is found, say so and note important unverified risks.
+12 -3
View File
@@ -1,10 +1,19 @@
--- ---
name: efcore-review name: efcore-review
description: Review Entity Framework Core changes for correctness and data access risks. description: Use when reviewing Entity Framework Core LINQ queries, models, migrations, or persistence behavior for correctness, provider behavior, performance, and data-scope risks.
--- ---
# EF Core Review # EF Core Review
Inspect queries, relationships, tracking, migrations, transactions, and performance-sensitive access. Use when reviewing Entity Framework Core queries, model changes, migrations, or persistence behavior.
Return concrete findings and relevant verification. Keep detailed EF Core standards for future customization. ## Review
- Check whether LINQ translates as intended for the configured provider and whether filters are applied before materialization.
- Look for N+1 access, unbounded result sets, unnecessary tracking, duplicate queries, and over-fetching. Verify eager or explicit loading matches the data needed.
- Review relationship configuration, delete behavior, keys, nullability, indexes, and concurrency tokens against the domain invariants.
- Check migration ordering, generated SQL, data backfills, rollback limits, and compatibility while old and new application versions may coexist.
- Inspect transaction scope, isolation, retry behavior, cancellation, and handling of concurrency conflicts where applicable.
- Ensure sensitive or tenant-scoped rows cannot be read or changed outside the authorized scope.
Report findings with the query or migration path, runtime or data impact, and relevant provider-aware verification. Avoid recommending indexes or caching without evidence of a query need.
@@ -1,10 +1,18 @@
--- ---
name: performance-review name: performance-review
description: Review changes for meaningful performance risks. description: Use when evaluating a code change for likely latency, throughput, memory, allocation, or resource-use regressions; distinguish measurements from estimates.
--- ---
# Performance Review # Performance Review
Identify measurable or plausible bottlenecks in the changed paths. Use when reviewing a change for meaningful latency, throughput, memory, or resource-use risk.
Return evidence, expected impact, and proportionate recommendations. Avoid speculative optimization. ## Review
1. Identify the changed execution paths and their expected frequency, input size, and concurrency.
2. Trace expensive work such as repeated I/O, network calls, database round trips, allocations, serialization, locks, and scans.
3. Estimate algorithmic cost and resource growth with realistic data sizes. Separate measured results from reasoned estimates.
4. Check whether a proposed cache, parallelism, or batching change preserves invalidation, ordering, cancellation, and failure behavior.
5. Recommend a benchmark or profiler only when it can answer a concrete uncertainty. Avoid micro-optimizations without a plausible user impact.
Report the affected path, evidence, likely impact, and smallest useful correction or measurement. Do not present theoretical concerns as confirmed regressions.
+11 -3
View File
@@ -1,10 +1,18 @@
--- ---
name: security-review name: security-review
description: Review changes for concrete security weaknesses. description: Use when asked to assess code or configuration changes for exploitable security weaknesses, trust boundaries, authorization flaws, unsafe input handling, or sensitive-data exposure.
--- ---
# Security Review # Security Review
Inspect trust boundaries, input handling, authorization, secrets, permissions, and sensitive data. Use when asked to review a change for exploitable security weaknesses.
Return evidence, impact, and actionable findings. Do not invent vulnerabilities without evidence. ## Review
1. Map inputs crossing a trust boundary and identify the principals, permissions, and resources involved.
2. Trace validation, authentication, authorization, tenant scoping, and output encoding through the actual code path.
3. Check secret handling, filesystem and process access, network destinations, deserialization, dependency sources, and sensitive-data exposure when relevant.
4. Build a plausible attack path: attacker capability, triggering input or state, missing control, and resulting impact.
5. Verify proposed mitigations preserve normal behavior and fail closed where the security boundary requires it.
Report only evidence-backed findings. State severity, preconditions, impact, and a practical fix. Separate confirmed vulnerabilities from defense-in-depth suggestions; do not invent attack scenarios unsupported by the code.
+12 -3
View File
@@ -1,10 +1,19 @@
--- ---
name: ui-ux-review name: ui-ux-review
description: Review user interface changes for usability and consistency. description: Use when reviewing a user-facing screen or interaction for task clarity, accessibility, validation, error recovery, responsive behavior, and consistency.
--- ---
# UI UX Review # UI UX Review
Inspect states, feedback, accessibility basics, consistency, and error handling. Use when reviewing a user-facing screen or interaction for usability, clarity, accessibility, or consistency.
Return concrete findings with affected user flows. Keep detailed design standards for future customization. ## Review
1. Identify the user's goal and walk through the normal flow, including loading, empty, success, error, and recovery states that apply.
2. Check whether labels, instructions, defaults, and feedback make the next action and system state clear.
3. Review keyboard access, focus order and visibility, semantic controls, readable contrast, and accessible names for interactive elements.
4. Check validation timing and placement, preservation of entered data, prevention of accidental destructive actions, and clear recovery from errors.
5. Compare navigation and interaction patterns with nearby screens. Consider narrow layouts and long or localized content where relevant.
6. Distinguish a usability defect from a stylistic preference and tie each finding to a specific user task.
Report the affected flow, user impact, evidence, and a focused improvement. Do not redesign unrelated parts of the interface.
+12 -3
View File
@@ -1,10 +1,19 @@
--- ---
name: wpf-review name: wpf-review
description: Review WPF changes for correctness and maintainability. description: Use when reviewing WPF, XAML, or MVVM desktop changes, including bindings, commands, resources, accessibility, lifecycle, and UI-thread behavior.
--- ---
# WPF Review # WPF Review
Inspect the changed WPF views, bindings, resources, commands, and lifecycle behavior. Use when reviewing WPF, XAML, or MVVM desktop changes.
Return concrete findings and relevant verification. Keep detailed WPF standards for future customization. ## Review
- Trace `DataContext`, binding paths, update modes, validation, and `INotifyPropertyChanged` behavior. Check collection updates and selection state.
- Review commands for enablement, asynchronous execution, cancellation, exception handling, and UI-thread affinity.
- Check resource dictionaries, styles, templates, theme behavior, and resource lookup scope.
- Inspect control names, focus and keyboard behavior, automation properties, and error feedback for accessibility.
- Check view and window lifetime, event subscriptions, disposal, timers, and owned resources for leaks or stale references.
- Review XAML parsing, converters, dependency properties, and virtualization for correctness and practical performance.
Report concrete findings with the view, binding, or lifecycle path and user-visible impact. Recommend focused WPF verification; avoid framework-wide advice unrelated to the diff.
@@ -1,10 +1,18 @@
--- ---
name: facts-verification name: facts-verification
description: Verify factual claims against available evidence. description: Use when an answer, report, or decision depends on factual claims that need checking against current or authoritative evidence; label each claim confirmed, contradicted, inferred, or unknown.
--- ---
# Facts Verification # Facts Verification
Check important claims against source code, documentation, commands, or other appropriate evidence. Use when an answer, report, or implementation decision depends on claims that need confirmation.
Return the claim, evidence, and confidence. Mark unknowns explicitly. ## Workflow
1. Break the statement into atomic claims that can be checked independently.
2. Choose evidence suited to each claim: current source, executable output, authoritative documentation, primary research, or direct measurement.
3. Prefer the source closest to the fact. Check dates, versions, and applicability when the claim can change over time.
4. Compare conflicting evidence and explain any difference in scope or version rather than silently selecting one.
5. Label the result as confirmed, contradicted, inferred, or unknown. Keep confidence proportional to evidence quality.
Return each claim with its evidence and conclusion. Do not turn absence of evidence into evidence that a claim is false.
Loaded 100 of 102 files, more files were not shown because too many files have changed in this diff. Show more