@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,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
@@ -0,0 +1,32 @@
1
+ # Report assets
2
+
3
+ Vendored browser assets inlined into `mate report` HTML output. Nothing here is
4
+ imported as a module or linted, formatted, or typechecked — these files are read
5
+ as strings at render time.
6
+
7
+ ## `mermaid.min.js`
8
+
9
+ | | |
10
+ | -------- | ------------------------------------------------------------------------ |
11
+ | Upstream | [`mermaid`](https://www.npmjs.com/package/mermaid) `dist/mermaid.min.js` |
12
+ | Version | `12.0.0` |
13
+ | License | MIT — see `mermaid.LICENSE` |
14
+ | Form | esbuild IIFE, self-contained, zero dynamic `import()` calls |
15
+ | Size | 5.58 MB raw |
16
+
17
+ The IIFE build is vendored rather than installed because `@uniqbit/mate-core`
18
+ publishes raw `src/` with no build step, and only mermaid's ESM build lazily
19
+ loads per-diagram chunks. It is inlined into a report document only when that
20
+ report carries a mermaid payload.
21
+
22
+ ### Bumping
23
+
24
+ ```sh
25
+ npm pack mermaid@<version>
26
+ tar -xzf mermaid-<version>.tgz package/dist/mermaid.min.js package/LICENSE
27
+ cp package/dist/mermaid.min.js packages/mate-core/src/templates/report-assets/mermaid.min.js
28
+ cp package/LICENSE packages/mate-core/src/templates/report-assets/mermaid.LICENSE
29
+ ```
30
+
31
+ Update the version above, confirm `grep -c 'import('` on the bundle stays `0`,
32
+ then re-run the report renderer tests.
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 - 2022 Knut Sveidqvist
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.