yadflow 3.17.0 → 3.17.1
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/CHANGELOG.md +2 -0
- package/cli/commit.mjs +1 -1
- package/cli/next.mjs +1 -1
- package/package.json +1 -1
- package/skills/sdlc/config.yaml +1 -1
- package/skills/yad-analysis/SKILL.md +1 -1
- package/skills/yad-checks/references/check-gates.md +3 -3
- package/skills/yad-connect-learning/references/learning-registry.md +1 -1
- package/skills/yad-engineer-review/references/ship-and-record.md +3 -3
- package/skills/yad-epic/SKILL.md +1 -1
- package/skills/yad-epic/references/state-schema.md +6 -6
- package/skills/yad-implement/SKILL.md +3 -3
- package/skills/yad-implement/references/implement-conventions.md +7 -7
- package/skills/yad-spec/SKILL.md +1 -1
- package/skills/yad-spec/references/spec-handoff.md +3 -3
- package/skills/yad-status/SKILL.md +1 -1
- package/skills/yad-stories/SKILL.md +1 -1
- package/skills/yad-test-cases/SKILL.md +1 -1
- package/skills/yad-test-cases/references/test-cases-schema.md +1 -1
- package/skills/yad-ui/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
package/cli/commit.mjs
CHANGED
|
@@ -38,7 +38,7 @@ export function buildCommitMessage({ type, subject, task, contractChange = false
|
|
|
38
38
|
return parts.join('\n');
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
// feat/EP-
|
|
41
|
+
// feat/EP-checkout-S01-T01-create-order -> EP-checkout-S01-T01
|
|
42
42
|
export function taskFromBranch(branch = '') {
|
|
43
43
|
const m = branch.match(/(.+-S\d+-T\d+)(?:-|$)/i);
|
|
44
44
|
return m ? m[1].replace(/^[a-z]+\//i, '') : null;
|
package/cli/next.mjs
CHANGED
|
@@ -46,7 +46,7 @@ const actionFor = (root, id) => ({
|
|
|
46
46
|
lineageKind: epicLineage(root, id).kind,
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
// EP-
|
|
49
|
+
// EP-checkout-S03 → S03 (the compact lane label for the roll-up). Falls back to the full id.
|
|
50
50
|
const shortStory = (s) => (s && s.match(/S\d+$/i)?.[0]) || s || '(story)';
|
|
51
51
|
|
|
52
52
|
// Every per-repo lane across the build, flattened with its story id attached.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yadflow",
|
|
3
|
-
"version": "3.17.
|
|
3
|
+
"version": "3.17.1",
|
|
4
4
|
"description": "Yadflow — the gated, team, multi-repo SDLC: author → review → build with a PR-driven review gate and a zero-dependency `yad` CLI (setup, gate, commit, open-pr, ship, repo, thread, reconcile). A BMAD module + 38 yad-* skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "AbdelRahman Nasr",
|
package/skills/sdlc/config.yaml
CHANGED
|
@@ -94,7 +94,7 @@ build:
|
|
|
94
94
|
spec_layout: speckit # follow Spec Kit's native spec/plan/tasks layout
|
|
95
95
|
speckit_ceremony: [specify, clarify, plan, analyze, checklist, tasks] # heavy run, once per story per repo
|
|
96
96
|
# Step B (yad-implement) — the light per-task loop. One atomic task = one branch = one PR/MR.
|
|
97
|
-
branch_convention: "feat/<story-id>-<task-id>-<short-slug>" # e.g. feat/EP-
|
|
97
|
+
branch_convention: "feat/<story-id>-<task-id>-<short-slug>" # e.g. feat/EP-checkout-S01-T01-create-order
|
|
98
98
|
commit_task_trailer: "Task: <story-id>-<task-id>" # final commit trailer; anchors the spec-link check (Step C)
|
|
99
99
|
contract_change_trailer: "Contract-Change: yes" # ONLY when the locked contract surface is touched (routes back to architecture gate)
|
|
100
100
|
# Commit subject + PR/MR title style (Conventional Commits — see CONTRIBUTING.md). PRs are squash-merged,
|
|
@@ -62,7 +62,7 @@ roadmap. **Optional & non-blocking:** if there is no discovery, or it has not ye
|
|
|
62
62
|
|
|
63
63
|
### Step 3 — Generate the Epic ID (engine-assigned, never by hand)
|
|
64
64
|
Derive `EP-<slug>` where `slug` is **2–4 lowercase words joined by hyphens**, drawn from the idea
|
|
65
|
-
(e.g. `EP-
|
|
65
|
+
(e.g. `EP-checkout`). Lowercase except the fixed `EP` prefix. `EP-discovery` is **reserved**
|
|
66
66
|
for the project front-zero — never use it for a feature. **The ID is assigned once and
|
|
67
67
|
never renamed** — renaming breaks every downstream link (build plan §6b). Check
|
|
68
68
|
`{project-root}/epics/` for collisions; if the slug exists, append a distinguishing word.
|
|
@@ -489,15 +489,15 @@ bash checks/commit-message.sh --profile code master
|
|
|
489
489
|
# pr-title / pr-template validate the actual PR/MR metadata (in CI they come from the event payload).
|
|
490
490
|
# By hand, pass the title, and a FILE holding the PR/MR description (the rendered/filled body, not the
|
|
491
491
|
# template source):
|
|
492
|
-
bash checks/pr-title.sh --profile code "feat: add the
|
|
492
|
+
bash checks/pr-title.sh --profile code "feat: add the order endpoint"
|
|
493
493
|
# save the PR/MR description to a file first (e.g. `gh pr view <n> --json body -q .body > /tmp/pr-body.md`)
|
|
494
494
|
bash checks/pr-template.sh --profile code /tmp/pr-body.md
|
|
495
495
|
```
|
|
496
496
|
|
|
497
|
-
## Proven behavior (demo: `demo-repos/backend`, story EP-
|
|
497
|
+
## Proven behavior (demo: `demo-repos/backend`, story EP-checkout-S01)
|
|
498
498
|
|
|
499
499
|
- **Good PR** (task branch with a `Task:` trailer, no surface change, passing tests) → all three **PASS**.
|
|
500
500
|
- **Bad PR A** (a code change committed with **no** `Task:` trailer) → spec-link **FAILS**.
|
|
501
|
-
- **Bad PR B** (edits `specs/.../contracts/
|
|
501
|
+
- **Bad PR B** (edits `specs/.../contracts/orders.md` to widen the surface, with a `Task:` trailer
|
|
502
502
|
but **no** `Contract-Change`) → spec-link passes, contract-check **FAILS** and routes back to the
|
|
503
503
|
architecture gate.
|
|
@@ -17,7 +17,7 @@ root, not under any `epics/EP-<slug>/.sdlc/`.
|
|
|
17
17
|
"tool": "deeptutor", // deeptutor | <adapter id> | none (harness-native)
|
|
18
18
|
"provider": "deeptutor-cli", // the concrete CLI: deeptutor-cli | null
|
|
19
19
|
"version": "1.4.5", // CLI version reported at detect time; null if absent
|
|
20
|
-
"kb": "yadflow-
|
|
20
|
+
"kb": "yadflow-checkout", // grounded knowledge-base name; null if not built
|
|
21
21
|
"kb_sources": ["epic.md", "architecture.md", "contract.md", "ui-design.md", "stories/", "code-context/*/code-map.md"],
|
|
22
22
|
"auth": "user", // ALWAYS the user's own DeepTutor config / LLM keys — never a token
|
|
23
23
|
"connectedAt": "2026-06-14", // first connect (YYYY-MM-DD)
|
|
@@ -36,13 +36,13 @@ without the `{ epic, ships }` wrapper):
|
|
|
36
36
|
|
|
37
37
|
```json
|
|
38
38
|
{
|
|
39
|
-
"epic": "EP-
|
|
39
|
+
"epic": "EP-checkout",
|
|
40
40
|
"ships": [
|
|
41
41
|
{
|
|
42
|
-
"story": "EP-
|
|
42
|
+
"story": "EP-checkout-S01",
|
|
43
43
|
"task": "T01",
|
|
44
44
|
"repo": "backend",
|
|
45
|
-
"branch": "feat/EP-
|
|
45
|
+
"branch": "feat/EP-checkout-S01-T01-create-order",
|
|
46
46
|
"pr": "<url|#|local>",
|
|
47
47
|
"mergeCommit": "<sha>",
|
|
48
48
|
"gates": ["spec-link", "contract-check", "build-test-lint"],
|
package/skills/yad-epic/SKILL.md
CHANGED
|
@@ -83,7 +83,7 @@ unchanged — do not consume an unapproved roadmap. After seeding the epic, the
|
|
|
83
83
|
### Step 3 — Generate the Epic ID (engine-assigned, never by hand) — analysis-skipped only
|
|
84
84
|
*(Skip when analysis ran — the ID was already assigned by `yad-analysis`.)*
|
|
85
85
|
Derive `EP-<slug>` where `slug` is **2–4 lowercase words joined by hyphens**, drawn from the idea
|
|
86
|
-
(e.g. `EP-
|
|
86
|
+
(e.g. `EP-checkout`). Lowercase except the fixed `EP` prefix. `EP-discovery` is **reserved**
|
|
87
87
|
for the project front-zero — never use it for a feature. **The ID is assigned once and
|
|
88
88
|
never renamed** — renaming breaks every downstream link (build plan §6b).
|
|
89
89
|
Check `{project-root}/epics/` for collisions; if the slug exists, append a distinguishing word.
|
|
@@ -416,7 +416,7 @@ only re-authored steps run. The seeder sets `currentStep` to the first re-author
|
|
|
416
416
|
```json
|
|
417
417
|
{ "id": "architecture", "type": "author", "artifact": "architecture.md",
|
|
418
418
|
"assistance": "review", "automation": "human_approve", "locked": true,
|
|
419
|
-
"status": "done", "inherited": true, "inheritedFrom": "EP-
|
|
419
|
+
"status": "done", "inherited": true, "inheritedFrom": "EP-checkout",
|
|
420
420
|
"boundHash": "sha256:…", "risk_tags": [] }
|
|
421
421
|
```
|
|
422
422
|
|
|
@@ -432,7 +432,7 @@ only re-authored steps run. The seeder sets `currentStep` to the first re-author
|
|
|
432
432
|
|
|
433
433
|
```json
|
|
434
434
|
{ "artifact": "architecture.md", "step": "architecture-review", "status": "inherited",
|
|
435
|
-
"from": "EP-
|
|
435
|
+
"from": "EP-checkout", "boundHash": "sha256:…", "date": "<YYYY-MM-DD>" }
|
|
436
436
|
```
|
|
437
437
|
|
|
438
438
|
## The pointer-lock — `contract-lock.json` in a change-epic
|
|
@@ -443,7 +443,7 @@ no `contract.md` in the child to edit, so the surface physically cannot drift.
|
|
|
443
443
|
|
|
444
444
|
```json
|
|
445
445
|
{ "artifact": "contract.md", "hash": "sha256:<parent hash, verbatim>", "lockedAt": "<date>",
|
|
446
|
-
"inheritedFrom": "EP-
|
|
446
|
+
"inheritedFrom": "EP-checkout", "ref": "../../EP-checkout/.sdlc/contract-lock.json" }
|
|
447
447
|
```
|
|
448
448
|
|
|
449
449
|
Omitting `architecture` from `inherits` (depth `contract-surface`) is what triggers a **real re-lock**:
|
|
@@ -455,9 +455,9 @@ architecture gate" with "open a contract-surface change-epic" — one mechanism,
|
|
|
455
455
|
Intake + triage record, one per change/defect/hotfix epic (sibling of `approvals.json`).
|
|
456
456
|
|
|
457
457
|
```json
|
|
458
|
-
{ "epicId": "EP-
|
|
458
|
+
{ "epicId": "EP-checkout-queue-filter", "thread": "EP-checkout", "parent": "EP-checkout",
|
|
459
459
|
"kind": "defect", "depth": "defect-fix", "intakeBy": "alice", "intakeDate": "<YYYY-MM-DD>",
|
|
460
|
-
"title": "Pending queue returns
|
|
460
|
+
"title": "Pending queue returns fulfilled orders", "description": "…",
|
|
461
461
|
"affectedArtifacts": ["stories", "test-cases"],
|
|
462
462
|
"reauthors": ["stories", "test-cases"], "inherits": ["epic", "architecture", "contract", "ui-design"],
|
|
463
463
|
"defect": { "origin": "production", "severity": "sev2", "escape_stage": "test-cases",
|
|
@@ -476,7 +476,7 @@ sharing `thread` and read each `change.json`; there is no duplicated thread regi
|
|
|
476
476
|
Append-only ledger of hotfix ship-first debt (a hotfix shipped code before its front gates approved).
|
|
477
477
|
|
|
478
478
|
```json
|
|
479
|
-
[ { "thread": "EP-
|
|
479
|
+
[ { "thread": "EP-checkout", "epicId": "EP-checkout-hotfix-x", "openedDate": "<date>",
|
|
480
480
|
"reason": "prod outage", "requires": ["artifacts-updated", "regression-test"],
|
|
481
481
|
"status": "open", "paidDate": null, "paidBy": null,
|
|
482
482
|
"evidence": { "artifacts": [], "regressionTest": "" } } ]
|
|
@@ -23,9 +23,9 @@ declares; it does not redesign, does not widen the contract, and does not pick u
|
|
|
23
23
|
- The work happens **inside the code repo** (a separate git repo) at
|
|
24
24
|
`{project-root}/demo-repos/<repo>/` (`config.yaml` `build.code_repos_root`). Use absolute paths.
|
|
25
25
|
- **Branch name:** `feat/<story-id>-<task-id>-<short-slug>` (e.g.
|
|
26
|
-
`feat/EP-
|
|
26
|
+
`feat/EP-checkout-S01-T01-create-order`). Branched off the code repo's default branch.
|
|
27
27
|
- **Commit message:** a conventional subject, body describing the change, and a **required `Task:`
|
|
28
|
-
trailer** (e.g. `Task: EP-
|
|
28
|
+
trailer** (e.g. `Task: EP-checkout-S01-T01`) in the trailer block. Add `Contract-Change: yes`
|
|
29
29
|
**only** if the diff touches the locked contract surface (see Step 5), and a per-commit
|
|
30
30
|
`Co-Authored-By:` for any AI tool that helped author the diff (the human author owns the commit;
|
|
31
31
|
trailer order `Task:` → `Contract-Change:` → `Co-Authored-By:`). The skill installs a `.gitmessage`
|
|
@@ -84,7 +84,7 @@ boundary breach). The same applies to the Step 5 contract-surface stop (`contrac
|
|
|
84
84
|
### Step 5 — Contract-surface check (local pre-flight for Step C)
|
|
85
85
|
Determine whether the diff touches the **locked contract surface** (the API/event/data-model shapes in
|
|
86
86
|
`epics/<epic>/contract.md`'s `CONTRACT-SURFACE` block). Normal implementation **consumes** the
|
|
87
|
-
contract (e.g. implementing `POST /
|
|
87
|
+
contract (e.g. implementing `POST /orders` to the agreed shape) — that is **not** a contract change.
|
|
88
88
|
A contract change means the diff alters the agreed cross-repo shape itself.
|
|
89
89
|
|
|
90
90
|
- If the diff does **not** change the surface: proceed; no `Contract-Change` trailer.
|
|
@@ -10,11 +10,11 @@ diff back to its task, story, and contract.
|
|
|
10
10
|
feat/<story-id>-<task-id>-<short-slug>
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
- `<story-id>` — the permanent story ID, e.g. `EP-
|
|
13
|
+
- `<story-id>` — the permanent story ID, e.g. `EP-checkout-S01`.
|
|
14
14
|
- `<task-id>` — the atomic task ID from `tasks.md`, e.g. `T01`.
|
|
15
|
-
- `<short-slug>` — 2–4 hyphenated words naming the change, e.g. `create-
|
|
15
|
+
- `<short-slug>` — 2–4 hyphenated words naming the change, e.g. `create-order`.
|
|
16
16
|
|
|
17
|
-
Example: `feat/EP-
|
|
17
|
+
Example: `feat/EP-checkout-S01-T01-create-order`. Branched off the code repo's default
|
|
18
18
|
branch. One task = one branch; never reuse a branch for a different task, never fork a second branch
|
|
19
19
|
for the same task.
|
|
20
20
|
|
|
@@ -33,9 +33,9 @@ Task: <story-id>-<task-id>
|
|
|
33
33
|
- The **`<type>` is lowercase** (`feat`, `fix`, `docs`, `refactor`, `test`, `perf`, `build`, `ci`,
|
|
34
34
|
`chore`, `revert`) and the **`<subject>` starts lowercase**, is **imperative**, and has **no trailing
|
|
35
35
|
period** — Conventional Commits (see `CONTRIBUTING.md` and `config.yaml` `build.commit_subject_style`).
|
|
36
|
-
Proper nouns/acronyms keep their case (`fix: refresh OAuth token`). e.g. `feat: add POST /
|
|
37
|
-
create path`, not `feat: Add POST /
|
|
38
|
-
- The **`Task:` trailer is required** (`Task: EP-
|
|
36
|
+
Proper nouns/acronyms keep their case (`fix: refresh OAuth token`). e.g. `feat: add POST /orders
|
|
37
|
+
create path`, not `feat: Add POST /orders create path.`
|
|
38
|
+
- The **`Task:` trailer is required** (`Task: EP-checkout-S01-T01`) — the anchor the spec-link
|
|
39
39
|
check (§C) and the PR (§D) read to connect the diff to its spec and story. It need not be the *last*
|
|
40
40
|
line: the spec-link gate finds it with git's native trailer parser
|
|
41
41
|
(`%(trailers:key=Task)`), which is order-independent. All trailers must sit in **one contiguous block**
|
|
@@ -85,7 +85,7 @@ The **locked contract surface** is the cross-repo agreement in `epics/<epic>/con
|
|
|
85
85
|
`CONTRACT-SURFACE` block, hash-locked at `.sdlc/contract-lock.json`). Distinguish:
|
|
86
86
|
|
|
87
87
|
- **Consuming the contract** (normal) — implementing an endpoint/event/entity to the shape the contract
|
|
88
|
-
already agreed (e.g. building `POST /
|
|
88
|
+
already agreed (e.g. building `POST /orders` to its agreed request/response). **Not** a contract
|
|
89
89
|
change; no trailer.
|
|
90
90
|
- **Changing the contract** (exceptional) — altering the agreed shape itself (new field crossing repos,
|
|
91
91
|
changed status enum, new shared endpoint). This is **not** an implementation decision. Stop, go back
|
package/skills/yad-spec/SKILL.md
CHANGED
|
@@ -25,7 +25,7 @@ is the same graceful-degradation pattern `yad-ui` uses for Impeccable.
|
|
|
25
25
|
- **Code repos are separate git repos**, one `.git` each, under `{project-root}/demo-repos/<repo>/`
|
|
26
26
|
(`config.yaml` `build.code_repos_root`). All Spec Kit outputs land **inside the code repo**, never
|
|
27
27
|
in the product repo.
|
|
28
|
-
- `{feature-id}` is the **story ID** (e.g. `EP-
|
|
28
|
+
- `{feature-id}` is the **story ID** (e.g. `EP-checkout-S01`) — **pinned**, never Spec Kit's
|
|
29
29
|
numbered auto-slug (which is unstable and severs the permanent story link). The spec folder is
|
|
30
30
|
`specs/<story-id>/`.
|
|
31
31
|
- Spec Kit output layout (RESEARCH-NOTES §2): `specs/<feature-id>/spec.md` (+ `research.md`,
|
|
@@ -48,11 +48,11 @@ comes from the story's acceptance criteria and the **locked contract surface**
|
|
|
48
48
|
contract endpoints/entities the story touches.
|
|
49
49
|
- **`research.md`** — list the decisions and any unknowns; if the story is unambiguous, say so. Note
|
|
50
50
|
where the contract already settles a question (e.g. status is server-owned).
|
|
51
|
-
- **`data-model.md`** — the entities/fields **this repo** implements. Shared entities (e.g. `
|
|
52
|
-
`
|
|
51
|
+
- **`data-model.md`** — the entities/fields **this repo** implements. Shared entities (e.g. `Order`,
|
|
52
|
+
`OrderStatus`) are **quoted from the contract** and marked as contract-owned; repo-private fields
|
|
53
53
|
are marked as local.
|
|
54
54
|
- **`contracts/`** — the slice of the API/event surface this repo implements, **quoted from the locked
|
|
55
|
-
`contract.md`** (e.g. `POST /
|
|
55
|
+
`contract.md`** (e.g. `POST /orders` request/response). Add a one-line note that this is a quote of
|
|
56
56
|
the locked surface, not a new definition.
|
|
57
57
|
- **`plan.md`** — the technical approach for this repo at story altitude (components, sequence,
|
|
58
58
|
test approach). No new cross-repo surface.
|
|
@@ -38,7 +38,7 @@ Print, in this order:
|
|
|
38
38
|
1. **Header:** render the kind noun from `epic.md` frontmatter `kind` — **Change request** (`change`),
|
|
39
39
|
**Defect** (`defect`), **Hotfix** (`hotfix`), or **Epic** (`feature`, and the default when `kind` is
|
|
40
40
|
absent) — followed by `epicId`, then `status` from `epic.md` frontmatter, `currentStep`, and `repos`
|
|
41
|
-
(the touched domains). Example: `Defect EP-
|
|
41
|
+
(the touched domains). Example: `Defect EP-checkout-queue-filter — draft @ stories`. A bug is a defect
|
|
42
42
|
(`kind: defect`) — there is no separate noun. This is presentation only; the artifact is still an epic.
|
|
43
43
|
2. **Steps table** — for every front step in `steps[]` order (10, or 12 when the optional analysis step
|
|
44
44
|
was run): `id`, `type`, `status`, `assistance`, `automation`, `locked`, and `risk_tags`. Mark the
|
|
@@ -18,7 +18,7 @@ There is **no `sm` agent** (Phase 0 Deviation 1): the `pm` lens breaks down the
|
|
|
18
18
|
|
|
19
19
|
- `{project-root}` resolves from the project working directory.
|
|
20
20
|
- Stories live under `{project-root}/epics/EP-<slug>/stories/` (build plan §6).
|
|
21
|
-
- Story files are named `EP-<slug>-S0N.md` (zero-padded, e.g. `EP-
|
|
21
|
+
- Story files are named `EP-<slug>-S0N.md` (zero-padded, e.g. `EP-checkout-S01.md`).
|
|
22
22
|
- Speak in the configured `communication_language`; write documents in `document_output_language`.
|
|
23
23
|
|
|
24
24
|
## On Activation
|
|
@@ -152,7 +152,7 @@ When Step 3b generated or linked automation, write the machine-readable case→t
|
|
|
152
152
|
"direction": "generated | linked",
|
|
153
153
|
"tests": [
|
|
154
154
|
{ "case": "TC-01", "story": "EP-<slug>-S01", "repo": "backend", "level": "e2e",
|
|
155
|
-
"path": "tests/
|
|
155
|
+
"path": "tests/order.spec.ts", "url": "<repo url to the test>" }
|
|
156
156
|
],
|
|
157
157
|
"source": "playwright-mcp"
|
|
158
158
|
}
|
|
@@ -45,7 +45,7 @@ Written by `yad-test-cases` Step 4b. Absent when the step ran artifacts-only (`t
|
|
|
45
45
|
"direction": "generated | linked",
|
|
46
46
|
"tests": [
|
|
47
47
|
{ "case": "TC-01", "story": "EP-<slug>-S01", "repo": "backend", "level": "e2e",
|
|
48
|
-
"path": "tests/
|
|
48
|
+
"path": "tests/order.spec.ts", "url": "<repo url to the test>" }
|
|
49
49
|
],
|
|
50
50
|
"source": "playwright-mcp"
|
|
51
51
|
}
|
package/skills/yad-ui/SKILL.md
CHANGED
|
@@ -160,7 +160,7 @@ When Step 3b generated or linked a design, write the machine-readable screen→f
|
|
|
160
160
|
"generatedAt": "<YYYY-MM-DD>",
|
|
161
161
|
"direction": "generated | linked",
|
|
162
162
|
"screens": [
|
|
163
|
-
{ "name": "
|
|
163
|
+
{ "name": "Checkout", "platform": "mobile",
|
|
164
164
|
"nodeId": "123:45", "url": "https://www.figma.com/file/<key>/?node-id=123-45" }
|
|
165
165
|
],
|
|
166
166
|
"source": "figma-mcp"
|