From 30420154bb07cfd5b28fca9bf2ae2f92e18650c1 Mon Sep 17 00:00:00 2001 From: Fuji Nguyen Date: Tue, 21 Apr 2026 13:17:32 -0400 Subject: [PATCH 1/2] Update Playwright submodule: Phase 3 AI submenu tests --- Tests/AngularNetTutorial-Playwright | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/AngularNetTutorial-Playwright b/Tests/AngularNetTutorial-Playwright index 2d7f081..27e726f 160000 --- a/Tests/AngularNetTutorial-Playwright +++ b/Tests/AngularNetTutorial-Playwright @@ -1 +1 @@ -Subproject commit 2d7f0819fa0cccb8aaae5ed859aa0272c502a0ff +Subproject commit 27e726f5c6fa43dec0031ac0037ae0fb187eba8b From 3849abe51b2d143999b361085eaca403c85ff423 Mon Sep 17 00:00:00 2001 From: Fuji Nguyen Date: Tue, 21 Apr 2026 13:33:54 -0400 Subject: [PATCH 2/2] add OpenWolf --- .claude/rules/openwolf.md | 15 + .claude/settings.json | 72 +- .wolf/OPENWOLF.md | 135 ++++ .wolf/anatomy.md | 921 ++++++++++++++++++++++ .wolf/buglog.json | 21 + .wolf/cerebrum.md | 23 + .wolf/config.json | 73 ++ .wolf/cron-manifest.json | 97 +++ .wolf/cron-state.json | 7 + .wolf/designqc-report.json | 6 + .wolf/hooks/_session.json | 27 + .wolf/hooks/package.json | 3 + .wolf/hooks/post-read.js | 69 ++ .wolf/hooks/post-write.js | 503 ++++++++++++ .wolf/hooks/pre-read.js | 80 ++ .wolf/hooks/pre-write.js | 121 +++ .wolf/hooks/session-start.js | 77 ++ .wolf/hooks/shared.js | 614 +++++++++++++++ .wolf/hooks/stop.js | 147 ++++ .wolf/identity.md | 9 + .wolf/memory.md | 6 + .wolf/reframe-frameworks.md | 597 ++++++++++++++ .wolf/suggestions.json | 4 + .wolf/token-ledger.json | 50 ++ CLAUDE.md | 7 + Clients/TalentManagement-Angular-Material | 2 +- 26 files changed, 3684 insertions(+), 2 deletions(-) create mode 100644 .claude/rules/openwolf.md create mode 100644 .wolf/OPENWOLF.md create mode 100644 .wolf/anatomy.md create mode 100644 .wolf/buglog.json create mode 100644 .wolf/cerebrum.md create mode 100644 .wolf/config.json create mode 100644 .wolf/cron-manifest.json create mode 100644 .wolf/cron-state.json create mode 100644 .wolf/designqc-report.json create mode 100644 .wolf/hooks/_session.json create mode 100644 .wolf/hooks/package.json create mode 100644 .wolf/hooks/post-read.js create mode 100644 .wolf/hooks/post-write.js create mode 100644 .wolf/hooks/pre-read.js create mode 100644 .wolf/hooks/pre-write.js create mode 100644 .wolf/hooks/session-start.js create mode 100644 .wolf/hooks/shared.js create mode 100644 .wolf/hooks/stop.js create mode 100644 .wolf/identity.md create mode 100644 .wolf/memory.md create mode 100644 .wolf/reframe-frameworks.md create mode 100644 .wolf/suggestions.json create mode 100644 .wolf/token-ledger.json diff --git a/.claude/rules/openwolf.md b/.claude/rules/openwolf.md new file mode 100644 index 0000000..9785410 --- /dev/null +++ b/.claude/rules/openwolf.md @@ -0,0 +1,15 @@ +--- +description: OpenWolf protocol enforcement — active on all files +globs: **/* +--- + +- Check .wolf/anatomy.md before reading any project file +- Check .wolf/cerebrum.md Do-Not-Repeat list before generating code +- After writing or editing files, update .wolf/anatomy.md and append to .wolf/memory.md +- After receiving a user correction, update .wolf/cerebrum.md immediately (Preferences, Learnings, or Do-Not-Repeat) +- LEARN from every interaction: if you discover a convention, user preference, or project pattern, add it to .wolf/cerebrum.md. Low threshold — when in doubt, log it. +- BEFORE fixing any bug or error: read .wolf/buglog.json for known fixes +- AFTER fixing any bug, error, failed test, failed build, or user-reported problem: ALWAYS log to .wolf/buglog.json with error_message, root_cause, fix, and tags +- If you edit a file more than twice in a session, that likely indicates a bug — log it to .wolf/buglog.json +- When the user asks to check/evaluate UI design: run `openwolf designqc` to capture screenshots, then read them from .wolf/designqc-captures/ +- When the user asks to change/pick/migrate UI framework: read .wolf/reframe-frameworks.md, ask decision questions, recommend a framework, then execute with the framework's prompt diff --git a/.claude/settings.json b/.claude/settings.json index 3d3b30a..b6bf5ad 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -9,5 +9,75 @@ "Bash(az sql:*)", "Bash(gh workflow:*)" ] + }, + "hooks": { + "SessionStart": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/session-start.js\"", + "timeout": 5 + } + ] + } + ], + "PreToolUse": [ + { + "matcher": "Read", + "hooks": [ + { + "type": "command", + "command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/pre-read.js\"", + "timeout": 5 + } + ] + }, + { + "matcher": "Write|Edit|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/pre-write.js\"", + "timeout": 5 + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Read", + "hooks": [ + { + "type": "command", + "command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/post-read.js\"", + "timeout": 5 + } + ] + }, + { + "matcher": "Write|Edit|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/post-write.js\"", + "timeout": 10 + } + ] + } + ], + "Stop": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/stop.js\"", + "timeout": 10 + } + ] + } + ] } -} +} \ No newline at end of file diff --git a/.wolf/OPENWOLF.md b/.wolf/OPENWOLF.md new file mode 100644 index 0000000..aae2a47 --- /dev/null +++ b/.wolf/OPENWOLF.md @@ -0,0 +1,135 @@ +# OpenWolf Operating Protocol + +You are working in an OpenWolf-managed project. These rules apply every turn. + +## File Navigation + +1. Check `.wolf/anatomy.md` BEFORE reading any file. It has a 2-3 line description and token estimate for every file in the project. +2. If the description in anatomy.md is sufficient for your task, do NOT read the full file. +3. If a file is not in anatomy.md, search with Grep/Glob, then update anatomy.md with the new entry. + +## Code Generation + +1. Before generating code, read `.wolf/cerebrum.md` and respect every entry. +2. Check the `## Do-Not-Repeat` section — these are past mistakes that must not recur. +3. Follow all conventions in `## Key Learnings` and `## User Preferences`. + +## After Actions + +1. After every significant action, append a one-line entry to `.wolf/memory.md`: + `| HH:MM | description | file(s) | outcome | ~tokens |` +2. After creating, deleting, or renaming files: update `.wolf/anatomy.md`. + +## Cerebrum Learning (MANDATORY — every session) + +OpenWolf's value comes from learning across sessions. You MUST update `.wolf/cerebrum.md` whenever you learn something useful. This is not optional. + +**Update `## User Preferences` when the user:** +- Corrects your approach ("no, do it this way instead") +- Expresses a style preference (naming, structure, formatting) +- Shows a preferred workflow or tool choice +- Rejects a suggestion — record what they preferred instead +- Asks for more/less detail, verbosity, explanation + +**Update `## Key Learnings` when you discover:** +- A project convention not obvious from the code (e.g., "tests go in __tests__/ not test/") +- A framework-specific pattern this project uses +- An API behavior that surprised you +- A dependency quirk or version constraint +- How modules connect or data flows through the system + +**Update `## Do-Not-Repeat` (with date) when:** +- The user corrects a mistake you made +- You try something that fails and find the right approach +- You discover a gotcha that would trip up a fresh session + +**Update `## Decision Log` when:** +- A significant architectural or technical choice is made +- The user explains why they chose approach A over B +- A trade-off is explicitly discussed + +**The bar is LOW.** If in doubt, add it. A cerebrum entry that's slightly redundant costs nothing. A missing entry means the next session repeats the same discovery process. + +## Bug Logging (MANDATORY) + +**Log a bug to `.wolf/buglog.json` whenever ANY of these happen:** +- The user reports an error, bug, or problem +- A test fails or a command produces an error +- You fix something that was broken +- You edit a file more than twice to get it right +- An import, module, or dependency is missing or wrong +- A runtime error, type error, or syntax error occurs +- A build or lint command fails +- A feature doesn't work as expected +- You change error handling, try/catch blocks, or validation logic +- The user says something "doesn't work", "is broken", or "shows wrong X" + +**Before fixing:** Read `.wolf/buglog.json` first — the fix may already be known. + +**After fixing:** ALWAYS append to `.wolf/buglog.json` with this structure: +```json +{ + "id": "bug-NNN", + "timestamp": "ISO date", + "error_message": "exact error or user complaint", + "file": "file that was fixed", + "root_cause": "why it broke", + "fix": "what you changed to fix it", + "tags": ["relevant", "keywords"], + "related_bugs": [], + "occurrences": 1, + "last_seen": "ISO date" +} +``` + +**The threshold is LOW.** When in doubt, log it. A false positive in the bug log costs nothing. A missed bug means repeating the same mistake later. + +## Token Discipline + +- Never re-read a file already read this session unless it was modified since. +- Prefer anatomy.md descriptions over full file reads when possible. +- Prefer targeted Grep over full file reads when searching for specific code. +- If appending to a file, do not read the entire file first. + +## Design QC + +When the user asks you to check, evaluate, or improve the design/UI of their app: + +1. Run `openwolf designqc` via Bash to capture screenshots. + - The command auto-detects a running dev server, or starts one from package.json if needed + - Use `--url ` only if auto-detection fails + - The command saves compressed JPEG screenshots to `.wolf/designqc-captures/` + - Full pages are captured as sectioned viewport-height images (top, section2, ..., bottom) +2. Read the captured screenshot images from `.wolf/designqc-captures/` using the Read tool. +3. Evaluate the design against modern standards (Shadcn UI, Tailwind, clean React patterns): + - Spacing and whitespace consistency + - Typography hierarchy and readability + - Color contrast and accessibility (WCAG) + - Visual hierarchy and focal points + - Component consistency + - Whether the design looks "dull" or "white-coded" (generic, no personality) +4. Provide specific, actionable feedback with fix suggestions. +5. If the user approves, implement the fixes directly in their code. +6. After fixes, re-run `openwolf designqc` to capture new screenshots and verify improvement. + +**Token awareness:** Each screenshot costs ~2500 tokens. The command compresses images (JPEG quality 70, max width 1200px) to minimize cost. For large apps, use `--routes / /specific-page` to limit captures. + +## Reframe — UI Framework Selection + +When the user asks to change, pick, migrate, or "reframe" their project's UI framework: + +1. Read `.wolf/reframe-frameworks.md` for the full framework knowledge base. +2. Ask the user the decision questions from the file (current stack, priority, Tailwind usage, theme preference, app type). Stop early once the choice narrows to 1-2 options. +3. Present a recommendation with reasoning based on the comparison matrix. +4. Once the user confirms, use the selected framework's prompt from the file — **adapted to the actual project** using `.wolf/anatomy.md` for real file paths, routes, and components. +5. Execute the migration: install dependencies, update config, refactor components. +6. After migration, run `openwolf designqc` to verify the new look. + +**Do NOT read the entire reframe-frameworks.md into context upfront.** Read the decision questions and comparison matrix first (~50 lines). Only read the specific framework's prompt section after the user chooses. + +## Session End + +Before ending or when asked to wrap up: + +1. Write a session summary to `.wolf/memory.md`. +2. Review the session: did you learn anything? Did the user correct you? Did you fix a bug? If yes, update `.wolf/cerebrum.md` and/or `.wolf/buglog.json`. diff --git a/.wolf/anatomy.md b/.wolf/anatomy.md new file mode 100644 index 0000000..3ef9479 --- /dev/null +++ b/.wolf/anatomy.md @@ -0,0 +1,921 @@ +# anatomy.md + +> Auto-maintained by OpenWolf. Last scanned: 2026-04-21T17:17:05.858Z +> Files: 527 tracked | Anatomy hits: 0 | Misses: 0 + +## ./ + +- `.dockerignore` — Docker ignore rules (~113 tok) +- `.gitignore` — Git ignore rules (~213 tok) +- `.gitmodules` (~182 tok) +- `.mcp.json` (~57 tok) +- `CLAUDE.md` — OpenWolf (~5228 tok) +- `package-lock.json` — npm lock file (~1030 tok) +- `package.json` — Node.js package manifest (~239 tok) +- `playwright.config.ts` — Playwright test configuration (~1152 tok) +- `README.md` — Project documentation (~1712 tok) +- `SETUP-SUBMODULES.md` — Setting Up Git Submodules (~1406 tok) +- `setup-submodules.ps1` — PowerShell script to set up Git submodules for CAT Pattern Tutorial (~419 tok) +- `sync-master-to-develop-auto.sh` — Script to sync master/main branch to develop branch in all submodules (auto-push version) (~717 tok) +- `sync-master-to-develop.sh` — Script to sync master/main branch to develop branch in all submodules (~753 tok) + +## .claude/ + +- `settings.json` (~530 tok) +- `settings.local.json` — Declares p (~408 tok) + +## .claude/rules/ + +- `openwolf.md` (~313 tok) + +## .github/agents/ + +- `playwright-test-generator.agent.md` — For each test you generate (~891 tok) +- `playwright-test-healer.agent.md` (~764 tok) +- `playwright-test-planner.agent.md` (~745 tok) + +## .github/workflows/ + +- `copilot-setup-steps.yml` — CI: "Copilot Setup Steps" (~195 tok) +- `deploy-angular.yml` — CI: Deploy Angular to Azure Static Web Apps (~772 tok) +- `deploy-api.yml` — CI: Deploy .NET API to Azure App Service (~932 tok) +- `deploy-gh-pages.yml` — CI: Deploy Angular to GitHub Pages (~590 tok) +- `deploy-identityserver.yml` — CI: Deploy IdentityServer to Azure App Service (~1694 tok) +- `deploy-infra.yml` — CI: Deploy Azure Infrastructure (Bicep) (~385 tok) +- `playwright.yml` — CI: Playwright Tests (~340 tok) + +## ApiResources/TalentManagement-API/ + +- `.gitattributes` — Git attributes (~689 tok) +- `.gitignore` — Git ignore rules (~1756 tok) +- `AGENTS.md` — Repository Guidelines (~756 tok) +- `ai-test.log` (~21 tok) +- `crud-bug-personname.md` — CRUD Bug Fix: Employee `PersonName` Mapping (~682 tok) +- `Directory.Build.props` (~111 tok) +- `docker-compose.dcproj` (~195 tok) +- `docker-compose.override.yml` — Docker Compose: 3 services (~432 tok) +- `docker-compose.yml` — Docker Compose services (~64 tok) +- `launchSettings.json` (~64 tok) +- `LICENSE.txt` (~273 tok) +- `README.md` — Project documentation (~6 tok) +- `Sync-To-MyOnion.md` — Sync-To-MyOnion (~951 tok) +- `TalentManagementAPI.slnx` (~379 tok) + +## ApiResources/TalentManagement-API/.dotnet/ + +- `10.0.103.aspNetCertificateSentinel` (~0 tok) +- `10.0.103.dotnetFirstUseSentinel` (~0 tok) +- `10.0.103.toolpath.sentinel` (~0 tok) + +## ApiResources/TalentManagement-API/.dotnet/.dotnet/ + +- `.workloadAdvertisingManifestSentinel10.0.200` (~0 tok) +- `10.0.201.aspNetCertificateSentinel` (~0 tok) +- `10.0.201.dotnetFirstUseSentinel` (~0 tok) +- `10.0.201.toolpath.sentinel` (~0 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/ardalis.specification.entityframeworkcore/9.3.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~3347 tok) +- `ardalis.specification.entityframeworkcore.9.3.1.nupkg` (~21145 tok) +- `ardalis.specification.entityframeworkcore.9.3.1.nupkg.sha512` (~24 tok) +- `ardalis.specification.entityframeworkcore.nuspec` (~515 tok) +- `LICENSE` — Project license (~285 tok) +- `readme-nuget.md` (~71 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/ardalis.specification.entityframeworkcore/9.3.1/lib/net8.0/ + +- `Ardalis.Specification.EntityFrameworkCore.xml` — Declares of (~4689 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/ardalis.specification.entityframeworkcore/9.3.1/lib/net9.0/ + +- `Ardalis.Specification.EntityFrameworkCore.xml` — Declares of (~4689 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/ardalis.specification/9.3.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~3349 tok) +- `ardalis.specification.9.3.1.nupkg` (~27790 tok) +- `ardalis.specification.9.3.1.nupkg.sha512` (~24 tok) +- `ardalis.specification.nuspec` (~386 tok) +- `LICENSE` — Project license (~285 tok) +- `readme-nuget.md` (~71 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/ardalis.specification/9.3.1/lib/net8.0/ + +- `Ardalis.Specification.xml` — Declares of (~33407 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/ardalis.specification/9.3.1/lib/net9.0/ + +- `Ardalis.Specification.xml` — Declares of (~33407 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/ardalis.specification/9.3.1/lib/netstandard2.0/ + +- `Ardalis.Specification.xml` — Declares of (~33244 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/asp.versioning.abstractions/8.1.0/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~6328 tok) +- `asp.versioning.abstractions.8.1.0.nupkg` (~47262 tok) +- `asp.versioning.abstractions.8.1.0.nupkg.sha512` (~24 tok) +- `asp.versioning.abstractions.nuspec` (~415 tok) +- `LICENSE.txt` (~281 tok) +- `README.md` — Project documentation (~248 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/asp.versioning.abstractions/8.1.0/lib/net8.0/ + +- `Asp.Versioning.Abstractions.xml` — Declares used (~41802 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/asp.versioning.abstractions/8.1.0/lib/netstandard1.0/ + +- `Asp.Versioning.Abstractions.xml` — Declares used (~39730 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/asp.versioning.abstractions/8.1.0/lib/netstandard2.0/ + +- `Asp.Versioning.Abstractions.xml` — Declares used (~42000 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/asp.versioning.http/8.1.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~6686 tok) +- `asp.versioning.http.8.1.1.nupkg` (~29582 tok) +- `asp.versioning.http.8.1.1.nupkg.sha512` (~24 tok) +- `asp.versioning.http.nuspec` (~376 tok) +- `LICENSE.txt` (~281 tok) +- `README.md` — Project documentation (~417 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/asp.versioning.http/8.1.1/lib/net8.0/ + +- `Asp.Versioning.Http.xml` (~38582 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/asp.versioning.mvc.apiexplorer/8.1.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~6688 tok) +- `asp.versioning.mvc.apiexplorer.8.1.1.nupkg` (~18752 tok) +- `asp.versioning.mvc.apiexplorer.8.1.1.nupkg.sha512` (~24 tok) +- `asp.versioning.mvc.apiexplorer.nuspec` (~334 tok) +- `LICENSE.txt` (~281 tok) +- `README.md` — Project documentation (~285 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/asp.versioning.mvc.apiexplorer/8.1.1/lib/net8.0/ + +- `Asp.Versioning.Mvc.ApiExplorer.xml` — Declares used (~13046 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/asp.versioning.mvc/8.1.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~6692 tok) +- `asp.versioning.mvc.8.1.1.nupkg` (~20181 tok) +- `asp.versioning.mvc.8.1.1.nupkg.sha512` (~24 tok) +- `asp.versioning.mvc.nuspec` (~330 tok) +- `LICENSE.txt` (~281 tok) +- `README.md` — Project documentation (~245 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/asp.versioning.mvc/8.1.1/lib/net8.0/ + +- `Asp.Versioning.Mvc.xml` (~25287 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/autobogus/2.13.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~2463 tok) +- `autobogus.2.13.1.nupkg` (~31581 tok) +- `autobogus.2.13.1.nupkg.sha512` (~24 tok) +- `autobogus.nuspec` (~455 tok) +- `LICENSE` — Project license (~291 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.core/1.46.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~6607 tok) +- `azure.core.1.46.1.nupkg.sha512` (~24 tok) +- `azure.core.nuspec` (~1192 tok) +- `CHANGELOG.md` — Change log (~6582 tok) +- `README.md` — Project documentation (~3408 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.core/1.46.1/lib/net462/ + +- `Azure.Core.xml` — Declares of (~155849 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.core/1.46.1/lib/net472/ + +- `Azure.Core.xml` — Declares of (~156346 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.core/1.46.1/lib/net6.0/ + +- `Azure.Core.xml` — Declares of (~148732 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.core/1.46.1/lib/net8.0/ + +- `Azure.Core.xml` — Declares of (~149114 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.core/1.46.1/lib/netstandard2.0/ + +- `Azure.Core.xml` — Declares of (~155652 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.core/1.47.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~6633 tok) +- `azure.core.1.47.1.nupkg` (~256758 tok) +- `azure.core.1.47.1.nupkg.sha512` (~24 tok) +- `azure.core.nuspec` (~1102 tok) +- `CHANGELOG.md` — Change log (~6746 tok) +- `README.md` — Project documentation (~3408 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.core/1.47.1/lib/net462/ + +- `Azure.Core.xml` — Declares of (~159408 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.core/1.47.1/lib/net472/ + +- `Azure.Core.xml` — Declares of (~159905 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.core/1.47.1/lib/net8.0/ + +- `Azure.Core.xml` — Declares of (~152673 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.core/1.47.1/lib/netstandard2.0/ + +- `Azure.Core.xml` — Declares of (~159211 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.identity/1.14.2/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~6638 tok) +- `azure.identity.1.14.2.nupkg` (~95444 tok) +- `azure.identity.1.14.2.nupkg.sha512` (~24 tok) +- `azure.identity.nuspec` (~549 tok) +- `CHANGELOG.md` — Change log (~13909 tok) +- `README.md` — Project documentation (~6853 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.identity/1.14.2/lib/net8.0/ + +- `Azure.Identity.xml` — Declares raised (~71612 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/azure.identity/1.14.2/lib/netstandard2.0/ + +- `Azure.Identity.xml` — Declares raised (~77669 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/bogus/31.0.3/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~2460 tok) +- `bogus.31.0.3.nupkg.sha512` (~24 tok) +- `bogus.nuspec` — Declares or (~884 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/bogus/31.0.3/lib/net40/ + +- `Bogus.xml` — Declares for (~49920 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/bogus/31.0.3/lib/netstandard1.3/ + +- `Bogus.xml` — Declares for (~49912 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/bogus/31.0.3/lib/netstandard2.0/ + +- `Bogus.xml` — Declares for (~49920 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/bogus/35.6.5/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~3348 tok) +- `bogus.35.6.5.nupkg.sha512` (~24 tok) +- `bogus.nuspec` — Declares or (~892 tok) +- `LICENSE` — Project license (~947 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/bogus/35.6.5/lib/net40/ + +- `Bogus.xml` — Declares for (~51088 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/bogus/35.6.5/lib/net6.0/ + +- `Bogus.xml` — Declares for (~52164 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/bogus/35.6.5/lib/netstandard1.3/ + +- `Bogus.xml` — Declares for (~51081 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/bogus/35.6.5/lib/netstandard2.0/ + +- `Bogus.xml` — Declares for (~51088 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/bouncycastle.cryptography/2.6.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~7092 tok) +- `bouncycastle.cryptography.2.6.1.nupkg.sha512` (~24 tok) +- `bouncycastle.cryptography.nuspec` (~375 tok) +- `LICENSE.md` (~284 tok) +- `README.md` — Project documentation (~979 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/castle.core/5.1.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~2987 tok) +- `ASL - Apache Software Foundation License.txt` (~2299 tok) +- `castle.core.5.1.1.nupkg` (~202091 tok) +- `castle.core.5.1.1.nupkg.sha512` (~24 tok) +- `castle.core.nuspec` (~471 tok) +- `CHANGELOG.md` — Change log (~7119 tok) +- `LICENSE` — Project license (~158 tok) +- `readme.txt` (~139 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/castle.core/5.1.1/lib/net462/ + +- `Castle.Core.xml` — Declares of (~122661 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/castle.core/5.1.1/lib/net6.0/ + +- `Castle.Core.xml` — Declares of (~119513 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/castle.core/5.1.1/lib/netstandard2.0/ + +- `Castle.Core.xml` — Declares of (~119513 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/castle.core/5.1.1/lib/netstandard2.1/ + +- `Castle.Core.xml` — Declares of (~119513 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/coverlet.collector/6.0.0/ + +- `.nupkg.metadata` (~55 tok) +- `.signature.p7s` (~5885 tok) +- `coverlet.collector.6.0.0.nupkg.sha512` (~24 tok) +- `coverlet.collector.nuspec` (~277 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/easycaching.core/1.8.0/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~2984 tok) +- `easycaching.core.1.8.0.nupkg` (~31816 tok) +- `easycaching.core.1.8.0.nupkg.sha512` (~24 tok) +- `easycaching.core.nuspec` (~514 tok) +- `LICENSE` — Project license (~291 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/easycaching.core/1.8.0/lib/net6.0/ + +- `EasyCaching.Core.xml` (~29443 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/easycaching.core/1.8.0/lib/netstandard2.0/ + +- `EasyCaching.Core.xml` (~29443 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/easycaching.inmemory/1.8.0/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~2984 tok) +- `easycaching.inmemory.1.8.0.nupkg` (~25373 tok) +- `easycaching.inmemory.1.8.0.nupkg.sha512` (~24 tok) +- `easycaching.inmemory.nuspec` (~288 tok) +- `LICENSE` — Project license (~291 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/easycaching.inmemory/1.8.0/lib/net6.0/ + +- `EasyCaching.InMemory.xml` — Declares parameter (~6234 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/easycaching.inmemory/1.8.0/lib/netstandard2.0/ + +- `EasyCaching.InMemory.xml` — Declares parameter (~6234 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/easycaching.redis/1.8.0/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~2985 tok) +- `easycaching.redis.1.8.0.nupkg` (~33135 tok) +- `easycaching.redis.1.8.0.nupkg.sha512` (~24 tok) +- `easycaching.redis.nuspec` (~343 tok) +- `LICENSE` — Project license (~291 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/easycaching.redis/1.8.0/lib/net6.0/ + +- `EasyCaching.Redis.xml` — Declares parameter (~7460 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/easycaching.redis/1.8.0/lib/netstandard2.0/ + +- `EasyCaching.Redis.xml` — Declares parameter (~7460 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/easycaching.serialization.systemtextjson/1.8.0/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~2982 tok) +- `easycaching.serialization.systemtextjson.1.8.0.nupkg` (~14776 tok) +- `easycaching.serialization.systemtextjson.1.8.0.nupkg.sha512` (~24 tok) +- `easycaching.serialization.systemtextjson.nuspec` (~333 tok) +- `LICENSE` — Project license (~291 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/easycaching.serialization.systemtextjson/1.8.0/lib/net6.0/ + +- `EasyCaching.Serialization.SystemTextJson.xml` — Declares parameter (~3403 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/easycaching.serialization.systemtextjson/1.8.0/lib/netstandard2.0/ + +- `EasyCaching.Serialization.SystemTextJson.xml` — Declares parameter (~3403 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/fluentassertions/6.12.0/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~2984 tok) +- `fluentassertions.6.12.0.nupkg.sha512` (~24 tok) +- `fluentassertions.nuspec` (~731 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/fluentassertions/6.12.0/lib/net47/ + +- `FluentAssertions.pdb` (~60124 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/fluentassertions/6.12.0/lib/net6.0/ + +- `FluentAssertions.pdb` (~57330 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/fluentassertions/6.12.0/lib/netcoreapp2.1/ + +- `FluentAssertions.pdb` (~60469 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/fluentassertions/6.12.0/lib/netcoreapp3.0/ + +- `FluentAssertions.pdb` (~57837 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/fluentassertions/6.12.0/lib/netstandard2.0/ + +- `FluentAssertions.pdb` (~58501 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/fluentassertions/6.12.0/lib/netstandard2.1/ + +- `FluentAssertions.pdb` (~57543 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/fluentvalidation.dependencyinjectionextensions/12.1.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~3347 tok) +- `fluentvalidation.dependencyinjectionextensions.12.1.1.nupkg` (~20135 tok) +- `fluentvalidation.dependencyinjectionextensions.12.1.1.nupkg.sha512` (~24 tok) +- `fluentvalidation.dependencyinjectionextensions.nuspec` (~351 tok) +- `README.md` — Project documentation (~300 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/fluentvalidation.dependencyinjectionextensions/12.1.1/lib/net8.0/ + +- `FluentValidation.DependencyInjectionExtensions.xml` — Declares whose (~1492 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/fluentvalidation/12.1.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~3348 tok) +- `fluentvalidation.12.1.1.nupkg` (~66406 tok) +- `fluentvalidation.12.1.1.nupkg.sha512` (~24 tok) +- `fluentvalidation.nuspec` — Declares to (~334 tok) +- `README.md` — Project documentation (~500 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/fluentvalidation/12.1.1/lib/net8.0/ + +- `FluentValidation.xml` — Declares for (~65437 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/humanizer.core/2.14.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~4851 tok) +- `humanizer.core.2.14.1.nupkg` (~136020 tok) +- `humanizer.core.2.14.1.nupkg.sha512` (~24 tok) +- `humanizer.core.nuspec` (~326 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/humanizer.core/2.14.1/lib/net6.0/ + +- `Humanizer.xml` (~113423 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/humanizer.core/2.14.1/lib/netstandard1.0/ + +- `Humanizer.xml` (~81834 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/humanizer.core/2.14.1/lib/netstandard2.0/ + +- `Humanizer.xml` (~81834 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/mailkit/4.14.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~3348 tok) +- `mailkit.4.14.1.nupkg.sha512` (~24 tok) +- `mailkit.nuspec` (~1522 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/mailkit/4.14.1/docs/ + +- `ExchangeOAuth2.md` — Using OAuth2 With Exchange (IMAP, POP3 or SMTP) (~3719 tok) +- `FAQ.md` — Frequently Asked Questions (~20562 tok) +- `GMailOAuth2.md` — Using OAuth2 With GMail (IMAP, POP3 or SMTP) (~1970 tok) +- `README.md` — Project documentation (~7092 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/mailkit/4.14.1/lib/net462/ + +- `MailKit.dll.config` (~201 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/mailkit/4.14.1/lib/net47/ + +- `MailKit.dll.config` (~201 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/mailkit/4.14.1/lib/net48/ + +- `MailKit.dll.config` (~201 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/mailkit/4.14.1/lib/net8.0/ + +- `MailKit.dll.config` (~8332 tok) +- `MailKit.pdb` (~83629 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/mailkit/4.14.1/lib/netstandard2.0/ + +- `MailKit.dll.config` (~114 tok) +- `MailKit.pdb` (~81470 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/mailkit/4.14.1/lib/netstandard2.1/ + +- `MailKit.dll.config` (~287 tok) +- `MailKit.pdb` (~81522 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/mapster.core/1.2.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~2987 tok) +- `mapster.core.1.2.1.nupkg` (~12327 tok) +- `mapster.core.1.2.1.nupkg.sha512` (~24 tok) +- `mapster.core.nuspec` (~269 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/mapster.dependencyinjection/1.0.0/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~2460 tok) +- `mapster.dependencyinjection.1.0.0.nupkg` (~7539 tok) +- `mapster.dependencyinjection.1.0.0.nupkg.sha512` (~24 tok) +- `mapster.dependencyinjection.nuspec` (~488 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/mapster/5.0.0/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~2462 tok) +- `mapster.5.0.0.nupkg` (~68403 tok) +- `mapster.5.0.0.nupkg.sha512` (~24 tok) +- `mapster.nuspec` (~552 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/mapster/7.4.0/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~2986 tok) +- `mapster.7.4.0.nupkg` (~40626 tok) +- `mapster.7.4.0.nupkg.sha512` (~24 tok) +- `mapster.nuspec` (~334 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.aspnetcore.authentication.jwtbearer/10.0.1/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~6643 tok) +- `microsoft.aspnetcore.authentication.jwtbearer.10.0.1.nupkg` (~17662 tok) +- `microsoft.aspnetcore.authentication.jwtbearer.10.0.1.nupkg.sha512` (~24 tok) +- `microsoft.aspnetcore.authentication.jwtbearer.nuspec` (~388 tok) +- `PACKAGE.md` — About (~791 tok) +- `THIRD-PARTY-NOTICES.TXT` (~14187 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.aspnetcore.authentication.jwtbearer/10.0.1/lib/net10.0/ + +- `Microsoft.AspNetCore.Authentication.JwtBearer.xml` — Declares provides (~8936 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.asyncinterfaces/8.0.0/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~6261 tok) +- `LICENSE.TXT` (~285 tok) +- `microsoft.bcl.asyncinterfaces.8.0.0.nupkg` (~25338 tok) +- `microsoft.bcl.asyncinterfaces.8.0.0.nupkg.sha512` (~24 tok) +- `microsoft.bcl.asyncinterfaces.nuspec` (~424 tok) +- `PACKAGE.md` — About (~582 tok) +- `THIRD-PARTY-NOTICES.TXT` — Declares of (~17226 tok) +- `useSharedDesignerContext.txt` (~0 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.asyncinterfaces/8.0.0/buildTransitive/net461/ + +- `Microsoft.Bcl.AsyncInterfaces.targets` (~177 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.asyncinterfaces/8.0.0/buildTransitive/net462/ + +- `_._` (~0 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.asyncinterfaces/8.0.0/lib/net462/ + +- `Microsoft.Bcl.AsyncInterfaces.xml` — Declares of (~8938 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.asyncinterfaces/8.0.0/lib/netstandard2.0/ + +- `Microsoft.Bcl.AsyncInterfaces.xml` — Declares of (~8938 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.asyncinterfaces/8.0.0/lib/netstandard2.1/ + +- `Microsoft.Bcl.AsyncInterfaces.xml` — Declares for (~2030 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.cryptography/9.0.4/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~6615 tok) +- `LICENSE.TXT` (~285 tok) +- `microsoft.bcl.cryptography.9.0.4.nupkg` (~58625 tok) +- `microsoft.bcl.cryptography.9.0.4.nupkg.sha512` (~24 tok) +- `microsoft.bcl.cryptography.nuspec` (~492 tok) +- `PACKAGE.md` — About (~322 tok) +- `THIRD-PARTY-NOTICES.TXT` (~18908 tok) +- `useSharedDesignerContext.txt` (~0 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.cryptography/9.0.4/buildTransitive/net461/ + +- `Microsoft.Bcl.Cryptography.targets` (~174 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.cryptography/9.0.4/buildTransitive/net462/ + +- `_._` (~0 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.cryptography/9.0.4/buildTransitive/net8.0/ + +- `_._` (~0 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.cryptography/9.0.4/buildTransitive/netcoreapp2.0/ + +- `Microsoft.Bcl.Cryptography.targets` (~174 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.cryptography/9.0.4/lib/net462/ + +- `Microsoft.Bcl.Cryptography.xml` — Declares are (~27863 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.cryptography/9.0.4/lib/net8.0/ + +- `Microsoft.Bcl.Cryptography.xml` — Declares checks (~12230 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.cryptography/9.0.4/lib/net9.0/ + +- `Microsoft.Bcl.Cryptography.xml` (~44 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.bcl.cryptography/9.0.4/lib/netstandard2.0/ + +- `Microsoft.Bcl.Cryptography.xml` — Declares are (~25400 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/17.11.31/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~6600 tok) +- `microsoft.build.framework.17.11.31.nupkg.sha512` (~24 tok) +- `microsoft.build.framework.nuspec` (~517 tok) +- `README.md` — Project documentation (~231 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/17.11.31/lib/net472/ + +- `Microsoft.Build.Framework.pdb` (~53964 tok) +- `Microsoft.Build.Framework.xml` — Declares is (~168182 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/17.11.31/lib/net8.0/ + +- `Microsoft.Build.Framework.pdb` (~52462 tok) +- `Microsoft.Build.Framework.xml` — Declares is (~143752 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/17.11.31/notices/ + +- `THIRDPARTYNOTICES.txt` (~508 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/17.11.31/ref/net472/ + +- `Microsoft.Build.Framework.xml` — Declares is (~168182 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/17.11.31/ref/net8.0/ + +- `Microsoft.Build.Framework.xml` — Declares is (~143752 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/17.11.31/ref/netstandard2.0/ + +- `Microsoft.Build.Framework.xml` — Declares is (~146384 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/18.0.2/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~6639 tok) +- `microsoft.build.framework.18.0.2.nupkg` (~244466 tok) +- `microsoft.build.framework.18.0.2.nupkg.sha512` (~24 tok) +- `microsoft.build.framework.nuspec` (~737 tok) +- `README.md` — Project documentation (~231 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/18.0.2/lib/net10.0/ + +- `Microsoft.Build.Framework.pdb` (~33483 tok) +- `Microsoft.Build.Framework.xml` — Declares in (~147354 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/18.0.2/lib/net472/ + +- `Microsoft.Build.Framework.pdb` (~37868 tok) +- `Microsoft.Build.Framework.xml` — Declares is (~177960 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/18.0.2/notices/ + +- `THIRDPARTYNOTICES.txt` (~792 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/18.0.2/ref/net10.0/ + +- `Microsoft.Build.Framework.xml` — Declares in (~147354 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/18.0.2/ref/net472/ + +- `Microsoft.Build.Framework.xml` — Declares is (~177960 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.build.framework/18.0.2/ref/netstandard2.0/ + +- `Microsoft.Build.Framework.xml` — Declares is (~156013 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.codeanalysis.analyzers/3.11.0/ + +- `.nupkg.metadata` (~49 tok) +- `.signature.p7s` (~6611 tok) +- `microsoft.codeanalysis.analyzers.3.11.0.nupkg.sha512` (~24 tok) +- `microsoft.codeanalysis.analyzers.nuspec` (~374 tok) +- `ThirdPartyNotices.txt` — Declares name (~3558 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.codeanalysis.analyzers/3.11.0/buildTransitive/ + +- `Microsoft.CodeAnalysis.Analyzers.props` (~271 tok) +- `Microsoft.CodeAnalysis.Analyzers.targets` (~14942 tok) + +## ApiResources/TalentManagement-API/.nuget/packages/microsoft.codeanalysis.analyzers/3.11.0/buildTransitive/config/ + +- `analysislevel_2_9_8_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1538 tok) +- `analysislevel_2_9_8_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~913 tok) +- `analysislevel_2_9_8_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1540 tok) +- `analysislevel_2_9_8_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~915 tok) +- `analysislevel_2_9_8_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1540 tok) +- `analysislevel_2_9_8_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~946 tok) +- `analysislevel_2_9_8_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1564 tok) +- `analysislevel_2_9_8_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1519 tok) +- `analysislevel_2_9_8_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1542 tok) +- `analysislevel_2_9_8_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~948 tok) +- `analysislevel_3_3_3_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1543 tok) +- `analysislevel_3_3_3_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~344 tok) +- `analysislevel_3_3_3_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1545 tok) +- `analysislevel_3_3_3_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~346 tok) +- `analysislevel_3_3_3_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1545 tok) +- `analysislevel_3_3_3_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~378 tok) +- `analysislevel_3_3_3_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1564 tok) +- `analysislevel_3_3_3_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1519 tok) +- `analysislevel_3_3_3_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1547 tok) +- `analysislevel_3_3_3_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~380 tok) +- `analysislevel_3_3_4_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1519 tok) +- `analysislevel_3_3_4_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~258 tok) +- `analysislevel_3_3_4_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1521 tok) +- `analysislevel_3_3_4_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~260 tok) +- `analysislevel_3_3_4_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1521 tok) +- `analysislevel_3_3_4_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~317 tok) +- `analysislevel_3_3_4_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1564 tok) +- `analysislevel_3_3_4_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1519 tok) +- `analysislevel_3_3_4_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1523 tok) +- `analysislevel_3_3_4_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~319 tok) +- `analysislevel_3_3_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1543 tok) +- `analysislevel_3_3_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~370 tok) +- `analysislevel_3_3_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1545 tok) +- `analysislevel_3_3_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~372 tok) +- `analysislevel_3_3_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1545 tok) +- `analysislevel_3_3_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~404 tok) +- `analysislevel_3_3_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1564 tok) +- `analysislevel_3_3_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1519 tok) +- `analysislevel_3_3_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1547 tok) +- `analysislevel_3_3_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~406 tok) +- `analysislevel_3_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1542 tok) +- `analysislevel_3_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~443 tok) +- `analysislevel_3_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1544 tok) +- `analysislevel_3_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~446 tok) +- `analysislevel_3_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1544 tok) +- `analysislevel_3_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~477 tok) +- `analysislevel_3_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1564 tok) +- `analysislevel_3_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1519 tok) +- `analysislevel_3_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1546 tok) +- `analysislevel_3_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~479 tok) +- `analysislevel_4_3_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1490 tok) +- `analysislevel_4_3_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~84 tok) +- `analysislevel_4_3_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1492 tok) +- `analysislevel_4_3_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~86 tok) +- `analysislevel_4_3_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1492 tok) +- `analysislevel_4_3_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~172 tok) +- `analysislevel_4_3_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1562 tok) +- `analysislevel_4_3_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1518 tok) +- `analysislevel_4_3_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~1495 tok) +- `analysislevel_4_3_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~174 tok) +- `analysislevelcorrectness_2_9_8_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~240 tok) +- `analysislevelcorrectness_2_9_8_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevelcorrectness_2_9_8_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~243 tok) +- `analysislevelcorrectness_2_9_8_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevelcorrectness_2_9_8_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~243 tok) +- `analysislevelcorrectness_2_9_8_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevelcorrectness_2_9_8_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~240 tok) +- `analysislevelcorrectness_2_9_8_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~196 tok) +- `analysislevelcorrectness_2_9_8_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~245 tok) +- `analysislevelcorrectness_2_9_8_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~97 tok) +- `analysislevelcorrectness_3_3_3_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~240 tok) +- `analysislevelcorrectness_3_3_3_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevelcorrectness_3_3_3_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~243 tok) +- `analysislevelcorrectness_3_3_3_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevelcorrectness_3_3_3_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~243 tok) +- `analysislevelcorrectness_3_3_3_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevelcorrectness_3_3_3_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~240 tok) +- `analysislevelcorrectness_3_3_3_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~196 tok) +- `analysislevelcorrectness_3_3_3_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~245 tok) +- `analysislevelcorrectness_3_3_3_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~97 tok) +- `analysislevelcorrectness_3_3_4_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~240 tok) +- `analysislevelcorrectness_3_3_4_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevelcorrectness_3_3_4_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~243 tok) +- `analysislevelcorrectness_3_3_4_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevelcorrectness_3_3_4_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~243 tok) +- `analysislevelcorrectness_3_3_4_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevelcorrectness_3_3_4_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~240 tok) +- `analysislevelcorrectness_3_3_4_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~196 tok) +- `analysislevelcorrectness_3_3_4_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~245 tok) +- `analysislevelcorrectness_3_3_4_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~97 tok) +- `analysislevelcorrectness_3_3_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~240 tok) +- `analysislevelcorrectness_3_3_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevelcorrectness_3_3_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~243 tok) +- `analysislevelcorrectness_3_3_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevelcorrectness_3_3_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~243 tok) +- `analysislevelcorrectness_3_3_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevelcorrectness_3_3_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~240 tok) +- `analysislevelcorrectness_3_3_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~196 tok) +- `analysislevelcorrectness_3_3_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~245 tok) +- `analysislevelcorrectness_3_3_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~97 tok) +- `analysislevelcorrectness_3_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~240 tok) +- `analysislevelcorrectness_3_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevelcorrectness_3_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~243 tok) +- `analysislevelcorrectness_3_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevelcorrectness_3_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~243 tok) +- `analysislevelcorrectness_3_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevelcorrectness_3_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~240 tok) +- `analysislevelcorrectness_3_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~196 tok) +- `analysislevelcorrectness_3_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~245 tok) +- `analysislevelcorrectness_3_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~97 tok) +- `analysislevelcorrectness_4_3_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~239 tok) +- `analysislevelcorrectness_4_3_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~91 tok) +- `analysislevelcorrectness_4_3_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~241 tok) +- `analysislevelcorrectness_4_3_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~94 tok) +- `analysislevelcorrectness_4_3_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~241 tok) +- `analysislevelcorrectness_4_3_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~94 tok) +- `analysislevelcorrectness_4_3_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~239 tok) +- `analysislevelcorrectness_4_3_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~194 tok) +- `analysislevelcorrectness_4_3_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~243 tok) +- `analysislevelcorrectness_4_3_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~96 tok) +- `analysislevellibrary_2_9_8_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~157 tok) +- `analysislevellibrary_2_9_8_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~91 tok) +- `analysislevellibrary_2_9_8_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~159 tok) +- `analysislevellibrary_2_9_8_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevellibrary_2_9_8_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~159 tok) +- `analysislevellibrary_2_9_8_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevellibrary_2_9_8_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~157 tok) +- `analysislevellibrary_2_9_8_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~113 tok) +- `analysislevellibrary_2_9_8_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~161 tok) +- `analysislevellibrary_2_9_8_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevellibrary_3_3_3_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~157 tok) +- `analysislevellibrary_3_3_3_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~91 tok) +- `analysislevellibrary_3_3_3_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~159 tok) +- `analysislevellibrary_3_3_3_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevellibrary_3_3_3_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~159 tok) +- `analysislevellibrary_3_3_3_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevellibrary_3_3_3_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~157 tok) +- `analysislevellibrary_3_3_3_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~113 tok) +- `analysislevellibrary_3_3_3_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~161 tok) +- `analysislevellibrary_3_3_3_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevellibrary_3_3_4_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~157 tok) +- `analysislevellibrary_3_3_4_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~91 tok) +- `analysislevellibrary_3_3_4_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~159 tok) +- `analysislevellibrary_3_3_4_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevellibrary_3_3_4_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~159 tok) +- `analysislevellibrary_3_3_4_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevellibrary_3_3_4_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~157 tok) +- `analysislevellibrary_3_3_4_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~113 tok) +- `analysislevellibrary_3_3_4_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~161 tok) +- `analysislevellibrary_3_3_4_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevellibrary_3_3_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~157 tok) +- `analysislevellibrary_3_3_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~91 tok) +- `analysislevellibrary_3_3_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~159 tok) +- `analysislevellibrary_3_3_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevellibrary_3_3_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~159 tok) +- `analysislevellibrary_3_3_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevellibrary_3_3_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~157 tok) +- `analysislevellibrary_3_3_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~113 tok) +- `analysislevellibrary_3_3_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~161 tok) +- `analysislevellibrary_3_3_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevellibrary_3_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~157 tok) +- `analysislevellibrary_3_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~91 tok) +- `analysislevellibrary_3_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~159 tok) +- `analysislevellibrary_3_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevellibrary_3_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~159 tok) +- `analysislevellibrary_3_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~93 tok) +- `analysislevellibrary_3_none_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~157 tok) +- `analysislevellibrary_3_none.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~113 tok) +- `analysislevellibrary_3_recommended_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~161 tok) +- `analysislevellibrary_3_recommended.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~95 tok) +- `analysislevellibrary_4_3_all_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~155 tok) +- `analysislevellibrary_4_3_all.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~89 tok) +- `analysislevellibrary_4_3_default_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~158 tok) +- `analysislevellibrary_4_3_default.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~91 tok) +- `analysislevellibrary_4_3_minimum_warnaserror.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~158 tok) +- `analysislevellibrary_4_3_minimum.globalconfig` — NOTE: Requires **VS2019 16.7** or later (~91 tok) + +## Tests/AngularNetTutorial-Playwright/tests/screenshots/ + +- `blog-screenshots.spec.ts` — Blog Screenshots (~10238 tok) diff --git a/.wolf/buglog.json b/.wolf/buglog.json new file mode 100644 index 0000000..419134e --- /dev/null +++ b/.wolf/buglog.json @@ -0,0 +1,21 @@ +{ + "version": 1, + "bugs": [ + { + "id": "bug-001", + "timestamp": "2026-04-21T17:17:05.870Z", + "error_message": "Type error", + "file": "Tests/AngularNetTutorial-Playwright/tests/screenshots/blog-screenshots.spec.ts", + "root_cause": "Missing or incorrect type annotation", + "fix": "Added type assertion/annotation", + "tags": [ + "auto-detected", + "type-fix", + "ts" + ], + "related_bugs": [], + "occurrences": 1, + "last_seen": "2026-04-21T17:17:05.870Z" + } + ] +} \ No newline at end of file diff --git a/.wolf/cerebrum.md b/.wolf/cerebrum.md new file mode 100644 index 0000000..895349b --- /dev/null +++ b/.wolf/cerebrum.md @@ -0,0 +1,23 @@ +# Cerebrum + +> OpenWolf's learning memory. Updated automatically as the AI learns from interactions. +> Do not edit manually unless correcting an error. +> Last updated: 2026-04-21 + +## User Preferences + + + +## Key Learnings + +- **Project:** angularnettutotial +- **Description:** This repository demonstrates the **Client, API Resource, and Token Service (CAT)** pattern using Angular and .NET technologies. + +## Do-Not-Repeat + + + + +## Decision Log + + diff --git a/.wolf/config.json b/.wolf/config.json new file mode 100644 index 0000000..d2c76ae --- /dev/null +++ b/.wolf/config.json @@ -0,0 +1,73 @@ +{ + "version": 1, + "openwolf": { + "enabled": true, + "anatomy": { + "auto_scan_on_init": true, + "rescan_interval_hours": 6, + "max_description_length": 100, + "max_files": 500, + "exclude_patterns": [ + "node_modules", + ".git", + "dist", + "build", + ".wolf", + ".next", + ".nuxt", + "coverage", + "__pycache__", + ".cache", + "target", + ".vscode", + ".idea", + ".turbo", + ".vercel", + ".netlify", + ".output", + "*.min.js", + "*.min.css" + ] + }, + "token_audit": { + "enabled": true, + "report_frequency": "weekly", + "waste_threshold_percent": 15, + "chars_per_token_code": 3.5, + "chars_per_token_prose": 4.0 + }, + "cron": { + "enabled": true, + "max_retry_attempts": 3, + "dead_letter_enabled": true, + "heartbeat_interval_minutes": 30, + "use_claude_p": true, + "api_key_env": null + }, + "memory": { + "consolidation_after_days": 7, + "max_entries_before_consolidation": 200 + }, + "cerebrum": { + "max_tokens": 2000, + "reflection_frequency": "weekly" + }, + "daemon": { + "port": 18790, + "log_level": "info" + }, + "dashboard": { + "enabled": true, + "port": 18791 + }, + "designqc": { + "enabled": true, + "viewports": [ + { "name": "desktop", "width": 1440, "height": 900 }, + { "name": "mobile", "width": 375, "height": 812 } + ], + "max_screenshots": 6, + "chrome_path": null + } + } +} diff --git a/.wolf/cron-manifest.json b/.wolf/cron-manifest.json new file mode 100644 index 0000000..7e2d7f3 --- /dev/null +++ b/.wolf/cron-manifest.json @@ -0,0 +1,97 @@ +{ + "version": 1, + "tasks": [ + { + "id": "anatomy-rescan", + "name": "Full anatomy rescan", + "schedule": "0 */6 * * *", + "description": "Re-scans project filesystem and reconciles anatomy.md", + "action": { "type": "scan_project" }, + "retry": { + "max_attempts": 3, + "backoff": "exponential", + "base_delay_seconds": 30 + }, + "failsafe": { + "on_failure": "log_and_continue", + "alert_after_consecutive_failures": 2, + "dead_letter": true + }, + "enabled": true + }, + { + "id": "memory-consolidation", + "name": "Consolidate old memory", + "schedule": "0 2 * * *", + "description": "Compress memory.md entries older than 7 days", + "action": { + "type": "consolidate_memory", + "params": { "older_than_days": 7 } + }, + "retry": { + "max_attempts": 2, + "backoff": "exponential", + "base_delay_seconds": 60 + }, + "failsafe": { + "on_failure": "skip_and_retry_next_cycle", + "dead_letter": false + }, + "enabled": true + }, + { + "id": "token-audit", + "name": "Token audit report", + "schedule": "0 0 * * 1", + "description": "Weekly waste pattern detection", + "action": { "type": "generate_token_report" }, + "retry": { + "max_attempts": 2, + "backoff": "linear", + "base_delay_seconds": 60 + }, + "failsafe": { "on_failure": "log_and_continue", "dead_letter": true }, + "enabled": true + }, + { + "id": "cerebrum-reflection", + "name": "Cerebrum reflection", + "schedule": "0 3 * * 0", + "description": "Weekly AI review of cerebrum.md — prune stale entries, consolidate duplicates", + "action": { + "type": "ai_task", + "params": { + "prompt": "Review this cerebrum.md. Remove duplicate preferences (keep newer). Remove Do-Not-Repeat entries older than 90 days if no longer relevant. Consolidate Key Learnings that overlap. Keep the file under 2000 tokens. Return the cleaned file content only.", + "context_files": [".wolf/cerebrum.md"] + } + }, + "retry": { + "max_attempts": 1, + "backoff": "none", + "base_delay_seconds": 0 + }, + "failsafe": { "on_failure": "log_and_continue", "dead_letter": false }, + "enabled": true + }, + { + "id": "project-suggestions", + "name": "AI suggestions", + "schedule": "0 4 * * 1", + "description": "Weekly AI analysis with project improvement suggestions", + "action": { + "type": "ai_task", + "params": { + "prompt": "Based on the recent memory entries and current project structure, provide: 1) Key achievements this week, 2) Code improvements to consider, 3) Logical next tasks, 4) Technical debt or risks. Be specific and actionable. Return as JSON: {achievements:[], improvements:[], next_tasks:[], risks:[]}", + "context_files": [".wolf/memory.md", ".wolf/anatomy.md"] + } + }, + "retry": { + "max_attempts": 1, + "backoff": "none", + "base_delay_seconds": 0 + }, + "failsafe": { "on_failure": "log_and_continue", "dead_letter": false }, + "enabled": true + } + ] +} diff --git a/.wolf/cron-state.json b/.wolf/cron-state.json new file mode 100644 index 0000000..ce4dcb7 --- /dev/null +++ b/.wolf/cron-state.json @@ -0,0 +1,7 @@ +{ + "last_heartbeat": null, + "engine_status": "initialized", + "execution_log": [], + "dead_letter_queue": [], + "upcoming": [] +} diff --git a/.wolf/designqc-report.json b/.wolf/designqc-report.json new file mode 100644 index 0000000..8f658ee --- /dev/null +++ b/.wolf/designqc-report.json @@ -0,0 +1,6 @@ +{ + "captured_at": null, + "captures": [], + "total_size_kb": 0, + "estimated_tokens": 0 +} \ No newline at end of file diff --git a/.wolf/hooks/_session.json b/.wolf/hooks/_session.json new file mode 100644 index 0000000..1eb4a4f --- /dev/null +++ b/.wolf/hooks/_session.json @@ -0,0 +1,27 @@ +{ + "session_id": "", + "started": "", + "files_read": { + "c:/apps/AngularNetTutotial/Tests/AngularNetTutorial-Playwright/tests/screenshots/blog-screenshots.spec.ts": { + "count": 2, + "tokens": 0, + "first_read": "2026-04-21T17:16:03.197Z" + } + }, + "files_written": [ + { + "file": "c:/apps/AngularNetTutotial/Tests/AngularNetTutorial-Playwright/tests/screenshots/blog-screenshots.spec.ts", + "action": "edit", + "tokens": 2896, + "at": "2026-04-21T17:17:05.867Z" + } + ], + "edit_counts": { + "Tests/AngularNetTutorial-Playwright/tests/screenshots/blog-screenshots.spec.ts": 1 + }, + "anatomy_hits": 0, + "anatomy_misses": 1, + "repeated_reads_warned": 1, + "cerebrum_warnings": 0, + "stop_count": 2 +} \ No newline at end of file diff --git a/.wolf/hooks/package.json b/.wolf/hooks/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/.wolf/hooks/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/.wolf/hooks/post-read.js b/.wolf/hooks/post-read.js new file mode 100644 index 0000000..5d22fbb --- /dev/null +++ b/.wolf/hooks/post-read.js @@ -0,0 +1,69 @@ +import * as path from "node:path"; +import { getWolfDir, ensureWolfDir, readJSON, writeJSON, readMarkdown, parseAnatomy, estimateTokens, readStdin, normalizePath } from "./shared.js"; +async function main() { + ensureWolfDir(); + const wolfDir = getWolfDir(); + const hooksDir = path.join(wolfDir, "hooks"); + const sessionFile = path.join(hooksDir, "_session.json"); + const raw = await readStdin(); + let input; + try { + input = JSON.parse(raw); + } + catch { + process.exit(0); + return; + } + const filePath = input.tool_input?.file_path ?? input.tool_input?.path ?? ""; + const content = input.tool_output?.content ?? ""; + if (!filePath) { + process.exit(0); + return; + } + const normalizedFile = normalizePath(filePath); + // Skip tracking for .wolf/ internal files — consistent with pre-read + const projectDir = normalizePath(process.env.CLAUDE_PROJECT_DIR || process.cwd()); + const relToProject = normalizedFile.startsWith(projectDir) + ? normalizedFile.slice(projectDir.length).replace(/^\//, "") + : ""; + if (relToProject.startsWith(".wolf/") || relToProject.startsWith(".wolf\\")) { + process.exit(0); + return; + } + const ext = path.extname(filePath).toLowerCase(); + const codeExts = new Set([".ts", ".js", ".tsx", ".jsx", ".py", ".rs", ".go", ".java", ".c", ".cpp", ".css", ".json", ".yaml", ".yml"]); + const proseExts = new Set([".md", ".txt", ".rst"]); + const type = codeExts.has(ext) ? "code" : proseExts.has(ext) ? "prose" : "mixed"; + let tokens = content ? estimateTokens(content, type) : 0; + // Fallback: if tool_output had no content, use anatomy token estimate + if (tokens === 0) { + const anatomyContent = readMarkdown(path.join(wolfDir, "anatomy.md")); + const sections = parseAnatomy(anatomyContent); + for (const [sectionKey, entries] of sections) { + for (const entry of entries) { + const entryRelPath = normalizePath(path.join(sectionKey, entry.file)); + if (normalizedFile.endsWith(entryRelPath) || normalizedFile.endsWith("/" + entryRelPath)) { + tokens = entry.tokens; + break; + } + } + if (tokens > 0) + break; + } + } + const session = readJSON(sessionFile, { files_read: {} }); + if (session.files_read[normalizedFile]) { + session.files_read[normalizedFile].tokens = tokens; + } + else { + session.files_read[normalizedFile] = { + count: 1, + tokens, + first_read: new Date().toISOString(), + }; + } + writeJSON(sessionFile, session); + process.exit(0); +} +main().catch(() => process.exit(0)); +//# sourceMappingURL=post-read.js.map \ No newline at end of file diff --git a/.wolf/hooks/post-write.js b/.wolf/hooks/post-write.js new file mode 100644 index 0000000..9a5ce97 --- /dev/null +++ b/.wolf/hooks/post-write.js @@ -0,0 +1,503 @@ +import * as fs from "node:fs"; +import * as path from "node:path"; +import * as crypto from "node:crypto"; +import { getWolfDir, ensureWolfDir, readJSON, writeJSON, parseAnatomy, serializeAnatomy, extractDescription, estimateTokens, appendMarkdown, timeShort, readStdin, normalizePath } from "./shared.js"; +async function main() { + ensureWolfDir(); + const wolfDir = getWolfDir(); + const hooksDir = path.join(wolfDir, "hooks"); + const sessionFile = path.join(hooksDir, "_session.json"); + const projectRoot = process.env.CLAUDE_PROJECT_DIR || process.cwd(); + const raw = await readStdin(); + let input; + try { + input = JSON.parse(raw); + } + catch { + process.exit(0); + return; + } + const toolName = input.tool_name ?? "Write"; + const filePath = input.tool_input?.file_path ?? input.tool_input?.path ?? ""; + if (!filePath) { + process.exit(0); + return; + } + const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(projectRoot, filePath); + // Skip processing for .wolf/ internal files to avoid slow self-referential updates + const relPath = normalizePath(path.relative(projectRoot, absolutePath)); + if (relPath.startsWith(".wolf/")) { + process.exit(0); + return; + } + // Never track .env files in anatomy — they contain secrets + const baseName = path.basename(absolutePath); + if (baseName === ".env" || baseName.startsWith(".env.")) { + process.exit(0); + return; + } + const oldStr = input.tool_input?.old_string ?? ""; + const newStr = input.tool_input?.new_string ?? ""; + // 1. Update anatomy.md + try { + const anatomyPath = path.join(wolfDir, "anatomy.md"); + let anatomyContent; + try { + anatomyContent = fs.readFileSync(anatomyPath, "utf-8"); + } + catch { + anatomyContent = "# anatomy.md\n\n> Auto-maintained by OpenWolf.\n"; + } + const sections = parseAnatomy(anatomyContent); + const relPathLocal = normalizePath(path.relative(projectRoot, absolutePath)); + const dir = path.dirname(relPathLocal); + const fileName = path.basename(relPathLocal); + const sectionKey = dir === "." ? "./" : dir + "/"; + let fileContent = ""; + try { + fileContent = fs.readFileSync(absolutePath, "utf-8"); + } + catch { + fileContent = input.tool_input?.content ?? ""; + } + const desc = extractDescription(absolutePath).slice(0, 100); + const ext = path.extname(absolutePath).toLowerCase(); + const codeExts = new Set([".ts", ".js", ".tsx", ".jsx", ".py", ".json", ".yaml", ".yml", ".css"]); + const proseExts = new Set([".md", ".txt", ".rst"]); + const type = codeExts.has(ext) ? "code" : proseExts.has(ext) ? "prose" : "mixed"; + const tokens = estimateTokens(fileContent, type); + if (!sections.has(sectionKey)) + sections.set(sectionKey, []); + const entries = sections.get(sectionKey); + const idx = entries.findIndex((e) => e.file === fileName); + if (idx !== -1) { + entries[idx] = { file: fileName, description: desc, tokens }; + } + else { + entries.push({ file: fileName, description: desc, tokens }); + } + let fileCount = 0; + for (const [, list] of sections) + fileCount += list.length; + const serialized = serializeAnatomy(sections, { + lastScanned: new Date().toISOString(), + fileCount, + hits: 0, + misses: 0, + }); + const tmp = anatomyPath + "." + crypto.randomBytes(4).toString("hex") + ".tmp"; + try { + fs.writeFileSync(tmp, serialized, "utf-8"); + fs.renameSync(tmp, anatomyPath); + } + catch { + try { + fs.writeFileSync(anatomyPath, serialized, "utf-8"); + } + catch { } + try { + fs.unlinkSync(tmp); + } + catch { } + } + } + catch { } + // 2. Append richer entry to memory.md + try { + const action = toolName === "Write" ? "Created" : toolName === "MultiEdit" ? "Multi-edited" : "Edited"; + const relFile = normalizePath(path.relative(projectRoot, absolutePath)); + const fileContent = input.tool_input?.content ?? ""; + const ext = path.extname(absolutePath).toLowerCase(); + const codeExts = new Set([".ts", ".js", ".tsx", ".jsx", ".py", ".json", ".yaml", ".yml", ".css"]); + const type = codeExts.has(ext) ? "code" : "mixed"; + const writeTokens = estimateTokens(fileContent || newStr, type); + let changeDesc = ""; + if (oldStr && newStr) { + changeDesc = summarizeEdit(oldStr, newStr, baseName); + } + const memoryPath = path.join(wolfDir, "memory.md"); + const outcome = changeDesc || "—"; + appendMarkdown(memoryPath, `| ${timeShort()} | ${action} ${relFile} | ${outcome} | ~${writeTokens} |\n`); + } + catch { } + // 3. Record in session tracker + track edit counts + try { + const session = readJSON(sessionFile, { files_written: [], edit_counts: {} }); + if (!session.edit_counts) + session.edit_counts = {}; + const normalizedFile = normalizePath(filePath); + const action = toolName === "Write" ? "create" : "edit"; + const fileContent = input.tool_input?.content ?? ""; + const tokens = estimateTokens(fileContent || newStr, "code"); + session.files_written.push({ + file: normalizedFile, + action, + tokens, + at: new Date().toISOString(), + }); + const editKey = normalizePath(path.relative(projectRoot, absolutePath)); + session.edit_counts[editKey] = (session.edit_counts[editKey] || 0) + 1; + writeJSON(sessionFile, session); + if (session.edit_counts[editKey] >= 3) { + process.stderr.write(`⚠️ OpenWolf: ${baseName} has been edited ${session.edit_counts[editKey]} times this session. If you're fixing a bug, remember to log it to .wolf/buglog.json.\n`); + } + } + catch { } + // 4. Auto-detect bug-fix patterns and log them + try { + if (oldStr && newStr) { + autoDetectBugFix(wolfDir, absolutePath, projectRoot, oldStr, newStr); + } + } + catch { } + process.exit(0); +} +// ─── Edit Summarizer ───────────────────────────────────────────── +function summarizeEdit(oldStr, newStr, filename) { + const oldLines = oldStr.split("\n"); + const newLines = newStr.split("\n"); + const oldCount = oldLines.length; + const newCount = newLines.length; + const ext = path.extname(filename).toLowerCase(); + // --- Structural fixes --- + if (newStr.includes("try") && newStr.includes("catch") && !oldStr.includes("catch")) { + return "added error handling"; + } + if (newStr.includes("?.") && !oldStr.includes("?.")) + return "added optional chaining"; + if (newStr.includes("?? ") && !oldStr.includes("?? ")) + return "added nullish coalescing"; + // --- Deleted code --- + if (!newStr.trim() || newStr.trim().length < oldStr.trim().length * 0.2) { + return `removed ${oldCount} lines`; + } + // --- Import changes --- + const oldImports = oldLines.filter(l => /^\s*(import|require|use |from )/.test(l)).length; + const newImports = newLines.filter(l => /^\s*(import|require|use |from )/.test(l)).length; + if (newImports > oldImports && Math.abs(newCount - oldCount) <= newImports - oldImports + 1) { + return `added ${newImports - oldImports} import(s)`; + } + // --- Value/string replacement (common bug fix: wrong value) --- + if (oldCount === 1 && newCount === 1) { + const o = oldStr.trim(); + const n = newStr.trim(); + // String literal change + const oStr = o.match(/['"`]([^'"`]+)['"`]/); + const nStr = n.match(/['"`]([^'"`]+)['"`]/); + if (oStr && nStr && oStr[1] !== nStr[1]) { + return `"${oStr[1].slice(0, 25)}" → "${nStr[1].slice(0, 25)}"`; + } + // Number change + const oNum = o.match(/\b(\d+\.?\d*)\b/); + const nNum = n.match(/\b(\d+\.?\d*)\b/); + if (oNum && nNum && oNum[1] !== nNum[1] && o.replace(oNum[1], "") === n.replace(nNum[1], "")) { + return `${oNum[1]} → ${nNum[1]}`; + } + return "inline fix"; + } + // --- Method/function call changes --- + const oldCalls = extractCalls(oldStr); + const newCalls = extractCalls(newStr); + const addedCalls = newCalls.filter(c => !oldCalls.includes(c)); + const removedCalls = oldCalls.filter(c => !newCalls.includes(c)); + if (removedCalls.length === 1 && addedCalls.length === 1) { + return `${removedCalls[0]}() → ${addedCalls[0]}()`; + } + // --- CSS/style changes --- + if (ext === ".css" || ext === ".scss" || ext === ".vue" || ext === ".tsx" || ext === ".jsx") { + const oldProps = (oldStr.match(/[\w-]+\s*:/g) || []).map(p => p.replace(/\s*:/, "")); + const newProps = (newStr.match(/[\w-]+\s*:/g) || []).map(p => p.replace(/\s*:/, "")); + const changed = newProps.filter(p => !oldProps.includes(p)); + if (changed.length > 0 && changed.length <= 3) { + return `CSS: ${changed.join(", ")}`; + } + } + // --- Condition changes --- + const oldConds = (oldStr.match(/if\s*\(([^)]+)\)/g) || []); + const newConds = (newStr.match(/if\s*\(([^)]+)\)/g) || []); + if (newConds.length > oldConds.length) { + return `added ${newConds.length - oldConds.length} condition(s)`; + } + // --- Function modified --- + const fnMatch = newStr.match(/(?:function|def|fn|func|async\s+function)\s+(\w+)/); + if (fnMatch) { + return `modified ${fnMatch[1]}()`; + } + // --- Class/method context --- + const methodMatch = newStr.match(/(?:public|private|protected)?\s*(?:async\s+)?(\w+)\s*\([^)]*\)\s*[:{]/); + if (methodMatch) { + return `modified ${methodMatch[1]}()`; + } + // --- Size-based fallback --- + if (newCount > oldCount + 5) + return `expanded (+${newCount - oldCount} lines)`; + if (oldCount > newCount + 5) + return `reduced (-${oldCount - newCount} lines)`; + return `${oldCount}→${newCount} lines`; +} +function extractCalls(code) { + return [...new Set((code.match(/(\w+)\s*\(/g) || []) + .map(m => m.match(/(\w+)/)?.[1] || "") + .filter(n => n.length > 2 && !["if", "for", "while", "switch", "catch", "function", "return", "new", "typeof", "instanceof", "const", "let", "var"].includes(n)))]; +} +// ─── Auto Bug Detection ────────────────────────────────────────── +function autoDetectBugFix(wolfDir, absolutePath, projectRoot, oldStr, newStr) { + const bugLogPath = path.join(wolfDir, "buglog.json"); + const bugLog = readJSON(bugLogPath, { version: 1, bugs: [] }); + const relFile = normalizePath(path.relative(projectRoot, absolutePath)); + const basename = path.basename(absolutePath); + const ext = path.extname(basename).toLowerCase(); + // Detect what kind of fix this is + const detection = detectFixPattern(oldStr, newStr, ext); + if (!detection) + return; + // Check for recent duplicate (same file + same category within 5 min) + const recentDupe = bugLog.bugs.find(b => { + if (path.basename(b.file) !== basename) + return false; + if (!b.tags.includes("auto-detected")) + return false; + if (!b.tags.includes(detection.category)) + return false; + const bugTime = new Date(b.last_seen).getTime(); + return (Date.now() - bugTime) < 5 * 60 * 1000; + }); + if (recentDupe) { + recentDupe.occurrences++; + recentDupe.last_seen = new Date().toISOString(); + // Append additional context + if (detection.context && !recentDupe.fix.includes(detection.context)) { + recentDupe.fix += ` | Also: ${detection.context}`; + } + writeJSON(bugLogPath, bugLog); + return; + } + const nextId = `bug-${String(bugLog.bugs.length + 1).padStart(3, "0")}`; + bugLog.bugs.push({ + id: nextId, + timestamp: new Date().toISOString(), + error_message: detection.summary, + file: relFile, + root_cause: detection.rootCause, + fix: detection.fix, + tags: ["auto-detected", detection.category, ext.replace(".", "") || "unknown"], + related_bugs: [], + occurrences: 1, + last_seen: new Date().toISOString(), + }); + writeJSON(bugLogPath, bugLog); +} +function detectFixPattern(oldStr, newStr, ext) { + const oldLines = oldStr.split("\n"); + const newLines = newStr.split("\n"); + // --- Error handling added --- + if (newStr.includes("catch") && !oldStr.includes("catch")) { + const fn = newStr.match(/(?:function|def|async)\s+(\w+)/)?.[1] || "unknown"; + return { + category: "error-handling", + summary: `Missing error handling in ${path.basename(fn)}`, + rootCause: "Code path had no error handling — exceptions would propagate uncaught", + fix: `Added try/catch block`, + context: extractChangedLines(oldStr, newStr), + }; + } + // --- Null/undefined safety --- + if ((newStr.includes("?.") && !oldStr.includes("?.")) || + (newStr.includes("?? ") && !oldStr.includes("?? ")) || + (/!==?\s*(null|undefined)/.test(newStr) && !/!==?\s*(null|undefined)/.test(oldStr))) { + return { + category: "null-safety", + summary: `Null/undefined access in ${path.basename(path.basename(""))}`, + rootCause: "Property access on potentially null/undefined value", + fix: `Added null safety (optional chaining or null check)`, + context: extractChangedLines(oldStr, newStr), + }; + } + // --- Guard clause / early return added --- + if (/if\s*\([^)]*\)\s*(return|throw|continue|break)/.test(newStr) && + !/if\s*\([^)]*\)\s*(return|throw|continue|break)/.test(oldStr)) { + const condition = newStr.match(/if\s*\(([^)]+)\)/)?.[1]?.trim().slice(0, 60) || "condition"; + return { + category: "guard-clause", + summary: `Missing guard clause`, + rootCause: `No early return/throw for edge case: ${condition}`, + fix: `Added guard clause: if (${condition.slice(0, 40)})`, + }; + } + // --- Wrong value / string fix (very common bug) --- + if (oldLines.length <= 3 && newLines.length <= 3) { + const oldJoined = oldStr.trim(); + const newJoined = newStr.trim(); + // String literal changed + const oStrs = oldJoined.match(/['"`]([^'"`]{2,})['"`]/g) || []; + const nStrs = newJoined.match(/['"`]([^'"`]{2,})['"`]/g) || []; + if (oStrs.length > 0 && nStrs.length > 0) { + for (let i = 0; i < Math.min(oStrs.length, nStrs.length); i++) { + if (oStrs[i] !== nStrs[i]) { + return { + category: "wrong-value", + summary: `Incorrect value in code`, + rootCause: `Had ${oStrs[i].slice(0, 50)}`, + fix: `Changed to ${nStrs[i].slice(0, 50)}`, + }; + } + } + } + // Variable name / method call changed + const oldTokens = tokenizeCode(oldJoined); + const newTokens = tokenizeCode(newJoined); + const changed = []; + for (let i = 0; i < Math.min(oldTokens.length, newTokens.length); i++) { + if (oldTokens[i] !== newTokens[i]) { + changed.push([oldTokens[i], newTokens[i]]); + } + } + if (changed.length === 1 && changed[0][0].length > 2) { + return { + category: "wrong-reference", + summary: `Wrong reference: ${changed[0][0]} should be ${changed[0][1]}`, + rootCause: `Used "${changed[0][0]}" instead of "${changed[0][1]}"`, + fix: `Changed ${changed[0][0]} → ${changed[0][1]}`, + }; + } + } + // --- Logic fix (condition changed) --- + const oldCond = oldStr.match(/if\s*\(([^)]+)\)/)?.[1]; + const newCond = newStr.match(/if\s*\(([^)]+)\)/)?.[1]; + if (oldCond && newCond && oldCond !== newCond && oldLines.length <= 5) { + return { + category: "logic-fix", + summary: `Wrong condition in logic`, + rootCause: `Condition was: if (${oldCond.slice(0, 50)})`, + fix: `Changed to: if (${newCond.slice(0, 50)})`, + }; + } + // --- Operator fix (=== vs ==, > vs >=, etc.) --- + const opChange = findOperatorChange(oldStr, newStr); + if (opChange) { + return { + category: "operator-fix", + summary: `Wrong operator: ${opChange.old} should be ${opChange.new}`, + rootCause: `Used "${opChange.old}" instead of "${opChange.new}"`, + fix: `Changed operator ${opChange.old} → ${opChange.new}`, + }; + } + // --- Missing import/require --- + const oldImports = new Set((oldStr.match(/(?:import|require)\s*\(?['"]([^'"]+)['"]\)?/g) || []).map(m => m)); + const newImports = (newStr.match(/(?:import|require)\s*\(?['"]([^'"]+)['"]\)?/g) || []); + const addedImports = newImports.filter(i => !oldImports.has(i)); + if (addedImports.length > 0 && newLines.length - oldLines.length <= addedImports.length + 2) { + const modules = addedImports.map(i => i.match(/['"]([^'"]+)['"]/)?.[1] || "").filter(Boolean); + return { + category: "missing-import", + summary: `Missing import: ${modules.join(", ")}`, + rootCause: `Module(s) not imported: ${modules.join(", ")}`, + fix: `Added import(s) for ${modules.join(", ")}`, + }; + } + // --- Return value fix --- + const oldReturn = oldStr.match(/return\s+(.+)/)?.[1]?.trim(); + const newReturn = newStr.match(/return\s+(.+)/)?.[1]?.trim(); + if (oldReturn && newReturn && oldReturn !== newReturn && oldLines.length <= 5) { + return { + category: "return-value", + summary: `Wrong return value`, + rootCause: `Was returning: ${oldReturn.slice(0, 50)}`, + fix: `Now returns: ${newReturn.slice(0, 50)}`, + }; + } + // --- Async/await fix --- + if (newStr.includes("await ") && !oldStr.includes("await ")) { + return { + category: "async-fix", + summary: `Missing await`, + rootCause: `Async call without await — returned Promise instead of value`, + fix: `Added await to async call`, + context: extractChangedLines(oldStr, newStr), + }; + } + if (newStr.includes("async ") && !oldStr.includes("async ")) { + return { + category: "async-fix", + summary: `Function not marked async`, + rootCause: `Function uses await but wasn't declared async`, + fix: `Added async modifier`, + }; + } + // --- Type annotation/cast fix --- + if (ext === ".ts" || ext === ".tsx") { + if ((newStr.includes(" as ") && !oldStr.includes(" as ")) || + (newStr.includes(": ") && !oldStr.includes(": ") && oldLines.length <= 3)) { + return { + category: "type-fix", + summary: `Type error`, + rootCause: `Missing or incorrect type annotation`, + fix: `Added type assertion/annotation`, + context: extractChangedLines(oldStr, newStr), + }; + } + } + // --- CSS/style fix --- + if (ext === ".css" || ext === ".scss" || ext === ".vue" || ext === ".tsx" || ext === ".jsx") { + const oldProps = extractCSSProps(oldStr); + const newProps = extractCSSProps(newStr); + const changedProps = [...newProps.entries()].filter(([k, v]) => oldProps.get(k) !== v && oldProps.has(k)); + if (changedProps.length > 0 && changedProps.length <= 3) { + const desc = changedProps.map(([k, v]) => `${k}: ${oldProps.get(k)} → ${v}`).join("; "); + return { + category: "style-fix", + summary: `CSS fix: ${changedProps.map(([k]) => k).join(", ")}`, + rootCause: desc, + fix: `Changed ${desc}`, + }; + } + } + // --- Significant diff (catch-all for substantial edits) --- + const diffRatio = Math.abs(newStr.length - oldStr.length) / Math.max(oldStr.length, 1); + if (diffRatio > 0.3 && oldLines.length >= 3 && newLines.length >= 3) { + // Only log if there's meaningful structural change, not just additions + const removedLines = oldLines.filter(l => l.trim() && !newLines.some(nl => nl.trim() === l.trim())); + if (removedLines.length >= 2) { + return { + category: "refactor", + summary: `Significant refactor of ${path.basename("")}`, + rootCause: `${removedLines.length} lines replaced/restructured`, + fix: `Rewrote ${oldLines.length}→${newLines.length} lines (${removedLines.length} removed)`, + context: removedLines.slice(0, 2).map(l => l.trim().slice(0, 50)).join("; "), + }; + } + } + return null; +} +function extractChangedLines(oldStr, newStr) { + const oldLines = new Set(oldStr.split("\n").map(l => l.trim()).filter(Boolean)); + const newLines = newStr.split("\n").map(l => l.trim()).filter(Boolean); + const added = newLines.filter(l => !oldLines.has(l)); + return added.slice(0, 2).map(l => l.slice(0, 60)).join("; "); +} +function tokenizeCode(code) { + return code.replace(/[^\w$]/g, " ").split(/\s+/).filter(t => t.length > 0); +} +function findOperatorChange(oldStr, newStr) { + const operators = ["===", "!==", "==", "!=", ">=", "<=", ">>", "<<", "&&", "||", "??"]; + for (const op of operators) { + if (oldStr.includes(op) && !newStr.includes(op)) { + for (const op2 of operators) { + if (op2 !== op && newStr.includes(op2) && !oldStr.includes(op2)) { + return { old: op, new: op2 }; + } + } + } + } + return null; +} +function extractCSSProps(code) { + const props = new Map(); + const matches = code.matchAll(/([\w-]+)\s*:\s*([^;}\n]+)/g); + for (const m of matches) { + props.set(m[1].trim(), m[2].trim()); + } + return props; +} +main().catch(() => process.exit(0)); +//# sourceMappingURL=post-write.js.map \ No newline at end of file diff --git a/.wolf/hooks/pre-read.js b/.wolf/hooks/pre-read.js new file mode 100644 index 0000000..6ba67d0 --- /dev/null +++ b/.wolf/hooks/pre-read.js @@ -0,0 +1,80 @@ +import * as path from "node:path"; +import { getWolfDir, ensureWolfDir, readJSON, writeJSON, readMarkdown, parseAnatomy, readStdin, normalizePath } from "./shared.js"; +async function main() { + ensureWolfDir(); + const wolfDir = getWolfDir(); + const hooksDir = path.join(wolfDir, "hooks"); + const sessionFile = path.join(hooksDir, "_session.json"); + const raw = await readStdin(); + let input; + try { + input = JSON.parse(raw); + } + catch { + process.exit(0); + return; + } + const filePath = input.tool_input?.file_path ?? input.tool_input?.path ?? ""; + if (!filePath) { + process.exit(0); + return; + } + const normalizedFile = normalizePath(filePath); + // Skip tracking for .wolf/ internal files — they're infrastructure, not project files. + // Counting them inflates anatomy miss rates since .wolf/ is excluded from anatomy scanning. + const projectDir = normalizePath(process.env.CLAUDE_PROJECT_DIR || process.cwd()); + const relToProject = normalizedFile.startsWith(projectDir) + ? normalizedFile.slice(projectDir.length).replace(/^\//, "") + : ""; + if (relToProject.startsWith(".wolf/") || relToProject.startsWith(".wolf\\")) { + process.exit(0); + return; + } + const session = readJSON(sessionFile, { + session_id: "", files_read: {}, anatomy_hits: 0, anatomy_misses: 0, + repeated_reads_warned: 0, + }); + // Check if already read this session + if (session.files_read[normalizedFile]) { + const prev = session.files_read[normalizedFile]; + process.stderr.write(`⚡ OpenWolf: ${path.basename(normalizedFile)} was already read this session (~${prev.tokens} tokens). Consider using your existing knowledge of this file.\n`); + session.files_read[normalizedFile].count++; + session.repeated_reads_warned++; + writeJSON(sessionFile, session); + process.exit(0); + return; + } + // Check anatomy.md for this file + const anatomyContent = readMarkdown(path.join(wolfDir, "anatomy.md")); + const sections = parseAnatomy(anatomyContent); + let found = false; + for (const [sectionKey, entries] of sections) { + for (const entry of entries) { + // Build the full relative path from the section key + filename for accurate matching + const entryRelPath = normalizePath(path.join(sectionKey, entry.file)); + if (normalizedFile.endsWith(entryRelPath) || normalizedFile.endsWith("/" + entryRelPath)) { + process.stderr.write(`📋 OpenWolf anatomy: ${entry.file} — ${entry.description} (~${entry.tokens} tok)\n`); + found = true; + break; + } + } + if (found) + break; + } + if (found) { + session.anatomy_hits++; + } + else { + session.anatomy_misses++; + } + // Record initial read entry (tokens will be updated in post-read) + session.files_read[normalizedFile] = { + count: 1, + tokens: 0, + first_read: new Date().toISOString(), + }; + writeJSON(sessionFile, session); + process.exit(0); +} +main().catch(() => process.exit(0)); +//# sourceMappingURL=pre-read.js.map \ No newline at end of file diff --git a/.wolf/hooks/pre-write.js b/.wolf/hooks/pre-write.js new file mode 100644 index 0000000..2e84303 --- /dev/null +++ b/.wolf/hooks/pre-write.js @@ -0,0 +1,121 @@ +import * as fs from "node:fs"; +import * as path from "node:path"; +import { getWolfDir, ensureWolfDir, readJSON, readMarkdown, readStdin } from "./shared.js"; +async function main() { + ensureWolfDir(); + const wolfDir = getWolfDir(); + const raw = await readStdin(); + let input; + try { + input = JSON.parse(raw); + } + catch { + process.exit(0); + return; + } + // For Edit tool, the meaningful content is old_string + new_string + const content = input.tool_input?.content ?? ""; + const oldStr = input.tool_input?.old_string ?? ""; + const newStr = input.tool_input?.new_string ?? ""; + const filePath = input.tool_input?.file_path ?? input.tool_input?.path ?? ""; + const allContent = [content, oldStr, newStr].join("\n"); + if (!allContent.trim()) { + process.exit(0); + return; + } + // 1. Cerebrum Do-Not-Repeat check + checkCerebrum(wolfDir, allContent); + // 2. Bug log: search for similar past bugs when editing code + // This fires when Claude is about to edit a file — if the edit looks like a fix + // (changing error handling, modifying catch blocks, etc.), check the bug log + if (filePath && (oldStr || content)) { + checkBugLog(wolfDir, filePath, oldStr, newStr, content); + } + process.exit(0); +} +function checkCerebrum(wolfDir, content) { + const cerebrumContent = readMarkdown(path.join(wolfDir, "cerebrum.md")); + const doNotRepeatSection = cerebrumContent.split("## Do-Not-Repeat")[1]; + if (!doNotRepeatSection) + return; + const entries = doNotRepeatSection.split("## ")[0]; + const lines = entries.split("\n").filter((l) => l.trim().startsWith("[") || l.trim().startsWith("-")); + for (const line of lines) { + const trimmed = line.trim().replace(/^[-*]\s*/, "").replace(/^\[[\d-]+\]\s*/, ""); + if (!trimmed) + continue; + const patterns = []; + const quotedMatches = trimmed.match(/"([^"]+)"/g) || trimmed.match(/'([^']+)'/g) || trimmed.match(/`([^`]+)`/g); + if (quotedMatches) { + for (const qm of quotedMatches) { + patterns.push(qm.replace(/["'`]/g, "")); + } + } + const neverMatch = trimmed.match(/(?:never use|avoid|don't use|do not use)\s+(\w+)/i); + if (neverMatch) + patterns.push(neverMatch[1]); + for (const pattern of patterns) { + try { + const regex = new RegExp(`\\b${pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "i"); + if (regex.test(content)) { + process.stderr.write(`⚠️ OpenWolf cerebrum warning: "${trimmed}" — check your code before proceeding.\n`); + } + } + catch { } + } + } +} +// Common words that appear in most code — must be excluded from similarity matching +const STOP_WORDS = new Set([ + "error", "function", "return", "const", "this", "that", "with", "from", + "import", "export", "class", "interface", "type", "undefined", "null", + "true", "false", "string", "number", "object", "array", "value", + "file", "path", "name", "data", "response", "request", "result", + "should", "must", "does", "have", "been", "will", "would", "could", + "when", "then", "else", "each", "some", "every", "only", +]); +function checkBugLog(wolfDir, filePath, oldStr, newStr, content) { + const bugLogPath = path.join(wolfDir, "buglog.json"); + if (!fs.existsSync(bugLogPath)) + return; + const bugLog = readJSON(bugLogPath, { version: 1, bugs: [] }); + if (bugLog.bugs.length === 0) + return; + const basename = path.basename(filePath); + // ONLY surface bugs that match the SAME file being edited. + // Cross-file matching is too noisy and risks misdirecting Claude. + const fileMatches = bugLog.bugs.filter(b => { + const bugBasename = path.basename(b.file); + return bugBasename === basename; + }); + if (fileMatches.length === 0) + return; + // Further filter: require tag or error_message overlap with the edit content + const editText = (oldStr + " " + newStr + " " + content).toLowerCase(); + const editTokens = tokenize(editText); + const relevant = fileMatches.filter(bug => { + // Check if any bug tag appears in the edit content + const tagHit = bug.tags.some(t => editText.includes(t.toLowerCase())); + if (tagHit) + return true; + // Check meaningful word overlap (excluding stop words) + const bugTokens = tokenize(bug.error_message + " " + bug.root_cause); + const overlap = [...editTokens].filter(t => bugTokens.has(t)); + // Require at least 3 meaningful overlapping words + return overlap.length >= 3; + }); + if (relevant.length === 0) + return; + // Surface as a FYI, not a directive — Claude should evaluate, not blindly apply + process.stderr.write(`📋 OpenWolf buglog: ${relevant.length} past bug(s) found for ${basename} — review for context, do NOT apply blindly:\n`); + for (const bug of relevant.slice(0, 2)) { + process.stderr.write(` [${bug.id}] "${bug.error_message.slice(0, 70)}"\n Cause: ${bug.root_cause.slice(0, 80)}\n Fix: ${bug.fix.slice(0, 80)}\n`); + } +} +function tokenize(text) { + return new Set(text.replace(/[^\w\s]/g, " ").split(/\s+/) + .filter(w => w.length > 3 && !STOP_WORDS.has(w.toLowerCase())) + .map(w => w.toLowerCase())); +} +main().catch(() => process.exit(0)); +//# sourceMappingURL=pre-write.js.map \ No newline at end of file diff --git a/.wolf/hooks/session-start.js b/.wolf/hooks/session-start.js new file mode 100644 index 0000000..7da38f3 --- /dev/null +++ b/.wolf/hooks/session-start.js @@ -0,0 +1,77 @@ +import * as fs from "node:fs"; +import * as path from "node:path"; +import { getWolfDir, ensureWolfDir, writeJSON, appendMarkdown, readJSON, timestamp, timeShort } from "./shared.js"; +async function main() { + ensureWolfDir(); + const wolfDir = getWolfDir(); + // Clean up stale .tmp files left from failed atomic writes + try { + const files = fs.readdirSync(wolfDir); + for (const f of files) { + if (f.endsWith(".tmp")) { + try { + fs.unlinkSync(path.join(wolfDir, f)); + } + catch { } + } + } + } + catch { } + const hooksDir = path.join(wolfDir, "hooks"); + const sessionFile = path.join(hooksDir, "_session.json"); + const now = new Date(); + const sessionId = `session-${now.toISOString().slice(0, 10)}-${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}`; + // Create fresh session state + writeJSON(sessionFile, { + session_id: sessionId, + started: timestamp(), + files_read: {}, + files_written: [], + edit_counts: {}, + anatomy_hits: 0, + anatomy_misses: 0, + repeated_reads_warned: 0, + cerebrum_warnings: 0, + stop_count: 0, + }); + // Append session header to memory.md + const memoryPath = path.join(wolfDir, "memory.md"); + const header = `\n## Session: ${now.toISOString().slice(0, 10)} ${timeShort()}\n\n| Time | Action | File(s) | Outcome | ~Tokens |\n|------|--------|---------|---------|--------|\n`; + appendMarkdown(memoryPath, header); + // Check cerebrum freshness — remind Claude to learn + try { + const cerebrumPath = path.join(wolfDir, "cerebrum.md"); + const cerebrumContent = fs.readFileSync(cerebrumPath, "utf-8"); + const stat = fs.statSync(cerebrumPath); + const daysSinceUpdate = (Date.now() - stat.mtimeMs) / (1000 * 60 * 60 * 24); + // Count actual entries (non-comment, non-empty lines in content sections) + const entryLines = cerebrumContent.split("\n").filter(l => { + const t = l.trim(); + return t.startsWith("- ") || t.startsWith("* ") || (t.startsWith("[") && t.includes("]")); + }); + if (entryLines.length < 3) { + process.stderr.write(`💡 OpenWolf: cerebrum.md has only ${entryLines.length} entries. Learn from this session — record user preferences, project conventions, and mistakes to .wolf/cerebrum.md.\n`); + } + else if (daysSinceUpdate > 3) { + process.stderr.write(`💡 OpenWolf: cerebrum.md hasn't been updated in ${Math.floor(daysSinceUpdate)} days. Look for opportunities to add learnings this session.\n`); + } + } + catch { } + // Check buglog — remind if empty + try { + const buglogPath = path.join(wolfDir, "buglog.json"); + const buglog = readJSON(buglogPath, { bugs: [] }); + if (buglog.bugs.length === 0) { + process.stderr.write(`📋 OpenWolf: buglog.json is empty. If you encounter or fix any bugs, errors, or failed tests this session, log them to .wolf/buglog.json.\n`); + } + } + catch { } + // Increment total_sessions in token-ledger + const ledgerPath = path.join(wolfDir, "token-ledger.json"); + const ledger = readJSON(ledgerPath, { version: 1, lifetime: { total_sessions: 0 } }); + ledger.lifetime.total_sessions++; + writeJSON(ledgerPath, ledger); + process.exit(0); +} +main().catch(() => process.exit(0)); +//# sourceMappingURL=session-start.js.map \ No newline at end of file diff --git a/.wolf/hooks/shared.js b/.wolf/hooks/shared.js new file mode 100644 index 0000000..e402d75 --- /dev/null +++ b/.wolf/hooks/shared.js @@ -0,0 +1,614 @@ +import * as fs from "node:fs"; +import * as path from "node:path"; +import * as crypto from "node:crypto"; +export function getWolfDir() { + // Prefer CLAUDE_PROJECT_DIR so hooks work even if CWD changes during a session + const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd(); + return path.join(projectDir, ".wolf"); +} +/** + * Bail out silently if .wolf/ directory doesn't exist in the current project. + * Call this at the top of every hook to avoid crashes in non-OpenWolf projects. + */ +export function ensureWolfDir() { + const wolfDir = getWolfDir(); + if (!fs.existsSync(wolfDir)) { + process.exit(0); + } +} +export function readJSON(filePath, fallback) { + try { + return JSON.parse(fs.readFileSync(filePath, "utf-8")); + } + catch { + return fallback; + } +} +export function writeJSON(filePath, data) { + const dir = path.dirname(filePath); + if (!fs.existsSync(dir)) + fs.mkdirSync(dir, { recursive: true }); + const tmp = filePath + "." + crypto.randomBytes(4).toString("hex") + ".tmp"; + try { + fs.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8"); + fs.renameSync(tmp, filePath); + } + catch { + // On Windows, rename can fail if another process holds a handle. + // Fall back to direct write and clean up the tmp file. + try { + fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8"); + } + catch { } + try { + fs.unlinkSync(tmp); + } + catch { } + } +} +export function readMarkdown(filePath) { + try { + return fs.readFileSync(filePath, "utf-8"); + } + catch { + return ""; + } +} +export function appendMarkdown(filePath, line) { + const dir = path.dirname(filePath); + if (!fs.existsSync(dir)) + fs.mkdirSync(dir, { recursive: true }); + fs.appendFileSync(filePath, line, "utf-8"); +} +export function parseAnatomy(content) { + const sections = new Map(); + let currentSection = ""; + for (const line of content.split("\n")) { + const sm = line.match(/^## (.+)/); + if (sm) { + currentSection = sm[1].trim(); + if (!sections.has(currentSection)) + sections.set(currentSection, []); + continue; + } + if (!currentSection) + continue; + const em = line.match(/^- `([^`]+)`(?:\s+—\s+(.+?))?\s*\(~(\d+)\s+tok\)$/); + if (em) { + sections.get(currentSection).push({ + file: em[1], + description: em[2] || "", + tokens: parseInt(em[3], 10), + }); + } + } + return sections; +} +export function serializeAnatomy(sections, metadata) { + const lines = [ + "# anatomy.md", + "", + `> Auto-maintained by OpenWolf. Last scanned: ${metadata.lastScanned}`, + `> Files: ${metadata.fileCount} tracked | Anatomy hits: ${metadata.hits} | Misses: ${metadata.misses}`, + "", + ]; + const keys = [...sections.keys()].sort(); + for (const key of keys) { + lines.push(`## ${key}`); + lines.push(""); + const entries = sections.get(key).sort((a, b) => a.file.localeCompare(b.file)); + for (const e of entries) { + const desc = e.description ? ` — ${e.description}` : ""; + lines.push(`- \`${e.file}\`${desc} (~${e.tokens} tok)`); + } + lines.push(""); + } + return lines.join("\n"); +} +export function extractDescription(filePath) { + const MAX_DESC = 150; + const basename = path.basename(filePath); + const ext = path.extname(basename).toLowerCase(); + const known = { + "package.json": "Node.js package manifest", + "tsconfig.json": "TypeScript configuration", + ".gitignore": "Git ignore rules", + "README.md": "Project documentation", + "composer.json": "PHP package manifest", + "requirements.txt": "Python dependencies", + "schema.sql": "Database schema", + "Dockerfile": "Docker container definition", + "docker-compose.yml": "Docker Compose services", + "Cargo.toml": "Rust package manifest", + "go.mod": "Go module definition", + "Gemfile": "Ruby dependencies", + "pubspec.yaml": "Dart/Flutter package manifest", + }; + if (known[basename]) + return known[basename]; + let content; + try { + const fd = fs.openSync(filePath, "r"); + const buf = Buffer.alloc(12288); // 12KB + const n = fs.readSync(fd, buf, 0, 12288, 0); + fs.closeSync(fd); + content = buf.subarray(0, n).toString("utf-8"); + } + catch { + return ""; + } + if (!content.trim()) + return ""; + const cap = (s) => s.length <= MAX_DESC ? s : s.slice(0, MAX_DESC - 3) + "..."; + // Markdown heading + if (ext === ".md" || ext === ".mdx") { + const m = content.match(/^#{1,2}\s+(.+)$/m); + if (m) + return cap(m[1].trim()); + } + // HTML title + if (ext === ".html" || ext === ".htm") { + const m = content.match(/]*>([^<]+)<\/title>/i); + if (m) + return cap(m[1].trim()); + } + // JSDoc / PHPDoc / Javadoc — first meaningful line + const jm = content.match(/\/\*\*\s*\n?\s*\*?\s*(.+)/); + if (jm) { + const l = jm[1].replace(/\*\/$/, "").trim(); + if (l && !l.startsWith("@") && l.length > 5) + return cap(l); + } + // Python docstring + if (ext === ".py") { + const dm = content.match(/^(?:#[^\n]*\n)*\s*(?:"""(.+?)"""|'''(.+?)''')/s); + if (dm) { + const first = (dm[1] || dm[2]).split("\n")[0].trim(); + if (first && first.length > 3) + return cap(first); + } + } + // Rust doc comments + if (ext === ".rs") { + const lines = content.split("\n"); + for (const line of lines.slice(0, 20)) { + const m = line.match(/^\s*(?:\/\/\/|\/\/!)\s*(.+)/); + if (m && m[1].length > 5) + return cap(m[1].trim()); + } + } + // Go package comment + if (ext === ".go") { + const m = content.match(/\/\/\s*Package\s+\w+\s+(.*)/); + if (m) + return cap(m[1].trim()); + } + // C# XML doc + if (ext === ".cs") { + const m = content.match(/\s*([\s\S]*?)\s*<\/summary>/); + if (m) { + const text = m[1].replace(/\/\/\/\s*/g, "").replace(/\s+/g, " ").trim(); + if (text.length > 5) + return cap(text); + } + } + // Elixir @moduledoc + if (ext === ".ex" || ext === ".exs") { + const m = content.match(/@moduledoc\s+"""\s*\n\s*(.*)/); + if (m) + return cap(m[1].trim()); + } + // Header comment (skip generic ones) + const hdrLines = content.split("\n"); + for (const line of hdrLines.slice(0, 15)) { + const t = line.trim(); + if (!t || t === " 5 && !lower.startsWith("copyright") && !lower.startsWith("license") && !lower.startsWith("@") && !lower.startsWith("strict") && !lower.startsWith("generated") && !lower.startsWith("eslint-") && !lower.startsWith("nolint")) { + return cap(text); + } + } + if (!t.startsWith("//") && !t.startsWith("#") && !t.startsWith("/*") && !t.startsWith("*") && !t.startsWith("--")) + break; + } + // ─── PHP / Laravel ─────────────────────────────────────── + if (ext === ".php") { + if (basename.endsWith(".blade.php")) { + const ext2 = content.match(/@extends\(\s*['"]([^'"]+)['"]\s*\)/); + const sections = (content.match(/@section\(\s*['"](\w+)['"]/g) || []).map(s => s.match(/['"](\w+)['"]/)?.[1]).filter(Boolean); + const parts = []; + if (ext2) + parts.push(`extends ${ext2[1]}`); + if (sections.length) + parts.push(`sections: ${sections.join(", ")}`); + return cap(parts.length ? `Blade: ${parts.join(", ")}` : "Blade template"); + } + const classM = content.match(/class\s+(\w+)(?:\s+extends\s+(\w+))?/); + const className = classM?.[1] || ""; + const parent = classM?.[2] || ""; + const pubMethods = (content.match(/public\s+function\s+(\w+)/g) || []) + .map(m => m.match(/public\s+function\s+(\w+)/)?.[1]) + .filter(n => n && n !== "__construct" && n !== "middleware"); + if (basename.endsWith("Controller.php") || parent === "Controller") { + if (pubMethods.length > 0) { + const display = pubMethods.slice(0, 5).join(", "); + return cap(pubMethods.length > 5 ? `${display} + ${pubMethods.length - 5} more` : display); + } + } + if (parent === "Model" || parent === "Authenticatable") { + const parts = []; + const tbl = content.match(/\$table\s*=\s*['"]([^'"]+)['"]/); + if (tbl) + parts.push(`table: ${tbl[1]}`); + const fill = content.match(/\$fillable\s*=\s*\[([^\]]*)\]/s); + if (fill) { + const c = (fill[1].match(/['"]/g) || []).length / 2; + parts.push(`${Math.floor(c)} fields`); + } + const rels = (content.match(/\$this->(hasMany|hasOne|belongsTo|belongsToMany|morphMany|morphTo)\(/g) || []).length; + if (rels) + parts.push(`${rels} rels`); + return cap(parts.length ? `Model — ${parts.join(", ")}` : `Model: ${className}`); + } + if (basename.match(/^\d{4}_\d{2}_\d{2}/)) { + const create = content.match(/Schema::create\(\s*['"]([^'"]+)['"]/); + if (create) + return `Migration: create ${create[1]} table`; + const alter = content.match(/Schema::table\(\s*['"]([^'"]+)['"]/); + if (alter) + return `Migration: alter ${alter[1]} table`; + return "Database migration"; + } + if (className && pubMethods.length > 0) { + const display = pubMethods.slice(0, 4).join(", "); + return cap(pubMethods.length > 4 ? `${className}: ${display} + ${pubMethods.length - 4} more` : `${className}: ${display}`); + } + } + // ─── TS/JS/React/Next.js ───────────────────────────────── + if (ext === ".ts" || ext === ".tsx" || ext === ".js" || ext === ".jsx" || ext === ".mjs" || ext === ".cjs") { + // React component + if (ext === ".tsx" || ext === ".jsx") { + const comp = content.match(/(?:export\s+(?:default\s+)?)?(?:function|const)\s+(\w+)/); + const parts = []; + if (comp) + parts.push(comp[1]); + const renders = []; + if (/<(?:form|Form)/i.test(content)) + renders.push("form"); + if (/<(?:table|Table|DataTable)/i.test(content)) + renders.push("table"); + if (/<(?:dialog|Dialog|Modal|Drawer)/i.test(content)) + renders.push("modal"); + if (renders.length) + parts.push(`renders ${renders.join(", ")}`); + if (parts.length) + return cap(parts.join(" — ")); + } + // Next.js conventions + if (basename === "page.tsx" || basename === "page.js") + return "Next.js page component"; + if (basename === "layout.tsx" || basename === "layout.js") + return "Next.js layout"; + if (basename === "route.ts" || basename === "route.js") { + const methods = [...new Set((content.match(/export\s+(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE)/g) || []) + .map(m => m.match(/(GET|POST|PUT|PATCH|DELETE)/)?.[1]))].filter(Boolean); + return methods.length ? `Next.js API route: ${methods.join(", ")}` : "Next.js API route"; + } + // Express/Fastify routes + const routeHits = content.match(/\.(get|post|put|patch|delete)\s*\(\s*['"`]/g); + if (routeHits && routeHits.length > 0) { + const methods = [...new Set(routeHits.map(r => r.match(/\.(get|post|put|patch|delete)/)?.[1]?.toUpperCase()))]; + return cap(`API routes: ${methods.join(", ")} (${routeHits.length} endpoints)`); + } + // tRPC router + if (content.includes("createTRPCRouter") || content.includes("publicProcedure")) { + const procs = (content.match(/\.(query|mutation|subscription)\s*\(/g) || []).length; + return procs ? `tRPC router: ${procs} procedures` : "tRPC router"; + } + // Zod schemas + if (content.includes("z.object") || content.includes("z.string")) { + const schemas = (content.match(/(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*z\./g) || []) + .map(s => s.match(/(?:const|let)\s+(\w+)/)?.[1]).filter(Boolean); + if (schemas.length) + return cap(`Zod schemas: ${schemas.slice(0, 4).join(", ")}${schemas.length > 4 ? ` + ${schemas.length - 4} more` : ""}`); + } + // Exports summary + const exports = (content.match(/export\s+(?:async\s+)?(?:function|class|const|interface|type|enum)\s+(\w+)/g) || []) + .map(e => e.match(/(\w+)$/)?.[1]).filter(Boolean); + if (exports.length > 0 && exports.length <= 5) + return `Exports ${exports.join(", ")}`; + if (exports.length > 5) + return cap(`Exports ${exports.slice(0, 4).join(", ")} + ${exports.length - 4} more`); + } + // ─── Python / Django / FastAPI / Flask ──────────────────── + if (ext === ".py") { + // Django model + if (content.includes("models.Model")) { + const cls = content.match(/class\s+(\w+)\(.*models\.Model\)/); + const fields = (content.match(/^\s+\w+\s*=\s*models\.\w+/gm) || []).length; + return cap(`Model: ${cls?.[1] || "unknown"}, ${fields} fields`); + } + // FastAPI/Flask routes + if (content.includes("@router.") || content.includes("@app.")) { + const routes = (content.match(/@(?:router|app)\.(get|post|put|patch|delete)\s*\(/g) || []); + return cap(routes.length ? `API: ${routes.length} endpoints` : "API router"); + } + // Pydantic + if (content.includes("BaseModel") && content.includes("Field(")) { + const cls = content.match(/class\s+(\w+)\(.*BaseModel\)/); + return cls ? `Pydantic: ${cls[1]}` : "Pydantic model"; + } + // Celery + if (content.includes("@shared_task") || content.includes("@app.task")) { + const tasks = (content.match(/def\s+(\w+)/g) || []).map(m => m.match(/def\s+(\w+)/)?.[1]).filter(n => n && !n.startsWith("_")); + return cap(tasks.length ? `Celery tasks: ${tasks.join(", ")}` : "Celery task"); + } + // Generic + const pyClass = content.match(/class\s+(\w+)/); + const funcs = (content.match(/def\s+(\w+)/g) || []).map(f => f.match(/def\s+(\w+)/)?.[1]).filter(n => n && !n.startsWith("_")); + if (pyClass && funcs.length > 0) + return cap(funcs.length > 4 ? `${pyClass[1]}: ${funcs.slice(0, 4).join(", ")} + ${funcs.length - 4} more` : `${pyClass[1]}: ${funcs.join(", ")}`); + if (funcs.length > 0) + return cap(funcs.slice(0, 4).join(", ")); + } + // ─── Go ────────────────────────────────────────────────── + if (ext === ".go") { + const handlers = (content.match(/func\s+(\w+)\s*\(\s*\w+\s+http\.ResponseWriter/g) || []) + .map(m => m.match(/func\s+(\w+)/)?.[1]).filter(Boolean); + if (handlers.length) + return cap(`HTTP handlers: ${handlers.slice(0, 5).join(", ")}`); + const iface = content.match(/type\s+(\w+)\s+interface\s*\{/); + if (iface) + return `Interface: ${iface[1]}`; + const structM = content.match(/type\s+(\w+)\s+struct\s*\{/); + if (structM) + return `Struct: ${structM[1]}`; + const funcs = (content.match(/^func\s+(\w+)/gm) || []).map(m => m.match(/func\s+(\w+)/)?.[1]).filter(n => n && n[0] === n[0].toUpperCase()); + if (funcs.length) + return cap(funcs.slice(0, 5).join(", ")); + } + // ─── Rust ──────────────────────────────────────────────── + if (ext === ".rs") { + const structM = content.match(/pub\s+struct\s+(\w+)/); + if (structM) { + const methods = (content.match(/pub\s+(?:async\s+)?fn\s+(\w+)/g) || []).map(m => m.match(/fn\s+(\w+)/)?.[1]).filter(Boolean); + return cap(methods.length ? `${structM[1]}: ${methods.slice(0, 4).join(", ")}` : `Struct: ${structM[1]}`); + } + const traitM = content.match(/pub\s+trait\s+(\w+)/); + if (traitM) + return `Trait: ${traitM[1]}`; + const enumM = content.match(/pub\s+enum\s+(\w+)/); + if (enumM) + return `Enum: ${enumM[1]}`; + const fns = (content.match(/pub\s+(?:async\s+)?fn\s+(\w+)/g) || []).map(m => m.match(/fn\s+(\w+)/)?.[1]).filter(Boolean); + if (fns.length) + return cap(fns.slice(0, 5).join(", ")); + } + // ─── Java / Spring ─────────────────────────────────────── + if (ext === ".java") { + const cls = content.match(/(?:public\s+)?class\s+(\w+)/); + const className = cls?.[1] || basename.replace(".java", ""); + const annotations = (content.match(/@(RestController|Controller|Service|Repository|Component|Entity|Configuration)/g) || []).map(a => a.slice(1)); + const mappings = (content.match(/@(?:Get|Post|Put|Patch|Delete|Request)Mapping/g) || []).length; + if (mappings) + return cap(`${annotations[0] || "Spring"}: ${className} (${mappings} endpoints)`); + if (annotations.length) + return `${annotations[0]}: ${className}`; + if (content.includes("@Entity")) + return `Entity: ${className}`; + const methods = (content.match(/public\s+(?:static\s+)?(?:\w+(?:<[\w,\s]+>)?)\s+(\w+)\s*\(/g) || []) + .map(m => m.match(/(\w+)\s*\(/)?.[1]).filter(n => n && n !== className); + if (methods.length) + return cap(`${className}: ${methods.slice(0, 4).join(", ")}`); + return className ? `Class: ${className}` : ""; + } + // ─── Kotlin ────────────────────────────────────────────── + if (ext === ".kt" || ext === ".kts") { + const cls = content.match(/(?:data\s+)?class\s+(\w+)/); + if (content.match(/data\s+class/)) + return `Data class: ${cls?.[1] || basename.replace(/\.kts?$/, "")}`; + if (content.includes("routing {")) + return "Ktor routing"; + const fns = (content.match(/fun\s+(\w+)/g) || []).map(m => m.match(/fun\s+(\w+)/)?.[1]).filter(Boolean); + if (cls && fns.length) + return cap(`${cls[1]}: ${fns.slice(0, 4).join(", ")}`); + if (fns.length) + return cap(fns.slice(0, 5).join(", ")); + } + // ─── C# / .NET ─────────────────────────────────────────── + if (ext === ".cs") { + const cls = content.match(/(?:public\s+)?(?:partial\s+)?class\s+(\w+)(?:\s*:\s*(\w+))?/); + const className = cls?.[1] || basename.replace(".cs", ""); + const parent = cls?.[2] || ""; + if (parent === "Controller" || parent === "ControllerBase" || content.includes("[ApiController]")) { + const actions = (content.match(/\[Http(Get|Post|Put|Patch|Delete)\]/g) || []).map(a => a.match(/Http(\w+)/)?.[1]).filter(Boolean); + return cap(actions.length ? `API Controller: ${className} (${[...new Set(actions)].join(", ")})` : `Controller: ${className}`); + } + if (parent === "DbContext" || content.includes("DbSet<")) { + const sets = (content.match(/DbSet<(\w+)>/g) || []).map(s => s.match(/<(\w+)>/)?.[1]).filter(Boolean); + return cap(sets.length ? `DbContext: ${sets.join(", ")}` : `DbContext: ${className}`); + } + return className ? `Class: ${className}` : ""; + } + // ─── Ruby / Rails ──────────────────────────────────────── + if (ext === ".rb") { + const cls = content.match(/class\s+(\w+)(?:\s*<\s*(\w+(?:::\w+)?))?/); + const className = cls?.[1] || ""; + const parent = cls?.[2] || ""; + if (parent?.includes("Controller")) { + const actions = (content.match(/def\s+(index|show|new|create|edit|update|destroy|\w+)/g) || []) + .map(m => m.match(/def\s+(\w+)/)?.[1]).filter(n => n && !n.startsWith("_")); + return cap(actions.length ? `Controller: ${actions.join(", ")}` : `Controller: ${className}`); + } + if (parent === "ApplicationRecord" || parent === "ActiveRecord::Base") + return `Model: ${className}`; + if (basename.match(/^\d{14}_/)) { + const create = content.match(/create_table\s+:(\w+)/); + return create ? `Migration: create ${create[1]}` : "Database migration"; + } + const methods = (content.match(/def\s+(\w+)/g) || []).map(m => m.match(/def\s+(\w+)/)?.[1]).filter(n => n && !n.startsWith("_")); + if (cls && methods.length) + return cap(`${className}: ${methods.slice(0, 4).join(", ")}`); + } + // ─── Swift ─────────────────────────────────────────────── + if (ext === ".swift") { + if (content.includes(": View") || content.includes("some View")) { + const name = content.match(/struct\s+(\w+)\s*:\s*View/); + return name ? `SwiftUI view: ${name[1]}` : "SwiftUI view"; + } + const proto = content.match(/protocol\s+(\w+)/); + if (proto) + return `Protocol: ${proto[1]}`; + const struct = content.match(/(?:public\s+)?struct\s+(\w+)/); + const cls = content.match(/(?:public\s+)?class\s+(\w+)/); + const name = struct?.[1] || cls?.[1] || ""; + if (name) + return `${struct ? "Struct" : "Class"}: ${name}`; + } + // ─── Dart / Flutter ────────────────────────────────────── + if (ext === ".dart") { + if (content.includes("StatefulWidget") || content.includes("StatelessWidget")) { + const name = content.match(/class\s+(\w+)\s+extends\s+(?:Stateful|Stateless)Widget/); + return name ? `${content.includes("StatefulWidget") ? "Stateful" : "Stateless"} widget: ${name[1]}` : "Flutter widget"; + } + const cls = content.match(/class\s+(\w+)/); + if (cls) + return `Class: ${cls[1]}`; + } + // ─── Vue / Svelte / Astro ──────────────────────────────── + if (ext === ".vue") { + const name = content.match(/name:\s*['"]([^'"]+)['"]/); + const setup = content.includes("