@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,75 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Blocks shell commands that destroy data or systems before they run.
|
|
4
|
+
// check({ command, cwd }) -> { block: true, reason } | { block: false }
|
|
5
|
+
// Pure and dependency-free: the kete-loom hook runner calls it for every host.
|
|
6
|
+
|
|
7
|
+
// shortcut: regex over shell segments, not a shell parser; quoting tricks can evade it. It is a
|
|
8
|
+
// seatbelt against agent mistakes, not a security boundary.
|
|
9
|
+
const SEGMENT_SPLIT = /\s*(?:&&|\|\||;|\n)\s*/;
|
|
10
|
+
const SUDO = /^(?:sudo|doas)\s+(?:-\S+\s+)*/;
|
|
11
|
+
|
|
12
|
+
const ROOTISH = String.raw`(?:/|/\*|~|~/|~/\*|\$HOME|\$\{HOME\}|\$HOME/\*|\.\.?/?|\*)`;
|
|
13
|
+
const PROTECTED = new Set(['main', 'master', 'trunk', 'production', 'prod', 'release']);
|
|
14
|
+
|
|
15
|
+
// `git push -f origin feature/main-fix` is fine; `git push -f origin main` or `+main` or `HEAD:main` is not.
|
|
16
|
+
function forcePushesProtected(s) {
|
|
17
|
+
const args = s.split(/\s+/).slice(2);
|
|
18
|
+
const force = args.some((a) => a === '-f' || a === '--force' || /^-[a-zA-Z]*f[a-zA-Z]*$/.test(a)) && !args.some((a) => a.startsWith('--force-with-lease'));
|
|
19
|
+
const refs = args.filter((a) => !a.startsWith('-')).slice(1); // first non-flag is the remote
|
|
20
|
+
return refs.some((r) => {
|
|
21
|
+
const dst = r.replace(/^\+/, '').split(':').pop().replace(/^refs\/heads\//, '');
|
|
22
|
+
return PROTECTED.has(dst) && (force || r.startsWith('+'));
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const RULES = [
|
|
27
|
+
{
|
|
28
|
+
// rm with both recursive and force flags, aimed at /, ~, $HOME, . or ..
|
|
29
|
+
test: (s) => /^rm\s/.test(s) && /\s-(?:[a-zA-Z]*r[a-zA-Z]*f|[a-zA-Z]*f[a-zA-Z]*r)\b|\s--recursive\b.*\s--force\b|\s--force\b.*\s--recursive\b|\s-r\b.*\s-f\b|\s-f\b.*\s-r\b/.test(s)
|
|
30
|
+
&& new RegExp(String.raw`\s["']?${ROOTISH}["']?(?:\s|$)`).test(s) || /^rm\s.*--no-preserve-root/.test(s),
|
|
31
|
+
reason: 'deletes everything under /, your home folder or the whole working tree. Delete the specific paths you mean instead.',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
test: (s) => /^git\s+push\b/.test(s) && forcePushesProtected(s),
|
|
35
|
+
reason: 'force-pushes a protected branch (main, master, release...). Push a new branch, or use --force-with-lease on your own branch.',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
test: (s) => /^(?:mkfs(?:\.\w+)?|wipefs|fdisk|sfdisk|parted)\b/.test(s) || /^dd\b.*\bof=\/dev\/(?!null\b|zero\b|stdout\b|stderr\b)/.test(s) || /(?:^|\s)>\s*\/dev\/(?:sd|nvme|disk|hd|vd)\w*/.test(s),
|
|
39
|
+
reason: 'writes directly to a disk device, which destroys its data.',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
test: (s) => /^(?:chmod|chown|chgrp)\s+(?:-\S*R\S*|--recursive)\b/.test(s) && new RegExp(String.raw`\s["']?(?:/|~|\$HOME|\$\{HOME\})["']?(?:\s|$)`).test(s),
|
|
43
|
+
reason: 'recursively changes permissions or ownership of / or your home folder.',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
test: (s) => /^(?:curl|wget)\b/.test(s) && /\|\s*(?:sudo\s+)?(?:ba|z|da|k)?sh\b/.test(s),
|
|
47
|
+
reason: 'pipes a script from the internet straight into a shell. Download it, read it, then run it.',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
test: (s) => /\bdrop\s+(?:database|schema)\b/i.test(s) && /^(?:psql|mysql|mariadb|sqlite3|sqlcmd|mongosh|clickhouse(?:-client)?|cockroach)\b/.test(s),
|
|
51
|
+
reason: 'drops a whole database or schema.',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
test: (s) => /^(?:terraform|tofu)\s+(?:destroy\b|apply\b.*\s-destroy\b)/.test(s) && /\s-auto-approve\b/.test(s),
|
|
55
|
+
reason: 'destroys infrastructure without a review step. Run the plan and let a person approve it.',
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
function check(event) {
|
|
60
|
+
const command = typeof event.command === 'string' ? event.command : '';
|
|
61
|
+
// Checked on the whole command: a fork bomb contains `;`, which the segment split would cut.
|
|
62
|
+
if (/:\s*\(\s*\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;?\s*:/.test(command)) {
|
|
63
|
+
return { block: true, reason: 'Blocked by kete-loom (block-dangerous-commands): this command is a fork bomb.' };
|
|
64
|
+
}
|
|
65
|
+
// Pipes stay inside a segment so `curl ... | sh` is seen whole.
|
|
66
|
+
for (const raw of command.split(SEGMENT_SPLIT)) {
|
|
67
|
+
const segment = raw.trim().replace(SUDO, '');
|
|
68
|
+
if (!segment) continue;
|
|
69
|
+
const rule = RULES.find((r) => r.test(segment));
|
|
70
|
+
if (rule) return { block: true, reason: `Blocked by kete-loom (block-dangerous-commands): \`${segment.slice(0, 120)}\` ${rule.reason}` };
|
|
71
|
+
}
|
|
72
|
+
return { block: false };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = { check };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: block-no-verify
|
|
3
|
+
description: Stops the agent from skipping the project's git hooks with git commit --no-verify, -n, HUSKY=0, SKIP= or a swapped core.hooksPath, so pre-commit checks keep running.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
kete-loom/version: "0.1.0"
|
|
7
|
+
kete-loom/category: security
|
|
8
|
+
kete-loom/risk: read-only
|
|
9
|
+
kete-loom/event: "pre-shell"
|
|
10
|
+
kete-loom/owner: "Kete-org"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Block --no-verify
|
|
14
|
+
|
|
15
|
+
Agents that hit a failing pre-commit or pre-push hook often retry with `--no-verify`. This hook refuses that, so the agent fixes what the hook reported instead.
|
|
16
|
+
|
|
17
|
+
| Blocks | Example |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `--no-verify` on commit, push, merge, rebase, am, cherry-pick, revert | `git commit --no-verify -m wip` |
|
|
20
|
+
| `-n` on `git commit` (short for `--no-verify`) | `git commit -nm wip` |
|
|
21
|
+
| Turning Husky off | `HUSKY=0 git commit` |
|
|
22
|
+
| Skipping pre-commit hooks | `SKIP=eslint git commit` |
|
|
23
|
+
| Pointing git at another hooks folder | `git -c core.hooksPath=/dev/null commit` |
|
|
24
|
+
|
|
25
|
+
Commit messages that mention `--no-verify` are fine. If you really need to skip hooks, run the command yourself.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_block": [
|
|
3
|
+
"git commit --no-verify -m 'wip'",
|
|
4
|
+
"git commit -m 'wip' --no-verify",
|
|
5
|
+
"git commit -n -m 'skip'",
|
|
6
|
+
"git commit -nm 'skip'",
|
|
7
|
+
"git commit -am 'x' -n",
|
|
8
|
+
"git push --no-verify origin feature/x",
|
|
9
|
+
"git -C app commit --no-verify -m x",
|
|
10
|
+
"HUSKY=0 git commit -m 'x'",
|
|
11
|
+
"HUSKY_SKIP_HOOKS=1 git push",
|
|
12
|
+
"git -c core.hooksPath=/dev/null commit -m x",
|
|
13
|
+
"git config core.hooksPath /tmp/empty",
|
|
14
|
+
"SKIP=eslint,prettier git commit -m 'x'",
|
|
15
|
+
"npm test && git commit --no-verify -m x",
|
|
16
|
+
"git -c user.name=bot commit --no-verify -m x"
|
|
17
|
+
],
|
|
18
|
+
"should_allow": [
|
|
19
|
+
"git commit -m 'fix: handle empty input'",
|
|
20
|
+
"git commit -am 'feat: add retry' ",
|
|
21
|
+
"git push origin feature/x",
|
|
22
|
+
"git log --no-merges -n 5",
|
|
23
|
+
"git config --get core.hooksPath",
|
|
24
|
+
"git commit -m 'docs: explain --no-verify policy'",
|
|
25
|
+
"npx husky init",
|
|
26
|
+
"git merge --no-ff feature/x",
|
|
27
|
+
"git commit -m \"skip -n flag docs\"",
|
|
28
|
+
"git -C app log -n 3"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Blocks shell commands that skip the project's git hooks (pre-commit, commit-msg, pre-push).
|
|
4
|
+
|
|
5
|
+
const SEGMENT_SPLIT = /\s*(?:&&|\|\||;|\n)\s*/;
|
|
6
|
+
// git's global options, including the ones that take a value (`-C dir`, `-c key=value`).
|
|
7
|
+
const GIT = String.raw`^git\s+(?:(?:-C|-c)\s+\S+\s+|--?[\w-]+(?:=\S+)?\s+)*`;
|
|
8
|
+
// Quoted text (commit messages) can mention --no-verify without using it.
|
|
9
|
+
const unquote = (s) => s.replace(/'[^']*'|"(?:[^"\\]|\\.)*"/g, "''");
|
|
10
|
+
|
|
11
|
+
const RULES = [
|
|
12
|
+
{
|
|
13
|
+
// --no-verify on any git subcommand that runs hooks; `-n` means --no-verify only for commit.
|
|
14
|
+
test: (s) => new RegExp(GIT + String.raw`(?:commit|push|merge|rebase|am|cherry-pick|revert)\b.*\s--no-verify\b`).test(s)
|
|
15
|
+
|| new RegExp(GIT + String.raw`commit\b(?:\s+\S+)*\s-(?!-)[a-zA-Z]*n[a-zA-Z]*(?:\s|$)`).test(s),
|
|
16
|
+
reason: 'skips the git hooks with --no-verify. Fix what the hook reports instead.',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
test: (s) => /^(?:\w+=\S*\s+)*(?:HUSKY|HUSKY_SKIP_HOOKS)=(?:0|false|1|true)\s+(?:\w+=\S*\s+)*git\b/.test(s)
|
|
20
|
+
&& /(?:^|\s)HUSKY=0\b|HUSKY_SKIP_HOOKS=(?:1|true)\b|HUSKY=false\b/.test(s),
|
|
21
|
+
reason: 'turns off Husky git hooks for this command. Fix what the hook reports instead.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
test: (s) => /^git\s+.*-c\s+core\.hooksPath=/.test(s) || /^git\s+config\b.*\bcore\.hooksPath\b/.test(s) && !/--get\b|--unset\b/.test(s) && /core\.hooksPath\s+\S+/.test(s),
|
|
25
|
+
reason: "points git at a different hooks folder, which bypasses the project's hooks.",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
test: (s) => /^(?:\w+=\S*\s+)*SKIP=\S+\s+(?:\w+=\S*\s+)*git\b/.test(s) || /^PRE_COMMIT_ALLOW_NO_CONFIG=1\s/.test(s),
|
|
29
|
+
reason: 'skips pre-commit hooks for this command. Fix what the hook reports instead.',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
function check(event) {
|
|
34
|
+
const command = typeof event.command === 'string' ? event.command : '';
|
|
35
|
+
for (const raw of command.split(SEGMENT_SPLIT)) {
|
|
36
|
+
const segment = raw.trim().replace(/^(?:sudo|doas)\s+/, '');
|
|
37
|
+
const rule = segment && RULES.find((r) => r.test(unquote(segment)));
|
|
38
|
+
if (rule) return { block: true, reason: `Blocked by kete-loom (block-no-verify): \`${segment.slice(0, 120)}\` ${rule.reason}` };
|
|
39
|
+
}
|
|
40
|
+
return { block: false };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { check };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: format-on-edit
|
|
3
|
+
description: After the agent edits a file, formats it with the formatter the project already uses (Biome or Prettier, Ruff or Black, gofmt, rustfmt), so agent changes match the project's style.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
kete-loom/version: "0.1.0"
|
|
7
|
+
kete-loom/category: coding
|
|
8
|
+
kete-loom/risk: runs-commands
|
|
9
|
+
kete-loom/event: "post-edit"
|
|
10
|
+
kete-loom/owner: "Kete-org"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Format on edit
|
|
14
|
+
|
|
15
|
+
Runs after each file write or edit and formats the changed file, only with a formatter the project has chosen:
|
|
16
|
+
|
|
17
|
+
| Files | Formatter | Used when |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| JS, TS, JSON, CSS, HTML, Markdown, YAML, Vue, Svelte… | Biome, else Prettier | the project has `biome.json` and a local Biome, or a local Prettier in `node_modules` |
|
|
20
|
+
| Python | Ruff, else Black | `[tool.ruff]`, `ruff.toml` or `[tool.black]` is configured and the tool is on your PATH |
|
|
21
|
+
| Go | `gofmt -w` | `gofmt` is on your PATH |
|
|
22
|
+
| Rust | `rustfmt`, with the edition from `Cargo.toml` | `rustfmt` is on your PATH |
|
|
23
|
+
|
|
24
|
+
It never installs anything, never formats files outside the project, never uses the network, and never fails an edit: if the formatter errors (for example on a half-finished edit), the file is left as the agent wrote it. Each run is limited to 15 seconds per file.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// After the agent edits files, formats each one with the formatter the project already uses.
|
|
4
|
+
// Never formats with a tool the project hasn't chosen, never touches files outside the project,
|
|
5
|
+
// never fails the edit. check({ files: [{ path }], cwd }) -> {}
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { execFileSync } = require('child_process');
|
|
10
|
+
|
|
11
|
+
const TIMEOUT_MS = 15000;
|
|
12
|
+
const WEB = /\.(?:[cm]?[jt]sx?|json|jsonc|css|scss|less|html|vue|svelte|astro|md|mdx|ya?ml|graphql)$/;
|
|
13
|
+
|
|
14
|
+
const exists = (p) => fs.existsSync(p);
|
|
15
|
+
const read = (p) => {
|
|
16
|
+
try {
|
|
17
|
+
return fs.readFileSync(p, 'utf8');
|
|
18
|
+
} catch {
|
|
19
|
+
return '';
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function onPath(bin) {
|
|
24
|
+
const exts = process.platform === 'win32' ? (process.env.PATHEXT || '.EXE;.CMD').split(';') : [''];
|
|
25
|
+
for (const dir of (process.env.PATH || '').split(path.delimiter)) {
|
|
26
|
+
for (const ext of exts) if (dir && exists(path.join(dir, bin + ext))) return path.join(dir, bin + ext);
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Nearest folder at or above `from` (but not above `root`) that contains `marker`.
|
|
32
|
+
function nearest(from, root, marker) {
|
|
33
|
+
for (let dir = from; ; dir = path.dirname(dir)) {
|
|
34
|
+
if (exists(path.join(dir, marker))) return dir;
|
|
35
|
+
if (dir === root || dir === path.dirname(dir)) return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function localBin(from, root, name) {
|
|
40
|
+
const dir = nearest(from, root, path.join('node_modules', '.bin', name));
|
|
41
|
+
return dir && path.join(dir, 'node_modules', '.bin', name);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Returns [program, args] for the project's formatter for this file, or null.
|
|
45
|
+
function formatterFor(file, root) {
|
|
46
|
+
const dir = path.dirname(file);
|
|
47
|
+
if (WEB.test(file)) {
|
|
48
|
+
const biomeDir = nearest(dir, root, 'biome.json') || nearest(dir, root, 'biome.jsonc');
|
|
49
|
+
const biome = biomeDir && localBin(dir, root, 'biome');
|
|
50
|
+
if (biome) return [biome, ['format', '--write', file]];
|
|
51
|
+
const prettier = localBin(dir, root, 'prettier');
|
|
52
|
+
if (prettier) return [prettier, ['--write', '--log-level', 'warn', file]];
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (file.endsWith('.py')) {
|
|
56
|
+
const pyproject = read(path.join(nearest(dir, root, 'pyproject.toml') || '', 'pyproject.toml'));
|
|
57
|
+
const ruffConfig = /\[tool\.ruff/.test(pyproject) || nearest(dir, root, 'ruff.toml') || nearest(dir, root, '.ruff.toml');
|
|
58
|
+
if (ruffConfig && onPath('ruff')) return [onPath('ruff'), ['format', file]];
|
|
59
|
+
if (/\[tool\.black/.test(pyproject) && onPath('black')) return [onPath('black'), ['-q', file]];
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
if (file.endsWith('.go') && onPath('gofmt')) return [onPath('gofmt'), ['-w', file]];
|
|
63
|
+
if (file.endsWith('.rs') && onPath('rustfmt')) {
|
|
64
|
+
const cargo = nearest(dir, root, 'Cargo.toml');
|
|
65
|
+
const edition = (/^\s*edition\s*=\s*"(\d{4})"/m.exec(read(path.join(cargo || '', 'Cargo.toml'))) || [])[1];
|
|
66
|
+
return [onPath('rustfmt'), [...(edition ? ['--edition', edition] : []), file]];
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function check(event) {
|
|
72
|
+
const root = path.resolve(event.cwd || process.cwd());
|
|
73
|
+
for (const f of event.files || []) {
|
|
74
|
+
if (!f || typeof f.path !== 'string') continue;
|
|
75
|
+
const file = path.resolve(root, f.path);
|
|
76
|
+
const rel = path.relative(root, file);
|
|
77
|
+
if (!rel || rel.startsWith('..') || path.isAbsolute(rel) || !exists(file)) continue;
|
|
78
|
+
const fmt = formatterFor(file, root);
|
|
79
|
+
if (!fmt) continue;
|
|
80
|
+
try {
|
|
81
|
+
execFileSync(fmt[0], fmt[1], { cwd: root, stdio: 'ignore', timeout: TIMEOUT_MS });
|
|
82
|
+
} catch {
|
|
83
|
+
// A formatter error (syntax error mid-edit, missing plugin) must never fail the edit.
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return {};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = { check, formatterFor };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: guard-secrets
|
|
3
|
+
description: Stops the agent from writing API keys, tokens, passwords, connection strings or private keys into files, using the same rules as the security-review secret scanner.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
kete-loom/version: "0.1.0"
|
|
7
|
+
kete-loom/category: security
|
|
8
|
+
kete-loom/risk: read-only
|
|
9
|
+
kete-loom/event: "pre-edit"
|
|
10
|
+
kete-loom/owner: "Kete-org"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Guard secrets
|
|
14
|
+
|
|
15
|
+
Runs before every file write or edit and checks the new text for secrets: AWS, GitHub, Anthropic, OpenAI, Paystack, Stripe, Slack and Google keys, private key blocks, JWTs, connection strings with passwords, and hard-coded `password`/`api_key`/`token` assignments. If it finds one, the edit is refused and the agent is told to read the value from an environment variable or secret store.
|
|
16
|
+
|
|
17
|
+
Placeholders such as `<your-api-key>`, `${API_KEY}`, `process.env...`, `changeme` or `your_key_here` are allowed. The reason message masks the value, so the secret isn't echoed back.
|
|
18
|
+
|
|
19
|
+
It uses the same rules as `skills/security-review/scripts/scan-secrets.js`, which a test keeps in sync. Not available in Cursor, which has no documented hook before an edit.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_block": [
|
|
3
|
+
{
|
|
4
|
+
"files": [
|
|
5
|
+
{
|
|
6
|
+
"path": "src/pay.ts",
|
|
7
|
+
"content": "const key = 'sk_live_{{repeat:a:40}}';"
|
|
8
|
+
}
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"files": [
|
|
13
|
+
{
|
|
14
|
+
"path": "config.py",
|
|
15
|
+
"content": "AWS_KEY = 'AKIA{{repeat:Q:16}}'"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"files": [
|
|
21
|
+
{
|
|
22
|
+
"path": "app.js",
|
|
23
|
+
"content": "const k = process.env.KEY || 'sk_live_{{repeat:b:40}}'"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"files": [
|
|
29
|
+
{
|
|
30
|
+
"path": "ci.yml",
|
|
31
|
+
"content": "token: ghp_{{repeat:a:36}}"
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"files": [
|
|
37
|
+
{
|
|
38
|
+
"path": "db.ts",
|
|
39
|
+
"content": "const url = 'postgres://admin:hunter2pass@db.internal:5432/app'"
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"files": [
|
|
45
|
+
{
|
|
46
|
+
"path": "keys/deploy",
|
|
47
|
+
"content": "{{repeat:-:5}}BEGIN OPENSSH PRIVATE KEY{{repeat:-:5}}\nabc"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"files": [
|
|
53
|
+
{
|
|
54
|
+
"path": "settings.json",
|
|
55
|
+
"content": "{ \"api_key\": \"q8Zr1xWm3Lp9TbV2\" }"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"should_allow": [
|
|
61
|
+
{
|
|
62
|
+
"files": [
|
|
63
|
+
{
|
|
64
|
+
"path": "src/pay.ts",
|
|
65
|
+
"content": "const key = process.env.PAYSTACK_SECRET_KEY"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"files": [
|
|
71
|
+
{
|
|
72
|
+
"path": ".env.example",
|
|
73
|
+
"content": "PAYSTACK_SECRET_KEY=sk_test_your_key_here"
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"files": [
|
|
79
|
+
{
|
|
80
|
+
"path": "docs/setup.md",
|
|
81
|
+
"content": "Set api_key: \"<your-api-key>\" in config"
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"files": [
|
|
87
|
+
{
|
|
88
|
+
"path": "db.ts",
|
|
89
|
+
"content": "const url = `postgres://${user}:${pass}@${host}/app`"
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"files": [
|
|
95
|
+
{
|
|
96
|
+
"path": "README.md",
|
|
97
|
+
"content": "Get a key from the dashboard and export it."
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"files": [
|
|
103
|
+
{
|
|
104
|
+
"path": "test/fixtures.ts",
|
|
105
|
+
"content": "const password = 'changeme-in-tests'"
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Blocks agent edits that would write a secret (API key, token, private key, password) into a file.
|
|
4
|
+
// check({ files: [{ path, content }], cwd }) -> verdict. Content is the new text being written.
|
|
5
|
+
|
|
6
|
+
// Same rules as skills/security-review/scripts/scan-secrets.js; tests/hooks.test.js keeps them in sync.
|
|
7
|
+
const RULES = [
|
|
8
|
+
['AWS access key', /\b(AKIA|ASIA)[0-9A-Z]{16}\b/],
|
|
9
|
+
['GitHub token', /\b(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}\b|\bgithub_pat_[A-Za-z0-9_]{60,}\b/],
|
|
10
|
+
['Anthropic API key', /\bsk-ant-[A-Za-z0-9_-]{20,}\b/],
|
|
11
|
+
['OpenAI API key', /\bsk-(proj-)?[A-Za-z0-9_-]{32,}\b/],
|
|
12
|
+
['Paystack secret key', /\bsk_(live|test)_[a-f0-9]{40}\b/],
|
|
13
|
+
['Stripe live key', /\b(sk|rk)_live_[A-Za-z0-9]{20,}\b/],
|
|
14
|
+
['Slack token', /\bxox[abprs]-[A-Za-z0-9-]{10,}\b/],
|
|
15
|
+
['Google API key', /\bAIza[0-9A-Za-z_-]{35}\b/],
|
|
16
|
+
['Private key block', /-----BEGIN (RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----/],
|
|
17
|
+
['JWT', /\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/],
|
|
18
|
+
['Connection string with password', /\b(postgres(ql)?|mysql|mongodb(\+srv)?|redis|amqp):\/\/[^\s:@/]+:[^\s@/]{3,}@/i],
|
|
19
|
+
['Hard-coded secret assignment', /\b(password|passwd|secret|api[_-]?key|access[_-]?token|auth[_-]?token)\b["']?\s*[:=]\s*["'][^"'\s]{8,}["']/i],
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const PLACEHOLDER = /(example|placeholder|changeme|your[_-]|xxxx|dummy|fake|<[^>]+>|\$\{|process\.env|os\.environ)/i;
|
|
23
|
+
|
|
24
|
+
const mask = (s) => (s.length <= 8 ? '****' : `${s.slice(0, 4)}…${s.slice(-2)} (${s.length} chars)`);
|
|
25
|
+
|
|
26
|
+
function check(event) {
|
|
27
|
+
for (const file of event.files || []) {
|
|
28
|
+
if (!file || typeof file.content !== 'string') continue;
|
|
29
|
+
const lines = file.content.split('\n');
|
|
30
|
+
for (let i = 0; i < lines.length; i++) {
|
|
31
|
+
for (const [label, re] of RULES) {
|
|
32
|
+
const m = re.exec(lines[i]);
|
|
33
|
+
if (!m || PLACEHOLDER.test(m[0])) continue;
|
|
34
|
+
return {
|
|
35
|
+
block: true,
|
|
36
|
+
reason: `Blocked by kete-loom (guard-secrets): this edit writes a ${label} (${mask(m[0])}) into ${file.path || 'a file'}. Read it from an environment variable or secret store instead, and if it is real, tell the user to rotate it.`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { block: false };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { check, RULES };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: load-session-context
|
|
3
|
+
description: At the start of each session, tells the agent the current branch, how many uncommitted changes there are, and the latest session handoff note, so it picks up where the last session stopped.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
kete-loom/version: "0.1.0"
|
|
7
|
+
kete-loom/category: meta
|
|
8
|
+
kete-loom/risk: runs-commands
|
|
9
|
+
kete-loom/event: "session-start"
|
|
10
|
+
kete-loom/owner: "Kete-org"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Load session context
|
|
14
|
+
|
|
15
|
+
Runs when a session starts, in a git repository only. It adds a short note to the agent's context:
|
|
16
|
+
|
|
17
|
+
- the current branch and the number of uncommitted changes (from `git status`);
|
|
18
|
+
- the session handoff note written by the `session-handoff` skill, if there is one: `.handoff/<branch>.md`, then `HANDOFF.md`, then the newest note in `.handoff/` or `docs/handoff/`. At most 60 lines are included, and the agent is told to check it against the repository before acting on it.
|
|
19
|
+
|
|
20
|
+
It only reads; it never changes files. Not available in OpenCode, which has no way for a hook to add context at session start.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// At session start, tells the agent where things stand: the branch, uncommitted work, and the
|
|
4
|
+
// latest session handoff note (written by the session-handoff skill). check({ cwd }) -> { context }
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { execFileSync } = require('child_process');
|
|
9
|
+
|
|
10
|
+
const MAX_NOTE_LINES = 60;
|
|
11
|
+
const MAX_NOTE_CHARS = 3000;
|
|
12
|
+
|
|
13
|
+
function git(cwd, args) {
|
|
14
|
+
try {
|
|
15
|
+
return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000 }).trim();
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function newest(dir) {
|
|
22
|
+
try {
|
|
23
|
+
return fs.readdirSync(dir).filter((f) => f.endsWith('.md'))
|
|
24
|
+
.map((f) => path.join(dir, f))
|
|
25
|
+
.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs)[0] || null;
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// The note for this branch first, then HANDOFF.md, then the newest note in the usual folders.
|
|
32
|
+
function findNote(root, branch) {
|
|
33
|
+
const candidates = [];
|
|
34
|
+
if (branch) for (const name of [branch, branch.replace(/\//g, '-')]) candidates.push(path.join(root, '.handoff', `${name}.md`));
|
|
35
|
+
candidates.push(path.join(root, 'HANDOFF.md'));
|
|
36
|
+
const direct = candidates.find((p) => fs.existsSync(p));
|
|
37
|
+
return direct || newest(path.join(root, '.handoff')) || newest(path.join(root, 'docs', 'handoff'));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function check(event) {
|
|
41
|
+
const cwd = event.cwd || process.cwd();
|
|
42
|
+
const root = git(cwd, ['rev-parse', '--show-toplevel']);
|
|
43
|
+
if (!root) return {};
|
|
44
|
+
const branch = git(root, ['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
45
|
+
const changes = (git(root, ['status', '--porcelain']) || '').split('\n').filter(Boolean).length;
|
|
46
|
+
const lines = [`Session context from kete-loom (load-session-context):`, `- Branch: ${branch || 'unknown'}, ${changes} uncommitted change(s).`];
|
|
47
|
+
const note = findNote(root, branch);
|
|
48
|
+
if (note) {
|
|
49
|
+
let text = fs.readFileSync(note, 'utf8').split('\n').slice(0, MAX_NOTE_LINES).join('\n');
|
|
50
|
+
if (text.length > MAX_NOTE_CHARS) text = `${text.slice(0, MAX_NOTE_CHARS)}\n…`;
|
|
51
|
+
lines.push(`- Handoff note ${path.relative(root, note)} (check it against the repository before acting on it):`, '', text);
|
|
52
|
+
}
|
|
53
|
+
return { context: lines.join('\n') };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { check };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: protect-files
|
|
3
|
+
description: Stops the agent from editing files that should never be hand-edited, such as .env files with secrets, package lockfiles, anything inside .git, and private keys.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
kete-loom/version: "0.1.0"
|
|
7
|
+
kete-loom/category: security
|
|
8
|
+
kete-loom/risk: read-only
|
|
9
|
+
kete-loom/event: "pre-edit"
|
|
10
|
+
kete-loom/owner: "Kete-org"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Protect files
|
|
14
|
+
|
|
15
|
+
Runs before every file write or edit. Blocks:
|
|
16
|
+
|
|
17
|
+
| Files | Why | What the agent should do instead |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `.env`, `.env.local`, `.env.production`… (not `.env.example`/`.sample`/`.template`) | They hold real secrets | Edit `.env.example` and tell you which values to set |
|
|
20
|
+
| Lockfiles (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `bun.lock`, `Cargo.lock`, `poetry.lock`, `uv.lock`, `go.sum`, `Gemfile.lock`, `composer.lock` and others) | Hand edits break dependency resolution | Run the package manager, which rewrites the lockfile |
|
|
21
|
+
| Anything under `.git/` | Editing git's internals can corrupt the repository | Use git commands |
|
|
22
|
+
| Private keys and keystores (`id_rsa`, `id_ed25519`, `*.pem`, `*.key`, `*.p12`…; `.pub` files are fine) | Keys don't belong in the repository | Generate and store keys outside it |
|
|
23
|
+
|
|
24
|
+
Not available in Cursor, which has no documented hook before an edit.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_block": [
|
|
3
|
+
{ "files": [{ "path": ".env", "content": "DB_URL=postgres://x" }] },
|
|
4
|
+
{ "files": [{ "path": "apps/api/.env.local", "content": "X=1" }] },
|
|
5
|
+
{ "files": [{ "path": ".env.production", "content": "X=1" }] },
|
|
6
|
+
{ "files": [{ "path": "package-lock.json", "content": "{}" }] },
|
|
7
|
+
{ "files": [{ "path": "web/pnpm-lock.yaml", "content": "" }] },
|
|
8
|
+
{ "files": [{ "path": "Cargo.lock", "content": "" }] },
|
|
9
|
+
{ "files": [{ "path": "go.sum", "content": "" }] },
|
|
10
|
+
{ "files": [{ "path": ".git/config", "content": "[core]" }] },
|
|
11
|
+
{ "files": [{ "path": ".git/hooks/pre-commit", "content": "exit 0" }] },
|
|
12
|
+
{ "files": [{ "path": "certs/server.key", "content": "x" }] },
|
|
13
|
+
{ "files": [{ "path": "deploy/id_ed25519", "content": "x" }] },
|
|
14
|
+
{ "files": [{ "path": "src/ok.ts", "content": "x" }, { "path": "yarn.lock", "content": "" }] }
|
|
15
|
+
],
|
|
16
|
+
"should_allow": [
|
|
17
|
+
{ "files": [{ "path": ".env.example", "content": "DB_URL=" }] },
|
|
18
|
+
{ "files": [{ "path": "config/.env.template", "content": "X=" }] },
|
|
19
|
+
{ "files": [{ "path": "src/env.ts", "content": "export const env = process.env" }] },
|
|
20
|
+
{ "files": [{ "path": "package.json", "content": "{}" }] },
|
|
21
|
+
{ "files": [{ "path": "docs/lockfiles.md", "content": "about package-lock.json" }] },
|
|
22
|
+
{ "files": [{ "path": ".github/workflows/ci.yml", "content": "on: push" }] },
|
|
23
|
+
{ "files": [{ "path": "deploy/id_ed25519.pub", "content": "ssh-ed25519 AAAA" }] },
|
|
24
|
+
{ "files": [{ "path": "src/keyboard.ts", "content": "x" }] },
|
|
25
|
+
{ "files": [{ "path": ".gitignore", "content": "node_modules" }] }
|
|
26
|
+
]
|
|
27
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Blocks agent edits to files that should never be hand-edited: secrets (.env), lockfiles,
|
|
4
|
+
// git internals and private keys. check({ files: [{ path, content }], cwd }) -> verdict.
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
const RULES = [
|
|
9
|
+
{
|
|
10
|
+
test: (rel, base) => /^\.env(?:\..+)?$/.test(base) && !/\.(?:example|sample|template|dist|defaults)$/.test(base),
|
|
11
|
+
reason: 'is an environment file that holds secrets. Edit .env.example and tell the user which values to set.',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
test: (rel, base) => /^(?:package-lock\.json|npm-shrinkwrap\.json|yarn\.lock|pnpm-lock\.yaml|bun\.lockb?|Cargo\.lock|poetry\.lock|uv\.lock|Pipfile\.lock|Gemfile\.lock|composer\.lock|go\.sum|pubspec\.lock|Podfile\.lock|mix\.lock|packages\.lock\.json|gradle\.lockfile|flake\.lock)$/.test(base),
|
|
15
|
+
reason: "is a lockfile. Change the dependency with the package manager (it rewrites the lockfile) instead of editing it.",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
test: (rel) => /(?:^|\/)\.git\//.test(rel) || rel === '.git',
|
|
19
|
+
reason: "is inside .git. Use git commands; editing git's internals can corrupt the repository.",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
test: (rel, base) => /^(?:id_(?:rsa|dsa|ecdsa|ed25519)|.*\.(?:pem|key|p12|pfx|jks|keystore))$/.test(base) && !/\.pub$/.test(base),
|
|
23
|
+
reason: 'looks like a private key or keystore. Keys are generated and stored outside the repository.',
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
function check(event) {
|
|
28
|
+
const cwd = event.cwd || process.cwd();
|
|
29
|
+
for (const file of event.files || []) {
|
|
30
|
+
if (!file || typeof file.path !== 'string') continue;
|
|
31
|
+
const rel = path.relative(cwd, path.resolve(cwd, file.path)).split(path.sep).join('/');
|
|
32
|
+
const base = path.basename(file.path);
|
|
33
|
+
const rule = RULES.find((r) => r.test(rel, base));
|
|
34
|
+
if (rule) return { block: true, reason: `Blocked by kete-loom (protect-files): \`${rel}\` ${rule.reason}` };
|
|
35
|
+
}
|
|
36
|
+
return { block: false };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = { check };
|