@rockysoft/opencode-hybrid-workflow 1.0.1 → 1.1.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.
package/README.md CHANGED
@@ -4,6 +4,8 @@ Let your strongest models plan, design and review. Let your implementation model
4
4
 
5
5
  One plugin. One or two reviewers. Configure everything inside OpenCode.
6
6
 
7
+ **Optional Impeccable guidance:** your design lead decides; your implementation model builds the approved design. Set it up below.
8
+
7
9
  **Requirements:** OpenCode 2, Node.js 22+, Git and Python 3. Windows and Linux; experimental macOS support.
8
10
 
9
11
  ## 1. Install
@@ -114,6 +116,19 @@ Type **`/hybrid`** or click **Hybrid Workflow settings** whenever you want to ch
114
116
 
115
117
  ## Optional tools
116
118
 
119
+ <details>
120
+ <summary><strong>Use Impeccable design guidance</strong></summary>
121
+
122
+ Choose **/hybrid → Design guidance → Impeccable**. Nothing else needs installing.
123
+
124
+ ![Choose Impeccable inside OpenCode](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@1.1.0/docs/images/22-design-guidance.png)
125
+
126
+ For one task, ask **“Use Impeccable to redesign this page”**, or use `/hybrid-impeccable polish the checkout`.
127
+
128
+ This supplies Impeccable's design guidance to your selected premium lead and reviewers. It does not run Impeccable's separate engine, hooks or audit commands. [Supported scope](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@1.1.0/docs/impeccable.md).
129
+
130
+ </details>
131
+
117
132
  <details>
118
133
  <summary><strong>Let reviewers inspect your app in a browser</strong></summary>
119
134
 
@@ -18,3 +18,8 @@ their respective files; the root license does not replace them. No user
18
18
  credentials, project memories, session logs or runtime reports are included.
19
19
 
20
20
  - Runtime dependency `diff` (jsdiff) 9.0.0, BSD-3-Clause, https://github.com/kpdecker/jsdiff. Its license is distributed with the dependency.
