@lemoncode/lemony 0.1.1 → 0.2.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.
@@ -149,14 +149,14 @@ Emitted by the Orchestrator when it transitions `spec-in-progress → spec-ready
149
149
  Emitted by the Orchestrator at closeout (after `gh pr view` confirms `MERGED`,
150
150
  before `git rm` of the task state).
151
151
 
152
- | Field | Type | Required | Axis | Notes |
153
- | ------------------- | ------ | -------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
154
- | `task_id` | string | yes | `identity` | Required for this type. |
155
- | `level` | string | yes | `internal-enum` | `L1` \| `L2` \| `L3` — the task-fit dial value used. |
156
- | `cycle_time_h` | number | yes | `metric` | Wall-clock hours from issue creation to merge. ≥ 0, finite. |
157
- | `review_rejections` | number | yes | `metric` | Count of `review_rejected` events for this `task_id` (≥ 0, int). |
158
- | `mode` | string | no | `internal-enum` | `all_at_once` \| `step_by_step` — the mode chosen at the L1 approval gate. **Absent on L2** (the question only exists where `tasks.md` does). |
159
- | `steps` | number | no | `metric` | Count of `step_completed` events for this task (≥ 1, int). Only meaningful when `mode` is `step_by_step`; < total tasks after a mid-task downgrade. |
152
+ | Field | Type | Required | Axis | Notes |
153
+ | ------------------- | ------ | -------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
154
+ | `task_id` | string | yes | `identity` | Required for this type. |
155
+ | `level` | string | yes | `internal-enum` | `L1` \| `L2` \| `L3` — the task-fit dial value used. |
156
+ | `cycle_time_h` | number | yes | `metric` | Wall-clock hours from issue creation to merge. ≥ 0, finite. |
157
+ | `review_rejections` | number | yes | `metric` | Count of `review_rejected` events for this `task_id` (≥ 0, int). |
158
+ | `mode` | string | no | `internal-enum` | `all_at_once` \| `step_by_step` — the mode chosen at the L1 approval gate. **Absent on L2** (the question only exists where `tasks.md` does). |
159
+ | `steps` | number | no | `metric` | Count of `step_completed` events for this task (≥ 1, int). Only meaningful when `mode` is `step_by_step`; < total groups after a mid-task downgrade. |
160
160
 
161
161
  ### 5. `review_rejected` _(P5)_
162
162
 
@@ -168,7 +168,7 @@ dedicated label).
168
168
  | `task_id` | string | yes | `identity` | Required for this type. |
169
169
  | `reason` | string | yes | `free-text` | Short human-readable reason (one line; never the full review comment). 1-500 chars. |
170
170
  | `iteration` | number | yes | `metric` | 1-based: the Nth rejection of this task (≥ 1, int). |
171
- | `step` | number | no | `metric` | The step (1-based `tasks.md` task number) whose per-step review rejected. **Absent** on full-pass and all-at-once rejections. |
171
+ | `step` | number | no | `metric` | The step (1-based; one step = one `tasks.md` group) whose per-step review rejected. **Absent** on full-pass and all-at-once rejections. |
172
172
  | `attributed_kind` | string | no | `internal-enum` | `agent` \| `skill` \| `playbook` — the kind of component the friction is attributed to. **Omitted when the emitter can't attribute.** |
173
173
  | `attributed_name` | string | no | `internal-enum` | The component's name (free string, 1-200 chars), e.g. `implementer`. Independently optional in the schema; emitters pair it with `attributed_kind` and omit both when they can't attribute. Free-string by design — see [Attribution](#attribution). |
174
174
 
@@ -218,7 +218,7 @@ humans bail out (`ok_downgrade`).
218
218
  | Field | Type | Required | Axis | Notes |
219
219
  | ------------------- | ------ | -------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
220
220
  | `task_id` | string | yes | `identity` | Required for this type. |
221
- | `step` | number | yes | `metric` | 1-based `tasks.md` task number the checkpoint belongs to (≥ 1, int). |
221
+ | `step` | number | yes | `metric` | 1-based step number the checkpoint belongs to (one step = one `tasks.md` group; ≥ 1, int). |
222
222
  | `review_iterations` | number | yes | `metric` | Reviewer invocations that preceded this checkpoint (≥ 1, int — every step is reviewed before the human; resets after a "changes"). |
223
223
  | `checkpoint_result` | string | yes | `internal-enum` | `ok` \| `changes` \| `ok_downgrade` (OK and switch the remaining tasks to all-at-once). |
224
224
  | `attributed_kind` | string | no | `internal-enum` | `agent` \| `skill` \| `playbook` — the kind of component the friction is attributed to. **Omitted when the emitter can't attribute.** |
@@ -63,8 +63,12 @@ the verdict is cheaper than mis-routing a real gap to a dismissable offer.
63
63
  ### 3. Report
64
64
 
65
65
  - **In-scope surviving mutants** → list them in your review verdict as an **advisory**
66
- block: file:line, the mutation that survived, and the assertion that would have caught
67
- it. This is **not** a REJECT on its own (decision: advisory). The Implementer may
66
+ block: file:line, the mutation that survived, and the assertion that would have
67
+ caught it all of them when few (over-reporting here is cheaper than mis-routing,
68
+ per step 2). On a noisy run, carry the highest-value ones in the verdict and route
69
+ the full list to the full-pass issue comment; on a per-step review — which posts no
70
+ comment — anything worth keeping goes in the verdict bullets (the Orchestrator
71
+ records those in `progress.md`); what you leave out dies with your context. This is **not** a REJECT on its own (decision: advisory). The Implementer may
68
72
  strengthen the tests; the Reviewer may still REJECT by _judgment_ if a survivor exposes
69
73
  a genuinely dangerous untested path — but the mutation result alone never auto-blocks.
70
74
  - **Pre-existing surviving mutants** → run **`note-side-finding`**: one `## Side-findings`
