@duro-app/cli 3.7.0 → 4.0.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 +27 -4
- package/dist/commands/hook.d.ts.map +1 -1
- package/dist/commands/hook.js +37 -21
- package/dist/commands/hook.js.map +1 -1
- package/dist/hook-script.d.ts +23 -1
- package/dist/hook-script.d.ts.map +1 -1
- package/dist/hook-script.js +45 -1
- package/dist/hook-script.js.map +1 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +2 -2
- package/dist/manifest.js.map +1 -1
- package/dist/skill-template.d.ts +1 -1
- package/dist/skill-template.d.ts.map +1 -1
- package/dist/skill-template.js +1 -1
- package/package.json +5 -5
- package/registry.json +3 -3
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ npx @duro-app/cli spacing # token scale + the deep import to copy
|
|
|
14
14
|
npx @duro-app/cli rules # critical rules + what the lint plugin enforces
|
|
15
15
|
npx @duro-app/cli "tags that wrap" # free text falls back to a need-search
|
|
16
16
|
npx @duro-app/cli list
|
|
17
|
-
npx @duro-app/cli hook install # wire the Claude Code
|
|
17
|
+
npx @duro-app/cli hook install # wire the Claude Code session + plan-mode hooks
|
|
18
18
|
npx @duro-app/cli skill install # wire the /duro-mockup workflow skill
|
|
19
19
|
npx @duro-app/cli mockup seed --out docs/mockups/approvals # token-seeded artboard
|
|
20
20
|
npx @duro-app/cli mockup check docs/mockups/approvals/*.dc.html
|
|
@@ -107,15 +107,16 @@ npx -y @duro-app/cli hook install # wire this repo
|
|
|
107
107
|
npx -y @duro-app/cli hook install --check # CI: exit 1 if it drifted
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
`install` is idempotent, and touches exactly
|
|
110
|
+
`install` is idempotent, and touches exactly four files:
|
|
111
111
|
|
|
112
112
|
| File | What install does |
|
|
113
113
|
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
114
114
|
| `.claude/hooks/duro-catalog.sh` | Writes the generated hook: fetches the catalog, caches it for 7 days (npx resolution dominates session start; the catalog only changes on upgrade), stays silent when offline with no cache. Then runs [`duro doctor --session`](#duro-doctor), re-run only when a file it reads changes. |
|
|
115
|
-
| `.claude/
|
|
115
|
+
| `.claude/hooks/duro-plan.sh` | Writes the generated [plan-mode nudge](#plan-mode-nudge). |
|
|
116
|
+
| `.claude/settings.json` | Adds the `SessionStart` and `UserPromptSubmit` entries. Existing hooks, permissions and skill overrides are preserved; a hand-renamed duro command is migrated in place rather than duplicated. Unparseable JSON is reported, never clobbered. |
|
|
116
117
|
| `.gitignore` | Ignores `.claude/.duro-session.cache*` — a glob, because a hook killed mid-`npx` leaves the staging `.tmp` behind. |
|
|
117
118
|
|
|
118
|
-
Do not hand-edit the generated
|
|
119
|
+
Do not hand-edit the generated scripts — `--check` is a byte comparison, so an
|
|
119
120
|
edit shows up as drift and the next `install` overwrites it. **Repo-specific
|
|
120
121
|
caveats go in `.claude/duro-hook.local.md`**: the hook appends that file after
|
|
121
122
|
the catalog, and regeneration leaves it alone. Use it for the things the
|
|
@@ -136,6 +137,24 @@ what the session-start injection gained since. After a major, re-run
|
|
|
136
137
|
`hook install` in every consumer and delete `.claude/.duro-session.cache*`
|
|
137
138
|
(the payload is cached seven days).
|
|
138
139
|
|
|
140
|
+
### Plan-mode nudge
|
|
141
|
+
|
|
142
|
+
The second hook runs on Claude Code's `UserPromptSubmit`. While the session is
|
|
143
|
+
in plan mode (the event's `permission_mode` is `"plan"`) it adds five lines to
|
|
144
|
+
the planner's context: a plan that adds a new screen, or makes a material
|
|
145
|
+
choice in layout, hierarchy or interaction, starts by picking a direction —
|
|
146
|
+
load the `duro-mockup` skill and draw 2–4 directions with `duro mockup` before
|
|
147
|
+
`ExitPlanMode` (fleet decision `handoff.fresh-directions-trigger`). A fix that
|
|
148
|
+
restores an existing design reuses the picked artboard; any other UI change
|
|
149
|
+
needs no mockup. So fresh directions come up while planning without anyone
|
|
150
|
+
typing `/duro-mockup`.
|
|
151
|
+
|
|
152
|
+
Outside plan mode, and on any payload it cannot read, it prints nothing. It
|
|
153
|
+
runs on every prompt, so it is plain `sh` and one `grep` over the event JSON —
|
|
154
|
+
no npx, no node, no network — and every path exits 0: it can never block or
|
|
155
|
+
fail a prompt. Existing `UserPromptSubmit` hooks in `settings.json` are kept;
|
|
156
|
+
the duro entry joins the array.
|
|
157
|
+
|
|
139
158
|
## duro doctor
|
|
140
159
|
|
|
141
160
|
The design system's stylesheet is layered: `reset`, then StyleX's
|
|
@@ -207,3 +226,7 @@ check, publish the canvas, implement the picked direction from its
|
|
|
207
226
|
`data-duro` map, screenshot the built route and put artboard and screenshot
|
|
208
227
|
side by side in the PR — and it explicitly overrides the `design` skill's
|
|
209
228
|
"lift resolved values" step. Repo notes go in `.claude/duro-mockup.local.md`.
|
|
229
|
+
Its description triggers on a request for a screen, page or mockups, and also
|
|
230
|
+
while planning (plan mode) a new screen or a material layout, hierarchy or
|
|
231
|
+
interaction change — the moment the [plan-mode nudge](#plan-mode-nudge) points
|
|
232
|
+
at it.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/commands/hook.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAA;AAClD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/commands/hook.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAA;AAClD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,aAAa,CAAA;AAoC9C,MAAM,WAAW,WAAW;IAC1B,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,8CAA8C;IAC9C,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAID,wBAAgB,OAAO,CACrB,QAAQ,EAAE,QAAQ,EAClB,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,GAAE,WAAgB,GACxB,aAAa,CAyBf"}
|
package/dist/commands/hook.js
CHANGED
|
@@ -4,7 +4,7 @@ import { dirname, join } from 'node:path';
|
|
|
4
4
|
import { runList } from './list.js';
|
|
5
5
|
import { relatedPairs } from '../disambiguation.js';
|
|
6
6
|
import { renderPairs } from '../format.js';
|
|
7
|
-
import { GITIGNORE_BLOCK, HOOK_CACHE_IGNORE, HOOK_COMMAND, HOOK_NOTES_PATH, HOOK_SCRIPT, HOOK_SCRIPT_PATH, HOOK_SETTINGS_PATH, } from '../hook-script.js';
|
|
7
|
+
import { GITIGNORE_BLOCK, HOOK_CACHE_IGNORE, HOOK_COMMAND, HOOK_NOTES_PATH, HOOK_SCRIPT, HOOK_SCRIPT_PATH, HOOK_SETTINGS_PATH, PLAN_HOOK_COMMAND, PLAN_HOOK_SCRIPT, PLAN_HOOK_SCRIPT_PATH, } from '../hook-script.js';
|
|
8
8
|
// Injected into agent context by a Claude Code SessionStart hook. The point
|
|
9
9
|
// is to turn "consult the design system before building UI" from a remembered
|
|
10
10
|
// step into ambient context: with the catalog already in front of it, an
|
|
@@ -80,28 +80,39 @@ function write(path, content) {
|
|
|
80
80
|
mkdirSync(dirname(path), { recursive: true });
|
|
81
81
|
writeFileSync(path, content, 'utf8');
|
|
82
82
|
}
|
|
83
|
+
const WIRINGS = [
|
|
84
|
+
{ event: 'SessionStart', script: HOOK_SCRIPT_PATH, command: HOOK_COMMAND },
|
|
85
|
+
{ event: 'UserPromptSubmit', script: PLAN_HOOK_SCRIPT_PATH, command: PLAN_HOOK_COMMAND },
|
|
86
|
+
];
|
|
83
87
|
/**
|
|
84
|
-
* Add
|
|
88
|
+
* Add each event's entry without disturbing anything else in the file:
|
|
85
89
|
* consuming repos keep their own hooks, permissions and skill overrides here.
|
|
86
|
-
* Returns null when the file is
|
|
90
|
+
* Returns the events that changed, and null for the text when the file is
|
|
91
|
+
* already wired correctly.
|
|
87
92
|
*/
|
|
88
93
|
function nextSettings(current) {
|
|
89
94
|
const parsed = current === null ? {} : JSON.parse(current);
|
|
90
95
|
const hooks = (parsed.hooks ?? (parsed.hooks = {}));
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
const events = [];
|
|
97
|
+
for (const { event, script, command } of WIRINGS) {
|
|
98
|
+
const groups = (hooks[event] ?? (hooks[event] = []));
|
|
99
|
+
const ours = groups
|
|
100
|
+
.flatMap((group) => group.hooks ?? [])
|
|
101
|
+
.find((entry) => entry.command?.includes(script));
|
|
102
|
+
if (ours) {
|
|
103
|
+
if (ours.command === command && ours.type === 'command')
|
|
104
|
+
continue;
|
|
105
|
+
ours.type = 'command';
|
|
106
|
+
ours.command = command;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
groups.push({ hooks: [{ type: 'command', command }] });
|
|
110
|
+
}
|
|
111
|
+
events.push(event);
|
|
103
112
|
}
|
|
104
|
-
|
|
113
|
+
if (events.length === 0)
|
|
114
|
+
return { text: null, events };
|
|
115
|
+
return { text: JSON.stringify(parsed, null, 2) + '\n', events };
|
|
105
116
|
}
|
|
106
117
|
/** Append the cache to .gitignore, unless some rule already covers the path. */
|
|
107
118
|
function nextGitignore(current) {
|
|
@@ -120,7 +131,7 @@ function nextGitignore(current) {
|
|
|
120
131
|
* to nobody. Best-effort: no git, no warning.
|
|
121
132
|
*/
|
|
122
133
|
function ignoredTrackables(root) {
|
|
123
|
-
const candidates = [HOOK_SCRIPT_PATH, HOOK_SETTINGS_PATH, HOOK_NOTES_PATH];
|
|
134
|
+
const candidates = [HOOK_SCRIPT_PATH, PLAN_HOOK_SCRIPT_PATH, HOOK_SETTINGS_PATH, HOOK_NOTES_PATH];
|
|
124
135
|
try {
|
|
125
136
|
const out = execFileSync('git', ['check-ignore', '--', ...candidates], {
|
|
126
137
|
cwd: root,
|
|
@@ -151,8 +162,13 @@ function runHookInstall(options) {
|
|
|
151
162
|
write(at(path), next);
|
|
152
163
|
changes.push({ path, status: 'written', detail });
|
|
153
164
|
};
|
|
154
|
-
const
|
|
155
|
-
|
|
165
|
+
for (const [path, content] of [
|
|
166
|
+
[HOOK_SCRIPT_PATH, HOOK_SCRIPT],
|
|
167
|
+
[PLAN_HOOK_SCRIPT_PATH, PLAN_HOOK_SCRIPT],
|
|
168
|
+
]) {
|
|
169
|
+
const script = read(at(path));
|
|
170
|
+
apply(path, script === content ? null : content, script === null ? 'created' : 'regenerated');
|
|
171
|
+
}
|
|
156
172
|
const settingsRaw = read(at(HOOK_SETTINGS_PATH));
|
|
157
173
|
let settingsNext;
|
|
158
174
|
try {
|
|
@@ -165,7 +181,7 @@ function runHookInstall(options) {
|
|
|
165
181
|
exitCode: 2,
|
|
166
182
|
};
|
|
167
183
|
}
|
|
168
|
-
apply(HOOK_SETTINGS_PATH, settingsNext, settingsRaw === null ? 'created' : '
|
|
184
|
+
apply(HOOK_SETTINGS_PATH, settingsNext.text, settingsRaw === null ? 'created' : `${settingsNext.events.join(' + ')} entry wired`);
|
|
169
185
|
const gitignoreRaw = read(at('.gitignore'));
|
|
170
186
|
apply('.gitignore', nextGitignore(gitignoreRaw), `ignores ${HOOK_CACHE_IGNORE}`);
|
|
171
187
|
const stale = changes.filter((change) => change.status === 'stale');
|
|
@@ -185,7 +201,7 @@ function runHookInstall(options) {
|
|
|
185
201
|
const ignored = ignoredTrackables(root);
|
|
186
202
|
return {
|
|
187
203
|
text: [
|
|
188
|
-
'Duro SessionStart
|
|
204
|
+
'Duro hooks installed: SessionStart (catalog) and UserPromptSubmit (plan-mode nudge).',
|
|
189
205
|
...lines,
|
|
190
206
|
'',
|
|
191
207
|
`Repo-specific caveats: put them in ${HOOK_NOTES_PATH} — the hook appends that file`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../../src/commands/hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAC,SAAS,EAAE,YAAY,EAAE,aAAa,EAAC,MAAM,SAAS,CAAA;AAC9D,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,MAAM,WAAW,CAAA;AAGvC,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AACjC,OAAO,EAAC,YAAY,EAAC,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAC,WAAW,EAAC,MAAM,cAAc,CAAA;AACxC,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../../src/commands/hook.ts"],"names":[],"mappings":"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"}
|
package/dist/hook-script.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The generated Claude Code SessionStart
|
|
2
|
+
* The generated Claude Code hooks — SessionStart (catalog) and UserPromptSubmit
|
|
3
|
+
* (plan-mode nudge) — and the wiring they need.
|
|
3
4
|
*
|
|
4
5
|
* Kept as data (not prose in a README) so `duro hook install` can write it and
|
|
5
6
|
* `duro hook install --check` can byte-compare it: every consuming repo runs
|
|
@@ -34,5 +35,26 @@ export declare const HOOK_COMMAND = "sh .claude/hooks/duro-catalog.sh";
|
|
|
34
35
|
*/
|
|
35
36
|
export declare const HOOK_MIN_CLI = "3.4.0";
|
|
36
37
|
export declare const HOOK_SCRIPT = "#!/bin/sh\n# Duro design-system session bootstrap \u2014 GENERATED by `duro hook install`.\n# Regenerate: npx -y @duro-app/cli hook install (--check verifies it in CI)\n#\n# Injects the consult-first preamble plus the component catalog into agent\n# context at SessionStart, so \"check the design system before building UI\" is\n# ambient context instead of a step an agent has to remember. Then runs\n# `duro doctor`, which says nothing unless this repo's build wiring flattens\n# the components' styles.\n#\n# Do not hand-edit. Repo-specific caveats go in\n# .claude/duro-hook.local.md \u2014 appended below, and left alone by regeneration.\ncache=\".claude/.duro-session.cache\"\nnotes=\".claude/duro-hook.local.md\"\ndoctor=\".claude/.duro-session.cache.doctor\"\n\n# npx resolution dominates session start; the catalog only changes on upgrade.\nif [ ! -s \"$cache\" ] || [ -n \"$(find \"$cache\" -mtime +7 2>/dev/null)\" ]; then\n if npx -y @duro-app/cli@^3.4.0 hook session-start >\"$cache.tmp\" 2>/dev/null; then\n mv \"$cache.tmp\" \"$cache\"\n else\n rm -f \"$cache.tmp\"\n fi\nfi\n\n# Offline with no cache yet: stay silent rather than fail the session.\nif [ -s \"$cache\" ]; then\n cat \"$cache\"\n if [ -s \"$notes\" ]; then\n echo\n cat \"$notes\"\n fi\nfi\n\n# duro doctor reads build config, entries and stylesheets. Re-run it when one\n# of those changes rather than on a timer, so an edit shows up next session\n# and a healthy repo only pays for npx after touching its config.\nkey=$(find . -maxdepth 4 \\( -name node_modules -o -name .git -o -name dist -o -name build \\) -prune -o \\\n -type f \\( -name package.json -o -name pnpm-workspace.yaml -o -name '*.css' -o -name 'vite*' \\\n -o -name 'babel*' -o -name '.babelrc*' -o -name '*.babel.*' -o -name 'postcss*' \\\n -o -name 'root.tsx' -o -name 'main.tsx' -o -name 'index.tsx' \\) -exec cksum {} + 2>/dev/null |\n sort | cksum)\nif [ \"$key\" != \"$(cat \"$doctor.key\" 2>/dev/null)\" ]; then\n if npx -y @duro-app/cli@^3.4.0 doctor --session >\"$doctor.tmp\" 2>/dev/null; then\n mv \"$doctor.tmp\" \"$doctor\"\n echo \"$key\" >\"$doctor.key\"\n else\n rm -f \"$doctor.tmp\"\n fi\nfi\nif [ -s \"$doctor\" ]; then\n echo\n cat \"$doctor\"\nfi\n";
|
|
38
|
+
/**
|
|
39
|
+
* The UserPromptSubmit hook: a plan-mode nudge toward fresh mockup
|
|
40
|
+
* directions (fleet decision `handoff.fresh-directions-trigger`), so a plan
|
|
41
|
+
* that adds a screen or makes a material UI choice puts the direction pick
|
|
42
|
+
* first without anyone typing /duro-mockup.
|
|
43
|
+
*/
|
|
44
|
+
export declare const PLAN_HOOK_SCRIPT_PATH = ".claude/hooks/duro-plan.sh";
|
|
45
|
+
/** What settings.json runs on UserPromptSubmit. Also the marker install matches on. */
|
|
46
|
+
export declare const PLAN_HOOK_COMMAND = "sh .claude/hooks/duro-plan.sh";
|
|
47
|
+
/** Printed into the planner's context, and only while in plan mode. */
|
|
48
|
+
export declare const PLAN_NUDGE = "Planning in a Duro consumer. If this plan adds a new screen, or makes a material choice in\nlayout, hierarchy or interaction (handoff.fresh-directions-trigger), its first \uD83E\uDDD1 decision is\npicking a direction: load the duro-mockup skill now (duro skill install if this repo lacks it)\nand draw 2\u20134 directions with `duro mockup` before ExitPlanMode. A fix that restores an existing\ndesign reuses the picked artboard instead; any other UI change needs no mockup.\n";
|
|
49
|
+
/**
|
|
50
|
+
* Runs on every prompt, so it is pure sh with one grep: no npx, no node, no
|
|
51
|
+
* network. Claude Code sends the event as one JSON object on stdin, and
|
|
52
|
+
* stdout of a UserPromptSubmit hook that exits 0 joins the model's context.
|
|
53
|
+
* Every path exits 0 — exit 2 would block the prompt. The whole payload is
|
|
54
|
+
* read before grep runs, so an early grep exit never breaks Claude Code's
|
|
55
|
+
* write. A `"permission_mode":"plan"` typed inside the prompt arrives
|
|
56
|
+
* escaped (\"permission_mode\"), which the pattern does not match.
|
|
57
|
+
*/
|
|
58
|
+
export declare const PLAN_HOOK_SCRIPT = "#!/bin/sh\n# Duro plan-mode nudge \u2014 GENERATED by `duro hook install`.\n# Regenerate: npx -y @duro-app/cli hook install (--check verifies it in CI)\n#\n# Claude Code runs this on every UserPromptSubmit with the event as JSON on\n# stdin. In plan mode it prints a short nudge toward fresh mockup directions\n# (handoff.fresh-directions-trigger); otherwise, or on input it cannot read,\n# it prints nothing. Always exits 0: it must never fail the prompt.\n#\n# Do not hand-edit.\n{ payload=$(cat); } 2>/dev/null || exit 0\nif printf '%s\\n' \"$payload\" |\n grep -Eq '\"permission_mode\"[[:space:]]*:[[:space:]]*\"plan\"' 2>/dev/null; then\n cat <<'NUDGE'\nPlanning in a Duro consumer. If this plan adds a new screen, or makes a material choice in\nlayout, hierarchy or interaction (handoff.fresh-directions-trigger), its first \uD83E\uDDD1 decision is\npicking a direction: load the duro-mockup skill now (duro skill install if this repo lacks it)\nand draw 2\u20134 directions with `duro mockup` before ExitPlanMode. A fix that restores an existing\ndesign reuses the picked artboard instead; any other UI change needs no mockup.\nNUDGE\nfi\nexit 0\n";
|
|
37
59
|
export declare const GITIGNORE_BLOCK = "# Duro session-hook cache (duro hook install)\n.claude/.duro-session.cache*\n";
|
|
38
60
|
//# sourceMappingURL=hook-script.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-script.d.ts","sourceRoot":"","sources":["../src/hook-script.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"hook-script.d.ts","sourceRoot":"","sources":["../src/hook-script.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,yCAAyC;AACzC,eAAO,MAAM,gBAAgB,kCAAkC,CAAA;AAC/D,eAAO,MAAM,kBAAkB,0BAA0B,CAAA;AACzD,eAAO,MAAM,eAAe,gCAAgC,CAAA;AAC5D;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,uCAA8B,CAAA;AACjE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,iCAAwB,CAAA;AACtD,gFAAgF;AAChF,eAAO,MAAM,eAAe,+BAA+B,CAAA;AAE3D,mEAAmE;AACnE,eAAO,MAAM,YAAY,qCAA2B,CAAA;AAEpD;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,UAAU,CAAA;AAEnC,eAAO,MAAM,WAAW,qtEAsDvB,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,+BAA+B,CAAA;AAEjE,uFAAuF;AACvF,eAAO,MAAM,iBAAiB,kCAAgC,CAAA;AAE9D,uEAAuE;AACvE,eAAO,MAAM,UAAU,ieAKtB,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,2oCAiB5B,CAAA;AAED,eAAO,MAAM,eAAe,kFAA0E,CAAA"}
|
package/dist/hook-script.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The generated Claude Code SessionStart
|
|
2
|
+
* The generated Claude Code hooks — SessionStart (catalog) and UserPromptSubmit
|
|
3
|
+
* (plan-mode nudge) — and the wiring they need.
|
|
3
4
|
*
|
|
4
5
|
* Kept as data (not prose in a README) so `duro hook install` can write it and
|
|
5
6
|
* `duro hook install --check` can byte-compare it: every consuming repo runs
|
|
@@ -88,5 +89,48 @@ if [ -s "$doctor" ]; then
|
|
|
88
89
|
cat "$doctor"
|
|
89
90
|
fi
|
|
90
91
|
`;
|
|
92
|
+
/**
|
|
93
|
+
* The UserPromptSubmit hook: a plan-mode nudge toward fresh mockup
|
|
94
|
+
* directions (fleet decision `handoff.fresh-directions-trigger`), so a plan
|
|
95
|
+
* that adds a screen or makes a material UI choice puts the direction pick
|
|
96
|
+
* first without anyone typing /duro-mockup.
|
|
97
|
+
*/
|
|
98
|
+
export const PLAN_HOOK_SCRIPT_PATH = '.claude/hooks/duro-plan.sh';
|
|
99
|
+
/** What settings.json runs on UserPromptSubmit. Also the marker install matches on. */
|
|
100
|
+
export const PLAN_HOOK_COMMAND = `sh ${PLAN_HOOK_SCRIPT_PATH}`;
|
|
101
|
+
/** Printed into the planner's context, and only while in plan mode. */
|
|
102
|
+
export const PLAN_NUDGE = `Planning in a Duro consumer. If this plan adds a new screen, or makes a material choice in
|
|
103
|
+
layout, hierarchy or interaction (handoff.fresh-directions-trigger), its first 🧑 decision is
|
|
104
|
+
picking a direction: load the duro-mockup skill now (duro skill install if this repo lacks it)
|
|
105
|
+
and draw 2–4 directions with \`duro mockup\` before ExitPlanMode. A fix that restores an existing
|
|
106
|
+
design reuses the picked artboard instead; any other UI change needs no mockup.
|
|
107
|
+
`;
|
|
108
|
+
/**
|
|
109
|
+
* Runs on every prompt, so it is pure sh with one grep: no npx, no node, no
|
|
110
|
+
* network. Claude Code sends the event as one JSON object on stdin, and
|
|
111
|
+
* stdout of a UserPromptSubmit hook that exits 0 joins the model's context.
|
|
112
|
+
* Every path exits 0 — exit 2 would block the prompt. The whole payload is
|
|
113
|
+
* read before grep runs, so an early grep exit never breaks Claude Code's
|
|
114
|
+
* write. A `"permission_mode":"plan"` typed inside the prompt arrives
|
|
115
|
+
* escaped (\"permission_mode\"), which the pattern does not match.
|
|
116
|
+
*/
|
|
117
|
+
export const PLAN_HOOK_SCRIPT = `#!/bin/sh
|
|
118
|
+
# Duro plan-mode nudge — GENERATED by \`duro hook install\`.
|
|
119
|
+
# Regenerate: npx -y @duro-app/cli hook install (--check verifies it in CI)
|
|
120
|
+
#
|
|
121
|
+
# Claude Code runs this on every UserPromptSubmit with the event as JSON on
|
|
122
|
+
# stdin. In plan mode it prints a short nudge toward fresh mockup directions
|
|
123
|
+
# (handoff.fresh-directions-trigger); otherwise, or on input it cannot read,
|
|
124
|
+
# it prints nothing. Always exits 0: it must never fail the prompt.
|
|
125
|
+
#
|
|
126
|
+
# Do not hand-edit.
|
|
127
|
+
{ payload=$(cat); } 2>/dev/null || exit 0
|
|
128
|
+
if printf '%s\\n' "$payload" |
|
|
129
|
+
grep -Eq '"permission_mode"[[:space:]]*:[[:space:]]*"plan"' 2>/dev/null; then
|
|
130
|
+
cat <<'NUDGE'
|
|
131
|
+
${PLAN_NUDGE}NUDGE
|
|
132
|
+
fi
|
|
133
|
+
exit 0
|
|
134
|
+
`;
|
|
91
135
|
export const GITIGNORE_BLOCK = `# Duro session-hook cache (duro hook install)\n${HOOK_CACHE_IGNORE}\n`;
|
|
92
136
|
//# sourceMappingURL=hook-script.js.map
|
package/dist/hook-script.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-script.js","sourceRoot":"","sources":["../src/hook-script.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"hook-script.js","sourceRoot":"","sources":["../src/hook-script.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,yCAAyC;AACzC,MAAM,CAAC,MAAM,gBAAgB,GAAG,+BAA+B,CAAA;AAC/D,MAAM,CAAC,MAAM,kBAAkB,GAAG,uBAAuB,CAAA;AACzD,MAAM,CAAC,MAAM,eAAe,GAAG,6BAA6B,CAAA;AAC5D;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,eAAe,SAAS,CAAA;AACjE;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,eAAe,GAAG,CAAA;AACtD,gFAAgF;AAChF,MAAM,CAAC,MAAM,eAAe,GAAG,4BAA4B,CAAA;AAE3D,mEAAmE;AACnE,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,gBAAgB,EAAE,CAAA;AAEpD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAA;AAEnC,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;IAWvB,eAAe;SACV,eAAe;SACf,eAAe;UACd,sBAAsB;;;;6BAIH,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;6BAyBZ,YAAY;;;;;;;;;;;CAWxC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,4BAA4B,CAAA;AAEjE,uFAAuF;AACvF,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,qBAAqB,EAAE,CAAA;AAE9D,uEAAuE;AACvE,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;CAKzB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;EAc9B,UAAU;;;CAGX,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,kDAAkD,iBAAiB,IAAI,CAAA"}
|
package/dist/manifest.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,qBAAqB,CAAA;AAGjD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;IACxF,KAAK,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;IAC7E,OAAO,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,CAAA;IAC7C,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,eAAO,MAAM,QAAQ,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,qBAAqB,CAAA;AAGjD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;IACxF,KAAK,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;IAC7E,OAAO,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,CAAA;IAC7C,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,eAAO,MAAM,QAAQ,EAAE,WAAW,EA6KjC,CAAA;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;EAqBhE"}
|
package/dist/manifest.js
CHANGED
|
@@ -55,12 +55,12 @@ export const COMMANDS = [
|
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
name: 'hook',
|
|
58
|
-
summary: 'Claude Code
|
|
58
|
+
summary: 'Claude Code hooks: session-start prints the consult-first preamble + full catalog; install wires it (SessionStart) plus a plan-mode mockup nudge (UserPromptSubmit) into the current repo',
|
|
59
59
|
args: [
|
|
60
60
|
{
|
|
61
61
|
name: 'event',
|
|
62
62
|
required: true,
|
|
63
|
-
description: 'session-start (print the bootstrap) | install (wire
|
|
63
|
+
description: 'session-start (print the bootstrap) | install (wire both hooks into this repo)',
|
|
64
64
|
valuesFrom: 'events',
|
|
65
65
|
},
|
|
66
66
|
],
|
package/dist/manifest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAA;AAgBzC,MAAM,CAAC,MAAM,QAAQ,GAAkB;IACrC;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EACL,+LAA+L;QACjM,IAAI,EAAE;YACJ;gBACE,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,IAAI;gBACd,WAAW,EACT,uGAAuG;gBACzG,UAAU,EAAE,OAAO;aACpB;SACF;QACD,KAAK,EAAE;YACL,EAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,yCAAyC,EAAC;YACtF,EAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,iBAAiB,EAAC;YAChE,EAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,oCAAoC,EAAC;SAC1F;QACD,OAAO,EAAE;YACP,KAAK,EACH,wFAAwF;YAC1F,WAAW,EAAE,uEAAuE;SACrF;QACD,QAAQ,EAAE;YACR,aAAa;YACb,yBAAyB;YACzB,+BAA+B;YAC/B,uBAAuB;SACxB;QACD,OAAO,EAAE,gBAAgB;KAC1B;IACD;QACE,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,yCAAyC;QAClD,IAAI,EAAE;YACJ;gBACE,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,KAAK;gBACf,WAAW,EAAE,8CAA8C;gBAC3D,UAAU,EAAE,OAAO;aACpB;SACF;QACD,KAAK,EAAE,EAAE;QACT,OAAO,EAAE,EAAC,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,wCAAwC,EAAC;QACtF,QAAQ,EAAE,CAAC,WAAW,EAAE,0BAA0B,CAAC;QACnD,OAAO,EAAE,cAAc;KACxB;IACD;QACE,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,8EAA8E;QACvF,IAAI,EAAE,EAAE;QACR,KAAK,EAAE,EAAE;QACT,OAAO,EAAE,EAAC,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,mDAAmD,EAAC;QAC9F,QAAQ,EAAE,CAAC,sBAAsB,CAAC;QAClC,OAAO,EAAE,kBAAkB;KAC5B;IACD;QACE,IAAI,EAAE,MAAM;QACZ,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}
|
package/dist/skill-template.d.ts
CHANGED
|
@@ -18,5 +18,5 @@ export declare const SKILL_NOTES_PATH = ".claude/duro-mockup.local.md";
|
|
|
18
18
|
* floating pin keeps patch releases from showing up as --check drift.
|
|
19
19
|
*/
|
|
20
20
|
export declare const SKILL_MIN_CLI = "3.0.0";
|
|
21
|
-
export declare const SKILL = "---\nname: duro-mockup\ndescription: Take a new screen from mockup directions to an implemented, verified PR without leaving the Duro design system. Use whenever the user asks for a screen, a page, a UI proposal, mockups, or \"show me options\" in a repo that consumes @duro-app/ui.\n---\n\n# duro-mockup\n\nGENERATED by `duro skill install` \u2014 do not hand-edit. Regenerate:\n`npx -y @duro-app/cli@^3.0.0 skill install` (`--check` verifies it in CI). Repo-specific notes\nlive in `.claude/duro-mockup.local.md`; read it if it exists.\n\nStanding rule (fleet decision `ui.mockup-handoff`): a mockup is written in\nthe design system's own vocabulary, and the direction the user picks is a\ncontract. What binds: layout, hierarchy, states, the `data-duro` component\nmap and the token names. What does not bind: the artboard's pixel geometry.\n\n**This overrides the `design` skill's step 0.** That skill says to lift\nresolved values from the real stylesheets. Here you never write a resolved\nvalue: every colour, spacing, radius, font, shadow and duration is a\n`var(--duro-*)` from the seeded token block, and every element a person can\nact on carries `data-duro=\"<Component>\"`. The rest of the `design` skill\n(artboard shape, `seed-canvas.mjs`, the canvas Artifact) still applies.\n\n## Args\n\n- `propose <screen>` \u2014 seed 2\u20134 direction artboards, check them, publish the canvas.\n- `implement <screen> <direction>` \u2014 build the picked direction from its component map.\n- `prove <route>` \u2014 screenshot the built route and assemble the PR section.\n- no args \u2014 infer the stage from what exists under `docs/mockups/<screen>/`.\n\n## propose\n\n```bash\nnpx -y @duro-app/cli@^3.0.0 mockup seed --out docs/mockups/<screen> --name Main # direction A\nnpx -y @duro-app/cli@^3.0.0 mockup seed --out docs/mockups/<screen> --name <Direction> # B, C, \u2026\n```\n\n- `<screen>` is kebab-case (`approval-gates`). Artboards are committed with\n the PR \u2014 never leave them in a scratch directory; a session scratchpad is\n garbage-collected and the canvas URL would be the only surviving artefact.\n- Author each artboard inside the seeded `<style>` and root div. Colours,\n spacing, radii, type, shadows and motion come only from the token block;\n widths, heights and positions are yours. Every button, row, chip, tab,\n input, link, table and dialog \u2014 whatever element it is drawn as \u2014 carries\n `data-duro=\"<Component>\"` or `data-duro=\"<Component>.<Part>\"`, using names\n from `npx -y @duro-app/cli@^3.0.0 list components`. Notes for the implementer go after the\n name: `data-duro=\"Button variant=primary\"`.\n- Start from a recipe when one fits the screen shape (`npx -y @duro-app/cli@^3.0.0 list recipes`\n \u2014 admin-detail-page, split-pane, page-with-sidenav, settings-page, \u2026) and\n say so in the artboard's header comment; a hand-rolled layout needs a reason.\n- Gate before publishing:\n\n```bash\nnpx -y @duro-app/cli@^3.0.0 mockup check docs/mockups/<screen>/*.dc.html\n```\n\n Exit 1 lists `file:line rule: message`. Fix every finding; do not publish\n an artboard that fails. Then seed the canvas with the `design` skill's\n helper (it lives under the current Claude Code version's bundled-skills\n directory \u2014 locate it, never hardcode the path) and publish the Artifact.\n- Ask the user to pick a direction. Record the answer as\n `<canvas URL> \u00B7 direction <X>` \u2014 it goes in the PR.\n- If the user edits an artboard in the canvas editor, its property panel\n writes literals back over `var()` by design. Re-extract and run\n `mockup check` again, but treat those findings as advisory: the authored\n file in `docs/mockups/` is the contract, the canvas is the review surface.\n\n## implement\n\n- Read the picked artboard. Its `data-duro` attributes ARE the component\n list: for each one, `npx -y @duro-app/cli@^3.0.0 <Component>` (or the `duro_ds_lookup` MCP\n tool) gives props and usage. Do not substitute a hand-rolled element for a\n named component; if a named component does not fit, say so and propose the\n design-system change rather than working around it.\n- Tokens map one to one: `var(--duro-spacing-md)` is `spacing.md` from\n `@duro-app/tokens/tokens/spacing.css`, `--duro-color-bg-card` is\n `colors.bgCard`, `--duro-radius-sm` is `radii.sm`, `--duro-typography-*`\n and `--duro-type-scale-*` are `typography`/`typeScale`, `--duro-shadow-*`\n is `shadows`, `--duro-duration-*`/`--duro-easing-*` are `duration`/`easing`.\n A media query on `768px` is `breakpoints.md` from\n `@duro-app/tokens/tokens/breakpoints.css`. The lint config refuses the raw\n value, so write the token the first time.\n- Layout comes from a recipe before a grid: `npx -y @duro-app/cli@^3.0.0 <recipe> --source-only`.\n- Geometry in the artboard is a sketch. Match its hierarchy and states, not\n its pixel widths.\n\n## prove\n\n- Run the app, load the route in a real browser (Chrome MCP), and take a\n screenshot at the artboard's width (1120px) in the artboard's theme.\n- The PR body carries, under `## Mockup`: the canvas URL and direction, the\n path `docs/mockups/<screen>/`, and the artboard next to the live screenshot.\n A PR that touches no screen writes `Mockup: none` instead. CI checks for\n one or the other.\n- If the screenshot and the artboard disagree on hierarchy or states, that is\n a finding to fix, not a caption to write.\n";
|
|
21
|
+
export declare const SKILL = "---\nname: duro-mockup\ndescription: Take a new screen from mockup directions to an implemented, verified PR without leaving the Duro design system. Use whenever the user asks for a screen, a page, a UI proposal, mockups, or \"show me options\" in a repo that consumes @duro-app/ui \u2014 and when planning (plan mode) a new screen or a material layout, hierarchy or interaction change there.\n---\n\n# duro-mockup\n\nGENERATED by `duro skill install` \u2014 do not hand-edit. Regenerate:\n`npx -y @duro-app/cli@^3.0.0 skill install` (`--check` verifies it in CI). Repo-specific notes\nlive in `.claude/duro-mockup.local.md`; read it if it exists.\n\nStanding rule (fleet decision `ui.mockup-handoff`): a mockup is written in\nthe design system's own vocabulary, and the direction the user picks is a\ncontract. What binds: layout, hierarchy, states, the `data-duro` component\nmap and the token names. What does not bind: the artboard's pixel geometry.\n\n**This overrides the `design` skill's step 0.** That skill says to lift\nresolved values from the real stylesheets. Here you never write a resolved\nvalue: every colour, spacing, radius, font, shadow and duration is a\n`var(--duro-*)` from the seeded token block, and every element a person can\nact on carries `data-duro=\"<Component>\"`. The rest of the `design` skill\n(artboard shape, `seed-canvas.mjs`, the canvas Artifact) still applies.\n\n## Args\n\n- `propose <screen>` \u2014 seed 2\u20134 direction artboards, check them, publish the canvas.\n- `implement <screen> <direction>` \u2014 build the picked direction from its component map.\n- `prove <route>` \u2014 screenshot the built route and assemble the PR section.\n- no args \u2014 infer the stage from what exists under `docs/mockups/<screen>/`.\n\n## propose\n\n```bash\nnpx -y @duro-app/cli@^3.0.0 mockup seed --out docs/mockups/<screen> --name Main # direction A\nnpx -y @duro-app/cli@^3.0.0 mockup seed --out docs/mockups/<screen> --name <Direction> # B, C, \u2026\n```\n\n- `<screen>` is kebab-case (`approval-gates`). Artboards are committed with\n the PR \u2014 never leave them in a scratch directory; a session scratchpad is\n garbage-collected and the canvas URL would be the only surviving artefact.\n- Author each artboard inside the seeded `<style>` and root div. Colours,\n spacing, radii, type, shadows and motion come only from the token block;\n widths, heights and positions are yours. Every button, row, chip, tab,\n input, link, table and dialog \u2014 whatever element it is drawn as \u2014 carries\n `data-duro=\"<Component>\"` or `data-duro=\"<Component>.<Part>\"`, using names\n from `npx -y @duro-app/cli@^3.0.0 list components`. Notes for the implementer go after the\n name: `data-duro=\"Button variant=primary\"`.\n- Start from a recipe when one fits the screen shape (`npx -y @duro-app/cli@^3.0.0 list recipes`\n \u2014 admin-detail-page, split-pane, page-with-sidenav, settings-page, \u2026) and\n say so in the artboard's header comment; a hand-rolled layout needs a reason.\n- Gate before publishing:\n\n```bash\nnpx -y @duro-app/cli@^3.0.0 mockup check docs/mockups/<screen>/*.dc.html\n```\n\n Exit 1 lists `file:line rule: message`. Fix every finding; do not publish\n an artboard that fails. Then seed the canvas with the `design` skill's\n helper (it lives under the current Claude Code version's bundled-skills\n directory \u2014 locate it, never hardcode the path) and publish the Artifact.\n- Ask the user to pick a direction. Record the answer as\n `<canvas URL> \u00B7 direction <X>` \u2014 it goes in the PR.\n- If the user edits an artboard in the canvas editor, its property panel\n writes literals back over `var()` by design. Re-extract and run\n `mockup check` again, but treat those findings as advisory: the authored\n file in `docs/mockups/` is the contract, the canvas is the review surface.\n\n## implement\n\n- Read the picked artboard. Its `data-duro` attributes ARE the component\n list: for each one, `npx -y @duro-app/cli@^3.0.0 <Component>` (or the `duro_ds_lookup` MCP\n tool) gives props and usage. Do not substitute a hand-rolled element for a\n named component; if a named component does not fit, say so and propose the\n design-system change rather than working around it.\n- Tokens map one to one: `var(--duro-spacing-md)` is `spacing.md` from\n `@duro-app/tokens/tokens/spacing.css`, `--duro-color-bg-card` is\n `colors.bgCard`, `--duro-radius-sm` is `radii.sm`, `--duro-typography-*`\n and `--duro-type-scale-*` are `typography`/`typeScale`, `--duro-shadow-*`\n is `shadows`, `--duro-duration-*`/`--duro-easing-*` are `duration`/`easing`.\n A media query on `768px` is `breakpoints.md` from\n `@duro-app/tokens/tokens/breakpoints.css`. The lint config refuses the raw\n value, so write the token the first time.\n- Layout comes from a recipe before a grid: `npx -y @duro-app/cli@^3.0.0 <recipe> --source-only`.\n- Geometry in the artboard is a sketch. Match its hierarchy and states, not\n its pixel widths.\n\n## prove\n\n- Run the app, load the route in a real browser (Chrome MCP), and take a\n screenshot at the artboard's width (1120px) in the artboard's theme.\n- The PR body carries, under `## Mockup`: the canvas URL and direction, the\n path `docs/mockups/<screen>/`, and the artboard next to the live screenshot.\n A PR that touches no screen writes `Mockup: none` instead. CI checks for\n one or the other.\n- If the screenshot and the artboard disagree on hierarchy or states, that is\n a finding to fix, not a caption to write.\n";
|
|
22
22
|
//# sourceMappingURL=skill-template.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-template.d.ts","sourceRoot":"","sources":["../src/skill-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,4CAA4C;AAC5C,eAAO,MAAM,SAAS,+BAA+B,CAAA;AACrD,eAAO,MAAM,UAAU,wCAA0B,CAAA;AACjD,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,iCAAiC,CAAA;AAE9D;;;GAGG;AACH,eAAO,MAAM,aAAa,UAAU,CAAA;AAIpC,eAAO,MAAM,KAAK,
|
|
1
|
+
{"version":3,"file":"skill-template.d.ts","sourceRoot":"","sources":["../src/skill-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,4CAA4C;AAC5C,eAAO,MAAM,SAAS,+BAA+B,CAAA;AACrD,eAAO,MAAM,UAAU,wCAA0B,CAAA;AACjD,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,iCAAiC,CAAA;AAE9D;;;GAGG;AACH,eAAO,MAAM,aAAa,UAAU,CAAA;AAIpC,eAAO,MAAM,KAAK,88KAgGjB,CAAA"}
|
package/dist/skill-template.js
CHANGED
|
@@ -21,7 +21,7 @@ export const SKILL_MIN_CLI = '3.0.0';
|
|
|
21
21
|
const DURO = `npx -y @duro-app/cli@^${SKILL_MIN_CLI}`;
|
|
22
22
|
export const SKILL = `---
|
|
23
23
|
name: duro-mockup
|
|
24
|
-
description: Take a new screen from mockup directions to an implemented, verified PR without leaving the Duro design system. Use whenever the user asks for a screen, a page, a UI proposal, mockups, or "show me options" in a repo that consumes @duro-app/ui.
|
|
24
|
+
description: Take a new screen from mockup directions to an implemented, verified PR without leaving the Duro design system. Use whenever the user asks for a screen, a page, a UI proposal, mockups, or "show me options" in a repo that consumes @duro-app/ui — and when planning (plan mode) a new screen or a material layout, hierarchy or interaction change there.
|
|
25
25
|
---
|
|
26
26
|
|
|
27
27
|
# duro-mockup
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duro-app/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Machine-queryable docs for the Duro design system: components, props, recipes, tokens and rules as CLI output, JSON, or an MCP server.",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@modelcontextprotocol/sdk": ">=1.0.0",
|
|
48
|
-
"@duro-app/ui": "^
|
|
48
|
+
"@duro-app/ui": "^4.0.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependenciesMeta": {
|
|
51
51
|
"@duro-app/ui": {
|
|
@@ -60,9 +60,9 @@
|
|
|
60
60
|
"@types/node": "^24.0.0",
|
|
61
61
|
"ts-morph": "^26.0.0",
|
|
62
62
|
"typescript": "^5.7.0",
|
|
63
|
-
"@duro-app/tokens": "^
|
|
64
|
-
"@duro-app/
|
|
65
|
-
"@duro-app/
|
|
63
|
+
"@duro-app/tokens": "^4.0.0",
|
|
64
|
+
"@duro-app/eslint-plugin": "^4.0.0",
|
|
65
|
+
"@duro-app/ui": "^4.0.0"
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
68
|
"registry": "node scripts/build-registry.mjs",
|
package/registry.json
CHANGED
|
@@ -2392,7 +2392,7 @@
|
|
|
2392
2392
|
"sourcePath": "packages/ui/src/components/Fieldset/Fieldset.tsx"
|
|
2393
2393
|
},
|
|
2394
2394
|
"Form": {
|
|
2395
|
-
"importPath": "@duro-app/ui",
|
|
2395
|
+
"importPath": "@duro-app/ui/form",
|
|
2396
2396
|
"kind": "component",
|
|
2397
2397
|
"meta": {
|
|
2398
2398
|
"description": "Form wrapper with Effect Schema validation and react-hook-form integration. Provides FormContext to child Field components.",
|
|
@@ -6622,7 +6622,7 @@
|
|
|
6622
6622
|
"peerDeps": [
|
|
6623
6623
|
"effect"
|
|
6624
6624
|
],
|
|
6625
|
-
"source": "import {Schema} from 'effect'\nimport {css, html} from 'react-strict-dom'\nimport {
|
|
6625
|
+
"source": "import {Schema} from 'effect'\nimport {css, html} from 'react-strict-dom'\nimport {Field, Input, Fieldset, Button, Stack, Heading} from '@duro-app/ui'\nimport {Form} from '@duro-app/ui/form'\n\nconst LoginSchema = Schema.Struct({\n username: Schema.String.pipe(\n Schema.minLength(3, {message: () => 'Username must be at least 3 characters'}),\n ),\n password: Schema.String.pipe(\n Schema.minLength(8, {message: () => 'Password must be at least 8 characters'}),\n ),\n})\n\nconst styles = css.create({\n wrap: {maxWidth: 400},\n})\n\nexport function LoginFormRecipe() {\n return (\n <html.div style={styles.wrap}>\n <Stack gap=\"lg\">\n <Heading level={2}>Log in</Heading>\n <Form\n schema={LoginSchema}\n defaultValues={{username: '', password: ''}}\n onSubmit={(data) => console.log('login', data)}\n >\n {({formState}) => (\n <Fieldset.Root gap=\"md\">\n <Field.Root name=\"username\">\n <Field.Label>Username</Field.Label>\n <Input placeholder=\"Enter username\" />\n <Field.Error />\n </Field.Root>\n\n <Field.Root name=\"password\">\n <Field.Label>Password</Field.Label>\n <Input type=\"password\" placeholder=\"Enter password\" />\n <Field.Error />\n </Field.Root>\n\n <Button type=\"submit\" disabled={!formState.isValid}>\n Log in\n </Button>\n </Fieldset.Root>\n )}\n </Form>\n </Stack>\n </html.div>\n )\n}\n",
|
|
6626
6626
|
"sourcePath": "docs/ai/recipes/login-form.recipe.tsx",
|
|
6627
6627
|
"title": "Login Form",
|
|
6628
6628
|
"usesComponents": [
|
|
@@ -6682,7 +6682,7 @@
|
|
|
6682
6682
|
"peerDeps": [
|
|
6683
6683
|
"effect"
|
|
6684
6684
|
],
|
|
6685
|
-
"source": "import {Schema} from 'effect'\nimport {css, html} from 'react-strict-dom'\nimport {PageShell, Tabs,
|
|
6685
|
+
"source": "import {Schema} from 'effect'\nimport {css, html} from 'react-strict-dom'\nimport {PageShell, Tabs, Field, Input, Fieldset, Button, Stack, Inline, Heading, Text, Card, Switch} from '@duro-app/ui'\nimport {Form} from '@duro-app/ui/form'\n\nconst ProfileSchema = Schema.Struct({\n displayName: Schema.String.pipe(Schema.minLength(1, {message: () => 'Display name is required'})),\n email: Schema.String.pipe(\n Schema.pattern(/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/, {message: () => 'Enter a valid email'}),\n ),\n})\n\nexport function SettingsPageRecipe() {\n return (\n <PageShell maxWidth=\"md\" padding=\"md\" header={<Heading level={1}>Settings</Heading>}>\n <Tabs.Root defaultValue=\"profile\">\n <Tabs.List>\n <Tabs.Tab value=\"profile\">Profile</Tabs.Tab>\n <Tabs.Tab value=\"notifications\">Notifications</Tabs.Tab>\n <Tabs.Tab value=\"security\">Security</Tabs.Tab>\n </Tabs.List>\n\n <Tabs.Panel value=\"profile\">\n <Stack gap=\"lg\">\n <Card>\n <Stack gap=\"lg\">\n <Heading level={3}>Profile Information</Heading>\n <Form\n schema={ProfileSchema}\n defaultValues={{displayName: '', email: ''}}\n onSubmit={(data) => console.log('save profile', data)}\n >\n {({formState}) => (\n <Fieldset.Root gap=\"md\">\n <Field.Root name=\"displayName\">\n <Field.Label>Display name</Field.Label>\n <Input placeholder=\"Your name\" />\n <Field.Error />\n </Field.Root>\n\n <Field.Root name=\"email\">\n <Field.Label>Email</Field.Label>\n <Input type=\"email\" placeholder=\"you@example.com\" />\n <Field.Error />\n </Field.Root>\n\n <Inline gap=\"sm\" justify=\"end\">\n <Button variant=\"secondary\">Cancel</Button>\n <Button type=\"submit\" disabled={!formState.isValid}>\n Save changes\n </Button>\n </Inline>\n </Fieldset.Root>\n )}\n </Form>\n </Stack>\n </Card>\n </Stack>\n </Tabs.Panel>\n\n <Tabs.Panel value=\"notifications\">\n <Card>\n <Stack gap=\"md\">\n <Heading level={3}>Notification Preferences</Heading>\n <Switch defaultChecked>Email notifications</Switch>\n <Switch>Push notifications</Switch>\n <Switch defaultChecked>Weekly digest</Switch>\n </Stack>\n </Card>\n </Tabs.Panel>\n\n <Tabs.Panel value=\"security\">\n <Card>\n <Stack gap=\"md\">\n <Heading level={3}>Security</Heading>\n <Text color=\"muted\">Security settings will appear here.</Text>\n </Stack>\n </Card>\n </Tabs.Panel>\n </Tabs.Root>\n </PageShell>\n )\n}\n",
|
|
6686
6686
|
"sourcePath": "docs/ai/recipes/settings-page.recipe.tsx",
|
|
6687
6687
|
"title": "Settings Page",
|
|
6688
6688
|
"usesComponents": [
|