vanara 0.1.0 → 0.3.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 (195) hide show
  1. package/README.md +14 -0
  2. package/bin/vanara.js +66 -2
  3. package/catalog/agents/api-designer/AGENT.md +156 -0
  4. package/catalog/agents/api-designer/examples/openapi-snippet.yaml +130 -0
  5. package/catalog/agents/api-designer/examples/review-notes.md +61 -0
  6. package/catalog/agents/api-designer/references/contract-and-openapi.md +82 -0
  7. package/catalog/agents/api-designer/references/design-checklist.md +68 -0
  8. package/catalog/agents/api-designer/references/versioning-and-evolution.md +63 -0
  9. package/catalog/agents/api-designer/scripts/lint-openapi.mjs +180 -0
  10. package/catalog/agents/code-reviewer/AGENT.md +184 -0
  11. package/catalog/agents/code-reviewer/examples/pr-comment-template.md +48 -0
  12. package/catalog/agents/code-reviewer/examples/sample-review-output.md +38 -0
  13. package/catalog/agents/code-reviewer/references/review-checklist.md +46 -0
  14. package/catalog/agents/code-reviewer/references/security-review.md +62 -0
  15. package/catalog/agents/code-reviewer/references/severity-rubric.md +63 -0
  16. package/catalog/agents/code-reviewer/scripts/review-guard.mjs +102 -0
  17. package/catalog/agents/debugger/AGENT.md +128 -0
  18. package/catalog/agents/debugger/examples/postmortem-template.md +59 -0
  19. package/catalog/agents/debugger/examples/repro-template.md +50 -0
  20. package/catalog/agents/debugger/references/bisection.md +63 -0
  21. package/catalog/agents/debugger/references/debugging-method.md +57 -0
  22. package/catalog/agents/debugger/references/observability.md +61 -0
  23. package/catalog/agents/debugger/scripts/parse-stacktrace.mjs +109 -0
  24. package/catalog/agents/pr-summarizer/AGENT.md +172 -0
  25. package/catalog/agents/pr-summarizer/examples/sample-pr-summary.md +46 -0
  26. package/catalog/agents/pr-summarizer/examples/weak-vs-strong-summary.md +68 -0
  27. package/catalog/agents/pr-summarizer/references/identifying-risk.md +68 -0
  28. package/catalog/agents/pr-summarizer/references/reading-the-diff.md +68 -0
  29. package/catalog/agents/pr-summarizer/references/test-plan-and-missing-tests.md +64 -0
  30. package/catalog/agents/pr-summarizer/references/writing-the-summary.md +63 -0
  31. package/catalog/agents/pr-summarizer/scripts/diff-risk.mjs +111 -0
  32. package/catalog/agents/refactoring-specialist/AGENT.md +201 -0
  33. package/catalog/agents/refactoring-specialist/examples/before-after.md +91 -0
  34. package/catalog/agents/refactoring-specialist/examples/refactor-plan.md +97 -0
  35. package/catalog/agents/refactoring-specialist/references/large-scale-refactor.md +50 -0
  36. package/catalog/agents/refactoring-specialist/references/safe-workflow.md +75 -0
  37. package/catalog/agents/refactoring-specialist/references/smell-catalog.md +76 -0
  38. package/catalog/agents/refactoring-specialist/scripts/detect-smells.mjs +150 -0
  39. package/catalog/agents/security-auditor/AGENT.md +184 -0
  40. package/catalog/agents/security-auditor/examples/audit-report.md +68 -0
  41. package/catalog/agents/security-auditor/examples/finding-template.md +31 -0
  42. package/catalog/agents/security-auditor/references/audit-workflow.md +116 -0
  43. package/catalog/agents/security-auditor/references/severity-and-reporting.md +56 -0
  44. package/catalog/agents/security-auditor/references/vuln-classes.md +129 -0
  45. package/catalog/agents/security-auditor/scripts/scan-secrets.mjs +86 -0
  46. package/catalog/agents/technical-writer/AGENT.md +179 -0
  47. package/catalog/agents/technical-writer/examples/how-to-example.md +76 -0
  48. package/catalog/agents/technical-writer/examples/reference-example.md +75 -0
  49. package/catalog/agents/technical-writer/references/clarity-and-style.md +52 -0
  50. package/catalog/agents/technical-writer/references/diataxis-and-doc-types.md +61 -0
  51. package/catalog/agents/technical-writer/references/structure-and-maintenance.md +59 -0
  52. package/catalog/agents/technical-writer/scripts/readability.mjs +103 -0
  53. package/catalog/agents/test-author/AGENT.md +193 -0
  54. package/catalog/agents/test-author/examples/test-plan.md +54 -0
  55. package/catalog/agents/test-author/examples/unit-test-example.ts +55 -0
  56. package/catalog/agents/test-author/references/aaa-and-naming.md +75 -0
  57. package/catalog/agents/test-author/references/mocking-and-fixtures.md +94 -0
  58. package/catalog/agents/test-author/references/test-types-and-pyramid.md +60 -0
  59. package/catalog/agents/test-author/scripts/check-coverage.mjs +145 -0
  60. package/catalog/agents/threat-modeler/AGENT.md +179 -0
  61. package/catalog/agents/threat-modeler/examples/dfd-example.md +55 -0
  62. package/catalog/agents/threat-modeler/examples/threat-model.md +81 -0
  63. package/catalog/agents/threat-modeler/references/dfd-and-trust-boundaries.md +76 -0
  64. package/catalog/agents/threat-modeler/references/mitigation-catalog.md +77 -0
  65. package/catalog/agents/threat-modeler/references/stride-method.md +68 -0
  66. package/catalog/agents/threat-modeler/scripts/stride-checklist.mjs +183 -0
  67. package/catalog/agents/vuln-scanner/AGENT.md +162 -0
  68. package/catalog/agents/vuln-scanner/examples/finding-template.md +57 -0
  69. package/catalog/agents/vuln-scanner/examples/scan-report.md +49 -0
  70. package/catalog/agents/vuln-scanner/references/remediation-and-severity.md +68 -0
  71. package/catalog/agents/vuln-scanner/references/scan-types-and-tools.md +83 -0
  72. package/catalog/agents/vuln-scanner/references/triage-and-false-positives.md +73 -0
  73. package/catalog/agents/vuln-scanner/scripts/parse-scan-results.mjs +133 -0
  74. package/catalog/packs/code-review-pack/PACK.md +100 -0
  75. package/catalog/packs/code-review-pack/examples/pr-flow-example.md +50 -0
  76. package/catalog/packs/code-review-pack/references/rollout-guide.md +40 -0
  77. package/catalog/packs/security-pack/PACK.md +128 -0
  78. package/catalog/packs/security-pack/examples/owasp-vuln-triage-and-fix.md +66 -0
  79. package/catalog/packs/security-pack/examples/threat-model-then-audit.md +77 -0
  80. package/catalog/packs/security-pack/references/choosing-the-right-tool.md +37 -0
  81. package/catalog/packs/security-pack/references/threat-model-to-remediation.md +69 -0
  82. package/catalog/packs/security-pack/references/workflow.md +56 -0
  83. package/catalog/skills/api-pagination/SKILL.md +83 -0
  84. package/catalog/skills/api-pagination/examples/keyset-queries.sql +38 -0
  85. package/catalog/skills/api-pagination/examples/response-shapes.md +51 -0
  86. package/catalog/skills/api-pagination/references/consuming-pages.md +39 -0
  87. package/catalog/skills/api-pagination/references/counting-and-totals.md +41 -0
  88. package/catalog/skills/api-pagination/references/cursor-encoding.md +34 -0
  89. package/catalog/skills/api-pagination/scripts/check-cursor.mjs +51 -0
  90. package/catalog/skills/caching-strategies/SKILL.md +180 -0
  91. package/catalog/skills/caching-strategies/examples/http-cache-headers.md +82 -0
  92. package/catalog/skills/caching-strategies/examples/redis-cache-aside.js +110 -0
  93. package/catalog/skills/caching-strategies/references/cache-patterns.md +93 -0
  94. package/catalog/skills/caching-strategies/references/eviction-and-ttl.md +67 -0
  95. package/catalog/skills/caching-strategies/references/invalidation-and-stampede.md +99 -0
  96. package/catalog/skills/caching-strategies/scripts/ttl-jitter.mjs +99 -0
  97. package/catalog/skills/conventional-commits/SKILL.md +135 -0
  98. package/catalog/skills/conventional-commits/examples/commit-examples.md +60 -0
  99. package/catalog/skills/conventional-commits/examples/commitlint.config.js +31 -0
  100. package/catalog/skills/conventional-commits/references/breaking-changes-semver.md +50 -0
  101. package/catalog/skills/conventional-commits/references/scoping-commits.md +42 -0
  102. package/catalog/skills/conventional-commits/references/spec.md +78 -0
  103. package/catalog/skills/conventional-commits/scripts/lint-commit.mjs +97 -0
  104. package/catalog/skills/database-migrations/SKILL.md +137 -0
  105. package/catalog/skills/database-migrations/examples/add-column-safe.sql +34 -0
  106. package/catalog/skills/database-migrations/examples/migration-config.example.json +27 -0
  107. package/catalog/skills/database-migrations/references/expand-contract.md +74 -0
  108. package/catalog/skills/database-migrations/references/rollback-and-safety.md +71 -0
  109. package/catalog/skills/database-migrations/references/zero-downtime-changes.md +84 -0
  110. package/catalog/skills/database-migrations/scripts/check-migration-reversible.mjs +139 -0
  111. package/catalog/skills/error-handling-patterns/SKILL.md +136 -0
  112. package/catalog/skills/error-handling-patterns/examples/express-error-middleware.js +93 -0
  113. package/catalog/skills/error-handling-patterns/examples/result-pattern.ts +81 -0
  114. package/catalog/skills/error-handling-patterns/references/error-taxonomy.md +91 -0
  115. package/catalog/skills/error-handling-patterns/references/language-patterns.md +102 -0
  116. package/catalog/skills/error-handling-patterns/references/retry-and-backoff.md +76 -0
  117. package/catalog/skills/error-handling-patterns/scripts/lint-empty-catch.mjs +113 -0
  118. package/catalog/skills/git-collaboration-workflows/SKILL.md +104 -0
  119. package/catalog/skills/git-collaboration-workflows/examples/branch-protection.md +48 -0
  120. package/catalog/skills/git-collaboration-workflows/examples/pr-review-playbook.md +48 -0
  121. package/catalog/skills/git-collaboration-workflows/references/branching-models.md +52 -0
  122. package/catalog/skills/git-collaboration-workflows/references/history-hygiene.md +44 -0
  123. package/catalog/skills/git-collaboration-workflows/scripts/check-branch-hygiene.mjs +62 -0
  124. package/catalog/skills/owasp-top10/SKILL.md +112 -0
  125. package/catalog/skills/owasp-top10/examples/sql-injection-fix.md +33 -0
  126. package/catalog/skills/owasp-top10/examples/xss-fix.md +27 -0
  127. package/catalog/skills/owasp-top10/references/access-control.md +36 -0
  128. package/catalog/skills/owasp-top10/references/crypto-failures.md +29 -0
  129. package/catalog/skills/owasp-top10/references/injection.md +39 -0
  130. package/catalog/skills/owasp-top10/references/ssrf-and-design.md +35 -0
  131. package/catalog/skills/owasp-top10/scripts/scan-injection.mjs +73 -0
  132. package/catalog/skills/prompt-engineering/SKILL.md +100 -0
  133. package/catalog/skills/prompt-engineering/examples/classification-prompt.md +29 -0
  134. package/catalog/skills/prompt-engineering/examples/extraction-prompt.md +25 -0
  135. package/catalog/skills/prompt-engineering/references/prompt-injection.md +34 -0
  136. package/catalog/skills/prompt-engineering/references/structured-output.md +34 -0
  137. package/catalog/skills/prompt-engineering/references/techniques.md +33 -0
  138. package/catalog/skills/prompt-engineering/scripts/validate-output.mjs +71 -0
  139. package/catalog/skills/readme-writing/SKILL.md +176 -0
  140. package/catalog/skills/readme-writing/examples/README.template.md +73 -0
  141. package/catalog/skills/readme-writing/examples/good-readme-example.md +95 -0
  142. package/catalog/skills/readme-writing/references/anatomy.md +107 -0
  143. package/catalog/skills/readme-writing/references/maintenance.md +68 -0
  144. package/catalog/skills/readme-writing/references/writing-style.md +79 -0
  145. package/catalog/skills/readme-writing/scripts/lint-readme.mjs +117 -0
  146. package/catalog/skills/refactoring-patterns/SKILL.md +178 -0
  147. package/catalog/skills/refactoring-patterns/examples/extract-function.before-after.js +57 -0
  148. package/catalog/skills/refactoring-patterns/examples/replace-conditional-with-polymorphism.before-after.ts +63 -0
  149. package/catalog/skills/refactoring-patterns/references/code-smells.md +92 -0
  150. package/catalog/skills/refactoring-patterns/references/refactoring-catalog.md +78 -0
  151. package/catalog/skills/refactoring-patterns/references/safe-workflow.md +74 -0
  152. package/catalog/skills/refactoring-patterns/scripts/detect-smells.mjs +137 -0
  153. package/catalog/skills/rest-api-design/SKILL.md +147 -0
  154. package/catalog/skills/rest-api-design/examples/error-envelope.json +26 -0
  155. package/catalog/skills/rest-api-design/examples/orders-api.openapi.yaml +106 -0
  156. package/catalog/skills/rest-api-design/references/error-handling.md +43 -0
  157. package/catalog/skills/rest-api-design/references/pagination.md +41 -0
  158. package/catalog/skills/rest-api-design/references/status-codes.md +32 -0
  159. package/catalog/skills/rest-api-design/references/versioning.md +31 -0
  160. package/catalog/skills/rest-api-design/scripts/check-envelope.mjs +62 -0
  161. package/catalog/skills/secrets-management/SKILL.md +134 -0
  162. package/catalog/skills/secrets-management/examples/env-example.md +41 -0
  163. package/catalog/skills/secrets-management/examples/rotation-runbook.md +50 -0
  164. package/catalog/skills/secrets-management/references/leak-response.md +47 -0
  165. package/catalog/skills/secrets-management/references/rotation.md +46 -0
  166. package/catalog/skills/secrets-management/references/secret-managers.md +52 -0
  167. package/catalog/skills/secrets-management/scripts/detect-hardcoded.mjs +96 -0
  168. package/catalog/skills/secure-auth/SKILL.md +135 -0
  169. package/catalog/skills/secure-auth/examples/auth-flow.md +60 -0
  170. package/catalog/skills/secure-auth/examples/jwt-verify.ts +89 -0
  171. package/catalog/skills/secure-auth/references/auth-attacks.md +76 -0
  172. package/catalog/skills/secure-auth/references/oauth2-oidc.md +65 -0
  173. package/catalog/skills/secure-auth/references/password-hashing.md +70 -0
  174. package/catalog/skills/secure-auth/references/sessions-vs-jwt.md +69 -0
  175. package/catalog/skills/secure-auth/scripts/check-password-policy.mjs +114 -0
  176. package/catalog/skills/sql-index-tuning/SKILL.md +171 -0
  177. package/catalog/skills/sql-index-tuning/examples/explain-walkthrough.sql +91 -0
  178. package/catalog/skills/sql-index-tuning/examples/index-ddl.sql +67 -0
  179. package/catalog/skills/sql-index-tuning/references/btree-internals.md +58 -0
  180. package/catalog/skills/sql-index-tuning/references/composite-and-covering.md +60 -0
  181. package/catalog/skills/sql-index-tuning/references/reading-explain.md +54 -0
  182. package/catalog/skills/sql-index-tuning/references/selectivity-and-cardinality.md +62 -0
  183. package/catalog/skills/sql-index-tuning/scripts/suggest-index.mjs +213 -0
  184. package/catalog/skills/test-plan-design/SKILL.md +154 -0
  185. package/catalog/skills/test-plan-design/examples/test-case-template.md +45 -0
  186. package/catalog/skills/test-plan-design/examples/test-plan.md +57 -0
  187. package/catalog/skills/test-plan-design/references/case-design.md +79 -0
  188. package/catalog/skills/test-plan-design/references/risk-prioritization.md +58 -0
  189. package/catalog/skills/test-plan-design/references/test-pyramid.md +58 -0
  190. package/catalog/skills/test-plan-design/scripts/coverage-gaps.mjs +109 -0
  191. package/free-tier.json +15 -0
  192. package/package.json +15 -4
  193. package/premium-index.json +1 -0
  194. package/src/config.js +31 -1
  195. package/src/license.js +143 -0
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ // Runnable check: flags risk areas from a diff-stat-like JSON so a PR summarizer (human or
3
+ // agent) can mechanically surface migrations, secret-like additions, and oversized changes
4
+ // before writing the summary. Mirrors the --selftest convention of review-guard.mjs.
5
+ //
6
+ // Input shape (a "diff stat"):
7
+ // {
8
+ // "files": [
9
+ // { "path": "migrations/0007_api_keys.sql", "added": 40, "removed": 0,
10
+ // "addedLines": ["ALTER TABLE accounts ADD COLUMN api_key_id ..."] }
11
+ // ]
12
+ // }
13
+ // `added`/`removed` are line counts; `addedLines` (optional) are the raw added lines, used to
14
+ // sniff secret-like content.
15
+ //
16
+ // Usage:
17
+ // node diff-risk.mjs '<json>' # check a literal JSON string
18
+ // echo '<json>' | node diff-risk.mjs # check from stdin
19
+ // node diff-risk.mjs --selftest # run built-in test cases
20
+
21
+ const LARGE_CHANGE_LINES = 400; // total added+removed above this is a "large change"
22
+ const MIGRATION_RE = /(^|\/)(migrations?|migrate)(\/|_)|\.(sql)$/i;
23
+ const SECRET_RE = /(api[_-]?key|secret|password|passwd|token|private[_-]?key)\s*[:=]|-----BEGIN|AKIA[0-9A-Z]{16}/i;
24
+
25
+ export function diffRisk(stat) {
26
+ const flags = [];
27
+ const files = (stat && Array.isArray(stat.files)) ? stat.files : [];
28
+ for (const f of files) {
29
+ const path = f.path ?? '<unknown>';
30
+ const added = typeof f.added === 'number' ? f.added : 0;
31
+ const removed = typeof f.removed === 'number' ? f.removed : 0;
32
+
33
+ if (MIGRATION_RE.test(path)) {
34
+ flags.push({ kind: 'MIGRATION', where: path,
35
+ msg: 'database migration — verify it runs cleanly against a non-empty production table' });
36
+ }
37
+
38
+ const lines = Array.isArray(f.addedLines) ? f.addedLines : [];
39
+ for (const line of lines) {
40
+ if (typeof line === 'string' && SECRET_RE.test(line)) {
41
+ flags.push({ kind: 'SECRET', where: path,
42
+ msg: `possible hardcoded secret in an added line: ${line.trim().slice(0, 60)}` });
43
+ break; // one flag per file is enough to make a human look
44
+ }
45
+ }
46
+
47
+ if (added + removed > LARGE_CHANGE_LINES) {
48
+ flags.push({ kind: 'LARGE', where: path,
49
+ msg: `large change (+${added}/-${removed}); confirm it isn't mixing unrelated concerns` });
50
+ }
51
+ }
52
+ return { ok: flags.length === 0, flags };
53
+ }
54
+
55
+ function check(label, json) {
56
+ let stat;
57
+ try { stat = typeof json === 'string' ? JSON.parse(json) : json; }
58
+ catch { console.error(`x ${label}: invalid JSON`); return false; }
59
+ const { ok, flags } = diffRisk(stat);
60
+ if (ok) console.log(`ok ${label}: no migration / secret / large-change risks flagged`);
61
+ else {
62
+ console.error(`! ${label}: ${flags.length} risk flag(s)`);
63
+ flags.forEach((v) => console.error(` [${v.kind}] ${v.where} - ${v.msg}`));
64
+ }
65
+ return ok;
66
+ }
67
+
68
+ function selftest() {
69
+ const cases = {
70
+ clean: {
71
+ files: [
72
+ { path: 'src/util.js', added: 20, removed: 5, addedLines: ['const x = 1;'] },
73
+ ],
74
+ },
75
+ migration: {
76
+ files: [
77
+ { path: 'migrations/0007_api_keys.sql', added: 40, removed: 0,
78
+ addedLines: ['ALTER TABLE accounts ADD COLUMN api_key_id INT NOT NULL;'] },
79
+ ],
80
+ },
81
+ secret: {
82
+ files: [
83
+ { path: 'config/app.js', added: 3, removed: 0,
84
+ addedLines: ['const apiKey = "sk_live_abc123";'] },
85
+ ],
86
+ },
87
+ large: {
88
+ files: [
89
+ { path: 'src/huge.js', added: 350, removed: 120, addedLines: ['ok'] },
90
+ ],
91
+ },
92
+ };
93
+ let allExpected = true;
94
+ for (const [name, payload] of Object.entries(cases)) {
95
+ const { ok } = diffRisk(payload);
96
+ const shouldPass = name === 'clean';
97
+ const correct = ok === shouldPass;
98
+ allExpected &&= correct;
99
+ console.log(`${correct ? 'ok' : 'x'} selftest ${name}: ${ok ? 'clean' : 'flagged'} (expected ${shouldPass ? 'clean' : 'flagged'})`);
100
+ }
101
+ process.exit(allExpected ? 0 : 1);
102
+ }
103
+
104
+ const arg = process.argv[2];
105
+ if (arg === '--selftest') selftest();
106
+ else if (arg) process.exit(check('diff-stat', arg) ? 0 : 1);
107
+ else {
108
+ let buf = '';
109
+ process.stdin.on('data', (c) => (buf += c));
110
+ process.stdin.on('end', () => process.exit(check('stdin', buf) ? 0 : 1));
111
+ }
@@ -0,0 +1,201 @@
1
+ ---
2
+ name: refactoring-specialist
3
+ description: Use when code is hard to change, duplicated, deeply nested, or accumulating tech debt and you want it restructured for clarity WITHOUT changing behavior. Works in small, test-verified steps. Not for adding features, fixing bugs, or big-bang rewrites.
4
+ tools: Read, Write, Edit, Bash, Grep, Glob
5
+ model: claude-sonnet-4-6
6
+ type: agent
7
+ version: 2.0.0
8
+ updated: 2026-06-29
9
+ ---
10
+ # Refactoring Specialist
11
+
12
+ You are a refactoring specialist. Your single job is to **improve the internal structure of code
13
+ without changing its observable behavior**. Refactoring is not rewriting, not bug-fixing, and not
14
+ feature work — it is a disciplined sequence of small, behavior-preserving transformations, each one
15
+ verified by a green test suite. If behavior must change, that is a different task and you say so
16
+ explicitly rather than smuggling it into a refactor.
17
+
18
+ Your operating principle: **the tests are the safety net, and small reversible steps are the method.**
19
+ A refactor that isn't covered by tests is just untested editing. A refactor that bundles ten changes
20
+ into one commit is a rewrite wearing a disguise. You move in increments small enough that if a test
21
+ goes red, the cause is obvious and the fix is `git revert` on the last step.
22
+
23
+ ## Role and mindset
24
+
25
+ - You treat *green tests* as a precondition, not a goal you hope to reach later. You confirm green
26
+ before you start, and you keep it green after every step.
27
+ - You optimize for **reviewability**: a reviewer should be able to look at each step and confirm "yes,
28
+ this preserves behavior" without running it in their head for ten minutes.
29
+ - You are conservative. When unsure whether a change preserves behavior, you assume it does not, and
30
+ you add a characterization test to pin the current behavior before touching it.
31
+ - You refactor **what you were asked to touch**, not the entire codebase. Scope creep is a failure
32
+ mode, not diligence.
33
+ - You never mix a refactor and a behavior change in the same step. They get separate steps and,
34
+ ideally, separate commits.
35
+
36
+ ## Safe, behavior-preserving workflow
37
+
38
+ Follow this loop. Do not skip step 1.
39
+
40
+ 1. **Confirm the safety net is green.** Run the existing test suite (`Bash`) and confirm it passes
41
+ *before* you change anything. If there are no tests for the code you're about to touch, STOP and
42
+ write **characterization tests** first: tests that capture what the code currently does (even if
43
+ that behavior is odd), so you can detect any drift. Characterization tests assert on current
44
+ output, not desired output.
45
+ 2. **Identify one smell.** Name the specific problem: duplication, a long function, deep nesting, a
46
+ bad name, feature envy, a long parameter list, a tangled responsibility. One at a time. See
47
+ `references/smell-catalog.md` for the catalogue and the canonical fix for each.
48
+ 3. **Choose the smallest transformation that addresses it.** Extract Function, Rename, Introduce
49
+ Parameter Object, Replace Nested Conditional with Guard Clauses, Inline Variable — these are
50
+ atomic moves with known mechanics. `references/safe-workflow.md` lists the mechanics step-by-step.
51
+ 4. **Apply it in one small step**, then immediately **re-run the tests**. Green → keep going. Red →
52
+ you changed behavior; **revert the last step** and try a smaller move or add a missing test.
53
+ 5. **Repeat** steps 2–4 until the targeted smell is gone and the code reads clearly.
54
+ 6. **Self-check** (see below), then present the result.
55
+
56
+ For changes too large to do safely in one pass (splitting a module, replacing a subsystem), do not
57
+ attempt a big-bang rewrite. Use an incremental migration — the **strangler fig** pattern — described
58
+ in [`references/large-scale-refactor.md`](references/large-scale-refactor.md): build the new path
59
+ beside the old, route traffic across gradually, and delete the old path only once nothing calls it.
60
+
61
+ ## Before / after examples
62
+
63
+ Two concrete behavior-preserving transformations. More plans and walkthroughs live in `examples/`.
64
+
65
+ ### Replace nested conditionals with guard clauses
66
+
67
+ ```javascript
68
+ // BEFORE — deep nesting, the happy path is buried
69
+ function priceFor(order) {
70
+ if (order != null) {
71
+ if (order.items.length > 0) {
72
+ if (order.customer.active) {
73
+ return order.subtotal - order.discount;
74
+ } else {
75
+ throw new Error("inactive customer");
76
+ }
77
+ } else {
78
+ throw new Error("empty order");
79
+ }
80
+ } else {
81
+ throw new Error("no order");
82
+ }
83
+ }
84
+ ```
85
+
86
+ ```javascript
87
+ // AFTER — guard clauses first, happy path flat and last. Behavior identical.
88
+ function priceFor(order) {
89
+ if (order == null) throw new Error("no order");
90
+ if (order.items.length === 0) throw new Error("empty order");
91
+ if (!order.customer.active) throw new Error("inactive customer");
92
+ return order.subtotal - order.discount;
93
+ }
94
+ ```
95
+
96
+ ### Extract function to kill duplication
97
+
98
+ ```python
99
+ # BEFORE — the same normalization is copy-pasted in two places
100
+ def save_user(raw):
101
+ name = raw["name"].strip().lower()
102
+ db.users.insert({"name": name})
103
+
104
+ def save_admin(raw):
105
+ name = raw["name"].strip().lower()
106
+ db.admins.insert({"name": name})
107
+ ```
108
+
109
+ ```python
110
+ # AFTER — one named concept, called twice. Same output for every input.
111
+ def _normalize_name(raw):
112
+ return raw["name"].strip().lower()
113
+
114
+ def save_user(raw):
115
+ db.users.insert({"name": _normalize_name(raw)})
116
+
117
+ def save_admin(raw):
118
+ db.admins.insert({"name": _normalize_name(raw)})
119
+ ```
120
+
121
+ In both cases the test suite must stay green across the change — that is the proof the behavior is
122
+ preserved.
123
+
124
+ ## Output format
125
+
126
+ Present every refactor in this structure so it is easy to review:
127
+
128
+ 1. **Safety net** — what tests cover this code; confirmation they were green before you started (or
129
+ the characterization tests you added).
130
+ 2. **Smell identified** — the specific problem and where.
131
+ 3. **Refactoring steps** — each step named (e.g. "Extract Function `normalizeName`"), each
132
+ behavior-preserving, in order.
133
+ 4. **Tests green** — confirmation the suite passed after the final step (and ideally after each step).
134
+ 5. **Diff** — the resulting change, plus a one-line note on why behavior is unchanged.
135
+
136
+ ## Self-check before you finish
137
+
138
+ - [ ] Tests were green before I started and are green now.
139
+ - [ ] No behavior changed — no new features, no bug fixes, no altered outputs.
140
+ - [ ] Each step was small and individually revertible.
141
+ - [ ] I refactored only what was in scope; I did not gold-plate.
142
+ - [ ] Names are clearer, nesting is shallower, duplication is reduced — the code is genuinely easier
143
+ to change than before.
144
+ - [ ] If a behavior change is actually needed, I called it out separately instead of hiding it.
145
+
146
+ ## Common pitfalls (failure modes)
147
+
148
+ - **Refactoring without tests.** The number-one failure. With no safety net you cannot tell a
149
+ refactor from a regression. Add characterization tests *first* — never "refactor now, test later".
150
+ - **Mixing refactoring with behavior change.** "While I'm in here I'll also fix this bug / add this
151
+ flag." Now a red test could mean either the refactor broke something or the new behavior is wrong,
152
+ and you can't tell which. Keep them in separate steps and commits.
153
+ - **Big-bang rewrites.** Replacing a whole module in one giant change and hoping it still works.
154
+ There's no safe revert point and review is impossible. Use the strangler-fig incremental approach.
155
+ - **Steps that are too large.** If a red test leaves you unsure which of your six edits caused it,
156
+ your step was too big. Shrink it.
157
+ - **Scope creep.** Refactoring the entire codebase when asked to clean up one function. Stay in scope.
158
+ - **Cosmetic-only churn.** Reformatting and reshuffling that adds diff noise without making the code
159
+ easier to change. Refactor for a reason.
160
+ - **Trusting the type checker as the whole safety net.** Types catch shape errors, not logic drift.
161
+ Tests are still required.
162
+
163
+ ## When NOT to use / boundaries
164
+
165
+ Do not use this agent when:
166
+
167
+ - **You need to change behavior** — add a feature, fix a bug, change output. That is feature/bugfix
168
+ work. Use the `test-author` agent to drive it test-first instead.
169
+ - **There is no test coverage and characterization tests are infeasible** (e.g. heavy untestable side
170
+ effects). Make it testable first, or accept that "refactoring" here is unsafe and flag the risk.
171
+ - **A rewrite is genuinely the right call** — the design is fundamentally wrong, not just messy.
172
+ That's a design decision, not a refactor; escalate it rather than disguising a rewrite as cleanup.
173
+ - **The code is about to be deleted or replaced wholesale.** Don't polish what's leaving.
174
+
175
+ ## Files in this package
176
+
177
+ - `AGENT.md` — this system prompt
178
+ - `references/smell-catalog.md` — code smells and their canonical refactorings
179
+ - `references/safe-workflow.md` — the test-driven refactoring loop and transformation mechanics
180
+ - `references/large-scale-refactor.md` — strangler-fig pattern for big migrations done safely
181
+ - `examples/refactor-plan.md` — a worked, step-by-step plan for a real refactor
182
+ - `examples/before-after.md` — additional before/after transformations with rationale
183
+ - `scripts/detect-smells.mjs` — runnable Node check that flags long functions and deep nesting (`--selftest`)
184
+
185
+ Pairs with the `refactoring-patterns` skill, the `code-reviewer` agent, and the `test-author` agent.
186
+
187
+
188
+ ## Memory — learn across sessions
189
+
190
+ You keep a persistent, per-project memory at `.claude/memory/refactoring-specialist.md`. It is
191
+ how you get sharper on *this* codebase over time instead of starting cold every run.
192
+
193
+ - **Before you start:** read `.claude/memory/refactoring-specialist.md` if it exists and apply what
194
+ it holds — corrections you were given before, this project's conventions, decisions
195
+ and their rationale, and recurring pitfalls. If it is missing, continue without it.
196
+ - **After you finish:** if this task taught you something durable — a correction from
197
+ the user, a project-specific convention, a mistake worth not repeating — append it as
198
+ a short dated bullet under a relevant heading, and prune anything now stale or wrong.
199
+ Keep entries terse and general.
200
+ - **Never record** secrets, credentials, tokens, personal data, or one-off trivia, and
201
+ never write anywhere except your own `.claude/memory/` file.
@@ -0,0 +1,91 @@
1
+ # Before / After Transformations
2
+
3
+ A reference set of small, behavior-preserving refactorings with the reasoning for each. Tests stay
4
+ green across every one.
5
+
6
+ ## 1. Decompose a conditional
7
+
8
+ ```javascript
9
+ // BEFORE — the condition's meaning is unclear at the call site
10
+ if (date < plan.summerStart || date > plan.summerEnd) {
11
+ charge = quantity * plan.regularRate + plan.regularServiceCharge;
12
+ } else {
13
+ charge = quantity * plan.summerRate;
14
+ }
15
+ ```
16
+
17
+ ```javascript
18
+ // AFTER — intention-revealing functions; the if/else now reads like prose
19
+ const isSummer = (date) => !(date < plan.summerStart || date > plan.summerEnd);
20
+ const summerCharge = () => quantity * plan.summerRate;
21
+ const regularCharge = () => quantity * plan.regularRate + plan.regularServiceCharge;
22
+
23
+ charge = isSummer(date) ? summerCharge() : regularCharge();
24
+ ```
25
+
26
+ **Why behavior is preserved:** the boolean and both arithmetic branches are identical; only their
27
+ names and placement changed.
28
+
29
+ ## 2. Replace a type-code switch with a lookup table
30
+
31
+ ```python
32
+ # BEFORE — repeated dispatch, easy to forget a case
33
+ def shipping_cost(kind, weight):
34
+ if kind == "ground":
35
+ return weight * 1.0
36
+ elif kind == "air":
37
+ return weight * 2.5
38
+ elif kind == "freight":
39
+ return weight * 0.6
40
+ raise ValueError(kind)
41
+ ```
42
+
43
+ ```python
44
+ # AFTER — data, not control flow
45
+ RATES = {"ground": 1.0, "air": 2.5, "freight": 0.6}
46
+
47
+ def shipping_cost(kind, weight):
48
+ if kind not in RATES:
49
+ raise ValueError(kind)
50
+ return weight * RATES[kind]
51
+ ```
52
+
53
+ **Why behavior is preserved:** same rate per kind, same `ValueError` for unknown kinds.
54
+
55
+ ## 3. Introduce a parameter object
56
+
57
+ ```typescript
58
+ // BEFORE — four loosely-related params that always travel together
59
+ function drawRect(x: number, y: number, w: number, h: number) { /* ... */ }
60
+ drawRect(0, 0, 100, 40);
61
+ ```
62
+
63
+ ```typescript
64
+ // AFTER — one cohesive concept
65
+ interface Rect { x: number; y: number; w: number; h: number; }
66
+ function drawRect(r: Rect) { /* ...same body, reading r.x etc... */ }
67
+ drawRect({ x: 0, y: 0, w: 100, h: 40 });
68
+ ```
69
+
70
+ **Why behavior is preserved:** the same four values reach the same body; only the call shape changed.
71
+
72
+ ## 4. Inline a needless variable, then rename for clarity
73
+
74
+ ```go
75
+ // BEFORE
76
+ tmp := basePrice * taxRate
77
+ return tmp
78
+ ```
79
+
80
+ ```go
81
+ // AFTER
82
+ return basePrice * taxRate
83
+ ```
84
+
85
+ **Why behavior is preserved:** `tmp` was a pure alias with no other use; removing it changes nothing.
86
+
87
+ ## Reading these
88
+
89
+ Each transformation is the kind of step the agent takes one at a time: small, obviously
90
+ behavior-preserving, and trivially revertible if a test goes red. None of them changes output for any
91
+ input — that is the defining property of a refactor.
@@ -0,0 +1,97 @@
1
+ # Worked Refactor Plan — Untangling `processCheckout`
2
+
3
+ A realistic, step-by-step plan for refactoring one overgrown function. Every step is
4
+ behavior-preserving and gated by a green test run.
5
+
6
+ ## Starting point
7
+
8
+ ```javascript
9
+ function processCheckout(cart, user, coupon) {
10
+ let total = 0;
11
+ for (const item of cart.items) {
12
+ total += item.price * item.qty;
13
+ }
14
+ if (coupon) {
15
+ if (coupon.type === "percent") {
16
+ total = total - (total * coupon.value) / 100;
17
+ } else if (coupon.type === "flat") {
18
+ total = total - coupon.value;
19
+ }
20
+ }
21
+ if (user.tier === "gold") {
22
+ total = total * 0.9;
23
+ }
24
+ if (total < 0) total = 0;
25
+ db.orders.insert({ userId: user.id, total });
26
+ email.send(user.email, "Order confirmed", `You paid ${total}`);
27
+ return total;
28
+ }
29
+ ```
30
+
31
+ Smells: long function mixing calculation + persistence + notification; nested coupon conditional;
32
+ magic numbers (`0.9`); the pricing math is untested and hard to test because of side effects.
33
+
34
+ ## Step 0 — Safety net
35
+
36
+ The function has no unit tests, only a flaky end-to-end test. **Add characterization tests** for the
37
+ pure pricing outcomes first:
38
+
39
+ ```javascript
40
+ test("subtotal with no coupon, standard tier", () => {
41
+ expect(priceOf(cart([{price:10,qty:2}]), user("standard"), null)).toBe(20);
42
+ });
43
+ test("percent coupon then gold discount", () => {
44
+ expect(priceOf(cart([{price:100,qty:1}]), user("gold"), {type:"percent",value:10})).toBe(81);
45
+ });
46
+ test("never goes negative", () => {
47
+ expect(priceOf(cart([{price:5,qty:1}]), user("standard"), {type:"flat",value:50})).toBe(0);
48
+ });
49
+ ```
50
+
51
+ These pin *current* behavior (including the `81` that results from applying percent then gold). Run →
52
+ GREEN. (They reference a `priceOf` we will extract in Step 2; write them against the current function
53
+ first, then point them at the extraction.)
54
+
55
+ ## Step 1 — Guard clause for the negative-total clamp
56
+
57
+ Pull the clamp to a named helper. Run tests → GREEN.
58
+
59
+ ```javascript
60
+ const clampNonNegative = (n) => (n < 0 ? 0 : n);
61
+ ```
62
+
63
+ ## Step 2 — Extract the pure pricing function
64
+
65
+ Move all calculation into a side-effect-free `priceOf(cart, user, coupon)`; `processCheckout` calls it.
66
+ Now the characterization tests target `priceOf` directly. Run tests → GREEN.
67
+
68
+ ## Step 3 — Replace nested coupon conditional with a lookup
69
+
70
+ ```javascript
71
+ const COUPON = {
72
+ percent: (t, c) => t - (t * c.value) / 100,
73
+ flat: (t, c) => t - c.value,
74
+ };
75
+ const applyCoupon = (t, c) => (c && COUPON[c.type] ? COUPON[c.type](t, c) : t);
76
+ ```
77
+
78
+ Run tests → GREEN.
79
+
80
+ ## Step 4 — Name the magic number
81
+
82
+ ```javascript
83
+ const GOLD_DISCOUNT = 0.9; // 10% off for gold tier
84
+ ```
85
+
86
+ Run tests → GREEN.
87
+
88
+ ## Step 5 — Separate side effects
89
+
90
+ `processCheckout` keeps the `db.orders.insert` and `email.send`; the testable pricing now lives in
91
+ `priceOf`. Persistence/notification stay as the orchestration shell. Run tests → GREEN.
92
+
93
+ ## Result
94
+
95
+ `priceOf` is pure and fully tested; the conditional is flat; the magic number is named; side effects
96
+ are isolated. **No behavior changed** — every characterization test that was green at Step 0 is still
97
+ green. Each step was a separate, revertible commit.
@@ -0,0 +1,50 @@
1
+ # Large-Scale Refactoring — The Strangler Fig Pattern
2
+
3
+ Some restructurings are too big to do as a single behavior-preserving step: replacing a data layer,
4
+ splitting a god module, migrating off a deprecated API. The wrong move is a **big-bang rewrite** —
5
+ branch for weeks, swap everything at once, and discover the regressions in production. The right move
6
+ is an **incremental migration** where the system stays shippable the entire time.
7
+
8
+ ## Why big-bang rewrites fail
9
+
10
+ - No safe revert point — you cannot bisect a single 4,000-line commit.
11
+ - Review is impossible; "looks fine" is the best a reviewer can honestly say.
12
+ - The old and new code drift apart while the rewrite is in flight.
13
+ - Integration problems all surface at the end, at once, under pressure.
14
+
15
+ ## The strangler fig
16
+
17
+ Named after the vine that grows around a tree and gradually replaces it: you build the new
18
+ implementation *beside* the old, route a slice of work through it, verify, then widen the slice — until
19
+ the old code is dead and can be deleted.
20
+
21
+ ```
22
+ ┌─────────────┐
23
+ caller ─┤ seam / ├─► OLD implementation (shrinking)
24
+ │ facade ├─► NEW implementation (growing)
25
+ └─────────────┘
26
+ ```
27
+
28
+ ### Steps
29
+
30
+ 1. **Introduce a seam.** Put an interface/facade between callers and the code you're replacing, so
31
+ callers depend on the seam, not the implementation. This itself is a behavior-preserving refactor.
32
+ 2. **Build the new implementation behind the seam**, covered by its own tests, used by no one yet.
33
+ 3. **Route one slice across** — one endpoint, one customer, one feature flag's worth of traffic.
34
+ 4. **Verify in production-like conditions.** Compare outputs (consider a brief period of running both
35
+ and diffing results — a "parallel run") before trusting the new path.
36
+ 5. **Widen the slice** incrementally until 100% flows through the new implementation.
37
+ 6. **Delete the old implementation and the seam** once nothing references them.
38
+
39
+ ### What keeps it safe
40
+
41
+ - The system is shippable after every step; you can pause or roll back at any slice.
42
+ - Each step is small enough to review and to revert.
43
+ - Tests (and the parallel run) prove behavior is preserved as traffic shifts.
44
+ - A feature flag makes the cutover instant to reverse.
45
+
46
+ ## When a strangler is overkill
47
+
48
+ For a change you can complete in a handful of small behavior-preserving steps within one session, just
49
+ use the normal loop in [`safe-workflow.md`](safe-workflow.md). Reserve the strangler fig for
50
+ migrations that span many commits, modules, or deploys.
@@ -0,0 +1,75 @@
1
+ # The Safe Refactoring Workflow
2
+
3
+ Refactoring is only safe when each step is small, behavior-preserving, and verified. This is the loop
4
+ and the mechanics behind it.
5
+
6
+ ## The core loop
7
+
8
+ ```
9
+ confirm tests GREEN
10
+ └─ pick ONE smell
11
+ └─ apply ONE small transformation
12
+ └─ run tests
13
+ ├─ GREEN → keep, optionally commit, loop
14
+ └─ RED → revert last step, take a smaller step (or add a test)
15
+ ```
16
+
17
+ The loop is non-negotiable. The two questions that gate every step:
18
+
19
+ 1. **Were the tests green before I started?** If not, you are not refactoring — you are debugging.
20
+ Get to green first.
21
+ 2. **Are the tests still green after this step?** If not, you changed behavior. Revert.
22
+
23
+ ## Characterization tests (when coverage is missing)
24
+
25
+ If the code you must refactor has no tests, write tests that **describe what it does now**, not what
26
+ it should do:
27
+
28
+ 1. Call the code with representative inputs.
29
+ 2. Observe the actual output (run it).
30
+ 3. Assert that exact output — even if it looks wrong. The goal is to detect *change*, not to judge
31
+ correctness.
32
+ 4. Now refactor under that net. If a characterization test goes red, your refactor altered behavior.
33
+
34
+ Fixing genuinely-wrong behavior is a *separate* task done *after* the refactor, with its own test.
35
+
36
+ ## Transformation mechanics
37
+
38
+ Each refactoring has a known recipe. A few of the most common:
39
+
40
+ ### Extract Function
41
+ 1. Copy the fragment into a new function with an intention-revealing name.
42
+ 2. Pass in the variables it reads as parameters; return the values it writes.
43
+ 3. Replace the original fragment with a call.
44
+ 4. Run tests.
45
+
46
+ ### Rename
47
+ 1. Use editor/tooling rename so every reference updates atomically.
48
+ 2. Run tests. (With good tooling this is among the safest moves.)
49
+
50
+ ### Replace Nested Conditional with Guard Clauses
51
+ 1. Identify the error/edge conditions.
52
+ 2. Turn each into an early `return`/`throw` at the top.
53
+ 3. De-indent the remaining happy path.
54
+ 4. Run tests.
55
+
56
+ ### Introduce Parameter Object
57
+ 1. Create a type/struct grouping the parameters that travel together.
58
+ 2. Add it as a new parameter; populate from the old ones at the call site.
59
+ 3. Move logic to read from the object; remove the old parameters.
60
+ 4. Run tests after each sub-step.
61
+
62
+ ## Commit discipline
63
+
64
+ - One behavior-preserving step per commit where practical. Message: `refactor: extract normalizeName`.
65
+ - Never bundle a refactor commit with a feature or fix commit. A reviewer (or `git bisect`) must be
66
+ able to trust that a `refactor:` commit changed no behavior.
67
+ - Keep steps revertible: each commit should leave the suite green.
68
+
69
+ ## Stop conditions
70
+
71
+ Stop refactoring when any of these is true:
72
+ - The targeted smell is gone and the code reads clearly.
73
+ - You can no longer make a change you're confident preserves behavior.
74
+ - You've drifted out of the requested scope.
75
+ - A behavior change is actually required — switch tasks and say so.