@uniqbit/mate-core 0.15.5 → 0.16.0-canary.1

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 (186) hide show
  1. package/claude-plugin/.claude-plugin/plugin.json +2 -2
  2. package/claude-plugin/hooks/hooks.json +3 -6
  3. package/claude-plugin/hooks/session-guidance.mjs +8 -0
  4. package/claude-plugin/hooks/ts-loader.mjs +19 -0
  5. package/package.json +7 -5
  6. package/src/cli/commands/artifact/artifact.ts +19 -3
  7. package/src/cli/commands/artifact/finish/command.ts +183 -57
  8. package/src/cli/commands/artifact/finish/engine.ts +80 -91
  9. package/src/cli/commands/artifact/finish/finisher.ts +26 -33
  10. package/src/cli/commands/artifact/finish/git.ts +34 -23
  11. package/src/cli/commands/artifact/finish/index.ts +9 -3
  12. package/src/cli/commands/artifact/finish/openspec.ts +225 -97
  13. package/src/cli/commands/artifact/pending/command.ts +175 -0
  14. package/src/cli/commands/artifact/pending/discovery.ts +249 -0
  15. package/src/cli/commands/artifact/pending/index.ts +17 -0
  16. package/src/cli/commands/cap/index-cmd.ts +9 -1
  17. package/src/cli/commands/cap/index.ts +2 -6
  18. package/src/cli/commands/cap/tokensave.ts +4 -4
  19. package/src/cli/commands/companion/companion.ts +5 -1
  20. package/src/cli/commands/companion/link.ts +2 -2
  21. package/src/cli/commands/companion/sync.ts +92 -0
  22. package/src/cli/commands/doctor.ts +0 -3
  23. package/src/cli/commands/launch/shared.ts +23 -5
  24. package/src/cli/commands/report/collector.ts +72 -78
  25. package/src/cli/commands/report/contract.ts +40 -1
  26. package/src/cli/commands/report/highlight.ts +27 -0
  27. package/src/cli/commands/report/index.ts +11 -18
  28. package/src/cli/commands/report/renderer.ts +199 -2
  29. package/src/cli/commands/report/types.ts +26 -1
  30. package/src/cli/commands/shared/companion-selection.ts +107 -10
  31. package/src/cli/commands/studio/areas.ts +68 -0
  32. package/src/cli/commands/studio/index.ts +69 -0
  33. package/src/cli/commands/studio/inventory.ts +55 -0
  34. package/src/cli/commands/studio/mate-inventory.ts +43 -0
  35. package/src/cli/commands/studio/openspec-cli.ts +198 -0
  36. package/src/cli/commands/studio/payload.ts +184 -0
  37. package/src/cli/commands/studio/routes.ts +2 -0
  38. package/src/cli/commands/studio/selection.ts +61 -0
  39. package/src/cli/commands/studio/server.ts +201 -0
  40. package/src/cli/commands/studio/snapshot.ts +63 -0
  41. package/src/cli/commands/studio/topology.ts +199 -0
  42. package/src/cli/commands/studio/views/client.ts +197 -0
  43. package/src/cli/commands/studio/views/companion-picker.tsx +91 -0
  44. package/src/cli/commands/studio/views/companion-selector.tsx +90 -0
  45. package/src/cli/commands/studio/views/dashboard/changes.tsx +107 -0
  46. package/src/cli/commands/studio/views/dashboard/index.tsx +19 -0
  47. package/src/cli/commands/studio/views/document.tsx +256 -0
  48. package/src/cli/commands/studio/views/error.tsx +20 -0
  49. package/src/cli/commands/studio/views/model.ts +34 -0
  50. package/src/cli/commands/studio/views/pairings.tsx +38 -0
  51. package/src/cli/commands/studio/views/skills/index.tsx +87 -0
  52. package/src/cli/commands/studio/views/specs/index.tsx +101 -0
  53. package/src/cli/commands/studio/views/styles.ts +389 -0
  54. package/src/cli/commands/studio/views/warnings.tsx +21 -0
  55. package/src/cli/commands/studio/views/workflow/index.tsx +21 -0
  56. package/src/cli/commands/studio/views/workflow/steps.ts +329 -0
  57. package/src/cli/commands/studio/views/workflow/transcript.tsx +190 -0
  58. package/src/cli/commands/unwrap.ts +70 -0
  59. package/src/cli/commands/wrap.ts +164 -0
  60. package/src/cli/main.ts +68 -19
  61. package/src/cli/parse-flags.ts +36 -11
  62. package/src/cli/usage.ts +12 -3
  63. package/src/framework.ts +1 -7
  64. package/src/hooks/session-banner.ts +64 -11
  65. package/src/hooks/session-guidance.ts +40 -0
  66. package/src/hooks/validate-artifact-path.ts +108 -35
  67. package/src/lib/fs-utils.ts +9 -0
  68. package/src/lib/install.ts +33 -0
  69. package/src/lib/orchestrator/adapters/base.ts +14 -125
  70. package/src/lib/orchestrator/adapters/claude.ts +0 -11
  71. package/src/lib/orchestrator/adapters/opencode.ts +2 -32
  72. package/src/lib/orchestrator/companion-git-sync.ts +94 -84
  73. package/src/lib/orchestrator/config-store.ts +2 -21
  74. package/src/lib/orchestrator/editor.ts +12 -22
  75. package/src/lib/orchestrator/framework-context.ts +17 -6
  76. package/src/lib/orchestrator/global-config-store.ts +1 -1
  77. package/src/lib/orchestrator/launcher.ts +97 -7
  78. package/src/lib/orchestrator/opencode-guidance.ts +4 -56
  79. package/src/lib/orchestrator/projection-claude-entry.ts +198 -0
  80. package/src/lib/orchestrator/projection-claude-skills.ts +120 -0
  81. package/src/lib/orchestrator/projection-companion-link.ts +62 -0
  82. package/src/lib/orchestrator/projection-entries.ts +377 -0
  83. package/src/lib/orchestrator/projection-record.ts +56 -0
  84. package/src/lib/orchestrator/projection-runtime-documents.ts +424 -0
  85. package/src/lib/orchestrator/projection-types.ts +169 -0
  86. package/src/lib/orchestrator/repo-local-registry.ts +37 -133
  87. package/src/lib/orchestrator/repo-local-store.ts +96 -0
  88. package/src/lib/orchestrator/setup-compatibilities.ts +1 -9
  89. package/src/lib/orchestrator/types.ts +1 -0
  90. package/src/lib/orchestrator/working-repo-projection.ts +366 -0
  91. package/src/lib/orchestrator/workspace-inventory.ts +1 -1
  92. package/src/lib/package-paths.ts +11 -1
  93. package/src/lib/public-npm.ts +2 -1
  94. package/src/lib/update-checker.ts +15 -9
  95. package/src/opencode/companion-hooks.ts +89 -245
  96. package/src/opencode/companion-policy.ts +35 -10
  97. package/src/opencode/index.ts +1 -0
  98. package/src/opencode/projected-guidance.ts +56 -0
  99. package/src/opencode/tui.tsx +13 -4
  100. package/src/playbooks/companion-guidance.ts +32 -116
  101. package/src/plugins.ts +0 -1
  102. package/src/runtime/companion-git-state.ts +156 -0
  103. package/src/runtime/companion-git.ts +203 -0
  104. package/src/runtime/companion-guidance.ts +222 -0
  105. package/src/runtime/companion-sync.ts +298 -0
  106. package/src/runtime/env-names.ts +30 -0
  107. package/src/runtime/env.ts +67 -35
  108. package/src/runtime/framework.ts +10 -0
  109. package/src/runtime/freshness.ts +58 -0
  110. package/src/runtime/index.ts +104 -0
  111. package/src/runtime/install.ts +30 -0
  112. package/src/runtime/policy.ts +66 -0
  113. package/src/runtime/projected-guidance.ts +45 -0
  114. package/src/runtime/projection.ts +224 -0
  115. package/src/runtime/repo-local.ts +64 -0
  116. package/src/templates/capabilities/openspec-cap/mate-minimal/schema.yaml +56 -0
  117. package/src/templates/capabilities/openspec-cap/mate-minimal/templates/spec.md +48 -0
  118. package/src/templates/capabilities/openspec-cap/mate-minimal/templates/tasks.md +22 -0
  119. package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +31 -90
  120. package/src/templates/capabilities/openspec-cap/mate-v1/templates/design.md +3 -0
  121. package/src/templates/capabilities/openspec-cap/mate-v1/templates/explore-brief.md +6 -6
  122. package/src/templates/capabilities/openspec-cap/mate-v1/templates/spec.md +3 -5
  123. package/src/templates/capabilities/openspec-cap/mate-v1/templates/tasks.md +3 -0
  124. package/src/templates/capabilities/openspec-cap/openspec-conventions.yaml +30 -0
  125. package/src/templates/capabilities/react-doctor/claude/hooks/react-doctor.sh +2 -2
  126. package/src/templates/mate-skills/agents/mate-artifact-publish/SKILL.md +185 -0
  127. package/src/templates/mate-skills/agents/mate-artifact-publish/references/openspec.md +227 -0
  128. package/src/templates/mate-skills/agents/mate-domain-modeling/SKILL.md +68 -0
  129. package/src/templates/mate-skills/agents/mate-domain-modeling/references/ADR-FORMAT.md +9 -0
  130. package/src/templates/mate-skills/agents/mate-domain-modeling/references/CONTEXT-FORMAT.md +18 -0
  131. package/src/templates/mate-skills/agents/mate-grill-me/SKILL.md +14 -0
  132. package/src/templates/mate-skills/agents/mate-grill-with-docs/SKILL.md +29 -0
  133. package/src/templates/mate-skills/agents/mate-grilling/SKILL.md +49 -0
  134. package/src/templates/mate-skills/agents/mate-interview-me/SKILL.md +156 -0
  135. package/src/templates/{capabilities/openspec-cap/mate-skills → mate-skills}/agents/mate-openspec-backfill/SKILL.md +4 -2
  136. package/src/templates/mate-skills/agents/mate-show-me/SKILL.md +139 -0
  137. package/src/templates/mate-skills/agents/mate-simplify-code/SKILL.md +503 -0
  138. package/src/templates/mate-skills/claude/mate-artifact-publish/SKILL.md +185 -0
  139. package/src/templates/mate-skills/claude/mate-artifact-publish/references/openspec.md +227 -0
  140. package/src/templates/mate-skills/claude/mate-domain-modeling/SKILL.md +68 -0
  141. package/src/templates/mate-skills/claude/mate-domain-modeling/references/ADR-FORMAT.md +9 -0
  142. package/src/templates/mate-skills/claude/mate-domain-modeling/references/CONTEXT-FORMAT.md +18 -0
  143. package/src/templates/mate-skills/claude/mate-grill-me/SKILL.md +14 -0
  144. package/src/templates/mate-skills/claude/mate-grill-with-docs/SKILL.md +29 -0
  145. package/src/templates/mate-skills/claude/mate-grilling/SKILL.md +49 -0
  146. package/src/templates/mate-skills/claude/mate-interview-me/SKILL.md +156 -0
  147. package/src/templates/mate-skills/claude/mate-openspec-backfill/SKILL.md +67 -0
  148. package/src/templates/mate-skills/claude/mate-show-me/SKILL.md +139 -0
  149. package/src/templates/mate-skills/claude/mate-simplify-code/SKILL.md +503 -0
  150. package/src/templates/report-assets/README.md +32 -0
  151. package/src/templates/report-assets/mermaid.LICENSE +21 -0
  152. package/src/templates/report-assets/mermaid.min.js +4376 -0
  153. package/src/templates/root/TEMPLATE_CLAUDE.md +1 -9
  154. package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +1476 -197
  155. package/src/tools/setup/capabilities/graphify.ts +16 -7
  156. package/src/tools/setup/capabilities/openspec.ts +63 -56
  157. package/src/tools/setup/capabilities/tokensave.ts +116 -2
  158. package/src/tools/setup/engine.ts +34 -6
  159. package/src/tools/setup/mate.ts +42 -13
  160. package/src/tools/setup/plugin.ts +9 -0
  161. package/src/tools/setup/plugins/guidance.ts +11 -1
  162. package/src/tools/setup/providers/claude-format.ts +49 -4
  163. package/src/tools/setup/providers/claude-plugin-hooks.ts +117 -0
  164. package/src/tools/setup/providers/claude.ts +55 -220
  165. package/src/tools/setup/providers/opencode.ts +41 -14
  166. package/src/tools/setup/runtime-documents.ts +174 -0
  167. package/src/tools/setup/surface-target.ts +50 -0
  168. package/src/tools/setup/working-repo-cleanup.ts +33 -26
  169. package/src/tools/setup/working-repo-local-state.ts +21 -1
  170. package/src/tools/setup.ts +25 -3
  171. package/wrappers/bin/graphify +57 -8
  172. package/wrappers/bin/openspec +50 -3
  173. package/claude-plugin/hooks/artifact-finish-nudge.mjs +0 -8
  174. package/src/cli/commands/cap/headroom.ts +0 -52
  175. package/src/cli/commands/workspace/list.ts +0 -25
  176. package/src/cli/commands/workspace/materialize.ts +0 -46
  177. package/src/cli/commands/workspace/workspace.ts +0 -22
  178. package/src/hooks/artifact-finish-nudge.ts +0 -244
  179. package/src/lib/orchestrator/headroom/proxy.ts +0 -116
  180. package/src/lib/orchestrator/workspace-materialize.ts +0 -80
  181. package/src/templates/capabilities/openspec-cap/mate-skills/agents/mate-artifact-finish/SKILL.md +0 -51
  182. package/src/templates/capabilities/openspec-cap/mate-skills/agents/mate-artifact-finish/references/openspec.md +0 -134
  183. package/src/templates/capabilities/openspec-cap/mate-skills/claude/mate-artifact-finish/SKILL.md +0 -58
  184. package/src/templates/capabilities/openspec-cap/mate-skills/claude/mate-artifact-finish/references/openspec.md +0 -139
  185. package/src/tools/setup/capabilities/headroom.ts +0 -57
  186. /package/src/cli/commands/{workspace → companion}/open.ts +0 -0
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: mate-show-me
3
+ description: Explain the current topic or the applied change in a browser-rendered Mate report. Use only when the user explicitly invokes the skill to see how something works, what a change did, or wants a diagram, call tree, or rendered diff of the current work.
4
+ disable-model-invocation: true
5
+ allowed-tools: Bash(git:*), Bash(mate:*)
6
+ license: MIT
7
+ compatibility: Requires the mate CLI and the openspec capability enabled.
8
+ metadata:
9
+ author: mate
10
+ version: "1.0"
11
+ ---
12
+
13
+ # Mate Show Me
14
+
15
+ > Inspired by [humanlayer's show-me skill](https://github.com/humanlayer/skills/tree/main/plugins/show-me/skills/show-me) and adapted here as a Mate process-driven skill.
16
+
17
+ Explain the current topic visually in a browser-rendered Mate report. Skip the preamble, keep prose brief, and pick the smallest report view that makes the key point clear.
18
+
19
+ ## Mate Workflow
20
+
21
+ - Explanation only. Never write or edit source code, tests, documentation, OpenSpec artifacts, context files, or ADRs. The only file this skill produces is the report document handed to `mate report --input`, which the CLI writes into the operating system temporary directory.
22
+ - Draw from the repositories, not from memory. Every diagram and every diff line comes from the inspected repository state, never from recollection of the conversation. Use the available code-graph tools before broad source scans.
23
+ - Code lives in the Working Repository; the Companion Repository holds only artifacts. Run `git diff` in the Working Repository.
24
+ - Never put an absolute local path, home directory, username, machine name, temporary report path, Companion Repository path, or raw path-valued environment variable in the report. Use repository-relative paths or neutral labels such as `Working Repository` and `Companion Repository`.
25
+ - Never commit, push, or create a pull request.
26
+
27
+ ## Browser Report Required
28
+
29
+ Always assemble and open a Mate report, even when a small inline sketch would be sufficient. Do not answer with an inline-only visual or paste the complete diagram or diff into the conversation. The final response should briefly state what the browser report shows without repeating its temporary file path.
30
+
31
+ ## Report Contents
32
+
33
+ - Show logic or an algorithm as a `diagram` section. Use a `mermaid` payload when branches, stages, or relationships benefit from a rendered visual; reserve `text` for compact pseudocode:
34
+
35
+ ```text
36
+ on(save)
37
+ if content is unchanged
38
+ return cached result
39
+ write new content
40
+ return fresh result
41
+ ```
42
+
43
+ - Show runtime control flow as a `diagram` section with a `mermaid` payload. Use `flowchart TD` for a call tree or control flow and `sequenceDiagram` when ordering between components matters:
44
+
45
+ ```mermaid
46
+ flowchart TD
47
+ renderHTML[renderHTML] --> validate[validateReportDocument]
48
+ renderHTML --> section[renderHTMLSection]
49
+ section --> diagram[renderHTMLDiagram]
50
+ section --> diff[renderHTMLDiff]
51
+ ```
52
+
53
+ - Show UI structure as a `diagram` section with a `mermaid` payload when showing relationships between components or modules. Use a `text` payload only when exact component syntax is the point, including the state boundaries that matter:
54
+
55
+ ```tsx
56
+ <WorkflowView> (studio/views/workflow/index.tsx)
57
+ workflowPlan(availableSkills)
58
+ <StepList>
59
+ <StepRow optional>
60
+ ```
61
+
62
+ - Show file responsibility or a broad refactor as a `diagram` section with a `text` payload containing a shallow file tree:
63
+
64
+ ```text
65
+ src/
66
+ |-- commands/ # parses user actions
67
+ |-- report/ # owns the report contract and renderer
68
+ `-- templates/ # ships assets and skills
69
+ ```
70
+
71
+ - Show what changes as a `diff` section when the surrounding shape already exists. Match the diff shape to the topic - component tree, file layout, call tree, or control flow:
72
+
73
+ ```diff
74
+ on(save)
75
+ - write content
76
+ + if content is unchanged
77
+ + return cached result
78
+ + write new content
79
+ ```
80
+
81
+ - Show a whole block in the report when most of it is new, when omitted context would hide ownership or order, or when the user needs a copyable target shape.
82
+
83
+ ## Browser Delivery
84
+
85
+ The report is mandatory. Prefer `mate report --input <temporary-json-file>` without `--json` so the CLI reads the complete document reliably, writes self-contained HTML, and opens it in the default browser. The CLI also supports `mate report --input -` when JSON is piped to stdin; if stdin delivery reports empty or truncated JSON, switch to a temporary JSON file rather than retrying the same transport. If validation fails, fix the report document and retry; do not fall back to an inline explanation or JSON-only delivery.
86
+
87
+ Never hand-write an HTML file, never start a server, and never open a browser by any other means. `mate report` is the only browser surface.
88
+
89
+ ## Assembling The Report
90
+
91
+ Build a JSON document that conforms to the `mate-create-report` contract, then hand it over:
92
+
93
+ 1. Serialize the complete document to an OS temporary JSON file outside both repositories. Do not place the report input in the Working Repository or Companion Repository.
94
+ 2. Run `mate report --input <temporary-json-file>`.
95
+
96
+ A report assembled by this skill carries:
97
+
98
+ - `metadata` naming the subject and a neutral repository label, never a local path
99
+ - one `diagram` section for the structure or flow at issue, even when the visual is small
100
+ - use a `mermaid` payload for runtime control flow, call trees, data flow, or component relationships; use `text` only when a sketch or pseudocode is clearer
101
+ - one `diff` section carrying the unified diff, when there is something to diff
102
+ - a short `text` section next to each visual, saying what the reader should notice
103
+
104
+ A `diagram` section carries exactly one payload: `mermaid` for diagram source the report draws as a picture, or `text` for a monospace ASCII sketch or pseudocode block. A `diff` section carries a unified `patch` string.
105
+
106
+ ```json
107
+ {
108
+ "id": "structure",
109
+ "title": "Report rendering",
110
+ "type": "diagram",
111
+ "mermaid": "classDiagram\n Renderer --> Highlight : uses"
112
+ }
113
+ ```
114
+
115
+ ```json
116
+ {
117
+ "id": "changes",
118
+ "title": "What changed",
119
+ "type": "diff",
120
+ "patch": "diff --git a/src/acme.ts b/src/acme.ts\n..."
121
+ }
122
+ ```
123
+
124
+ If the document fails contract validation, report the diagnostic and correct the document. Do not fall back to hand-written HTML.
125
+
126
+ ## Mermaid Authoring Rules
127
+
128
+ - Use Mermaid whenever the user asks for a diagram or the subject has meaningful control flow, sequencing, lifecycle, or relationships. A `text` payload is a deliberate fallback, not the default for runtime flows.
129
+ - Pick the diagram type that matches the question: `classDiagram` for structure, `sequenceDiagram` for interaction between components, `stateDiagram-v2` for lifecycles, `flowchart TD` for control flow and call trees.
130
+ - Keep labels short enough to read at report width. A label that needs a sentence belongs in the neighbouring `text` section.
131
+ - Do not use ELK-only layouts or math labels. The report inlines the self-contained mermaid bundle, which may not carry those chunks.
132
+ - Pair each Mermaid visual with a short neighbouring `text` section that calls out the ownership boundary or invariant the reader should notice.
133
+ - Prefer the `text` payload for pseudocode, shallow file trees, or cases where a picture would genuinely obscure the point.
134
+
135
+ ## Reviewing An Applied Change
136
+
137
+ - Read the change scope before drawing it, then diagram the structure or flow the change establishes - not the structure it replaced.
138
+ - Take the diff from `git diff` in the Working Repository. Keep only repository-relative paths in the patch, default to the working tree, and ask the user when their intent is a committed range instead.
139
+ - When the inspected scope has no changes, say so and omit the `diff` section. Never invent a patch.
@@ -0,0 +1,503 @@
1
+ ---
2
+ name: mate-simplify-code
3
+ description: Simplifies code for clarity. Use when refactoring code for clarity without changing behavior. Use when code works but is harder to read, maintain, or extend than it should be. Use when reviewing code that has accumulated unnecessary complexity.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Mate Simplify Code
8
+
9
+ > Inspired by [Addy Osmani's code-simplification skill](https://github.com/addyosmani/agent-skills/tree/main/skills/code-simplification) and adapted here as a Mate process-driven skill.
10
+
11
+ ## Overview
12
+
13
+ Simplify code by reducing complexity while preserving exact behavior. The goal is not fewer lines — it's code that is easier to read, understand, modify, and debug. Every simplification must pass a simple test: "Would a new team member understand this faster than the original?"
14
+
15
+ ## Mate Workflow
16
+
17
+ - Inspect the requested scope and its callers, callees, and tests before editing. Use the available code-graph tools before broad source scans.
18
+ - Keep the refactor limited to the requested scope; do not make unrelated cleanup changes.
19
+ - Run the relevant tests after each simplification. Do not modify tests merely to make a refactor pass.
20
+ - Format touched files with the project's own formatter — detect it first (see Principle 2); never run a formatter the project has not adopted.
21
+ - Run `mate cap index --tokensave` after code changes.
22
+ - Never commit, push, or create a pull request unless the user explicitly asks.
23
+
24
+ ## When to Use
25
+
26
+ - After a feature is working and tests pass, but the implementation feels heavier than it needs to be
27
+ - During code review when readability or complexity issues are flagged
28
+ - When you encounter deeply nested logic, long functions, or unclear names
29
+ - When refactoring code written under time pressure
30
+ - When consolidating related logic scattered across files
31
+ - After merging changes that introduced duplication or inconsistency
32
+
33
+ **When NOT to use:**
34
+
35
+ - Code is already clean and readable — don't simplify for the sake of it
36
+ - You don't understand what the code does yet — comprehend before you simplify
37
+ - The code is performance-critical and the "simpler" version would be measurably slower
38
+ - You're about to rewrite the module entirely — simplifying throwaway code wastes effort
39
+
40
+ ## The Five Principles
41
+
42
+ ### 1. Preserve Behavior Exactly
43
+
44
+ Don't change what the code does — only how it expresses it. All inputs, outputs, side effects, error behavior, and edge cases must remain identical. If you're not sure a simplification preserves behavior, don't make it.
45
+
46
+ ```
47
+ ASK BEFORE EVERY CHANGE:
48
+ → Does this produce the same output for every input?
49
+ → Does this maintain the same error behavior?
50
+ → Does this preserve the same side effects and ordering?
51
+ → Do all existing tests still pass without modification?
52
+ ```
53
+
54
+ ### 2. Follow Project Conventions
55
+
56
+ Simplification means making code more consistent with the codebase, not imposing external preferences. Before simplifying:
57
+
58
+ ```
59
+ 1. Read CLAUDE.md / project conventions
60
+ 2. Study how neighboring code handles similar patterns
61
+ 3. Match the project's style for:
62
+ - Import ordering and module system
63
+ - Function declaration style
64
+ - Naming conventions
65
+ - Error handling patterns
66
+ - Type annotation depth
67
+ ```
68
+
69
+ Simplification that breaks project consistency is not simplification — it's churn.
70
+
71
+ **Formatting is a project convention, not yours.** Never reach for a formatter by habit. Reformatting with a tool the project has not adopted rewrites lines you never touched and buries the refactor in noise.
72
+
73
+ Detect the formatter before formatting anything — first match wins:
74
+
75
+ | Signal in the repo | Formatter to run |
76
+ | --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
77
+ | `format` / `fmt` script in `package.json`, `Makefile`, `justfile`, `Taskfile.yml` | Run that script — it encodes the project's intent and needs no further detection |
78
+ | `biome.json` / `biome.jsonc` | `biome format --write` |
79
+ | `.oxfmtrc.json`, or `oxfmt` in devDependencies | `oxfmt` |
80
+ | `dprint.json` / `.dprint.jsonc` | `dprint fmt` |
81
+ | `.prettierrc*`, `prettier.config.*`, or a `prettier` key in `package.json` | `prettier --write` |
82
+ | `rustfmt.toml`, or any Cargo crate | `cargo fmt` |
83
+ | `[tool.ruff]` in `pyproject.toml` | `ruff format` |
84
+ | `[tool.black]` in `pyproject.toml` | `black` |
85
+ | Go module | `gofmt -w` (or `goimports -w` if already used) |
86
+ | Only `.editorconfig`, or no signal at all | Do not format — match the surrounding style by hand |
87
+
88
+ Rules:
89
+
90
+ - Invoke the project's pinned local binary through its package manager or runner, inferred from the lockfile (`bun`, `pnpm`, `yarn`, `npm`) — never a global install and never an `npx`-downloaded version, which may differ from the one that formatted the committed code.
91
+ - If several formatters are configured, the `format` script decides. If there is no script and the signals conflict, ask which is canonical instead of picking one.
92
+ - Format only the files you changed. A repo-wide format pass is a separate change with its own diff.
93
+ - If the formatter rewrites far more than the lines you touched, its config disagrees with the committed code. Stop, revert the formatting, and report it — do not fold that drift into a simplification change.
94
+
95
+ ### 3. Prefer Clarity Over Cleverness
96
+
97
+ Explicit code is better than compact code when the compact version requires a mental pause to parse.
98
+
99
+ ```typescript
100
+ // UNCLEAR: Dense ternary chain
101
+ const label = isNew ? "New" : isUpdated ? "Updated" : isArchived ? "Archived" : "Active";
102
+
103
+ // CLEAR: Readable mapping
104
+ function getStatusLabel(item: Item): string {
105
+ if (item.isNew) return "New";
106
+ if (item.isUpdated) return "Updated";
107
+ if (item.isArchived) return "Archived";
108
+ return "Active";
109
+ }
110
+ ```
111
+
112
+ ```typescript
113
+ // UNCLEAR: Chained reduces with inline logic
114
+ const result = items.reduce(
115
+ (acc, item) => ({
116
+ ...acc,
117
+ [item.id]: { ...acc[item.id], count: (acc[item.id]?.count ?? 0) + 1 },
118
+ }),
119
+ {},
120
+ );
121
+
122
+ // CLEAR: Named intermediate step
123
+ const countById = new Map<string, number>();
124
+ for (const item of items) {
125
+ countById.set(item.id, (countById.get(item.id) ?? 0) + 1);
126
+ }
127
+ ```
128
+
129
+ ### 4. Maintain Balance
130
+
131
+ Simplification has a failure mode: over-simplification. Watch for these traps:
132
+
133
+ - **Inlining too aggressively** — removing a helper that gave a concept a name makes the call site harder to read
134
+ - **Combining unrelated logic** — two simple functions merged into one complex function is not simpler
135
+ - **Removing "unnecessary" abstraction** — some abstractions exist for extensibility or testability, not complexity
136
+ - **Optimizing for line count** — fewer lines is not the goal; easier comprehension is
137
+
138
+ ### 5. Scope to What Changed
139
+
140
+ Default to simplifying recently modified code. Avoid drive-by refactors of unrelated code unless explicitly asked to broaden scope. Unscoped simplification creates noise in diffs and risks unintended regressions.
141
+
142
+ ## The Simplification Process
143
+
144
+ ### Step 1: Understand Before Touching (Chesterton's Fence)
145
+
146
+ Before changing or removing anything, understand why it exists. This is Chesterton's Fence: if you see a fence across a road and don't understand why it's there, don't tear it down. First understand the reason, then decide if the reason still applies.
147
+
148
+ ```
149
+ BEFORE SIMPLIFYING, ANSWER:
150
+ - What is this code's responsibility?
151
+ - What calls it? What does it call?
152
+ - What are the edge cases and error paths?
153
+ - Are there tests that define the expected behavior?
154
+ - Why might it have been written this way? (Performance? Platform constraint? Historical reason?)
155
+ - Check git blame: what was the original context for this code?
156
+ ```
157
+
158
+ If you can't answer these, you're not ready to simplify. Read more context first.
159
+
160
+ ### Step 2: Identify Simplification Opportunities
161
+
162
+ #### Step 2a: Mechanical pre-scan (required)
163
+
164
+ Run the scan before reading source. Its output **is** the candidate list — a pattern
165
+ nobody looked for is a pattern nobody finds, and prose tables alone are read with a
166
+ bias toward "this file looks fine".
167
+
168
+ ```
169
+ PRE-SCAN THE REQUESTED SCOPE:
170
+ 1. Code graph, if available — tokensave_module_api (export surface vs. real
171
+ consumers), tokensave_dead_code, tokensave_similar (near-duplicate bodies),
172
+ tokensave_complexity / tokensave_largest (nesting, long functions)
173
+ 2. Unused-export detector, if the project already has one configured
174
+ (knip, ts-prune, eslint import-x/no-unused-modules, Python vulture)
175
+ 3. Grep for consumers when neither is available — including test files,
176
+ and including the bare symbol name, not just import statements
177
+ ```
178
+
179
+ Do not skip the pre-scan because the file "looks clean". Judgment applies to the
180
+ candidates it produces, not to whether to produce them. Report candidates you
181
+ deliberately leave alone, with the reason.
182
+
183
+ **A pre-scan hit is a question, not a verdict.** These tools find symbols nothing
184
+ _imports_; they cannot see symbols resolved by name — framework exports, reflective
185
+ lookups, config-referenced files. Every "unused export" candidate must clear the
186
+ **Behavior-preservation rules for the module surface** below before you touch it.
187
+ Read that section before acting on this list, not after.
188
+
189
+ Then scan for these patterns — each one is a concrete signal, not a vague smell:
190
+
191
+ **Structural complexity:**
192
+
193
+ | Pattern | Signal | Simplification |
194
+ | -------------------------- | ---------------------------------- | --------------------------------------------------------- |
195
+ | Deep nesting (3+ levels) | Hard to follow control flow | Extract conditions into guard clauses or helper functions |
196
+ | Long functions (50+ lines) | Multiple responsibilities | Split into focused functions with descriptive names |
197
+ | Nested ternaries | Requires mental stack to parse | Replace with if/else chains, switch, or lookup objects |
198
+ | Boolean parameter flags | `doThing(true, false, true)` | Replace with options objects or separate functions |
199
+ | Repeated conditionals | Same `if` check in multiple places | Extract to a well-named predicate function |
200
+
201
+ **Naming and readability:**
202
+
203
+ | Pattern | Signal | Simplification |
204
+ | -------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------ |
205
+ | Generic names | `data`, `result`, `temp`, `val`, `item` | Rename to describe the content: `userProfile`, `validationErrors` |
206
+ | Abbreviated names | `usr`, `cfg`, `btn`, `evt` | Use full words unless the abbreviation is universal (`id`, `url`, `api`) |
207
+ | Misleading names | Function named `get` that also mutates state | Rename to reflect actual behavior |
208
+ | Comments explaining "what" | `// increment counter` above `count++` | Delete the comment — the code is clear enough |
209
+ | Comments explaining "why" | `// Retry because the API is flaky under load` | Keep these — they carry intent the code can't express |
210
+
211
+ **Redundancy:**
212
+
213
+ | Pattern | Signal | Simplification |
214
+ | ------------------------- | ------------------------------------------------------------ | --------------------------------------------------------- |
215
+ | Duplicated logic | Same 5+ lines in multiple places | Extract to a shared function |
216
+ | Dead code | Unreachable branches, unused variables, commented-out blocks | Remove (after confirming it's truly dead) |
217
+ | Unnecessary abstractions | Wrapper that adds no value | Inline the wrapper, call the underlying function directly |
218
+ | Over-engineered patterns | Factory-for-a-factory, strategy-with-one-strategy | Replace with the simple direct approach |
219
+ | Redundant type assertions | Casting to a type that's already inferred | Remove the assertion |
220
+
221
+ **Module surface:**
222
+
223
+ A module's public API should match what is actually consumed. Surface bloat is
224
+ invisible inside a single file — it only shows up when you compare exports against
225
+ callers, which is what the Step 2a pre-scan does.
226
+
227
+ | Pattern | Signal | Simplification |
228
+ | ----------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------- |
229
+ | Over-exported module | Export has no consumer outside its own file | Drop `export` — keep the symbol, narrow its visibility |
230
+ | Leaked intermediate types | Type exported only to annotate a private helper or internal step | Unexport — but only if it appears in no exported signature |
231
+ | Speculative public API | Export added "for later" with no caller | Unexport first; delete only per the dead-code rule above |
232
+ | Single-consumer bag-of-things | Many exports, exactly one importer | **Propose only** — API reshape, not a visibility change (see below) |
233
+ | Re-export passthrough | Barrel file that only forwards a single symbol | **Propose only** — changes module resolution repo-wide (see below) |
234
+
235
+ Narrowing visibility is not inlining — the named helper survives, so this does not
236
+ conflict with the over-simplification traps in Principle 4. Only inline a helper when
237
+ the name itself carries no meaning at the call site.
238
+
239
+ #### Behavior-preservation rules for the module surface
240
+
241
+ Principle 1 governs this table without exception. Visibility is not behavior — until
242
+ something resolves the symbol by name rather than by import. Then it is.
243
+
244
+ **Unexport, don't delete.** In a type-checked project, removing `export` is verified by
245
+ the compiler: any importer becomes a compile error, so a mistake fails loudly and
246
+ immediately. Deleting the symbol has no such net. Narrow visibility as its own step;
247
+ treat deletion as a separate decision under the dead-code rule, not as part of the
248
+ same edit.
249
+
250
+ **That safety net requires a type checker that actually runs over the file.** Plain
251
+ JavaScript, TypeScript with `checkJs` off, or a project with no `tsc --noEmit` gate
252
+ gets no compile error — a broken import fails at runtime instead, possibly only on one
253
+ code path. In an unchecked project, do not unexport on pre-scan evidence alone: confirm
254
+ each consumer by grep first, or leave the export and report it.
255
+
256
+ **A test is a consumer.** If the only importer is a test file, the export is load-
257
+ bearing — unexporting it breaks the test, and Principle 1 forbids editing tests to make
258
+ a refactor pass. Leave it exported. "No production consumer" is not "no consumer";
259
+ report it as a possible test-only seam instead of acting on it.
260
+
261
+ **A type used in an exported signature stays exported.** If an exported function takes
262
+ or returns the type, consumers need to name it — unexporting breaks call sites and
263
+ declaration emit even though nothing imports the type directly today. Only unexport a
264
+ type that appears exclusively in module-private positions.
265
+
266
+ **Never touch an export the framework resolves by name.** These have no importer
267
+ anywhere by design, so "no consumer found" is meaningless for them — the pre-scan and
268
+ every unused-export detector will report them as dead, and they are not:
269
+
270
+ ```
271
+ FRAMEWORK-RESOLVED — OUT OF SCOPE, DO NOT UNEXPORT OR RENAME:
272
+ - Next.js app router: default, metadata, generateMetadata, generateStaticParams,
273
+ revalidate, dynamic, runtime, viewport, route handlers (GET/POST/...),
274
+ middleware, error/loading/not-found boundaries
275
+ - Next.js pages router: default, getServerSideProps, getStaticProps, getStaticPaths
276
+ - Test and story files: Storybook CSF (default + named story exports), fixtures,
277
+ setup files referenced by config rather than imported
278
+ - Package entry points: anything reachable from package.json exports/main/types,
279
+ or from a documented public API
280
+ - Config-referenced modules: paths named in tsconfig, bundler, or tool config
281
+ - Reflective resolution: dynamic import() with a computed specifier, glob imports
282
+ (import.meta.glob, require.context), DI containers, decorators, plugin registries
283
+ ```
284
+
285
+ **Propose-only rows are not yours to apply.** Collapsing a multi-export module to one
286
+ entry point, or deleting a barrel, is an API reshape: it rewrites call sites in files
287
+ outside the requested scope, changes module resolution for deep importers, and is a
288
+ design decision rather than a behavior-preserving edit. Both collide with Principle 5.
289
+ Describe the change and the affected files, then stop and let the user decide — the
290
+ same treatment the prop-drilling case gets under React guidance.
291
+
292
+ **When the pre-scan flags a symbol you cannot prove is unreferenced, leave it and say
293
+ so.** An export you were unsure about and kept costs a line of explanation. An export
294
+ you removed on a guess costs a production incident. Unverifiable candidates are
295
+ reported, not acted on.
296
+
297
+ ```
298
+ BEFORE REMOVING ANY export, ALL MUST HOLD:
299
+ [ ] Not framework-resolved (checked against the list above)
300
+ [ ] Not reachable from a package entry point or documented API
301
+ [ ] No importer anywhere — including tests, stories, and config
302
+ [ ] If a type: appears in no exported signature
303
+ [ ] A type checker covers this file and will run before the change is accepted
304
+ [ ] The symbol name greps clean outside its own file
305
+ Any box unchecked → report the candidate, do not touch it.
306
+ ```
307
+
308
+ ### Step 3: Apply Changes Incrementally
309
+
310
+ Make one simplification at a time. Run tests after each change. **Submit refactoring changes separately from feature or bug fix changes.** A PR that refactors and adds a feature is two PRs — split them.
311
+
312
+ ```
313
+ FOR EACH SIMPLIFICATION:
314
+ 1. Make the change
315
+ 2. Run the type checker / compiler, then the test suite
316
+ (visibility changes surface as compile errors, not test failures — a green
317
+ test run alone does not prove an unexport was safe)
318
+ 3. If both pass → continue to the next simplification; commit only if the user explicitly asks
319
+ 4. If either fails → revert and reconsider
320
+ ```
321
+
322
+ Avoid batching multiple simplifications into a single untested change. If something breaks, you need to know which simplification caused it.
323
+
324
+ **The Rule of 500:** If a refactoring would touch more than 500 lines, invest in automation (codemods or AST transforms) rather than making the changes by hand. Manual edits at that scale are error-prone and exhausting to review.
325
+
326
+ ### Step 4: Verify the Result
327
+
328
+ Run the project's formatter (detected in Principle 2) over the files you touched, then re-run the type checker and test suite — formatting must never be the last unverified step.
329
+
330
+ Then step back and evaluate the whole:
331
+
332
+ ```
333
+ COMPARE BEFORE AND AFTER:
334
+ - Is the simplified version genuinely easier to understand?
335
+ - Did you introduce any new patterns inconsistent with the codebase?
336
+ - Is the diff clean and reviewable?
337
+ - Would a teammate approve this change?
338
+ ```
339
+
340
+ If the "simplified" version is harder to understand or review, revert. Not every simplification attempt succeeds.
341
+
342
+ ## Language-Specific Guidance
343
+
344
+ ### TypeScript / JavaScript
345
+
346
+ ```typescript
347
+ // SIMPLIFY: Unnecessary async wrapper
348
+ // Before
349
+ async function getUser(id: string): Promise<User> {
350
+ return await userService.findById(id);
351
+ }
352
+ // After
353
+ function getUser(id: string): Promise<User> {
354
+ return userService.findById(id);
355
+ }
356
+
357
+ // SIMPLIFY: Verbose conditional assignment
358
+ // Before
359
+ let displayName: string;
360
+ if (user.nickname) {
361
+ displayName = user.nickname;
362
+ } else {
363
+ displayName = user.fullName;
364
+ }
365
+ // After
366
+ const displayName = user.nickname || user.fullName;
367
+
368
+ // SIMPLIFY: Manual array building
369
+ // Before
370
+ const activeUsers: User[] = [];
371
+ for (const user of users) {
372
+ if (user.isActive) {
373
+ activeUsers.push(user);
374
+ }
375
+ }
376
+ // After
377
+ const activeUsers = users.filter((user) => user.isActive);
378
+
379
+ // SIMPLIFY: Redundant boolean return
380
+ // Before
381
+ function isValid(input: string): boolean {
382
+ if (input.length > 0 && input.length < 100) {
383
+ return true;
384
+ }
385
+ return false;
386
+ }
387
+ // After
388
+ function isValid(input: string): boolean {
389
+ return input.length > 0 && input.length < 100;
390
+ }
391
+ ```
392
+
393
+ ### Python
394
+
395
+ ```python
396
+ # SIMPLIFY: Verbose dictionary building
397
+ # Before
398
+ result = {}
399
+ for item in items:
400
+ result[item.id] = item.name
401
+ # After
402
+ result = {item.id: item.name for item in items}
403
+
404
+ # SIMPLIFY: Nested conditionals with early return
405
+ # Before
406
+ def process(data):
407
+ if data is not None:
408
+ if data.is_valid():
409
+ if data.has_permission():
410
+ return do_work(data)
411
+ else:
412
+ raise PermissionError("No permission")
413
+ else:
414
+ raise ValueError("Invalid data")
415
+ else:
416
+ raise TypeError("Data is None")
417
+ # After
418
+ def process(data):
419
+ if data is None:
420
+ raise TypeError("Data is None")
421
+ if not data.is_valid():
422
+ raise ValueError("Invalid data")
423
+ if not data.has_permission():
424
+ raise PermissionError("No permission")
425
+ return do_work(data)
426
+ ```
427
+
428
+ ### React / JSX
429
+
430
+ ```tsx
431
+ // SIMPLIFY: Verbose conditional rendering
432
+ // Before
433
+ function UserBadge({ user }: Props) {
434
+ if (user.isAdmin) {
435
+ return <Badge variant="admin">Admin</Badge>;
436
+ } else {
437
+ return <Badge variant="default">User</Badge>;
438
+ }
439
+ }
440
+ // After
441
+ function UserBadge({ user }: Props) {
442
+ const variant = user.isAdmin ? "admin" : "default";
443
+ const label = user.isAdmin ? "Admin" : "User";
444
+ return <Badge variant={variant}>{label}</Badge>;
445
+ }
446
+
447
+ // SIMPLIFY: Prop drilling through intermediate components
448
+ // Before — consider whether context or composition solves this better.
449
+ // This is a judgment call — flag it, don't auto-refactor.
450
+ ```
451
+
452
+ ## Common Rationalizations
453
+
454
+ | Rationalization | Reality |
455
+ | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
456
+ | "It's working, no need to touch it" | Working code that's hard to read will be hard to fix when it breaks. Simplifying now saves time on every future change. |
457
+ | "Fewer lines is always simpler" | A 1-line nested ternary is not simpler than a 5-line if/else. Simplicity is about comprehension speed, not line count. |
458
+ | "I'll just quickly simplify this unrelated code too" | Unscoped simplification creates noisy diffs and risks regressions in code you didn't intend to change. Stay focused. |
459
+ | "The types make it self-documenting" | Types document structure, not intent. A well-named function explains _why_ better than a type signature explains _what_. |
460
+ | "This abstraction might be useful later" | Don't preserve speculative abstractions. If it's not used now, it's complexity without value. Remove it and re-add when needed. |
461
+ | "The original author must have had a reason" | Maybe. Check git blame — apply Chesterton's Fence. But accumulated complexity often has no reason; it's just the residue of iteration under pressure. |
462
+ | "I'll refactor while adding this feature" | Separate refactoring from feature work. Mixed changes are harder to review, revert, and understand in history. |
463
+
464
+ ## Red Flags
465
+
466
+ - Simplification that requires modifying tests to pass (you likely changed behavior)
467
+ - "Simplified" code that is longer and harder to follow than the original
468
+ - Renaming things to match your preferences rather than project conventions
469
+ - Removing error handling because "it makes the code cleaner"
470
+ - Simplifying code you don't fully understand
471
+ - Batching many simplifications into one large, hard-to-review commit
472
+ - Refactoring code outside the scope of the current task without being asked
473
+ - Deleting an export because a tool reported it unused, without checking whether the
474
+ framework resolves it by name (`page.tsx`, `route.ts`, stories, config-referenced files)
475
+ - Acting on a pre-scan candidate you could not verify — report it instead
476
+ - Treating a green test run as proof a visibility change was safe without a type check
477
+ - Unexporting a symbol whose only importer is a test, then editing the test to match
478
+ - Applying a propose-only row (module collapse, barrel deletion) without user sign-off
479
+ - Trusting "the compiler would catch it" in a project the type checker does not cover
480
+
481
+ ## Verification
482
+
483
+ After completing a simplification pass:
484
+
485
+ - [ ] All existing tests pass without modification
486
+ - [ ] Build succeeds with no new warnings
487
+ - [ ] The project's own formatter was detected and run on the touched files — no formatter the project has not adopted was used
488
+ - [ ] Formatting touched only the changed files (no repo-wide reformat mixed in)
489
+ - [ ] Linter passes (no style regressions)
490
+ - [ ] Each simplification is a reviewable, incremental change
491
+ - [ ] The diff is clean — no unrelated changes mixed in
492
+ - [ ] Simplified code follows project conventions (checked against CLAUDE.md or equivalent)
493
+ - [ ] No error handling was removed or weakened
494
+ - [ ] No dead code was left behind (unused imports, unreachable branches)
495
+ - [ ] The Step 2a pre-scan was run, and every candidate is either fixed or explained
496
+ - [ ] Export surface matches actual consumers — nothing exported without a caller outside its file
497
+ - [ ] Type checker passes — no unexport broke an importer
498
+ - [ ] No framework-resolved export was unexported, renamed, or deleted
499
+ - [ ] Every removed `export` cleared all boxes of the pre-removal checklist
500
+ - [ ] No test was edited to accommodate a visibility change
501
+ - [ ] Propose-only findings were reported, not applied
502
+ - [ ] Behavior is bit-for-bit identical: same inputs, outputs, side effects, error paths
503
+ - [ ] A teammate or review agent would approve the change as a net improvement