@lemoncode/lemony 0.5.1 → 0.6.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.
@@ -20,6 +20,11 @@ ahead. That asymmetry shapes everything here:
20
20
  - **drift** is one-directional and deterministic: "has the JSON changed since the last
21
21
  export?" — a pure-JSON check that needs no tool connection.
22
22
 
23
+ **Which file.** `docs/design-tokens.json` is the default path. When `harness.config.yml` sets
24
+ `design_tokens.file`, the path it names (relative to the repository root) is the token file:
25
+ read the binding from it and expect the CLI's import and export to act on it, wherever this
26
+ skill says `docs/design-tokens.json`. Never create a second file at the default.
27
+
23
28
  You are invoked two ways: by `/sync-design-tokens` (the human's explicit handle, argument
24
29
  `import` or `export`), and at DEFINE when the drift check shows an export is pending and the
25
30
  current user has the tool connected.
@@ -80,10 +85,11 @@ variables and the DTCG JSON:
80
85
 
81
86
  The CLI maps this to the 3-tier DTCG model: `ref` → an alias `{path}`, `modes` →
82
87
  `$extensions["com.lemony.modes"]`, and the tier follows the path. A name without a tier
83
- lands on the one tier `docs/design-tokens.json` already has it in; otherwise a literal
84
- defaults to `primitive` and an alias to `semantic`. A bare `ref` — or a bare `{alias}` mode
85
- value — resolves to the tiered path its target lands at in the same neutral file, or to the
86
- one tier the token file has it in.
88
+ lands on the one tier `docs/design-tokens.json` already has it in; otherwise a variable that
89
+ is literal in every mode defaults to `primitive`, and one that is an alias in its base value
90
+ or any mode to `semantic` (a primitive is literal in every mode). A bare `ref` — or a bare
91
+ `{alias}` mode value — resolves to the tiered path its target lands at in the same neutral
92
+ file, or to the one tier the token file has it in.
87
93
 
88
94
  ## import — tool → JSON
89
95
 
@@ -109,9 +115,11 @@ one tier the token file has it in.
109
115
  `primitive` target is neither in the file nor in the slice: add the target to the same
110
116
  `--only`, or fix the file first. When the neutral file does not carry the target at
111
117
  all, the refusal says so: create it in the tool first, or leave out what references
112
- it. An `--only` path no variable lands at is refused rather than skipped. A refusal
113
- prints the preview again, so the slice can be re-curated from the paths it lists. Then run
114
- `lemony design-tokens validate` to confirm the result is well-formed.
118
+ it. A fault no slice fixes, such as a `primitive` that aliases, is named the same way:
119
+ fix that variable in the tool, or leave it out. An `--only` path no variable lands at
120
+ is refused rather than skipped. A refusal prints the preview again, so the slice can be
121
+ re-curated from the paths it lists. Then run `lemony design-tokens validate` to confirm
122
+ the result is well-formed.
115
123
 
116
124
  ## export — JSON → tool
117
125
 
