From citadel
Audits project health with three passes: architectural compliance (files in right layers), filesystem hygiene (loose/misplaced files), bloat detection (oversized/binaries). Reports score, manages directory manifests.
npx claudepluginhub sethgammon/citadel --plugin citadelThis skill uses the workspace's default tool permissions.
**Use when:** Auditing whether source files are in the right architectural layers, detecting loose files and misplaced assets, or configuring directory manifests and cleanup policies for a project.
Enforces file organization standards from CLAUDE.md/PROJECT.md with auto-fix on file create/move and directory create events.
Safely scans, previews, and reorganizes project structure with read-only analysis, dry-runs, user confirmations, and protections for git, node_modules, secrets, and builds.
Share bugs, ideas, or general feedback.
Use when: Auditing whether source files are in the right architectural layers, detecting loose files and misplaced assets, or configuring directory manifests and cleanup policies for a project.
Don't use when: You want a structural map of code relationships and module dependencies (use /map), you're moving logic between modules (use /refactor), or you need to execute a single organizational task without a health audit (use /marshal).
| Command | Behavior |
|---|---|
/organize | Full flow: scan, detect, recommend, configure |
/organize --audit | Check current files against the manifest, report violations |
/organize --cleanup | Run dynamic directory cleanup based on TTL policy |
/organize --show | Display current organization manifest |
/organize --unlock | Set locked: false so enforcement is advisory |
/organize --lock | Set locked: true so enforcement blocks violations |
.claude/harness.json and check for an organization keyorganization exists and user ran bare /organize:
organization exists and user ran --audit: Jump to Step 6organization exists and user ran --cleanup: Jump to Step 7organization key: Continue to Step 2Every scan runs all three passes. A user running /organize gets the full picture.
Do NOT use find or Get-ChildItem -- use the Glob tool and Bash (git ls-files, du, wc) for cross-platform compatibility.
**/ to discover directories. Filter out:
node_modules, .git, .planning, .citadel, .claude, dist, build,
__pycache__, .next, target, .venv, venv.
Cap at 200 directories. If too large, scan only top 3 levels.package.json, Cargo.toml, pyproject.toml, go.mod, or equivalent for stacklanguage and framework fieldssrc/components/, src/hooks/, src/utils/ -> layer-basedsrc/features/auth/, src/features/dashboard/ -> feature-basedsrc/auth/components/, src/auth/hooks/ -> hybridsrc/ with no subdirectories -> flatRecord findings:
Detected: {convention}
Confidence: {high|medium|low}
Roots: [{path, purpose, file_count}]
Signals: [{pattern, evidence, convention_match}]
Anomalies: [{path, issue}]
Scoring: architecture_score = compliant_files / total_source_files * 100.
If no manifest exists, score is based on how consistently the detected convention is followed.
Loose files in project root. List every file in the project root. Expected root files:
package.json, tsconfig*.json, *.config.{js,ts,mjs,cjs}, .eslintrc*,
.prettierrc*, babel.config.*, jest.config.*, vite.config.*, next.config.*,
rollup.config.*, webpack.config.*, Cargo.toml, pyproject.toml, go.mod,
Makefile, Dockerfile, docker-compose*.yml, .env*, .editorconfig,
.gitignore, .gitattributes, .npmrc, .nvmrc, .node-version, .tool-versionsREADME*, LICENSE*, CHANGELOG*, CONTRIBUTING*, CLAUDE.md,
QUICKSTART*, CODE_OF_CONDUCT*, SECURITY**.lock, package-lock.json, yarn.lock, pnpm-lock.yaml,
Gemfile.lock, .github/, .husky/.claude/, .planning/, .citadel/, hooks/, hooks_src/,
skills/, agents/, scripts/.gitignoreEverything else in root (images, PDFs, ZIPs, random scripts, stray source files) is a finding.
Images and assets outside designated directories. Glob for
**/*.{png,jpg,jpeg,gif,svg,ico,webp,mp4,mp3,wav,pdf}. Check if they
live under a recognized asset directory (assets/, public/, static/,
images/, img/, media/, docs/images/, src/assets/). Files outside are findings.
Large files (>1 MB). Use git ls-files -z | xargs -0 stat or equivalent
to find tracked files over 1 MB. Report each with path and size.
Empty directories. Glob for directories, check which contain zero files
(excluding .gitkeep). Report as clutter.
Stale files in active directories. Files in src/ not modified in 6+ months
while siblings are active. Report as "potentially stale" -- advisory only.
Duplicate filenames. Scan for files with identical names in different directories. Flag for awareness.
Scoring: Start at 100, deduct:
Floor at 0. hygiene_score = max(0, 100 - deductions).
Project size vs. source size.
git ls-files -z | xargs -0 stat (sum sizes).ts, .tsx, .js, .jsx, .py, .rs,
.go, .java, .css, .scss, .html, .mdsource_bytes / total_bytes. Healthy = >60%. Below 40% means non-source dominates.Largest files ranked. List top 10 largest tracked files with sizes.
Binary files in git. Glob for:
*.{png,jpg,jpeg,gif,ico,webp,mp4,mp3,wav,woff,woff2,ttf,eot,zip,tar,gz,jar,dll,so,dylib,exe,bin,dat,db,sqlite,pdf}.
Check if each could be gitignored or moved to Git LFS. Report count and total size.
Accidentally committed directories. Check if any of these exist as tracked paths:
node_modules/, dist/, build/, .next/, target/, __pycache__/, .venv/,
venv/, .cache/, .parcel-cache/, .turbo/, coverage/. Any hit is a critical finding.
Compressible assets. PNGs over 500 KB, SVGs over 100 KB, any video/audio file.
Scoring: Start at 100, deduct:
Floor at 0. bloat_score = max(0, 100 - deductions).
composite_score = round(
architecture_score * 0.40 +
hygiene_score * 0.35 +
bloat_score * 0.25
)
Always report all four numbers. Never report just the architecture score.
=== Project Health: {project_name} ===
Architecture: {score}% -- source file placement, layer boundaries, test colocation
Hygiene: {score}% -- loose files, misplaced assets, stale artifacts, empty dirs
Bloat: {score}% -- project size ratio, binaries in git, compressible assets
-------------------------------
Overall: {composite}%
Follow scores with the most actionable findings from each pass. Group by severity (critical first, advisory last). Cap at 10 findings per pass; note "and N more" if truncated.
Present a tailored recommendation based on confidence level:
If confidence is HIGH: Present the detected convention and its roots. Ask: [Accept], [Adjust], or [Show alternatives].
If confidence is MEDIUM: Present the detected convention with anomaly count and 2-3 alternatives including reorganization cost (number of files to move). Ask: [1/2/3].
If confidence is LOW: Present the four convention options (feature-based, layer-based, hybrid, flat) with a one-line best-for description each, plus [Custom]. Ask: [1/2/3/4/5].
Wait for user response before proceeding.
4a. Build the roots tree:
For each detected root directory:
{
"purpose": "short description of what belongs here",
"children": { ... } // recursive, only if subdirectories have distinct purposes
}
Only go 2-3 levels deep.
4b. Build placement rules:
Derive placement rules from the detected convention:
| Convention | Example Rules |
|---|---|
| Feature-based | *.test.ts -> colocated with source, *.types.ts -> colocated |
| Layer-based | *.test.ts -> __tests__/ or tests/, *.types.ts -> types/ |
| Hybrid | *.test.ts -> colocated within feature, *.types.ts -> {feature}/types/ |
| Flat | No placement rules |
For each rule:
{
"glob": "*.test.{ts,tsx}",
"rule": "colocated",
"target": null,
"reason": "Tests live next to the code they test"
}
Rule types:
colocated -- file must be in the same directory as its sourcesibling-dir -- file must be in target directory adjacent to sourceroot-dir -- file must be under target from project rootwithin-root -- file must be under one of the declared rootsAsk the user if they want to adjust any rules before writing.
4c. Build dynamic directory entries:
Scan for directories created dynamically by the harness or tools. For each:
{ "path": ".planning/screenshots/", "scope": "session", "cleanup": "empty-on-expire" }
Scopes: session | campaign | task | permanent
Cleanup strategies: empty-on-expire | archive-then-delete | delete | ignore
4d. Set cleanup policy:
Ask the user:
When dynamic directories expire, how should cleanup work?
1. Auto -- Clean up silently on session end
2. Prompt -- Show what would be cleaned and ask first
3. Manual -- Just report stale directories, don't touch them
[1/2/3] (default: 2)
4e. Write to harness.json:
Read-modify-write. Merge the organization key. Do NOT overwrite other keys.
{
"organization": {
"convention": "layer",
"roots": { ... },
"placement": [ ... ],
"dynamic": [ ... ],
"cleanupPolicy": "prompt",
"locked": false
}
}
Set locked: false initially. Tell the user they can run /organize --lock once confident.
--lock, --audit, and --cleanup commandsRun all three passes from Step 2. Output the composite score block first:
=== Project Health: {project_name} ===
Architecture: {score}%
Hygiene: {score}%
Bloat: {score}%
-------------------------------
Overall: {composite}%
--- Architecture ({N} violations) ---
{glob pattern} should be {rule}:
- {violating_path} -> {expected_path}
--- Hygiene ({N} findings) ---
Loose files in project root:
- {filename}
Images outside asset directories:
- {path} -> should be {target}
Large files (>1 MB):
- {path} ({size})
Empty directories:
- {path}
--- Bloat ({N} findings) ---
Source ratio: {ratio}% ({source_size} source / {total_size} total)
Top 5 largest files:
1. {path} ({size})
Binary files that could be gitignored:
- {path} (generated/build output)
--- Suggested Actions ---
[Concrete fixes, quick wins first, larger reorganizations last]
Run quick fixes (move misplaced files, delete empty dirs)? [y/n]
If the user says yes, execute safe fixes (moves, empty dir removal). Never auto-delete files with content -- only move them or flag for manual review.
Read the dynamic entries from the organization manifest.
For each entry:
session: check .planning/telemetry/ for last session end timestamp. Files older than last session start are stale.campaign: check .planning/campaigns/ for associated campaign status. If completed or parked, contents are stale.task: check if the task ID still exists.permanent: skipempty-on-expire: delete contents, keep directoryarchive-then-delete: move to .planning/archive/{YYYY-MM-DD}/, then emptydelete: remove directory and contents (recreate if in PLANNING_DIRS)ignore: report but don't touchRespect cleanupPolicy: auto executes silently; prompt asks before each category; manual lists stale dirs without modifying.
Output:
=== Cleanup Report ===
Scanned: {N} dynamic directories
Stale: {M} directories ({total_size})
{path} ({scope}-scoped, {N} files, {size})
Strategy: {strategy}
Action: {Cleaned | Would clean | Skipped}
Summary: {N} directories cleaned, {M} archived, {K} skipped
.planning/ does not exist: Run /do setup first to initialize the harness state directory; then re-run.protectedFiles conflict with placement rules, warn and ask which takes precedence..planning/archive/ exceeds 50MB, warn and suggest manual pruning.Disclosure: "Running project health scan. Phase 3 (cleanup) may move files — will confirm scope before executing."
Reversibility: amber — may move files or rename directories in cleanup phase; undo with git checkout -- . or git revert
Trust gates:
organization key--lock, --audit, and --cleanup commands---HANDOFF---
- Health: {composite}% (Architecture {arch}%, Hygiene {hyg}%, Bloat {bloat}%)
- Convention: {convention} applied to {project}
- {N} roots, {M} placement rules, {K} dynamic directories configured
- Top findings: {1-2 most impactful issues from hygiene/bloat passes}
- Enforcement: {"advisory (unlocked)" | "blocking (locked)"}
- Reversibility: amber — file moves undoable with `git checkout -- .`; harness.json changes undoable with `git revert`
- Next: Run `/organize --lock` when confident, `/organize --audit` to recheck
---