@@ -29,7 +29,7 @@ the gap by guessing — surface it (see "When the PRD is insufficient").
29
29
  real from the start — there is no `draft-<slug>` holder:
30
30
  - `requirements.md` — EARS requirements with acceptance criteria
31
31
  - `design.md` — files, functions, approach, edge cases, testing
32
- - `tasks.md` — atomic, ordered, checkbox tasks
32
+ - `tasks.md` — atomic, ordered checkbox tasks, grouped under risk-sized step headers
33
33
 
34
34
  ## Process
35
35
 
@@ -126,16 +126,40 @@ Atomic, ordered checkboxes. Each task is a **vertical slice** (a behavior, not a
126
126
  layer) so the Implementer can run TDD red→green per task. Reference the requirements
127
127
  each task satisfies.
128
128
 
129
+ Group the tasks under **risk-sized step headers**. In step-by-step mode the loop runs
130
+ one implement→review→human-checkpoint cycle **per group**, so the grouping decides how
131
+ often the human is interrupted; the human approves it at the spec gate with the rest
132
+ of the spec. Grouping criterion:
133
+
134
+ 1. **Risk isolates — hard rule.** A task gets its own group iff it concentrates risk:
135
+ a new seam, a hard-to-reverse decision, a data-loss or security surface.
136
+ 2. **Runnable surface closes — soft rule.** Among trivial stretches, prefer group
137
+ boundaries that leave something the human can run at the checkpoint. Never invent
138
+ a group to fabricate a demo.
139
+ 3. **Soft cap: 4 tasks per group**, exceedable only with explicit justification in
140
+ the group's rationale (e.g. "5 identical mechanical renames").
141
+ 4. **Every group header carries a one-line boundary rationale** so the grouping is
142
+ auditable at the spec gate.
143
+
129
144
  ```markdown
130
145
  # Tasks — <topic>
131
146
 
147
+ ## Group 1 — <name> _(<one-line boundary rationale>)_
148
+
132
149
  - [ ] T1 — <smallest behavior that proves the path end-to-end> (R1)
133
150
  - [ ] T2 — <next behavior> (R2)
151
+
152
+ ## Group 2 — <name> _(<one-line boundary rationale>)_
153
+
134
154
  - [ ] T3 — <error path> (R2, R3)
135
155
  ```
136
156
 
