@mmerterden/multi-agent-pipeline 11.2.0 → 11.3.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 +68 -0
- package/README.md +2 -2
- package/docs/FIGMA_PIPELINE.md +12 -25
- package/docs/architecture.md +3 -5
- package/docs/features.md +8 -8
- package/install/_dev-only-files.mjs +0 -6
- package/install/claude.mjs +39 -17
- package/install/copilot.mjs +22 -16
- package/install/templates/copilot-instructions.md +3 -3
- package/package.json +1 -1
- package/pipeline/commands/{multi-agent.md → multi-agent/SKILL.md} +39 -39
- package/pipeline/commands/multi-agent/{analysis.md → analysis/SKILL.md} +18 -18
- package/pipeline/commands/multi-agent/{analysis-resolve.md → analysis-resolve/SKILL.md} +4 -4
- package/pipeline/commands/multi-agent/{autopilot.md → autopilot/SKILL.md} +4 -4
- package/pipeline/commands/multi-agent/{build-optimize.md → build-optimize/SKILL.md} +1 -1
- package/pipeline/commands/multi-agent/{channels.md → channels/SKILL.md} +12 -12
- package/pipeline/commands/multi-agent/{dev.md → dev/SKILL.md} +4 -4
- package/pipeline/commands/multi-agent/{dev-autopilot.md → dev-autopilot/SKILL.md} +4 -4
- package/pipeline/commands/multi-agent/{dev-local.md → dev-local/SKILL.md} +5 -5
- package/pipeline/commands/multi-agent/{dev-local-autopilot.md → dev-local-autopilot/SKILL.md} +4 -4
- package/pipeline/commands/multi-agent/{finish.md → finish/SKILL.md} +6 -6
- package/pipeline/commands/multi-agent/{generate.md → generate/SKILL.md} +3 -3
- package/pipeline/commands/multi-agent/{help.md → help/SKILL.md} +4 -4
- package/pipeline/commands/multi-agent/{local.md → local/SKILL.md} +5 -5
- package/pipeline/commands/multi-agent/{local-autopilot.md → local-autopilot/SKILL.md} +5 -5
- package/pipeline/commands/multi-agent/{manual-test.md → manual-test/SKILL.md} +1 -1
- package/pipeline/commands/multi-agent/refactor/SKILL.md +257 -0
- package/pipeline/commands/multi-agent/{resume.md → resume/SKILL.md} +1 -1
- package/pipeline/commands/multi-agent/{review.md → review/SKILL.md} +5 -5
- package/pipeline/commands/multi-agent/{setup.md → setup/SKILL.md} +1 -1
- package/pipeline/commands/multi-agent/{sync.md → sync/SKILL.md} +20 -58
- package/pipeline/lib/credential-store.sh +8 -4
- package/pipeline/{commands/multi-agent → multi-agent-refs}/_account-picker.md +2 -2
- package/pipeline/{commands/multi-agent → multi-agent-refs}/_dev-context.md +2 -2
- package/pipeline/{commands/multi-agent → multi-agent-refs}/_repo-picker.md +2 -2
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/android-guide.md +1 -1
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/jira.md +1 -1
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/wiki.md +7 -7
- package/pipeline/multi-agent-refs/component-dispatch.md +92 -0
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/cross-cli-contract.md +10 -77
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/verify-by-test.md +1 -1
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/generate-issue.md +2 -2
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/issue-jira-triad.md +1 -1
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/keychain.md +1 -1
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/log-format.md +1 -1
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/modes.md +2 -2
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/operations.md +1 -1
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-0-init.md +10 -10
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-1-analysis.md +6 -6
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-2-planning.md +3 -3
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-3-dev.md +10 -10
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-4-review.md +8 -8
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-5-test.md +3 -3
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-6-commit.md +4 -4
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-7-report.md +6 -6
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases.md +12 -12
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/progress-contract.md +4 -4
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/swiftui-guide.md +1 -1
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/tracker-contract.md +3 -3
- package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/wiki-capture.md +4 -4
- package/pipeline/preferences-template.json +17 -1
- package/pipeline/rules/figma-pipeline.md +2 -2
- package/pipeline/schemas/prefs.schema.json +0 -34
- package/pipeline/scripts/README.md +1 -3
- package/pipeline/scripts/build-skills-index.mjs +3 -6
- package/pipeline/scripts/eval-mine-corpus.mjs +187 -0
- package/pipeline/scripts/fixtures/install-layout.tsv +6 -5
- package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
- package/pipeline/scripts/lint-skills.mjs +26 -1
- package/pipeline/scripts/phase-banner.sh +2 -2
- package/pipeline/scripts/review-scope.mjs +94 -0
- package/pipeline/scripts/scan-agent-config.sh +2 -2
- package/pipeline/scripts/smoke-agent-log-cost.sh +1 -1
- package/pipeline/scripts/smoke-agent-model-routing.sh +4 -4
- package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +2 -2
- package/pipeline/scripts/smoke-channels-approval-gate.sh +2 -2
- package/pipeline/scripts/smoke-channels-flow.sh +5 -5
- package/pipeline/scripts/smoke-clarify.sh +1 -1
- package/pipeline/scripts/smoke-commands-skills-parity.sh +3 -3
- package/pipeline/scripts/smoke-community-gates.sh +1 -1
- package/pipeline/scripts/smoke-compliance-skills.sh +2 -2
- package/pipeline/scripts/smoke-cost-summary.sh +1 -1
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +17 -43
- package/pipeline/scripts/smoke-cross-phase-cohesion.sh +1 -1
- package/pipeline/scripts/smoke-dev-critic.sh +1 -1
- package/pipeline/scripts/smoke-diff-explain.sh +1 -1
- package/pipeline/scripts/smoke-diff-risk.sh +1 -1
- package/pipeline/scripts/smoke-evidence-gate.sh +1 -1
- package/pipeline/scripts/smoke-figma-dispatch.sh +46 -57
- package/pipeline/scripts/smoke-gate-hooks.sh +1 -1
- package/pipeline/scripts/smoke-generate-issue.sh +8 -8
- package/pipeline/scripts/smoke-handoff-contract.sh +3 -3
- package/pipeline/scripts/smoke-identity-isolation.sh +2 -2
- package/pipeline/scripts/smoke-intent-guard.sh +1 -1
- package/pipeline/scripts/smoke-issue-comment-template.sh +2 -2
- package/pipeline/scripts/smoke-issue-jira-triad.sh +4 -4
- package/pipeline/scripts/smoke-language-axis.sh +6 -6
- package/pipeline/scripts/smoke-learnings-ledger.sh +3 -3
- package/pipeline/scripts/smoke-mode-dispatch-drift.sh +16 -16
- package/pipeline/scripts/smoke-model-fallback.sh +3 -3
- package/pipeline/scripts/smoke-multi-repo-integration.sh +2 -2
- package/pipeline/scripts/smoke-multi-repo-worktree.sh +1 -1
- package/pipeline/scripts/smoke-no-token-prompt.sh +5 -5
- package/pipeline/scripts/smoke-phase-0-multi-repo.sh +1 -1
- package/pipeline/scripts/smoke-phase-6-multi.sh +1 -1
- package/pipeline/scripts/smoke-phase4-gates.sh +45 -0
- package/pipeline/scripts/smoke-plan-approval-gate.sh +5 -5
- package/pipeline/scripts/smoke-plan-todos.sh +3 -3
- package/pipeline/scripts/smoke-pr-review-actions.sh +3 -3
- package/pipeline/scripts/smoke-prefs-language.sh +1 -1
- package/pipeline/scripts/smoke-progress-contract.sh +2 -2
- package/pipeline/scripts/smoke-push-retry.sh +1 -1
- package/pipeline/scripts/smoke-repo-map.sh +2 -2
- package/pipeline/scripts/smoke-shadow-git.sh +1 -1
- package/pipeline/scripts/smoke-skill-authoring.sh +4 -9
- package/pipeline/scripts/smoke-tasklist-ordering.sh +10 -10
- package/pipeline/scripts/smoke-test-gap.sh +1 -1
- package/pipeline/scripts/smoke-token-budget.sh +1 -1
- package/pipeline/scripts/smoke-token-preflight.sh +2 -2
- package/pipeline/scripts/smoke-tracker-contract.sh +13 -13
- package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +5 -5
- package/pipeline/scripts/smoke-triage-memory.sh +2 -2
- package/pipeline/scripts/smoke-update-check.sh +1 -1
- package/pipeline/scripts/smoke-url-enrichment.sh +2 -2
- package/pipeline/scripts/smoke-validator-gates.sh +1 -1
- package/pipeline/scripts/smoke-verify-by-test.sh +3 -3
- package/pipeline/scripts/smoke-wiki-integration.sh +9 -33
- package/pipeline/scripts/smoke-work-summary.sh +1 -1
- package/pipeline/scripts/smoke-worktree-path-convention.sh +4 -4
- package/pipeline/scripts/test-integrity-gate.mjs +83 -0
- package/pipeline/scripts/uninstall.mjs +17 -6
- package/pipeline/skills/.skill-manifest.json +40 -160
- package/pipeline/skills/.skills-index.json +3 -373
- package/pipeline/skills/shared/README.md +1 -1
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +3 -3
- package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +116 -51
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +8 -30
- package/pipeline/skills/shared/external/compose-components/SKILL.md +14 -14
- package/pipeline/skills/skills-index.md +3 -44
- package/pipeline/commands/multi-agent/refactor.md +0 -188
- package/pipeline/commands/multi-agent/refs/component-dispatch.md +0 -92
- package/pipeline/scripts/smoke-existing-discovery-gate.sh +0 -71
- package/pipeline/scripts/smoke-figma-android-parity.sh +0 -148
- package/pipeline/scripts/smoke-figma-credential-store.sh +0 -105
- package/pipeline/scripts/smoke-figma-cross-cli-inventory.sh +0 -177
- package/pipeline/scripts/smoke-figma-sync.sh +0 -149
- package/pipeline/scripts/smoke-sync-delegation.sh +0 -74
- package/pipeline/skills/figma-android/README.md +0 -38
- package/pipeline/skills/figma-android/figma-component-code-connect/SKILL.md +0 -62
- package/pipeline/skills/figma-android/figma-component-implement/SKILL.md +0 -158
- package/pipeline/skills/figma-android/figma-component-test/SKILL.md +0 -120
- package/pipeline/skills/figma-android/figma-component-wiki/SKILL.md +0 -35
- package/pipeline/skills/figma-android/figma-to-component/SKILL.md +0 -124
- package/pipeline/skills/figma-common/README.md +0 -64
- package/pipeline/skills/figma-common/figma-bottom-sheets/SKILL.md +0 -152
- package/pipeline/skills/figma-common/figma-cli-iterate/SKILL.md +0 -282
- package/pipeline/skills/figma-common/figma-cli-iterate-mend/SKILL.md +0 -503
- package/pipeline/skills/figma-common/figma-cli-lean-iterate/SKILL.md +0 -288
- package/pipeline/skills/figma-common/figma-cli-skip/SKILL.md +0 -362
- package/pipeline/skills/figma-common/figma-commit/COMMON_REBASE.md +0 -206
- package/pipeline/skills/figma-common/figma-commit/REVIEW.md +0 -337
- package/pipeline/skills/figma-common/figma-commit/SKILL.md +0 -216
- package/pipeline/skills/figma-common/figma-component-confluence-sync/SKILL.md +0 -223
- package/pipeline/skills/figma-common/figma-component-start/SKILL.md +0 -251
- package/pipeline/skills/figma-common/figma-component-status-update/SKILL.md +0 -78
- package/pipeline/skills/figma-common/figma-evolve-component/SKILL.md +0 -61
- package/pipeline/skills/figma-common/figma-fix/SKILL.md +0 -321
- package/pipeline/skills/figma-common/figma-form-integration/SKILL.md +0 -542
- package/pipeline/skills/figma-common/figma-issue/SKILL.md +0 -750
- package/pipeline/skills/figma-common/figma-iterate/SKILL.md +0 -208
- package/pipeline/skills/figma-common/figma-iteration-commit/SKILL.md +0 -1020
- package/pipeline/skills/figma-common/figma-mend/SKILL.md +0 -331
- package/pipeline/skills/figma-common/figma-navigation/SKILL.md +0 -156
- package/pipeline/skills/figma-common/figma-overlays/SKILL.md +0 -142
- package/pipeline/skills/figma-common/figma-price-integration/SKILL.md +0 -398
- package/pipeline/skills/figma-common/figma-remote-mcp-auth/SKILL.md +0 -104
- package/pipeline/skills/figma-common/figma-review/SKILL.md +0 -400
- package/pipeline/skills/figma-common/figma-setup/SKILL.md +0 -514
- package/pipeline/skills/figma-common/figma-setup/scripts/fetch-mcp-token.py +0 -592
- package/pipeline/skills/figma-common/figma-skip/SKILL.md +0 -129
- package/pipeline/skills/figma-common/figma-ui-patterns/SKILL.md +0 -105
- package/pipeline/skills/figma-common/figma-ui-patterns/patterns/animated-gradient-border.md +0 -116
- package/pipeline/skills/figma-common/figma-utility/SKILL.md +0 -274
- package/pipeline/skills/figma-common/figma-utility/scripts/figma-utility.py +0 -808
- package/pipeline/skills/figma-common/figma-validate/SKILL.md +0 -633
- package/pipeline/skills/figma-common/performance-iteration-commit-all/SKILL.md +0 -716
- package/pipeline/skills/figma-common/performance-review-next/SKILL.md +0 -238
- package/pipeline/skills/figma-common/performance-start/SKILL.md +0 -430
- package/pipeline/skills/figma-common/performance-swiftui/SKILL.md +0 -706
- package/pipeline/skills/figma-common/performance-tour/SKILL.md +0 -423
- package/pipeline/skills/figma-ios/REVIEW_CHECKLIST.md +0 -67
- package/pipeline/skills/figma-ios/figma-component-code-connect/SKILL.md +0 -178
- package/pipeline/skills/figma-ios/figma-component-implement/SKILL.md +0 -184
- package/pipeline/skills/figma-ios/figma-component-test/SKILL.md +0 -219
- package/pipeline/skills/figma-ios/figma-component-wiki/SKILL.md +0 -274
- package/pipeline/skills/figma-ios/figma-to-component/SKILL.md +0 -416
- package/pipeline/skills/figma-ios/figma-to-component/halt-return-protocol.md +0 -57
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-0-init.md +0 -307
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1-gathering.md +0 -119
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1.5-existing-discovery.md +0 -174
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2-orchestrator.md +0 -333
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2a-testing-identifiers.md +0 -368
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2b-localization.md +0 -393
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2c-accessibility.md +0 -617
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2d-analytics.md +0 -352
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3-orchestrator.md +0 -337
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3a-location.md +0 -206
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3b-tokens.md +0 -235
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3c-nested.md +0 -214
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3d-patterns.md +0 -902
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3e-assets.md +0 -156
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3f-utilities.md +0 -175
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3g-property-coverage.md +0 -176
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3h-variant-config.md +0 -333
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4-orchestrator.md +0 -412
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4a-configuration.md +0 -336
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4b-view.md +0 -705
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4c-documentation.md +0 -332
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4d-preview.md +0 -380
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4e-modifiers.md +0 -262
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5-orchestrator.md +0 -482
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5a-viewinspector.md +0 -274
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5b-snapshot.md +0 -636
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5c-unit.md +0 -142
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-6-code-connect.md +0 -547
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7-wiki.md +0 -39
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-confluence-generate.md +0 -659
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-wiki-generate.md +0 -580
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-8-cleanup.md +0 -51
- package/pipeline/skills/figma-ios/figma-to-component/reference/accessibility.md +0 -183
- package/pipeline/skills/figma-ios/figma-to-component/reference/analytics-events.md +0 -64
- package/pipeline/skills/figma-ios/figma-to-component/reference/code-connect.md +0 -531
- package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-api.md +0 -89
- package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-xhtml.md +0 -155
- package/pipeline/skills/figma-ios/figma-to-component/reference/figma-to-swiftui-effects.md +0 -196
- package/pipeline/skills/figma-ios/figma-to-component/reference/halt-return-protocol.md +0 -57
- package/pipeline/skills/figma-ios/figma-to-component/reference/localization-naming.md +0 -89
- package/pipeline/skills/figma-ios/figma-to-component/reference/macros.md +0 -227
- package/pipeline/skills/figma-ios/figma-to-component/reference/missing-tokens.md +0 -157
- package/pipeline/skills/figma-ios/figma-to-component/reference/orchestrator-discipline.md +0 -90
- package/pipeline/skills/figma-ios/figma-to-component/reference/registry.md +0 -116
- package/pipeline/skills/figma-ios/figma-to-component/reference/remote-mcp-script.md +0 -153
- package/pipeline/skills/figma-ios/figma-to-component/reference/rest-api-script.md +0 -130
- package/pipeline/skills/figma-ios/figma-to-component/reference/scripts-inventory.md +0 -218
- package/pipeline/skills/figma-ios/figma-to-component/reference/snapshot-testing.md +0 -188
- package/pipeline/skills/figma-ios/figma-to-component/reference/subcomponent-graph.md +0 -93
- package/pipeline/skills/figma-ios/figma-to-component/reference/testing-identifiers-naming.md +0 -98
- package/pipeline/skills/figma-ios/figma-to-component/reference/tools.md +0 -261
- package/pipeline/skills/figma-ios/figma-to-component/reference/viewinspector.md +0 -147
- package/pipeline/skills/figma-ios/figma-to-component/reference/wiki-to-confluence-mapping.md +0 -182
- package/pipeline/skills/figma-ios/figma-to-component/scripts/apply-author-login-map.py +0 -185
- package/pipeline/skills/figma-ios/figma-to-component/scripts/backfill-status.py +0 -609
- package/pipeline/skills/figma-ios/figma-to-component/scripts/build-author-registry.py +0 -332
- package/pipeline/skills/figma-ios/figma-to-component/scripts/bulk-sync-issues.py +0 -261
- package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-data-gather.py +0 -184
- package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-publish.sh +0 -188
- package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status-upload.py +0 -768
- package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status.py +0 -191
- package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-data-gather.py +0 -420
- package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-page-ids.example.json +0 -9
- package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-publish.py +0 -336
- package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-subcomponent-graph.py +0 -391
- package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-update.py +0 -292
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/__init__.py +0 -1
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/issue_sync_propagate.py +0 -93
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/registry_writer.py +0 -299
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_backfill_status.py +0 -343
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update.py +0 -206
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update_http.py +0 -149
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_phase_clis.py +0 -281
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_registry_writer.py +0 -332
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_issue.py +0 -176
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_review.py +0 -98
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue.py +0 -298
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue_gh.py +0 -195
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase1-gather.py +0 -1298
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase2-finalize.py +0 -228
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase3-scripts.py +0 -1089
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase4-finalize.py +0 -141
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase5-finalize.py +0 -106
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase6-finalize.py +0 -162
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase7-finalize.py +0 -105
- package/pipeline/skills/figma-ios/figma-to-component/scripts/register-icons-codeconnect.py +0 -179
- package/pipeline/skills/figma-ios/figma-to-component/scripts/remote-mcp-fetch.py +0 -260
- package/pipeline/skills/figma-ios/figma-to-component/scripts/resolve-author-logins.py +0 -260
- package/pipeline/skills/figma-ios/figma-to-component/scripts/run-uicomponents-tests.sh +0 -86
- package/pipeline/skills/figma-ios/figma-to-component/scripts/sidebar-generator.py +0 -321
- package/pipeline/skills/figma-ios/figma-to-component/scripts/update-issue-from-registry.py +0 -1470
- package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase4.sh +0 -176
- package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase6.sh +0 -147
- package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase7a.py +0 -629
- /package/pipeline/commands/multi-agent/{delete.md → delete/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{diff-explain.md → diff-explain/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{garbage-collect.md → garbage-collect/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{issue.md → issue/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{jira.md → jira/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{kill.md → kill/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{language.md → language/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{log.md → log/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{prune-logs.md → prune-logs/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{purge.md → purge/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{scan.md → scan/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{search.md → search/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{stack.md → stack/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{status.md → status/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{test.md → test/SKILL.md} +0 -0
- /package/pipeline/commands/multi-agent/{update.md → update/SKILL.md} +0 -0
- /package/pipeline/{commands/multi-agent → multi-agent-refs}/_input-parser.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/analysis-template.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/audit-guide.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/backend-guide.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/confluence.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/issue-comment.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/pr-review-actions.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/pr.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/conventions-defaults.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/autopilot-circuit-breaker.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/dev-critic.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/external-context-injection.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/model-fallback.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/plan-todos.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/prior-fix-detection.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/repo-map.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/shadow-git.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/frontend-guide.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/knowledge.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/multi-repo-integration-build.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/picker-contract.md +0 -0
- /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/rules.md +0 -0
|
@@ -1,808 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Figma Utility Script
|
|
4
|
-
Standalone tool for querying Figma data via REST API and MCP.
|
|
5
|
-
|
|
6
|
-
Usage:
|
|
7
|
-
python3 figma-utility.py <command> [options]
|
|
8
|
-
|
|
9
|
-
Commands:
|
|
10
|
-
screenshot --url <figma-url> [--output path] [--scale 1|2|3|4]
|
|
11
|
-
metadata --url <figma-url> [--output path]
|
|
12
|
-
properties --url <figma-url> [--output path]
|
|
13
|
-
tokens --url <figma-url> [--output path]
|
|
14
|
-
context --url <figma-url> [--output path]
|
|
15
|
-
code-connect --url <figma-url> [--output path]
|
|
16
|
-
node --url <figma-url> [--output path] [--depth N]
|
|
17
|
-
components --url <figma-url> [--output path]
|
|
18
|
-
styles --url <figma-url> [--output path]
|
|
19
|
-
file --url <figma-url> [--output path] [--depth N]
|
|
20
|
-
whoami
|
|
21
|
-
|
|
22
|
-
Token resolution: --figma-token / --mcp-token CLI arg > macOS Keychain > Claude Code credentials
|
|
23
|
-
|
|
24
|
-
Exit 0 = success (prints result to stdout)
|
|
25
|
-
Exit 1 = failure (prints JSON error to stderr)
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
import argparse
|
|
29
|
-
import base64
|
|
30
|
-
import http.client
|
|
31
|
-
import json
|
|
32
|
-
import os
|
|
33
|
-
import re
|
|
34
|
-
import subprocess
|
|
35
|
-
import sys
|
|
36
|
-
from pathlib import Path
|
|
37
|
-
from urllib.error import HTTPError, URLError
|
|
38
|
-
from urllib.parse import urlparse
|
|
39
|
-
from urllib.request import Request, urlopen
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# ─── Globals ─────────────────────────────────────────────────────────────────
|
|
43
|
-
|
|
44
|
-
MCP_MODE = None # "remote" or "local"
|
|
45
|
-
MCP_URL = "http://127.0.0.1:3845/mcp"
|
|
46
|
-
MCP_TOKEN = None
|
|
47
|
-
SESSION_ID = None
|
|
48
|
-
FIGMA_TOKEN = None
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def log(msg):
|
|
52
|
-
print(f"[figma-utility] {msg}", file=sys.stderr)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def error_exit(code, message, hint=""):
|
|
56
|
-
print(json.dumps({
|
|
57
|
-
"status": "error",
|
|
58
|
-
"message": message,
|
|
59
|
-
"hint": hint,
|
|
60
|
-
}, indent=2), file=sys.stderr)
|
|
61
|
-
sys.exit(code)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
# ─── URL Parsing ─────────────────────────────────────────────────────────────
|
|
65
|
-
|
|
66
|
-
def parse_figma_url(url):
|
|
67
|
-
"""Extract fileKey and nodeId from a Figma URL.
|
|
68
|
-
|
|
69
|
-
Returns (fileKey, nodeId) where nodeId uses colon format (e.g. '123:456').
|
|
70
|
-
nodeId may be None if not present in the URL.
|
|
71
|
-
"""
|
|
72
|
-
if not url:
|
|
73
|
-
return None, None
|
|
74
|
-
|
|
75
|
-
# Match: figma.com/design/{fileKey}/... or figma.com/file/{fileKey}/...
|
|
76
|
-
m = re.search(r'figma\.com/(?:design|file)/([A-Za-z0-9]+)', url)
|
|
77
|
-
if not m:
|
|
78
|
-
# Try branch URL
|
|
79
|
-
m = re.search(r'figma\.com/(?:design|file)/[^/]+/[^?]*\?.*branch-id=([A-Za-z0-9]+)', url)
|
|
80
|
-
file_key = m.group(1) if m else None
|
|
81
|
-
|
|
82
|
-
# Extract node-id from query params
|
|
83
|
-
node_id = None
|
|
84
|
-
nm = re.search(r'node-id=([0-9]+-[0-9]+)', url)
|
|
85
|
-
if nm:
|
|
86
|
-
node_id = nm.group(1).replace("-", ":")
|
|
87
|
-
else:
|
|
88
|
-
nm = re.search(r'node-id=([0-9]+%3A[0-9]+)', url)
|
|
89
|
-
if nm:
|
|
90
|
-
node_id = nm.group(1).replace("%3A", ":")
|
|
91
|
-
|
|
92
|
-
return file_key, node_id
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
# ─── Token Resolution ────────────────────────────────────────────────────────
|
|
96
|
-
|
|
97
|
-
def get_figma_token(cli_token=None):
|
|
98
|
-
"""Resolve Figma REST API token: CLI arg > Keychain FIGMA_ACCESS_TOKEN."""
|
|
99
|
-
if cli_token:
|
|
100
|
-
return cli_token
|
|
101
|
-
try:
|
|
102
|
-
token = subprocess.check_output(
|
|
103
|
-
["security", "find-generic-password", "-a", os.environ["USER"],
|
|
104
|
-
"-s", "FIGMA_ACCESS_TOKEN", "-w"],
|
|
105
|
-
stderr=subprocess.DEVNULL,
|
|
106
|
-
).decode().strip()
|
|
107
|
-
if token:
|
|
108
|
-
log("REST token from Keychain: FIGMA_ACCESS_TOKEN")
|
|
109
|
-
return token
|
|
110
|
-
except (subprocess.CalledProcessError, KeyError):
|
|
111
|
-
pass
|
|
112
|
-
return None
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def get_mcp_token(cli_token=None):
|
|
116
|
-
"""Resolve MCP OAuth token: CLI arg > Keychain FIGMA_MCP_TOKEN > Claude Code credentials."""
|
|
117
|
-
if cli_token:
|
|
118
|
-
return cli_token
|
|
119
|
-
# Keychain: dedicated entry
|
|
120
|
-
try:
|
|
121
|
-
token = subprocess.check_output(
|
|
122
|
-
["security", "find-generic-password", "-a", os.environ["USER"],
|
|
123
|
-
"-s", "FIGMA_MCP_TOKEN", "-w"],
|
|
124
|
-
stderr=subprocess.DEVNULL,
|
|
125
|
-
).decode().strip()
|
|
126
|
-
if token:
|
|
127
|
-
log("MCP token from Keychain: FIGMA_MCP_TOKEN")
|
|
128
|
-
return token
|
|
129
|
-
except (subprocess.CalledProcessError, KeyError):
|
|
130
|
-
pass
|
|
131
|
-
# Keychain: extract from Claude Code credentials
|
|
132
|
-
try:
|
|
133
|
-
raw = subprocess.check_output(
|
|
134
|
-
["security", "find-generic-password", "-s", "Claude Code-credentials",
|
|
135
|
-
"-a", os.environ["USER"], "-w"],
|
|
136
|
-
stderr=subprocess.DEVNULL,
|
|
137
|
-
).decode()
|
|
138
|
-
match = re.search(
|
|
139
|
-
r'"figma-remote\|[^"]+":\{[^}]*"accessToken":"([^"]+)"', raw
|
|
140
|
-
)
|
|
141
|
-
if match:
|
|
142
|
-
log("MCP token extracted from Claude Code credentials")
|
|
143
|
-
return match.group(1)
|
|
144
|
-
except (subprocess.CalledProcessError, KeyError):
|
|
145
|
-
pass
|
|
146
|
-
return None
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
# ─── REST API ────────────────────────────────────────────────────────────────
|
|
150
|
-
|
|
151
|
-
def figma_get(endpoint):
|
|
152
|
-
"""GET from Figma REST API. Returns parsed JSON."""
|
|
153
|
-
if not FIGMA_TOKEN:
|
|
154
|
-
error_exit(1, "No Figma REST API token found.",
|
|
155
|
-
"Run /figma-setup or pass --figma-token.")
|
|
156
|
-
url = f"https://api.figma.com/v1/{endpoint}"
|
|
157
|
-
req = Request(url, headers={"X-Figma-Token": FIGMA_TOKEN})
|
|
158
|
-
try:
|
|
159
|
-
with urlopen(req, timeout=60) as resp:
|
|
160
|
-
return json.loads(resp.read().decode())
|
|
161
|
-
except HTTPError as e:
|
|
162
|
-
body = e.read().decode() if hasattr(e, 'read') else ""
|
|
163
|
-
error_exit(1, f"Figma API HTTP {e.code}: {body[:300]}",
|
|
164
|
-
f"Endpoint: {endpoint}")
|
|
165
|
-
except URLError as e:
|
|
166
|
-
error_exit(1, f"Figma API connection error: {e.reason}",
|
|
167
|
-
"Check your network connection.")
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
def download_file(url, dest):
|
|
171
|
-
"""Download a URL to a local file path."""
|
|
172
|
-
req = Request(url)
|
|
173
|
-
with urlopen(req, timeout=60) as resp:
|
|
174
|
-
Path(dest).parent.mkdir(parents=True, exist_ok=True)
|
|
175
|
-
with open(dest, "wb") as f:
|
|
176
|
-
f.write(resp.read())
|
|
177
|
-
return True
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
# ─── MCP Helpers ─────────────────────────────────────────────────────────────
|
|
181
|
-
|
|
182
|
-
def _parse_sse_json(raw):
|
|
183
|
-
"""Parse JSON from SSE response. Handles multiline data payloads."""
|
|
184
|
-
data_start = raw.find("\ndata: ")
|
|
185
|
-
if data_start == -1:
|
|
186
|
-
data_start = raw.find("data: ")
|
|
187
|
-
if data_start == -1:
|
|
188
|
-
return None
|
|
189
|
-
|
|
190
|
-
json_str = raw[raw.index("data: ", data_start) + 6:].strip()
|
|
191
|
-
try:
|
|
192
|
-
return json.loads(json_str)
|
|
193
|
-
except json.JSONDecodeError:
|
|
194
|
-
depth = 0
|
|
195
|
-
end = 0
|
|
196
|
-
for i, ch in enumerate(json_str):
|
|
197
|
-
if ch == "{":
|
|
198
|
-
depth += 1
|
|
199
|
-
elif ch == "}":
|
|
200
|
-
depth -= 1
|
|
201
|
-
if depth == 0:
|
|
202
|
-
end = i + 1
|
|
203
|
-
break
|
|
204
|
-
if end > 0:
|
|
205
|
-
try:
|
|
206
|
-
return json.loads(json_str[:end])
|
|
207
|
-
except json.JSONDecodeError:
|
|
208
|
-
pass
|
|
209
|
-
return None
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
def _mcp_post_local(body_dict):
|
|
213
|
-
"""POST to local MCP (HTTP, session-based)."""
|
|
214
|
-
parsed = urlparse(MCP_URL)
|
|
215
|
-
conn = http.client.HTTPConnection(parsed.hostname, parsed.port, timeout=120)
|
|
216
|
-
headers = {
|
|
217
|
-
"Content-Type": "application/json",
|
|
218
|
-
"Accept": "application/json, text/event-stream",
|
|
219
|
-
}
|
|
220
|
-
if SESSION_ID:
|
|
221
|
-
headers["mcp-session-id"] = SESSION_ID
|
|
222
|
-
conn.request("POST", parsed.path, body=json.dumps(body_dict), headers=headers)
|
|
223
|
-
resp = conn.getresponse()
|
|
224
|
-
session = resp.getheader("mcp-session-id")
|
|
225
|
-
raw = resp.read().decode()
|
|
226
|
-
conn.close()
|
|
227
|
-
return _parse_sse_json(raw), session
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
def _mcp_post_remote(body_dict):
|
|
231
|
-
"""POST to remote MCP (HTTPS, Bearer auth, stateless)."""
|
|
232
|
-
conn = http.client.HTTPSConnection("mcp.figma.com", timeout=120)
|
|
233
|
-
headers = {
|
|
234
|
-
"Content-Type": "application/json",
|
|
235
|
-
"Accept": "application/json, text/event-stream",
|
|
236
|
-
"Authorization": f"Bearer {MCP_TOKEN}",
|
|
237
|
-
}
|
|
238
|
-
conn.request("POST", "/mcp", body=json.dumps(body_dict), headers=headers)
|
|
239
|
-
resp = conn.getresponse()
|
|
240
|
-
raw = resp.read().decode()
|
|
241
|
-
conn.close()
|
|
242
|
-
if resp.status != 200:
|
|
243
|
-
raise RuntimeError(f"Remote MCP HTTP {resp.status}: {raw[:200]}")
|
|
244
|
-
return _parse_sse_json(raw), None
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
def _mcp_post(body_dict):
|
|
248
|
-
if MCP_MODE == "remote":
|
|
249
|
-
return _mcp_post_remote(body_dict)
|
|
250
|
-
else:
|
|
251
|
-
return _mcp_post_local(body_dict)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
def mcp_init():
|
|
255
|
-
"""Initialize MCP connection. Tries local first, falls back to remote."""
|
|
256
|
-
global MCP_MODE, SESSION_ID
|
|
257
|
-
|
|
258
|
-
init_body = {
|
|
259
|
-
"jsonrpc": "2.0",
|
|
260
|
-
"method": "initialize",
|
|
261
|
-
"params": {
|
|
262
|
-
"capabilities": {},
|
|
263
|
-
"clientInfo": {"name": "figma-utility", "version": "1.0.0"},
|
|
264
|
-
"protocolVersion": "2025-03-26",
|
|
265
|
-
},
|
|
266
|
-
"id": 1,
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
# Try local first (faster, no rate limits)
|
|
270
|
-
try:
|
|
271
|
-
log(f"MCP: trying local ({MCP_URL})...")
|
|
272
|
-
result, session = _mcp_post_local(init_body)
|
|
273
|
-
if session:
|
|
274
|
-
SESSION_ID = session
|
|
275
|
-
MCP_MODE = "local"
|
|
276
|
-
log(f"MCP: local connected (session: {SESSION_ID[:12]}...)")
|
|
277
|
-
return True
|
|
278
|
-
except Exception as exc:
|
|
279
|
-
log(f"MCP: local failed ({exc})")
|
|
280
|
-
|
|
281
|
-
# Fall back to remote
|
|
282
|
-
if MCP_TOKEN:
|
|
283
|
-
try:
|
|
284
|
-
log("MCP: trying remote (mcp.figma.com)...")
|
|
285
|
-
result, _ = _mcp_post_remote(init_body)
|
|
286
|
-
if result:
|
|
287
|
-
MCP_MODE = "remote"
|
|
288
|
-
log("MCP: remote connected")
|
|
289
|
-
return True
|
|
290
|
-
except Exception as exc:
|
|
291
|
-
log(f"MCP: remote failed ({exc})")
|
|
292
|
-
|
|
293
|
-
return False
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
_MCP_CALL_ID = 10
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
def mcp_call(tool_name, arguments, file_key=None):
|
|
300
|
-
"""Call an MCP tool. Returns (texts, images) lists."""
|
|
301
|
-
global _MCP_CALL_ID
|
|
302
|
-
_MCP_CALL_ID += 1
|
|
303
|
-
|
|
304
|
-
# Remote MCP requires fileKey
|
|
305
|
-
if MCP_MODE == "remote" and file_key and "fileKey" not in arguments:
|
|
306
|
-
arguments = {**arguments, "fileKey": file_key}
|
|
307
|
-
|
|
308
|
-
body = {
|
|
309
|
-
"jsonrpc": "2.0",
|
|
310
|
-
"method": "tools/call",
|
|
311
|
-
"params": {"name": tool_name, "arguments": arguments},
|
|
312
|
-
"id": _MCP_CALL_ID,
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
result_json, _ = _mcp_post(body)
|
|
316
|
-
if not result_json:
|
|
317
|
-
raise RuntimeError(f"MCP {tool_name}: empty response")
|
|
318
|
-
if "error" in result_json:
|
|
319
|
-
raise RuntimeError(f"MCP {tool_name}: {result_json['error']}")
|
|
320
|
-
|
|
321
|
-
texts = []
|
|
322
|
-
images = []
|
|
323
|
-
content = (result_json.get("result") or {}).get("content", [])
|
|
324
|
-
for item in content:
|
|
325
|
-
if item.get("type") == "text":
|
|
326
|
-
texts.append(item["text"])
|
|
327
|
-
elif item.get("type") == "image":
|
|
328
|
-
images.append({
|
|
329
|
-
"mimeType": item.get("mimeType", "image/png"),
|
|
330
|
-
"data": item.get("data", ""),
|
|
331
|
-
})
|
|
332
|
-
return texts, images
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
# ─── Commands ────────────────────────────────────────────────────────────────
|
|
336
|
-
|
|
337
|
-
def cmd_screenshot(file_key, node_id, output_path=None, scale=2):
|
|
338
|
-
"""Get PNG screenshot of a node."""
|
|
339
|
-
# Try REST API first (returns URL, more efficient for saving)
|
|
340
|
-
if FIGMA_TOKEN:
|
|
341
|
-
log(f"Fetching screenshot via REST API (scale={scale})...")
|
|
342
|
-
data = figma_get(f"images/{file_key}?ids={node_id}&format=png&scale={scale}")
|
|
343
|
-
images = data.get("images", {})
|
|
344
|
-
img_url = images.get(node_id)
|
|
345
|
-
if img_url:
|
|
346
|
-
if output_path:
|
|
347
|
-
download_file(img_url, output_path)
|
|
348
|
-
log(f"Screenshot saved: {output_path}")
|
|
349
|
-
print(json.dumps({
|
|
350
|
-
"status": "ok",
|
|
351
|
-
"command": "screenshot",
|
|
352
|
-
"output": output_path,
|
|
353
|
-
"source": "rest-api",
|
|
354
|
-
}, indent=2))
|
|
355
|
-
return
|
|
356
|
-
else:
|
|
357
|
-
print(json.dumps({
|
|
358
|
-
"status": "ok",
|
|
359
|
-
"command": "screenshot",
|
|
360
|
-
"url": img_url,
|
|
361
|
-
"source": "rest-api",
|
|
362
|
-
"hint": "Use --output to save to file",
|
|
363
|
-
}, indent=2))
|
|
364
|
-
return
|
|
365
|
-
|
|
366
|
-
# Fallback to MCP
|
|
367
|
-
if MCP_MODE:
|
|
368
|
-
log("Fetching screenshot via MCP...")
|
|
369
|
-
texts, images = mcp_call("get_screenshot", {"nodeId": node_id}, file_key)
|
|
370
|
-
if images:
|
|
371
|
-
img_data = base64.b64decode(images[0]["data"])
|
|
372
|
-
if output_path:
|
|
373
|
-
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
374
|
-
with open(output_path, "wb") as f:
|
|
375
|
-
f.write(img_data)
|
|
376
|
-
log(f"Screenshot saved: {output_path} ({len(img_data)} bytes)")
|
|
377
|
-
print(json.dumps({
|
|
378
|
-
"status": "ok",
|
|
379
|
-
"command": "screenshot",
|
|
380
|
-
"output": output_path,
|
|
381
|
-
"bytes": len(img_data),
|
|
382
|
-
"source": f"mcp-{MCP_MODE}",
|
|
383
|
-
}, indent=2))
|
|
384
|
-
return
|
|
385
|
-
else:
|
|
386
|
-
print(json.dumps({
|
|
387
|
-
"status": "ok",
|
|
388
|
-
"command": "screenshot",
|
|
389
|
-
"bytes": len(img_data),
|
|
390
|
-
"source": f"mcp-{MCP_MODE}",
|
|
391
|
-
"hint": "Use --output to save to file (base64 data available via MCP)",
|
|
392
|
-
}, indent=2))
|
|
393
|
-
return
|
|
394
|
-
|
|
395
|
-
error_exit(1, "No screenshot source available.",
|
|
396
|
-
"Provide --figma-token for REST API or ensure MCP is available.")
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
def cmd_metadata(file_key, node_id, output_path=None):
|
|
400
|
-
"""Get layer hierarchy (XML) of a node."""
|
|
401
|
-
if not MCP_MODE:
|
|
402
|
-
error_exit(1, "MCP required for metadata.", "Ensure Figma Desktop or remote MCP is available.")
|
|
403
|
-
|
|
404
|
-
log("Fetching metadata via MCP...")
|
|
405
|
-
texts, _ = mcp_call("get_metadata", {"nodeId": node_id}, file_key)
|
|
406
|
-
result = "\n".join(texts)
|
|
407
|
-
|
|
408
|
-
if output_path:
|
|
409
|
-
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
410
|
-
Path(output_path).write_text(result)
|
|
411
|
-
log(f"Metadata saved: {output_path} ({len(result)} chars)")
|
|
412
|
-
|
|
413
|
-
print(result)
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
def cmd_properties(file_key, node_id, output_path=None):
|
|
417
|
-
"""Get component properties/variants via REST API."""
|
|
418
|
-
log("Fetching node data via REST API...")
|
|
419
|
-
data = figma_get(f"files/{file_key}/nodes?ids={node_id}")
|
|
420
|
-
|
|
421
|
-
nodes = data.get("nodes", {})
|
|
422
|
-
node_data = nodes.get(node_id, {})
|
|
423
|
-
document = node_data.get("document", {})
|
|
424
|
-
|
|
425
|
-
# Extract component property definitions
|
|
426
|
-
prop_defs = document.get("componentPropertyDefinitions", {})
|
|
427
|
-
|
|
428
|
-
# Also check children for component set
|
|
429
|
-
if not prop_defs and document.get("type") == "COMPONENT_SET":
|
|
430
|
-
children = document.get("children", [])
|
|
431
|
-
if children:
|
|
432
|
-
prop_defs = children[0].get("componentPropertyDefinitions", {})
|
|
433
|
-
|
|
434
|
-
# Build structured output
|
|
435
|
-
properties = {}
|
|
436
|
-
for prop_name, prop_def in prop_defs.items():
|
|
437
|
-
properties[prop_name] = {
|
|
438
|
-
"type": prop_def.get("type"),
|
|
439
|
-
"defaultValue": prop_def.get("defaultValue"),
|
|
440
|
-
}
|
|
441
|
-
if prop_def.get("variantOptions"):
|
|
442
|
-
properties[prop_name]["options"] = prop_def["variantOptions"]
|
|
443
|
-
|
|
444
|
-
result = {
|
|
445
|
-
"status": "ok",
|
|
446
|
-
"command": "properties",
|
|
447
|
-
"nodeId": node_id,
|
|
448
|
-
"name": document.get("name", ""),
|
|
449
|
-
"type": document.get("type", ""),
|
|
450
|
-
"propertyCount": len(properties),
|
|
451
|
-
"properties": properties,
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
# Count variants if component set
|
|
455
|
-
if document.get("type") == "COMPONENT_SET":
|
|
456
|
-
result["variantCount"] = len(document.get("children", []))
|
|
457
|
-
|
|
458
|
-
output = json.dumps(result, indent=2)
|
|
459
|
-
if output_path:
|
|
460
|
-
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
461
|
-
Path(output_path).write_text(output)
|
|
462
|
-
log(f"Properties saved: {output_path}")
|
|
463
|
-
|
|
464
|
-
print(output)
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
def cmd_tokens(file_key, node_id, output_path=None):
|
|
468
|
-
"""Get design tokens/variables for a node."""
|
|
469
|
-
if not MCP_MODE:
|
|
470
|
-
# Fallback to REST API (may 403 on non-Enterprise)
|
|
471
|
-
log("MCP unavailable, trying REST API for variables...")
|
|
472
|
-
try:
|
|
473
|
-
data = figma_get(f"files/{file_key}/variables/local")
|
|
474
|
-
output = json.dumps({"status": "ok", "command": "tokens", "source": "rest-api", "data": data}, indent=2)
|
|
475
|
-
if output_path:
|
|
476
|
-
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
477
|
-
Path(output_path).write_text(output)
|
|
478
|
-
print(output)
|
|
479
|
-
return
|
|
480
|
-
except SystemExit:
|
|
481
|
-
error_exit(1, "Neither MCP nor REST API available for tokens.",
|
|
482
|
-
"Ensure Figma Desktop or remote MCP is available.")
|
|
483
|
-
|
|
484
|
-
log("Fetching tokens via MCP...")
|
|
485
|
-
texts, _ = mcp_call("get_variable_defs", {"nodeId": node_id}, file_key)
|
|
486
|
-
result_text = "\n".join(texts)
|
|
487
|
-
|
|
488
|
-
# Try to parse as JSON for structured output
|
|
489
|
-
try:
|
|
490
|
-
token_data = json.loads(result_text)
|
|
491
|
-
output = json.dumps({
|
|
492
|
-
"status": "ok",
|
|
493
|
-
"command": "tokens",
|
|
494
|
-
"source": f"mcp-{MCP_MODE}",
|
|
495
|
-
"tokenCount": len(token_data) if isinstance(token_data, dict) else None,
|
|
496
|
-
"data": token_data,
|
|
497
|
-
}, indent=2)
|
|
498
|
-
except json.JSONDecodeError:
|
|
499
|
-
output = result_text
|
|
500
|
-
|
|
501
|
-
if output_path:
|
|
502
|
-
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
503
|
-
Path(output_path).write_text(output)
|
|
504
|
-
log(f"Tokens saved: {output_path}")
|
|
505
|
-
|
|
506
|
-
print(output)
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
def cmd_context(file_key, node_id, output_path=None):
|
|
510
|
-
"""Get design context (code generation) for a node."""
|
|
511
|
-
if not MCP_MODE:
|
|
512
|
-
error_exit(1, "MCP required for design context.", "Ensure Figma Desktop or remote MCP is available.")
|
|
513
|
-
|
|
514
|
-
log("Fetching design context via MCP...")
|
|
515
|
-
args = {
|
|
516
|
-
"nodeId": node_id,
|
|
517
|
-
"disableCodeConnect": True,
|
|
518
|
-
}
|
|
519
|
-
texts, _ = mcp_call("get_design_context", args, file_key)
|
|
520
|
-
result = "\n".join(texts)
|
|
521
|
-
|
|
522
|
-
if output_path:
|
|
523
|
-
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
524
|
-
Path(output_path).write_text(result)
|
|
525
|
-
log(f"Design context saved: {output_path} ({len(result)} chars)")
|
|
526
|
-
|
|
527
|
-
print(result)
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
def cmd_code_connect(file_key, node_id, output_path=None):
|
|
531
|
-
"""Get Code Connect mappings for a node."""
|
|
532
|
-
if not MCP_MODE:
|
|
533
|
-
error_exit(1, "MCP required for Code Connect.", "Ensure Figma Desktop or remote MCP is available.")
|
|
534
|
-
|
|
535
|
-
log("Fetching Code Connect mappings via MCP...")
|
|
536
|
-
args = {"nodeId": node_id, "codeConnectLabel": "SwiftUI"}
|
|
537
|
-
texts, _ = mcp_call("get_code_connect_map", args, file_key)
|
|
538
|
-
result = "\n".join(texts)
|
|
539
|
-
|
|
540
|
-
# Try to structure the output
|
|
541
|
-
try:
|
|
542
|
-
cc_data = json.loads(result)
|
|
543
|
-
output = json.dumps({
|
|
544
|
-
"status": "ok",
|
|
545
|
-
"command": "code-connect",
|
|
546
|
-
"source": f"mcp-{MCP_MODE}",
|
|
547
|
-
"mappings": cc_data,
|
|
548
|
-
}, indent=2)
|
|
549
|
-
except json.JSONDecodeError:
|
|
550
|
-
output = result
|
|
551
|
-
|
|
552
|
-
if output_path:
|
|
553
|
-
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
554
|
-
Path(output_path).write_text(output)
|
|
555
|
-
log(f"Code Connect saved: {output_path}")
|
|
556
|
-
|
|
557
|
-
print(output)
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
def cmd_node(file_key, node_id, output_path=None, depth=None):
|
|
561
|
-
"""Get raw node data from REST API."""
|
|
562
|
-
log("Fetching node data via REST API...")
|
|
563
|
-
endpoint = f"files/{file_key}/nodes?ids={node_id}"
|
|
564
|
-
if depth is not None:
|
|
565
|
-
endpoint += f"&depth={depth}"
|
|
566
|
-
|
|
567
|
-
data = figma_get(endpoint)
|
|
568
|
-
output = json.dumps(data, indent=2)
|
|
569
|
-
|
|
570
|
-
if output_path:
|
|
571
|
-
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
572
|
-
Path(output_path).write_text(output)
|
|
573
|
-
log(f"Node data saved: {output_path} ({len(output)} chars)")
|
|
574
|
-
|
|
575
|
-
print(output)
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
def cmd_components(file_key, output_path=None):
|
|
579
|
-
"""List all components in a Figma file."""
|
|
580
|
-
log("Fetching components via REST API...")
|
|
581
|
-
data = figma_get(f"files/{file_key}/components")
|
|
582
|
-
|
|
583
|
-
components = data.get("meta", {}).get("components", [])
|
|
584
|
-
result = {
|
|
585
|
-
"status": "ok",
|
|
586
|
-
"command": "components",
|
|
587
|
-
"fileKey": file_key,
|
|
588
|
-
"count": len(components),
|
|
589
|
-
"components": [
|
|
590
|
-
{
|
|
591
|
-
"key": c.get("key"),
|
|
592
|
-
"name": c.get("name"),
|
|
593
|
-
"nodeId": c.get("node_id"),
|
|
594
|
-
"containingFrame": c.get("containing_frame", {}).get("name"),
|
|
595
|
-
"pageName": c.get("containing_frame", {}).get("pageName"),
|
|
596
|
-
}
|
|
597
|
-
for c in components
|
|
598
|
-
],
|
|
599
|
-
}
|
|
600
|
-
output = json.dumps(result, indent=2)
|
|
601
|
-
|
|
602
|
-
if output_path:
|
|
603
|
-
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
604
|
-
Path(output_path).write_text(output)
|
|
605
|
-
log(f"Components saved: {output_path}")
|
|
606
|
-
|
|
607
|
-
print(output)
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
def cmd_styles(file_key, output_path=None):
|
|
611
|
-
"""Get all styles in a Figma file."""
|
|
612
|
-
log("Fetching styles via REST API...")
|
|
613
|
-
data = figma_get(f"files/{file_key}/styles")
|
|
614
|
-
|
|
615
|
-
styles = data.get("meta", {}).get("styles", [])
|
|
616
|
-
result = {
|
|
617
|
-
"status": "ok",
|
|
618
|
-
"command": "styles",
|
|
619
|
-
"fileKey": file_key,
|
|
620
|
-
"count": len(styles),
|
|
621
|
-
"styles": [
|
|
622
|
-
{
|
|
623
|
-
"key": s.get("key"),
|
|
624
|
-
"name": s.get("name"),
|
|
625
|
-
"styleType": s.get("style_type"),
|
|
626
|
-
"nodeId": s.get("node_id"),
|
|
627
|
-
"description": s.get("description"),
|
|
628
|
-
}
|
|
629
|
-
for s in styles
|
|
630
|
-
],
|
|
631
|
-
}
|
|
632
|
-
output = json.dumps(result, indent=2)
|
|
633
|
-
|
|
634
|
-
if output_path:
|
|
635
|
-
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
636
|
-
Path(output_path).write_text(output)
|
|
637
|
-
log(f"Styles saved: {output_path}")
|
|
638
|
-
|
|
639
|
-
print(output)
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
def cmd_file(file_key, output_path=None, depth=None):
|
|
643
|
-
"""Get file document tree (summary)."""
|
|
644
|
-
log("Fetching file tree via REST API...")
|
|
645
|
-
endpoint = f"files/{file_key}"
|
|
646
|
-
if depth is not None:
|
|
647
|
-
endpoint += f"?depth={depth}"
|
|
648
|
-
|
|
649
|
-
data = figma_get(endpoint)
|
|
650
|
-
|
|
651
|
-
# Build summary
|
|
652
|
-
doc = data.get("document", {})
|
|
653
|
-
pages = []
|
|
654
|
-
for page in doc.get("children", []):
|
|
655
|
-
page_info = {
|
|
656
|
-
"name": page.get("name"),
|
|
657
|
-
"id": page.get("id"),
|
|
658
|
-
"childCount": len(page.get("children", [])),
|
|
659
|
-
}
|
|
660
|
-
pages.append(page_info)
|
|
661
|
-
|
|
662
|
-
result = {
|
|
663
|
-
"status": "ok",
|
|
664
|
-
"command": "file",
|
|
665
|
-
"fileName": data.get("name"),
|
|
666
|
-
"lastModified": data.get("lastModified"),
|
|
667
|
-
"version": data.get("version"),
|
|
668
|
-
"pageCount": len(pages),
|
|
669
|
-
"pages": pages,
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
# If output requested, save full data; otherwise print summary
|
|
673
|
-
if output_path:
|
|
674
|
-
full_output = json.dumps(data, indent=2)
|
|
675
|
-
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
676
|
-
Path(output_path).write_text(full_output)
|
|
677
|
-
log(f"File data saved: {output_path} ({len(full_output)} chars)")
|
|
678
|
-
print(json.dumps(result, indent=2))
|
|
679
|
-
else:
|
|
680
|
-
print(json.dumps(result, indent=2))
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
def cmd_whoami():
|
|
684
|
-
"""Check MCP auth status."""
|
|
685
|
-
if not MCP_MODE:
|
|
686
|
-
error_exit(1, "MCP not available.", "Ensure Figma Desktop or remote MCP is available.")
|
|
687
|
-
|
|
688
|
-
if MCP_MODE == "local":
|
|
689
|
-
print(json.dumps({
|
|
690
|
-
"status": "ok",
|
|
691
|
-
"command": "whoami",
|
|
692
|
-
"mode": "local",
|
|
693
|
-
"message": "Connected to local Figma MCP (Figma Desktop)",
|
|
694
|
-
"endpoint": MCP_URL,
|
|
695
|
-
}, indent=2))
|
|
696
|
-
return
|
|
697
|
-
|
|
698
|
-
# Remote: call whoami
|
|
699
|
-
log("Calling whoami on remote MCP...")
|
|
700
|
-
texts, _ = mcp_call("whoami", {})
|
|
701
|
-
result = "\n".join(texts)
|
|
702
|
-
try:
|
|
703
|
-
user_data = json.loads(result)
|
|
704
|
-
print(json.dumps({
|
|
705
|
-
"status": "ok",
|
|
706
|
-
"command": "whoami",
|
|
707
|
-
"mode": "remote",
|
|
708
|
-
"data": user_data,
|
|
709
|
-
}, indent=2))
|
|
710
|
-
except json.JSONDecodeError:
|
|
711
|
-
print(json.dumps({
|
|
712
|
-
"status": "ok",
|
|
713
|
-
"command": "whoami",
|
|
714
|
-
"mode": "remote",
|
|
715
|
-
"raw": result,
|
|
716
|
-
}, indent=2))
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
# ─── Main ────────────────────────────────────────────────────────────────────
|
|
720
|
-
|
|
721
|
-
def main():
|
|
722
|
-
parser = argparse.ArgumentParser(
|
|
723
|
-
description="Figma Utility - query Figma data via REST API and MCP",
|
|
724
|
-
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
725
|
-
)
|
|
726
|
-
parser.add_argument("command", choices=[
|
|
727
|
-
"screenshot", "metadata", "properties", "tokens", "context",
|
|
728
|
-
"code-connect", "node", "components", "styles", "file", "whoami",
|
|
729
|
-
])
|
|
730
|
-
parser.add_argument("--url", help="Figma URL")
|
|
731
|
-
parser.add_argument("--file-key", help="Figma file key (alternative to URL)")
|
|
732
|
-
parser.add_argument("--node-id", help="Node ID in colon format e.g. 123:456 (alternative to URL)")
|
|
733
|
-
parser.add_argument("--output", "-o", help="Save output to file path")
|
|
734
|
-
parser.add_argument("--scale", type=int, default=2, choices=[1, 2, 3, 4],
|
|
735
|
-
help="Screenshot scale (default: 2)")
|
|
736
|
-
parser.add_argument("--depth", type=int, help="Depth limit for node/file tree")
|
|
737
|
-
parser.add_argument("--figma-token", help="Figma REST API token (falls back to Keychain)")
|
|
738
|
-
parser.add_argument("--mcp-token", help="MCP OAuth token (falls back to Keychain)")
|
|
739
|
-
parser.add_argument("--mcp-url", default="http://127.0.0.1:3845/mcp",
|
|
740
|
-
help="Local MCP endpoint URL")
|
|
741
|
-
|
|
742
|
-
args = parser.parse_args()
|
|
743
|
-
|
|
744
|
-
# ── Resolve tokens ──
|
|
745
|
-
global FIGMA_TOKEN, MCP_TOKEN, MCP_URL
|
|
746
|
-
FIGMA_TOKEN = get_figma_token(args.figma_token)
|
|
747
|
-
MCP_TOKEN = get_mcp_token(args.mcp_token)
|
|
748
|
-
MCP_URL = args.mcp_url
|
|
749
|
-
|
|
750
|
-
# ── Parse URL or use explicit file-key/node-id ──
|
|
751
|
-
file_key = args.file_key
|
|
752
|
-
node_id = args.node_id
|
|
753
|
-
|
|
754
|
-
if args.url:
|
|
755
|
-
url_fk, url_nid = parse_figma_url(args.url)
|
|
756
|
-
file_key = file_key or url_fk
|
|
757
|
-
node_id = node_id or url_nid
|
|
758
|
-
|
|
759
|
-
# ── Validate required params ──
|
|
760
|
-
if args.command == "whoami":
|
|
761
|
-
# whoami doesn't need URL
|
|
762
|
-
pass
|
|
763
|
-
elif args.command in ("components", "styles", "file"):
|
|
764
|
-
# These only need file_key
|
|
765
|
-
if not file_key:
|
|
766
|
-
error_exit(1, "File key required.", "Provide --url or --file-key.")
|
|
767
|
-
else:
|
|
768
|
-
# All other commands need file_key + node_id
|
|
769
|
-
if not file_key:
|
|
770
|
-
error_exit(1, "File key required.", "Provide --url or --file-key.")
|
|
771
|
-
if not node_id:
|
|
772
|
-
error_exit(1, "Node ID required.", "Provide a URL with node-id or --node-id.")
|
|
773
|
-
|
|
774
|
-
# ── Init MCP for commands that need it ──
|
|
775
|
-
mcp_commands = {"metadata", "tokens", "context", "code-connect", "whoami", "screenshot"}
|
|
776
|
-
if args.command in mcp_commands:
|
|
777
|
-
mcp_ok = mcp_init()
|
|
778
|
-
if not mcp_ok and args.command not in ("screenshot", "tokens"):
|
|
779
|
-
error_exit(1, "MCP connection failed.",
|
|
780
|
-
"Ensure Figma Desktop is running (Dev Mode) or remote MCP token is available.")
|
|
781
|
-
|
|
782
|
-
# ── Dispatch ──
|
|
783
|
-
if args.command == "screenshot":
|
|
784
|
-
cmd_screenshot(file_key, node_id, args.output, args.scale)
|
|
785
|
-
elif args.command == "metadata":
|
|
786
|
-
cmd_metadata(file_key, node_id, args.output)
|
|
787
|
-
elif args.command == "properties":
|
|
788
|
-
cmd_properties(file_key, node_id, args.output)
|
|
789
|
-
elif args.command == "tokens":
|
|
790
|
-
cmd_tokens(file_key, node_id, args.output)
|
|
791
|
-
elif args.command == "context":
|
|
792
|
-
cmd_context(file_key, node_id, args.output)
|
|
793
|
-
elif args.command == "code-connect":
|
|
794
|
-
cmd_code_connect(file_key, node_id, args.output)
|
|
795
|
-
elif args.command == "node":
|
|
796
|
-
cmd_node(file_key, node_id, args.output, args.depth)
|
|
797
|
-
elif args.command == "components":
|
|
798
|
-
cmd_components(file_key, args.output)
|
|
799
|
-
elif args.command == "styles":
|
|
800
|
-
cmd_styles(file_key, args.output)
|
|
801
|
-
elif args.command == "file":
|
|
802
|
-
cmd_file(file_key, args.output, args.depth)
|
|
803
|
-
elif args.command == "whoami":
|
|
804
|
-
cmd_whoami()
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
if __name__ == "__main__":
|
|
808
|
-
main()
|