skilldrop-cli 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.
- package/LICENSE +21 -0
- package/README.md +413 -0
- package/bin/skilldrop.js +338 -0
- package/model-routing.json +147 -0
- package/package.json +36 -0
- package/packs.json +90 -0
- package/skills/accessibility-audit/SKILL.md +74 -0
- package/skills/accessibility-audit/examples/login-form.md +76 -0
- package/skills/accessibility-audit/manifest.json +11 -0
- package/skills/accessibility-audit/reference.md +69 -0
- package/skills/adr-generator/SKILL.md +49 -0
- package/skills/adr-generator/manifest.json +11 -0
- package/skills/adr-generator/templates/madr.md +56 -0
- package/skills/adr-generator/templates/nygard.md +19 -0
- package/skills/ai-usage-report/SKILL.md +111 -0
- package/skills/ai-usage-report/examples/weekly-engineering-team.md +133 -0
- package/skills/ai-usage-report/manifest.json +11 -0
- package/skills/ai-usage-report/scripts/build_report.py +436 -0
- package/skills/ai-usage-report/templates/report-per-user.md +42 -0
- package/skills/ai-usage-report/templates/report-team-rollup.md +49 -0
- package/skills/ai-usage-report/templates/usage-event-schema.md +76 -0
- package/skills/api-contract-draft/SKILL.md +69 -0
- package/skills/api-contract-draft/manifest.json +11 -0
- package/skills/api-contract-draft/reference.md +73 -0
- package/skills/api-contract-draft/templates/openapi-skeleton.yaml +148 -0
- package/skills/architecture-diagrams/SKILL.md +66 -0
- package/skills/architecture-diagrams/examples/aws-three-tier.md +44 -0
- package/skills/architecture-diagrams/examples/c4-container-saas.md +51 -0
- package/skills/architecture-diagrams/examples/microservices-sequence.md +49 -0
- package/skills/architecture-diagrams/manifest.json +28 -0
- package/skills/architecture-diagrams/templates/c4-container.puml +31 -0
- package/skills/architecture-diagrams/templates/mermaid-aws.md +52 -0
- package/skills/architecture-diagrams/templates/mermaid-flowchart.md +37 -0
- package/skills/architecture-diagrams/templates/mermaid-sequence.md +39 -0
- package/skills/audience-profile/SKILL.md +73 -0
- package/skills/audience-profile/manifest.json +11 -0
- package/skills/audience-profile/reference.md +175 -0
- package/skills/brief-intake/SKILL.md +78 -0
- package/skills/brief-intake/manifest.json +11 -0
- package/skills/brief-intake/templates/brief-adr.md +41 -0
- package/skills/brief-intake/templates/brief-decision-log.md +35 -0
- package/skills/brief-intake/templates/brief-deck.md +43 -0
- package/skills/brief-intake/templates/brief-design-doc.md +48 -0
- package/skills/brief-intake/templates/brief-exec-summary.md +47 -0
- package/skills/brief-intake/templates/brief-generic.md +45 -0
- package/skills/brief-intake/templates/brief-runbook.md +44 -0
- package/skills/brief-intake/templates/brief-tech-comparison.md +38 -0
- package/skills/bug-triage/SKILL.md +67 -0
- package/skills/bug-triage/manifest.json +11 -0
- package/skills/bug-triage/templates/bug-ticket.md +86 -0
- package/skills/business-case/SKILL.md +64 -0
- package/skills/business-case/manifest.json +11 -0
- package/skills/business-case/templates/business-case.md +60 -0
- package/skills/capacity-cost-model/SKILL.md +64 -0
- package/skills/capacity-cost-model/manifest.json +11 -0
- package/skills/capacity-cost-model/reference.md +54 -0
- package/skills/capacity-cost-model/templates/cost-model.md +68 -0
- package/skills/council-review/SKILL.md +106 -0
- package/skills/council-review/examples/redis-cache-decision.md +75 -0
- package/skills/council-review/manifest.json +11 -0
- package/skills/council-review/reference.md +76 -0
- package/skills/council-review/seats/architect.md +29 -0
- package/skills/council-review/seats/bench.md +39 -0
- package/skills/council-review/seats/operator.md +27 -0
- package/skills/council-review/seats/pragmatist.md +29 -0
- package/skills/council-review/seats/security-engineer.md +29 -0
- package/skills/council-review/seats/user-advocate.md +27 -0
- package/skills/council-review/templates/council-review.md +59 -0
- package/skills/data-contract/SKILL.md +67 -0
- package/skills/data-contract/manifest.json +11 -0
- package/skills/data-contract/reference.md +46 -0
- package/skills/data-contract/templates/data-contract.md +69 -0
- package/skills/db-schema-design/SKILL.md +68 -0
- package/skills/db-schema-design/manifest.json +11 -0
- package/skills/db-schema-design/reference.md +56 -0
- package/skills/db-schema-design/templates/schema-design.md +75 -0
- package/skills/decision-log/SKILL.md +67 -0
- package/skills/decision-log/manifest.json +11 -0
- package/skills/deck-builder/SKILL.md +114 -0
- package/skills/deck-builder/examples/exec-board-update.md +108 -0
- package/skills/deck-builder/manifest.json +17 -0
- package/skills/deck-builder/reference.md +92 -0
- package/skills/deck-builder/requirements.txt +1 -0
- package/skills/deck-builder/scripts/build_deck.py +309 -0
- package/skills/deck-builder/templates/deck-spec.json +97 -0
- package/skills/deck-builder/templates/palettes.json +59 -0
- package/skills/design-doc/SKILL.md +49 -0
- package/skills/design-doc/manifest.json +11 -0
- package/skills/design-doc/templates/design-doc.md +98 -0
- package/skills/devils-advocate/SKILL.md +142 -0
- package/skills/devils-advocate/lenses/adversarial-review.md +88 -0
- package/skills/devils-advocate/lenses/edge-cases.md +95 -0
- package/skills/devils-advocate/lenses/future-proofing.md +75 -0
- package/skills/devils-advocate/lenses/test-coverage.md +90 -0
- package/skills/devils-advocate/manifest.json +11 -0
- package/skills/devils-advocate/templates/challenge.md +118 -0
- package/skills/doc-critique/SKILL.md +119 -0
- package/skills/doc-critique/manifest.json +11 -0
- package/skills/doc-critique/rubrics/adr.md +36 -0
- package/skills/doc-critique/rubrics/decision-log.md +39 -0
- package/skills/doc-critique/rubrics/deck.md +45 -0
- package/skills/doc-critique/rubrics/design-doc.md +42 -0
- package/skills/doc-critique/rubrics/exec-summary.md +41 -0
- package/skills/doc-critique/rubrics/generic.md +40 -0
- package/skills/doc-critique/rubrics/runbook.md +42 -0
- package/skills/doc-critique/rubrics/tech-comparison.md +40 -0
- package/skills/doc-critique/templates/critique.md +49 -0
- package/skills/exec-summary/SKILL.md +45 -0
- package/skills/exec-summary/manifest.json +11 -0
- package/skills/exec-summary/templates/exec-summary.md +48 -0
- package/skills/feature-implement-loop/SKILL.md +83 -0
- package/skills/feature-implement-loop/manifest.json +11 -0
- package/skills/figma-diagrams/SKILL.md +85 -0
- package/skills/figma-diagrams/manifest.json +35 -0
- package/skills/figma-diagrams/reference.md +91 -0
- package/skills/figma-diagrams/requirements.txt +1 -0
- package/skills/figma-diagrams/scripts/_figma_client.py +71 -0
- package/skills/figma-diagrams/scripts/frame_to_mermaid.py +102 -0
- package/skills/figma-diagrams/scripts/inspect_file.py +46 -0
- package/skills/figma-diagrams/scripts/list_comments.py +35 -0
- package/skills/figma-diagrams/scripts/post_comment.py +52 -0
- package/skills/figma-diagrams/templates/figjam-import.json +20 -0
- package/skills/guide-builder/SKILL.md +76 -0
- package/skills/guide-builder/examples/local-setup-guide.md +127 -0
- package/skills/guide-builder/manifest.json +11 -0
- package/skills/guide-builder/templates/api-reference.md +120 -0
- package/skills/guide-builder/templates/design-walkthrough.md +72 -0
- package/skills/guide-builder/templates/setup-guide.md +109 -0
- package/skills/incident-comms/SKILL.md +68 -0
- package/skills/incident-comms/manifest.json +11 -0
- package/skills/incident-comms/reference.md +43 -0
- package/skills/incident-comms/templates/messages.md +82 -0
- package/skills/llm-eval-harness/SKILL.md +73 -0
- package/skills/llm-eval-harness/manifest.json +11 -0
- package/skills/llm-eval-harness/reference.md +58 -0
- package/skills/llm-eval-harness/templates/eval-plan.md +63 -0
- package/skills/llm-eval-harness/templates/golden-case.jsonl +5 -0
- package/skills/migration-plan/SKILL.md +68 -0
- package/skills/migration-plan/manifest.json +11 -0
- package/skills/migration-plan/templates/migration-plan.md +68 -0
- package/skills/nfr-spec/SKILL.md +63 -0
- package/skills/nfr-spec/manifest.json +11 -0
- package/skills/nfr-spec/reference.md +74 -0
- package/skills/nfr-spec/templates/nfr-spec.md +56 -0
- package/skills/observability-plan/SKILL.md +70 -0
- package/skills/observability-plan/manifest.json +11 -0
- package/skills/observability-plan/reference.md +67 -0
- package/skills/observability-plan/templates/observability-plan.md +62 -0
- package/skills/okr-cascade/SKILL.md +59 -0
- package/skills/okr-cascade/evals/eval_queries.json +12 -0
- package/skills/okr-cascade/evals/evals.json +28 -0
- package/skills/okr-cascade/manifest.json +11 -0
- package/skills/okr-cascade/templates/okr-cascade.md +46 -0
- package/skills/postmortem-generator/SKILL.md +65 -0
- package/skills/postmortem-generator/manifest.json +11 -0
- package/skills/postmortem-generator/templates/postmortem.md +67 -0
- package/skills/prd-draft/SKILL.md +62 -0
- package/skills/prd-draft/manifest.json +11 -0
- package/skills/prd-draft/templates/prd.md +66 -0
- package/skills/prfaq/SKILL.md +75 -0
- package/skills/prfaq/evals/eval_queries.json +13 -0
- package/skills/prfaq/evals/evals.json +32 -0
- package/skills/prfaq/manifest.json +11 -0
- package/skills/prfaq/templates/prfaq.md +60 -0
- package/skills/release-notes/SKILL.md +61 -0
- package/skills/release-notes/manifest.json +11 -0
- package/skills/release-notes/templates/release-notes.md +83 -0
- package/skills/requirements-interview/SKILL.md +61 -0
- package/skills/requirements-interview/manifest.json +11 -0
- package/skills/requirements-interview/templates/interview-kit.md +63 -0
- package/skills/reverse-architecture/SKILL.md +103 -0
- package/skills/reverse-architecture/manifest.json +11 -0
- package/skills/reverse-architecture/reference.md +150 -0
- package/skills/reverse-architecture/templates/description.md +52 -0
- package/skills/reverse-architecture/templates/extraction.md +55 -0
- package/skills/runbook-generator/SKILL.md +42 -0
- package/skills/runbook-generator/manifest.json +11 -0
- package/skills/runbook-generator/templates/runbook.md +109 -0
- package/skills/slide-outliner/SKILL.md +58 -0
- package/skills/slide-outliner/manifest.json +11 -0
- package/skills/slide-outliner/templates/deck-outline.md +131 -0
- package/skills/sonar-onboard/SKILL.md +170 -0
- package/skills/sonar-onboard/manifest.json +11 -0
- package/skills/sonar-onboard/reference.md +151 -0
- package/skills/sonar-onboard/templates/github-actions-sonar-server.yml.tmpl +50 -0
- package/skills/sonar-onboard/templates/github-actions-sonarcloud.yml.tmpl +53 -0
- package/skills/sonar-onboard/templates/readme-snippet.md +38 -0
- package/skills/sonar-onboard/templates/sonar-project.properties.tmpl +31 -0
- package/skills/sonar-review/SKILL.md +220 -0
- package/skills/sonar-review/lenses/bugs.md +48 -0
- package/skills/sonar-review/lenses/code-smells.md +64 -0
- package/skills/sonar-review/lenses/coverage-and-duplication.md +74 -0
- package/skills/sonar-review/lenses/security-hotspots.md +56 -0
- package/skills/sonar-review/lenses/vulnerabilities.md +52 -0
- package/skills/sonar-review/manifest.json +37 -0
- package/skills/sonar-review/reference.md +167 -0
- package/skills/sonar-review/templates/report.md +84 -0
- package/skills/strategy-analysis/SKILL.md +63 -0
- package/skills/strategy-analysis/evals/eval_queries.json +13 -0
- package/skills/strategy-analysis/evals/evals.json +36 -0
- package/skills/strategy-analysis/manifest.json +11 -0
- package/skills/success-metrics/SKILL.md +63 -0
- package/skills/success-metrics/evals/eval_queries.json +11 -0
- package/skills/success-metrics/evals/evals.json +27 -0
- package/skills/success-metrics/manifest.json +11 -0
- package/skills/success-metrics/templates/metrics-plan.md +55 -0
- package/skills/tech-comparison-matrix/SKILL.md +56 -0
- package/skills/tech-comparison-matrix/manifest.json +11 -0
- package/skills/tech-comparison-matrix/templates/matrix.md +45 -0
- package/skills/test-plan-generator/SKILL.md +59 -0
- package/skills/test-plan-generator/manifest.json +11 -0
- package/skills/test-plan-generator/templates/test-plan.md +83 -0
- package/skills/threat-model/SKILL.md +65 -0
- package/skills/threat-model/examples/document-upload.md +56 -0
- package/skills/threat-model/manifest.json +11 -0
- package/skills/threat-model/reference.md +69 -0
- package/skills/user-journey-map/SKILL.md +64 -0
- package/skills/user-journey-map/evals/eval_queries.json +12 -0
- package/skills/user-journey-map/evals/evals.json +30 -0
- package/skills/user-journey-map/examples/b2b-saas-onboarding.md +69 -0
- package/skills/user-journey-map/manifest.json +11 -0
- package/skills/user-journey-map/templates/journey-map.md +47 -0
- package/skills/user-story-splitter/SKILL.md +88 -0
- package/skills/user-story-splitter/examples/notifications-epic.md +123 -0
- package/skills/user-story-splitter/manifest.json +11 -0
- package/skills/user-story-splitter/templates/story.md +40 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: exec-summary
|
|
3
|
+
description: Compress a long technical document, design doc, or proposal into a one-page executive summary aimed at non-technical or time-poor stakeholders. Use when the user has a long doc and needs a 30-second readable version, or is writing a 1-pager for a steering committee, partner, or executive sponsor.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# exec-summary
|
|
7
|
+
|
|
8
|
+
You help the user turn dense technical material into a one-page brief that a VP can read between meetings and act on.
|
|
9
|
+
|
|
10
|
+
## How to respond
|
|
11
|
+
|
|
12
|
+
1. **Get the source.** Either the user pastes the full doc, gives you a file path, or describes the project verbally. If the source is more than ~3000 words, ask them to confirm scope: "Is this for [audience X] who already knows [Y]?"
|
|
13
|
+
|
|
14
|
+
2. **Identify the audience.** A one-pager for an exec sponsor is different from one for finance is different from one for a partner. Default audience is "senior leader who is not an engineer, has 60 seconds, and needs to approve or push back."
|
|
15
|
+
|
|
16
|
+
3. **Use [`templates/exec-summary.md`](templates/exec-summary.md)** — six fixed sections:
|
|
17
|
+
1. **The Ask** (one line — what decision/approval are we requesting?)
|
|
18
|
+
2. **TL;DR** (three bullets — what's the project, why now, what's the recommendation)
|
|
19
|
+
3. **Business impact** (revenue / cost / risk / time — quantified where possible)
|
|
20
|
+
4. **Cost & timeline** (rough numbers — say "rough" if they are)
|
|
21
|
+
5. **Risks & how we're managing them** (max 3 — pick the ones the exec would ask about)
|
|
22
|
+
6. **What we need from you** (concrete asks — money, headcount, an exec decision, a connection)
|
|
23
|
+
|
|
24
|
+
4. **Compress ruthlessly.** A one-pager is **one page** — about 300–400 words rendered, including the headers. Cut:
|
|
25
|
+
- Adjectives ("robust", "scalable", "next-generation")
|
|
26
|
+
- Hedging ("we believe", "in many cases")
|
|
27
|
+
- Technical jargon (replace or define inline once)
|
|
28
|
+
- History the audience already knows
|
|
29
|
+
|
|
30
|
+
5. **Lead with the conclusion.** Execs read top-to-bottom and stop when they have what they need. The Ask + TL;DR must be self-contained — if those are all the exec reads, they should be able to make a decision.
|
|
31
|
+
|
|
32
|
+
## Quality bar
|
|
33
|
+
|
|
34
|
+
- **Quantify or qualify.** Numbers are best ("\$2.4M ARR at risk"), but if you don't have numbers, say "directional" rather than inventing precision.
|
|
35
|
+
- **Plain language.** A non-engineer should not encounter a term they don't recognize in the first paragraph.
|
|
36
|
+
- **No internal-team-speak.** "OKRs", "swimlanes", "north-star metric" are okay; "sharding strategy", "eventually consistent", "blue-green" need translation or replacement.
|
|
37
|
+
- **Active voice, second person where natural.** "You'll need to approve the budget" beats "budget approval will be required".
|
|
38
|
+
|
|
39
|
+
## Anti-patterns to avoid
|
|
40
|
+
|
|
41
|
+
- ❌ Burying the recommendation on page 2 of a 1-pager.
|
|
42
|
+
- ❌ Listing technical wins ("reduced p99 by 40ms") with no business translation.
|
|
43
|
+
- ❌ Risk sections full of generic risks ("scope creep", "team turnover"). Pick the *project-specific* ones.
|
|
44
|
+
- ❌ "Next steps: align with stakeholders." Empty calorie. Replace with the actual action.
|
|
45
|
+
- ❌ Asking the exec to "review the full design doc for details" without saying which sections matter.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "exec-summary",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Compress a long technical document, design doc, or proposal into a one-page executive summary aimed at non-technical or time-poor stakeholders. Use when the user has a long doc and needs a 30-second readable version, or is writing a 1-pager for a steering committee, partner, or executive sponsor.",
|
|
5
|
+
"entrypoint": "SKILL.md",
|
|
6
|
+
"deps": { "npm": [], "pip": [] },
|
|
7
|
+
"env": { "required": [], "optional": [] },
|
|
8
|
+
"related": [],
|
|
9
|
+
"tags": ["executive", "summary", "stakeholder-communication", "one-pager"],
|
|
10
|
+
"model": { "tier": "standard", "rationale": "One-page compression. Needs to keep the right signal; escalates on very long source docs." }
|
|
11
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# {Project / initiative name}
|
|
2
|
+
|
|
3
|
+
**To:** {Audience — e.g. "CFO + Steering Committee"}
|
|
4
|
+
**From:** {Author / team}
|
|
5
|
+
**Date:** {YYYY-MM-DD}
|
|
6
|
+
**Status:** {For decision / FYI / For approval}
|
|
7
|
+
|
|
8
|
+
## The Ask
|
|
9
|
+
|
|
10
|
+
{One sentence — the specific decision, approval, or input you need from this audience. If there's no Ask, this is a status update, not an exec summary.}
|
|
11
|
+
|
|
12
|
+
## TL;DR
|
|
13
|
+
|
|
14
|
+
- **What:** {one bullet — what this project is, in plain language}
|
|
15
|
+
- **Why now:** {one bullet — the forcing function}
|
|
16
|
+
- **Recommendation:** {one bullet — what we're recommending the audience approve / change / do}
|
|
17
|
+
|
|
18
|
+
## Business impact
|
|
19
|
+
|
|
20
|
+
- **Revenue / cost:** {quantified where possible — "$X savings per year" or "$Y revenue protected"}
|
|
21
|
+
- **Risk:** {what risk is mitigated, or what risk is incurred}
|
|
22
|
+
- **Time:** {when the business sees impact}
|
|
23
|
+
|
|
24
|
+
## Cost & timeline
|
|
25
|
+
|
|
26
|
+
| What | Estimate | Confidence |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| Engineering effort | {N person-months} | {rough / directional / firm} |
|
|
29
|
+
| Vendor / infra cost | {$X over N months} | {…} |
|
|
30
|
+
| Calendar timeline | {N months end-to-end} | {…} |
|
|
31
|
+
|
|
32
|
+
## Top risks & mitigations
|
|
33
|
+
|
|
34
|
+
| Risk | Mitigation |
|
|
35
|
+
|---|---|
|
|
36
|
+
| {Project-specific risk #1} | {What we're doing about it} |
|
|
37
|
+
| {Risk #2} | {…} |
|
|
38
|
+
| {Risk #3} | {…} |
|
|
39
|
+
|
|
40
|
+
## What we need from you
|
|
41
|
+
|
|
42
|
+
- {Concrete ask — e.g. "Approve $400k FY26 budget by end of Q2"}
|
|
43
|
+
- {Concrete ask — e.g. "Decision: build in-house vs. buy from Vendor X"}
|
|
44
|
+
- {Concrete ask — e.g. "Intro to Acme's VP of Engineering"}
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
*Full design doc: {link} — sections {N, M} are most relevant if you want to dig in.*
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: feature-implement-loop
|
|
3
|
+
description: Implement a feature or story so it ends up *verified*, not just generated. Takes a description + acceptance criteria, writes code and tests, then runs an adversarial review pass that hunts for gaps (broken edge cases, baked-in assumptions, uncovered acceptance criteria), and re-generates to close them — looping until the review is clean or a 3-round cap is hit. Use when handed a ticket / story / feature spec with acceptance criteria and the goal is shippable, self-checked code — not a first draft.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# feature-implement-loop
|
|
7
|
+
|
|
8
|
+
You drive a feature from spec to *verified* implementation through a self-correcting loop. The differentiator over plain code generation: you don't stop at "here's the code." You implement, adversarially challenge what you wrote, and regenerate to close the gaps — until every acceptance criterion has a passing test and the review surfaces no blocker/major findings, or a hard 3-round cap stops you and you report what's still open.
|
|
9
|
+
|
|
10
|
+
This is the **dev-workflow loop** counterpart to the artifact pipeline. It composes the [`devils-advocate`](../devils-advocate/SKILL.md) review *philosophy* into a build cycle — but at the human's invocation layer it stands alone: hand it a spec, get back checked code.
|
|
11
|
+
|
|
12
|
+
## Input
|
|
13
|
+
|
|
14
|
+
A feature/story with two parts:
|
|
15
|
+
- **Description** — what to build and why.
|
|
16
|
+
- **Acceptance criteria** — the conditions that define "done." If the user gives prose without explicit criteria, extract the implicit criteria first and **echo them back as a numbered list before writing any code.** No criteria, no loop — the criteria are the gate.
|
|
17
|
+
|
|
18
|
+
If acceptance criteria are missing and can't be inferred, ask for them (one question). Don't invent a gate the user didn't agree to.
|
|
19
|
+
|
|
20
|
+
## How to respond
|
|
21
|
+
|
|
22
|
+
1. **Restate the spec as a criteria checklist.** Number every acceptance criterion. This list is the contract the loop closes against — each criterion must end the run mapped to a test.
|
|
23
|
+
|
|
24
|
+
2. **Plan the verification first.** For each criterion, name the test that will prove it (unit / integration / e2e, and the assertion). Surface criteria that *can't* be tested automatically (e.g. "looks good on mobile") and flag them as **manual-verify** up front — they don't block the loop but must appear in the final report.
|
|
25
|
+
|
|
26
|
+
3. **Generate code + tests together.** Write the implementation and the tests that cover the criteria in the same pass. Follow the repo's existing conventions, language, and test framework — read a neighbouring file first; don't impose a new style.
|
|
27
|
+
|
|
28
|
+
4. **Run the adversarial review pass.** This is the heart of the loop. Challenge what you just wrote:
|
|
29
|
+
- **In Claude Code (or any tool with subagents):** delegate to the `devils-advocate` subagent on the generated diff.
|
|
30
|
+
- **Everywhere else (Codex, Cursor, Aider, …):** run the review inline, sweeping the four lenses yourself:
|
|
31
|
+
1. **Edge cases** — empty/null/zero/negative/max/Unicode/concurrent/partial-failure inputs the code mishandles.
|
|
32
|
+
2. **Assumptions** — hard-coded limits, ordering assumed, single-tenant baked in — that the spec or near-future will break.
|
|
33
|
+
3. **Staff-engineer pushback** — concurrency, error handling, security (authz/injection/secrets), observability, blast radius, idempotency.
|
|
34
|
+
4. **Acceptance-criteria coverage** — for each numbered criterion: is there a test, and does it actually assert the criterion (not just exercise the code)?
|
|
35
|
+
- Tag findings 🟥 blocker · 🟧 major · 🟨 minor · ⚪ nit, each with `file:line` and a concrete fix or the missing test case.
|
|
36
|
+
|
|
37
|
+
5. **Apply the gate.** Compute whether gaps remain:
|
|
38
|
+
- **Gap = any 🟥 blocker, OR any 🟧 major, OR any acceptance criterion without a passing, asserting test.**
|
|
39
|
+
- 🟨 minor / ⚪ nit findings do **not** force another round — list them, don't loop on them.
|
|
40
|
+
|
|
41
|
+
6. **Loop or stop.**
|
|
42
|
+
- **Gaps remain and rounds used < 3** → regenerate to close *only* the gap findings (don't churn unrelated code), then go back to step 4. Increment the round counter.
|
|
43
|
+
- **No gaps** → stop: the implementation is verified. Go to the report.
|
|
44
|
+
- **Gaps remain and rounds == 3** → stop anyway. Do **not** loop further. Report the remaining gaps explicitly as open — never silently declare done.
|
|
45
|
+
|
|
46
|
+
7. **Report.** Always end with:
|
|
47
|
+
- **Status:** `VERIFIED` (clean) · `VERIFIED WITH OPEN ITEMS` (cap hit, gaps remain) · `BLOCKED` (couldn't generate / couldn't run tests).
|
|
48
|
+
- **Acceptance-criteria table:** every criterion → ✅ covered (test name) · ⚠️ manual-verify · ❌ open (why).
|
|
49
|
+
- **Round log:** what each round found and fixed (1 → 2 → 3). This is the proof of work — it shows the loop did something.
|
|
50
|
+
- **Open items:** any remaining gaps after the cap, with the finding and why it wasn't auto-closable, so a human can finish it.
|
|
51
|
+
|
|
52
|
+
## Quality bar
|
|
53
|
+
|
|
54
|
+
- **Every acceptance criterion maps to a test by the final report.** A criterion with no asserting test is an ❌ open item — not a pass. This is the rule that makes the skill more than codegen.
|
|
55
|
+
- **The review challenges the code; it doesn't rubber-stamp it.** A round-1 review that finds nothing on a non-trivial feature is a smell — re-run it adversarially, assuming the first pass missed something (it usually did).
|
|
56
|
+
- **Tests assert behavior, not just execution.** ❌ a test that calls the function and checks it didn't throw. ✅ a test that asserts the criterion's expected output. Catch assertion theater in the review.
|
|
57
|
+
- **Regeneration is targeted.** Each round fixes the named gaps and leaves passing code alone. Don't rewrite the whole feature every round — that loses ground and never converges.
|
|
58
|
+
- **The cap is real.** 3 rounds, then stop and report. A loop that "just one more round"s past 3 is a bug. If it can't converge in 3, a human needs to see why.
|
|
59
|
+
- **The round log is honest.** If round 2 found nothing new, say so. If a gap was downgraded rather than fixed, say why. The log is the audit trail.
|
|
60
|
+
|
|
61
|
+
## When to use this skill
|
|
62
|
+
|
|
63
|
+
- ✅ A ticket / story / feature spec arrives **with acceptance criteria** and the goal is shippable, self-checked code.
|
|
64
|
+
- ✅ When the user says "implement this and make sure it actually meets the criteria" / "build it and check your own work."
|
|
65
|
+
- ✅ Net-new features and well-scoped enhancements where the criteria are concrete.
|
|
66
|
+
|
|
67
|
+
## When NOT to use this skill
|
|
68
|
+
|
|
69
|
+
- ❌ One-line fixes / trivial changes — the loop is overhead; just make the change.
|
|
70
|
+
- ❌ Greenfield spikes / throwaway prototypes — verifying code you'll delete wastes rounds.
|
|
71
|
+
- ❌ Specs with no acceptance criteria and none inferable — there's no gate to close. Get criteria first.
|
|
72
|
+
- ❌ Pure code *review* of code you didn't generate — that's [`devils-advocate`](../devils-advocate/SKILL.md) standalone.
|
|
73
|
+
- ❌ Open-ended design questions ("should we use Postgres or Dynamo?") — that's `design-doc` + `doc-critique`.
|
|
74
|
+
|
|
75
|
+
## Anti-patterns to avoid
|
|
76
|
+
|
|
77
|
+
- ❌ **Declaring "done" with an uncovered criterion.** The whole point is the criteria gate. A criterion without an asserting test is open, full stop.
|
|
78
|
+
- ❌ **A toothless review round.** Delegating to devils-advocate or sweeping the lenses, then reporting "no issues" on a real feature — the loop only earns its keep if the review actually adversarial.
|
|
79
|
+
- ❌ **Looping past 3 rounds.** Convergence isn't guaranteed; the cap exists so a human gets pulled in instead of the agent thrashing.
|
|
80
|
+
- ❌ **Wholesale regeneration each round.** Rewriting passing code to fix one gap loses verified ground. Patch the gap.
|
|
81
|
+
- ❌ **Calling the `devils-advocate` *skill* from here.** Skills don't invoke skills (see CLAUDE.md). Drive the *subagent* where one exists, or inline the four lenses. Same applies to any other skill.
|
|
82
|
+
- ❌ **Assertion theater.** Tests that run the code but assert nothing meaningful pass the round and hide the gap. The review must reject them.
|
|
83
|
+
- ❌ **Silent manual-verify criteria.** Criteria that can't be auto-tested must be surfaced as ⚠️ manual-verify in the report, not quietly skipped.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "feature-implement-loop",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Implement a feature or story so it ends up *verified*, not just generated. Takes a description + acceptance criteria, writes code and tests, then runs an adversarial review pass that hunts for gaps (broken edge cases, baked-in assumptions, uncovered acceptance criteria), and re-generates to close them — looping until the review is clean or a 3-round cap is hit. Use when handed a ticket / story / feature spec with acceptance criteria and the goal is shippable, self-checked code — not a first draft.",
|
|
5
|
+
"entrypoint": "SKILL.md",
|
|
6
|
+
"deps": { "npm": [], "pip": [] },
|
|
7
|
+
"env": { "required": [], "optional": [] },
|
|
8
|
+
"related": ["design-doc", "devils-advocate", "doc-critique"],
|
|
9
|
+
"tags": ["dev-workflow", "implementation", "tdd", "acceptance-criteria", "adversarial", "self-correcting", "code-generation"],
|
|
10
|
+
"model": { "tier": "standard", "rationale": "Code + test generation against acceptance criteria. The adversarial pass delegates to devils-advocate (heavy) where available; the loop itself is generation." }
|
|
11
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: figma-diagrams
|
|
3
|
+
description: Work with Figma and FigJam files via the Figma REST API — read existing file structure, list frames/pages, post comments, and produce FigJam-importable diagram specs. Use when the user mentions a Figma URL, asks to inspect/audit a Figma file, wants to comment on a design programmatically, or wants their architecture diagram in Figma.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# figma-diagrams
|
|
7
|
+
|
|
8
|
+
You help the user **interact with Figma files programmatically** — most often to bridge an architecture diagram from text/Mermaid into a Figma design surface their team is already reviewing in.
|
|
9
|
+
|
|
10
|
+
## Important: what's possible, what isn't
|
|
11
|
+
|
|
12
|
+
The Figma REST API is **mostly read-only for file contents**. Be honest with the user about this:
|
|
13
|
+
|
|
14
|
+
| Task | How |
|
|
15
|
+
|---|---|
|
|
16
|
+
| Read a Figma file's pages, frames, components | ✅ REST API — `GET /v1/files/:file_key` |
|
|
17
|
+
| List images / thumbnails of frames | ✅ REST API — `GET /v1/images/:file_key` |
|
|
18
|
+
| Post a comment on a file or pin to a region | ✅ REST API — `POST /v1/files/:file_key/comments` |
|
|
19
|
+
| Read team / project / variables | ✅ REST API |
|
|
20
|
+
| **Create / mutate nodes** in a design file | ❌ Not via REST. Requires the **Figma Plugin API** (runs inside Figma desktop/web) |
|
|
21
|
+
| Push a diagram into FigJam | ⚠️ Indirect — generate FigJam-importable JSON or have the user paste Mermaid via the "Mermaid → FigJam" community plugin |
|
|
22
|
+
|
|
23
|
+
When the user asks you to "draw this in Figma directly," set expectations up front and offer the indirect paths (Mermaid plugin, FigJam JSON spec, or plugin code that they can paste into a Figma plugin).
|
|
24
|
+
|
|
25
|
+
## Prerequisites
|
|
26
|
+
|
|
27
|
+
The user needs a Figma Personal Access Token, exported as `FIGMA_TOKEN`:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
export FIGMA_TOKEN="figd_..."
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
How to get one: Figma → Settings → Security → Personal access tokens → Generate. Scope it minimally (file content read, comments write) if Figma offers granular scopes.
|
|
34
|
+
|
|
35
|
+
If `FIGMA_TOKEN` is not set, tell the user how to set it before running the scripts.
|
|
36
|
+
|
|
37
|
+
## How to respond
|
|
38
|
+
|
|
39
|
+
1. **Detect the URL.** Figma URLs look like `https://www.figma.com/file/<FILE_KEY>/<name>` or `https://www.figma.com/design/<FILE_KEY>/<name>`. Extract `<FILE_KEY>` — that's what the API takes.
|
|
40
|
+
|
|
41
|
+
2. **Pick the right script.** Helper scripts live in `scripts/` next to this `SKILL.md`:
|
|
42
|
+
|
|
43
|
+
- `inspect_file.py <FILE_KEY>` — prints a tree of pages → frames → top-level groups
|
|
44
|
+
- `list_comments.py <FILE_KEY>` — lists existing comments with author + timestamp
|
|
45
|
+
- `post_comment.py <FILE_KEY> "<message>"` — adds a comment at the file root (or `--node-id N` to pin to a node)
|
|
46
|
+
- `frame_to_mermaid.py <FILE_KEY> <NODE_ID>` — exports a frame's structure as a first-pass Mermaid diagram (best-effort)
|
|
47
|
+
|
|
48
|
+
The path you use depends on where this skill is installed:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Claude Code exposes the skill's folder as $CLAUDE_SKILL_DIR
|
|
52
|
+
python3 "${CLAUDE_SKILL_DIR}/scripts/inspect_file.py" <FILE_KEY>
|
|
53
|
+
|
|
54
|
+
# In any other IDE (Cursor, Kiro, Continue, …) cd into the copied skill folder first
|
|
55
|
+
cd path/to/figma-diagrams && python3 scripts/inspect_file.py <FILE_KEY>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Dependencies (just `requests`) are declared in `manifest.json` and `requirements.txt`. Install once with:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
python3 -m pip install -r requirements.txt
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
3. **For "I want my Mermaid diagram in Figma":** the cleanest path is the [Mermaid → Figma/FigJam community plugin](https://www.figma.com/community/plugin/1222021271365394457). Generate the Mermaid first (via the [`architecture-diagrams`](../architecture-diagrams/SKILL.md) skill), then guide the user to paste it. Don't pretend to push it via REST.
|
|
65
|
+
|
|
66
|
+
4. **For team audits** (e.g., "what's in our design system file?"), run `inspect_file.py` and summarize. The full JSON dump is huge — only show the user the structure that answers their question.
|
|
67
|
+
|
|
68
|
+
## Useful references
|
|
69
|
+
|
|
70
|
+
- [`reference.md`](reference.md) — Figma REST API endpoints, auth, rate limits, common gotchas
|
|
71
|
+
- [`templates/figjam-import.json`](templates/figjam-import.json) — example shape of a JSON spec that a FigJam plugin could consume to materialize a diagram
|
|
72
|
+
- [`manifest.json`](manifest.json) — declared dependencies and required env vars (read by some IDEs at install time)
|
|
73
|
+
|
|
74
|
+
## Quality bar
|
|
75
|
+
|
|
76
|
+
- **Never log the token.** Scripts use `os.environ["FIGMA_TOKEN"]`; don't pass it on the command line.
|
|
77
|
+
- **Don't dump 5 MB of JSON** to the user. Filter to pages/frames/groups (3 levels) and summarize.
|
|
78
|
+
- **Confirm before posting comments.** A comment is visible to anyone with file access — show the user the exact text and the file URL before calling `post_comment.py`.
|
|
79
|
+
- **Handle 429s.** The Figma API rate-limits (~25 req/min for some endpoints). If a script returns 429, back off and retry; don't loop.
|
|
80
|
+
|
|
81
|
+
## Anti-patterns to avoid
|
|
82
|
+
|
|
83
|
+
- ❌ Claiming the script "drew" something in the user's Figma file. It didn't (REST API can't).
|
|
84
|
+
- ❌ Asking the user to paste their token into chat. They should `export FIGMA_TOKEN=…` in their own shell.
|
|
85
|
+
- ❌ Posting test comments to an unfamiliar file without explicit confirmation.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "figma-diagrams",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Work with Figma and FigJam files via the Figma REST API — read existing file structure, list frames/pages, post comments, and produce FigJam-importable diagram specs. Use when the user mentions a Figma URL, asks to inspect/audit a Figma file, wants to comment on a design programmatically, or wants their architecture diagram in Figma.",
|
|
5
|
+
"entrypoint": "SKILL.md",
|
|
6
|
+
"deps": {
|
|
7
|
+
"npm": [],
|
|
8
|
+
"pip": [
|
|
9
|
+
"requests>=2.28"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"env": {
|
|
13
|
+
"required": [
|
|
14
|
+
"FIGMA_TOKEN"
|
|
15
|
+
],
|
|
16
|
+
"optional": []
|
|
17
|
+
},
|
|
18
|
+
"related": [
|
|
19
|
+
"architecture-diagrams"
|
|
20
|
+
],
|
|
21
|
+
"install": {
|
|
22
|
+
"pip": "python3 -m pip install -r requirements.txt"
|
|
23
|
+
},
|
|
24
|
+
"tags": [
|
|
25
|
+
"figma",
|
|
26
|
+
"figjam",
|
|
27
|
+
"diagrams",
|
|
28
|
+
"design",
|
|
29
|
+
"api"
|
|
30
|
+
],
|
|
31
|
+
"model": {
|
|
32
|
+
"tier": "standard",
|
|
33
|
+
"rationale": "API calls + spec generation. Bounded transformation."
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Figma REST API — quick reference
|
|
2
|
+
|
|
3
|
+
Official docs: https://www.figma.com/developers/api
|
|
4
|
+
|
|
5
|
+
## Auth
|
|
6
|
+
|
|
7
|
+
Every request needs the `X-Figma-Token` header:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
X-Figma-Token: figd_...
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Tokens are scoped to the user who created them — they can do anything that user can do in the UI.
|
|
14
|
+
|
|
15
|
+
## Endpoints used by this skill
|
|
16
|
+
|
|
17
|
+
| Verb | Path | Purpose |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `GET` | `/v1/files/:file_key` | Full file JSON: document tree (pages, frames, all nodes). Large. |
|
|
20
|
+
| `GET` | `/v1/files/:file_key/nodes?ids=A,B` | Same shape as above but scoped to specific node IDs — cheaper. |
|
|
21
|
+
| `GET` | `/v1/files/:file_key/comments` | List all comments. |
|
|
22
|
+
| `POST` | `/v1/files/:file_key/comments` | Add a comment. Body: `{"message": "...", "client_meta": {...}}` |
|
|
23
|
+
| `GET` | `/v1/images/:file_key?ids=A,B&format=svg` | Render frames as PNG/SVG/PDF/JPG. Returns S3 URLs (expire ~30 days). |
|
|
24
|
+
| `GET` | `/v1/me` | Sanity-check the token. |
|
|
25
|
+
| `GET` | `/v1/teams/:team_id/projects` | List projects in a team (needs team-scope token). |
|
|
26
|
+
| `GET` | `/v1/projects/:project_id/files` | List files in a project. |
|
|
27
|
+
|
|
28
|
+
## File key extraction
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
https://www.figma.com/file/AbC123XyZ/My-Architecture
|
|
32
|
+
^^^^^^^^^^ <-- FILE_KEY
|
|
33
|
+
|
|
34
|
+
https://www.figma.com/design/AbC123XyZ/My-Architecture (newer URL format)
|
|
35
|
+
^^^^^^^^^^
|
|
36
|
+
|
|
37
|
+
https://www.figma.com/board/AbC123XyZ/My-Whiteboard (FigJam)
|
|
38
|
+
^^^^^^^^^^
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Regex that works on all three: `figma\.com/(?:file|design|board)/([A-Za-z0-9]+)/`
|
|
42
|
+
|
|
43
|
+
## Node IDs
|
|
44
|
+
|
|
45
|
+
Nodes have IDs like `1:23` or `1-23` (older URL format used `-`, JSON uses `:`). Both refer to the same node — normalize to `:` when calling the API.
|
|
46
|
+
|
|
47
|
+
## Rate limits
|
|
48
|
+
|
|
49
|
+
Roughly **~25 requests/minute per token** for read-heavy endpoints; image renders are slower. The API returns `429 Too Many Requests` with `Retry-After`. Back off, don't hammer.
|
|
50
|
+
|
|
51
|
+
## Common gotchas
|
|
52
|
+
|
|
53
|
+
- **`GET /v1/files/:key` is huge** — even small design files can be many MB. Prefer `/nodes?ids=...` if you know the node you want.
|
|
54
|
+
- **Image rendering is async on the server.** The endpoint returns synchronously but Figma may take a few seconds for large frames; image URLs expire (~30 days).
|
|
55
|
+
- **Comments accept lightweight markdown** in the `message` field, but no rich formatting beyond newlines.
|
|
56
|
+
- **The Plugin API ≠ the REST API.** Anything you find that says "create rectangle / set fill" is the Plugin API (TypeScript inside Figma). You can't call it from outside Figma.
|
|
57
|
+
- **FigJam files use the same endpoints** but the node shapes are different (`STICKY`, `SHAPE_WITH_TEXT`, `CONNECTOR`).
|
|
58
|
+
|
|
59
|
+
## Document shape (skeleton)
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"document": {
|
|
64
|
+
"id": "0:0",
|
|
65
|
+
"name": "Document",
|
|
66
|
+
"type": "DOCUMENT",
|
|
67
|
+
"children": [
|
|
68
|
+
{
|
|
69
|
+
"id": "0:1",
|
|
70
|
+
"name": "Page 1",
|
|
71
|
+
"type": "CANVAS",
|
|
72
|
+
"children": [
|
|
73
|
+
{
|
|
74
|
+
"id": "1:2",
|
|
75
|
+
"name": "Architecture",
|
|
76
|
+
"type": "FRAME",
|
|
77
|
+
"children": [...]
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
"components": {...},
|
|
84
|
+
"styles": {...},
|
|
85
|
+
"name": "My file",
|
|
86
|
+
"lastModified": "2026-05-20T...",
|
|
87
|
+
"version": "..."
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Most interesting node types: `FRAME`, `GROUP`, `COMPONENT`, `INSTANCE`, `RECTANGLE`, `TEXT`, `CONNECTOR` (FigJam), `STICKY` (FigJam).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests>=2.28
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Tiny shared client for the Figma REST API.
|
|
2
|
+
|
|
3
|
+
Used by the other scripts in this directory. Not meant to be run directly.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
import sys
|
|
11
|
+
import time
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
import requests
|
|
15
|
+
|
|
16
|
+
API_BASE = "https://api.figma.com"
|
|
17
|
+
FILE_KEY_RE = re.compile(r"figma\.com/(?:file|design|board)/([A-Za-z0-9]+)/")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def token() -> str:
|
|
21
|
+
tok = os.environ.get("FIGMA_TOKEN")
|
|
22
|
+
if not tok:
|
|
23
|
+
sys.stderr.write(
|
|
24
|
+
"error: FIGMA_TOKEN is not set.\n"
|
|
25
|
+
" Get one at: Figma → Settings → Security → Personal access tokens\n"
|
|
26
|
+
" Then: export FIGMA_TOKEN=figd_...\n"
|
|
27
|
+
)
|
|
28
|
+
sys.exit(2)
|
|
29
|
+
return tok
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def extract_file_key(arg: str) -> str:
|
|
33
|
+
"""Accept either a Figma URL or a bare file key."""
|
|
34
|
+
m = FILE_KEY_RE.search(arg)
|
|
35
|
+
if m:
|
|
36
|
+
return m.group(1)
|
|
37
|
+
if re.fullmatch(r"[A-Za-z0-9]+", arg):
|
|
38
|
+
return arg
|
|
39
|
+
sys.stderr.write(f"error: could not extract a Figma file key from {arg!r}\n")
|
|
40
|
+
sys.exit(2)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _request(method: str, path: str, **kwargs: Any) -> requests.Response:
|
|
44
|
+
headers = kwargs.pop("headers", {})
|
|
45
|
+
headers["X-Figma-Token"] = token()
|
|
46
|
+
url = f"{API_BASE}{path}"
|
|
47
|
+
for attempt in range(3):
|
|
48
|
+
resp = requests.request(method, url, headers=headers, timeout=30, **kwargs)
|
|
49
|
+
if resp.status_code == 429:
|
|
50
|
+
retry = int(resp.headers.get("Retry-After", "2"))
|
|
51
|
+
sys.stderr.write(f"rate-limited, sleeping {retry}s\n")
|
|
52
|
+
time.sleep(retry)
|
|
53
|
+
continue
|
|
54
|
+
return resp
|
|
55
|
+
return resp # last response, even if 429
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get(path: str, **kwargs: Any) -> dict[str, Any]:
|
|
59
|
+
resp = _request("GET", path, **kwargs)
|
|
60
|
+
if not resp.ok:
|
|
61
|
+
sys.stderr.write(f"error: GET {path} → {resp.status_code} {resp.text[:200]}\n")
|
|
62
|
+
sys.exit(1)
|
|
63
|
+
return resp.json()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def post(path: str, json: dict[str, Any]) -> dict[str, Any]:
|
|
67
|
+
resp = _request("POST", path, json=json)
|
|
68
|
+
if not resp.ok:
|
|
69
|
+
sys.stderr.write(f"error: POST {path} → {resp.status_code} {resp.text[:200]}\n")
|
|
70
|
+
sys.exit(1)
|
|
71
|
+
return resp.json()
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Best-effort: turn a Figma frame's child structure into a first-pass Mermaid flowchart.
|
|
3
|
+
|
|
4
|
+
This won't perfectly capture visual layout — it walks the node tree, looks at
|
|
5
|
+
text labels and parent/child relationships, and produces a Mermaid `flowchart TB`
|
|
6
|
+
you can hand-edit. Useful as a starting point when reverse-engineering a
|
|
7
|
+
diagram a colleague drew in Figma.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python frame_to_mermaid.py <FILE_KEY_OR_URL> <NODE_ID>
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import re
|
|
16
|
+
import sys
|
|
17
|
+
|
|
18
|
+
from _figma_client import extract_file_key, get
|
|
19
|
+
|
|
20
|
+
SAFE_ID_RE = re.compile(r"[^A-Za-z0-9_]+")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def safe_id(node_id: str) -> str:
|
|
24
|
+
return "n" + SAFE_ID_RE.sub("_", node_id)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def best_label(node: dict) -> str:
|
|
28
|
+
"""Pick a human label for a node — its name, or a TEXT descendant if name is generic."""
|
|
29
|
+
name = (node.get("name") or "").strip()
|
|
30
|
+
if name and not name.lower().startswith(("frame", "group", "rectangle", "ellipse")):
|
|
31
|
+
return name
|
|
32
|
+
for child in node.get("children", []) or []:
|
|
33
|
+
if child.get("type") == "TEXT":
|
|
34
|
+
chars = (child.get("characters") or "").strip()
|
|
35
|
+
if chars:
|
|
36
|
+
return chars
|
|
37
|
+
deeper = best_label(child)
|
|
38
|
+
if deeper and deeper != child.get("name"):
|
|
39
|
+
return deeper
|
|
40
|
+
return name or node.get("type", "node")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def emit(node: dict, parent_id: str | None, lines: list[str], edges: list[str]) -> None:
|
|
44
|
+
type_ = node.get("type")
|
|
45
|
+
if type_ in {"DOCUMENT", "CANVAS"}:
|
|
46
|
+
for c in node.get("children", []) or []:
|
|
47
|
+
emit(c, parent_id, lines, edges)
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
my_id = safe_id(node["id"])
|
|
51
|
+
label = best_label(node).replace('"', "'")[:60]
|
|
52
|
+
|
|
53
|
+
if type_ in {"FRAME", "GROUP", "SECTION"}:
|
|
54
|
+
lines.append(f' subgraph {my_id}["{label}"]')
|
|
55
|
+
for c in node.get("children", []) or []:
|
|
56
|
+
emit(c, my_id, lines, edges)
|
|
57
|
+
lines.append(" end")
|
|
58
|
+
elif type_ in {"RECTANGLE", "ELLIPSE", "COMPONENT", "INSTANCE", "TEXT"}:
|
|
59
|
+
if type_ == "ELLIPSE":
|
|
60
|
+
lines.append(f' {my_id}(("{label}"))')
|
|
61
|
+
else:
|
|
62
|
+
lines.append(f' {my_id}["{label}"]')
|
|
63
|
+
if parent_id:
|
|
64
|
+
edges.append(f" {parent_id} --- {my_id}")
|
|
65
|
+
elif type_ == "CONNECTOR":
|
|
66
|
+
ep = node.get("connectorStart", {})
|
|
67
|
+
eq = node.get("connectorEnd", {})
|
|
68
|
+
a = ep.get("endpointNodeId")
|
|
69
|
+
b = eq.get("endpointNodeId")
|
|
70
|
+
if a and b:
|
|
71
|
+
edges.append(f" {safe_id(a)} --> {safe_id(b)}")
|
|
72
|
+
else:
|
|
73
|
+
for c in node.get("children", []) or []:
|
|
74
|
+
emit(c, parent_id, lines, edges)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def main(argv: list[str]) -> int:
|
|
78
|
+
if len(argv) != 2:
|
|
79
|
+
sys.stderr.write("usage: frame_to_mermaid.py <FILE_KEY_OR_URL> <NODE_ID>\n")
|
|
80
|
+
return 2
|
|
81
|
+
file_key = extract_file_key(argv[0])
|
|
82
|
+
node_id = argv[1]
|
|
83
|
+
|
|
84
|
+
data = get(f"/v1/files/{file_key}/nodes", params={"ids": node_id})
|
|
85
|
+
node = data.get("nodes", {}).get(node_id, {}).get("document")
|
|
86
|
+
if not node:
|
|
87
|
+
sys.stderr.write(f"error: node {node_id} not found in {file_key}\n")
|
|
88
|
+
return 1
|
|
89
|
+
|
|
90
|
+
lines: list[str] = ["flowchart TB"]
|
|
91
|
+
edges: list[str] = []
|
|
92
|
+
emit(node, None, lines, edges)
|
|
93
|
+
lines.extend(edges)
|
|
94
|
+
|
|
95
|
+
print("```mermaid")
|
|
96
|
+
print("\n".join(lines))
|
|
97
|
+
print("```")
|
|
98
|
+
return 0
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Print a tree summary of a Figma file: pages → frames → top-level groups.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python inspect_file.py <FILE_KEY_OR_URL> [--depth N]
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
from _figma_client import extract_file_key, get
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def walk(node: dict, depth: int, max_depth: int) -> None:
|
|
17
|
+
if depth > max_depth:
|
|
18
|
+
return
|
|
19
|
+
indent = " " * depth
|
|
20
|
+
name = node.get("name", "<unnamed>")
|
|
21
|
+
type_ = node.get("type", "?")
|
|
22
|
+
id_ = node.get("id", "?")
|
|
23
|
+
print(f"{indent}- [{type_}] {name} (id={id_})")
|
|
24
|
+
for child in node.get("children", []) or []:
|
|
25
|
+
walk(child, depth + 1, max_depth)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def main(argv: list[str]) -> int:
|
|
29
|
+
ap = argparse.ArgumentParser(description=__doc__)
|
|
30
|
+
ap.add_argument("file", help="Figma file URL or bare file key")
|
|
31
|
+
ap.add_argument("--depth", type=int, default=3, help="Max tree depth to print")
|
|
32
|
+
args = ap.parse_args(argv)
|
|
33
|
+
|
|
34
|
+
file_key = extract_file_key(args.file)
|
|
35
|
+
data = get(f"/v1/files/{file_key}")
|
|
36
|
+
|
|
37
|
+
print(f"file: {data.get('name')!r}")
|
|
38
|
+
print(f"last modified: {data.get('lastModified')}")
|
|
39
|
+
print(f"version: {data.get('version')}")
|
|
40
|
+
print()
|
|
41
|
+
walk(data["document"], depth=0, max_depth=args.depth)
|
|
42
|
+
return 0
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
sys.exit(main(sys.argv[1:]))
|