21
+ # Impeccable guidance
22
+
23
+ Selected unmodified Markdown references from Impeccable 4.8.0 by Paul Bakaus and contributors, commit `9fadc41729c38fd47d7ae622b501558653f9d2e9`, https://github.com/pbakaus/impeccable.
24
+ Licensed under Apache-2.0; full license and provenance are in `assets/integrations/impeccable/`. The upstream executable engine, hooks and command runner are not included.
25
+
@@ -0,0 +1,191 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to the Licensor for inclusion in the Work by the copyright
52
+ owner or by an individual or Legal Entity authorized to submit on
53
+ behalf of the copyright owner. For the purposes of this definition,
54
+ "submitted" means any form of electronic, verbal, or written
55
+ communication sent to the Licensor or its representatives, including
56
+ but not limited to communication on electronic mailing lists, source
57
+ code control systems, and issue tracking systems that are managed by,
58
+ or on behalf of, the Licensor for the purpose of discussing and
59
+ improving the Work, but excluding communication that is conspicuously
60
+ marked or otherwise designated in writing by the copyright owner as
61
+ "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ Copyright 2025 Paul Bakaus
180
+
181
+ Licensed under the Apache License, Version 2.0 (the "License");
182
+ you may not use this file except in compliance with the License.
183
+ You may obtain a copy of the License at
184
+
185
+ http://www.apache.org/licenses/LICENSE-2.0
186
+
187
+ Unless required by applicable law or agreed to in writing, software
188
+ distributed under the License is distributed on an "AS IS" BASIS,
189
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190
+ See the License for the specific language governing permissions and
191
+ limitations under the License.
@@ -0,0 +1,11 @@
1
+ # Third-Party Notices
2
+
3
+ This project includes content derived from third-party work, used under the terms of its original license.
4
+
5
+ ## Platform Design Skills
6
+
7
+ The `skill/reference/ios.md` and `skill/reference/android.md` platform reference files are distilled from ehmo's `platform-design-skills` (Apple Human Interface Guidelines and Material Design 3 rules), rewritten in Impeccable's voice.
8
+
9
+ **Original work:** https://github.com/ehmo/platform-design-skills
10
+ **Original license:** MIT
11
+ **Author:** ehmo
@@ -0,0 +1,86 @@
1
+ > **Additional context needed**: existing brand colors.
2
+
3
+ Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it.
4
+
5
+ ---
6
+
7
+ ## Visitor mode
8
+
9
+ - **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it.
10
+ - **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force.
11
+
12
+ ## Audit before choosing
13
+
14
+ Read DESIGN.md, tokens, assets, current themes, and representative states. Identify:
15
+
16
+ - which colors are confirmed brand commitments;
17
+ - current surface, text, action, and semantic roles;
18
+ - places where grayscale obscures hierarchy or state;
19
+ - contrast failures and color-only communication;
20
+ - light/dark or data-visualization requirements;
21
+ - whether the task asks for more color or a new identity.
22
+
23
+ If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred.
24
+
25
+ ## Choose a strategy
26
+
27
+ Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule.
28
+
29
+ Build roles, not a bag of swatches:
30
+
31
+ - canvas and elevated surfaces;
32
+ - primary and secondary text;
33
+ - action, focus, and selection;
34
+ - borders and separators;
35
+ - success, warning, error, and information;
36
+ - data categories or scales when needed.
37
+
38
+ Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association.
39
+
40
+ ## Apply at system scale
41
+
42
+ - Let the strongest color own a deliberate region or role instead of scattering tiny accents.
43
+ - Keep the primary action easy to find; do not spend its color on decoration.
44
+ - Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world.
45
+ - On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray.
46
+ - Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues.
47
+ - For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code.
48
+ - In dark mode, design surface elevation and contrast explicitly; do not invert the light theme mechanically.
49
+ - Define primitive values and semantic tokens when the project has a token system. Theme changes should normally remap semantic roles.
50
+
51
+ Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy.
52
+
53
+ ## Contrast and perception
54
+
55
+ Verify computed foreground/background pairs:
56
+
57
+ | Content | WCAG AA minimum |
58
+ |---|---|
59
+ | body text | 4.5:1 |
60
+ | large text | 3:1 |
61
+ | controls, icons, focus indicators | 3:1 |
62
+
63
+ Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position.
64
+
65
+ When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent.
66
+
67
+ ## Verify
68
+
69
+ - Every color has a stable role or a world-specific atmospheric purpose.
70
+ - Attention lands on the intended action, content, or state.
71
+ - The palette works across quiet, dense, interactive, error, and empty states.
72
+ - Light and dark themes are each composed, not mechanically inverted.
73
+ - Contrast and non-color cues pass in all relevant states.
74
+ - The result is recognizably this product, not a generic “colorful” treatment.
75
+
76
+ When the palette earns its place, hand off to `/impeccable polish` for the final pass.
77
+
78
+ ## Live-mode signature params
79
+
80
+ When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration.
81
+
82
+ ```json
83
+ {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"}
84
+ ```
85
+
86
+ Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract.
@@ -0,0 +1,44 @@
1
+ # Craft floor
2
+
3
+ Load this after the direction is settled, and build without announcing the checklist. A pinned brief or the committed visual world overrides anything here except a line marked as a ban; your own habit does not. When the design hook is active it already enforces the mechanical checks below as you edit: act on its findings instead of re-auditing each rule.
4
+
5
+ ## Verify
6
+
7
+ Each of these is a check on the built result, not an intention. Run them together in each batched look at the render, not as separate screenshot trips; the checks share one render.
8
+
9
+ - **Contrast:** body and placeholder text ≥4.5:1, large text ≥3:1. On colored surfaces tint secondary text from that hue or the foreground; never gray.
10
+ - **Depth:** shadows carry an offset and a soft blur. A zero-offset colored halo is decoration.
11
+ - **Spacing:** tight groups, generous separation, more space above a heading than below it. Read the computed values.
12
+ - **Type:** body measure 65–75ch, display max 6rem, tracking floor -0.04em, balanced headings, obvious scale and weight steps. Run the real copy at every breakpoint and fix what overflows.
13
+ - **Motion:** one authored moment, not scattered effects and not one identical entrance on every section. Exponential ease-out from an already-visible default. Reach past transform and opacity: blur, backdrop-filter, clip-path, mask, and shadow belong to the palette when they stay smooth.
14
+ - **States:** hover, disabled, loading, error, empty. Plus real content, working controls, responsive composition, keyboard focus.
15
+ - **Browser surfaces:** the parts you did not draw still carry the design. Text selection, the caret, custom scrollbars, focus rings, underline offset, and the numerals in tabular data all ship with browser defaults that belong to no design system. Theme them from the palette. This is the cheapest signal that a page was built rather than assembled, and the one models skip most reliably.
16
+ - **Copy:** the product's own language. Controls name their action; errors name the problem and the recovery.
17
+ - **Coverage:** every brief requirement present and findable within seconds.
18
+
19
+ ## Refuse
20
+
21
+ These are the category's defaults, not bans: the brief's own words can earn any of them. Reaching for one when the axis is free means you were not deciding; recognizing that means rewriting the element, not softening it.
22
+
23
+ Page scaffolds:
24
+
25
+ - Same-size cards of icon plus heading plus text as the page structure. Cards are the lazy container; nested cards are always wrong.
26
+ - The hero-metric template: big number, small label, supporting stats, accent.
27
+ - A kicker or eyebrow above a heading. This one is a ban, not a default: no brief earns it back. The heading carries its own weight; delete the label and let the heading speak.
28
+ - Section numbers (01 / 02 / 03) unless the sequence itself carries information the reader needs.
29
+ - A modal for a task that needs neither interruption nor protected focus.
30
+
31
+ Surface habits:
32
+
33
+ - Gradient text. Emphasis comes from weight or size.
34
+ - Glass and blur as decoration rather than as a specific effect.
35
+ - A colored `border-left` or `border-right` above 1px on cards, list items, callouts, or alerts.
36
+ - Hard offset shadows (`box-shadow: 4px 4px 0`) outside a world that is actually neobrutalist. The zero-blur block shadow is a costume, not a depth system; a world that did not choose it never earns it as a default.
37
+ - Sparklines, progress rings, and soft-shadowed rounded rectangles standing in for content.
38
+ - Monospace as a costume for "technical" rather than for code, data, or measurement.
39
+ - A system display face (Impact, Arial Black, the platform sans) as the display voice of an own-world page. Source and self-host a face whose character matches the approved lettering; the closest installed font is a failure, not a fallback.
40
+ - Unicode glyphs or emoji standing in for an icon system. Icons are drawn, from a real library or authored SVG, in one consistent stroke and weight.
41
+ - Geometric masks standing in for organic contours. A circle, polygon, or radial-gradient cutout approximating a photographic subject's edge is the cheap version of the effect and reads worse than omitting it. Derive an alpha matte from the actual image, or produce a cut-out asset.
42
+ - Light or dark picked by category. Pick it from the use scene: who, where, under what ambient light.
43
+
44
+ The floor holds the mechanics; it never picks the direction. With every check green, spend the page on the committed world, and when torn between refined and committed, commit.
@@ -0,0 +1,84 @@
1
+ Layout turns product priority into reading order, grouping, rhythm, and usable space. Diagnose the structural problem before moving boxes.
2
+
3
+ ---
4
+
5
+ ## Visitor mode
6
+
7
+ - **Persuade + Experience:** composition may be asymmetric, fluid, or intentionally disruptive when the selected world earns it.
8
+ - **Operate + Read:** predictable structure, stable density, and navigable linearity are affordances.
9
+ - **Native:** follow [ios.md](ios.md) or [android.md](android.md) for navigation, insets, adaptation, and touch targets.
10
+
11
+ Preserve the established visual world. A layout command changes structure inside it; identity replacement belongs to [new-work.md](new-work.md).
12
+
13
+ ## Two isolated assessments
14
+
15
+ When a sub-agent tool is available and permitted, run these independently; otherwise run them yourself in this order.
16
+
17
+ 1. **Layout assessment:** inspect representative states and viewports. Answer every question below with rendered or source evidence:
18
+ - **Reading order:** Apply the squint test. With detail blurred, can you still identify the primary element, the secondary element, and the major groups in order?
19
+ - **Grouping:** Are related items close and distinct groups separated, or are containers compensating for weak proximity?
20
+ - **Rhythm:** Do tight and generous intervals create a deliberate cadence, or is one spacing value repeated until everything has equal weight?
21
+ - **Structure:** Does the topology match the content and task? Are repeated cards, columns, or sections genuinely equivalent, or merely a framework default?
22
+ - **Density:** Does the amount of information per region fit use frequency, decision complexity, and visitor mode?
23
+ - **Adaptation:** At narrow, intermediate, wide, zoomed, and localized states, what reorders, collapses, wraps, scrolls, or remains fixed? Does DOM and focus order still agree with the visual order?
24
+ - **Extremes:** Do long content, empty states, overlays, sticky elements, safe areas, and small touch targets expose structural failures?
25
+ 2. **Mechanical scan:** run:
26
+
27
+ ```bash
28
+ .opencode/skills/impeccable/scripts/impeccable detect --json --scope layout [target files or dirs]
29
+ ```
30
+
31
+ Also inspect arbitrary spacing, overflow, stacking, and container behavior the detector cannot resolve. Keep mechanical evidence out of the first assessment, then synthesize both passes before editing. A clean scan cannot prove hierarchy or rhythm.
32
+
33
+ ## Set the spatial thesis
34
+
35
+ Before editing, name:
36
+
37
+ - the primary reading or task path;
38
+ - what belongs together and what must separate;
39
+ - which element leads and which supports;
40
+ - the intended density and spacing rhythm;
41
+ - how the structure changes across containers, viewports, input modes, and content extremes.
42
+
43
+ Choose the simplest structural model that expresses those relationships. Use layout primitives according to the relationships they control, and name reusable spacing and container roles semantically.
44
+
45
+ ## Apply
46
+
47
+ - Group by meaning. Use proximity before adding containers or decoration.
48
+ - Create rhythm through deliberate contrast between tight and generous intervals.
49
+ - Use a documented spacing scale rather than one-off values. A 4-unit base usually provides the useful middle steps that an 8-only scale misses.
50
+ - Let hierarchy follow product priority, not framework defaults.
51
+ - Keep distinct content visually distinct without turning every group into an isolated component.
52
+ - Make responsive behavior structural: reorder, collapse, reflow, or reveal based on what remains important.
53
+ - Prefer container-aware components when the same component appears in different contexts.
54
+ - Use `gap` for sibling rhythm when it expresses the relationship more directly than child margins.
55
+ - Keep touch targets usable even when their visible marks are small.
56
+ - Use depth only when it clarifies state or hierarchy.
57
+ - Make optical corrections only after inspecting the rendered result.
58
+
59
+ Variation is not a goal by itself. Repetition should support recognition; break it only when content or priority changes.
60
+
61
+ ## Verify
62
+
63
+ - The squint test still reveals the primary, secondary, and major groups in order.
64
+ - The reading and task path remains clear at every supported size.
65
+ - Related content groups naturally; unrelated content does not blur together.
66
+ - Tight and generous spacing create intentional rhythm instead of monotonous repetition.
67
+ - Density matches use frequency and content complexity.
68
+ - Long text, empty states, localization, zoom, and dynamic content do not break the structure.
69
+ - Keyboard, touch, and assistive-technology order agree with the visual order.
70
+ - The final mechanical scan has no unexplained findings.
71
+
72
+ Answer each item with rendered or source evidence, then rerun the scan. Do not substitute a bare “yes” for verification.
73
+
74
+ When the structure holds, hand off to `/impeccable polish`.
75
+
76
+ ## Live-mode signature params
77
+
78
+ Every variant declares a coarse `density` parameter and authors spacing against `var(--p-density, 1)`.
79
+
80
+ ```json
81
+ {"id":"density","kind":"range","min":0.6,"max":1.4,"step":0.05,"default":1,"label":"Density"}
82
+ ```
83
+
84
+ Add one structural parameter only when the topology genuinely branches. Follow [live.md](live.md)'s parameter contract.
@@ -0,0 +1,105 @@
1
+ > **Additional context needed**: quality bar and shipping constraints.
2
+
3
+ Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement.
4
+
5
+ A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path.
6
+
7
+ ## 1. Establish the system
8
+
9
+ Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions.
10
+
11
+ Classify each drift before fixing it:
12
+
13
+ - **missing token:** the system needs a reusable value;
14
+ - **one-off implementation:** an existing shared component or pattern should replace it;
15
+ - **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas;
16
+ - **local defect:** the implementation is simply incomplete or inconsistent.
17
+
18
+ Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred.
19
+
20
+ ## 2. Gather the evidence
21
+
22
+ Use the feature yourself at the surface's representative sizes: desktop and mobile on the web; on a native platform (`ios` / `android` / `adaptive`), the shipped device classes on the simulator, emulator, or hardware, captured per the platform reference's Verifying the build section. Determine:
23
+
24
+ - whether the path is functionally complete;
25
+ - the intended quality bar and time available;
26
+ - known constraints or deliberately unfinished work;
27
+ - the states, content lengths, roles, and input methods users will actually encounter.
28
+
29
+ If a prior critique exists, use it as one input:
30
+
31
+ ```bash
32
+ .opencode/skills/impeccable/scripts/impeccable critique-storage latest "<resolved target>" --json
33
+ ```
34
+
35
+ Exit 0 returns JSON with the latest snapshot's `body` and an exact `snapshot_file` identity. Retain `snapshot_file` until the end of the pass. For a local file target, the helper compares the file's exact current content fingerprint with the fingerprint captured by critique. Unchanged staged, unstaged, or untracked content remains current; any byte change, deletion, or replacement with a non-file closes the backlog it identified while preserving its trend history and exits 2. A URL target has no local fingerprint and remains current until explicitly closed. When current, incorporate relevant P0/P1 findings from `body` and name the snapshot read. Exit 2 means none exists or the target changed. Perform an independent pass either way.
36
+
37
+ ## 3. Triage
38
+
39
+ Separate functional defects from cosmetic ones and fix in this order:
40
+
41
+ 1. broken or blocked tasks, data loss, misleading state, and inaccessible paths;
42
+ 2. missing loading, empty, error, success, disabled, and permission states;
43
+ 3. flow, hierarchy, responsive, and design-system drift;
44
+ 4. visual and motion inconsistencies;
45
+ 5. code and asset cleanup.
46
+
47
+ Do not perfect one corner while leaving the rest below the same quality bar.
48
+
49
+ ## 4. Polish the whole path
50
+
51
+ ### Flow and hierarchy
52
+
53
+ - Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns.
54
+ - Make the primary task and current state obvious without flattening every element to equal weight.
55
+ - Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens.
56
+
57
+ ### Layout and type
58
+
59
+ - Align to the project's grid and spacing scale; fix optical as well as mathematical alignment.
60
+ - Group related content tightly and separate distinct groups generously.
61
+ - Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading.
62
+ - Verify every supported viewport rather than correcting only the current screenshot.
63
+
64
+ ### Color, imagery, and icons
65
+
66
+ - Use semantic tokens and stable color meanings across themes.
67
+ - Verify text, control, and focus contrast in every state.
68
+ - Keep icon families, stroke/weight, sizing, and optical alignment coherent.
69
+ - Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text.
70
+
71
+ ### Interaction and state
72
+
73
+ - Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior.
74
+ - Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets.
75
+ - Keep motion coherent, interruptible, and performant. Do not add animation merely to make polish visible.
76
+ - Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it.
77
+
78
+ ### Content and code
79
+
80
+ - Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims.
81
+ - Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication.
82
+ - Replace custom implementations with shared components where the system owns the pattern.
83
+ - Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception.
84
+
85
+ ## 5. Verify and finish
86
+
87
+ Walk the complete path again with mouse, keyboard, and touch where applicable. Check:
88
+
89
+ - mobile, intermediate, and wide layouts on the web; phone and tablet size classes in both supported orientations on native;
90
+ - loading, empty, error, success, disabled, long-content, and missing-content states;
91
+ - zoom, contrast, focus, semantics, and screen-reader names;
92
+ - console errors, layout shift, interaction latency, and image loading everywhere; supported browsers on the web; supported OS versions, runtime warnings, and dropped frames on native;
93
+ - agreement with DESIGN.md, neighboring features, and the user's scope.
94
+
95
+ Follow the quality guidance supplied by `impeccable context` and hooks, then run any other relevant QA commands. Context requests a manual scan only when no automatic detector is active; never add another detector pass. Fix real defects and document only narrow intentional exceptions. A clean scan does not replace visual judgment.
96
+
97
+ Finish with a source diff: remove accidental churn, orphaned code, redundant values, and temporary artifacts. Ship only when the feature is functionally complete and consistently finished across the path.
98
+
99
+ When this pass clears every Priority Issue it took from a snapshot, close that snapshot:
100
+
101
+ ```bash
102
+ .opencode/skills/impeccable/scripts/impeccable critique-storage close "<resolved target>" "<snapshot_file returned by latest>"
103
+ ```
104
+
105
+ This closes only the snapshot this pass actually processed; if a newer critique landed meanwhile, its backlog stays live. Do not close when no snapshot was read, when `snapshot_file` was not retained, or when Priority Issues remain.
@@ -0,0 +1,8 @@
1
+ {
2
+ "project": "Impeccable",
3
+ "license": "Apache-2.0",
4
+ "commit": "9fadc41729c38fd47d7ae622b501558653f9d2e9",
5
+ "scope": "Unmodified selected Markdown references; upstream executable engine and hooks are not bundled.",
6
+ "version": "4.8.0",
7
+ "source": "https://github.com/pbakaus/impeccable"
8
+ }
@@ -0,0 +1,90 @@
1
+ Typography carries information, hierarchy, and voice. Improve it inside the established visual world; do not replace the identity unless the user asked to.
2
+
3
+ ---
4
+
5
+ ## Visitor mode
6
+
7
+ - **Persuade + Experience:** display type may carry the voice. Use decisive contrast and responsive scale when the composition benefits.
8
+ - **Operate + Read:** stability, scanability, and measure come first. A single well-tuned family and fixed role scale are often right.
9
+ - **Native:** follow [ios.md](ios.md) or [android.md](android.md), including platform scaling and accessibility behavior.
10
+
11
+ If typography replacement would create a new identity, route through [new-work.md](new-work.md) and update DESIGN.md. Otherwise preserve confirmed families and improve their use.
12
+
13
+ ## Two isolated assessments
14
+
15
+ When a sub-agent tool is available and permitted, run these independently; otherwise run them yourself in this order. Do not let detector findings anchor the design assessment.
16
+
17
+ 1. **Typographic assessment:** inspect representative pages and styles. Answer every question below with a file, selector, or computed value:
18
+ - **Authority and fit:** Which faces, weights, and roles are established? Do they fit the product and selected world, or are they unexamined defaults? Is every family necessary?
19
+ - **Hierarchy:** Can heading, body, label, metadata, and data roles be distinguished at a glance? Are adjacent sizes or weights too close to carry different jobs?
20
+ - **Scale and consistency:** Is there a deliberate role scale, or a collection of arbitrary values? Do repeated roles stay identical across screens and states?
21
+ - **Reading:** Does body copy stay within a comfortable 45–75 character measure? Are line height, paragraph rhythm, contrast, and tracking tuned to the actual face, width, language, and surface?
22
+ - **Stress:** What happens with long headings, localization expansion, zoom, narrow containers, missing weights, and font fallback?
23
+ - **Delivery:** Are only used assets loaded? Do fallback metrics, loading strategy, and variable-font settings avoid invisible text and disruptive reflow?
24
+ 2. **Mechanical scan:** run:
25
+
26
+ ```bash
27
+ .opencode/skills/impeccable/scripts/impeccable detect --json --scope type [target files or dirs]
28
+ ```
29
+
30
+ Also inspect dynamic or arbitrary font values the detector cannot interpret. Synthesize both assessments before editing, noting what each caught alone. A clean scan is a floor, not proof of good typography.
31
+
32
+ ## Set the system
33
+
34
+ Before editing, state:
35
+
36
+ - the roles the interface needs;
37
+ - the intended contrast between those roles;
38
+ - the reading measure and density;
39
+ - which existing faces and weights are authoritative;
40
+ - any performance, localization, or accessibility constraints.
41
+
42
+ Use the fewest roles and families that make the hierarchy unmistakable. Combine size, weight, space, and tone deliberately instead of asking size alone to do all the work. Role names and tokens should describe purpose rather than values.
43
+
44
+ ## Apply
45
+
46
+ - Keep body copy comfortably readable and zoomable. Use 1rem / 16px as the ordinary web body floor unless a dense role, platform convention, or user setting justifies otherwise.
47
+ - Keep prose in the 45–75ch range. Tune line height inversely with measure: wider lines generally need more leading.
48
+ - Compensate light text on dark surfaces on all three perceptual axes: slightly more line height, a touch more tracking, and one step more weight when the face needs it.
49
+ - Tune line height to the face, width, language, and contrast, not a universal ratio.
50
+ - Keep repeated roles consistent across screens and states.
51
+ - Use numeric, tabular, code, and label features when their content benefits.
52
+ - Load only used font assets and weights. Provide metric-compatible fallbacks and avoid blocking text.
53
+ - Let marketing display type respond to available space when useful; keep dense product and reading surfaces spatially predictable.
54
+ - Preserve browser zoom, user font settings, Dynamic Type, and platform text scaling.
55
+ - Use paragraph spacing or first-line indentation as the primary paragraph rhythm; combining both usually double-marks the boundary.
56
+
57
+ Do not make type decorative at the expense of comprehension, or introduce a second family without a clear role it alone can perform.
58
+
59
+ ## Line breaks
60
+
61
+ After Matthew Butterick's *Practical Typography*, paraphrased; where his numbers differ from these, these stand.
62
+
63
+ - Set `text-wrap: balance` on headings and short centered blocks, and `text-wrap: pretty` on body copy. Both are progressive: a browser that ignores them keeps its default wrap.
64
+ - `pretty` pulls a word down beside a lone last word. It does not fill the last line, and a two-word tail is fine.
65
+ - In a column too narrow for either to help, edit the copy or widen the column.
66
+ - Break a heading by hand only between phrases, and check the break at every width the heading renders at. A hard break that lands mid-line on a phone is worse than the default wrap.
67
+ - An uneven break can carry meaning: a headline that lands on one word for weight, a poster line, a pull quote. Make it deliberate with a hard break or a wrap set on that element, and keep it rare. The detector's `straggler` and `unbalanced-heading` findings are advisory for this reason; say which breaks are intended.
68
+
69
+ ## Verify
70
+
71
+ - Primary, secondary, body, and metadata roles are recognizable without reading the copy.
72
+ - Long text remains comfortable across relevant widths and languages.
73
+ - The typography belongs to the product and its established world.
74
+ - Loading does not create disruptive reflow or invisible text.
75
+ - Zoom, text scaling, focus, contrast, and reduced viewport paths remain usable.
76
+ - The final mechanical scan has no unexplained findings.
77
+
78
+ Answer each item with rendered or source evidence, then rerun the scan. Do not substitute a bare “yes” for verification.
79
+
80
+ When the hierarchy holds, hand off to `/impeccable polish`.
81
+
82
+ ## Live-mode signature params
83
+
84
+ Every variant declares a coarse `scale` parameter and authors its type ramp against `var(--p-scale, 1)`.
85
+
86
+ ```json
87
+ {"id":"scale","kind":"range","min":0.85,"max":1.3,"step":0.05,"default":1,"label":"Scale"}
88
+ ```
89
+
90
+ Add at most one pairing or weight parameter when it represents a real system choice. Follow [live.md](live.md)'s parameter contract.
@@ -40,7 +40,7 @@ For UI implementation reviews compare the supplied approved Claude design or est
40
40
  For planning report a concrete plan: requirements, responsibility boundaries, contracts, alternatives/tradeoffs, security, data handling, investigation points, specialist-owned slices, acceptance checks and relevant design skills.
