@lifeaitools/rdc-skills 0.34.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/.claude-plugin/plugin.json +284 -1
  2. package/VALIDATOR-ARCHITECTURE.md +534 -0
  3. package/commands/analyze-tests.md +11 -0
  4. package/commands/check-clean-code.md +11 -0
  5. package/commands/check-packages.md +10 -0
  6. package/commands/compare-compliance.md +14 -0
  7. package/commands/full-analysis.md +50 -0
  8. package/commands/get-refactoring-plan.md +13 -0
  9. package/commands/quick-check.md +13 -0
  10. package/commands/recover.md +149 -0
  11. package/commands/review-arch.md +12 -0
  12. package/commands/review.md +12 -113
  13. package/commands/suggest-patterns.md +11 -0
  14. package/commands/validate-solid.md +11 -0
  15. package/package.json +14 -2
  16. package/scripts/architecture-score.mjs +157 -0
  17. package/scripts/clean-code-score.mjs +177 -0
  18. package/scripts/duplication-score.mjs +66 -0
  19. package/scripts/lib/architecture-scoring.mjs +695 -0
  20. package/scripts/lib/clean-code-scoring.mjs +258 -0
  21. package/scripts/lib/duplication-scoring.mjs +238 -0
  22. package/scripts/lib/language-plugin.mjs +82 -0
  23. package/scripts/lib/package-metrics.mjs +439 -0
  24. package/scripts/lib/pattern-scoring.mjs +351 -0
  25. package/scripts/lib/plugins/treesitter.mjs +1182 -0
  26. package/scripts/lib/plugins/typescript.mjs +672 -0
  27. package/scripts/lib/refactoring-scoring.mjs +307 -0
  28. package/scripts/lib/solid-scoring.mjs +101 -0
  29. package/scripts/lib/test-smell-scoring.mjs +581 -0
  30. package/scripts/lib/vendor/codeflow-parser/.source-commit +1 -0
  31. package/scripts/lib/vendor/codeflow-parser/grammars.d.ts +23 -0
  32. package/scripts/lib/vendor/codeflow-parser/grammars.js +57 -0
  33. package/scripts/lib/vendor/codeflow-parser/memberFacts.d.ts +274 -0
  34. package/scripts/lib/vendor/codeflow-parser/memberFacts.js +1117 -0
  35. package/scripts/lib/vendor/codeflow-parser/nativeParser.d.ts +115 -0
  36. package/scripts/lib/vendor/codeflow-parser/nativeParser.js +759 -0
  37. package/scripts/lib/vendor/codeflow-parser/package.json +3 -0
  38. package/scripts/lib/vendor/codeflow-parser/xmlParser.d.ts +77 -0
  39. package/scripts/lib/vendor/codeflow-parser/xmlParser.js +400 -0
  40. package/scripts/package-metrics-cli.mjs +112 -0
  41. package/scripts/pattern-score.mjs +143 -0
  42. package/scripts/refactoring-score.mjs +253 -0
  43. package/scripts/solid-score.mjs +337 -0
  44. package/skills/architecture-reviewer/SKILL.md +287 -0
  45. package/skills/clean-code-analyzer/SKILL.md +147 -0
  46. package/skills/package-design/SKILL.md +118 -0
  47. package/skills/pattern-advisor/SKILL.md +237 -0
  48. package/skills/pattern-refactoring-guide/SKILL.md +262 -0
  49. package/skills/review/SKILL.md +29 -0
  50. package/skills/solid-validator/SKILL.md +92 -0
  51. package/skills/testing-strategy/SKILL.md +132 -0
  52. package/tests/lib/architecture-scoring.test.mjs +335 -0
  53. package/tests/lib/clean-code-scoring.test.mjs +241 -0
  54. package/tests/lib/duplication-scoring.test.mjs +144 -0
  55. package/tests/lib/fixtures.mjs +58 -0
  56. package/tests/lib/package-metrics.test.mjs +241 -0
  57. package/tests/lib/pattern-scoring.test.mjs +251 -0
  58. package/tests/lib/refactoring-scoring.test.mjs +264 -0
  59. package/tests/lib/solid-scoring.test.mjs +291 -0
  60. package/tests/lib/test-smell-scoring.test.mjs +281 -0
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: full-analysis
3
+ description: >-
4
+ Usage `rdc:full-analysis <path> [--diff <ref>]` — runs all seven surfaces
5
+ (solid-validator, architecture-reviewer, clean-code-analyzer,
6
+ package-design, pattern-advisor, testing-strategy, and
7
+ pattern-refactoring-guide on anything the others flag) and merges into
8
+ one report. The full form/fit/function pass — use before merging a new
9
+ package or a significant refactor, not for routine edits.
10
+ ---
11
+
12
+ > **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
13
+ > Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
14
+ > One checklist upfront, updated in place, shown again at end with a 1-line verdict.
15
+
16
+ # full-analysis — Complete Form/Fit/Function Pass
17
+
18
+ ## Order — mechanical first, judgment second, refactor plans last
19
+
20
+ 1. **FORM** — Skill tool, skill: "solid-validator" (path, `--diff`)
21
+ 2. **FIT** — Skill tool, skill: "architecture-reviewer" (path, `--diff`) —
22
+ reuses step 1's `boundaryFindings`, does not re-run the mechanical check.
23
+ 3. **FUNCTION** — Skill tool, skill: "testing-strategy" (path)
24
+ 4. **Naming/dead-code/complexity** — Skill tool, skill: "clean-code-analyzer"
25
+ 5. **Package boundaries** — Skill tool, skill: "package-design"
26
+ 6. **Patterns** — Skill tool, skill: "pattern-advisor"
27
+ 7. **For every HIGH/CRITICAL finding from steps 1-6** — Skill tool, skill:
28
+ "pattern-refactoring-guide", one dispatch per finding, producing a
29
+ concrete plan.
30
+
31
+ ## Report
32
+
33
+ ```
34
+ ## Full Analysis — <path>
35
+ ### FORM (solid-validator)
36
+ ### FIT (architecture-reviewer)
37
+ ### FUNCTION (testing-strategy)
38
+ ### Clean Code
39
+ ### Package Design
40
+ ### Pattern Suggestions
41
+ ### Refactor Plans (for every high/critical finding above)
42
+ ## Verdict: CLEAN / HAS ISSUES
43
+ ```
44
+
45
+ ## Rules
46
+
47
+ - Do not re-run the mechanical boundary check inside step 2 — pass step 1's
48
+ result through.
49
+ - A CLEAN verdict requires all seven surfaces to report clean, not a
50
+ majority.
@@ -0,0 +1,13 @@
1
+ ---
2
+ name: get-refactoring-plan
3
+ description: >-
4
+ Usage `rdc:get-refactoring-plan <finding>` — turns a solid-validator/
5
+ architecture-reviewer/pattern-advisor finding into a step-ordered
6
+ before/after refactor plan. Produces a plan, does not apply it. See
7
+ skills/pattern-refactoring-guide.
8
+ ---
9
+
10
+ # get-refactoring-plan
11
+
12
+ Use Skill tool with skill: "pattern-refactoring-guide", passing the finding
13
+ (file:line + source skill) as args.
@@ -0,0 +1,13 @@
1
+ ---
2
+ name: quick-check
3
+ description: >-
4
+ Usage `rdc:quick-check <path>` — fast mechanical-only pass: solid-validator
5
+ score + boundary check. No dispatched judgment agents. For a tight
6
+ iteration loop, not a merge gate.
7
+ ---
8
+
9
+ # quick-check
10
+
11
+ Use Skill tool with skill: "solid-validator", passing the path as args.
12
+ This is the mechanical-only subset — no judgment dispatch, meant to run in
13
+ seconds during active editing. Use `full-analysis` before a merge.
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: recover
3
+ description: >-
4
+ Usage `rdc:recover [list|start]` — deterministic post-crash recovery: detect, repair (CodeFlow/PM2 only), verify, then find and resume abandoned Claude/Codex worktree-lane sessions.
5
+ ---
6
+
7
+ > **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
8
+ > Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
9
+ > One checklist upfront, updated in place, shown again at end with a 1-line verdict.
10
+
11
+ > **Sandbox contract:** This skill honors `RDC_TEST=1` per `guides/agent-bootstrap.md` §
12
+ > RDC_TEST Sandbox Contract. Under `$RDC_TEST=1`, Phase 3 (REPAIR) is skipped —
13
+ > echo `[RDC_TEST] skipping CodeFlow/PM2 repair` and proceed. DETECT, DIAGNOSE, VERIFY,
14
+ > and SESSIONS are all read-only probes and run normally.
15
+
16
+ # rdc:recover — post-crash session recovery
17
+
18
+ ## When to Use
19
+ - After a box crash, terminal-window crash, or unclean shutdown
20
+ - Checking whether any Claude/Codex worktree-lane session was mid-work when it happened
21
+ - Resuming a session that got abandoned, without hunting for its id by hand
22
+
23
+ ## The one thing to understand first
24
+
25
+ **This is a script, not a hunt.** All the actual logic — crash detection, engine-specific
26
+ liveness, lane classification, self-elevation to a visible window — lives in the project's
27
+ own `scripts/box-recovery.ps1` / `scripts/recover-session.ps1` / `scripts/lib/session-detect.ps1`.
28
+ This skill is a thin, deterministic dispatcher onto those scripts. Do not reimplement any of
29
+ their classification logic here, and do not improvise an alternate repair path if a phase
30
+ fails — report the failure with its exact command + output instead.
31
+
32
+ **Both scripts self-elevate to a real, persistent, visible terminal window on their own** —
33
+ they detect a non-interactive invocation (`[Console]::IsOutputRedirected`, true when your
34
+ tool call captures/pipes their output) and relaunch themselves via `wt.exe new-tab`, then
35
+ exit immediately. Do NOT manually wrap them in your own `wt.exe` call — just invoke the
36
+ script directly. That used to be the caller's job and got done wrong repeatedly live; it's
37
+ the script's own job now.
38
+
39
+ ## Usage
40
+
41
+ ```
42
+ rdc:recover # full pipeline — detect, repair, verify, report+offer sessions
43
+ rdc:recover list # report only — no repair, no launch, no prompt
44
+ rdc:recover start # skip detect/diagnose/repair/verify — just resume matched sessions
45
+ ```
46
+
47
+ ## Subcommands
48
+
49
+ ### `rdc:recover` (no argument) — full pipeline
50
+
51
+ Resolve the caller's actual project root first (never assume it's this plugin's own repo):
52
+
53
+ ```bash
54
+ ROOT=$(git rev-parse --show-toplevel)
55
+ pwsh -File "$ROOT/scripts/box-recovery.ps1"
56
+ ```
57
+
58
+ Deterministic, five phases in order — do not skip ahead, do not improvise alternate repair
59
+ commands:
60
+
61
+ 1. **DETECT** — was there actually an unclean shutdown (Kernel-Power Event 41), and is
62
+ memory/commit charge under pressure right now.
63
+ 2. **DIAGNOSE** — is CodeFlow's local gateway (`:3109`) up, is the local PM2 process table
64
+ empty (nothing resurrected after reboot).
65
+ 3. **REPAIR** — owner-sanctioned repair only: `node scripts/codeflow-repair.mjs` for
66
+ CodeFlow, `pm2 resurrect` for the local PM2 fleet. **Never** auto-starts an app named
67
+ `rtp` even if it's in the saved PM2 dump — see the script's own header for why. Do not
68
+ override that by hand-invoking `pm2 start rtp`.
69
+ 4. **VERIFY** — `pnpm agent:readiness` must report `ok:true`, plus a live probe of clauth
70
+ (`:52437/ping`) and CodeFlow (`:3109/health`). Do not report recovery as complete on
71
+ anything less than a live probe.
72
+ 5. **SESSIONS** — reports every worktree lane that was mid-session at crash time and is not
73
+ live right now (`aborted`), lanes with a crash-window transcript that have already
74
+ reattached (`✅ Already recovered`), and general stale lanes unrelated to this crash
75
+ (`orphan`). Prints exact resume commands for everything pending, then offers one
76
+ interactive prompt to resume some/all of it. A lane whose session engine doesn't match
77
+ its lane's owning engine (a Claude session in an `x-codex-*` lane, or vice versa) is
78
+ reported as a `LANE MISMATCH` and is a HARD RULE never launched, under any flag.
79
+
80
+ Because this launches a real visible window and its own interactive prompt, when running as
81
+ an agent (no human at that window to answer it) do not rely on the bare form to resume
82
+ anything — the window opens, the prompt waits, nothing gets resumed until a human answers
83
+ it. Report that the window is open and stop there. To have the agent itself resume sessions
84
+ non-interactively, use `rdc:recover start` below instead.
85
+
86
+ ### `rdc:recover list` — report only, no repair, no launch, no prompt
87
+
88
+ Skips the entire detect/diagnose/repair/verify stack entirely. Pure report:
89
+
90
+ ```bash
91
+ ROOT=$(git rev-parse --show-toplevel)
92
+ pwsh -File "$ROOT/scripts/recover-session.ps1" -ListOnly
93
+ ```
94
+
95
+ Add `-All` for the full inventory including `live` and `clean` lanes, not just
96
+ `aborted`/`orphan`.
97
+
98
+ ### `rdc:recover start` — resume, skipping the whole recovery stack
99
+
100
+ Also skips detect/diagnose/repair/verify — this is the addressable primitive, not the
101
+ crash orchestrator. Since an agent invoking this has no human sitting at the window to
102
+ answer an interactive prompt, always pass `-Launch` explicitly to skip it:
103
+
104
+ ```bash
105
+ ROOT=$(git rev-parse --show-toplevel)
106
+
107
+ # all matched (aborted + orphan by default; narrow with -Status/-Engine/-Role)
108
+ pwsh -File "$ROOT/scripts/recover-session.ps1" -Launch
109
+
110
+ # one specific session
111
+ pwsh -File "$ROOT/scripts/recover-session.ps1" -Id <sessionId> -Launch
112
+ ```
113
+
114
+ ## After launching, in every subcommand
115
+
116
+ Tell the user the window is open and to watch/answer it there — do not try to relay live
117
+ phase-by-phase output, you don't have it. If you need to verify the result yourself (e.g.
118
+ to answer a follow-up), read the run-record log the script writes on completion under
119
+ `C:\Dev\.logs\regen-root\<script-name>\<date>\` (newest file), rather than re-running the
120
+ script a second time in your own context.
121
+
122
+ ## Rules
123
+
124
+ - **Never reimplement the classification logic in this skill.** Engine-specific liveness,
125
+ crash-window matching, lane-mismatch detection, and orphan/aborted status all live in
126
+ `scripts/lib/session-detect.ps1` — read it if you need to understand *why* a session was
127
+ classified a certain way, never re-derive the answer independently.
128
+ - **Lane mismatch is never launched, under any flag or filter.** A Claude session found
129
+ sitting in a Codex-owned lane (or vice versa) is a HARD RULE violation if resumed there —
130
+ both scripts already enforce this; do not work around it.
131
+ - **If any phase fails, report exactly what failed and why** (cited command + output) — do
132
+ not guess a cause or invent a fix outside the script's own repair paths. If CodeFlow
133
+ repair itself fails, that is CodeFlow's own failure: report it, do not hand-patch
134
+ `packages/codeflow`.
135
+ - **Do not disrupt what you're trying to recover.** Never restart clauth, CodeFlow, or PM2
136
+ mid-diagnosis as a troubleshooting step — that can BE the reason a symptom changes.
137
+ - **-AutoRun's launch (if the caller wires this into logon) stays scoped to crash casualties
138
+ only.** Orphan lanes are reported for visibility but never silently auto-launched on an
139
+ unrelated crash's recovery run — see `box-recovery.ps1`'s own `$launchSet` handling.
140
+
141
+ ## Verification
142
+
143
+ The scripts themselves are the source of truth; this skill does not carry its own test
144
+ suite. To verify a change to the underlying scripts, from the caller's project root:
145
+
146
+ ```bash
147
+ pwsh -Command '$e=$null; [System.Management.Automation.Language.Parser]::ParseFile("scripts/box-recovery.ps1", [ref]$null, [ref]$e); if ($e.Count -gt 0) { $e } else { "OK" }'
148
+ pwsh -File scripts/recover-session.ps1 -ListOnly -All -NoSelfElevate
149
+ ```
@@ -0,0 +1,12 @@
1
+ ---
2
+ name: review-arch
3
+ description: >-
4
+ Usage `rdc:review-arch <path> [--diff <ref>]` — layering/dependency-
5
+ direction review, mechanical + judgment (FIT corner). See
6
+ skills/architecture-reviewer.
7
+ ---
8
+
9
+ # review-arch
10
+
11
+ Use Skill tool with skill: "architecture-reviewer", passing the path and any
12
+ `--diff` flag as args.
@@ -1,120 +1,19 @@
1
1
  ---
2
2
  name: review
3
3
  description: >-
4
- Usage `rdc:review [--unattended]` — tests, typecheck, stale docs, export conflicts across modified packages. Fixes issues found. Use after a build session or before merging to main.
4
+ Usage `rdc:review [--unattended]` — post-build quality gate: tsc, tests,
5
+ stale docs, export conflicts, a mandatory pr-review-toolkit:code-reviewer
6
+ pass, and the form/fit/function gate (solid-validator + architecture-
7
+ reviewer) across modified packages. See skills/review.
5
8
  ---
6
9
 
7
- > **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
8
- > Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
9
- > One checklist upfront, updated in place, shown again at end with a 1-line verdict.
10
+ # review
10
11
 
11
- > If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md`).
12
+ Use Skill tool with skill: "review", passing `--unattended` through if given.
12
13
 