137
157
  Rules: order so the first task is a tracer bullet; never "write all tests" then
138
- "write all code"; keep each task small enough to verify on its own.
158
+ "write all code"; keep each task small enough to verify on its own. Grouping never
159
+ changes task granularity — checkboxes stay atomic and TDD runs per task; only review
160
+ and checkpoint frequency follow the groups (all-at-once mode ignores the headers).
161
+ Tasks added mid-implementation (from a discovery) default to **their own group** —
162
+ they are risk by definition.
139
163
 
140
164
  ### 5. Self-check before handing off
141
165
 
@@ -143,6 +167,8 @@ Rules: order so the first task is a tracer bullet; never "write all tests" then
143
167
  - [ ] Every requirement is EARS-formatted and testable.
144
168
  - [ ] At least one unwanted-behavior (`If … then …`) requirement exists.
145
169
  - [ ] Every requirement is covered by at least one task.
170
+ - [ ] Every task sits in a group, and every group header carries a one-line
171
+ boundary rationale.
146
172
  - [ ] No closed PRD decision is contradicted.
147
173
 
148
174
  ### 6. Emit `spec_created`
@@ -64,7 +64,9 @@ is a second discovery — append it, don't fold it in.
64
64
 
65
65
  The agent that **created** an artifact is the one that updates it. Map the decision to
66
66
  the artifact it changes, then invoke that owner (fresh context, Task tool) with the
67
- decision + the entry as context:
67
+ decision **stated in full** — at this moment it exists nowhere on disk; the entry's
68
+ `**Resolution**` block is only written at step 4 — plus the `discoveries.md` entry
69
+ **by path** for the surrounding context (it reads the entry itself):
68
70
 
69
71
  | Artifact changed by the decision | Owner to invoke |
70
72
  | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -104,7 +106,9 @@ do not edit the raise fields):
104
106
  Same fail-loud contract: a non-zero exit means the flag/status did not flip back.
105
107
 
106
108
  2. Re-invoke the paused sub-agent (fresh context) with: the issue, the task-state
107
- paths, the decision, and the updated artifact. It picks up from where it stopped.
109
+ paths, a one-line gist of the decision (the full `**Resolution**` block now
110
+ lives in the `discoveries.md` entry — reference it), and the updated artifact's
111
+ **path**. It reads the files itself and picks up from where it stopped.
108
112
 
109
113
  ## Closeout invariant
110
114
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: spec-compliance-check
3
- description: Walk an L1 spec point by point against the implementation and emit a per-requirement pass/fail verdict. Use during review of an SDD task to prove every EARS requirement (including unwanted-behavior paths) is satisfied and tested — a committable audit trail. Complements `senior-review` (quality) with traceability (coverage of intent).
3
+ description: Walk an L1 spec point by point against the implementation and emit a per-requirement pass/fail verdict. Use during review of an SDD task to prove every EARS requirement (including unwanted-behavior paths) is satisfied and tested — a durable audit trail. Complements `senior-review` (quality) with traceability (coverage of intent).
4
4
  origin: vendor
5
5
  vendor_version: '{{vendor_version}}'
6
6
  phase: post-implementation
@@ -11,7 +11,7 @@ invoked-by: [reviewer]
11
11
 
12
12
  `senior-review` judges whether the code is _good_; this skill proves it does what the
13
13
  **spec said** — every requirement, point by point. It produces a traceability matrix:
14
- a committable audit trail mapping each requirement to its implementation and its test.
14
+ a durable audit trail mapping each requirement to its implementation and its test.
15
15
  Only meaningful for **L1 (SDD) tasks** that have a spec under
16
16
  `.claude/state/tasks/<id>/spec/`; skip it for L2.
17
17
 
@@ -25,8 +25,8 @@ Read all three spec artifacts for the task:
25
25
  state-driven / optional-feature / **unwanted-behavior**), each numbered with
26
26
  acceptance criteria.
27
27
  - `design.md` — the intended files, interfaces, approach, edge cases.
28
- - `tasks.md` — the atomic checklist, each item referencing the requirements it
29
- satisfies.
28
+ - `tasks.md` — the atomic checklist grouped under risk-sized step headers, each
29
+ item referencing the requirements it satisfies.
30
30
 
