canicode 0.11.0 → 0.11.2
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 +18 -6
- package/dist/cli/index.js +670 -188
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +41 -21
- package/dist/index.js +45 -9
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +45 -8
- package/dist/mcp/server.js.map +1 -1
- package/docs/CUSTOMIZATION.md +62 -3
- package/package.json +2 -2
- package/skills/canicode/SKILL.md +6 -0
- package/skills/canicode-gotchas/SKILL.md +91 -66
- package/skills/canicode-roundtrip/SKILL.md +74 -248
- package/skills/canicode-roundtrip/canicode-roundtrip-helpers.d.ts +54 -0
- package/skills/canicode-roundtrip/helpers-bootstrap.js +21 -0
- package/skills/canicode-roundtrip/helpers-installer.js +14 -0
- package/skills/canicode-roundtrip/helpers.js +287 -17
- package/skills/cursor/canicode/SKILL.md +6 -0
- package/skills/cursor/canicode-gotchas/SKILL.md +91 -66
- package/skills/cursor/canicode-roundtrip/SKILL.md +74 -248
- package/skills/cursor/canicode-roundtrip/canicode-roundtrip-helpers.d.ts +54 -0
- package/skills/cursor/canicode-roundtrip/helpers-bootstrap.js +21 -0
- package/skills/cursor/canicode-roundtrip/helpers-installer.js +14 -0
- package/skills/cursor/canicode-roundtrip/helpers.js +287 -17
package/docs/CUSTOMIZATION.md
CHANGED
|
@@ -180,6 +180,22 @@ Override score, severity, or enable/disable individual rules:
|
|
|
180
180
|
|
|
181
181
|
Configure the canicode MCP server so Cursor exposes `analyze`, `gotcha-survey`, and other tools.
|
|
182
182
|
|
|
183
|
+
### Which MCP file affects which host?
|
|
184
|
+
|
|
185
|
+
Two different JSON locations are easy to confuse because both can live in a git repo and both use an `mcpServers` object. (See GitHub #436.)
|
|
186
|
+
|
|
187
|
+
| Path | Read by | Purpose |
|
|
188
|
+
| --- | --- | --- |
|
|
189
|
+
| **`.mcp.json`** at the **repository root** | **Claude Code** — `claude mcp add …` (project or user scope) persists entries here. | Shared with teammates when committed; this is the file skills and onboarding mean when they say “check `.mcp.json`” in a **Claude Code** context. |
|
|
190
|
+
| **`.cursor/mcp.json`** | **Cursor** — project-scoped MCP list ([Cursor MCP docs — configuration locations](https://cursor.com/docs/context/mcp)). | Team-shared MCP for Cursor Agent in that workspace. |
|
|
191
|
+
| **`~/.cursor/mcp.json`** | **Cursor** — global MCP list (same doc). | Personal MCP available across all projects; merged with project config (project wins if the same server name appears twice). |
|
|
192
|
+
|
|
193
|
+
**Cursor does not use the repo-root `.mcp.json` for MCP.** If you only edit `.mcp.json` because you copied a Claude Code snippet, Cursor will not load those servers until you add the same `mcpServers` entries under **`.cursor/mcp.json`** or **`~/.cursor/mcp.json`**, then reload MCP or restart Cursor.
|
|
194
|
+
|
|
195
|
+
**Claude Code does not read `.cursor/mcp.json`.** Register servers with `claude mcp add …` (or maintain the root `.mcp.json` format Claude Code expects for your install).
|
|
196
|
+
|
|
197
|
+
**Canicode CLI (`canicode init`):** When suggesting Figma MCP setup for Cursor-heavy flows, the init command treats **either** repo-root `.mcp.json` **or** `.cursor/mcp.json` as evidence that Figma MCP is configured — so mixed-host repos still get accurate “already configured” detection. That does **not** mean Cursor loads `.mcp.json`; it only avoids false negatives when both files exist.
|
|
198
|
+
|
|
183
199
|
### Project config (`.cursor/mcp.json`)
|
|
184
200
|
|
|
185
201
|
Create or merge into `.cursor/mcp.json` in your repository root:
|
|
@@ -197,20 +213,63 @@ Create or merge into `.cursor/mcp.json` in your repository root:
|
|
|
197
213
|
|
|
198
214
|
Use long-form `--package` (short `-p` can confuse some parsers). Set your Figma token once with `npx canicode init --token figd_…` — the MCP server reads `~/.canicode/config.json`; you do not need `FIGMA_TOKEN` in the MCP block unless your team prefers env injection.
|
|
199
215
|
|
|
216
|
+
### Figma MCP server id and tool names in Cursor (#437)
|
|
217
|
+
|
|
218
|
+
Cursor may show an MCP **server id** in the UI that is **not** literally the key you typed in `mcpServers` (for example a workspace or project prefix). Skills and docs often say “the `figma` MCP” or “call `use_figma`” as shorthand — **your session’s truth is the live tool list** after MCP reload (Cursor: MCP / Tools panel), not the string `figma` or `use_figma` read from a config file alone.
|
|
219
|
+
|
|
220
|
+
- If roundtrip fails with “cannot find `use_figma`” but Figma MCP shows as connected, open the tool list and note the **exact** tool identifier Cursor exposes (it may be namespaced).
|
|
221
|
+
- Keep the Figma MCP entry in `.cursor/mcp.json` (or `~/.cursor/mcp.json`) per [Figma’s MCP docs](https://developers.figma.com/docs/figma-mcp-server/) — then rely on the host-reported tool name when wiring skills or debugging.
|
|
222
|
+
|
|
223
|
+
### Post-install checklist (MCP + skills + Figma) (#461)
|
|
224
|
+
|
|
225
|
+
After editing MCP JSON or running `canicode init`:
|
|
226
|
+
|
|
227
|
+
1. **Restart the host** (Cursor or Claude Code) or use the product’s **reload MCP** action — on-disk config is not live until the next session.
|
|
228
|
+
2. **Confirm the live tool list** (Cursor: Settings → MCP; Claude: tool picker) — the JSON file is not proof; the model can only call tools the host exposes.
|
|
229
|
+
3. **Figma** — for MCP-driven canvas access, complete any **Connect / file access** steps your Figma account requires so `use_figma` can run against the target file.
|
|
230
|
+
4. **Community / read-only files** — roundtrip Step 4 writes to the file. If the design is read-only, duplicate it into a workspace you own before running `/canicode-roundtrip` or `@canicode-roundtrip`.
|
|
231
|
+
|
|
200
232
|
### Gotcha survey in Cursor
|
|
201
233
|
|
|
202
234
|
1. Add the MCP config above and restart Cursor (or reload MCP).
|
|
203
|
-
2.
|
|
204
|
-
3. In chat,
|
|
235
|
+
2. Install skills: **`canicode init --token … --cursor-skills`** saves the Figma token and installs everything; or **`canicode init --cursor-skills`** without `--token` installs Claude skills plus Cursor copies — you still need **`canicode init --token …`** (or `FIGMA_TOKEN`) before REST **`analyze`** / **`gotcha-survey`** against a live URL. The shared gotchas answer file lives under `.claude/skills/canicode-gotchas/` when the gotchas skill is installed. Authoring is single-source under `.claude/skills/` in the repo; the npm build writes `skills/cursor/` (gotchas strip `# Collected Gotchas`; other skills are full copies).
|
|
236
|
+
3. In chat, **@-mention** **canicode**, **canicode-gotchas**, or **canicode-roundtrip** when your Cursor setup uses Agent-attached skills. Some teams prefer **slash commands** or rules instead of `@` — use whichever your workspace enables; roundtrip still needs **`use_figma`** from the Figma MCP in that session.
|
|
205
237
|
|
|
206
238
|
### Manual test checklist (#407)
|
|
207
239
|
|
|
208
240
|
- [ ] MCP: Cursor shows `canicode` connected and the tools list includes `gotcha-survey` (and `analyze` if testing roundtrip Step 1).
|
|
209
241
|
- [ ] Figma MCP: `use_figma` is available when testing **roundtrip** (install + restart host if tools are missing).
|
|
210
242
|
- [ ] Calling `gotcha-survey` with a local fixture path returns JSON with `designGrade` and `questions` / `isReadyForCodeGen`.
|
|
211
|
-
- [ ] After the Q&A loop, `npx canicode upsert-gotcha-section
|
|
243
|
+
- [ ] After the Q&A loop, `npx canicode upsert-gotcha-section --file … --design-key … --input=-` (JSON payload on stdin per `canicode-gotchas` Step 4b) succeeds and updates `.claude/skills/canicode-gotchas/SKILL.md`.
|
|
212
244
|
- [ ] Optional: @ **canicode-roundtrip** — Step 4 reads `helpers.js` from `.cursor/skills/canicode-roundtrip/helpers.js` after `canicode init --cursor-skills`.
|
|
213
245
|
|
|
246
|
+
### Troubleshooting: MCP not available or roundtrip Step 4 fails
|
|
247
|
+
|
|
248
|
+
Work through these checks in order before concluding that an MCP server is broken.
|
|
249
|
+
|
|
250
|
+
1. **Cursor host — ensure the server is enabled and the tools appear in the live session.**
|
|
251
|
+
MCP servers are not always active immediately after install. Open **Settings → MCP** and verify the server shows a connected status. If the entry is present but the tools are missing from the active tool list, use the reload MCP action (or restart Cursor). The live tool list — not the on-disk JSON — is what the model can actually call.
|
|
252
|
+
|
|
253
|
+
2. **Two separate servers — identify which step failed before attributing blame.**
|
|
254
|
+
canicode (provides `analyze` and `gotcha-survey`) and Figma (provides `use_figma`) are independent MCP servers. Step 1 (analyze) and Step 3 (gotcha-survey) use the canicode server. Step 4 (canvas write) uses the Figma server. A failure in Step 4 is a Figma MCP issue, not a canicode issue — and vice versa. Check which server exposes the missing tool before editing config.
|
|
255
|
+
|
|
256
|
+
3. **Step 4 (canvas write) — `helpers.js` must be prepended to every `use_figma` `code` string.**
|
|
257
|
+
`CanICodeRoundtrip` is not a Figma built-in. It is the global registered by the bundled IIFE in `helpers.js` (at `.claude/skills/canicode-roundtrip/helpers.js` for Claude Code, or `.cursor/skills/canicode-roundtrip/helpers.js` after `canicode init --cursor-skills`). If the bundle is missing from the string, the first Plugin API call throws `ReferenceError: CanICodeRoundtrip is not defined`. This is **not** the same as "canicode MCP is missing" — the canicode server may be healthy; only the Step 4 `use_figma` script is incomplete. Smoke-check with:
|
|
258
|
+
```javascript
|
|
259
|
+
// <contents of helpers.js prepended here>
|
|
260
|
+
return { ok: typeof CanICodeRoundtrip !== 'undefined' };
|
|
261
|
+
```
|
|
262
|
+
See the Step 4 preflight block in [`docs/roundtrip-protocol.md`](https://github.com/let-sunny/canicode/blob/main/docs/roundtrip-protocol.md) and the corresponding preflight check in the `canicode-roundtrip` SKILL.md for the full prepend procedure.
|
|
263
|
+
|
|
264
|
+
4. **Size / delivery — measure the code string before assuming a server bug.**
|
|
265
|
+
If the `use_figma` call silently fails, truncates, or the host reports the tool payload is too large, the `helpers.js` bundle combined with the apply script may exceed the host's per-message or per-tool-call limit. Measure with `Buffer.byteLength(code, "utf8")` (Node) or `wc -c` (shell). If the string is too large for the chat/tool input, write it to a file and paste it into the MCP `use_figma` UI directly instead of relying on the model to pass the full string inline. See also the delivery notes in [#462](https://github.com/let-sunny/canicode/issues/462) and [`docs/roundtrip-protocol.md`](https://github.com/let-sunny/canicode/blob/main/docs/roundtrip-protocol.md).
|
|
266
|
+
|
|
267
|
+
5. **Common symptom → likely cause mapping:**
|
|
268
|
+
- `ReferenceError: CanICodeRoundtrip is not defined` → `helpers.js` was not prepended (check 3 above).
|
|
269
|
+
- Truncated response or no server round-trip returned → delivery size issue (check 4 above).
|
|
270
|
+
- Permission error or "cannot edit" → Figma file seat or edit access; a Viewer seat cannot write via Plugin API.
|
|
271
|
+
- Tools missing from live tool list despite on-disk config → MCP server not enabled or not restarted (check 1 above).
|
|
272
|
+
|
|
214
273
|
---
|
|
215
274
|
|
|
216
275
|
## Telemetry
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "canicode",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"mcpName": "io.github.let-sunny/canicode",
|
|
5
5
|
"description": "Lint Figma designs for AI code-gen and roundtrip the answers back into the file. CLI + MCP server.",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "tsup --config tsup.config.ts && pnpm build:roundtrip && pnpm bundle:skills",
|
|
21
21
|
"build:web": "bash scripts/build-web.sh",
|
|
22
|
-
"build:roundtrip": "tsup --config tsup.roundtrip.config.ts",
|
|
22
|
+
"build:roundtrip": "tsup --config tsup.roundtrip.config.ts && tsx scripts/bundle-roundtrip-cache.ts",
|
|
23
23
|
"bundle:skills": "bash scripts/bundle-skills.sh",
|
|
24
24
|
"dev": "tsup --watch",
|
|
25
25
|
"test": "vitest",
|
package/skills/canicode/SKILL.md
CHANGED
|
@@ -13,6 +13,12 @@ This skill works with either channel — the CLI or the canicode MCP server. Bot
|
|
|
13
13
|
- A **saved fixture** (from `canicode calibrate-save-fixture`)
|
|
14
14
|
- A **FIGMA_TOKEN** for live Figma URLs
|
|
15
15
|
|
|
16
|
+
### Step 0: Verify canicode MCP tools are loaded (optional fast path)
|
|
17
|
+
|
|
18
|
+
Before shelling out to `npx canicode analyze …`, check whether the **`analyze` MCP tool** is available in **this** session — not only whether `.mcp.json` lists `canicode`. New MCP registrations usually need a **restart or MCP reload** before tools appear.
|
|
19
|
+
|
|
20
|
+
If you must use the CLI fallback, say so out loud: the user may have added `claude mcp add canicode …` but not restarted yet (#433). After restart/reload, `analyze` via MCP avoids the `npx` spawn. The fallback is valid — silence makes users think the MCP install failed.
|
|
21
|
+
|
|
16
22
|
## How to Analyze
|
|
17
23
|
|
|
18
24
|
### From a Figma URL
|
|
@@ -5,6 +5,8 @@ description: Gotcha survey (Claude Code or Cursor) — Q&A workflow; answers acc
|
|
|
5
5
|
|
|
6
6
|
# CanICode Gotchas — Design Gotcha Survey
|
|
7
7
|
|
|
8
|
+
**Channel contrast:** **`canicode-gotchas`** (**this skill**) persists answers **only** in **local** `.claude/skills/canicode-gotchas/SKILL.md` — **memo-only**, no Plugin write to Figma. **`canicode-roundtrip`** writes to the **canvas**. Use gotchas when you want Q&A captured for code-gen context without mutating the file.
|
|
9
|
+
|
|
8
10
|
Run a gotcha survey on a Figma design to collect implementation context that Figma cannot encode natively, capture developer/designer answers, and upsert them into **`.claude/skills/canicode-gotchas/SKILL.md`** so downstream `figma-implement-design` runs have annotation-ready context. In this model, rules do rule-based best-practice detection, and gotcha is the annotation output from that detection. Some gotchas come from violation rules (what is wrong and how to resolve it); others come from info-collection rules (neutral context Figma cannot represent, like interaction intent/state).
|
|
9
11
|
|
|
10
12
|
**Install location:** The workflow prose may live under `.claude/skills/canicode-gotchas/SKILL.md` (default `canicode init`) or be copied to `.cursor/skills/canicode-gotchas/SKILL.md` (`canicode init --cursor-skills`). The **authoritative gotcha store** is always **`.claude/skills/canicode-gotchas/SKILL.md`** — the CLI `upsert-gotcha-section` writes there only. In the `.claude` copy, this file has two regions: the **Workflow** below (installed by `canicode init`, never overwritten manually) and the **Collected Gotchas** region at the bottom (one numbered section per design, replaced in place on re-runs).
|
|
@@ -18,6 +20,12 @@ Run a gotcha survey on a Figma design to collect implementation context that Fig
|
|
|
18
20
|
|
|
19
21
|
## Workflow
|
|
20
22
|
|
|
23
|
+
### Step 0: Verify canicode MCP tools are loaded (optional fast path)
|
|
24
|
+
|
|
25
|
+
Before Step 1, verify that `gotcha-survey` is callable in **this** session — not merely listed in `.mcp.json`. Newly registered MCP servers usually need a **host restart or MCP reload** before tools appear (same pattern as `/canicode-roundtrip` Step 0 for the Figma MCP).
|
|
26
|
+
|
|
27
|
+
When you fall back to `npx canicode gotcha-survey … --json`, tell the user explicitly: the canicode MCP may not be loaded yet. They should register it (`claude mcp add canicode -- npx --yes --package=canicode canicode-mcp`, or the Cursor/`mcp.json` equivalent in the Customization guide) and **restart the IDE or reload MCP** — then the next session can use the MCP tool without spawning `npx`. The CLI fallback is correct behavior; silence makes users think registration failed (#433).
|
|
28
|
+
|
|
21
29
|
### Step 1: Run the gotcha survey
|
|
22
30
|
|
|
23
31
|
If the `gotcha-survey` MCP tool is available, call it with the user's Figma URL:
|
|
@@ -44,13 +52,40 @@ If `isReadyForCodeGen` is `true` or `questions` is empty:
|
|
|
44
52
|
- Do NOT write to `.claude/skills/canicode-gotchas/SKILL.md`.
|
|
45
53
|
- Stop here.
|
|
46
54
|
|
|
55
|
+
### Step 3 — preamble: match the user's language
|
|
56
|
+
|
|
57
|
+
Before rendering any question, detect the user's conversation language from their recent messages in **this** session. Korean vs. English vs. other is usually unambiguous; when ambiguous, default to English and ask the user once which language they prefer.
|
|
58
|
+
|
|
59
|
+
When the user's language is non-English, localize only the **human-readable** strings rendered in the prompt templates below: the `question` text, the `why` line (if shown), the `Hint:` body, the `Example:` body, and the batch shared-prompt wording — including the "Reply with one answer to apply to all …, or **split** to answer each individually" sentence and the `skip` / `n/a` affordance sentence that follows it. Translate at render time only; the rule templates in `core/rules/*` stay English-only per CLAUDE.md and the issue's "Out of scope" list — do not rewrite source.
|
|
60
|
+
|
|
61
|
+
Keep the following English even when localizing, because they are identifiers or structural markers that downstream tools grep for: `ruleId`, `nodeId`, `nodeName`, the severity label in brackets (`[blocking]`, `[risk]`, `[missing-info]`, `[suggestion]`), and the entire markdown scaffolding of the Step 4 upsert section (`## #NNN — …` headings, `Design key`, `#### Skipped (N)`, the per-record field labels). `renderGotchaSection` is the source of truth for that on-disk markdown (ADR-016) and its output stays English.
|
|
62
|
+
|
|
63
|
+
In Step 4, pass the user's answer through **verbatim** into the `answers[<nodeId>].answer` field — do **not** back-translate answers to English. `figma-implement-design` is cross-language by design (see #461), and a round-trip to English introduces translation loss and defeats the "shared language for designer/PM" framing.
|
|
64
|
+
|
|
47
65
|
### Step 3: Present questions to the user
|
|
48
66
|
|
|
49
|
-
The survey response carries a pre-computed `groupedQuestions.groups[].batches[]` shape so this skill never has to sort, partition, or maintain a batchable-rule whitelist in prose. The sort key, `_no-source` sentinel, and batchable-rule
|
|
67
|
+
The survey response carries a pre-computed `groupedQuestions.groups[].batches[]` shape so this skill never has to sort, partition, or maintain a batchable-rule whitelist in prose. The sort key, `_no-source` sentinel, and both batchable-rule lists (`BATCHABLE_RULE_IDS` for `safe` mode, `OPT_IN_BATCHABLE_RULE_IDS` for `opt-in` mode) all live in `core/gotcha/group-and-batch-questions.ts` with vitest coverage (per ADR-016). Iterate over it:
|
|
68
|
+
|
|
69
|
+
**Before presenting the first batch**, display this shortcut notice once so the user knows they can exit early at any point:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
Survey: {totalBatchCount} question(s) to answer.
|
|
73
|
+
Tip: reply `skip remaining` at any point to bypass the rest with a default no-op annotation and finish immediately.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Where `totalBatchCount` is `groupedQuestions.groups.flatMap((g) => g.batches).length`.
|
|
77
|
+
|
|
78
|
+
**After every 3rd batch** (i.e. after batches 3, 6, 9, …), re-surface the shortcut as a brief reminder before presenting the next batch:
|
|
50
79
|
|
|
51
|
-
|
|
80
|
+
```
|
|
81
|
+
(You can still reply `skip remaining` to bypass the remaining questions.)
|
|
82
|
+
```
|
|
52
83
|
|
|
53
|
-
|
|
84
|
+
When the user replies `skip remaining` at any point during Step 3, immediately treat all unanswered batches as skipped (`{ "skipped": true }` for each unanswered `nodeId`) and proceed directly to Step 4 without asking further questions.
|
|
85
|
+
|
|
86
|
+
For every `batch` in `groupedQuestions.groups.flatMap((g) => g.batches)`, branch on `batch.batchMode`:
|
|
87
|
+
|
|
88
|
+
- **`batch.batchMode === "none"`** — single-question batch; the helper guarantees `batch.questions.length === 1`. Render the standard prompt for `batch.questions[0]`:
|
|
54
89
|
|
|
55
90
|
```
|
|
56
91
|
**[{severity}] {ruleId}** — node: {nodeName}
|
|
@@ -61,7 +96,7 @@ For every `batch` in `groupedQuestions.groups.flatMap((g) => g.batches)`:
|
|
|
61
96
|
> Example: {example}
|
|
62
97
|
```
|
|
63
98
|
|
|
64
|
-
-
|
|
99
|
+
- **`batch.batchMode === "safe"` with `batch.questions.length >= 2`** (#369) — rule in `BATCHABLE_RULE_IDS`; one answer is uniformly applicable. Render one shared prompt:
|
|
65
100
|
|
|
66
101
|
```
|
|
67
102
|
**[{severity}] {ruleId}** — {batch.questions.length} instances:
|
|
@@ -79,13 +114,32 @@ For every `batch` in `groupedQuestions.groups.flatMap((g) => g.batches)`:
|
|
|
79
114
|
|
|
80
115
|
Where `sharedQuestionPrompt` reuses the rule's `question` text with the per-node noun replaced by the rule's plural noun (e.g. "These layers all use FILL sizing without min/max constraints. What size boundaries should they share?" instead of repeating the singular phrasing N times).
|
|
81
116
|
|
|
82
|
-
-
|
|
117
|
+
- **`batch.batchMode === "opt-in"` with `batch.questions.length >= 2`** (#426) — rule in `OPT_IN_BATCHABLE_RULE_IDS` (currently `missing-prototype`). The same answer is usually shareable across siblings but may legitimately differ per node — signal that explicitly so the user can opt out of the shared answer with `split`:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
**[{severity}] {batch.ruleId}** — {batch.questions.length} instances of the same rule:
|
|
121
|
+
- {nodeName₁}
|
|
122
|
+
- {nodeName₂}
|
|
123
|
+
- …
|
|
124
|
+
|
|
125
|
+
{sharedQuestionPrompt}
|
|
126
|
+
|
|
127
|
+
Apply this answer to all {batch.questions.length} occurrences of `{batch.ruleId}`, or reply **split** to answer each individually.
|
|
128
|
+
|
|
129
|
+
> Hint: {hint}
|
|
130
|
+
> Example: {example}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Unlike `safe` batches, the prompt frames the answer as a suggested default, not a uniform truth — reuse the rule's existing `example` (e.g. `missing-prototype`'s "navigates to `/product/{id}` detail page") so the user knows the answer can be a pattern, not a literal string shared character-for-character.
|
|
134
|
+
|
|
135
|
+
- **Single-member `safe` or `opt-in` batch (`batch.questions.length === 1`)** — render the single-question template above; the shared-prompt framing collapses to the rule's own wording when there is only one node.
|
|
83
136
|
|
|
84
137
|
Wait for the user's answer before moving to the next batch. The user may:
|
|
85
|
-
- Answer the question / batch directly
|
|
86
|
-
- Say **split** (batch only) to fall back to per-question prompting for that batch
|
|
138
|
+
- Answer the question / batch directly (single value or pattern covers all batch members)
|
|
139
|
+
- Say **split** (batch only) to fall back to per-question prompting for that batch — works the same for both `safe` and `opt-in` batches
|
|
87
140
|
- Say **skip** to skip the question / the entire batch
|
|
88
141
|
- Say **n/a** if the question / the entire batch is not applicable
|
|
142
|
+
- Say **skip remaining** to immediately skip all remaining unanswered batches and proceed to Step 4
|
|
89
143
|
|
|
90
144
|
When applying the batched answer, expand back to per-question records in Step 4 — the gotcha section format stores one record per `nodeId`.
|
|
91
145
|
|
|
@@ -115,77 +169,48 @@ The `core/contracts/design-key.ts` helper (`computeDesignKey`) handles every sha
|
|
|
115
169
|
|
|
116
170
|
File-state detection (4-way: missing / valid / missing-heading / clobbered) and section walking (find existing `## #NNN — ...` by `Design key` substring, otherwise compute the next monotonic zero-padded NNN) are deterministic markdown operations and live in `core/gotcha/upsert-gotcha-section.ts` with vitest coverage — do not re-implement them in prose (per ADR-016).
|
|
117
171
|
|
|
118
|
-
|
|
172
|
+
Build **one JSON object** on stdin for `upsert-gotcha-section`. The CLI renders the section markdown from `survey` + `answers` via `renderGotchaSection` in TypeScript (#439) — severity, rule text, node ids, and instance context come **verbatim** from `gotcha-survey --json`; the skill must not paste LLM-authored section prose.
|
|
173
|
+
|
|
174
|
+
Payload shape:
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"survey": {
|
|
179
|
+
"designKey": "<same as Step 4a>",
|
|
180
|
+
"designGrade": "<from gotcha-survey>",
|
|
181
|
+
"questions": "<full questions[] array from gotcha-survey — preserve order>"
|
|
182
|
+
},
|
|
183
|
+
"answers": {
|
|
184
|
+
"<nodeId>": { "answer": "…" }
|
|
185
|
+
},
|
|
186
|
+
"designName": "<Figma file name or fixture label>",
|
|
187
|
+
"figmaUrl": "<the user's input URL or path>",
|
|
188
|
+
"analyzedAt": "<ISO 8601 timestamp when you upsert>",
|
|
189
|
+
"today": "<YYYY-MM-DD local date for the section title>"
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
For skipped / n/a: use `{ "skipped": true }` for that `nodeId`, or omit the key. Skipped questions do **not** get per-question rows; `renderGotchaSection` appends a compact **`#### Skipped (N)`** block listing each `ruleId` with a count (`ruleId` lines sorted lexically — see `src/core/gotcha/render-gotcha-section.ts`).
|
|
194
|
+
|
|
195
|
+
Invoke (cac requires `--input=-`, not `--input -`, so the stdin sentinel survives parsing — #420):
|
|
119
196
|
|
|
120
197
|
```bash
|
|
121
198
|
npx canicode upsert-gotcha-section \
|
|
122
199
|
--file .claude/skills/canicode-gotchas/SKILL.md \
|
|
123
200
|
--design-key "<designKey from Step 4a>" \
|
|
124
|
-
--
|
|
201
|
+
--input=-
|
|
125
202
|
```
|
|
126
203
|
|
|
127
|
-
|
|
204
|
+
Pipe the JSON object on stdin. `--design-key` must equal `survey.designKey` (the CLI validates the match).
|
|
205
|
+
|
|
206
|
+
The CLI prints JSON `{ state, action, sectionNumber, wrote, userMessage, designKey }`:
|
|
128
207
|
|
|
129
208
|
- `wrote: true` → success. `action` is `"replace"` (preserved `sectionNumber`) or `"append"` (next monotonic `sectionNumber`).
|
|
130
209
|
- `wrote: false` with `state: "missing"` → tell the user: *"Your gotchas SKILL.md is not installed yet. Run `canicode init` first, then re-invoke this skill."* Stop here.
|
|
131
210
|
- `wrote: false` with `state: "clobbered"` → tell the user: *"Your gotchas SKILL.md is missing the canicode YAML frontmatter (pre-#340 single-design clobber). Run `canicode init --force` to restore the workflow, then re-run this survey — your answers will land in a clean numbered section."* Stop here.
|
|
132
211
|
- `wrote: true` with `state: "missing-heading"` → silent recovery. The CLI injected the `# Collected Gotchas` heading and appended the section; the workflow region above is untouched.
|
|
133
212
|
|
|
134
|
-
The Workflow region above must never be touched.
|
|
135
|
-
|
|
136
|
-
## Output Template
|
|
137
|
-
|
|
138
|
-
Each per-design section in the `# Collected Gotchas` region has this exact shape:
|
|
139
|
-
|
|
140
|
-
````markdown
|
|
141
|
-
## #NNN — {designName} — {YYYY-MM-DD}
|
|
142
|
-
|
|
143
|
-
- **Figma URL**: {figmaUrl}
|
|
144
|
-
- **Design key**: {designKey}
|
|
145
|
-
- **Grade**: {designGrade}
|
|
146
|
-
- **Analyzed at**: {analyzedAt}
|
|
147
|
-
|
|
148
|
-
### Gotchas
|
|
149
|
-
|
|
150
|
-
#### {ruleId} — {nodeName}
|
|
151
|
-
|
|
152
|
-
- **Severity**: {severity}
|
|
153
|
-
- **Node ID**: {nodeId}
|
|
154
|
-
- **Instance context** (omit this bullet if `instanceContext` was not in the survey question): parent instance `parentInstanceNodeId`, source node `sourceNodeId`, component `sourceComponentName` / `sourceComponentId` when present — roundtrip apply uses this to write on the source definition when instance overrides fail.
|
|
155
|
-
- **Question**: {question}
|
|
156
|
-
- **Answer**: {userAnswer}
|
|
157
|
-
|
|
158
|
-
(repeat for each question)
|
|
159
|
-
````
|
|
160
|
-
|
|
161
|
-
### Field mapping
|
|
162
|
-
|
|
163
|
-
| Field | Source |
|
|
164
|
-
|-------|--------|
|
|
165
|
-
| `NNN` | `sectionNumber` — zero-padded three-digit index. Preserved on re-run, incremented on append. |
|
|
166
|
-
| `designName` | Figma file name or fixture name from the input |
|
|
167
|
-
| `YYYY-MM-DD` | Today's date (the day you are running the survey) |
|
|
168
|
-
| `figmaUrl` | The input URL or fixture path provided by the user |
|
|
169
|
-
| `designKey` | `survey.designKey` from the gotcha-survey response (see Step 4a) |
|
|
170
|
-
| `designGrade` | `designGrade` from gotcha-survey response |
|
|
171
|
-
| `analyzedAt` | Current timestamp (ISO 8601) |
|
|
172
|
-
| `ruleId` | `ruleId` from each question |
|
|
173
|
-
| `nodeName` | `nodeName` from each question |
|
|
174
|
-
| `severity` | `severity` from each question (blocking / risk / missing-info — the last surfaces only for info-collection rules per #406) |
|
|
175
|
-
| `nodeId` | `nodeId` from each question |
|
|
176
|
-
| `instanceContext` | When present on the question, copy `parentInstanceNodeId`, `sourceNodeId`, `sourceComponentId`, `sourceComponentName` into the bullet above (roundtrip / Plugin apply) |
|
|
177
|
-
| `question` | `question` from each question |
|
|
178
|
-
| `userAnswer` | The answer collected from the user in Step 3 |
|
|
179
|
-
|
|
180
|
-
### Skipped questions
|
|
181
|
-
|
|
182
|
-
If the user skipped a question or said "n/a", still include it in the section with:
|
|
183
|
-
|
|
184
|
-
```markdown
|
|
185
|
-
- **Answer**: _(skipped)_
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
This ensures the code generation agent knows the gotcha exists even if no answer was provided.
|
|
213
|
+
The Workflow region above must never be touched.
|
|
189
214
|
|
|
190
215
|
## Edge Cases
|
|
191
216
|
|
|
@@ -195,7 +220,7 @@ This ensures the code generation agent knows the gotcha exists even if no answer
|
|
|
195
220
|
- **Workflow region**: Never modified. If you notice the Workflow region has been edited by the user, leave their edits alone — only the `# Collected Gotchas` region is under skill control.
|
|
196
221
|
- **Pre-#340 clobbered file** (the YAML frontmatter was rewritten to a per-design variant, so the canonical `canicode-gotchas` frontmatter is missing): tell the user to run `canicode init --force` to restore the workflow, then re-run the survey. The prior single-design content cannot be automatically migrated into a `## #001` section — the user re-runs and gets a clean section.
|
|
197
222
|
- **MCP tool not available**: Fall back to `npx canicode gotcha-survey <input> --json` — the CLI returns the same `GotchaSurvey` shape. If the CLI is also unavailable (e.g. no node runtime), tell the user to install the canicode MCP server or the `canicode` npm package (see Prerequisites).
|
|
198
|
-
- **Partial answers**: If the user stops mid-survey, upsert the section with answers collected so far.
|
|
223
|
+
- **Partial answers**: If the user stops mid-survey, upsert the section with answers collected so far. Remaining questions count toward **`#### Skipped (N)`** (omit keys or `{ "skipped": true }`).
|
|
199
224
|
- **Manual section deletion**: If the user deletes a middle section by hand, do not renumber existing sections. The next new section still gets `(highest existing number) + 1`; numeric gaps are acceptable (same pattern as `.claude/docs/ADR.md`).
|
|
200
225
|
|
|
201
226
|
# Collected Gotchas
|