41
41
  For author/proposal/synthesis stages, pass means your proposed document is ready for independent validation, not implementation approval. Do not use revise merely because you rewrote a plan: revise requires a concrete open finding and blocked requires a material open finding.
42
42
  Architecture planning precedes design for new applications and substantive behavior/architecture changes; existing-app visual redesigns use mode=ui and go directly to design. Claude/Opus owns new UI/UX design, using the supplied design skills and research; Astra reviews the exact design. Never assign new design decisions to the implementation model or its frontend agent. Existing/supplied design patterns may be implemented faithfully without a new design phase. Architecture approval alone is not visual-design approval. Do not block architecture planning because later design artifacts do not exist yet. The reviewer CLI receives captured skill evidence rather than OpenCode's native skill catalog.
43
- New websites/redesigns must use frontend-design and ui-ux-pro-max before implementation, and web-design-guidelines for review. Use pnpm where appropriate; honor installed versions and project rules.
43
+ New websites/redesigns must use the supplied design guidance before implementation. When the packet selects Impeccable, its bundled references replace frontend-design/ui-ux-pro-max research for this checkpoint; follow its integration policy, not upstream executable workflow or delegation commands. Otherwise use frontend-design and ui-ux-pro-max before implementation, and web-design-guidelines for review. Use pnpm where appropriate; honor installed versions and project rules.
44
44
  You may read files under source/ and the packet. Never search the web with private source or internal URLs. No commands that change anything. Keep reports concise and proportional to risk. Missing evidence must be explicit.
