@ketecode/loom 0.0.0-stage → 0.1.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 +516 -2
- package/agents/accessibility-reviewer/AGENT.md +70 -0
- package/agents/accessibility-reviewer/evals/triggers.json +15 -0
- package/agents/agent-evaluator/AGENT.md +74 -0
- package/agents/agent-evaluator/evals/triggers.json +15 -0
- package/agents/architect/AGENT.md +82 -0
- package/agents/architect/evals/triggers.json +17 -0
- package/agents/billing-reviewer/AGENT.md +68 -0
- package/agents/billing-reviewer/evals/triggers.json +16 -0
- package/agents/code-reviewer/AGENT.md +51 -0
- package/agents/code-reviewer/evals/triggers.json +14 -0
- package/agents/code-simplifier/AGENT.md +74 -0
- package/agents/code-simplifier/evals/triggers.json +15 -0
- package/agents/cpp-build-resolver/AGENT.md +73 -0
- package/agents/cpp-build-resolver/evals/triggers.json +16 -0
- package/agents/cpp-reviewer/AGENT.md +59 -0
- package/agents/cpp-reviewer/evals/triggers.json +16 -0
- package/agents/csharp-build-resolver/AGENT.md +84 -0
- package/agents/csharp-build-resolver/evals/triggers.json +17 -0
- package/agents/csharp-reviewer/AGENT.md +88 -0
- package/agents/csharp-reviewer/evals/triggers.json +17 -0
- package/agents/dart-build-resolver/AGENT.md +80 -0
- package/agents/dart-build-resolver/evals/triggers.json +17 -0
- package/agents/dart-reviewer/AGENT.md +66 -0
- package/agents/dart-reviewer/evals/triggers.json +17 -0
- package/agents/database-reviewer/AGENT.md +72 -0
- package/agents/database-reviewer/evals/triggers.json +16 -0
- package/agents/django-reviewer/AGENT.md +61 -0
- package/agents/django-reviewer/evals/triggers.json +15 -0
- package/agents/doc-updater/AGENT.md +92 -0
- package/agents/doc-updater/evals/triggers.json +14 -0
- package/agents/e2e-runner/AGENT.md +88 -0
- package/agents/e2e-runner/evals/triggers.json +16 -0
- package/agents/fastapi-reviewer/AGENT.md +63 -0
- package/agents/fastapi-reviewer/evals/triggers.json +15 -0
- package/agents/flutter-reviewer/AGENT.md +70 -0
- package/agents/flutter-reviewer/evals/triggers.json +15 -0
- package/agents/fsharp-build-resolver/AGENT.md +81 -0
- package/agents/fsharp-build-resolver/evals/triggers.json +15 -0
- package/agents/fsharp-reviewer/AGENT.md +77 -0
- package/agents/fsharp-reviewer/evals/triggers.json +15 -0
- package/agents/go-build-resolver/AGENT.md +85 -0
- package/agents/go-build-resolver/evals/triggers.json +16 -0
- package/agents/go-reviewer/AGENT.md +66 -0
- package/agents/go-reviewer/evals/triggers.json +16 -0
- package/agents/healthcare-reviewer/AGENT.md +65 -0
- package/agents/healthcare-reviewer/evals/triggers.json +16 -0
- package/agents/i18n-reviewer/AGENT.md +64 -0
- package/agents/i18n-reviewer/evals/triggers.json +16 -0
- package/agents/infra-reviewer/AGENT.md +93 -0
- package/agents/infra-reviewer/evals/triggers.json +15 -0
- package/agents/java-build-resolver/AGENT.md +82 -0
- package/agents/java-build-resolver/evals/triggers.json +16 -0
- package/agents/java-reviewer/AGENT.md +64 -0
- package/agents/java-reviewer/evals/triggers.json +16 -0
- package/agents/kotlin-build-resolver/AGENT.md +89 -0
- package/agents/kotlin-build-resolver/evals/triggers.json +16 -0
- package/agents/kotlin-reviewer/AGENT.md +75 -0
- package/agents/kotlin-reviewer/evals/triggers.json +16 -0
- package/agents/mle-reviewer/AGENT.md +68 -0
- package/agents/mle-reviewer/evals/triggers.json +16 -0
- package/agents/network-config-reviewer/AGENT.md +68 -0
- package/agents/network-config-reviewer/evals/triggers.json +17 -0
- package/agents/payments-reviewer/AGENT.md +64 -0
- package/agents/payments-reviewer/evals/triggers.json +16 -0
- package/agents/performance-optimizer/AGENT.md +86 -0
- package/agents/performance-optimizer/evals/triggers.json +16 -0
- package/agents/perl-build-resolver/AGENT.md +84 -0
- package/agents/perl-build-resolver/evals/triggers.json +15 -0
- package/agents/perl-reviewer/AGENT.md +77 -0
- package/agents/perl-reviewer/evals/triggers.json +15 -0
- package/agents/php-build-resolver/AGENT.md +88 -0
- package/agents/php-build-resolver/evals/triggers.json +16 -0
- package/agents/php-reviewer/AGENT.md +61 -0
- package/agents/php-reviewer/evals/triggers.json +15 -0
- package/agents/planner/AGENT.md +75 -0
- package/agents/planner/evals/triggers.json +17 -0
- package/agents/pr-test-analyzer/AGENT.md +82 -0
- package/agents/pr-test-analyzer/evals/triggers.json +16 -0
- package/agents/python-build-resolver/AGENT.md +87 -0
- package/agents/python-build-resolver/evals/triggers.json +16 -0
- package/agents/python-reviewer/AGENT.md +63 -0
- package/agents/python-reviewer/evals/triggers.json +15 -0
- package/agents/rag-reviewer/AGENT.md +71 -0
- package/agents/rag-reviewer/evals/triggers.json +16 -0
- package/agents/react-reviewer/AGENT.md +70 -0
- package/agents/react-reviewer/evals/triggers.json +16 -0
- package/agents/refactor-cleaner/AGENT.md +82 -0
- package/agents/refactor-cleaner/evals/triggers.json +16 -0
- package/agents/ruby-build-resolver/AGENT.md +93 -0
- package/agents/ruby-build-resolver/evals/triggers.json +16 -0
- package/agents/ruby-reviewer/AGENT.md +62 -0
- package/agents/ruby-reviewer/evals/triggers.json +16 -0
- package/agents/rust-build-resolver/AGENT.md +98 -0
- package/agents/rust-build-resolver/evals/triggers.json +16 -0
- package/agents/rust-reviewer/AGENT.md +78 -0
- package/agents/rust-reviewer/evals/triggers.json +15 -0
- package/agents/security-reviewer/AGENT.md +84 -0
- package/agents/security-reviewer/evals/triggers.json +16 -0
- package/agents/silent-failure-hunter/AGENT.md +66 -0
- package/agents/silent-failure-hunter/evals/triggers.json +16 -0
- package/agents/solidity-reviewer/AGENT.md +70 -0
- package/agents/solidity-reviewer/evals/triggers.json +16 -0
- package/agents/swift-build-resolver/AGENT.md +85 -0
- package/agents/swift-build-resolver/evals/triggers.json +15 -0
- package/agents/swift-reviewer/AGENT.md +75 -0
- package/agents/swift-reviewer/evals/triggers.json +15 -0
- package/agents/typescript-build-resolver/AGENT.md +76 -0
- package/agents/typescript-build-resolver/evals/triggers.json +15 -0
- package/agents/typescript-reviewer/AGENT.md +76 -0
- package/agents/typescript-reviewer/evals/triggers.json +15 -0
- package/agents/vue-reviewer/AGENT.md +75 -0
- package/agents/vue-reviewer/evals/triggers.json +15 -0
- package/bin/kete-loom.js +683 -0
- package/commands/aside.md +19 -0
- package/commands/build-fix.md +26 -0
- package/commands/campaign.md +19 -0
- package/commands/changelog.md +36 -0
- package/commands/checkpoint.md +26 -0
- package/commands/codemap.md +25 -0
- package/commands/commit.md +26 -0
- package/commands/deps-audit.md +32 -0
- package/commands/epic-decompose.md +21 -0
- package/commands/explain.md +37 -0
- package/commands/feature-dev.md +22 -0
- package/commands/hookify.md +23 -0
- package/commands/i18n-check.md +39 -0
- package/commands/issue-sync.md +22 -0
- package/commands/jira.md +19 -0
- package/commands/learn.md +23 -0
- package/commands/loom.md +21 -0
- package/commands/loop.md +20 -0
- package/commands/model-route.md +27 -0
- package/commands/onboard.md +35 -0
- package/commands/orchestrate.md +21 -0
- package/commands/plan-feature.md +36 -0
- package/commands/postmortem.md +42 -0
- package/commands/pr-describe.md +39 -0
- package/commands/pr.md +22 -0
- package/commands/prd.md +41 -0
- package/commands/project-init.md +21 -0
- package/commands/refactor-clean.md +24 -0
- package/commands/resume-session.md +22 -0
- package/commands/review.md +35 -0
- package/commands/save-session.md +37 -0
- package/commands/security-scan.md +32 -0
- package/commands/sessions.md +21 -0
- package/commands/skill-create.md +21 -0
- package/commands/tdd.md +36 -0
- package/commands/test-coverage.md +24 -0
- package/commands/threat-model.md +36 -0
- package/commands/update-docs.md +25 -0
- package/commands/verify.md +21 -0
- package/hooks/block-dangerous-commands/HOOK.md +30 -0
- package/hooks/block-dangerous-commands/cases.json +50 -0
- package/hooks/block-dangerous-commands/hook.js +75 -0
- package/hooks/block-no-verify/HOOK.md +25 -0
- package/hooks/block-no-verify/cases.json +30 -0
- package/hooks/block-no-verify/hook.js +43 -0
- package/hooks/format-on-edit/HOOK.md +24 -0
- package/hooks/format-on-edit/hook.js +89 -0
- package/hooks/guard-secrets/HOOK.md +19 -0
- package/hooks/guard-secrets/cases.json +110 -0
- package/hooks/guard-secrets/hook.js +44 -0
- package/hooks/load-session-context/HOOK.md +20 -0
- package/hooks/load-session-context/hook.js +56 -0
- package/hooks/protect-files/HOOK.md +24 -0
- package/hooks/protect-files/cases.json +27 -0
- package/hooks/protect-files/hook.js +39 -0
- package/hooks/run.js +132 -0
- package/mcp/atlassian.json +11 -0
- package/mcp/chrome-devtools.json +18 -0
- package/mcp/cloudflare.json +11 -0
- package/mcp/context7.json +11 -0
- package/mcp/fetch.json +17 -0
- package/mcp/figma.json +11 -0
- package/mcp/git.json +17 -0
- package/mcp/github.json +11 -0
- package/mcp/linear.json +11 -0
- package/mcp/memory.json +18 -0
- package/mcp/notion.json +11 -0
- package/mcp/playwright.json +18 -0
- package/mcp/sentry.json +11 -0
- package/mcp/stripe.json +11 -0
- package/mcp/supabase.json +11 -0
- package/mcp/time.json +17 -0
- package/package.json +59 -4
- package/profiles.json +165 -0
- package/rules/common.md +21 -0
- package/rules/cpp.md +29 -0
- package/rules/csharp.md +25 -0
- package/rules/dart.md +29 -0
- package/rules/fsharp.md +28 -0
- package/rules/git.md +26 -0
- package/rules/go.md +29 -0
- package/rules/java.md +29 -0
- package/rules/kotlin.md +27 -0
- package/rules/perl.md +26 -0
- package/rules/php.md +28 -0
- package/rules/python.md +30 -0
- package/rules/ruby.md +27 -0
- package/rules/rust.md +26 -0
- package/rules/security.md +29 -0
- package/rules/swift.md +28 -0
- package/rules/testing.md +25 -0
- package/rules/typescript.md +29 -0
- package/schemas/skill-frontmatter.schema.json +52 -0
- package/scripts/lib/audit.js +96 -0
- package/scripts/lib/frontmatter.js +65 -0
- package/scripts/lib/hooks-install.js +292 -0
- package/scripts/lib/i18n.js +51 -0
- package/scripts/lib/inventory.js +85 -0
- package/scripts/lib/lint.js +158 -0
- package/scripts/lib/mcp-install.js +284 -0
- package/scripts/lib/profiles.js +128 -0
- package/scripts/lib/render.js +86 -0
- package/scripts/lib/rules-install.js +118 -0
- package/scripts/lib/skills.js +86 -0
- package/scripts/lib/targets.js +113 -0
- package/skills/accounting-api-integration/SKILL.md +81 -0
- package/skills/accounting-api-integration/evals/triggers.json +17 -0
- package/skills/accounting-api-integration/references/quickbooks.md +48 -0
- package/skills/accounting-api-integration/references/xero.md +45 -0
- package/skills/agent-harness-design/SKILL.md +125 -0
- package/skills/agent-harness-design/evals/triggers.json +21 -0
- package/skills/ai-media-generation/SKILL.md +89 -0
- package/skills/ai-media-generation/evals/triggers.json +16 -0
- package/skills/ai-media-generation/references/job-pipeline.md +76 -0
- package/skills/angular-patterns/SKILL.md +122 -0
- package/skills/angular-patterns/evals/triggers.json +16 -0
- package/skills/api-design/SKILL.md +96 -0
- package/skills/api-design/evals/triggers.json +18 -0
- package/skills/api-design/references/conventions.md +133 -0
- package/skills/architecture-decision-records/SKILL.md +98 -0
- package/skills/architecture-decision-records/evals/triggers.json +17 -0
- package/skills/article-writing/SKILL.md +108 -0
- package/skills/article-writing/evals/triggers.json +16 -0
- package/skills/aspnet-core-patterns/SKILL.md +149 -0
- package/skills/aspnet-core-patterns/evals/triggers.json +16 -0
- package/skills/auth-patterns/SKILL.md +104 -0
- package/skills/auth-patterns/evals/triggers.json +17 -0
- package/skills/auth-patterns/references/oauth-oidc.md +64 -0
- package/skills/autonomous-loops/SKILL.md +99 -0
- package/skills/autonomous-loops/evals/triggers.json +14 -0
- package/skills/aws-patterns/SKILL.md +91 -0
- package/skills/aws-patterns/evals/triggers.json +16 -0
- package/skills/azure-patterns/SKILL.md +86 -0
- package/skills/azure-patterns/evals/triggers.json +16 -0
- package/skills/benchmark-methodology/SKILL.md +88 -0
- package/skills/benchmark-methodology/evals/triggers.json +16 -0
- package/skills/benchmark-methodology/references/language-harnesses.md +130 -0
- package/skills/bgp-diagnostics/SKILL.md +84 -0
- package/skills/bgp-diagnostics/evals/triggers.json +18 -0
- package/skills/bgp-diagnostics/references/bgp-reference.md +81 -0
- package/skills/bioinformatics-workflows/SKILL.md +108 -0
- package/skills/bioinformatics-workflows/evals/triggers.json +16 -0
- package/skills/bioinformatics-workflows/references/commands.md +71 -0
- package/skills/bioinformatics-workflows/references/formats.md +24 -0
- package/skills/bioinformatics-workflows/references/workflow-managers.md +107 -0
- package/skills/brand-voice/SKILL.md +112 -0
- package/skills/brand-voice/evals/triggers.json +16 -0
- package/skills/browser-qa/SKILL.md +99 -0
- package/skills/browser-qa/evals/triggers.json +16 -0
- package/skills/bun-runtime/SKILL.md +93 -0
- package/skills/bun-runtime/evals/triggers.json +16 -0
- package/skills/cisco-ios-config/SKILL.md +101 -0
- package/skills/cisco-ios-config/evals/triggers.json +18 -0
- package/skills/cisco-ios-config/references/verification.md +63 -0
- package/skills/clickhouse-patterns/SKILL.md +101 -0
- package/skills/clickhouse-patterns/evals/triggers.json +18 -0
- package/skills/clickhouse-patterns/references/engines-and-views.md +138 -0
- package/skills/clinical-decision-support/SKILL.md +90 -0
- package/skills/clinical-decision-support/evals/triggers.json +16 -0
- package/skills/clinical-decision-support/references/cds-hooks.md +76 -0
- package/skills/clinical-decision-support/references/regulatory-boundaries.md +37 -0
- package/skills/code-tour/SKILL.md +97 -0
- package/skills/code-tour/evals/triggers.json +14 -0
- package/skills/codebase-onboarding/SKILL.md +112 -0
- package/skills/codebase-onboarding/evals/triggers.json +14 -0
- package/skills/cold-outreach-sequences/SKILL.md +90 -0
- package/skills/cold-outreach-sequences/evals/triggers.json +16 -0
- package/skills/content-repurposing/SKILL.md +82 -0
- package/skills/content-repurposing/evals/triggers.json +16 -0
- package/skills/context-budget/SKILL.md +84 -0
- package/skills/context-budget/evals/triggers.json +14 -0
- package/skills/conventional-commits/SKILL.md +144 -0
- package/skills/conventional-commits/evals/triggers.json +16 -0
- package/skills/cpp-patterns/SKILL.md +129 -0
- package/skills/cpp-patterns/evals/triggers.json +16 -0
- package/skills/cpp-patterns/references/cmake-and-tooling.md +88 -0
- package/skills/cpp-patterns/references/undefined-behaviour.md +75 -0
- package/skills/cpp-testing/SKILL.md +131 -0
- package/skills/cpp-testing/evals/triggers.json +16 -0
- package/skills/csharp-patterns/SKILL.md +110 -0
- package/skills/csharp-patterns/evals/triggers.json +17 -0
- package/skills/csharp-patterns/references/analyzers.md +59 -0
- package/skills/csharp-testing/SKILL.md +85 -0
- package/skills/csharp-testing/evals/triggers.json +17 -0
- package/skills/csharp-testing/references/integration-tests.md +77 -0
- package/skills/css-architecture/SKILL.md +143 -0
- package/skills/css-architecture/evals/triggers.json +16 -0
- package/skills/customs-trade-compliance/SKILL.md +95 -0
- package/skills/customs-trade-compliance/evals/triggers.json +16 -0
- package/skills/customs-trade-compliance/references/incoterms-2020.md +42 -0
- package/skills/dart-patterns/SKILL.md +157 -0
- package/skills/dart-patterns/evals/triggers.json +18 -0
- package/skills/dart-testing/SKILL.md +164 -0
- package/skills/dart-testing/evals/triggers.json +18 -0
- package/skills/data-analysis-notebooks/SKILL.md +113 -0
- package/skills/data-analysis-notebooks/evals/triggers.json +16 -0
- package/skills/data-analysis-notebooks/references/dataframe-pitfalls.md +49 -0
- package/skills/data-analysis-notebooks/references/statistics-pitfalls.md +24 -0
- package/skills/data-pipelines/SKILL.md +90 -0
- package/skills/data-pipelines/evals/triggers.json +16 -0
- package/skills/data-pipelines/references/orchestrators.md +64 -0
- package/skills/database-migrations/SKILL.md +102 -0
- package/skills/database-migrations/evals/triggers.json +16 -0
- package/skills/database-migrations/references/recipes.md +98 -0
- package/skills/database-migrations/references/tools.md +21 -0
- package/skills/dbt-patterns/SKILL.md +96 -0
- package/skills/dbt-patterns/evals/triggers.json +16 -0
- package/skills/dbt-patterns/references/yaml-examples.md +114 -0
- package/skills/deep-research/SKILL.md +107 -0
- package/skills/deep-research/evals/triggers.json +14 -0
- package/skills/dependency-audit/SKILL.md +103 -0
- package/skills/dependency-audit/evals/triggers.json +16 -0
- package/skills/dependency-audit/references/tools.md +124 -0
- package/skills/deployment-strategies/SKILL.md +108 -0
- package/skills/deployment-strategies/evals/triggers.json +16 -0
- package/skills/django-patterns/SKILL.md +123 -0
- package/skills/django-patterns/evals/triggers.json +16 -0
- package/skills/docker-patterns/SKILL.md +132 -0
- package/skills/docker-patterns/evals/triggers.json +16 -0
- package/skills/docker-patterns/references/dockerfiles.md +105 -0
- package/skills/e2e-testing-playwright/SKILL.md +94 -0
- package/skills/e2e-testing-playwright/evals/triggers.json +16 -0
- package/skills/e2e-testing-playwright/references/config-and-ci.md +129 -0
- package/skills/ef-core-patterns/SKILL.md +117 -0
- package/skills/ef-core-patterns/evals/triggers.json +16 -0
- package/skills/email-deliverability/SKILL.md +99 -0
- package/skills/email-deliverability/evals/triggers.json +17 -0
- package/skills/email-deliverability/references/dns-records.md +85 -0
- package/skills/error-handling-patterns/SKILL.md +112 -0
- package/skills/error-handling-patterns/evals/triggers.json +18 -0
- package/skills/error-handling-patterns/references/language-idioms.md +49 -0
- package/skills/estimation-and-scoping/SKILL.md +97 -0
- package/skills/estimation-and-scoping/evals/triggers.json +14 -0
- package/skills/evm-dapp-integration/SKILL.md +73 -0
- package/skills/evm-dapp-integration/evals/triggers.json +16 -0
- package/skills/fastapi-patterns/SKILL.md +127 -0
- package/skills/fastapi-patterns/evals/triggers.json +16 -0
- package/skills/ffmpeg-media-processing/SKILL.md +94 -0
- package/skills/ffmpeg-media-processing/evals/triggers.json +16 -0
- package/skills/ffmpeg-media-processing/references/recipes.md +112 -0
- package/skills/fhir-integration/SKILL.md +81 -0
- package/skills/fhir-integration/evals/triggers.json +16 -0
- package/skills/fhir-integration/references/search-and-bundles.md +73 -0
- package/skills/fhir-integration/references/smart-on-fhir.md +50 -0
- package/skills/flask-patterns/SKILL.md +132 -0
- package/skills/flask-patterns/evals/triggers.json +16 -0
- package/skills/flutter-patterns/SKILL.md +99 -0
- package/skills/flutter-patterns/evals/triggers.json +16 -0
- package/skills/flutterwave-integration/SKILL.md +75 -0
- package/skills/flutterwave-integration/evals/triggers.json +16 -0
- package/skills/flutterwave-integration/references/api.md +73 -0
- package/skills/flutterwave-integration/references/webhooks.md +49 -0
- package/skills/fsharp-patterns/SKILL.md +133 -0
- package/skills/fsharp-patterns/evals/triggers.json +16 -0
- package/skills/fsharp-testing/SKILL.md +136 -0
- package/skills/fsharp-testing/evals/triggers.json +16 -0
- package/skills/gcp-patterns/SKILL.md +86 -0
- package/skills/gcp-patterns/evals/triggers.json +16 -0
- package/skills/git-workflow/SKILL.md +114 -0
- package/skills/git-workflow/evals/triggers.json +16 -0
- package/skills/github-actions-ci/SKILL.md +117 -0
- package/skills/github-actions-ci/evals/triggers.json +16 -0
- package/skills/github-api-automation/SKILL.md +101 -0
- package/skills/github-api-automation/evals/triggers.json +17 -0
- package/skills/go-patterns/SKILL.md +118 -0
- package/skills/go-patterns/evals/triggers.json +17 -0
- package/skills/go-patterns/references/concurrency.md +162 -0
- package/skills/go-testing/SKILL.md +154 -0
- package/skills/go-testing/evals/triggers.json +17 -0
- package/skills/hexagonal-architecture/SKILL.md +89 -0
- package/skills/hexagonal-architecture/evals/triggers.json +17 -0
- package/skills/hipaa-compliance/SKILL.md +91 -0
- package/skills/hipaa-compliance/evals/triggers.json +18 -0
- package/skills/hipaa-compliance/references/safeguards.md +68 -0
- package/skills/hl7v2-integration/SKILL.md +69 -0
- package/skills/hl7v2-integration/evals/triggers.json +16 -0
- package/skills/hl7v2-integration/references/messages-and-ack.md +78 -0
- package/skills/homelab-network-setup/SKILL.md +125 -0
- package/skills/homelab-network-setup/evals/triggers.json +18 -0
- package/skills/homelab-network-setup/references/firewall-and-dns.md +45 -0
- package/skills/homelab-network-setup/references/wireguard.md +72 -0
- package/skills/hubtel-integration/SKILL.md +77 -0
- package/skills/hubtel-integration/evals/triggers.json +16 -0
- package/skills/hubtel-integration/references/api.md +92 -0
- package/skills/i18n-localization/SKILL.md +114 -0
- package/skills/i18n-localization/evals/triggers.json +16 -0
- package/skills/i18n-localization/references/libraries.md +52 -0
- package/skills/incident-postmortem/SKILL.md +118 -0
- package/skills/incident-postmortem/evals/triggers.json +14 -0
- package/skills/inventory-demand-planning/SKILL.md +89 -0
- package/skills/inventory-demand-planning/evals/triggers.json +16 -0
- package/skills/inventory-demand-planning/references/formulas.md +62 -0
- package/skills/investor-materials/SKILL.md +127 -0
- package/skills/investor-materials/evals/triggers.json +16 -0
- package/skills/investor-materials/references/templates.md +74 -0
- package/skills/invoicing-and-tax/SKILL.md +84 -0
- package/skills/invoicing-and-tax/evals/triggers.json +17 -0
- package/skills/invoicing-and-tax/references/e-invoicing.md +24 -0
- package/skills/invoicing-and-tax/references/invoice-fields.md +38 -0
- package/skills/invoicing-and-tax/references/rounding.md +26 -0
- package/skills/java-patterns/SKILL.md +141 -0
- package/skills/java-patterns/evals/triggers.json +17 -0
- package/skills/java-testing/SKILL.md +142 -0
- package/skills/java-testing/evals/triggers.json +16 -0
- package/skills/jetpack-compose-patterns/SKILL.md +88 -0
- package/skills/jetpack-compose-patterns/evals/triggers.json +16 -0
- package/skills/jira-integration/SKILL.md +92 -0
- package/skills/jira-integration/evals/triggers.json +17 -0
- package/skills/jpa-hibernate/SKILL.md +92 -0
- package/skills/jpa-hibernate/evals/triggers.json +18 -0
- package/skills/jpa-hibernate/references/mapping.md +99 -0
- package/skills/kotlin-multiplatform/SKILL.md +97 -0
- package/skills/kotlin-multiplatform/evals/triggers.json +16 -0
- package/skills/kotlin-patterns/SKILL.md +128 -0
- package/skills/kotlin-patterns/evals/triggers.json +16 -0
- package/skills/kotlin-testing/SKILL.md +117 -0
- package/skills/kotlin-testing/evals/triggers.json +16 -0
- package/skills/ktor-patterns/SKILL.md +135 -0
- package/skills/ktor-patterns/evals/triggers.json +18 -0
- package/skills/kubernetes-patterns/SKILL.md +106 -0
- package/skills/kubernetes-patterns/evals/triggers.json +16 -0
- package/skills/kubernetes-patterns/references/manifests.md +115 -0
- package/skills/laravel-patterns/SKILL.md +136 -0
- package/skills/laravel-patterns/evals/triggers.json +16 -0
- package/skills/linear-integration/SKILL.md +98 -0
- package/skills/linear-integration/evals/triggers.json +17 -0
- package/skills/literature-review/SKILL.md +109 -0
- package/skills/literature-review/evals/triggers.json +16 -0
- package/skills/literature-review/references/appraisal.md +41 -0
- package/skills/literature-review/references/search-sources.md +39 -0
- package/skills/llm-app-patterns/SKILL.md +97 -0
- package/skills/llm-app-patterns/evals/triggers.json +17 -0
- package/skills/llm-evals/SKILL.md +98 -0
- package/skills/llm-evals/evals/triggers.json +16 -0
- package/skills/llm-evals/references/graders.md +66 -0
- package/skills/logistics-exception-management/SKILL.md +90 -0
- package/skills/logistics-exception-management/evals/triggers.json +16 -0
- package/skills/manim-animations/SKILL.md +106 -0
- package/skills/manim-animations/evals/triggers.json +16 -0
- package/skills/market-research/SKILL.md +117 -0
- package/skills/market-research/evals/triggers.json +16 -0
- package/skills/marketing-campaign/SKILL.md +111 -0
- package/skills/marketing-campaign/evals/triggers.json +16 -0
- package/skills/mcp-server-patterns/SKILL.md +127 -0
- package/skills/mcp-server-patterns/evals/triggers.json +18 -0
- package/skills/mcp-server-patterns/references/protocol-notes.md +68 -0
- package/skills/metrics-dashboards/SKILL.md +113 -0
- package/skills/metrics-dashboards/evals/triggers.json +16 -0
- package/skills/ml-experiment-workflow/SKILL.md +125 -0
- package/skills/ml-experiment-workflow/evals/triggers.json +16 -0
- package/skills/mongodb-patterns/SKILL.md +115 -0
- package/skills/mongodb-patterns/evals/triggers.json +18 -0
- package/skills/mongodb-patterns/references/indexes-and-aggregation.md +94 -0
- package/skills/mpesa-daraja-integration/SKILL.md +70 -0
- package/skills/mpesa-daraja-integration/evals/triggers.json +16 -0
- package/skills/mpesa-daraja-integration/references/api.md +103 -0
- package/skills/mpesa-daraja-integration/references/callbacks.md +74 -0
- package/skills/mtn-momo-integration/SKILL.md +66 -0
- package/skills/mtn-momo-integration/evals/triggers.json +16 -0
- package/skills/mtn-momo-integration/references/api.md +153 -0
- package/skills/mtn-momo-integration/references/callbacks.md +92 -0
- package/skills/mtn-momo-integration/references/sandbox-and-go-live.md +51 -0
- package/skills/mtn-momo-integration/scripts/sandbox-setup.js +79 -0
- package/skills/mysql-patterns/SKILL.md +96 -0
- package/skills/mysql-patterns/evals/triggers.json +16 -0
- package/skills/mysql-patterns/references/explain.md +34 -0
- package/skills/mysql-patterns/references/online-ddl.md +41 -0
- package/skills/nestjs-patterns/SKILL.md +114 -0
- package/skills/nestjs-patterns/evals/triggers.json +16 -0
- package/skills/nestjs-patterns/references/testing.md +68 -0
- package/skills/network-automation/SKILL.md +107 -0
- package/skills/network-automation/evals/triggers.json +18 -0
- package/skills/network-automation/references/tool-snippets.md +153 -0
- package/skills/network-troubleshooting/SKILL.md +152 -0
- package/skills/network-troubleshooting/evals/triggers.json +18 -0
- package/skills/network-troubleshooting/references/commands.md +33 -0
- package/skills/nextjs-patterns/SKILL.md +150 -0
- package/skills/nextjs-patterns/evals/triggers.json +18 -0
- package/skills/nextjs-patterns/references/caching.md +87 -0
- package/skills/node-backend-patterns/SKILL.md +103 -0
- package/skills/node-backend-patterns/evals/triggers.json +16 -0
- package/skills/node-backend-patterns/references/templates.md +122 -0
- package/skills/nuxt-patterns/SKILL.md +103 -0
- package/skills/nuxt-patterns/evals/triggers.json +16 -0
- package/skills/parallel-agent-orchestration/SKILL.md +106 -0
- package/skills/parallel-agent-orchestration/evals/triggers.json +14 -0
- package/skills/paystack-integration/SKILL.md +59 -0
- package/skills/paystack-integration/evals/triggers.json +14 -0
- package/skills/paystack-integration/references/api.md +73 -0
- package/skills/paystack-integration/references/mobile-money.md +50 -0
- package/skills/paystack-integration/references/webhooks.md +80 -0
- package/skills/pci-dss-scope/SKILL.md +103 -0
- package/skills/pci-dss-scope/evals/triggers.json +18 -0
- package/skills/pci-dss-scope/references/saq-and-data.md +36 -0
- package/skills/perl-patterns/SKILL.md +135 -0
- package/skills/perl-patterns/evals/triggers.json +18 -0
- package/skills/perl-testing/SKILL.md +126 -0
- package/skills/perl-testing/evals/triggers.json +17 -0
- package/skills/php-patterns/SKILL.md +116 -0
- package/skills/php-patterns/evals/triggers.json +16 -0
- package/skills/php-testing/SKILL.md +123 -0
- package/skills/php-testing/evals/triggers.json +16 -0
- package/skills/postgres-patterns/SKILL.md +103 -0
- package/skills/postgres-patterns/evals/triggers.json +17 -0
- package/skills/postgres-patterns/references/explain.md +59 -0
- package/skills/postgres-patterns/references/locks.md +53 -0
- package/skills/prd-writing/SKILL.md +124 -0
- package/skills/prd-writing/evals/triggers.json +15 -0
- package/skills/prisma-patterns/SKILL.md +83 -0
- package/skills/prisma-patterns/evals/triggers.json +18 -0
- package/skills/prisma-patterns/references/prisma-8.md +46 -0
- package/skills/prisma-patterns/references/workflow.md +50 -0
- package/skills/privacy-compliance/SKILL.md +121 -0
- package/skills/privacy-compliance/evals/triggers.json +18 -0
- package/skills/privacy-compliance/references/jurisdictions.md +51 -0
- package/skills/production-scheduling/SKILL.md +98 -0
- package/skills/production-scheduling/evals/triggers.json +16 -0
- package/skills/prompt-engineering/SKILL.md +92 -0
- package/skills/prompt-engineering/evals/triggers.json +16 -0
- package/skills/python-patterns/SKILL.md +107 -0
- package/skills/python-patterns/evals/triggers.json +16 -0
- package/skills/python-patterns/references/tooling.md +101 -0
- package/skills/python-testing/SKILL.md +126 -0
- package/skills/python-testing/evals/triggers.json +16 -0
- package/skills/python-testing/references/mocking.md +71 -0
- package/skills/pytorch-patterns/SKILL.md +139 -0
- package/skills/pytorch-patterns/evals/triggers.json +16 -0
- package/skills/pytorch-patterns/references/debugging.md +39 -0
- package/skills/quarkus-patterns/SKILL.md +93 -0
- package/skills/quarkus-patterns/evals/triggers.json +18 -0
- package/skills/rag-pipeline-patterns/SKILL.md +117 -0
- package/skills/rag-pipeline-patterns/evals/triggers.json +18 -0
- package/skills/rag-pipeline-patterns/references/evaluation.md +37 -0
- package/skills/rag-pipeline-patterns/references/retrieval-recipes.md +78 -0
- package/skills/rails-patterns/SKILL.md +132 -0
- package/skills/rails-patterns/evals/triggers.json +16 -0
- package/skills/react-native-patterns/SKILL.md +101 -0
- package/skills/react-native-patterns/evals/triggers.json +16 -0
- package/skills/react-patterns/SKILL.md +140 -0
- package/skills/react-patterns/evals/triggers.json +18 -0
- package/skills/react-performance/SKILL.md +107 -0
- package/skills/react-performance/evals/triggers.json +18 -0
- package/skills/redis-patterns/SKILL.md +109 -0
- package/skills/redis-patterns/evals/triggers.json +18 -0
- package/skills/redis-patterns/references/operations.md +61 -0
- package/skills/refactoring-safely/SKILL.md +107 -0
- package/skills/refactoring-safely/evals/triggers.json +17 -0
- package/skills/release-management/SKILL.md +141 -0
- package/skills/release-management/evals/triggers.json +16 -0
- package/skills/remotion-video/SKILL.md +91 -0
- package/skills/remotion-video/evals/triggers.json +16 -0
- package/skills/remotion-video/references/lambda.md +66 -0
- package/skills/returns-reverse-logistics/SKILL.md +97 -0
- package/skills/returns-reverse-logistics/evals/triggers.json +16 -0
- package/skills/ruby-patterns/SKILL.md +127 -0
- package/skills/ruby-patterns/evals/triggers.json +18 -0
- package/skills/ruby-testing/SKILL.md +113 -0
- package/skills/ruby-testing/evals/triggers.json +18 -0
- package/skills/runbook-writing/SKILL.md +101 -0
- package/skills/runbook-writing/evals/triggers.json +14 -0
- package/skills/rust-patterns/SKILL.md +118 -0
- package/skills/rust-patterns/evals/triggers.json +16 -0
- package/skills/rust-patterns/references/cargo-and-clippy.md +105 -0
- package/skills/rust-patterns/references/errors-and-traits.md +97 -0
- package/skills/rust-testing/SKILL.md +148 -0
- package/skills/rust-testing/evals/triggers.json +16 -0
- package/skills/rust-testing/references/fakes-proptest-async.md +113 -0
- package/skills/search-first/SKILL.md +73 -0
- package/skills/search-first/evals/triggers.json +14 -0
- package/skills/security-review/SKILL.md +58 -0
- package/skills/security-review/evals/triggers.json +14 -0
- package/skills/security-review/references/checklist.md +54 -0
- package/skills/security-review/scripts/scan-secrets.js +79 -0
- package/skills/seo-audit/SKILL.md +111 -0
- package/skills/seo-audit/evals/triggers.json +16 -0
- package/skills/seo-audit/references/checks.md +65 -0
- package/skills/session-handoff/SKILL.md +101 -0
- package/skills/session-handoff/evals/triggers.json +14 -0
- package/skills/skill-authoring/SKILL.md +87 -0
- package/skills/skill-authoring/evals/triggers.json +14 -0
- package/skills/skill-authoring/references/frontmatter.md +40 -0
- package/skills/slack-app-integration/SKILL.md +108 -0
- package/skills/slack-app-integration/evals/triggers.json +17 -0
- package/skills/smart-contract-security/SKILL.md +110 -0
- package/skills/smart-contract-security/evals/triggers.json +16 -0
- package/skills/smart-contract-security/references/vulnerability-checklist.md +79 -0
- package/skills/social-media-publishing/SKILL.md +84 -0
- package/skills/social-media-publishing/evals/triggers.json +17 -0
- package/skills/social-media-publishing/references/platform-apis.md +116 -0
- package/skills/solidity-patterns/SKILL.md +95 -0
- package/skills/solidity-patterns/evals/triggers.json +16 -0
- package/skills/spring-boot-patterns/SKILL.md +128 -0
- package/skills/spring-boot-patterns/evals/triggers.json +18 -0
- package/skills/spring-boot-patterns/references/testing.md +72 -0
- package/skills/stripe-integration/SKILL.md +69 -0
- package/skills/stripe-integration/evals/triggers.json +16 -0
- package/skills/stripe-integration/references/api.md +71 -0
- package/skills/stripe-integration/references/billing.md +58 -0
- package/skills/stripe-integration/references/webhooks.md +76 -0
- package/skills/subscription-billing-ops/SKILL.md +118 -0
- package/skills/subscription-billing-ops/evals/triggers.json +17 -0
- package/skills/subscription-billing-ops/references/proration.md +47 -0
- package/skills/subscription-billing-ops/references/revenue-metrics.md +38 -0
- package/skills/svelte-patterns/SKILL.md +122 -0
- package/skills/svelte-patterns/evals/triggers.json +16 -0
- package/skills/swift-patterns/SKILL.md +113 -0
- package/skills/swift-patterns/evals/triggers.json +16 -0
- package/skills/swift-patterns/references/concurrency-migration.md +40 -0
- package/skills/swift-testing/SKILL.md +121 -0
- package/skills/swift-testing/evals/triggers.json +16 -0
- package/skills/swift-testing/references/swift-testing-cheatsheet.md +81 -0
- package/skills/swiftui-patterns/SKILL.md +102 -0
- package/skills/swiftui-patterns/evals/triggers.json +16 -0
- package/skills/tdd-workflow/SKILL.md +67 -0
- package/skills/tdd-workflow/evals/triggers.json +14 -0
- package/skills/technical-writing/SKILL.md +95 -0
- package/skills/technical-writing/evals/triggers.json +14 -0
- package/skills/terraform-patterns/SKILL.md +122 -0
- package/skills/terraform-patterns/evals/triggers.json +16 -0
- package/skills/terraform-patterns/references/backends.md +67 -0
- package/skills/test-coverage-improvement/SKILL.md +107 -0
- package/skills/test-coverage-improvement/evals/triggers.json +16 -0
- package/skills/threat-modeling/SKILL.md +113 -0
- package/skills/threat-modeling/evals/triggers.json +16 -0
- package/skills/typescript-patterns/SKILL.md +122 -0
- package/skills/typescript-patterns/evals/triggers.json +16 -0
- package/skills/typescript-testing/SKILL.md +101 -0
- package/skills/typescript-testing/evals/triggers.json +16 -0
- package/skills/verification-loop/SKILL.md +70 -0
- package/skills/verification-loop/evals/triggers.json +14 -0
- package/skills/verification-loop/scripts/detect-checks.js +102 -0
- package/skills/vite-patterns/SKILL.md +117 -0
- package/skills/vite-patterns/evals/triggers.json +16 -0
- package/skills/vue-patterns/SKILL.md +104 -0
- package/skills/vue-patterns/evals/triggers.json +16 -0
- package/skills/web-accessibility/SKILL.md +119 -0
- package/skills/web-accessibility/evals/triggers.json +16 -0
- package/skills/web-performance/SKILL.md +107 -0
- package/skills/web-performance/evals/triggers.json +16 -0
- package/skills/web-scraping/SKILL.md +80 -0
- package/skills/web-scraping/evals/triggers.json +16 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ffmpeg-media-processing
|
|
3
|
+
description: FFmpeg and ffprobe command lines for audio and video work - transcoding with libx264, libx265, libvpx-vp9, AAC and Opus, choosing containers, stream copy versus re-encode, frame-accurate trimming, concat demuxer joins, scale and pad filters, burned-in or soft subtitles, thumbnails and contact sheets, HLS VOD ladders, NVENC, VideoToolbox, QSV and VAAPI hardware encoding, and calling ffmpeg safely from code with argument arrays. Use when writing or debugging an ffmpeg command, converting or compressing media files, cutting or joining clips, producing HLS segments, or running ffmpeg on user uploads in a backend.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs ffmpeg and ffprobe on PATH; hardware encoders need a matching GPU, driver and an ffmpeg build that includes them.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: coding
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/requires: "ffmpeg, ffprobe"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# FFmpeg media processing
|
|
15
|
+
|
|
16
|
+
Probe first, decide copy or re-encode, write the smallest command that does it, then probe the output. Ready-made recipes are in `references/recipes.md`.
|
|
17
|
+
|
|
18
|
+
## 0. Probe the input
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
ffprobe -v error -show_entries format=duration,bit_rate:stream=index,codec_type,codec_name,width,height,r_frame_rate,pix_fmt,channels,sample_rate -of json input.mp4
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Record: container, video codec, resolution, frame rate (variable?), pixel format (10-bit `yuv420p10le`?), audio codec and channels, subtitle streams, duration. Check `ffmpeg -hide_banner -encoders | grep -E "264|265|vp9|av1|nvenc|videotoolbox|qsv|vaapi"` to see what this build can encode.
|
|
25
|
+
|
|
26
|
+
## 1. Copy or re-encode
|
|
27
|
+
|
|
28
|
+
| Situation | Do |
|
|
29
|
+
|---|---|
|
|
30
|
+
| Only changing the container (MKV to MP4) and codecs are allowed in the target | `-c copy` (seconds, no quality loss) |
|
|
31
|
+
| Cutting on keyframes is fine | `-ss` before `-i` with `-c copy` |
|
|
32
|
+
| Cutting must be frame-accurate, scaling, filters, burning subtitles | re-encode the video; audio can often still be `-c:a copy` |
|
|
33
|
+
| Target must play everywhere (web, phones) | H.264 `-c:v libx264 -pix_fmt yuv420p` + AAC in MP4 with `-movflags +faststart` |
|
|
34
|
+
| Smaller files, modern players | H.265 (`libx265`, add `-tag:v hvc1` for Apple players), VP9/Opus in WebM, or AV1 (`libsvtav1`) |
|
|
35
|
+
|
|
36
|
+
Filters (`-vf`, `-af`, `-filter_complex`) never work with `-c copy` for that stream.
|
|
37
|
+
|
|
38
|
+
## 2. Quality settings
|
|
39
|
+
|
|
40
|
+
- x264/x265: control quality with `-crf` (x264: 18 visually near-lossless, 23 default, 28 small; x265 defaults to 28) and speed with `-preset` (`veryfast` ... `slow`). Use CRF for files, not a fixed bitrate, unless a target bitrate is required (streaming, size cap).
|
|
41
|
+
- For a size cap use two-pass with `-b:v` (`-pass 1 -an -f null /dev/null`, then `-pass 2`).
|
|
42
|
+
- VP9: `-c:v libvpx-vp9 -crf 31 -b:v 0` (the `-b:v 0` is what makes it constant quality).
|
|
43
|
+
- Audio: `-c:a aac -b:a 128k`, or `-c:a libopus -b:a 96k` in WebM/MKV. Normalise loudness with `-af loudnorm=I=-16:TP=-1.5:LRA=11`.
|
|
44
|
+
- Always `-pix_fmt yuv420p` for H.264 meant for browsers; 4:4:4 or 10-bit H.264 won't play in many of them.
|
|
45
|
+
|
|
46
|
+
## 3. Common jobs
|
|
47
|
+
|
|
48
|
+
- **Trim**: `ffmpeg -ss 00:01:00 -i in.mp4 -t 30 -c:v libx264 -crf 20 -c:a aac out.mp4`. `-ss` before `-i` seeks fast and is accurate when re-encoding; with `-c copy` the cut snaps to the previous keyframe. `-t` is a duration, `-to` an end time.
|
|
49
|
+
- **Concatenate** files with identical codecs and settings: concat demuxer (`-f concat -safe 0 -i list.txt -c copy`). Different codecs or sizes: the `concat` filter in `-filter_complex` and re-encode. Recipe in `references/recipes.md`.
|
|
50
|
+
- **Scale**: `-vf "scale=1280:-2"` (`-2` keeps aspect ratio with an even height). Fit into a box with letterboxing: `scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2`.
|
|
51
|
+
- **Frame rate**: `-vf fps=30` (or `-r 30` on output). Variable frame rate phone video: re-encode with `fps=` to stop audio drift in editors.
|
|
52
|
+
- **Audio only**: `-vn -c:a libmp3lame -q:a 2 out.mp3` or `-vn -c:a copy out.m4a` when the source is AAC.
|
|
53
|
+
- **Subtitles**: burn in with `-vf "subtitles=subs.srt"` (needs libass, re-encodes); soft subs in MP4 with `-c:s mov_text`, in MKV with `-c:s srt` or `ass`. Map streams explicitly with `-map 0:v -map 0:a -map 1:s`.
|
|
54
|
+
- **Thumbnail**: `ffmpeg -ss 10 -i in.mp4 -frames:v 1 -q:v 2 thumb.jpg`. One per 10 s: `-vf fps=1/10 thumb_%03d.jpg` (several frames need a numbered output pattern). Contact sheet of the first 16 samples: `-vf "fps=1/10,scale=320:-2,tile=4x4" -frames:v 1 sheet.jpg`.
|
|
55
|
+
- **HLS**: see `references/recipes.md` for a single rendition and a multi-bitrate ladder with `-master_pl_name` and `-var_stream_map`.
|
|
56
|
+
|
|
57
|
+
## 4. Hardware acceleration
|
|
58
|
+
|
|
59
|
+
Use it when throughput matters more than size-for-quality; hardware encoders need a higher bitrate for the same quality as x264/x265.
|
|
60
|
+
|
|
61
|
+
| Platform | Encode | Decode/filters |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| NVIDIA | `-c:v h264_nvenc` / `hevc_nvenc` with `-preset p1`..`p7` and `-cq` | `-hwaccel cuda -hwaccel_output_format cuda`, `scale_cuda` |
|
|
64
|
+
| macOS | `-c:v h264_videotoolbox` / `hevc_videotoolbox` with `-b:v` or `-q:v` | `-hwaccel videotoolbox` |
|
|
65
|
+
| Intel | `-c:v h264_qsv` / `hevc_qsv` with `-global_quality` | `-hwaccel qsv` |
|
|
66
|
+
| Linux VAAPI | `-vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' -c:v h264_vaapi` | `-hwaccel vaapi` |
|
|
67
|
+
|
|
68
|
+
If a hardware encoder errors, check `ffmpeg -encoders` and the driver, then fall back to the software encoder rather than shipping a broken path. Mixing a hardware-decoded frame with a software filter needs `hwdownload,format=nv12`.
|
|
69
|
+
|
|
70
|
+
## 5. Running ffmpeg from code
|
|
71
|
+
|
|
72
|
+
1. **Pass an argument array, never a shell string.** `spawn('ffmpeg', ['-i', inputPath, ...])` in Node, `subprocess.run(['ffmpeg', ...], check=True)` in Python. No `shell=True`, no string interpolation of file names, titles or user text into a command line.
|
|
73
|
+
2. **Generate the file names yourself** (UUIDs in a temp dir) and keep user names only as metadata. A user file name starting with `-` would otherwise be read as an option.
|
|
74
|
+
3. **Treat uploads as hostile.** FFmpeg picks the demuxer from the file's content, not its extension, so an "mp4" upload can really be an HLS playlist or concat list that makes ffmpeg fetch URLs or read other local files. Probe with ffprobe first (in the same sandbox) and reject anything whose `format_name` isn't on your allowlist (for example `mov,mp4,m4a,3gp,3g2,mj2`, `matroska,webm`); never accept `hls`, `concat` or similar playlist formats. Then enforce limits (duration, resolution, stream count, allowed codecs). `-protocol_whitelist file` (an input option, placed before `-i`) blocks network protocols, but it still allows reading any local file, so it does not replace the format allowlist or the sandbox.
|
|
75
|
+
4. **Bound the run**: a wall-clock timeout, `-threads` if sharing a host, `-nostdin` so it never waits for input, `-y` or `-n` decided explicitly (overwrite or fail), and run it in a sandbox or container without network for user uploads.
|
|
76
|
+
5. **Check the exit code and stderr**, then ffprobe the output (duration within tolerance, expected streams). A zero exit code with a 0-second output is still a failure.
|
|
77
|
+
6. Report progress with `-progress pipe:1 -nostats` and parse `out_time_us=`.
|
|
78
|
+
|
|
79
|
+
## Never
|
|
80
|
+
|
|
81
|
+
- Never put `-ss` and `-c copy` together when the user needs an exact cut, without telling them it will snap to a keyframe.
|
|
82
|
+
- Never re-encode when `-c copy` does the job, and never stream-copy when the target needs a different codec.
|
|
83
|
+
- Never build ffmpeg commands by concatenating strings from user input.
|
|
84
|
+
- Never overwrite the source file; write to a new path and swap after verifying.
|
|
85
|
+
|
|
86
|
+
## Stop and ask the user
|
|
87
|
+
|
|
88
|
+
- Quality versus size trade-off when no target is given (CRF, resolution, bitrate cap).
|
|
89
|
+
- Before deleting or replacing original media files.
|
|
90
|
+
- When the source is DRM-protected or the user may not have rights to transcode it.
|
|
91
|
+
|
|
92
|
+
## Report back
|
|
93
|
+
|
|
94
|
+
The exact command(s), why copy or re-encode, the input and output ffprobe summary (codec, resolution, fps, duration, size), and any limit (keyframe snapping, hardware fallback) the user should know about.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Convert this MOV from my iPhone into an MP4 that plays in every browser",
|
|
4
|
+
"Cut 00:02:10 to 00:02:40 out of the lecture recording without losing sync",
|
|
5
|
+
"Turn our uploaded videos into an HLS ladder with 1080p, 720p and 480p renditions",
|
|
6
|
+
"Our Node backend runs ffmpeg on user uploads with exec and a template string, make it safe",
|
|
7
|
+
"Use NVENC to speed up transcoding on the GPU box, the software encoder is too slow"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Render my Remotion composition to MP4 on AWS Lambda",
|
|
11
|
+
"Animate a matrix multiplication step by step with Manim",
|
|
12
|
+
"Generate a voice-over track from a script using a text-to-speech API",
|
|
13
|
+
"Set up a CDN in front of our S3 bucket of images",
|
|
14
|
+
"Write a Dockerfile for our Python API with a small image"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# FFmpeg recipes
|
|
2
|
+
|
|
3
|
+
Each recipe assumes the input was probed first. Adjust CRF, bitrates and sizes to the user's target. Check flags against `ffmpeg -h full` or ffmpeg.org/ffmpeg-all.html for the installed version.
|
|
4
|
+
|
|
5
|
+
## Web-ready MP4 (H.264 + AAC)
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
ffmpeg -nostdin -i in.mov -c:v libx264 -preset medium -crf 23 -pix_fmt yuv420p \
|
|
9
|
+
-c:a aac -b:a 128k -movflags +faststart out.mp4
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
`+faststart` moves the index to the front so playback starts before the whole file downloads.
|
|
13
|
+
|
|
14
|
+
## WebM (VP9 + Opus)
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
ffmpeg -nostdin -i in.mp4 -c:v libvpx-vp9 -crf 31 -b:v 0 -row-mt 1 -c:a libopus -b:a 96k out.webm
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Concatenate identical files (no re-encode)
|
|
21
|
+
|
|
22
|
+
`list.txt` (paths relative to the list file, single-quoted):
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
file 'part1.mp4'
|
|
26
|
+
file 'part2.mp4'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
ffmpeg -nostdin -f concat -safe 0 -i list.txt -c copy joined.mp4
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Only write `list.txt` from paths your code generated. `-safe 0` allows any path, so never use it with a list built from user input.
|
|
34
|
+
|
|
35
|
+
## Concatenate different files (re-encode)
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
ffmpeg -nostdin -i a.mp4 -i b.mp4 -filter_complex \
|
|
39
|
+
"[0:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2,setsar=1,fps=30[v0]; \
|
|
40
|
+
[1:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2,setsar=1,fps=30[v1]; \
|
|
41
|
+
[v0][0:a][v1][1:a]concat=n=2:v=1:a=1[v][a]" \
|
|
42
|
+
-map "[v]" -map "[a]" -c:v libx264 -crf 20 -pix_fmt yuv420p -c:a aac out.mp4
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Every input needs an audio stream for `a=1`; for a clip that has none, add a silent input of the same length, `-f lavfi -t <clip-seconds> -i anullsrc=r=48000:cl=stereo`, and use that input's audio in its place. The `-t` matters: `anullsrc` never ends on its own, and `-shortest` does not stop it inside `concat`.
|
|
46
|
+
|
|
47
|
+
## Burn in subtitles with styling
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
ffmpeg -nostdin -i in.mp4 -vf "subtitles=subs.srt:force_style='FontName=Inter,FontSize=24,Outline=2'" \
|
|
51
|
+
-c:v libx264 -crf 20 -c:a copy out.mp4
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Filter paths with `:` or `'` need escaping; copy the subtitle file to a simple generated name instead.
|
|
55
|
+
|
|
56
|
+
## Add a soft subtitle track to MP4
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
ffmpeg -nostdin -i in.mp4 -i subs.srt -map 0 -map 1 -c copy -c:s mov_text -metadata:s:s:0 language=eng out.mp4
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Watermark / overlay
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
ffmpeg -nostdin -i in.mp4 -i logo.png -filter_complex "overlay=W-w-24:H-h-24" -c:v libx264 -crf 20 -c:a copy out.mp4
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## GIF with a good palette
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
ffmpeg -nostdin -i in.mp4 -vf "fps=12,scale=480:-1:flags=lanczos,split[a][b];[a]palettegen[p];[b][p]paletteuse" out.gif
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## HLS, single rendition (VOD)
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
ffmpeg -nostdin -i in.mp4 -c:v libx264 -crf 21 -g 48 -keyint_min 48 -sc_threshold 0 -c:a aac -b:a 128k \
|
|
78
|
+
-f hls -hls_time 6 -hls_playlist_type vod -hls_segment_filename "out/seg_%03d.ts" out/index.m3u8
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
A fixed GOP (`-g` = fps x 2 here for 24 fps and 2 s keyframes, `-sc_threshold 0`) keeps segment boundaries even. Use `-hls_segment_type fmp4` for fMP4 segments (needed for HEVC in HLS).
|
|
82
|
+
|
|
83
|
+
## HLS ladder (three renditions, master playlist)
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
ffmpeg -nostdin -i in.mp4 -filter_complex \
|
|
87
|
+
"[0:v]split=3[v1][v2][v3];[v1]scale=w=1920:h=-2[v1o];[v2]scale=w=1280:h=-2[v2o];[v3]scale=w=854:h=-2[v3o]" \
|
|
88
|
+
-map "[v1o]" -c:v:0 libx264 -b:v:0 5000k -maxrate:v:0 5350k -bufsize:v:0 7500k \
|
|
89
|
+
-map "[v2o]" -c:v:1 libx264 -b:v:1 2800k -maxrate:v:1 2996k -bufsize:v:1 4200k \
|
|
90
|
+
-map "[v3o]" -c:v:2 libx264 -b:v:2 1400k -maxrate:v:2 1498k -bufsize:v:2 2100k \
|
|
91
|
+
-map 0:a:0 -map 0:a:0 -map 0:a:0 -c:a aac -b:a 128k -ac 2 \
|
|
92
|
+
-g 48 -keyint_min 48 -sc_threshold 0 -pix_fmt yuv420p \
|
|
93
|
+
-f hls -hls_time 6 -hls_playlist_type vod \
|
|
94
|
+
-hls_segment_filename "out/v%v/seg_%03d.ts" -master_pl_name master.m3u8 \
|
|
95
|
+
-var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" out/v%v/index.m3u8
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`-map` always needs the input index (`0:a:0`, not `a:0`). `-g 48` assumes 24 fps (2 s GOP); set it to fps x 2 from the probe. The scale filters upscale small sources, so drop renditions taller than the input.
|
|
99
|
+
|
|
100
|
+
Serve `.m3u8` as `application/vnd.apple.mpegurl` and `.ts` as `video/mp2t`, with CORS if the player is on another origin.
|
|
101
|
+
|
|
102
|
+
## Extract audio and normalise loudness
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
ffmpeg -nostdin -i in.mp4 -vn -af loudnorm=I=-16:TP=-1.5:LRA=11 -c:a aac -b:a 160k voice.m4a
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
For best results run `loudnorm` twice: first with `print_format=json` to measure, then pass the measured values back.
|
|
109
|
+
|
|
110
|
+
## Progress from code
|
|
111
|
+
|
|
112
|
+
Add `-progress pipe:1 -nostats` and parse lines such as `out_time_us=12345678` and `progress=end` from stdout; divide by the probed duration for a percentage.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fhir-integration
|
|
3
|
+
description: HL7 FHIR R4 and R4B REST client and server work, covering resource modelling and references, search parameters with _include and chained queries, paging through Bundle next links, transaction and batch bundles with urn:uuid references, SMART on FHIR authorization (EHR and standalone launch, v2 scopes, PKCE, backend services), profile validation with $validate and the HL7 validator, and testing against public sandboxes. Use when calling or exposing a FHIR API, registering a SMART app with an EHR such as Epic or Oracle Health, writing FHIR search queries, posting transaction bundles, or debugging OperationOutcome errors.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Live calls need network access to a FHIR server or sandbox. Examples use plain HTTP and JSON; the rules apply to any language or FHIR client library.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: integrations
|
|
9
|
+
kete-loom/risk: network
|
|
10
|
+
kete-loom/owner: "Kete-org"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# FHIR integration
|
|
14
|
+
|
|
15
|
+
Build a FHIR integration that reads and writes the right patient's data, survives paging and partial failure, and validates against the profiles the other side expects. This skill covers the FHIR REST API and SMART on FHIR. For HL7 v2 pipe-delimited messages use `hl7v2-integration`; for decision support cards use `clinical-decision-support`; for HIPAA safeguards around the data use `hipaa-compliance`.
|
|
16
|
+
|
|
17
|
+
This is engineering guidance, not clinical or legal advice.
|
|
18
|
+
|
|
19
|
+
## Ground rules
|
|
20
|
+
|
|
21
|
+
1. **Pin the FHIR version.** Read `GET [base]/metadata` (the CapabilityStatement) and check `fhirVersion`: `4.0.1` is R4, `4.3.0` is R4B, `5.0.0` is R5. Never mix resource shapes across versions. Send `Accept: application/fhir+json` (optionally with `; fhirVersion=4.0`).
|
|
22
|
+
2. **Trust the CapabilityStatement, not assumptions.** Which resources, interactions, search parameters, `_include`s and operations a server supports differs per server. Check `rest.resource[].searchParam` before relying on a parameter; unsupported parameters may be silently ignored, which returns too much data.
|
|
23
|
+
3. **Patient identity is not a name match.** Find patients by a business identifier (`Patient?identifier=system|value`), never by name and birth date alone. If a match is ambiguous, stop and surface it to a human; never pick the first result.
|
|
24
|
+
4. **References are typed and relative.** Write `"subject": {"reference": "Patient/123"}`. Resolve relative references against the server base. Treat `contained` resources as part of their parent only.
|
|
25
|
+
5. **Follow paging to the end.** A search returns a `searchset` Bundle with at most `_count` entries; keep following `Bundle.link` where `relation = "next"` exactly as given (it is opaque) until there is none. `Bundle.total` is optional and may be absent or estimated.
|
|
26
|
+
6. **Use the right bundle type.** `transaction` is all-or-nothing; `batch` runs entries independently and you must check every `entry.response.status`. Inside a transaction, link new resources with `fullUrl: "urn:uuid:<uuid>"` references, which the server rewrites to real IDs.
|
|
27
|
+
7. **Writes are conditional and versioned.** Use conditional create (`If-None-Exist: identifier=system|value`) to avoid duplicates on retry, and `If-Match: W/"<versionId>"` on updates to avoid lost updates. A `412 Precondition Failed` means someone else changed it: re-read, re-apply, never force.
|
|
28
|
+
8. **Errors are OperationOutcome.** On any 4xx/5xx read `OperationOutcome.issue[].severity`, `code` and `diagnostics`. Never log the request body or full resource when logging errors; it holds PHI. Log resource type, ID, status and issue codes.
|
|
29
|
+
9. **Least-privilege SMART scopes.** Ask only for the scopes the feature needs, for example `patient/Observation.rs`, not `patient/*.cruds`. Keep tokens and refresh tokens server-side or in secure platform storage, never in `localStorage`.
|
|
30
|
+
|
|
31
|
+
## Steps
|
|
32
|
+
|
|
33
|
+
1. **Discover.** Fetch `[base]/metadata` and `[base]/.well-known/smart-configuration`. Record FHIR version, supported resources and search parameters, the `authorization_endpoint`, `token_endpoint`, supported scopes and `code_challenge_methods_supported`.
|
|
34
|
+
2. **Pick the auth flow** with `references/smart-on-fhir.md`:
|
|
35
|
+
- App launched from inside the EHR: EHR launch (`launch` scope plus the `launch` parameter).
|
|
36
|
+
- App started by the user outside the EHR: standalone launch (`launch/patient`).
|
|
37
|
+
- Server-to-server job with no user: SMART Backend Services (`client_credentials` with a signed JWT `client_assertion`, `system/` scopes).
|
|
38
|
+
3. **Identify the implementation guide.** In the US that is usually US Core; elsewhere a national base profile (for example AU Base, UK Core) or a project IG. Note the profile canonical URLs and must-support elements you must populate and handle.
|
|
39
|
+
4. **Model the data.** Map each domain concept to a resource and profile. Use coded values with both `system` and `code` (LOINC for observations, SNOMED CT for clinical findings, RxNorm in the US for medications, UCUM for units). Never store free-text where the profile requires a code.
|
|
40
|
+
5. **Query** with the patterns in `references/search-and-bundles.md`: one search with `_include` or `_revinclude` instead of N+1 reads, `_elements` or `_summary` to cut payloads, dates with prefixes (`ge`, `lt`).
|
|
41
|
+
6. **Write** with conditional create or update, and transactions for sets of resources that must land together.
|
|
42
|
+
7. **Validate** every resource you create against its profile before sending: `POST [base]/[type]/$validate` where supported, or the HL7 validator locally (`java -jar validator_cli.jar file.json -version 4.0.1 -ig <package>`). Treat `error` and `fatal` issues as blocking.
|
|
43
|
+
8. **Test** against a sandbox before any real endpoint (see `references/search-and-bundles.md` for public servers). Use synthetic patients only (for example Synthea data); never paste real patient data into a public server.
|
|
44
|
+
|
|
45
|
+
## Stop and ask the user
|
|
46
|
+
|
|
47
|
+
- Before any write against a production EHR, or any test that uses real patient data.
|
|
48
|
+
- When a patient search returns more than one candidate, or zero where one was expected.
|
|
49
|
+
- When the server's version, IG or supported search parameters differ from what the code assumes.
|
|
50
|
+
- Before requesting `user/*` or `system/*` wildcard scopes or `offline_access`.
|
|
51
|
+
|
|
52
|
+
## Never
|
|
53
|
+
|
|
54
|
+
- Never build search URLs by string concatenation of unescaped user input; URL-encode values, and escape a literal `,`, `$`, `|` or `\` inside a parameter value by prefixing it with `\`, per the FHIR search spec.
|
|
55
|
+
- Never assume a single page of results is all results.
|
|
56
|
+
- Never drop unknown extensions or modifier extensions on round-trip; a `modifierExtension` you don't understand can change meaning, so reject the resource instead of ignoring it.
|
|
57
|
+
- Never treat `Observation.status = entered-in-error`, `cancelled` or `preliminary` results as final clinical data without handling the status.
|
|
58
|
+
- Never put PHI in URLs that end up in access logs if a `POST [type]/_search` form is available.
|
|
59
|
+
|
|
60
|
+
## Reviewing an existing integration
|
|
61
|
+
|
|
62
|
+
Check each ground rule, then look for:
|
|
63
|
+
|
|
64
|
+
- Paging stops after the first page, or the `next` link is rebuilt by hand.
|
|
65
|
+
- Patient matched by name, or the first `Patient` search result used blindly.
|
|
66
|
+
- Batch bundles where failed entries are never checked.
|
|
67
|
+
- Updates without `If-Match`, retries without conditional create.
|
|
68
|
+
- Search parameters the server does not list in its CapabilityStatement.
|
|
69
|
+
- Tokens logged, stored in the browser, or scopes far broader than the feature.
|
|
70
|
+
- Resources never validated against the required profile.
|
|
71
|
+
|
|
72
|
+
## Report format
|
|
73
|
+
|
|
74
|
+
When asked to review or design, finish with:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
FHIR version / IG: <e.g. R4 4.0.1, US Core 6.1.0>
|
|
78
|
+
Auth flow: <EHR launch | standalone | backend services> with scopes <list>
|
|
79
|
+
Findings: <ranked list, file:line, rule broken, fix>
|
|
80
|
+
Not verified: <server behaviours you could not test>
|
|
81
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Our SMART on FHIR app gets invalid_scope from the Epic sandbox, what scopes should we request?",
|
|
4
|
+
"Write a client that pulls all lab Observations for a patient from a FHIR R4 server, including every page",
|
|
5
|
+
"How do I post a Patient and its Encounter together in one FHIR transaction bundle so the reference resolves?",
|
|
6
|
+
"Validate our generated Condition resources against the US Core profile before we send them",
|
|
7
|
+
"Set up a backend services client_credentials flow with a signed JWT to export data from our EHR's FHIR API"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Parse incoming ADT^A01 messages from the hospital's interface engine over MLLP",
|
|
11
|
+
"Build a CDS Hooks service that shows a warning card at order-sign",
|
|
12
|
+
"Do we need a BAA with our cloud vendor before storing patient records?",
|
|
13
|
+
"Review our patient portal code for PHI showing up in error logs",
|
|
14
|
+
"Design a REST API with pagination and versioning for our appointment booking service"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# FHIR search, paging and bundles
|
|
2
|
+
|
|
3
|
+
Source: FHIR R4 specification, http.html and search.html (hl7.org/fhir/R4). Check the target server's CapabilityStatement for what it really supports.
|
|
4
|
+
|
|
5
|
+
## Search patterns
|
|
6
|
+
|
|
7
|
+
| Need | Query |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Patient by identifier | `GET Patient?identifier=http://hospital.example/mrn\|12345` |
|
|
10
|
+
| Lab results since a date | `GET Observation?patient=Patient/123&category=laboratory&date=ge2024-01-01` |
|
|
11
|
+
| Latest vitals | `GET Observation?patient=123&category=vital-signs&_sort=-date&_count=10` |
|
|
12
|
+
| Specific code | `GET Observation?patient=123&code=http://loinc.org\|4548-4` |
|
|
13
|
+
| Encounters plus their patient | `GET Encounter?date=ge2024-06-01&_include=Encounter:subject` |
|
|
14
|
+
| Patient plus its conditions | `GET Patient?_id=123&_revinclude=Condition:subject` |
|
|
15
|
+
| Chained search | `GET Observation?subject:Patient.identifier=http://hospital.example/mrn\|12345` |
|
|
16
|
+
| Smaller payload | `_summary=true`, or `_elements=id,code,valueQuantity` |
|
|
17
|
+
| Long or PHI-bearing query | `POST Observation/_search` with `application/x-www-form-urlencoded` body |
|
|
18
|
+
|
|
19
|
+
Modifiers: `:exact`, `:contains` (strings), `:missing=true`, `:not`, `:text` (tokens). Date prefixes: `eq ne gt lt ge le sa eb ap`.
|
|
20
|
+
|
|
21
|
+
`_include`d resources come back with `entry.search.mode = "include"`; only `"match"` entries are search results.
|
|
22
|
+
|
|
23
|
+
## Paging
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
url = first search URL
|
|
27
|
+
loop:
|
|
28
|
+
bundle = GET url
|
|
29
|
+
for entry in bundle.entry where entry.search.mode == "match": handle(entry.resource)
|
|
30
|
+
next = bundle.link.find(l => l.relation == "next")
|
|
31
|
+
if not next: break
|
|
32
|
+
url = next.url # opaque; never rebuild it
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Some servers expire paging cursors; handle a 4xx on a `next` link by restarting the search, not by skipping.
|
|
36
|
+
|
|
37
|
+
## Transaction bundle
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"resourceType": "Bundle",
|
|
42
|
+
"type": "transaction",
|
|
43
|
+
"entry": [
|
|
44
|
+
{
|
|
45
|
+
"fullUrl": "urn:uuid:3f1c...",
|
|
46
|
+
"resource": { "resourceType": "Patient", "identifier": [{ "system": "http://hospital.example/mrn", "value": "12345" }] },
|
|
47
|
+
"request": { "method": "POST", "url": "Patient", "ifNoneExist": "identifier=http://hospital.example/mrn|12345" }
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"resource": { "resourceType": "Observation", "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "4548-4" }] }, "subject": { "reference": "urn:uuid:3f1c..." } },
|
|
51
|
+
"request": { "method": "POST", "url": "Observation" }
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
POST it to `[base]` (not to a resource type). Success returns a `transaction-response` Bundle with one `entry.response` per entry, in order, each with `status` and `location`. Any failure rolls back everything and returns an OperationOutcome.
|
|
58
|
+
|
|
59
|
+
Use `batch` only when entries are truly independent, and check each `entry.response.status`.
|
|
60
|
+
|
|
61
|
+
## Other interactions
|
|
62
|
+
|
|
63
|
+
- `GET Patient/123/_history/2`: a specific version.
|
|
64
|
+
- `GET Patient/123/$everything`: everything for a patient, where supported (paged).
|
|
65
|
+
- `GET Observation?_lastUpdated=gt2024-06-01T00:00:00Z`: incremental sync.
|
|
66
|
+
- Bulk Data (`$export`, async with `Prefer: respond-async`, NDJSON output): for population-scale extracts; use backend services auth.
|
|
67
|
+
|
|
68
|
+
## Public test servers (synthetic data only)
|
|
69
|
+
|
|
70
|
+
- HAPI FHIR public server: `https://hapi.fhir.org/baseR4` (shared, data is public and wiped occasionally).
|
|
71
|
+
- SMART App Launcher: `https://launch.smarthealthit.org` (simulates EHR and standalone launch).
|
|
72
|
+
- Vendor sandboxes: Epic on FHIR (fhir.epic.com) and Oracle Health (Cerner) code console; each requires registering an app.
|
|
73
|
+
- Run your own: the `hapiproject/hapi` Docker image for a local server you can reset.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# SMART on FHIR authorization
|
|
2
|
+
|
|
3
|
+
Source: SMART App Launch IG (STU 2.x, hl7.org/fhir/smart-app-launch). Check the EHR vendor's own developer docs for registration steps and which scopes they actually grant.
|
|
4
|
+
|
|
5
|
+
## Discovery
|
|
6
|
+
|
|
7
|
+
`GET [base]/.well-known/smart-configuration` returns JSON with `authorization_endpoint`, `token_endpoint`, `scopes_supported`, `capabilities` (for example `launch-ehr`, `launch-standalone`, `client-public`, `client-confidential-asymmetric`, `context-ehr-patient`, `permission-v2`) and `code_challenge_methods_supported`.
|
|
8
|
+
|
|
9
|
+
## Scopes (v2 syntax)
|
|
10
|
+
|
|
11
|
+
`<context>/<ResourceType>.<letters>` where context is `patient`, `user` or `system`, and letters are a subset of `c r u d s` in that order.
|
|
12
|
+
|
|
13
|
+
| Scope | Meaning |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `patient/Observation.rs` | read and search Observations for the patient in context |
|
|
16
|
+
| `patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category\|laboratory` | same, limited to lab results |
|
|
17
|
+
| `user/Patient.rs` | read and search any Patient the user may see |
|
|
18
|
+
| `system/*.rs` | backend service, read everything granted to the client |
|
|
19
|
+
| `launch` | EHR launch; pair with the `launch` URL parameter |
|
|
20
|
+
| `launch/patient` | standalone launch; ask the EHR to select a patient |
|
|
21
|
+
| `openid fhirUser` | ID token with a `fhirUser` claim (the user's Practitioner/Patient URL) |
|
|
22
|
+
| `offline_access` / `online_access` | refresh token, long-lived or only while the user is online |
|
|
23
|
+
|
|
24
|
+
v1 servers use `.read`, `.write`, `.*`. v1 `.read` maps to v2 `.rs`, `.write` to `.cud`. Request what the server advertises (`permission-v1` / `permission-v2`). The granted scopes in the token response can be narrower than requested: always read `scope` from the response and degrade the feature, rather than assuming.
|
|
25
|
+
|
|
26
|
+
## EHR launch
|
|
27
|
+
|
|
28
|
+
1. EHR opens your launch URL with `iss` (the FHIR base) and `launch` (an opaque handle).
|
|
29
|
+
2. Check `iss` against an allowlist of FHIR bases you registered with. Never fetch configuration from an arbitrary `iss`.
|
|
30
|
+
3. Redirect to `authorization_endpoint` with `response_type=code`, `client_id`, `redirect_uri`, `scope` (including `launch`), `state` (random, checked on return), `aud` (= the FHIR base), `launch`, `code_challenge` and `code_challenge_method=S256`.
|
|
31
|
+
4. Exchange the code at `token_endpoint` with `code_verifier`. The response carries `access_token`, `expires_in`, `scope`, and context such as `patient`, `encounter`, `id_token`, `refresh_token`.
|
|
32
|
+
5. Use only the `patient` in the token response as the patient in context.
|
|
33
|
+
|
|
34
|
+
## Standalone launch
|
|
35
|
+
|
|
36
|
+
Same as EHR launch without `iss`/`launch` from the EHR: you know the base in advance and request `launch/patient`.
|
|
37
|
+
|
|
38
|
+
## Backend services (no user)
|
|
39
|
+
|
|
40
|
+
1. Register a public key (JWKS URL or JWK) with the server.
|
|
41
|
+
2. Build a JWT signed with `RS384` or `ES384`: `iss` and `sub` = client_id, `aud` = token endpoint, `exp` at most 5 minutes ahead, unique `jti`.
|
|
42
|
+
3. `POST token_endpoint` with `grant_type=client_credentials`, `scope=system/...`, `client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer`, `client_assertion=<jwt>`.
|
|
43
|
+
4. Cache the token until shortly before `expires_in`.
|
|
44
|
+
|
|
45
|
+
## Common failures
|
|
46
|
+
|
|
47
|
+
- `invalid_scope`: the scope isn't registered for the client, or v1/v2 syntax mismatch.
|
|
48
|
+
- `invalid_client` on backend services: wrong `aud`, clock skew on `exp`, wrong algorithm, or a key not yet registered.
|
|
49
|
+
- 401 on FHIR calls: token expired, or `aud` didn't match the FHIR base.
|
|
50
|
+
- 403 on a resource: scope granted is narrower than requested, or the user lacks access in the EHR.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flask-patterns
|
|
3
|
+
description: Flask application structure with a create_app factory, blueprints per feature, config loaded from objects and environment variables, extensions such as Flask-SQLAlchemy and Flask-Migrate initialised with init_app, application and request context pitfalls with current_app and g, security hardening, and pytest tests through the test client. Use when starting or restructuring a Flask app, adding a blueprint or extension, debugging "working outside of application context" errors, or testing Flask views.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs Python and the project's Flask app. Guidance targets Flask 2.x and 3.x with Flask-SQLAlchemy 3.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: coding
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/requires: "python"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Flask patterns
|
|
15
|
+
|
|
16
|
+
Keep a Flask app importable without side effects, configurable per environment, and testable with a fresh app per test.
|
|
17
|
+
|
|
18
|
+
## 0. Before you change anything
|
|
19
|
+
|
|
20
|
+
1. Find the app: a `create_app()` factory or a module-level `app = Flask(__name__)`. Check Flask and extension versions in `pyproject.toml` or `requirements*.txt`.
|
|
21
|
+
2. Find how it runs (`flask --app <module> run`, gunicorn target, `FLASK_APP`) and how tests run. Run the tests once; if they already fail, **stop and tell the user**.
|
|
22
|
+
3. If the app is a single module-level `app`, ask the user before converting it to a factory; it touches every import of `app`.
|
|
23
|
+
|
|
24
|
+
## 1. Application factory
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
# myapp/__init__.py
|
|
28
|
+
from flask import Flask
|
|
29
|
+
from .extensions import db, migrate
|
|
30
|
+
|
|
31
|
+
def create_app(config_object="myapp.config.Config", test_config=None):
|
|
32
|
+
app = Flask(__name__, instance_relative_config=True)
|
|
33
|
+
app.config.from_object(config_object)
|
|
34
|
+
app.config.from_prefixed_env() # FLASK_SECRET_KEY, FLASK_SQLALCHEMY_DATABASE_URI, ...
|
|
35
|
+
if test_config:
|
|
36
|
+
app.config.update(test_config)
|
|
37
|
+
|
|
38
|
+
db.init_app(app)
|
|
39
|
+
migrate.init_app(app, db)
|
|
40
|
+
|
|
41
|
+
from .orders import bp as orders_bp
|
|
42
|
+
app.register_blueprint(orders_bp, url_prefix="/orders")
|
|
43
|
+
return app
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- Extensions are created unbound in `extensions.py` (`db = SQLAlchemy()`) and bound with `init_app(app)` in the factory. This avoids circular imports and lets tests build many apps.
|
|
47
|
+
- Import blueprints inside the factory (or in a module that doesn't import the app) to avoid import cycles.
|
|
48
|
+
- No work at import time: no database connections, no reading secrets into module globals, no `app.run()` outside `if __name__ == "__main__":`.
|
|
49
|
+
|
|
50
|
+
## 2. Blueprints
|
|
51
|
+
|
|
52
|
+
- One blueprint per feature (`auth`, `orders`, `admin`), each in its own package with `routes.py`, templates in a per-blueprint subfolder of the templates folder.
|
|
53
|
+
- Use `url_for("orders.detail", order_id=o.id)` (blueprint-qualified endpoint names), never hard-coded paths.
|
|
54
|
+
- Protect a whole blueprint with `@bp.before_request` that checks login or role, then add per-view checks for object ownership.
|
|
55
|
+
- Register error handlers per blueprint with `@bp.errorhandler` or app-wide with `@app.errorhandler`; return JSON for API blueprints and HTML for page blueprints, consistently.
|
|
56
|
+
|
|
57
|
+
## 3. Config
|
|
58
|
+
|
|
59
|
+
- Base `Config` class with safe defaults, subclasses `DevConfig`, `TestConfig`, `ProdConfig` if the project uses them; or one class plus environment variables.
|
|
60
|
+
- `SECRET_KEY` and database URLs come from the environment or the `instance/` folder (which is not committed). Fail at startup in production if `SECRET_KEY` is missing.
|
|
61
|
+
- Read config at request time through `current_app.config["KEY"]`, not by importing a module constant, so tests can override it.
|
|
62
|
+
- `DEBUG`/`app.debug` must be off in production; the Werkzeug debugger allows arbitrary code execution.
|
|
63
|
+
|
|
64
|
+
## 4. Flask-SQLAlchemy and Flask-Migrate
|
|
65
|
+
|
|
66
|
+
- Use the session the extension scopes to the app context: `db.session`. It is removed at the end of each request; don't create your own `Session()` per request.
|
|
67
|
+
- SQLAlchemy 2 style queries: `db.session.execute(db.select(Order).filter_by(owner_id=uid)).scalars()`, `db.get_or_404(Order, id)`, `db.paginate(...)`. Follow the existing style if the project still uses `Model.query`.
|
|
68
|
+
- Avoid N+1 queries with `selectinload`/`joinedload` in `options(...)` when a template walks a relationship.
|
|
69
|
+
- Commit once per unit of work; on exception call `db.session.rollback()` before re-raising or returning an error.
|
|
70
|
+
- Migrations: `flask db migrate -m "add order status"`, read the generated script (autogenerate misses renames and some type changes), then `flask db upgrade`. Run `flask db downgrade` and `upgrade` again locally to prove reversibility.
|
|
71
|
+
- **Stop and ask the user** before a migration that drops columns or tables or rewrites a large table. Never run `flask db upgrade` against a shared or production database yourself.
|
|
72
|
+
|
|
73
|
+
## 5. Contexts: current_app, g, request, session
|
|
74
|
+
|
|
75
|
+
| Symptom | Cause | Fix |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| `RuntimeError: Working outside of application context` | Using `current_app`, `db.session` or `g` in a script, CLI, thread or test with no app pushed | `with app.app_context():` around the work, or use a `@app.cli.command()` (which pushes one) |
|
|
78
|
+
| `Working outside of request context` | Touching `request` or `session` outside a request | Pass values in as arguments; in tests use `app.test_request_context()` |
|
|
79
|
+
| Data shared between users | Storing per-request state in a module global | Store it on `g` (lives for one app context) |
|
|
80
|
+
| Background thread loses context | Contexts are per thread | Pass plain data and push `app.app_context()` in the thread; better, use a task queue |
|
|
81
|
+
|
|
82
|
+
- Never pass `request`, `g` or `current_app` proxies to a thread or task. Use `current_app._get_current_object()` only when you must hand the real app object over.
|
|
83
|
+
|
|
84
|
+
## 6. Security
|
|
85
|
+
|
|
86
|
+
- Forms: Flask-WTF's `CSRFProtect` (create `csrf = CSRFProtect()` in `extensions.py`, call `csrf.init_app(app)` in the factory) for any cookie-authenticated POST. Exempt only token-authenticated API endpoints, and say why.
|
|
87
|
+
- Cookies in production: `SESSION_COOKIE_SECURE=True`, `SESSION_COOKIE_HTTPONLY=True` (default), `SESSION_COOKIE_SAMESITE="Lax"`. The default session is signed, not encrypted: never put secrets in it.
|
|
88
|
+
- Templates autoescape `.html` files; never use `Markup(...)` or `|safe` on user input, and never `render_template_string` with user-controlled template text.
|
|
89
|
+
- Uploads: `secure_filename`, `MAX_CONTENT_LENGTH`, an allow-list of extensions, and save outside the static folder.
|
|
90
|
+
- Redirects: validate `next` parameters against your own host before redirecting.
|
|
91
|
+
- SQL: use the ORM or `text()` with bound parameters, never f-strings.
|
|
92
|
+
- Behind a proxy, wrap with `werkzeug.middleware.proxy_fix.ProxyFix` set to the real number of proxies, not more.
|
|
93
|
+
|
|
94
|
+
## 7. Testing
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# tests/conftest.py
|
|
98
|
+
import pytest
|
|
99
|
+
from myapp import create_app
|
|
100
|
+
from myapp.extensions import db
|
|
101
|
+
|
|
102
|
+
@pytest.fixture
|
|
103
|
+
def app():
|
|
104
|
+
app = create_app(test_config={"TESTING": True,
|
|
105
|
+
"SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:",
|
|
106
|
+
"WTF_CSRF_ENABLED": False})
|
|
107
|
+
with app.app_context():
|
|
108
|
+
db.create_all()
|
|
109
|
+
yield app # outside the context, so tests catch missing-context bugs
|
|
110
|
+
with app.app_context():
|
|
111
|
+
db.drop_all()
|
|
112
|
+
|
|
113
|
+
@pytest.fixture
|
|
114
|
+
def client(app):
|
|
115
|
+
return app.test_client()
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- A fresh app per test via the factory; never import a shared global app into tests.
|
|
119
|
+
- Use `client.get/post(...)`, `follow_redirects=True` where the flow redirects, and `with client.session_transaction() as s:` to set session state.
|
|
120
|
+
- Test CLI commands with `app.test_cli_runner()`.
|
|
121
|
+
- If production uses PostgreSQL, run at least the query-heavy tests against PostgreSQL; SQLite hides type and constraint differences.
|
|
122
|
+
- For each view: happy path, invalid form, unauthenticated, another user's object.
|
|
123
|
+
|
|
124
|
+
## Rules
|
|
125
|
+
|
|
126
|
+
- Never leave `debug=True` or a hard-coded `SECRET_KEY` in committed code.
|
|
127
|
+
- Never disable CSRF outside test config.
|
|
128
|
+
- Never add global mutable state for per-request data.
|
|
129
|
+
|
|
130
|
+
## Report back
|
|
131
|
+
|
|
132
|
+
End with: files changed, blueprints and routes added, extensions or config keys added, migrations generated (with what you checked in the script), the test command and result, and anything left untested.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Convert my single-file Flask app into an application factory with blueprints",
|
|
4
|
+
"I get RuntimeError: Working outside of application context in my Flask CLI script",
|
|
5
|
+
"Add Flask-SQLAlchemy and Flask-Migrate to this Flask project",
|
|
6
|
+
"Write pytest tests for my Flask login view using the test client",
|
|
7
|
+
"How do I load Flask config from environment variables per environment?"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Add a Django admin page for the orders model",
|
|
11
|
+
"Create FastAPI dependencies for the database session",
|
|
12
|
+
"Review my Django change for unsafe raw SQL",
|
|
13
|
+
"Write a Dockerfile to deploy my Python web app",
|
|
14
|
+
"Fix the pip dependency conflict that stops my Python package from installing"
|
|
15
|
+
]
|
|
16
|
+
}
|