@ketecode/loom 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +616 -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/behavior.json +60 -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/inbox-triager/AGENT.md +98 -0
- package/agents/inbox-triager/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/spec-reviewer/AGENT.md +95 -0
- package/agents/spec-reviewer/evals/behavior.json +57 -0
- package/agents/spec-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/task-implementer/AGENT.md +89 -0
- package/agents/task-implementer/evals/behavior.json +47 -0
- package/agents/task-implementer/evals/triggers.json +16 -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 +723 -0
- package/commands/aside.md +19 -0
- package/commands/brainstorm.md +25 -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/debug.md +27 -0
- package/commands/deps-audit.md +32 -0
- package/commands/dual-review.md +34 -0
- package/commands/epic-decompose.md +21 -0
- package/commands/explain.md +37 -0
- package/commands/feature-dev.md +23 -0
- package/commands/finish-branch.md +26 -0
- package/commands/fix-bug.md +38 -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/production-readiness.md +45 -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/usage-report.md +43 -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-foreground-servers/HOOK.md +34 -0
- package/hooks/block-foreground-servers/cases.json +31 -0
- package/hooks/block-foreground-servers/hook.js +41 -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/capture-lessons/HOOK.md +24 -0
- package/hooks/capture-lessons/hook.js +48 -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/notify-on-stop/HOOK.md +25 -0
- package/hooks/notify-on-stop/hook.js +27 -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/protect-lint-config/HOOK.md +29 -0
- package/hooks/protect-lint-config/cases.json +18 -0
- package/hooks/protect-lint-config/fixtures/.eslintrc.json +1 -0
- package/hooks/protect-lint-config/fixtures/.golangci.yml +1 -0
- package/hooks/protect-lint-config/fixtures/.prettierrc +1 -0
- package/hooks/protect-lint-config/fixtures/.rubocop.yml +1 -0
- package/hooks/protect-lint-config/fixtures/biome.json +1 -0
- package/hooks/protect-lint-config/fixtures/eslint.config.mjs +1 -0
- package/hooks/protect-lint-config/fixtures/ruff.toml +1 -0
- package/hooks/protect-lint-config/hook.js +25 -0
- package/hooks/read-before-edit/HOOK.md +26 -0
- package/hooks/read-before-edit/cases.json +15 -0
- package/hooks/read-before-edit/hook.js +50 -0
- package/hooks/run.js +148 -0
- package/hooks/verify-before-stop/HOOK.md +32 -0
- package/hooks/verify-before-stop/hook.js +70 -0
- package/mcp/atlassian.json +11 -0
- package/mcp/chrome-devtools.json +18 -0
- package/mcp/clickhouse.json +11 -0
- package/mcp/cloudflare.json +11 -0
- package/mcp/context7.json +11 -0
- package/mcp/exa.json +11 -0
- package/mcp/fetch.json +17 -0
- package/mcp/figma.json +11 -0
- package/mcp/firecrawl.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/mcp/vercel.json +11 -0
- package/package.json +61 -4
- package/profiles.json +214 -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/rules/use-skills.md +23 -0
- package/schemas/skill-frontmatter.schema.json +52 -0
- package/scripts/lib/audit.js +96 -0
- package/scripts/lib/behavior.js +52 -0
- package/scripts/lib/frontmatter.js +65 -0
- package/scripts/lib/hooks-install.js +294 -0
- package/scripts/lib/i18n.js +51 -0
- package/scripts/lib/inventory.js +85 -0
- package/scripts/lib/lint.js +180 -0
- package/scripts/lib/mcp-install.js +288 -0
- package/scripts/lib/profiles.js +128 -0
- package/scripts/lib/render.js +92 -0
- package/scripts/lib/rules-install.js +118 -0
- package/scripts/lib/skills.js +87 -0
- package/scripts/lib/targets.js +123 -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-failure-debugging/SKILL.md +92 -0
- package/skills/agent-failure-debugging/evals/triggers.json +16 -0
- package/skills/agent-harness-design/SKILL.md +125 -0
- package/skills/agent-harness-design/evals/triggers.json +21 -0
- package/skills/agent-outbound-messaging/SKILL.md +92 -0
- package/skills/agent-outbound-messaging/evals/triggers.json +16 -0
- package/skills/agent-security/SKILL.md +117 -0
- package/skills/agent-security/evals/triggers.json +16 -0
- package/skills/agent-security/references/host-configs.md +37 -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-client-integration/SKILL.md +86 -0
- package/skills/api-client-integration/evals/triggers.json +16 -0
- package/skills/api-client-integration/references/resilience.md +57 -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/blender-automation/SKILL.md +100 -0
- package/skills/blender-automation/evals/triggers.json +16 -0
- package/skills/blender-automation/references/batch-and-render.md +77 -0
- package/skills/blender-automation/references/bpy-inspection.md +91 -0
- package/skills/blender-automation/references/rig-and-retarget-checks.md +23 -0
- package/skills/brand-strategy/SKILL.md +83 -0
- package/skills/brand-strategy/evals/triggers.json +16 -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/change-impact-analysis/SKILL.md +89 -0
- package/skills/change-impact-analysis/evals/triggers.json +16 -0
- package/skills/change-impact-analysis/references/graph-commands.md +28 -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/contract-drafting/SKILL.md +96 -0
- package/skills/contract-drafting/evals/triggers.json +16 -0
- package/skills/contract-drafting/references/term-sheets.md +68 -0
- package/skills/contract-first-development/SKILL.md +86 -0
- package/skills/contract-first-development/evals/triggers.json +16 -0
- package/skills/contract-first-development/references/tooling.md +46 -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/decision-council/SKILL.md +98 -0
- package/skills/decision-council/evals/triggers.json +16 -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/design-brainstorming/SKILL.md +132 -0
- package/skills/design-brainstorming/evals/behavior.json +55 -0
- package/skills/design-brainstorming/evals/triggers.json +16 -0
- package/skills/design-brainstorming/references/visual-mockups.md +34 -0
- package/skills/desktop-app-testing/SKILL.md +104 -0
- package/skills/desktop-app-testing/evals/triggers.json +16 -0
- package/skills/desktop-app-testing/references/drivers.md +81 -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/document-processing/SKILL.md +100 -0
- package/skills/document-processing/evals/triggers.json +17 -0
- package/skills/document-processing/references/extraction-and-ocr.md +71 -0
- package/skills/document-processing/references/forms-and-conversion.md +69 -0
- package/skills/document-processing/references/redaction.md +61 -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/energy-procurement/SKILL.md +106 -0
- package/skills/energy-procurement/evals/triggers.json +16 -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/finishing-a-branch/SKILL.md +127 -0
- package/skills/finishing-a-branch/evals/behavior.json +49 -0
- package/skills/finishing-a-branch/evals/triggers.json +16 -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/freight-carrier-management/SKILL.md +107 -0
- package/skills/freight-carrier-management/evals/triggers.json +16 -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/git-worktrees/SKILL.md +130 -0
- package/skills/git-worktrees/evals/behavior.json +56 -0
- package/skills/git-worktrees/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/google-workspace-integration/SKILL.md +65 -0
- package/skills/google-workspace-integration/evals/triggers.json +17 -0
- package/skills/google-workspace-integration/references/api.md +121 -0
- package/skills/harmonyos-arkts/SKILL.md +117 -0
- package/skills/harmonyos-arkts/evals/triggers.json +16 -0
- package/skills/harmonyos-arkts/references/build-errors.md +42 -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/implementation-plans/SKILL.md +117 -0
- package/skills/implementation-plans/evals/behavior.json +54 -0
- package/skills/implementation-plans/evals/triggers.json +16 -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/ios-platform-features/SKILL.md +99 -0
- package/skills/ios-platform-features/evals/triggers.json +16 -0
- package/skills/ios-platform-features/references/foundation-models.md +90 -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/jvm-sql-mappers/SKILL.md +98 -0
- package/skills/jvm-sql-mappers/evals/triggers.json +16 -0
- package/skills/jvm-sql-mappers/references/examples.md +129 -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/mobile-accessibility/SKILL.md +94 -0
- package/skills/mobile-accessibility/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-design/SKILL.md +98 -0
- package/skills/network-design/evals/triggers.json +16 -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/observability/SKILL.md +125 -0
- package/skills/observability/evals/triggers.json +17 -0
- package/skills/observability/references/otel-setup.md +87 -0
- package/skills/observability/references/slo-alerts.md +70 -0
- package/skills/open-source-release/SKILL.md +107 -0
- package/skills/open-source-release/evals/triggers.json +15 -0
- package/skills/open-source-release/references/history-scrub.md +65 -0
- package/skills/parallel-agent-orchestration/SKILL.md +106 -0
- package/skills/parallel-agent-orchestration/evals/triggers.json +14 -0
- package/skills/patent-research/SKILL.md +94 -0
- package/skills/patent-research/evals/triggers.json +16 -0
- package/skills/patent-research/references/databases-and-queries.md +52 -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/prospect-research/SKILL.md +87 -0
- package/skills/prospect-research/evals/triggers.json +16 -0
- package/skills/python-app-distribution/SKILL.md +107 -0
- package/skills/python-app-distribution/evals/triggers.json +16 -0
- package/skills/python-app-distribution/references/pypi-trusted-publishing.md +55 -0
- package/skills/python-app-distribution/references/windows-macos-signing.md +70 -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/quality-nonconformance/SKILL.md +107 -0
- package/skills/quality-nonconformance/evals/triggers.json +16 -0
- package/skills/quality-nonconformance/references/8d-and-capa.md +46 -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/react-testing/SKILL.md +121 -0
- package/skills/react-testing/evals/triggers.json +18 -0
- package/skills/react-testing/references/msw.md +64 -0
- package/skills/react-testing/references/queries.md +41 -0
- package/skills/react-testing/references/setup.md +74 -0
- package/skills/recommender-systems/SKILL.md +119 -0
- package/skills/recommender-systems/evals/triggers.json +16 -0
- package/skills/recommender-systems/references/evaluation.md +39 -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/behavior.json +57 -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/reproducible-dev-environments/SKILL.md +132 -0
- package/skills/reproducible-dev-environments/evals/triggers.json +16 -0
- package/skills/responding-to-code-review/SKILL.md +124 -0
- package/skills/responding-to-code-review/evals/behavior.json +54 -0
- package/skills/responding-to-code-review/evals/triggers.json +16 -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/behavior.json +56 -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/behavior.json +55 -0
- package/skills/session-handoff/evals/triggers.json +14 -0
- package/skills/skill-authoring/SKILL.md +98 -0
- package/skills/skill-authoring/evals/triggers.json +15 -0
- package/skills/skill-authoring/references/frontmatter.md +40 -0
- package/skills/skill-authoring/references/pressure-testing.md +87 -0
- package/skills/slack-app-integration/SKILL.md +108 -0
- package/skills/slack-app-integration/evals/triggers.json +17 -0
- package/skills/slide-decks/SKILL.md +107 -0
- package/skills/slide-decks/evals/triggers.json +18 -0
- package/skills/slide-decks/references/html-decks.md +86 -0
- package/skills/slide-decks/references/layouts.md +45 -0
- package/skills/slide-decks/references/pptx.md +87 -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/subagent-driven-development/SKILL.md +109 -0
- package/skills/subagent-driven-development/evals/behavior.json +53 -0
- package/skills/subagent-driven-development/evals/triggers.json +15 -0
- package/skills/subagent-driven-development/references/implementer-brief.md +60 -0
- package/skills/subagent-driven-development/references/reviewer-briefs.md +52 -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/systematic-debugging/SKILL.md +92 -0
- package/skills/systematic-debugging/evals/behavior.json +59 -0
- package/skills/systematic-debugging/evals/triggers.json +16 -0
- package/skills/systematic-debugging/references/condition-waiting.md +52 -0
- package/skills/systematic-debugging/references/root-cause-tracing.md +49 -0
- package/skills/systematic-debugging/references/test-pollution.md +46 -0
- package/skills/tdd-workflow/SKILL.md +69 -0
- package/skills/tdd-workflow/evals/behavior.json +55 -0
- package/skills/tdd-workflow/evals/triggers.json +15 -0
- package/skills/tdd-workflow/references/testing-anti-patterns.md +87 -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/transactional-email/SKILL.md +68 -0
- package/skills/transactional-email/evals/triggers.json +17 -0
- package/skills/transactional-email/references/providers.md +63 -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/ui-demo-recording/SKILL.md +93 -0
- package/skills/ui-demo-recording/evals/triggers.json +16 -0
- package/skills/ui-demo-recording/references/cursor-and-captions.md +48 -0
- package/skills/ui-demo-recording/references/playwright-recording.md +63 -0
- package/skills/ui-demo-recording/references/post-processing.md +57 -0
- package/skills/ui-design-polish/SKILL.md +113 -0
- package/skills/ui-design-polish/evals/triggers.json +18 -0
- package/skills/ui-design-polish/references/detail-checklist.md +45 -0
- package/skills/ui-design-polish/references/tokens.md +68 -0
- package/skills/ui-flow-audit/SKILL.md +112 -0
- package/skills/ui-flow-audit/evals/triggers.json +17 -0
- package/skills/ui-motion/SKILL.md +103 -0
- package/skills/ui-motion/evals/triggers.json +18 -0
- package/skills/ui-motion/references/patterns.md +138 -0
- package/skills/ui-motion/references/tokens.md +52 -0
- package/skills/unicode-text-processing/SKILL.md +103 -0
- package/skills/unicode-text-processing/evals/triggers.json +17 -0
- package/skills/unicode-text-processing/references/apis.md +89 -0
- package/skills/unicode-text-processing/references/script-notes.md +50 -0
- package/skills/verification-loop/SKILL.md +70 -0
- package/skills/verification-loop/evals/behavior.json +52 -0
- package/skills/verification-loop/evals/triggers.json +14 -0
- package/skills/verification-loop/scripts/detect-checks.js +102 -0
- package/skills/video-editing/SKILL.md +111 -0
- package/skills/video-editing/evals/triggers.json +16 -0
- package/skills/video-editing/references/edl-and-logging.md +44 -0
- package/skills/video-editing/references/ffmpeg-edit-recipes.md +92 -0
- package/skills/video-editing/references/resolve-scripting.md +53 -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,288 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Registers curated MCP servers (mcp/<name>.json) with each host (verified against host docs and Kete
|
|
4
|
+
// Code's source, Oct 2026). Servers carry no secrets: remote ones sign in with the host's own OAuth
|
|
5
|
+
// flow, local ones need no tokens. Each host gets the server in its own shape:
|
|
6
|
+
// Claude Code user: `claude mcp add-json --scope user` (never edits ~/.claude.json); project: .mcp.json
|
|
7
|
+
// Codex [mcp_servers.<name>] tables in a marked block of config.toml
|
|
8
|
+
// Gemini CLI settings.json `mcpServers` (httpUrl = streamable HTTP)
|
|
9
|
+
// Cursor mcp.json `mcpServers`
|
|
10
|
+
// Copilot CLI ~/.copilot/mcp-config.json `mcpServers`; local servers only (its OAuth isn't documented)
|
|
11
|
+
// OpenCode opencode.json `mcp` (command is an array)
|
|
12
|
+
// Kete Code kete.json `mcp.servers`
|
|
13
|
+
// Kiro ~/.kiro/settings/mcp.json `mcpServers` (remote servers sign in with OAuth)
|
|
14
|
+
// An entry is ours when it equals what we would write, so a server you configured yourself under the
|
|
15
|
+
// same name is never touched. Claude Code's CLI-managed servers are tracked in ~/.kete-loom/mcp.json.
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const { execFileSync } = require('child_process');
|
|
20
|
+
const { readJson, writeAtomic, CommentedFileError } = require('./hooks-install');
|
|
21
|
+
|
|
22
|
+
const MCP_DIR = path.join(__dirname, '..', '..', 'mcp');
|
|
23
|
+
const listMcp = (dir = MCP_DIR) =>
|
|
24
|
+
fs.existsSync(dir)
|
|
25
|
+
? fs.readdirSync(dir).filter((f) => f.endsWith('.json')).sort().map((f) => ({ kind: 'mcp', dir: f.slice(0, -5), file: path.join(dir, f), ...JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8')) }))
|
|
26
|
+
: [];
|
|
27
|
+
|
|
28
|
+
const http = (s) => s.transport === 'http';
|
|
29
|
+
// Each host's JSON for one server, or null when that host can't run it.
|
|
30
|
+
const RENDER = {
|
|
31
|
+
claude: (s) => (http(s) ? { type: 'http', url: s.url } : { type: 'stdio', command: s.command, args: s.args }),
|
|
32
|
+
cursor: (s) => (http(s) ? { url: s.url } : { type: 'stdio', command: s.command, args: s.args }),
|
|
33
|
+
gemini: (s) => (http(s) ? { httpUrl: s.url } : { command: s.command, args: s.args }),
|
|
34
|
+
copilot: (s) => (http(s) ? null : { type: 'local', command: s.command, args: s.args, tools: ['*'] }),
|
|
35
|
+
opencode: (s) => (http(s) ? { type: 'remote', url: s.url, enabled: true } : { type: 'local', command: [s.command, ...s.args], enabled: true }),
|
|
36
|
+
kete: (s) => (http(s) ? { type: 'remote', url: s.url } : { type: 'local', command: [s.command, ...s.args] }),
|
|
37
|
+
kiro: (s) => (http(s) ? { url: s.url } : { command: s.command, args: s.args }),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const SIGN_IN = {
|
|
41
|
+
claude: (n) => `run /mcp in Claude Code to sign in to ${n}`,
|
|
42
|
+
codex: (n) => `codex mcp login ${n}`,
|
|
43
|
+
gemini: (n) => `run /mcp auth ${n} in Gemini CLI`,
|
|
44
|
+
cursor: (n) => `Cursor asks you to sign in to ${n} the first time it connects`,
|
|
45
|
+
opencode: (n) => `opencode mcp auth ${n}`,
|
|
46
|
+
kete: (n) => `kete mcp auth ${n}`,
|
|
47
|
+
kiro: (n) => `Kiro opens a browser to sign in to ${n} the first time it connects`,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const keteFile = (dir) => (fs.existsSync(path.join(dir, 'kete.jsonc')) ? path.join(dir, 'kete.jsonc') : path.join(dir, 'kete.json'));
|
|
51
|
+
|
|
52
|
+
// JSON hosts: file per scope, and the object that holds the servers.
|
|
53
|
+
const JSON_HOSTS = {
|
|
54
|
+
claude: { file: { project: (cwd) => path.join(cwd, '.mcp.json') }, holder: (d) => (d.mcpServers ||= {}), standalone: true },
|
|
55
|
+
cursor: { file: { user: (h) => path.join(h, '.cursor', 'mcp.json'), project: (cwd) => path.join(cwd, '.cursor', 'mcp.json') }, holder: (d) => (d.mcpServers ||= {}), standalone: true },
|
|
56
|
+
gemini: { file: { user: (h) => path.join(h, '.gemini', 'settings.json'), project: (cwd) => path.join(cwd, '.gemini', 'settings.json') }, holder: (d) => (d.mcpServers ||= {}) },
|
|
57
|
+
copilot: { file: { user: (h) => path.join(process.env.COPILOT_HOME || path.join(h, '.copilot'), 'mcp-config.json') }, holder: (d) => (d.mcpServers ||= {}), standalone: true },
|
|
58
|
+
opencode: { file: { user: (h) => path.join(h, '.config', 'opencode', 'opencode.json'), project: (cwd) => path.join(cwd, 'opencode.json') }, holder: (d) => (d.mcp ||= {}) },
|
|
59
|
+
kete: { file: { user: (h) => keteFile(path.join(h, '.config', 'kete')), project: (cwd) => keteFile(path.join(cwd, '.kete')) }, holder: (d) => ((d.mcp ||= {}).servers ||= {}) },
|
|
60
|
+
kiro: { file: { user: (h) => path.join(h, '.kiro', 'settings', 'mcp.json'), project: (cwd) => path.join(cwd, '.kiro', 'settings', 'mcp.json') }, holder: (d) => (d.mcpServers ||= {}), standalone: true },
|
|
61
|
+
};
|
|
62
|
+
const MCP_HOSTS = ['claude', 'codex', 'gemini', 'cursor', 'copilot', 'opencode', 'kete', 'kiro'];
|
|
63
|
+
|
|
64
|
+
const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
65
|
+
|
|
66
|
+
function pruneJson(data) {
|
|
67
|
+
const empty = (v) => v && typeof v === 'object' && !Array.isArray(v) && !Object.keys(v).length;
|
|
68
|
+
if (data.mcp && empty(data.mcp.servers)) delete data.mcp.servers;
|
|
69
|
+
for (const k of ['mcpServers', 'mcp']) if (empty(data[k])) delete data[k];
|
|
70
|
+
return data;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function jsonRegister(host, servers, { file, dryRun }) {
|
|
74
|
+
let data;
|
|
75
|
+
try {
|
|
76
|
+
data = readJson(file);
|
|
77
|
+
} catch (e) {
|
|
78
|
+
const reason = e instanceof CommentedFileError ? `${e.message}; add the servers yourself` : e.message;
|
|
79
|
+
return servers.map((s) => ({ name: s.dir, action: 'skipped', reason, dest: file }));
|
|
80
|
+
}
|
|
81
|
+
const holder = JSON_HOSTS[host].holder(data);
|
|
82
|
+
const rows = servers.map((s) => {
|
|
83
|
+
const want = RENDER[host](s);
|
|
84
|
+
if (!want) return { name: s.dir, action: 'skipped', reason: `${host} can't sign in to remote MCP servers yet; add ${s.dir} there yourself`, dest: file };
|
|
85
|
+
const have = holder[s.dir];
|
|
86
|
+
if (have && !same(have, want)) return { name: s.dir, action: 'skipped', reason: `a server named ${s.dir} already exists there and wasn't installed by kete-loom`, dest: file };
|
|
87
|
+
holder[s.dir] = want;
|
|
88
|
+
return { name: s.dir, action: have ? 'updated' : 'installed', dest: file };
|
|
89
|
+
});
|
|
90
|
+
pruneJson(data);
|
|
91
|
+
if (!dryRun && rows.some((r) => r.action !== 'skipped')) writeAtomic(file, JSON.stringify(data, null, 2) + '\n');
|
|
92
|
+
return rows;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function jsonUnregister(host, servers, { file, dryRun }) {
|
|
96
|
+
if (!fs.existsSync(file)) return [];
|
|
97
|
+
let data;
|
|
98
|
+
try {
|
|
99
|
+
data = readJson(file);
|
|
100
|
+
} catch {
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
const holder = JSON_HOSTS[host].holder(data);
|
|
104
|
+
const gone = servers.filter((s) => holder[s.dir] && RENDER[host](s) && same(holder[s.dir], RENDER[host](s)));
|
|
105
|
+
if (!gone.length) return [];
|
|
106
|
+
for (const s of gone) delete holder[s.dir];
|
|
107
|
+
pruneJson(data);
|
|
108
|
+
if (!dryRun) {
|
|
109
|
+
if (JSON_HOSTS[host].standalone && !Object.keys(data).length) fs.rmSync(file);
|
|
110
|
+
else writeAtomic(file, JSON.stringify(data, null, 2) + '\n');
|
|
111
|
+
}
|
|
112
|
+
return gone.map((s) => ({ name: s.dir, action: 'removed', dest: file }));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Codex: TOML tables in a block we own. Strings and arrays are written as JSON, which is valid TOML.
|
|
116
|
+
const TOML_START = '# kete-loom:mcp:start (managed by kete-loom: edit outside this block)';
|
|
117
|
+
const TOML_END = '# kete-loom:mcp:end';
|
|
118
|
+
const TOML_BLOCK_RE = /\n*# kete-loom:mcp:start[^\n]*\n[\s\S]*?# kete-loom:mcp:end\n?/;
|
|
119
|
+
const codexFile = (scope, home, cwd) => (scope === 'project' ? path.join(cwd, '.codex', 'config.toml') : path.join(home, '.codex', 'config.toml'));
|
|
120
|
+
const tomlTable = (s) =>
|
|
121
|
+
[`[mcp_servers.${s.dir}]`, ...(http(s) ? [`url = ${JSON.stringify(s.url)}`] : [`command = ${JSON.stringify(s.command)}`, `args = ${JSON.stringify(s.args)}`])].join('\n');
|
|
122
|
+
const blockNames = (text) => [...((TOML_BLOCK_RE.exec(text) || [''])[0].matchAll(/^\[mcp_servers\.([a-z0-9-]+)\]$/gm))].map((m) => m[1]);
|
|
123
|
+
|
|
124
|
+
function codexWrite(file, text, names, all, dryRun) {
|
|
125
|
+
const rest = text.replace(TOML_BLOCK_RE, '\n').trimEnd();
|
|
126
|
+
const tables = all.filter((s) => names.includes(s.dir)).map(tomlTable);
|
|
127
|
+
const next = tables.length ? `${rest ? `${rest}\n\n` : ''}${TOML_START}\n${tables.join('\n\n')}\n${TOML_END}\n` : rest ? `${rest}\n` : '';
|
|
128
|
+
if (dryRun || next === text) return;
|
|
129
|
+
if (next) writeAtomic(file, next);
|
|
130
|
+
else fs.rmSync(file);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function codexRegister(servers, all, { file, dryRun }) {
|
|
134
|
+
const text = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
|
|
135
|
+
const outside = text.replace(TOML_BLOCK_RE, '\n');
|
|
136
|
+
const ours = blockNames(text);
|
|
137
|
+
const rows = servers.map((s) => {
|
|
138
|
+
// A table or dotted key for this name outside our block would make config.toml invalid TOML.
|
|
139
|
+
if (new RegExp(`(^|\\n)\\s*\\[mcp_servers\\.(?:"${s.dir}"|${s.dir})\\]|mcp_servers\\.(?:"${s.dir}"|${s.dir})\\.`).test(outside)) {
|
|
140
|
+
return { name: s.dir, action: 'skipped', reason: `config.toml already defines mcp_servers.${s.dir}`, dest: file };
|
|
141
|
+
}
|
|
142
|
+
return { name: s.dir, action: ours.includes(s.dir) ? 'updated' : 'installed', dest: file };
|
|
143
|
+
});
|
|
144
|
+
const names = [...new Set([...ours, ...rows.filter((r) => r.action !== 'skipped').map((r) => r.name)])];
|
|
145
|
+
codexWrite(file, text, names, all, dryRun);
|
|
146
|
+
return rows;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function codexUnregister(servers, all, { file, dryRun }) {
|
|
150
|
+
if (!fs.existsSync(file)) return [];
|
|
151
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
152
|
+
const ours = blockNames(text);
|
|
153
|
+
const gone = ours.filter((n) => servers.some((s) => s.dir === n));
|
|
154
|
+
if (!gone.length) return [];
|
|
155
|
+
codexWrite(file, text, ours.filter((n) => !gone.includes(n)), all, dryRun);
|
|
156
|
+
return gone.map((name) => ({ name, action: 'removed', dest: file }));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Claude Code user scope goes through its CLI; ~/.kete-loom/mcp.json records which servers we added.
|
|
160
|
+
const stateFile = (home) => path.join(home, '.kete-loom', 'mcp.json');
|
|
161
|
+
const readState = (home) => {
|
|
162
|
+
try {
|
|
163
|
+
return JSON.parse(fs.readFileSync(stateFile(home), 'utf8'));
|
|
164
|
+
} catch {
|
|
165
|
+
return {};
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
function writeState(home, state) {
|
|
169
|
+
const file = stateFile(home);
|
|
170
|
+
if (Object.values(state).some((v) => v.length)) {
|
|
171
|
+
// Our own state file: atomic, but no backup (backups are for files that belong to you).
|
|
172
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
173
|
+
fs.writeFileSync(`${file}.tmp`, JSON.stringify(state, null, 2) + '\n');
|
|
174
|
+
fs.renameSync(`${file}.tmp`, file);
|
|
175
|
+
}
|
|
176
|
+
else if (fs.existsSync(file)) {
|
|
177
|
+
fs.rmSync(file);
|
|
178
|
+
if (!fs.readdirSync(path.dirname(file)).length) fs.rmdirSync(path.dirname(file));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const claudeBin = () => process.env.KETE_LOOM_CLAUDE_BIN || 'claude';
|
|
182
|
+
const claude = (args) => execFileSync(claudeBin(), args, { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8', timeout: 30000 });
|
|
183
|
+
|
|
184
|
+
function claudeUserRegister(servers, { home, dryRun }) {
|
|
185
|
+
const state = readState(home);
|
|
186
|
+
const mine = new Set(state['claude-user'] || []);
|
|
187
|
+
const rows = servers.map((s) => {
|
|
188
|
+
const json = JSON.stringify(RENDER.claude(s));
|
|
189
|
+
const dest = `claude mcp (user scope)`;
|
|
190
|
+
try {
|
|
191
|
+
if (mine.has(s.dir)) {
|
|
192
|
+
if (!dryRun) {
|
|
193
|
+
claude(['mcp', 'remove', s.dir, '--scope', 'user']);
|
|
194
|
+
claude(['mcp', 'add-json', s.dir, json, '--scope', 'user']);
|
|
195
|
+
}
|
|
196
|
+
return { name: s.dir, action: 'updated', dest };
|
|
197
|
+
}
|
|
198
|
+
let exists = true;
|
|
199
|
+
try {
|
|
200
|
+
claude(['mcp', 'get', s.dir]);
|
|
201
|
+
} catch (e) {
|
|
202
|
+
if (e.code === 'ENOENT') throw e;
|
|
203
|
+
exists = false;
|
|
204
|
+
}
|
|
205
|
+
if (exists) return { name: s.dir, action: 'skipped', reason: `Claude Code already has a server named ${s.dir}, not installed by kete-loom`, dest };
|
|
206
|
+
if (!dryRun) claude(['mcp', 'add-json', s.dir, json, '--scope', 'user']);
|
|
207
|
+
mine.add(s.dir);
|
|
208
|
+
return { name: s.dir, action: 'installed', dest };
|
|
209
|
+
} catch (e) {
|
|
210
|
+
const reason = e.code === 'ENOENT' ? `the claude CLI isn't on your PATH; run: claude mcp add-json ${s.dir} '${json}' --scope user` : `claude mcp failed: ${(e.stderr || e.message).trim().slice(0, 200)}`;
|
|
211
|
+
return { name: s.dir, action: 'skipped', reason, dest };
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
if (!dryRun) writeState(home, { ...state, 'claude-user': [...mine].sort() });
|
|
215
|
+
return rows;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function claudeUserUnregister(servers, { home, dryRun }) {
|
|
219
|
+
const state = readState(home);
|
|
220
|
+
const mine = state['claude-user'] || [];
|
|
221
|
+
const gone = mine.filter((n) => servers.some((s) => s.dir === n));
|
|
222
|
+
const removed = [];
|
|
223
|
+
for (const name of gone) {
|
|
224
|
+
try {
|
|
225
|
+
if (!dryRun) claude(['mcp', 'remove', name, '--scope', 'user']);
|
|
226
|
+
removed.push(name);
|
|
227
|
+
} catch (e) {
|
|
228
|
+
// Already removed by hand: forget it either way.
|
|
229
|
+
if (e.code !== 'ENOENT') removed.push(name);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (!dryRun) writeState(home, { ...state, 'claude-user': mine.filter((n) => !removed.includes(n)) });
|
|
233
|
+
return removed.map((name) => ({ name, action: 'removed', dest: 'claude mcp (user scope)' }));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function fileFor(host, scope, home, cwd) {
|
|
237
|
+
if (host === 'codex') return codexFile(scope, home, cwd);
|
|
238
|
+
const f = JSON_HOSTS[host].file[scope];
|
|
239
|
+
return f ? f(scope === 'project' ? cwd : home) : null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Registers servers with one host. Returns rows { target, kind, name, action, dest, reason?, note? }.
|
|
243
|
+
function installMcp(host, servers, all, { scope = 'user', home, cwd = process.cwd(), dryRun = false }) {
|
|
244
|
+
let rows;
|
|
245
|
+
if (host === 'claude' && scope === 'user') rows = claudeUserRegister(servers, { home, dryRun });
|
|
246
|
+
else {
|
|
247
|
+
const file = fileFor(host, scope, home, cwd);
|
|
248
|
+
if (!file) {
|
|
249
|
+
const reason = host === 'copilot' ? "Copilot CLI reads the project's .mcp.json, which kete-loom writes for Claude Code" : `${host} has no ${scope} MCP config`;
|
|
250
|
+
return servers.map((s) => ({ target: host, kind: 'mcp', name: s.dir, action: 'skipped', reason, dest: '' }));
|
|
251
|
+
}
|
|
252
|
+
rows = host === 'codex' ? codexRegister(servers, all, { file, dryRun }) : jsonRegister(host, servers, { file, dryRun });
|
|
253
|
+
}
|
|
254
|
+
return rows.map((r) => {
|
|
255
|
+
const s = servers.find((x) => x.dir === r.name);
|
|
256
|
+
const note = r.action !== 'skipped' && s && http(s) && SIGN_IN[host] ? `Remote MCP servers sign in with OAuth. ${host}: ${SIGN_IN[host]('<server>')}.` : undefined;
|
|
257
|
+
return { target: host, kind: 'mcp', ...r, ...(note ? { note } : {}) };
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function uninstallMcp(host, servers, all, { scope = 'user', home, cwd = process.cwd(), dryRun = false }) {
|
|
262
|
+
let rows;
|
|
263
|
+
if (host === 'claude' && scope === 'user') rows = claudeUserUnregister(servers, { home, dryRun });
|
|
264
|
+
else {
|
|
265
|
+
const file = fileFor(host, scope, home, cwd);
|
|
266
|
+
if (!file) return [];
|
|
267
|
+
rows = host === 'codex' ? codexUnregister(servers, all, { file, dryRun }) : jsonUnregister(host, servers, { file, dryRun });
|
|
268
|
+
}
|
|
269
|
+
return rows.map((r) => ({ target: host, kind: 'mcp', ...r }));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Names of kete-loom MCP servers registered with a host (exact matches only).
|
|
273
|
+
function registeredMcp(host, all, { scope = 'user', home, cwd = process.cwd() }) {
|
|
274
|
+
if (host === 'claude' && scope === 'user') return readState(home)['claude-user'] || [];
|
|
275
|
+
const file = fileFor(host, scope, home, cwd);
|
|
276
|
+
if (!file || !fs.existsSync(file)) return [];
|
|
277
|
+
if (host === 'codex') return blockNames(fs.readFileSync(file, 'utf8'));
|
|
278
|
+
let data;
|
|
279
|
+
try {
|
|
280
|
+
data = readJson(file);
|
|
281
|
+
} catch {
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
const holder = JSON_HOSTS[host].holder(data);
|
|
285
|
+
return all.filter((s) => holder[s.dir] && RENDER[host](s) && same(holder[s.dir], RENDER[host](s))).map((s) => s.dir);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
module.exports = { MCP_HOSTS, MCP_DIR, listMcp, RENDER, installMcp, uninstallMcp, registeredMcp, tomlTable };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Install profiles (profiles.json): named selections of skills, agents, commands and hooks.
|
|
4
|
+
// A profile can extend others, add whole categories, and `detect` the project's languages,
|
|
5
|
+
// frameworks and tools so only the packs it uses are installed.
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const PROFILES_FILE = path.join(__dirname, '..', '..', 'profiles.json');
|
|
11
|
+
const KINDS = ['skills', 'agents', 'commands', 'rules', 'hooks', 'mcp'];
|
|
12
|
+
|
|
13
|
+
const loadProfiles = (file = PROFILES_FILE) => JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
14
|
+
|
|
15
|
+
// What a project uses -> the items that help with it. `when` gets helpers over the project folder.
|
|
16
|
+
const lang = (name) => ({ skills: [`${name}-patterns`, `${name}-testing`], agents: [`${name}-reviewer`, `${name}-build-resolver`], rules: [name] });
|
|
17
|
+
const DETECT = [
|
|
18
|
+
{ what: 'TypeScript/JavaScript', when: (p) => p.has('package.json') || p.has('tsconfig.json'), ...lang('typescript') },
|
|
19
|
+
{ what: 'Python', when: (p) => p.has('pyproject.toml') || p.has('requirements.txt') || p.has('setup.py') || p.has('Pipfile'), ...lang('python') },
|
|
20
|
+
{ what: 'Go', when: (p) => p.has('go.mod'), ...lang('go') },
|
|
21
|
+
{ what: 'Rust', when: (p) => p.has('Cargo.toml'), ...lang('rust') },
|
|
22
|
+
{ what: 'Java', when: (p) => p.has('pom.xml') || p.has('build.gradle') || p.ext('.java'), ...lang('java') },
|
|
23
|
+
{ what: 'Kotlin', when: (p) => p.has('build.gradle.kts') || p.ext('.kt'), ...lang('kotlin') },
|
|
24
|
+
{ what: 'C#', when: (p) => p.ext('.csproj') || p.ext('.sln'), ...lang('csharp') },
|
|
25
|
+
{ what: 'F#', when: (p) => p.ext('.fsproj'), ...lang('fsharp') },
|
|
26
|
+
{ what: 'C++', when: (p) => p.has('CMakeLists.txt') || p.ext('.cpp') || p.ext('.hpp'), ...lang('cpp') },
|
|
27
|
+
{ what: 'Swift', when: (p) => p.has('Package.swift') || p.ext('.xcodeproj'), ...lang('swift') },
|
|
28
|
+
{ what: 'Dart', when: (p) => p.has('pubspec.yaml'), ...lang('dart') },
|
|
29
|
+
{ what: 'PHP', when: (p) => p.has('composer.json'), ...lang('php') },
|
|
30
|
+
{ what: 'Ruby', when: (p) => p.has('Gemfile'), ...lang('ruby') },
|
|
31
|
+
{ what: 'Perl', when: (p) => p.has('cpanfile') || p.has('Makefile.PL'), ...lang('perl') },
|
|
32
|
+
{ what: 'React', when: (p) => p.dep('react'), skills: ['react-patterns', 'react-performance'], agents: ['react-reviewer'] },
|
|
33
|
+
{ what: 'Next.js', when: (p) => p.dep('next'), skills: ['nextjs-patterns'] },
|
|
34
|
+
{ what: 'Vue', when: (p) => p.dep('vue'), skills: ['vue-patterns'], agents: ['vue-reviewer'] },
|
|
35
|
+
{ what: 'Nuxt', when: (p) => p.dep('nuxt'), skills: ['nuxt-patterns'] },
|
|
36
|
+
{ what: 'Angular', when: (p) => p.dep('@angular/core'), skills: ['angular-patterns'] },
|
|
37
|
+
{ what: 'Svelte', when: (p) => p.dep('svelte'), skills: ['svelte-patterns'] },
|
|
38
|
+
{ what: 'React Native', when: (p) => p.dep('react-native'), skills: ['react-native-patterns'] },
|
|
39
|
+
{ what: 'NestJS', when: (p) => p.dep('@nestjs/core'), skills: ['nestjs-patterns'] },
|
|
40
|
+
{ what: 'Node HTTP server', when: (p) => p.dep('express') || p.dep('fastify'), skills: ['node-backend-patterns'] },
|
|
41
|
+
{ what: 'Prisma', when: (p) => p.dep('prisma') || p.dep('@prisma/client'), skills: ['prisma-patterns'] },
|
|
42
|
+
{ what: 'Vite', when: (p) => p.dep('vite'), skills: ['vite-patterns'] },
|
|
43
|
+
{ what: 'Playwright', when: (p) => p.dep('@playwright/test'), skills: ['e2e-testing-playwright'], agents: ['e2e-runner'] },
|
|
44
|
+
{ what: 'Django', when: (p) => p.py('django'), skills: ['django-patterns'], agents: ['django-reviewer'] },
|
|
45
|
+
{ what: 'FastAPI', when: (p) => p.py('fastapi'), skills: ['fastapi-patterns'], agents: ['fastapi-reviewer'] },
|
|
46
|
+
{ what: 'Flask', when: (p) => p.py('flask'), skills: ['flask-patterns'] },
|
|
47
|
+
{ what: 'PyTorch', when: (p) => p.py('torch'), skills: ['pytorch-patterns'], agents: ['mle-reviewer'] },
|
|
48
|
+
{ what: 'Rails', when: (p) => /\brails\b/.test(p.read('Gemfile')), skills: ['rails-patterns'] },
|
|
49
|
+
{ what: 'Laravel', when: (p) => /laravel\/framework/.test(p.read('composer.json')), skills: ['laravel-patterns'] },
|
|
50
|
+
{ what: 'Spring Boot', when: (p) => /spring-boot/.test(p.read('pom.xml') + p.read('build.gradle') + p.read('build.gradle.kts')), skills: ['spring-boot-patterns', 'jpa-hibernate'] },
|
|
51
|
+
{ what: 'Quarkus', when: (p) => /io\.quarkus/.test(p.read('pom.xml') + p.read('build.gradle') + p.read('build.gradle.kts')), skills: ['quarkus-patterns'] },
|
|
52
|
+
{ what: 'Ktor', when: (p) => /io\.ktor/.test(p.read('build.gradle.kts') + p.read('build.gradle')), skills: ['ktor-patterns'] },
|
|
53
|
+
{ what: 'ASP.NET Core', when: (p) => p.ext('.csproj') && /Microsoft\.NET\.Sdk\.Web|AspNetCore/.test(p.readExt('.csproj')), skills: ['aspnet-core-patterns'] },
|
|
54
|
+
{ what: 'EF Core', when: (p) => /EntityFrameworkCore/.test(p.readExt('.csproj')), skills: ['ef-core-patterns'] },
|
|
55
|
+
{ what: 'Flutter', when: (p) => /\bflutter:/.test(p.read('pubspec.yaml')), skills: ['flutter-patterns'], agents: ['flutter-reviewer'] },
|
|
56
|
+
{ what: 'Docker', when: (p) => p.has('Dockerfile') || p.has('compose.yaml') || p.has('docker-compose.yml'), skills: ['docker-patterns'] },
|
|
57
|
+
{ what: 'Terraform', when: (p) => p.ext('.tf'), skills: ['terraform-patterns'], agents: ['infra-reviewer'] },
|
|
58
|
+
{ what: 'GitHub Actions', when: (p) => p.has('.github/workflows'), skills: ['github-actions-ci'] },
|
|
59
|
+
{ what: 'dbt', when: (p) => p.has('dbt_project.yml'), skills: ['dbt-patterns'] },
|
|
60
|
+
{ what: 'Solidity', when: (p) => p.has('foundry.toml') || p.has('hardhat.config.ts') || p.has('hardhat.config.js'), skills: ['solidity-patterns', 'smart-contract-security'], agents: ['solidity-reviewer'] },
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
// Read-only view of a project folder (top level only, plus package.json/pyproject dependencies).
|
|
64
|
+
function inspect(dir) {
|
|
65
|
+
let entries = [];
|
|
66
|
+
try {
|
|
67
|
+
entries = fs.readdirSync(dir);
|
|
68
|
+
} catch {
|
|
69
|
+
// unreadable or missing folder: nothing detected
|
|
70
|
+
}
|
|
71
|
+
const read = (f) => {
|
|
72
|
+
try {
|
|
73
|
+
return fs.readFileSync(path.join(dir, f), 'utf8');
|
|
74
|
+
} catch {
|
|
75
|
+
return '';
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
let pkg = {};
|
|
79
|
+
try {
|
|
80
|
+
pkg = JSON.parse(read('package.json') || '{}');
|
|
81
|
+
} catch {
|
|
82
|
+
// invalid package.json: no JS dependencies detected
|
|
83
|
+
}
|
|
84
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies, ...pkg.peerDependencies };
|
|
85
|
+
const python = (read('pyproject.toml') + read('requirements.txt') + read('setup.py') + read('Pipfile')).toLowerCase();
|
|
86
|
+
return {
|
|
87
|
+
has: (f) => fs.existsSync(path.join(dir, f)),
|
|
88
|
+
ext: (e) => entries.some((f) => f.endsWith(e)),
|
|
89
|
+
read,
|
|
90
|
+
readExt: (e) => entries.filter((f) => f.endsWith(e)).map(read).join('\n'),
|
|
91
|
+
dep: (name) => Object.prototype.hasOwnProperty.call(deps, name),
|
|
92
|
+
py: (name) => new RegExp(`(^|[\\s"'\\[,])${name}([\\s"'<>=~!\\],;]|$)`, 'm').test(python),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function detect(dir) {
|
|
97
|
+
const p = inspect(dir);
|
|
98
|
+
return DETECT.filter((d) => d.when(p));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Resolves profile names to { all, skills, agents, commands, hooks: Set, detected: [labels] }.
|
|
102
|
+
function resolveProfiles(names, { catalog, cwd = process.cwd(), profiles = loadProfiles() }) {
|
|
103
|
+
const out = { all: false, detected: [], ...Object.fromEntries(KINDS.map((k) => [k, new Set()])) };
|
|
104
|
+
const seen = new Set();
|
|
105
|
+
const visit = (name, chain = []) => {
|
|
106
|
+
const p = profiles[name];
|
|
107
|
+
if (!p) throw new Error(`unknown profile "${name}". Known: ${Object.keys(profiles).join(', ')}`);
|
|
108
|
+
if (chain.includes(name)) throw new Error(`profile "${name}" extends itself: ${[...chain, name].join(' -> ')}`);
|
|
109
|
+
if (seen.has(name)) return;
|
|
110
|
+
seen.add(name);
|
|
111
|
+
for (const parent of p.extends || []) visit(parent, [...chain, name]);
|
|
112
|
+
if (p.all) out.all = true;
|
|
113
|
+
for (const k of KINDS) for (const n of p[k] || []) out[k].add(n);
|
|
114
|
+
// MCP servers reach the network and run code, so they come only from a profile's explicit `mcp` list.
|
|
115
|
+
for (const cat of p.categories || []) for (const k of KINDS.filter((x) => x !== 'mcp')) for (const item of catalog[k] || []) if (item.category === cat) out[k].add(item.dir);
|
|
116
|
+
if (p.detect) {
|
|
117
|
+
for (const d of detect(cwd)) {
|
|
118
|
+
out.detected.push(d.what);
|
|
119
|
+
for (const k of ['skills', 'agents', 'rules']) for (const n of d[k] || []) out[k].add(n);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
for (const n of names) visit(n);
|
|
124
|
+
out.detected = [...new Set(out.detected)];
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = { loadProfiles, resolveProfiles, detect, DETECT, PROFILES_FILE };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Turns one portable agent or command into the file each host expects.
|
|
4
|
+
// Every rendered file carries a `# kete-loom <name>@<version>` comment (YAML or TOML),
|
|
5
|
+
// which is how install and uninstall recognise files kete-loom wrote.
|
|
6
|
+
|
|
7
|
+
const TOOLS = ['read', 'search', 'shell', 'edit', 'web'];
|
|
8
|
+
// `# kete-loom name@version` inside YAML/TOML, or `<!-- kete-loom name@version -->` in plain Markdown.
|
|
9
|
+
const MARKER_RE = /^(?:# |<!-- )kete-loom [a-z0-9-]+@/m;
|
|
10
|
+
|
|
11
|
+
const CLAUDE_TOOLS = { read: ['Read'], search: ['Grep', 'Glob'], shell: ['Bash'], edit: ['Edit', 'Write'], web: ['WebFetch', 'WebSearch'] };
|
|
12
|
+
const COPILOT_TOOLS = { read: 'read', search: 'search', shell: 'execute', edit: 'edit', web: 'web' };
|
|
13
|
+
const KIRO_TOOLS = { read: ['read'], search: ['glob', 'grep'], shell: ['shell'], edit: ['write'], web: ['web_search', 'web_fetch'] };
|
|
14
|
+
|
|
15
|
+
const marker = (item) => `# kete-loom ${item.dir}@${item.version}`;
|
|
16
|
+
// JSON strings are valid YAML double-quoted scalars and TOML basic strings (TOML also bans raw DEL).
|
|
17
|
+
const str = (s) => JSON.stringify(String(s)).replace(/\x7f/g, '\\u007f');
|
|
18
|
+
const body = (item) => item.body.replace(/^\s*\n/, '');
|
|
19
|
+
|
|
20
|
+
function markdown(item, fields) {
|
|
21
|
+
const lines = [marker(item)];
|
|
22
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
23
|
+
if (v === undefined || v === null) continue;
|
|
24
|
+
if (Array.isArray(v)) lines.push(`${k}: [${v.map(str).join(', ')}]`);
|
|
25
|
+
else if (typeof v === 'object') {
|
|
26
|
+
lines.push(`${k}:`);
|
|
27
|
+
for (const [k2, v2] of Object.entries(v)) lines.push(` ${k2}: ${v2}`);
|
|
28
|
+
} else lines.push(`${k}: ${typeof v === 'string' ? str(v) : v}`);
|
|
29
|
+
}
|
|
30
|
+
return `---\n${lines.join('\n')}\n---\n\n${body(item)}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Multi-line literal ('''...''') keeps prompts readable; it can't hold ''' or control characters other than tab/newline.
|
|
34
|
+
const tomlValue = (v) => (/\n/.test(v) && !v.includes("'''") && !/[\x00-\x08\x0b-\x1f\x7f]/.test(v) ? `'''\n${v}'''` : str(v));
|
|
35
|
+
|
|
36
|
+
function toml(item, fields) {
|
|
37
|
+
return [marker(item), ...Object.entries(fields).filter(([, v]) => v !== undefined).map(([k, v]) => `${k} = ${tomlValue(v)}`)].join('\n') + '\n';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const has = (item, tool) => !item.tools || item.tools.includes(tool);
|
|
41
|
+
|
|
42
|
+
// OpenCode restricts with permissions rather than a tool list.
|
|
43
|
+
function opencodePermission(item) {
|
|
44
|
+
if (!item.tools) return undefined;
|
|
45
|
+
const p = {};
|
|
46
|
+
if (!has(item, 'edit')) p.edit = 'deny';
|
|
47
|
+
if (!has(item, 'shell')) p.bash = 'deny';
|
|
48
|
+
if (!has(item, 'web')) p.webfetch = 'deny';
|
|
49
|
+
return Object.keys(p).length ? p : undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// shortcut: Codex and Gemini agents inherit every tool (Codex can't set a sandbox per agent; Gemini's
|
|
53
|
+
// tool names aren't mapped yet), so `kete-loom/tools` is advisory there. Map them when hosts allow it.
|
|
54
|
+
const AGENT = {
|
|
55
|
+
claude: (a) => ({ file: `${a.dir}.md`, content: markdown(a, { name: a.dir, description: a.description, tools: a.tools && a.tools.flatMap((t) => CLAUDE_TOOLS[t]).join(', ') }) }),
|
|
56
|
+
codex: (a) => ({ file: `${a.dir}.toml`, content: toml(a, { name: a.dir, description: a.description, developer_instructions: body(a) }) }),
|
|
57
|
+
opencode: (a) => ({ file: `${a.dir}.md`, content: markdown(a, { description: a.description, mode: 'subagent', permission: opencodePermission(a) }) }),
|
|
58
|
+
cursor: (a) => ({ file: `${a.dir}.md`, content: markdown(a, { name: a.dir, description: a.description, readonly: a.tools && !has(a, 'edit') && !has(a, 'shell') ? true : undefined }) }),
|
|
59
|
+
gemini: (a) => ({ file: `${a.dir}.md`, content: markdown(a, { name: a.dir, description: a.description }) }),
|
|
60
|
+
kete: (a) => AGENT.opencode(a), // Kete Code reads the OpenCode agent format
|
|
61
|
+
copilot: (a) => ({ file: `${a.dir}.agent.md`, content: markdown(a, { name: a.dir, description: a.description, tools: a.tools && a.tools.map((t) => COPILOT_TOOLS[t]) }) }),
|
|
62
|
+
kiro: (a) => ({ file: `${a.dir}.md`, content: markdown(a, { name: a.dir, description: a.description, tools: a.tools && a.tools.flatMap((t) => KIRO_TOOLS[t]) }) }),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const COMMAND = {
|
|
66
|
+
claude: (c) => ({ file: `${c.dir}.md`, content: markdown(c, { description: c.description, 'argument-hint': c.argumentHint }) }),
|
|
67
|
+
opencode: (c) => ({ file: `${c.dir}.md`, content: markdown(c, { description: c.description }) }),
|
|
68
|
+
kete: (c) => COMMAND.opencode(c),
|
|
69
|
+
// Kiro prompts are plain Markdown and take $ARGUMENTS as is.
|
|
70
|
+
kiro: (c) => ({ file: `${c.dir}.md`, content: `<!-- ${marker(c).slice(2)} -->\n${body(c)}` }),
|
|
71
|
+
gemini: (c) => ({ file: `${c.dir}.toml`, content: toml(c, { description: c.description, prompt: body(c).replace(/\$ARGUMENTS/g, '{{args}}') }) }),
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Rules: path-scoped where the host supports it (Claude Code, Copilot, Cursor). Hosts that can't scope
|
|
75
|
+
// get plain Markdown that says which files it is about (scripts/lib/rules-install.js).
|
|
76
|
+
const appliesTo = (r) => (r.paths.length ? `Applies when working on files matching: ${r.paths.join(', ')}.\n\n` : '');
|
|
77
|
+
const RULE = {
|
|
78
|
+
claude: (r) => ({ file: `${r.dir}.md`, content: markdown(r, { paths: r.paths.length ? r.paths : undefined }) }),
|
|
79
|
+
copilot: (r) => ({ file: `${r.dir}.instructions.md`, content: markdown(r, { applyTo: r.paths.length ? r.paths.join(',') : '**' }) }),
|
|
80
|
+
cursor: (r) => ({ file: `${r.dir}.mdc`, content: markdown(r, { description: r.description, globs: r.paths.length ? r.paths.join(', ') : undefined, alwaysApply: !r.paths.length }) }),
|
|
81
|
+
// Steering front matter must come first; inclusion: fileMatch scopes it like a path rule.
|
|
82
|
+
kiro: (r) => ({ file: `${r.dir}.md`, content: markdown(r, r.paths.length ? { inclusion: 'fileMatch', fileMatchPattern: r.paths } : { inclusion: 'always' }) }),
|
|
83
|
+
plain: (r) => ({ file: `${r.dir}.md`, content: `<!-- ${marker(r).slice(2)} -->\n# ${r.dir} rules\n\n${appliesTo(r)}${body(r)}` }),
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
function render(item, target) {
|
|
87
|
+
if (item.kind === 'rule') return RULE[target] ? RULE[target](item) : null;
|
|
88
|
+
const table = item.kind === 'agent' ? AGENT : COMMAND;
|
|
89
|
+
return table[target] ? table[target](item) : null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = { TOOLS, MARKER_RE, render };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Rules for hosts that can't scope instructions to file paths (verified against host docs and Kete
|
|
4
|
+
// Code's source, Oct 2026). User scope only. Each rule is written to a kete-loom folder that is the
|
|
5
|
+
// source of truth, and the host's instructions are pointed at that folder:
|
|
6
|
+
// Kete Code `@kete-loom/rules/<name>.md` imports in ~/.config/kete/AGENTS.md (relative to that file)
|
|
7
|
+
// Gemini CLI `@./kete-loom/rules/<name>.md` imports in ~/.gemini/GEMINI.md
|
|
8
|
+
// OpenCode one `<folder>/*.md` entry in the `instructions` array of ~/.config/opencode/opencode.json
|
|
9
|
+
// Codex no imports: the rule text inline in ~/.codex/AGENTS.md, kept under its 32 KiB limit
|
|
10
|
+
// Shared files are only changed between our markers, backed up and written atomically.
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const { render } = require('./render');
|
|
15
|
+
const { readJson, writeAtomic, CommentedFileError } = require('./hooks-install');
|
|
16
|
+
|
|
17
|
+
const START = '<!-- kete-loom:rules:start (managed by kete-loom: edit outside this block) -->';
|
|
18
|
+
const END = '<!-- kete-loom:rules:end -->';
|
|
19
|
+
const BLOCK_RE = /\n*<!-- kete-loom:rules:start[^\n]*-->[\s\S]*?<!-- kete-loom:rules:end -->\n?/;
|
|
20
|
+
const CODEX_MAX_BYTES = 32768;
|
|
21
|
+
|
|
22
|
+
const HOSTS = {
|
|
23
|
+
kete: { dir: (h) => path.join(h, '.config', 'kete', 'kete-loom', 'rules'), file: (h) => path.join(h, '.config', 'kete', 'AGENTS.md'), mode: 'import', ref: (n) => `@kete-loom/rules/${n}.md` },
|
|
24
|
+
gemini: { dir: (h) => path.join(h, '.gemini', 'kete-loom', 'rules'), file: (h) => path.join(h, '.gemini', 'GEMINI.md'), mode: 'import', ref: (n) => `@./kete-loom/rules/${n}.md` },
|
|
25
|
+
opencode: { dir: (h) => path.join(h, '.config', 'opencode', 'kete-loom', 'rules'), file: (h) => path.join(h, '.config', 'opencode', 'opencode.json'), mode: 'instructions' },
|
|
26
|
+
codex: { dir: (h) => path.join(h, '.codex', 'kete-loom', 'rules'), file: (h) => path.join(h, '.codex', 'AGENTS.md'), mode: 'inline' },
|
|
27
|
+
};
|
|
28
|
+
const RULE_HOSTS = Object.keys(HOSTS);
|
|
29
|
+
|
|
30
|
+
const installedNames = (dir) => (fs.existsSync(dir) ? fs.readdirSync(dir).filter((f) => f.endsWith('.md')).map((f) => f.slice(0, -3)).sort() : []);
|
|
31
|
+
|
|
32
|
+
// Replaces our block in a Markdown file (or appends one); an empty block is removed.
|
|
33
|
+
function withBlock(text, lines) {
|
|
34
|
+
const rest = text.replace(BLOCK_RE, '\n').replace(/\n{3,}$/, '\n\n');
|
|
35
|
+
if (!lines.length) return rest.trim() ? `${rest.trimEnd()}\n` : '';
|
|
36
|
+
return `${rest.trimEnd() ? `${rest.trimEnd()}\n\n` : ''}${START}\n${lines.join('\n')}\n${END}\n`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Points the host at whatever is in its kete-loom rules folder now. Returns a skip reason or null.
|
|
40
|
+
function sync(host, home, dryRun) {
|
|
41
|
+
const h = HOSTS[host];
|
|
42
|
+
const dir = h.dir(home);
|
|
43
|
+
const names = installedNames(dir);
|
|
44
|
+
const file = h.file(home);
|
|
45
|
+
if (h.mode === 'instructions') {
|
|
46
|
+
let data;
|
|
47
|
+
try {
|
|
48
|
+
data = readJson(file);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return e instanceof CommentedFileError ? `${e.message}. Add "${path.join(dir, '*.md')}" to its "instructions" array yourself.` : e.message;
|
|
51
|
+
}
|
|
52
|
+
const entry = path.join(dir, '*.md');
|
|
53
|
+
const list = (Array.isArray(data.instructions) ? data.instructions : []).filter((i) => i !== entry);
|
|
54
|
+
if (names.length) list.push(entry);
|
|
55
|
+
if (list.length) data.instructions = list;
|
|
56
|
+
else delete data.instructions;
|
|
57
|
+
if (!dryRun) {
|
|
58
|
+
if (Object.keys(data).length) writeAtomic(file, JSON.stringify(data, null, 2) + '\n');
|
|
59
|
+
else if (fs.existsSync(file)) fs.rmSync(file);
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const lines = h.mode === 'import'
|
|
64
|
+
? names.map(h.ref)
|
|
65
|
+
: names.map((n) => fs.readFileSync(path.join(dir, `${n}.md`), 'utf8').replace(/^<!--[^\n]*-->\n/, '').trimEnd());
|
|
66
|
+
const old = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
|
|
67
|
+
const next = withBlock(old, h.mode === 'inline' ? [lines.join('\n\n')].filter(Boolean) : lines);
|
|
68
|
+
if (h.mode === 'inline' && Buffer.byteLength(next) > CODEX_MAX_BYTES) {
|
|
69
|
+
return `~/.codex/AGENTS.md would be ${Buffer.byteLength(next)} bytes, over Codex's ${CODEX_MAX_BYTES}-byte limit; install fewer rules for Codex`;
|
|
70
|
+
}
|
|
71
|
+
if (!dryRun && next !== old) {
|
|
72
|
+
if (next) writeAtomic(file, next);
|
|
73
|
+
else fs.rmSync(file);
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Installs rules for one host. Returns result rows like the main installer's.
|
|
79
|
+
function installRules(host, rules, { home, dryRun = false }) {
|
|
80
|
+
const h = HOSTS[host];
|
|
81
|
+
const dir = h.dir(home);
|
|
82
|
+
const before = installedNames(dir);
|
|
83
|
+
const rows = rules.map((r) => ({ target: host, kind: 'rule', name: r.dir, action: before.includes(r.dir) ? 'updated' : 'installed', dest: h.file(home) }));
|
|
84
|
+
if (dryRun) return rows;
|
|
85
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
86
|
+
const previous = new Map(before.map((n) => [n, fs.readFileSync(path.join(dir, `${n}.md`), 'utf8')]));
|
|
87
|
+
for (const r of rules) fs.writeFileSync(path.join(dir, `${r.dir}.md`), render(r, 'plain').content);
|
|
88
|
+
const reason = sync(host, home, false);
|
|
89
|
+
if (reason) {
|
|
90
|
+
// Leave the folder as it was so it keeps matching what the host loads.
|
|
91
|
+
for (const r of rules) {
|
|
92
|
+
if (previous.has(r.dir)) fs.writeFileSync(path.join(dir, `${r.dir}.md`), previous.get(r.dir));
|
|
93
|
+
else fs.rmSync(path.join(dir, `${r.dir}.md`), { force: true });
|
|
94
|
+
}
|
|
95
|
+
return rows.map((row) => ({ ...row, action: 'skipped', reason }));
|
|
96
|
+
}
|
|
97
|
+
return rows;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Removes the named rules (or all kete-loom rules) for one host.
|
|
101
|
+
function uninstallRules(host, names, { home, dryRun = false }) {
|
|
102
|
+
const h = HOSTS[host];
|
|
103
|
+
const dir = h.dir(home);
|
|
104
|
+
const gone = installedNames(dir).filter((n) => !names || names.includes(n));
|
|
105
|
+
if (!gone.length) return [];
|
|
106
|
+
if (!dryRun) {
|
|
107
|
+
for (const n of gone) fs.rmSync(path.join(dir, `${n}.md`));
|
|
108
|
+
sync(host, home, false);
|
|
109
|
+
if (!installedNames(dir).length) {
|
|
110
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
111
|
+
const parent = path.dirname(dir);
|
|
112
|
+
if (fs.existsSync(parent) && !fs.readdirSync(parent).length) fs.rmdirSync(parent);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return gone.map((n) => ({ target: host, kind: 'rule', name: n, action: 'removed', dest: h.file(home) }));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = { RULE_HOSTS, HOSTS, installRules, uninstallRules, installedNames, withBlock };
|