canicode 0.10.2 → 0.10.4
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 +36 -31
- package/dist/cli/index.js +401 -22
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +261 -3
- package/dist/index.js +102 -10
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +200 -24
- package/dist/mcp/server.js.map +1 -1
- package/package.json +3 -2
- package/skills/canicode-gotchas/SKILL.md +66 -28
- package/skills/canicode-roundtrip/SKILL.md +290 -86
- package/skills/canicode-roundtrip/helpers.js +270 -10
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "canicode",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.4",
|
|
4
4
|
"mcpName": "io.github.let-sunny/canicode",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Lint Figma designs for AI code-gen and roundtrip the answers back into the file. CLI + MCP server.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"lint": "tsc --noEmit",
|
|
28
28
|
"build:plugin": "bash scripts/build-plugin.sh",
|
|
29
29
|
"sync-docs": "tsx scripts/sync-rule-docs.ts",
|
|
30
|
+
"check:skill-determinism": "tsx scripts/check-skill-determinism.ts",
|
|
30
31
|
"clean": "rm -rf dist skills"
|
|
31
32
|
},
|
|
32
33
|
"files": [
|
|
@@ -9,7 +9,7 @@ Run a gotcha survey on a Figma design to identify implementation pitfalls, colle
|
|
|
9
9
|
|
|
10
10
|
## Prerequisites
|
|
11
11
|
|
|
12
|
-
- **canicode MCP server** (preferred): `claude mcp add canicode
|
|
12
|
+
- **canicode MCP server** (preferred): `claude mcp add canicode -- npx --yes --package=canicode canicode-mcp` — long-form flags only; the short-form `-y -p` collides with `claude mcp add`'s parser (#366). The MCP server reads `FIGMA_TOKEN` from `~/.canicode/config.json` or the host environment, so do **not** pass `-e FIGMA_TOKEN=…` here (#364).
|
|
13
13
|
- **Without canicode MCP** (fallback): the `canicode gotcha-survey --json` CLI produces the same response shape — no MCP installation required.
|
|
14
14
|
- **FIGMA_TOKEN** configured for live Figma URLs
|
|
15
15
|
|
|
@@ -43,21 +43,50 @@ If `isReadyForCodeGen` is `true` or `questions` is empty:
|
|
|
43
43
|
|
|
44
44
|
### Step 3: Present questions to the user
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
The survey response carries a pre-computed `groupedQuestions.groups[].batches[]` shape so the SKILL never has to sort, partition, or maintain a batchable-rule whitelist in prose. The sort key, `_no-source` sentinel, and batchable-rule list all live in `core/gotcha/group-and-batch-questions.ts` with vitest coverage (per ADR-016). Iterate over it:
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
**[{severity}] {ruleId}** — node: {nodeName}
|
|
48
|
+
For every `batch` in `groupedQuestions.groups.flatMap((g) => g.batches)`:
|
|
50
49
|
|
|
51
|
-
|
|
50
|
+
- **Single-question batch (`batch.questions.length === 1`)** — render the standard prompt for `batch.questions[0]`:
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
```
|
|
53
|
+
**[{severity}] {ruleId}** — node: {nodeName}
|
|
54
|
+
|
|
55
|
+
{question}
|
|
56
|
+
|
|
57
|
+
> Hint: {hint}
|
|
58
|
+
> Example: {example}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- **Batch of N ≥ 2 with `batch.batchable === true`** (#369) — render one shared prompt covering every member:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
**[{severity}] {ruleId}** — {batch.questions.length} instances:
|
|
65
|
+
- {nodeName₁}
|
|
66
|
+
- {nodeName₂}
|
|
67
|
+
- …
|
|
68
|
+
|
|
69
|
+
{sharedQuestionPrompt}
|
|
70
|
+
|
|
71
|
+
Reply with one answer to apply to all {batch.questions.length}, or **split** to answer each individually.
|
|
72
|
+
|
|
73
|
+
> Hint: {hint}
|
|
74
|
+
> Example: {example}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
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).
|
|
56
78
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
-
|
|
79
|
+
- **Any batch with `batch.batchable === false`** is always rendered as a single-question prompt — the helper guarantees `questions.length === 1` for those (identity-typed answers like `non-semantic-name`, structural-mod rules).
|
|
80
|
+
|
|
81
|
+
Wait for the user's answer before moving to the next batch. The user may:
|
|
82
|
+
- Answer the question / batch directly
|
|
83
|
+
- Say **split** (batch only) to fall back to per-question prompting for that batch
|
|
84
|
+
- Say **skip** to skip the question / the entire batch
|
|
85
|
+
- Say **n/a** if the question / the entire batch is not applicable
|
|
86
|
+
|
|
87
|
+
When applying the batched answer, expand back to per-question records in Step 4 — the gotcha section format stores one record per `nodeId`.
|
|
88
|
+
|
|
89
|
+
> The `groupedQuestions.groups[].instanceContext` field exists for the `canicode-roundtrip` SKILL's "Instance note" hoist (#370). This skill ignores it — every record gets its own `Instance context` bullet in Step 4 anyway.
|
|
61
90
|
|
|
62
91
|
### Step 4: Upsert the gotcha section
|
|
63
92
|
|
|
@@ -69,26 +98,35 @@ After collecting all answers, **upsert** this design's section into the `# Colle
|
|
|
69
98
|
|
|
70
99
|
This file goes in the **user's project** (current working directory), NOT in the canicode repo. The Workflow region above **must never be modified** — only the `# Collected Gotchas` region below is touched.
|
|
71
100
|
|
|
72
|
-
#### Step 4a:
|
|
101
|
+
#### Step 4a: Use the `designKey` from the survey response
|
|
102
|
+
|
|
103
|
+
`designKey` uniquely identifies the design so re-running on the same URL replaces the existing section in place. The survey response carries it on `survey.designKey` — read it directly. Do **not** parse the input URL in prose.
|
|
104
|
+
|
|
105
|
+
The `core/contracts/design-key.ts` helper (`computeDesignKey`) handles every shape with vitest coverage so the SKILL stays ADR-016-compliant:
|
|
73
106
|
|
|
74
|
-
|
|
107
|
+
- **Figma URL** → `<fileKey>#<nodeId>` with `-` → `:` normalization on the nodeId. Example: `https://figma.com/design/abc123XYZ/My-File?node-id=42-100&t=ref` → `designKey = "abc123XYZ#42:100"`. Trailing query parameters (`?t=...`, `?mode=...`) are dropped.
|
|
108
|
+
- **Figma URL without `node-id`** → just `<fileKey>` (file-level key).
|
|
109
|
+
- **Fixture path / JSON file** → absolute path.
|
|
75
110
|
|
|
76
|
-
|
|
77
|
-
- **Fixture path** — use the absolute path, e.g. `/Users/me/project/fixtures/simple.json`.
|
|
111
|
+
#### Step 4b: Upsert via the canicode CLI
|
|
78
112
|
|
|
79
|
-
|
|
113
|
+
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 — the SKILL never re-implements them in prose (per ADR-016).
|
|
114
|
+
|
|
115
|
+
Render the per-design section markdown using the **Output Template** below with the literal string `{{SECTION_NUMBER}}` in the header (the CLI substitutes the right NNN for you — preserves it on replace, computes the next monotonic value on append). Then invoke:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npx canicode upsert-gotcha-section \
|
|
119
|
+
--file .claude/skills/canicode-gotchas/SKILL.md \
|
|
120
|
+
--design-key "<designKey from Step 4a>" \
|
|
121
|
+
--section - # then pipe the rendered section markdown through stdin
|
|
122
|
+
```
|
|
80
123
|
|
|
81
|
-
|
|
124
|
+
The CLI prints a JSON result `{ state, action, sectionNumber, wrote, userMessage }`:
|
|
82
125
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- **File has YAML frontmatter but no `# Collected Gotchas` heading** (an older workflow install, or a user-edited workflow that dropped the trailing heading) → preserve everything above unchanged and append a new `# Collected Gotchas` heading at the bottom, then proceed to step 3.
|
|
88
|
-
- **File missing the YAML frontmatter** (a pre-#340 single-design clobber — the old overwrite rewrote the frontmatter's `description` to the per-design variant, so a well-formed canicode frontmatter is the cleanest discriminator) → **do not attempt to reconstruct the workflow inline**. Tell the user: "Your gotchas SKILL.md looks like the pre-#340 single-design format. Run `canicode init --force` to restore the workflow, then re-run this survey — your answers will land in a clean numbered section." Stop here.
|
|
89
|
-
3. Walk the existing `## #NNN — ...` sections under `# Collected Gotchas` and look for one whose `- **Design key**:` bullet matches the `designKey` from Step 4a. Substring match against the bullet value is sufficient.
|
|
90
|
-
- **Found** → replace that section in place. **Preserve its `#NNN` number** so external references (downstream skills, user notes) remain stable.
|
|
91
|
-
- **Not found** → append a new section at the bottom of the region. `#NNN = (highest existing number) + 1`, zero-padded to three digits. Never reuse a number that appeared earlier and was deleted; numbering is monotonic.
|
|
126
|
+
- `wrote: true` → success. `action` is `"replace"` (preserved `sectionNumber`) or `"append"` (next monotonic `sectionNumber`).
|
|
127
|
+
- `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.
|
|
128
|
+
- `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.
|
|
129
|
+
- `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.
|
|
92
130
|
|
|
93
131
|
The Workflow region above must never be touched. Do NOT copy Workflow prose into the per-design section; the section only carries metadata + gotcha answers.
|
|
94
132
|
|
|
@@ -125,7 +163,7 @@ Each per-design section in the `# Collected Gotchas` region has this exact shape
|
|
|
125
163
|
| `designName` | Figma file name or fixture name from the input |
|
|
126
164
|
| `YYYY-MM-DD` | Today's date (the day you are running the survey) |
|
|
127
165
|
| `figmaUrl` | The input URL or fixture path provided by the user |
|
|
128
|
-
| `designKey` |
|
|
166
|
+
| `designKey` | `survey.designKey` from the gotcha-survey response (see Step 4a) |
|
|
129
167
|
| `designGrade` | `designGrade` from gotcha-survey response |
|
|
130
168
|
| `analyzedAt` | Current timestamp (ISO 8601) |
|
|
131
169
|
| `ruleId` | `ruleId` from each question |
|