@@ -75,7 +75,10 @@ the design direction it settles can inform the spec. Before the first question:
75
75
  [Persisting personas](#persisting-personas-when-absent)) — an opt-in offer on your human-facing
76
76
  surface, never an unasked write.
77
77
  - **Consume `docs/design-tokens.json` if it exists** — the single source of truth for tokens. The
78
- handoff §11 points at it; you never inline token values.
78
+ handoff §11 points at it; you never inline token values. That is the default path: when
79
+ `harness.config.yml` sets `design_tokens.file`, the path it names (relative to the repository
80
+ root) is the token file — read, scaffold, bind and point at that path wherever this skill says
81
+ `docs/design-tokens.json`, and never create a second file at the default.
79
82
  - **Read `docs/architecture.md` if it exists** — orient against the system's shape so design
80
83
  proposals don't contradict existing surfaces. Absent is fine; never push the user to create it.
81
84
 
@@ -84,7 +84,8 @@ Every section is **N/A-able** per task: mark a section N/A rather than padding i
84
84
 
85
85
  ## 11. Token reference
86
86
 
87
- Reference `docs/design-tokens.json` — the single, client-owned source of truth, a 3-tier
87
+ Reference the token file — `docs/design-tokens.json`, or the path `design_tokens.file` names in
88
+ `harness.config.yml` — the single, client-owned source of truth, a 3-tier
88
89
  W3C-DTCG model (`primitive` → `semantic` → `component`). **Never inline token values here; point at
89
90
  the file.** If the project has no token file yet, the interview offers to scaffold one (derived from
90
91
  the answers) or to connect a design tool; if the human declines both, say so and capture it as an
@@ -71,19 +71,33 @@ Without a script, each probe is yours to run. The mechanics:
71
71
  as accounted-for, is the ledger contract's rule, not this skill's — the floor asks
72
72
  for accounting, never exhaustion. One or two well-aimed probes per logic-bearing
73
73
  file usually answer the question.
74
- - **The round trip is one composite command with the revert unconditional.** Apply the
75
- mutation, run the focused test, and restore — `;`-separated or a scripted loop,
76
- **never `&&` before the revert** (a red test must still revert), and never separate
77
- edit / test / revert calls. It leaves the tree exactly as you found it:
74
+ - **The round trip is one composite command whose revert is a trap, installed before
75
+ the mutation.** Apply the mutation, run the focused test, and restore — in one
76
+ shell invocation, never separate edit / test / revert calls, and never a revert
77
+ that a red test or an interrupt can skip. The session can be stopped at any instant
78
+ (Esc stops the running command with SIGTERM), and a mutant left behind reads as
79
+ real work to whoever resumes. The `EXIT` trap restores on every way out — green,
80
+ red, or interrupted:
78
81
 
79
82
  ```bash
80
- sed -i.bak '128s/>=/>/' src/queue.ts; \
81
- npx vitest run src/queue.spec.ts; \
82
- mv src/queue.ts.bak src/queue.ts
83
+ ( trap 'mv -f src/queue.ts.bak src/queue.ts 2>/dev/null' EXIT; \
84
+ trap 'kill $! 2>/dev/null; exit 130' INT TERM HUP; \
85
+ sed -i.bak '128s/>=/>/' src/queue.ts; \
86
+ npx vitest run src/queue.spec.ts & wait $! )
83
87
  ```
84
88
 
85
89
  (Line-addressed on purpose — one targeted mutant per trip keeps the kill
86
- attributable; the `.bak` the in-place edit leaves is the revert.)
90
+ attributable; the `.bak` the in-place edit leaves is the revert. The subshell
91
+ scopes the traps to the probe; the `INT TERM HUP` trap stops the runner and turns
92
+ the signal into an ordinary exit so the `EXIT` trap runs. The test runs in the
93
+ background under `wait` because a shell runs no trap while a foreground child is
94
+ still running, and Claude Code follows Esc's SIGTERM with a SIGKILL within a
95
+ second or two: `wait` returns on the signal, so the restore lands first. The
96
+ `kill $!` matters for a Ctrl-C too — a background job in a non-interactive shell
97
+ ignores SIGINT. Only a `SIGKILL` before the trap runs or a machine crash can
98
+ still strand a mutant — `git status` after the
99
+ trip shows the tree exactly as you found it, and a leftover `.bak` is the sign
100
+ one was stranded.)
87
101
 
88
102
  A **killed** mutant (the focused test went red) is the good outcome; a **survivor**
89
103
  (still green on broken code) is the finding. Batching buys trips, never
@@ -209,7 +209,11 @@ close, is always read as the declaration: it is reported if it does not end the
209
209
  even when a list ends a later line. Keep every other `(R<n>)` mention in detail prose
210
210
  **mid-line**. When two lists end continuation lines the validator cannot tell which is
211
211
  the declaration and reports the task; on a task that declares no list, the one