31
31
  ### 2. Trace each requirement to the implementation
32
32
 
@@ -55,6 +55,10 @@ and asserts the rejection/guard.
55
55
 
56
56
  ### 4. Emit the matrix
57
57
 
58
+ Destination: the full-pass **issue comment**. In your return summary — and always on
59
+ a per-step review, which posts no comment — carry only the counts + the failing
60
+ rows, per the Reviewer's verdict-size rule.
61
+
58
62
  ```
59
63
  ## Spec Compliance — task #<id>
60
64
 
@@ -87,8 +87,9 @@ The three differ in **who decides** — and the asymmetry is deliberate:
87
87
 
88
88
  - **`update-architecture` — automatic dispatch, no pre-offer.** Only when
89
89
  `docs/architecture.md` exists (the skill installs solely then). Dispatch the
90
- **Architect** (`update-architecture`, fresh context) with the task's **merged diff**
91
- (`gh pr diff <pr>`) plus the task's `spec/design.md` (still live here — archival is
90
+ **Architect** (`update-architecture`, fresh context) with the task's **PR number**
91
+ (it runs `gh pr diff <pr>` itself) plus the **path** of the task's `spec/design.md`
92
+ (still live here — archival is
92
93
  step 4 below). The Architect reads the change, makes
93
94
  the smallest true edit if the system's **shape** moved (referencing any ADR just
94
95
  written — `see ADR-NNNN`), or reports **no-op** when nothing architectural changed.
@@ -52,6 +52,10 @@ For each file in the "always" set:
52
52
 
53
53
  ### 3. Report
54
54
 
55
+ Destination: the full-pass **issue comment**. In your return summary — and always on
56
+ a per-step review, which posts no comment — carry only the counts + the gaps, per
57
+ the Reviewer's verdict-size rule (a REJECT lifts the cap entirely).
58
+
55
59
  ```
56
60
  ## Test Gap Report — <task name>
57
61
 