45
45
  ${WORKFLOW_POLICY}`;
46
46
 
package/core/design.mjs CHANGED
@@ -6,10 +6,12 @@ import { runProcess } from './workers.mjs';
6
6
  import { captureImages } from './images.mjs';
7
7
  import {findingChecklist,findingDecision} from './findings.mjs';
8
8
  import {browserEvidence} from '../lib/browser-evidence/receipt.mjs';
9
+ import {impeccableIntent,impeccableEvidence} from '../lib/impeccable.mjs';
9
10
 
10
11
  export const DESIGN_POLICY = `UI/UX ownership: the implementation model must not invent visual direction or interaction design. Claude/Opus authors new design decisions and Astra reviews them. This applies to new projects, substantial features without a design, and later redesign/improve-UI requests. Small/medium additions can reuse an established design system and supplied Figma/Claude/local designs can be implemented faithfully, with concrete reference evidence. Escalate material gaps instead of filling them with your own design. Before edits call hybrid_checkpoint design when design is pending; do not bypass through subagents. Read the returned approved design document and give it to frontend-engineer. Existing/supplied exceptions require designMode and designEvidence at begin. Architecture planning does not authorize new visual decisions.`;
11
12
 
12
13
  export function designIntent(text = '') {
14
+ if(impeccableIntent(text))return 'new';
13
15
  // This is an admission safety net, not a substitute for the semantic routing rule.
14
16
  const value = text.toLowerCase().replace(/\b(?:do not|don't|without|no)\s+(?:re-?design|redesign|redisign)\b/g, 'preserve');
15
17
  const supplied = /\b(figma|supplied design|provided design|claude design|design file|reference design)\b/.test(value) && /\b(implement|match|follow|reproduce|replicate)\b/.test(value);
@@ -85,6 +87,8 @@ export async function design(state, input, signal) {
85
87
  const brief = pending.length ? pending.map(r=>r.text).join('\n\n') : state.brief;
86
88
  const evidence = {brief, userDirections:pending, plan:state.plan, referenceEvidence:state.designEvidence ?? '', supplied:input.checks ?? ''};
87
89
  const tree = snapshot(this.directory);
90
+ const integration=impeccableEvidence(this.config,brief,tree);
91
+ if(integration)evidence.impeccable={name:integration.name,mode:integration.mode,provenance:integration.provenance,digest:integration.digest};
88
92
  const captured=browserEvidence(this,state,tree.fingerprint);
89
93
  const images=[...captureImages(this.directory,input.images,tree.fingerprint),...captured.images].map((image,index)=>({...image,manifest:{...image.manifest,path:`images/${index}.png`}}));
90
94
  evidence.browserEvidence=captured.summary;
@@ -95,7 +99,7 @@ export async function design(state, input, signal) {
95
99
  state.designResumePhase ??= ['fix','review'].includes(state.phase) ? 'fix' : 'implement';
96
100
  if(state.mode==='cosmetic') state.mode='small';
97
101
  state.phase='design'; this.save(state);
98
- const skills = await (this.designEvidence ?? skillEvidence)(this,brief,state.mode === 'small');
102
+ const skills = integration ? {impeccable:integration,guidance:integration.policy} : await (this.designEvidence ?? skillEvidence)(this,brief,state.mode === 'small');
99
103
  if (state.designEvidenceHash !== evidenceHash) { state.designNeedsRevision=true; state.designEvidenceHash=evidenceHash; delete state.designPause; }
100
104
  while (true) {
101
105
  if(signal?.aborted) throw new Error('Design cancelled; approval remains closed.');
package/core/plugin.mjs CHANGED
@@ -8,6 +8,7 @@ import { Controller } from './controller.mjs';
8
8
  import { readDashboard } from './monitor-data.mjs';
9
9
  import { DESIGN_POLICY, recordDesignRequest } from './design.mjs';
10
10
  import { researchTool } from './research-tools.mjs';
11
+ import {registerImpeccableCommand,IMPECCABLE_POLICY} from '../lib/impeccable.mjs';
11
12
 
12
13
  const here = path.dirname(fileURLToPath(import.meta.url));
13
14
  // V2's Plugin.define is an identity function. This plain plugin needs no installed dependencies.
@@ -20,6 +21,7 @@ export function createPlugin(config, storage = config.runtimeDirectory ?? path.j
20
21
  const compatible = config.compatibleOpenCode ?? [config.targetOpenCode];
21
22
  if (!compatible.includes(ctx.app.version)) throw new Error(`Hybrid plugin compatibility check required: running OpenCode ${ctx.app.version}; verified versions: ${compatible.join(', ')}. Run the isolated compatibility probe before adding this version. Existing task history is preserved.`);
22
23
  const controllers = new Map();
24
+ await registerImpeccableCommand(ctx);
23
25
  const forSession = async sessionID => {
24
26
  const result = await ctx.session.get({ sessionID });
25
27
  const session = result.data ?? result;
@@ -40,6 +42,7 @@ export function createPlugin(config, storage = config.runtimeDirectory ?? path.j
40
42
  if(await isReviewerSession(ctx,event.sessionID))return;
41
43
  event.system.push({type:'text',text:roleText(DESIGN_POLICY)});
42
44
  event.system.push({type:'text',text:roleText(WORKFLOW_POLICY)});
45
+ event.system.push({type:'text',text:IMPECCABLE_POLICY});
43
46
  event.system.push({type:'text',text:`${FRONTEND_COMMON}\n${FRONTEND_BUILD}`});
44
47
  event.system.push({type:'text',text:'Hybrid recovery: completed negative/blocked code reviews allow corrections and evidence collection in fix; continue automatically and re-review, never claim acceptance. action=fix recovers a legacy completed review. New evidence in checks/images resumes paused plan/design; action=replan with concrete changed requirements opens architecture revision while preserving findings and history. action=inspect checks installed tools and CLI logins without model calls and is available during planning. Never edit bridge state or reset history. Playwright research outputs use plain filenames in .playwright-mcp/hybrid-evidence; this generated evidence is excluded from source snapshots. Navigation/resizing and screenshot capture are available while planning/designing. Supply actual PNGs to plan/design/review as images [{path,caption,artifactHash}], using status.currentArtifactHash for the code/reference being inspected. Recapture after code changes and include interaction evidence in checks. Both reviewers receive pixels; never substitute hashes or ask the user to attest images merely because they live outside Git.'});
45
48
  });
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Release notes
2
2
 
3
+ ## 1.1.0
4
+
5
+ - Add optional Impeccable design guidance in Hybrid settings and `/hybrid-impeccable redesign` or `polish`. The configured design lead authors the direction, reviewers validate it, and the implementation model follows the approved document.
6
+ - Bundle selected, version-pinned Impeccable references with their license. No separate engine, hooks or runtime downloads are required.
7
+ - Recover interrupted native-provider reports as complete responses instead of accepting a continuation fragment. Incomplete recovery still blocks approval.
8
+ - Accept an exact single JSON code-block wrapper from native reviewers while retaining strict response validation.
9
+
3
10
  ## 1.0.1
4
11
 
5
12
  - Refresh usage sidebar settings immediately after saving Hybrid setup, including reviewer models, effort, reviewer count and telemetry location. No restart is needed for these sidebar changes.
@@ -0,0 +1,22 @@
1
+ # Impeccable design guidance
2
+
3
+ In **/hybrid → Design guidance**, select **Impeccable**. Your selected design lead and reviewers stay in charge. Nothing else needs installing.
4
+
5
+ ![Select design guidance inside OpenCode](images/22-design-guidance.png)
6
+
7
+ For a single task, use:
8
+
9
+ ```text
10
+ /hybrid-impeccable redesign the portfolio
11
+ /hybrid-impeccable polish the checkout
12
+ ```
13
+
14
+ Or ask normally: “Use Impeccable to redesign this page.” Say “without Impeccable” to override the global setting for a request.
15
+
16
+ The lead receives selected Impeccable references and existing project design documents, authors concrete implementation instructions, and submits them to the configured reviewers. Your implementation model follows the approved document. Polish preserves the current identity; redesign can replace it. The existing rule still applies: all configured reports, at least one pass, and no unresolved medium/high/critical findings.
17
+
18
+ This integrates **Impeccable 4.8.0 design guidance**, not its separate executable engine. It does not install or run upstream hooks, detectors, live mode, concept boards, image generation or audit commands. Those tools' results are never implied by using the guidance. `/hybrid-impeccable` is separate from an independently installed `/impeccable` command; use the Hybrid command for this workflow.
19
+
20
+ The setting affects future design checkpoints without invalidating existing approvals or changing reviewers on active tasks. The command is available on hosts with OpenCode's command transform API; normal chat routing remains available on other supported OpenCode 2 hosts.
21
+
22
+ Bundled references carry their original license and exact source commit in `assets/integrations/impeccable/`. No third-party scripts or runtime downloads are required. Relevant project documents come from the same sanitized snapshot as the source evidence.
package/lib/config.mjs CHANGED
@@ -34,6 +34,7 @@ export function readSettings(options={},env=process.env) {
34
34
  const config={...defaults,...supplied,reviewers:Object.fromEntries(['astra','opus'].map(who=>[who,supplied.reviewers?.[who]===null?null:supplied.reviewers?.[who]?.adapter&&supplied.reviewers[who].adapter!==defaults.reviewers[who].adapter?supplied.reviewers[who]:{...defaults.reviewers[who],...supplied.reviewers?.[who]}])),
35
35
  design:{...defaults.design,...supplied.design},verification:{...defaults.verification,...supplied.verification}};
36
36
  if(options.enabled===false)config.enabled=false;
37
+ if(config.design.impeccable!==undefined&&typeof config.design.impeccable!=='boolean')throw Error('design.impeccable must be true or false.');
37
38
  validateBrowser(config.reviewBrowser);
38
39
  for(const who of ['astra','opus']) {
39
40
  if(supplied.reviewers?.[who]===null)config.reviewers[who]=null;
@@ -0,0 +1,36 @@
1
+ import {readFileSync} from 'node:fs';
2
+ import {createHash} from 'node:crypto';
3
+ const root=new URL('../assets/integrations/impeccable/',import.meta.url);
4
+ const optedOut=text=>/\b(?:without|disable|do not use|don't use|no)\s+impecc?able\b/i.test(text);
5
+ export const IMPECCABLE_POLICY='Impeccable integration: /hybrid-impeccable redesign or polish routes through hybrid_checkpoint design, never directly to a Build subagent. The configured design lead owns decisions; implementation follows its approved document. Bundled Impeccable references are design guidance, not executable workflow instructions. Hybrid owns approval, delegation, retries and tools. Do not run referenced Impeccable binaries, hooks, detectors, installers or nested commands, or claim their results. Use actual source, screenshots and existing verification tools. No additional rounds, quotas or approval gates are introduced. Polish preserves the current visual identity; redesign may replace it within the user brief. Read-only audits are not supported by this command; they must not authorize edits.';
6
+ export function impeccableIntent(text='') {
7
+ if(!/\bimpecc?able\b/i.test(text)||optedOut(text))return null;
8
+ const explicit=/\buse\s+impecc?able\s+(?:for this\s+)?(redesign|polish)\b/i.exec(text);
9
+ if(explicit)return explicit[1].toLowerCase();
10
+ if(/\b(?:redesign|redisign|restyle|rebrand)\b/i.test(text))return 'redesign';
11
+ if(/\bpolish\b/i.test(text))return 'polish';
12
+ return null;
13
+ }
14
+ export function impeccableEvidence(config,brief,tree) {
15
+ if(optedOut(brief))return null;
16
+ const requested=impeccableIntent(brief);
17
+ if(!requested&&!config.design?.impeccable)return null;
18
+ const mode=requested??(/\bpolish\b/i.test(brief)?'polish':'redesign');
19
+ const files=mode==='polish'?['craft-floor.md','polish.md']:['craft-floor.md','layout.md','typeset.md','colorize.md'];
20
+ const provenance=JSON.parse(readFileSync(new URL('provenance.json',root),'utf8'));
21
+ const references=Object.fromEntries(files.map(name=>[name,readFileSync(new URL(name,root),'utf8')]));
22
+ // Use the already sanitized, immutable snapshot. Never follow a project link or
23
+ // load a model-supplied path to collect additional context.
24
+ const projectContext=Object.fromEntries(['PRODUCT.md','DESIGN.md','docs/design-brief.md','docs/design-system.md'].filter(name=>tree.files[name]?.text&&!tree.files[name].hidden).map(name=>[name,tree.files[name].text]));
25
+ const digest=createHash('sha256').update(JSON.stringify({provenance,references,projectContext,mode})).digest('hex');
26
+ return {name:'Impeccable',mode,provenance,digest,policy:IMPECCABLE_POLICY,references,projectContext};
27
+ }
28
+ export async function registerImpeccableCommand(ctx) {
29
+ if(!ctx.command?.transform)return; // Normal chat routing works on older v2 hosts.
30
+ await ctx.command.transform(editor=>editor.add({name:'hybrid-impeccable',description:'Impeccable redesign or polish through Hybrid’s design lead and reviewers',execute:async({sessionID,prompt,delivery})=>{
31
+ const match=/^\s*(redesign|polish)\b\s*([\s\S]*)$/i.exec(prompt.text??'');
32
+ if(!match)throw Error('Use /hybrid-impeccable redesign <scope> or /hybrid-impeccable polish <scope>. This command does not run read-only audits or the upstream CLI.');
33
+ // Keep attachment mention offsets valid by appending, not prepending.
34
+ await ctx.session.prompt({...prompt,sessionID,delivery,text:`${prompt.text}\n\nUse Impeccable for this ${match[1].toLowerCase()}. ${IMPECCABLE_POLICY}\nBegin a ui task with designMode new if no task is active, then call hybrid_checkpoint design before editing. Existing active tasks keep their history and reviewers.`});
35
+ }}));
36
+ }
@@ -0,0 +1,31 @@
1
+ import {validateResult} from '../core/workers.mjs';
2
+ const unwrap=x=>x.data??x;
3
+ function interrupted(messages){
4
+ const lastPrompt=messages.findLastIndex(m=>m.type==='user');
5
+ return messages.slice(lastPrompt+1).some(m=>m.type==='assistant'&&(m.error||m.finish==='error'||m.finish==='length'));
6
+ }
7
+ export async function readNativeResult(ctx,sessionID,{isAborted=()=>false,onRecovery=()=>{}}={}){
8
+ let messages=unwrap(await ctx.session.context({sessionID}));
9
+ if(interrupted(messages)){
10
+ if(isAborted())throw Error('Review cancelled; no approval recorded.');
11
+ onRecovery();
12
+ // OpenCode can recover a broken stream with "continue without repeating".
13
+ // Its final message may then be valid JSON containing only a report fragment.
14
+ // Ask the same model to assemble a whole response; never stitch partial JSON
15
+ // or accept approval from the suffix alone.
16
+ await ctx.session.prompt({sessionID,text:'Transport recovery: your previous response was interrupted and the host requested a continuation. Return ONE complete, self-contained JSON result now, including the ENTIRE report, findings and verifiedResolutions. Reconstruct the whole artifact from this conversation, not just its ending. Preserve all substantive decisions and unresolved findings; concise tables are welcome. Do not omit earlier sections, refer to an unavailable earlier report, or claim any new verification. This is response assembly, not a new design or approval. Return only the complete JSON object.'});
17
+ await ctx.session.wait({sessionID});
18
+ messages=unwrap(await ctx.session.context({sessionID}));
19
+ if(interrupted(messages))throw Error('Reviewer response was interrupted again during complete-result recovery; no approval recorded. Retry after resolving the transport failure.');
20
+ }
21
+ const session=unwrap(await ctx.session.get({sessionID}));
22
+ if(isAborted()||session.outcome!=='succeeded')throw Error('OpenCode reviewer did not complete successfully; no approval recorded.');
23
+ const assistant=messages.slice(messages.findLastIndex(m=>m.type==='user')+1).filter(m=>m.type==='assistant').at(-1);
24
+ if(!assistant||assistant.error||assistant.finish!=='stop')throw Error('Reviewer response is incomplete; no approval recorded.');
25
+ const text=assistant.content.filter(c=>c.type==='text').map(c=>c.text).join('').trim();
26
+ // Accept only an exact single JSON fence wrapper, not prose or guessed JSON
27
+ // substrings. The complete enclosed object still passes the same strict schema.
28
+ const fenced=/^```(?:json)?[ \t]*\r?\n([\s\S]*)\r?\n```$/.exec(text);
29
+ let result;try{result=validateResult(JSON.parse(fenced?fenced[1]:text));}catch{throw Error('Reviewer returned invalid JSON; no approval recorded.');}
30
+ return {result,session};
31
+ }
@@ -2,13 +2,14 @@ import {publicDocument,documentTool,documentSchema,documentDescription} from './
2
2
  import {readFileSync,realpathSync,readdirSync,statSync} from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import {reviewer} from './reviewers.mjs';
5
- import {validateResult,resultSchema} from '../core/workers.mjs';
5
+ import {resultSchema} from '../core/workers.mjs';
6
6
  import {Telemetry} from '../core/telemetry.mjs';
7
7
  import {NativeBrowser} from './native-browser.mjs';
8
8
  import {browserInstructions} from './reviewer-browser.mjs';
9
9
  import {tracedTool} from './run-trace.mjs';
10
10
  import {EvidenceStore} from './evidence-store.mjs';
11
11
  import {queryEvidence,querySchema,queryDescription} from './evidence-query.mjs';
12
+ import {readNativeResult} from './native-result.mjs';
12
13
 
13
14
  const active=new Map();
14
15
  const readTools=['hybrid_evidence_read','hybrid_evidence_query',documentTool];
@@ -123,15 +124,10 @@ export async function installNativeReviewer(ctx) {
123
124
  await ctx.session.prompt({sessionID:id,text:nativePrompt});
124
125
  await ctx.session.wait({sessionID:id});
125
126
  if(interrupt)await interrupt;
126
- const final=unwrap(await ctx.session.get({sessionID:id}));
127
+ const {result,session:final}=await readNativeResult(ctx,id,{isAborted:()=>aborted,onRecovery:()=>{telemetry.value.currentAction='Recovering complete report after interrupted provider response';telemetry.value.responseRecovery=true;telemetry.save();}});
127
128
  telemetry.value.usage=nativeUsage(final.tokens);telemetry.save();
128
129
  if(aborted||final.outcome!=='succeeded')throw Error('OpenCode reviewer did not complete successfully; no approval recorded.');
129
130
  if(model.cost?.some(rate=>rate.input>0||rate.output>0))telemetry.value.apiCost=final.cost;
130
- const messages=unwrap(await ctx.session.context({sessionID:id}));
131
- const assistant=messages.filter(m=>m.type==='assistant').at(-1);
132
- if(!assistant||assistant.error||assistant.finish!=='stop')throw Error('Reviewer response is incomplete; no approval recorded.');
133
- const text=assistant.content.filter(c=>c.type==='text').map(c=>c.text).join('');
134
- let result;try{result=validateResult(JSON.parse(text.trim()));}catch{throw Error('Reviewer returned invalid JSON; no approval recorded.');}
135
131
  await closeBrowser();if(browserCloseError)throw browserCloseError;
136
132
  telemetry.end('completed',result.verdict);return result;
137
133
  }catch(error){
@@ -6,5 +6,6 @@ export const SettingsRPC={id:'hybrid.settings',methods:{
6
6
  save:{input:{type:'object',additionalProperties:false,properties:{revision:{type:'string'},reviewers:object,designAuthor:{enum:['astra','opus']}},required:['revision','reviewers','designAuthor']},output:object},
7
7
  browser:{input:{type:'object',additionalProperties:false,properties:{action:{enum:['enable','disable','install']}},required:['action']},output:object},
8
8
  diagnostics:{input:{type:'object',additionalProperties:false},output:object},
9
+ designGuidance:{input:{type:'object',additionalProperties:false,properties:{revision:{type:'string'},enabled:{type:'boolean'}},required:['revision','enabled']},output:object},
9
10
  dependencies:{input:{type:'object',additionalProperties:false,properties:{action:{enum:['pnpm','context7']}},required:['action']},output:object},
10
11
  },events:{}};
@@ -48,7 +48,7 @@ export function createSettingsService(ctx,options={}){
48
48
  async function get(){
49
49
  const config=load(),models=await catalog();
50
50
  return {configured:existsSync(file)&&config.setupComplete!==false,readOnly,revision:revision(file),version:JSON.parse(readFileSync(path.join(packageRoot,'package.json'),'utf8')).version,
51
- reviewers:config.reviewers,designAuthor:config.designAuthor??(config.reviewers.opus?'opus':'astra'),browserEnabled:!!config.reviewBrowser?.enabled,
51
+ reviewers:config.reviewers,designAuthor:config.designAuthor??(config.reviewers.opus?'opus':'astra'),browserEnabled:!!config.reviewBrowser?.enabled,impeccableEnabled:config.design.impeccable===true,
52
52
  catalog:models.map(m=>({providerID:m.providerID,id:m.id,name:m.name,variants:(m.variants??[]).map(v=>v.id),vision:m.capabilities.input?.includes('image')===true})),
53
53
  configFile:file,implementationModel:globalRead().model??'Selected in OpenCode',host:process.platform};
54
54
  }
@@ -76,6 +76,13 @@ export function createSettingsService(ctx,options={}){
76
76
  return folder;
77
77
  }
78
78
  return {get,diagnostics,
79
+ designGuidance:input=>exclusive(async()=>{
80
+ if(input.revision!==revision(file))throw Error('Settings changed in another window. Reopen settings and try again.');
81
+ if(typeof input.enabled!=='boolean')throw Error('Choose enabled or disabled.');
82
+ const existing=existsSync(file)?JSON.parse(readFileSync(file,'utf8')):{};
83
+ persist({design:{...existing.design,impeccable:input.enabled}});
84
+ return {message:'Saved. Future design checkpoints '+(input.enabled?'use bundled Impeccable guidance.':'use standard guidance unless Impeccable is explicitly requested.')+' Existing approvals and reviewer assignments are preserved.'};
85
+ }),
79
86
  cliModels:(input,context={})=>{
80
87
  if(!['astra','opus'].includes(input.who)||!['codex-cli','claude-cli'].includes(input.adapter))throw Error('Unknown reviewer connection.');
81
88
  const previous=load().reviewers?.[input.who];
package/package.json CHANGED
@@ -1,76 +1,76 @@
1
- {
2
- "name": "@rockysoft/opencode-hybrid-workflow",
3
- "version": "1.0.1",
4
- "description": "Portable configurable-reviewer planning, design, implementation checkpoints and usage sidebar for OpenCode 2",
5
- "type": "module",
6
- "license": "MIT",
7
- "author": "RockySoft",
8
- "keywords": [
9
- "opencode",
10
- "opencode-plugin",
11
- "agents",
12
- "code-review",
13
- "workflow"
14
- ],
15
- "publishConfig": {
16
- "access": "public",
17
- "tag": "latest"
18
- },
19
- "exports": {
20
- ".": "./index.js",
21
- "./tui": "./tui.js"
22
- },
23
- "bin": {
24
- "opencode-hybrid": "./cli.mjs"
25
- },
26
- "files": [
27
- "index.js",
28
- "tui.js",
29
- "cli.mjs",
30
- "lib/",
31
- "core/",
32
- "sidebar/",
33
- "assets/",
34
- "README.md",
35
- "THIRD_PARTY_NOTICES.md",
36
- "LICENSE",
37
- "docs/"
38
- ],
39
- "engines": {
40
- "node": ">=22"
41
- },
42
- "scripts": {
43
- "build": "node scripts/build.mjs",
44
- "prepack": "npm run build",
45
- "test": "node --test test/*.test.mjs",
46
- "doctor": "node cli.mjs doctor"
47
- },
48
- "devDependencies": {
49
- "@babel/core": "7.29.7",
50
- "@babel/preset-typescript": "7.27.1",
51
- "babel-preset-solid": "1.9.12"
52
- },
53
- "peerDependencies": {
54
- "@opencode/plugin": ">=2.1.2 <3",
55
- "@opentui/core": ">=0.6.6",
56
- "@opentui/solid": ">=0.6.6",
57
- "solid-js": ">=1.9.0"
58
- },
59
- "peerDependenciesMeta": {
60
- "@opencode/plugin": {
61
- "optional": true
62
- },
63
- "@opentui/core": {
64
- "optional": true
65
- },
66
- "@opentui/solid": {
67
- "optional": true
68
- },
69
- "solid-js": {
70
- "optional": true
71
- }
72
- },
73
- "dependencies": {
74
- "diff": "9.0.0"
75
- }
76
- }
1
+ {
2
+ "name": "@rockysoft/opencode-hybrid-workflow",
3
+ "version": "1.1.0",
4
+ "description": "Portable configurable-reviewer planning, design, implementation checkpoints and usage sidebar for OpenCode 2",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "RockySoft",
8
+ "keywords": [
9
+ "opencode",
10
+ "opencode-plugin",
11
+ "agents",
12
+ "code-review",
13
+ "workflow"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "public",
17
+ "tag": "latest"
18
+ },
19
+ "exports": {
20
+ ".": "./index.js",
21
+ "./tui": "./tui.js"
22
+ },
23
+ "bin": {
24
+ "opencode-hybrid": "./cli.mjs"
25
+ },
26
+ "files": [
27
+ "index.js",
28
+ "tui.js",
29
+ "cli.mjs",
30
+ "lib/",
31
+ "core/",
32
+ "sidebar/",
33
+ "assets/",
34
+ "README.md",
35
+ "THIRD_PARTY_NOTICES.md",
36
+ "LICENSE",
37
+ "docs/"
38
+ ],
39
+ "engines": {
40
+ "node": ">=22"
41
+ },
42
+ "scripts": {
43
+ "build": "node scripts/build.mjs",
44
+ "prepack": "npm run build",
45
+ "test": "node --test test/*.test.mjs",
46
+ "doctor": "node cli.mjs doctor"
47
+ },
48
+ "devDependencies": {
49
+ "@babel/core": "7.29.7",
50
+ "@babel/preset-typescript": "7.27.1",
51
+ "babel-preset-solid": "1.9.12"
52
+ },
53
+ "peerDependencies": {
54
+ "@opencode/plugin": ">=2.1.2 <3",
55
+ "@opentui/core": ">=0.6.6",
56
+ "@opentui/solid": ">=0.6.6",
57
+ "solid-js": ">=1.9.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "@opencode/plugin": {
61
+ "optional": true
62
+ },
63
+ "@opentui/core": {
64
+ "optional": true
65
+ },
66
+ "@opentui/solid": {
67
+ "optional": true
68
+ },
69
+ "solid-js": {
70
+ "optional": true
71
+ }
72
+ },
73
+ "dependencies": {
74
+ "diff": "9.0.0"
75
+ }
76
+ }
@@ -87,6 +87,7 @@ export function createSettingsUI(context,onSaved=()=>{}){
87
87
  {title:'Connect provider',value:'connect',description:'Open OpenCode’s account connection menu'},
88
88
  {title:'Implementation model',value:'models',description:'Select the model that builds your code in OpenCode'},
89
89
  {title:'Reviewer browser',value:'browser',description:state.browserEnabled?'Enabled · isolated sessions':'Disabled'},
90
+ {title:'Design guidance',value:'designGuidance',description:state.impeccableEnabled?'Impeccable · enabled':'Standard · Impeccable available per request'},
90
91
  {title:'Tools and dependencies',value:'dependencies',description:'Browser installation is in Reviewer browser'},
91
92
  {title:'Run diagnostics',value:'diagnostics',description:'Check setup and logins without model calls'},
92
93
  {title:'About this setup',value:'about',description:'Global configuration and active-task behavior'},
@@ -96,6 +97,10 @@ export function createSettingsUI(context,onSaved=()=>{}){
96
97
  if(action==='reviewers')await wizard(state);
97
98
  else if(action==='connect'||action==='models'){dispatch(action);return;}
98
99
  else if(action==='browser')await browser(state);
100
+ else if(action==='designGuidance'){
101
+ const value=await select('Design guidance',[{title:'Impeccable',value:'impeccable',description:'Guidance for your premium design lead'},{title:'Standard',value:'standard',description:'Impeccable only when requested'}],state.impeccableEnabled?'impeccable':'standard');
102
+ if(value){const result=await invoke('designGuidance',{revision:state.revision,enabled:value==='impeccable'});onSaved();await alert('Design guidance',result.message);}
103
+ }
99
104
  else if(action==='dependencies')await dependencies();
100
105
  else if(action==='diagnostics'){
101
106
  context.ui.toast.show({message:'Checking Hybrid setup…',variant:'info'});