212
- line-ending mention is read as its declaration, and a mid-line one is reported.
212
+ line-ending mention is read as its declaration, and a mid-line one is reported. An
213
+ aside that must open a parenthesis with a requirement id opens it with `per` —
214
+ `(per R42, …)` — because a bare `(R42…` falls under the rules above exactly like a
215
+ declaration, and can be elected over the real list, sometimes silently. A `per` aside
216
+ is never read, so it never stands in for the list: the task still declares its own.
213
217
 
214
218
  The review evidence ledger's validator enumerates each group's review slice from these
215
219
  refs; a task it cannot read is reported as `malformed-task-refs` — a spec defect that
@@ -220,14 +224,32 @@ when the task reaches green, with the task's commit. Write every task `- [ ]`; t
220
224
  validator reports a task still `[ ]` in a group the loop has passed
221
225
  (`unticked-completed-task`), so the file tracks the progress its shape promises.
222
226
 
227
+ **Line shapes — read by the same script.** A task line is `- [ ]` or `- [x]` and a bare
228
+ `T<n>` id, at the task list's own indentation; a group header is exactly `## Group <n>`,
229
+ numbered from 1, with `Group` in English even when the spec is written in another
230
+ language. A line that looks like one but is not — an in-progress `[-]`, a suffixed
231
+ `T12b` or `T1.1`, a numbered `1. [ ]` item, a `- T6` with no box, a task inside a `>`
232
+ quote, a `T<n>` sub-item indented under another task, `### Group 3`, `## Group 0`, a
233
+ digit-less `## Group P`, a translated `## Grupo 2 — … _(…)_` — is reported
234
+ (`malformed-task-line`, `malformed-group-header`), never skipped; a note bullet about a
235
+ task opens with
236
+ something other than its `T<n>`. Every `T<n>` is used once in the file
237
+ (`duplicate-task-id` otherwise). Detail prose may run to several paragraphs: a blank
238
+ line inside a task is fine while the next line stays indented at least to the task's
239
+ text (two spaces under `- [ ]`); less, and it is no longer the task's. Code fences and
240
+ `>` quotes are never read as tasks, but a task or header shape inside one is reported
241
+ too — a quoted sample looks exactly like a task under a fence left open — so quote
242
+ samples without a `T<n>` id or a group header.
243
+
223
244
  Rules: order so the first task is a tracer bullet; never "write all tests" then
224
245
  "write all code"; keep each task small enough to verify on its own. Grouping never
225
246
  changes task granularity — checkboxes stay atomic and TDD runs per task; only review
226
247
  and checkpoint frequency follow the groups (all-at-once mode ignores the headers).
227
- Tasks added mid-implementation (from a discovery) default to **their own group** —
228
- they are risk by definition, and grouping rule 5 applies to them unchanged — but they
229
- are added **after** the spec gate, and no later gate presents a group header, so their
230
- tags get no human reading at all. Tag them for the record; do not rely on anyone
248
+ Tasks added mid-implementation (from a discovery) take the **next free `T<n>`** (never
249
+ a suffixed `T12b`) and default to **their own group** — they are risk by definition,
250
+ and grouping rule 5 applies to them unchanged — but they are added **after** the spec
251
+ gate, and no later gate presents a group header, so their tags get no human reading at
252
+ all. Tag them for the record; do not rely on anyone
231
253
  catching a wrong one.
232
254
 
233
255
  ### 5. Self-check before handing off
@@ -37,7 +37,7 @@ Read the repo's `CLAUDE.md` (and `CONTEXT.md` if present). Summarize in ≤ 50 l
37
37
  code-style rules, comment conventions, naming, architecture constraints. This is what
38
38
  the analysis judges "against project convention" by.
39
39
 
40
- ### 2. Analyze (single sub-agent, the review lenses as a checklist)
40
+ ### 2. Analyze (one pass — a single sub-agent, or inline — the review lenses as a checklist)
41
41
 
42
42
  Spawn **one** `Explore` sub-agent with the full diff, the changed-files list, and the
43
43
  project context. Its prompt walks the five review lenses as a checklist — **not** five