@@ -84,6 +84,9 @@ and the error output (unexpected warnings/errors even when the happy path worked
84
84
  **Verdict**: works / does not work — <one-line reason>
85
85
  ```
86
86
 
87
+ This block is your working record; a capped return summary condenses it to the
88
+ verdict line (plus any ❌ detail — failures are signal, never trimmed).
89
+
87
90
  On a failure, read the **full** error, find the origin (not where it surfaced), fix
88
91
  the root cause, and re-run from the failed gate. If a failure reveals the spec itself
89
92
  is wrong or silent, that's a **discovery** — run `raise-discovery`, don't paper over
@@ -57,7 +57,8 @@ A task deserves the harness if it is **specifiable**, **verifiable**, or
57
57
  `harness:sdd` + `harness:status:spec-in-progress`) and the branch
58
58
  `harness/<id>-<slug>`. `<id>` is the GitHub issue number in this build; spec and code
59
59
  both live on that branch — nothing touches the default branch until the merge gate.
60
- 3. **UI design (if it touches UI)** — **UI design gate** (§UI design in `orchestrator.md`):
60
+ 3. **UI design (if it touches UI)** — **UI design gate** (canonical: the orchestrator's
61
+ L1 step 3, evaluated core-only — read `.claude/agents/ui-design.md` only when it fires):
61
62
  if the repo has a frontend AND the task touches UI, put `harness:needs-design`, offer the
62
63
  design-stop, and on "continue" **run `grill-ui` yourself** (the interactive design
63
64
  interview) to author `ui-handoff.md` under `tasks/<id>/spec/`, then dispatch the **UI
@@ -67,7 +68,7 @@ A task deserves the harness if it is **specifiable**, **verifiable**, or
67
68
  under `tasks/<id>/spec/`) then `spec-to-issue` (fills the issue body — it creates nothing
68
69
  and moves no labels).
69
70
  5. **Spec-ready + handoff** — remove `harness:needs-design` once `ui-handoff.md` is
70
- complete (a spec-ready task never carries it), flip to `harness:status:spec-ready`,
71
+ complete (a spec-ready task never carries the label), flip to `harness:status:spec-ready`,
71
72
  commit and push the task state to the branch. DEFINE can stop here: the spec-ready
72
73
  queue (`gh issue list -l harness:status:spec-ready`) is the handoff. Ask: implement
73
74
  now or hand off?
@@ -83,8 +84,10 @@ A task deserves the harness if it is **specifiable**, **verifiable**, or
83
84
  — either lens rejecting routes back to the Implementer.
84
85
  9. **Merge gate** — never auto-merge. Surface the approved PR; the human merges (or
85
86
  authorizes you to). The task stays at `in-review` until merged.
86
- 10. **Closeout** — `task-closeout`: confirm the merge via `gh`, offer durable discoveries
87
- to the Architect (`write-adr`, HITL), archive the spec + discoveries to `_archive/<id>/` (drop
87
+ 10. **Closeout** — `task-closeout`: confirm the merge via `gh`, run the three Architect
88
+ activations (`write-adr` HITL offer per resolved discovery; `update-architecture`
89
+ automatic when `docs/architecture.md` exists; `playbook-iterate` — HITL offer once
90
+ per task), archive the spec + discoveries to `_archive/<id>/` (drop
88
91
  `progress.md`), and land `history.md` + the archival via a dedicated
89
92
  `harness/closeout-<id>` PR (`gh pr merge --auto`) — never a direct push to the base.
90
93
  If protection needs approval the PR waits: park at `closeout-pending`, `/resume`
@@ -92,6 +95,8 @@ A task deserves the harness if it is **specifiable**, **verifiable**, or
92
95
 
93
96
  ## L2 round-trip (TRIAGE)
94
97
 
98
+ Authority for the steps: `.claude/agents/triage.md` (the Orchestrator's lazy companion).
99
+
95
100
  1. **Triage** — `triage-issue`: investigate, find root cause, draft a TDD-based fix
96
101
  plan, and create the issue with `harness:managed` (no `harness:sdd` — its absence
97
102
  marks the lightweight path).
package/dist/cli.mjs CHANGED
@@ -45,7 +45,8 @@ const formatIssue = (schema, issue) => {
45
45
  const valid = validKeysAt(schema, issue.path);
46
46
  return issue.keys.map((key) => {
47
47
  const guess = valid ? nearestKey(key, valid) : null;
48
- return `Unknown key "${key}" in ${where}.${guess ? ` Did you mean "${guess}"?` : ""}`;
48
+ const hint = guess ? ` Did you mean "${guess}"?` : "";
49
+ return `Unknown key "${key}" in ${where}.${hint}`;
49
50
  });
50
51
  }
51
52
  if (issue.code === "invalid_format" && issue.format === "regex") {
@@ -206,7 +207,8 @@ const setConfigValues = (rawYaml, updates) => {
206
207
  };
207
208
  const writeConfigValues = async (repoRoot, updates) => {
208
209
  const configPath = join(repoRoot, HARNESS_CONFIG_FILENAME);
209
- await writeFile(configPath, setConfigValues(await readFile(configPath, "utf8"), updates));
210
+ const raw = await readFile(configPath, "utf8");
211
+ await writeFile(configPath, setConfigValues(raw, updates));
210
212
  };
211
213
  //#endregion
212
214
  //#region src/config/pointer.schema.ts
@@ -1079,7 +1081,8 @@ const runValidate = async (inputs) => {
1079
1081
  filesScanned: 0,
1080
1082
  violations: []
1081
1083
  };
1082
- const fileCheck = validateTokenFile(await readFile(tokenPath, "utf8"));
1084
+ const raw = await readFile(tokenPath, "utf8");
1085
+ const fileCheck = validateTokenFile(raw);
1083
1086
  if (!fileCheck.ok) return {
1084
1087
  ok: false,
1085
1088
  tokensFound: true,
@@ -1410,7 +1413,8 @@ const linearizeChannel = (channel) => {
1410
1413
  const relativeLuminance = ({ r, g, b }) => .2126 * linearizeChannel(r) + .7152 * linearizeChannel(g) + .0722 * linearizeChannel(b);
1411
1414
  const contrastRatio = (foreground, background) => {
1412
1415
  const bg = background.a < 1 ? flatten(background, WHITE) : background;
1413
- const l1 = relativeLuminance(foreground.a < 1 ? flatten(foreground, bg) : foreground);
1416
+ const fg = foreground.a < 1 ? flatten(foreground, bg) : foreground;
1417
+ const l1 = relativeLuminance(fg);
1414
1418
  const l2 = relativeLuminance(bg);
1415
1419
  const light = Math.max(l1, l2);
1416
1420
  const dark = Math.min(l1, l2);
@@ -1457,7 +1461,9 @@ const runContrast = async (inputs) => {
1457
1461
  };
1458
1462
  const tokens = collectTokens(parsed);
1459
1463
  const problems = [];
1460
- const pairs = measurePairs(discoverPairs(tokens, problems), declaredModes(tokens), tokens);
1464
+ const specs = discoverPairs(tokens, problems);
1465
+ const modes = declaredModes(tokens);
1466
+ const pairs = measurePairs(specs, modes, tokens);
1461
1467
  return {
1462
1468
  ok: problems.length === 0 && pairs.every((pair) => pair.passes),
1463
1469
  tokensFound: true,
@@ -3303,7 +3309,8 @@ const runStatus = async (deps) => {
3303
3309
  const config = await readHarnessConfig(repoRoot);
3304
3310
  const pointer = await readPointer(repoRoot, deps.readGitUserEmail);
3305
3311
  const { branch, behind } = await deps.gitBehind(repoRoot);
3306
- const openDiscoveries = await countOpenDiscoveries(createTaskTrackerProvider(config.task_storage.type, deps.runCommand), config.task_storage.repo);
3312
+ const provider = createTaskTrackerProvider(config.task_storage.type, deps.runCommand);
3313
+ const openDiscoveries = await countOpenDiscoveries(provider, config.task_storage.repo);
3307
3314
  return {
3308
3315
  vendorVersion: config.vendor_version,
3309
3316
  taskStorageRepo: config.task_storage.repo,
@@ -3659,7 +3666,10 @@ const hasConflictMarkers = (content) => CONFLICT_FENCE.test(content);
3659
3666
  //#endregion
3660
3667
  //#region src/merge/three-way-merge.ts
3661
3668
  const threeWayMerge = (base, client, vendor) => {
3662
- const regions = diff3Regions(base.split("\n"), client.split("\n"), vendor.split("\n"));
3669
+ const baseLines = base.split("\n");
3670
+ const clientLines = client.split("\n");
3671
+ const vendorLines = vendor.split("\n");
3672
+ const regions = diff3Regions(baseLines, clientLines, vendorLines);
3663
3673
  const out = [];
3664
3674
  let conflicted = false;
3665
3675
  for (const region of regions) {
@@ -3920,6 +3930,12 @@ const CORE_AGENTS = [
3920
3930
  "architect",
3921
3931
  "ui-designer"
3922
3932
  ];
3933
+ const COMPANION_DOCS = [
3934
+ "fit-assessment",
3935
+ "triage",
3936
+ "spinoff",
3937
+ "ui-design"
3938
+ ];
3923
3939
  const renderFile = async (templatePath, relPath, vars) => {
3924
3940
  return {
3925
3941
  relPath,
@@ -3945,7 +3961,7 @@ const materializeVendorFiles = async (ctx) => {
3945
3961
  SKILLS: renderRoleSkills(skills, role),
3946
3962
  vendor_version: vendorVersion
3947
3963
  }), vendorVersion)));
3948
- const fitAssessment = await copyFileEntry(join(vendorRoot, "agents", "fit-assessment.md"), join(AGENTS_DIR, "fit-assessment.md"));
3964
+ const companionFiles = await Promise.all(COMPANION_DOCS.map((doc) => copyFileEntry(join(vendorRoot, "agents", `${doc}.md`), join(AGENTS_DIR, `${doc}.md`))));
3949
3965
  const skillFiles = (await Promise.all(skills.map(async (skill) => {
3950
3966
  const skillDir = join(vendorRoot, "skills", skill.name);
3951
3967
  const rels = await listFiles(skillDir);
@@ -3962,7 +3978,7 @@ const materializeVendorFiles = async (ctx) => {
3962
3978
  return [
3963
3979
  entryProtocol,
3964
3980
  ...agentFiles,
3965
- fitAssessment,
3981
+ ...companionFiles,
3966
3982
  ...skillFiles,
3967
3983
  playbooksReadme,
3968
3984
  ...hookFiles,
@@ -4824,12 +4840,13 @@ const update = async (args) => {
4824
4840
  const repoRoot = cwd();
4825
4841
  const onConflict = parseOnConflict(args);
4826
4842
  const dryRun = args.includes("--dry-run");
4843
+ const allowDowngrade = args.includes("--allow-downgrade");
4827
4844
  const result = await runUpdate({
4828
4845
  repoRoot,
4829
4846
  vendorRoot: VENDOR_ROOT,
4830
4847
  onConflict,
4831
4848
  dryRun,
4832
- allowDowngrade: args.includes("--allow-downgrade"),
4849
+ allowDowngrade,
4833
4850
  runCommand: makeRunCommand()
4834
4851
  });
4835
4852
  const verb = (applied, preview) => dryRun ? preview : applied;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lemoncode/lemony",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Lemony — a Harness for AI Coding. Vendor package: installer, agent role catalog, generic skill catalog, hooks, and templates for a Spec-Driven Development workflow.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -28,7 +28,7 @@
28
28
  ],
29
29
  "engines": {
30
30
  "node": ">=24",
31
- "npm": ">=11"
31
+ "pnpm": ">=11.5.1"
32
32
  },
33
33
  "publishConfig": {
34
34
  "registry": "https://registry.npmjs.org",
@@ -43,6 +43,24 @@
43
43
  "agents",
44
44
  "scaffolding"
45
45
  ],
46
+ "devDependencies": {
47
+ "@changesets/cli": "2.31.1",
48
+ "@stryker-mutator/core": "9.6.1",
49
+ "@stryker-mutator/vitest-runner": "9.6.1",
50
+ "@types/node": "24.13.3",
51
+ "husky": "9.1.7",
52
+ "lint-staged": "17.2.0",
53
+ "oxlint": "1.75.0",
54
+ "prettier": "3.9.6",
55
+ "tsdown": "0.22.14",
56
+ "tsx": "4.23.1",
57
+ "typescript": "7.0.2",
58
+ "vitest": "4.1.10"
59
+ },
60
+ "dependencies": {
61
+ "yaml": "2.9.0",
62
+ "zod": "4.4.3"
63
+ },
46
64
  "scripts": {
47
65
  "build": "tsdown",
48
66
  "check-types": "tsc --noEmit",
@@ -60,30 +78,7 @@
60
78
  "notice:check": "tsx scripts/generate-notice.ts --check",
61
79
  "telemetry:aggregate": "tsx scripts/aggregate-telemetry.ts",
62
80
  "changeset": "changeset",
63
- "changeset:version": "changeset version && tsx scripts/sync-catalog-version.ts",
81
+ "changeset:version": "changeset version && pnpm install --lockfile-only && tsx scripts/sync-catalog-version.ts",
64
82
  "changeset:publish": "node --run build && changeset publish"
65
- },
66
- "devDependencies": {
67
- "@changesets/cli": "2.31.0",
68
- "@stryker-mutator/core": "9.6.1",
69
- "@stryker-mutator/vitest-runner": "9.6.1",
70
- "@types/node": "24.13.2",
71
- "oxlint": "1.71.0",
72
- "prettier": "3.9.1",
73
- "tsdown": "0.22.3",
74
- "tsx": "4.22.4",
75
- "typescript": "6.0.3",
76
- "vitest": "4.1.9"
77
- },
78
- "dependencies": {
79
- "yaml": "2.9.0",
80
- "zod": "4.4.3"
81
- },
82
- "overrides": {
83
- "typed-rest-client": {
84
- "qs": "6.15.2"
85
- },
86
- "js-yaml": "4.2.0",
87
- "vite": "8.0.16"
88
83
  }
89
- }
84
+ }