13
- > **Sandbox contract:** This skill honors `RDC_TEST=1` per `guides/agent-bootstrap.md` § RDC_TEST Sandbox Contract. Destructive external calls short-circuit under the flag.
14
-
15
-
16
- # rdc:review Quality Gate
17
-
18
- ## When to Use
19
- - After a build session (especially overnight builds)
20
- - Before merging development → main/production
21
- - Project lead asks "review the work", "is everything clean"
22
- - Before any production deployment
23
- - Called by `rdc:overnight` after each epic build completes
24
-
25
- ## Arguments
26
- - `rdc:review` — interactive review, pauses on issues needing judgment
27
- - `rdc:review --unattended` — silent mode, auto-fixes everything fixable
28
-
29
- ## Procedure
30
-
31
- 1. **Identify modified packages:**
32
- ```bash
33
- git diff --name-only origin/main...HEAD | grep "^packages/" | cut -d/ -f2 | sort -u
34
- ```
35
-
36
- 2. **Run tests for each modified package:**
37
- ```bash
38
- cd packages/<name> && npx vitest run 2>&1 | tail -10
39
- ```
40
- Report: package → test count → pass/fail → new tests added
41
-
42
- **IMPORTANT:** `pnpm build` must NEVER be run (crashes system). Use `npx tsc --noEmit --project <path>/tsconfig.json` for typecheck instead. For packages without tests, typecheck is the verification method. Do NOT run vitest across the entire monorepo — check only modified packages individually.
43
-
44
- 3. **Check test coverage delta:**
45
- ```bash
46
- git diff origin/main...HEAD -- packages/*/src/ | grep -c "^+" | head -5
47
- git diff origin/main...HEAD -- packages/*/test* packages/*/src/**/*.test.* packages/*/src/**/*.spec.* 2>/dev/null | grep -c "^+" || echo 0
48
- ```
49
- Flag any package where implementation lines added > 50 but test lines added = 0.
50
-
51
- 4. **Check for export conflicts:**
52
- - Read `packages/*/src/index.ts` for any package with new exports
53
- - Look for duplicate export names across the barrel
54
- - Verify aliased exports don't shadow each other
55
-
56
- 5. **Check for TODO/FIXME/HACK:**
57
- ```bash
58
- grep -rn "TODO\|FIXME\|HACK\|XXX" packages/*/src/ --include="*.ts" --include="*.tsx"
59
- ```
60
-
61
- 6. **Check package versions:**
62
- - Any package with significant new code should have a version bump
63
- - Compare package.json versions to what's in `docs/SYSTEM-STATE.md`
64
-
65
- 7. **Check for stale CLAUDE.md:**
66
- - If new modules were added to a package, does its CLAUDE.md mention them?
67
- - Flag any package where exports grew by >10 lines but CLAUDE.md wasn't updated
68
-
69
- 8. **Orphan work item audit:**
70
- ```sql
71
- SELECT id, title, item_type, status, source, created_at::date
72
- FROM work_items
73
- WHERE parent_id IS NULL
74
- AND item_type NOT IN ('epic', 'bug')
75
- AND status NOT IN ('done', 'archived')
76
- ORDER BY created_at DESC;
77
- ```
78
- For each orphaned task found:
79
- - If it clearly belongs to an open epic → attach it: `UPDATE work_items SET parent_id = '<epic-id>' WHERE id = '<task-id>'`
80
- - If unclear → report (interactive) or flag in REVIEW_STATUS (unattended)
81
- - Never silently leave orphaned tasks
82
-
83
- 9. **Verification gate — dispatch the verify agent:**
84
- After any fixes land, run the verify gate on every touched package. See `guides/agents/verify.md`.
85
- **Iron Law: no CLEAN verdict without fresh evidence.** Quote the vitest + tsc output in the report.
86
- If verify fails → do NOT emit CLEAN. Loop back, fix, re-run verify.
87
-
88
- 10. **Fix issues found:**
89
- - Failing tests → fix and commit
90
- - Export conflicts → resolve and commit
91
- - Missing version bumps → bump and commit
92
- - All fixes as separate commits with descriptive messages
93
-
94
- **Judgment calls:**
95
- - Interactive: report — don't guess
96
- - Unattended: escalate via advisor tool with: error message, surrounding context,
97
- two most likely fix paths. Resume with advisor's recommendation.
98
- If advisor unavailable: take the most conservative path, flag in status block.
99
-
100
- 11. **Report:**
101
- - Interactive:
102
- ```
103
- ## Review Results
104
- | Package | Tests | Pass/Fail | New Tests | Issues |
105
- ## Fixed
106
- ## Remaining Issues
107
- ## Verdict: CLEAN / HAS ISSUES
108
- ```
109
- - Unattended: emit status block only:
110
- ```
111
- REVIEW_STATUS: { verdict: "CLEAN|HAS_ISSUES", packages_checked, tests_passed, tests_failed, new_tests_added, fixes_applied, escalations }
112
- ```
113
-
114
- ## Rules
115
- - Do NOT run `pnpm build` (crashes system) — vitest only
116
- - Interactive: fix what you can, flag what needs decision
117
- - Unattended: fix everything fixable; escalate judgment calls to advisor
118
- - Each fix is a separate commit (not batched)
119
- - Always push fixes to origin after committing *(skip if `$RDC_TEST=1` — echo `[RDC_TEST] skipping git push` instead)*
120
- - Unattended: NEVER pause for input
14
+ > This file previously carried a full duplicate of the procedure now owned by
15
+ > `skills/review/SKILL.md`. The two drifted — this copy was missing the
16
+ > mandatory code-review gate (step 8b) and the `engineering-behavior.md` read
17
+ > that the other had. One home for the content closes that class of bug
18
+ > permanently rather than requiring the next editor to remember to update
19
+ > both.
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: suggest-patterns
3
+ description: >-
4
+ Usage `rdc:suggest-patterns <path>` — design pattern suggestions,
5
+ advisory only, "no pattern needed" is a valid verdict. See
6
+ skills/pattern-advisor.
7
+ ---
8
+
9
+ # suggest-patterns
10
+
11
+ Use Skill tool with skill: "pattern-advisor", passing the path as args.
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: validate-solid
3
+ description: >-
4
+ Usage `rdc:validate-solid <path> [--diff <ref>]` — deterministic SOLID +
5
+ Clean Architecture scoring (FORM corner). See skills/solid-validator.
6
+ ---
7
+
8
+ # validate-solid
9
+
10
+ Use Skill tool with skill: "solid-validator", passing the path and any
11
+ `--diff`/`--config` flags as args.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/rdc-skills",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "description": "RDC typed-agent dispatch skill suite for Claude Code - plan, build, review, overnight builds",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -25,9 +25,18 @@
25
25
  "bin": {
26
26
  "rdc-skills-install": "scripts/install-rdc-skills.js",
27
27
  "rdc-skills-self-test": "scripts/self-test.mjs",
28
- "rdc-skills-mcp": "bin/rdc-skills-mcp.mjs"
28
+ "rdc-skills-mcp": "bin/rdc-skills-mcp.mjs",
29
+ "rdc-solid-score": "scripts/solid-score.mjs",
30
+ "rdc-clean-code-score": "scripts/clean-code-score.mjs",
31
+ "rdc-package-metrics": "scripts/package-metrics-cli.mjs",
32
+ "rdc-architecture-score": "scripts/architecture-score.mjs",
33
+ "rdc-pattern-score": "scripts/pattern-score.mjs",
34
+ "rdc-refactoring-score": "scripts/refactoring-score.mjs",
35
+ "rdc-test-smell-score": "scripts/lib/test-smell-scoring.mjs",
36
+ "rdc-duplication-score": "scripts/duplication-score.mjs"
29
37
  },