@@ -58,6 +58,12 @@ The agent returns a strict JSON array of findings (see [reference.md](reference.
58
58
  the exact prompt and schema). It returns `[]` when the diff is clean — it must **not**
59
59
  manufacture findings to seem thorough.
60
60
 
61
+ **A Reviewer, or no `Agent` tool?** A Reviewer never spawns, whatever its tools (the
62
+ opening rule of the Reviewer contract, `.claude/agents/reviewer.md`), and an agent at
63
+ Claude Code's nesting depth limit has no `Agent` tool unless it is a fork. Then walk
64
+ the five lenses yourself, in this context, with the same prompt as your checklist, and
65
+ produce the same JSON array; the rest of the flow is unchanged.
66
+
61
67
  ### 3. Dry-run
62
68
 
63
69
  Print the findings as a numbered table (don't post anything yet):
@@ -2,8 +2,9 @@
2
2
 
3
3
  ## Analysis agent prompt
4
4
 
5
- Pass this to the single `Explore` sub-agent. The five lenses are a **checklist one
6
- agent walks**, not a fan-out.
5
+ Pass this to the single `Explore` sub-agent — or, as a Reviewer or with no `Agent`
6
+ tool (SKILL.md §2), walk it yourself. The five lenses are a **checklist one agent
7
+ walks**, not a fan-out.
7
8
 
8
9
  ```
9
10
  You are reviewing a GitHub PR diff. Find real issues worth posting as inline review
@@ -105,7 +105,8 @@ A task deserves the harness if it is **specifiable**, **verifiable**, or
105
105
  checks precondition (`.claude/hooks/lib/merge-pr.sh --approve-issue <issue>`)
106
106
  and never lands on red, absent, or still-pending checks without asking — nor
107
107
  content that no longer matches what the Reviewer's APPROVE reviewed (the
108
- stale-approve guard; a clean update-branch stays valid, any content change
108
+ stale-approve guard; a clean update-branch or a task-state write under
109
+ `.claude/state` stays valid, any other content change
109
110
  routes back to re-review). The task stays at `in-review`
110
111
  until merged.
111
112
  10. **Closeout** — `task-closeout`: confirm the merge via `gh`, run the three Architect
@@ -83,7 +83,7 @@ rollback:
83
83
  # - test
84
84
  # - build
85
85
 
86
- # Design tokens (`design-tokens validate` / `design-tokens contrast`).
86
+ # Design tokens (the `design-tokens` verbs, and the token lines of `doctor` / `status`).
87
87
  # `scan_extensions`: the anti-hardcode scan inspects a built-in set of UI/style
88
88
  # extensions (.css/.scss/.ts/.tsx/.vue/.svelte/.astro/.js/.mdx/.html/…). Add extra
89
89
  # suffixes here for a stack the built-ins don't cover — additive, never a replacement.
@@ -92,10 +92,16 @@ rollback:
92
92
  # (through `sh -c`, in the repo root) — your design system's own verifier for the rules
93
93
  # only it can know (palette under colour-vision-deficiency simulation, property grammar,
94
94
  # CSS scans). Its output is forwarded; a non-zero exit fails the gate. It runs on every
95
- # path: with no docs/design-tokens.json only the WCAG pair check is skipped, and a file
95
+ # path: with no token file only the WCAG pair check is skipped, and a file
96
96
  # that cannot be read or parsed is reported alongside it — never the verifier you
97
97
  # declared. Default none.
98
+ # `file`: where the token file lives, relative to the repository root — for a DTCG
99
+ # file kept (or generated) somewhere other than docs/design-tokens.json. Every
100
+ # design-tokens verb, `doctor` and `status` read it from here. An absolute path, a `\`
101
+ # separator, a path that leads out of the repository or names its root, or one ending
102
+ # in `/` or `/.` is a config error. Default docs/design-tokens.json.
98
103
  # design_tokens:
99
104
  # scan_extensions:
100
105
  # - .foo
101
106
  # verify: pnpm exec my-design-system verify
107
+ # file: design/tokens.json