30
38
  "scripts": {
39
+ "test:validator": "node --test tests/lib/*.test.mjs",
31
40
  "postinstall": "node scripts/postinstall.js",
32
41
  "install-rdc-skills": "node scripts/install-rdc-skills.js",
33
42
  "uninstall:win": "powershell -ExecutionPolicy Bypass -File scripts/uninstall.ps1",
@@ -51,6 +60,9 @@
51
60
  "dependencies": {
52
61
  "@modelcontextprotocol/sdk": "^1.29.0",
53
62
  "express": "^5.0.0",
63
+ "tree-sitter-wasms": "^0.1.13",
64
+ "ts-morph": "^24.0.0",
65
+ "web-tree-sitter": "^0.24.7",
54
66
  "yaml": "^2.9.0",
55
67
  "zod": "^3.25.76"
56
68
  }
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * architecture-score — mechanical Clean Architecture boundary / dependency-
4
+ * direction / layer-separation scoring, ported from
5
+ * github.com/OnSightTeam/architecture-toolkit (MIT) — see
6
+ * `lib/architecture-scoring.mjs`'s header for the full provenance and what
7
+ * was deliberately NOT ported (the toolkit's own broken circular-dependency
8
+ * check).
9
+ *
10
+ * Same architecture as `package-metrics-cli.mjs`: this file owns argv
11
+ * parsing, file discovery, config loading, and output formatting. It does
12
+ * NOT know what an AST is — `lib/architecture-scoring.mjs` is plain
13
+ * `node:fs` + regex/path parsing, independent of the ts-morph
14
+ * `language-plugin.mjs` used by SOLID/clean-code.
15
+ *
16
+ * Config (`--config <file>`, YAML) — same loading discipline as
17
+ * `solid-score.mjs`'s `--config`: an explicitly-passed path that doesn't
18
+ * exist is an error, not a silent fallback to defaults; an absent flag
19
+ * legitimately means "use `DEFAULT_LAYERS`."
20
+ *
21
+ * layers:
22
+ * - name: Entities
23
+ * level: 4
24
+ * globs: ["**\/entities/**", "**\/domain/**"]
25
+ *
26
+ * Usage:
27
+ * node architecture-score.mjs <path> [--config <file>] [--format text|json]
28
+ *
29
+ * DETERMINISM (ATF golden-record requirement): file walk order is sorted
30
+ * (see `walkSourceFiles`), every finding array is emitted in a stable order
31
+ * (files sorted by relPath, rule keys in a fixed object-literal order,
32
+ * cycles canonicalized+sorted), no absolute paths in output (every
33
+ * `location` is scan-root-relative), no timestamps. Verified by running
34
+ * this CLI twice against the same target and diffing — see the task report
35
+ * for the actual command and its empty diff.
36
+ *
37
+ * Exit code is always 0 — a reporting tool, not a gate (same policy as
38
+ * `package-metrics-cli.mjs`; nothing asked for a `--fail-on` this round).
39
+ */
40
+
41
+ import { readFileSync, existsSync, statSync, realpathSync } from 'node:fs';
42
+ import path from 'node:path';
43
+ import { pathToFileURL } from 'node:url';
44
+ import { parse as parseYaml } from 'yaml';
45
+
46
+ import { walkSourceFiles, architectureScoreAll, DEFAULT_LAYERS } from './lib/architecture-scoring.mjs';
47
+
48
+ function arg(name, fallback = null) {
49
+ const i = process.argv.indexOf(name);
50
+ return i !== -1 ? process.argv[i + 1] : fallback;
51
+ }
52
+
53
+ /**
54
+ * Bash-tool / MSYS argv can hand this native-Windows process a POSIX-shaped
55
+ * path — normalize before use. Same fix as solid-score.mjs / clean-code-score.mjs.
56
+ */
57
+ function normalizePath(p) {
58
+ const m = /^\/([A-Za-z])\/(.*)$/.exec(p);
59
+ const windowsShaped = m ? `${m[1].toUpperCase()}:/${m[2]}` : p;
60
+ const abs = path.resolve(process.cwd(), windowsShaped);
61
+ try {
62
+ return realpathSync(abs);
63
+ } catch {
64
+ return abs;
65
+ }
66
+ }
67
+
68
+ export function loadConfig(configPath) {
69
+ if (!configPath) return { layers: DEFAULT_LAYERS, configPath: null, configLoaded: false };
70
+ if (!existsSync(configPath)) throw new Error(`--config ${configPath} does not exist`);
71
+ const raw = parseYaml(readFileSync(configPath, 'utf8')) ?? {};
72
+ const layers = raw.layers ?? DEFAULT_LAYERS;
73
+ return { layers, configPath, configLoaded: true };
74
+ }
75
+
76
+ function printHelp() {
77
+ console.error('Usage: node architecture-score.mjs <path> [--config <file>] [--format text|json]');
78
+ console.error('');
79
+ console.error(' <path> file or directory to scan (required)');
80
+ console.error(' --config <file> YAML file with a `layers:` section (see lib/architecture-scoring.mjs header). Default: DEFAULT_LAYERS (Entities/UseCases/InterfaceAdapters/Frameworks).');
81
+ console.error(' --format text (default) or json');
82
+ }
83
+
84
+ function printText(output) {
85
+ for (const r of output.results) {
86
+ if (r.totalFindings === 0) continue;
87
+ console.log(`${r.file} [layer: ${r.layer ?? 'UNCLASSIFIED'}${r.layer ? ` via ${r.layerBasis}` : ''}] — ${r.totalFindings} finding(s)`);
88
+ for (const rule of Object.values(r.rules)) {
89
+ for (const f of rule.findings) {
90
+ console.log(` [${rule.ruleId}] [${f.severity}] [${f.confidence}] ${f.detail}`);
91
+ }
92
+ }
93
+ }
94
+ if (output.circularLayerDependency.findings.length) {
95
+ console.log('\nCIRCULAR LAYER DEPENDENCIES:');
96
+ for (const f of output.circularLayerDependency.findings) console.log(` ${f.location}`);
97
+ } else {
98
+ console.log('\nNo circular layer dependencies found.');
99
+ }
100
+ if (output.unclassifiedFiles.length) {
101
+ console.log(`\n${output.unclassifiedFiles.length} file(s) matched no configured layer (path or name-hint) — skipped by every layer-aware rule, not silently passed as any specific layer:`);
102
+ for (const f of output.unclassifiedFiles) console.log(` ${f}`);
103
+ }
104
+ console.log(`\nLayers in effect: ${output.layers.map((l) => `${l.name}(${l.level})`).join(' > ')}`);
105
+ }
106
+
107
+ async function main() {
108
+ const rawTarget = process.argv[2]?.startsWith('--') ? null : process.argv[2];
109
+ if (!rawTarget) {
110
+ printHelp();
111
+ process.exit(1);
112
+ }
113
+ const targetPath = normalizePath(rawTarget);
114
+ const configArg = arg('--config');
115
+ const format = arg('--format', 'text');
116
+
117
+ if (!existsSync(targetPath)) throw new Error(`target path does not exist: ${targetPath}`);
118
+ const { layers, configPath, configLoaded } = loadConfig(configArg ? normalizePath(configArg) : null);
119
+
120
+ const isFile = statSync(targetPath).isFile();
121
+ const root = isFile ? path.dirname(targetPath) : targetPath;
122
+ const files = isFile ? [targetPath] : walkSourceFiles(targetPath);
123
+
124
+ const scored = architectureScoreAll(files, root, { layers });
125
+ const output = {
126
+ results: scored.results,
127
+ circularLayerDependency: scored.circularLayerDependency,
128
+ unclassifiedFiles: scored.unclassifiedFiles,
129
+ layers: scored.layers,
130
+ config: { configPath, configLoaded, layerRuleCount: layers.length },
131
+ };
132
+
133
+ if (format === 'json') {
134
+ console.log(JSON.stringify(output, null, 2));
135
+ } else {
136
+ printText(output);
137
+ }
138
+
139
+ process.exit(0);
140
+ }
141
+
142
+ function realFileURL(p) {
143
+ try {
144
+ return pathToFileURL(realpathSync(p)).href;
145
+ } catch {
146
+ return pathToFileURL(p).href;
147
+ }
148
+ }
149
+ const isMain = process.argv[1] && import.meta.url === realFileURL(process.argv[1]);
150
+ if (isMain) {
151
+ main().catch((err) => {
152
+ console.error(err.stack || String(err));
153
+ process.exit(2);
154
+ });
155
+ }
156
+
157
+ export { normalizePath };