@mmerterden/multi-agent-pipeline 12.6.0 → 12.8.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/CHANGELOG.md +209 -0
- package/README.md +18 -18
- package/docs/FIGMA_PIPELINE.md +34 -34
- package/docs/adr/0001-three-model-triage.md +12 -12
- package/docs/adr/0002-instruction-driven-flag.md +5 -5
- package/docs/adr/0003-unified-shared-skills.md +5 -5
- package/docs/adr/0004-zero-dependency-philosophy.md +5 -5
- package/docs/adr/0005-lazy-phase-docs.md +2 -2
- package/docs/adr/0006-skills-core-external-split.md +6 -6
- package/docs/adr/0007-multi-tool-adapter-framework.md +19 -19
- package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +19 -19
- package/docs/adr/README.md +1 -1
- package/docs/best-practices.md +3 -3
- package/docs/features.md +28 -28
- package/docs/performance.md +16 -16
- package/docs/recovery-guide.md +39 -39
- package/index.js +4 -4
- package/install/_common.mjs +53 -11
- package/install/_copilot-instructions.mjs +2 -2
- package/install/_dev-only-files.mjs +126 -6
- package/install/_platform-filter.mjs +1 -1
- package/install/_telemetry.mjs +1 -1
- package/install/claude.mjs +20 -13
- package/install/copilot.mjs +11 -23
- package/install/index.mjs +7 -15
- package/install/templates/copilot-instructions.md +54 -54
- package/install.js +1 -1
- package/package.json +29 -11
- package/pipeline/commands/multi-agent/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/analysis/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/build-optimize/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/create-jira/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/design-check/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-local/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-local-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/diff-explain/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/finish/SKILL.md +6 -6
- package/pipeline/commands/multi-agent/forget/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/help/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/issue/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/jira/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/kill/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/language/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/local/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/log/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/manual-test/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/prune-logs/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/purge/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/refactor/SKILL.md +16 -8
- package/pipeline/commands/multi-agent/resume/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/review/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/review-issue/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/review-jira/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/routines/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/save/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/scan/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/search/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/setup/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/stack/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/status/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/sync/SKILL.md +5 -5
- package/pipeline/commands/multi-agent/test/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/uninstall/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/update/SKILL.md +3 -3
- package/pipeline/lib/account-resolver.sh +1 -1
- package/pipeline/lib/channels-multi-repo.sh +1 -1
- package/pipeline/lib/context-link-extractor.sh +1 -1
- package/pipeline/lib/credential-store.sh +21 -1
- package/pipeline/lib/fetch-confluence.sh +1 -1
- package/pipeline/lib/fetch-crashlytics.sh +1 -1
- package/pipeline/lib/fetch-fortify.sh +1 -1
- package/pipeline/lib/fetch-graylog.sh +1 -1
- package/pipeline/lib/fetch-swagger.sh +1 -1
- package/pipeline/lib/issue-fetcher.sh +1 -1
- package/pipeline/lib/multi-repo-pipeline.sh +1 -1
- package/pipeline/lib/repo-cache.sh +1 -1
- package/pipeline/lib/submodule-detector.sh +1 -1
- package/pipeline/multi-agent-refs/_account-picker.md +1 -1
- package/pipeline/multi-agent-refs/_dev-context.md +1 -1
- package/pipeline/multi-agent-refs/_repo-picker.md +1 -1
- package/pipeline/multi-agent-refs/component-dispatch.md +1 -1
- package/pipeline/multi-agent-refs/component-generation.md +121 -0
- package/pipeline/multi-agent-refs/cross-cli-contract.md +1 -1
- package/pipeline/multi-agent-refs/features/model-fallback.md +2 -2
- package/pipeline/multi-agent-refs/phases/operations.md +28 -0
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +1 -0
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +1 -2
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +50 -5
- package/pipeline/preferences-template.json +5 -11
- package/pipeline/schemas/agent-state.schema.json +39 -9
- package/pipeline/schemas/analysis-output.schema.json +18 -4
- package/pipeline/schemas/analysis-spec.schema.json +120 -32
- package/pipeline/schemas/clarify-output.schema.json +15 -5
- package/pipeline/schemas/design-check-config.schema.json +32 -11
- package/pipeline/schemas/dev-critic-output.schema.json +20 -5
- package/pipeline/schemas/figma-project-config.schema.json +42 -10
- package/pipeline/schemas/learnings-ledger.schema.json +10 -2
- package/pipeline/schemas/migrations/figma-config-1.0.0-to-2.0.0.mjs +1 -4
- package/pipeline/schemas/migrations/prefs-2.0.0-to-2.1.0.mjs +24 -7
- package/pipeline/schemas/plan-todos.schema.json +6 -3
- package/pipeline/schemas/planning-output.schema.json +5 -1
- package/pipeline/schemas/prefs.schema.json +97 -229
- package/pipeline/schemas/test-gap.schema.json +5 -5
- package/pipeline/schemas/token-budget.json +8 -8
- package/pipeline/schemas/triage-corpus.schema.json +1 -1
- package/pipeline/scripts/_smoke-root.sh +61 -0
- package/pipeline/scripts/aggregate-metrics.mjs +18 -6
- package/pipeline/scripts/audit-log.sh +25 -0
- package/pipeline/scripts/build-skills-index.mjs +6 -2
- package/pipeline/scripts/build-stack-plugins.mjs +142 -39
- package/pipeline/scripts/check-derived-drift.mjs +196 -0
- package/pipeline/scripts/classify-plan-safety.mjs +20 -7
- package/pipeline/scripts/cost-budget-check.mjs +2 -1
- package/pipeline/scripts/cost-table.json +1 -1
- package/pipeline/scripts/diff-explain.mjs +7 -3
- package/pipeline/scripts/diff-risk-score.mjs +13 -3
- package/pipeline/scripts/evidence-gate.mjs +7 -2
- package/pipeline/scripts/gen-mode-dispatch.mjs +38 -21
- package/pipeline/scripts/gen-skills-index.mjs +18 -3
- package/pipeline/scripts/learning-curve.mjs +13 -3
- package/pipeline/scripts/learnings-ledger.mjs +103 -36
- package/pipeline/scripts/localize-commands.mjs +6 -1
- package/pipeline/scripts/match-skills.mjs +15 -4
- package/pipeline/scripts/migrate-prefs.mjs +33 -16
- package/pipeline/scripts/phase-tracker.sh +3 -1
- package/pipeline/scripts/repo-map.mjs +110 -64
- package/pipeline/scripts/review-scope.mjs +7 -1
- package/pipeline/scripts/routine-registry.mjs +4 -9
- package/pipeline/scripts/run-aggregator.mjs +11 -5
- package/pipeline/scripts/run-metrics.mjs +13 -8
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +21 -7
- package/pipeline/scripts/test-gap-scan.mjs +44 -12
- package/pipeline/scripts/test-integrity-gate.mjs +5 -1
- package/pipeline/scripts/token-budget-report.mjs +44 -21
- package/pipeline/scripts/triage-memory.mjs +126 -34
- package/pipeline/scripts/uninstall.mjs +74 -30
- package/pipeline/scripts/validate-analysis-doc.mjs +15 -5
- package/pipeline/scripts/validate-diff-risk.mjs +32 -18
- package/pipeline/scripts/validate-test-gap.mjs +17 -7
- package/pipeline/scripts/validate-triage.mjs +17 -5
- package/pipeline/scripts/write-state.mjs +32 -9
- package/pipeline/skills/.skills-index.json +91 -91
- package/pipeline/skills/shared/README.md +57 -57
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +1 -1
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +26 -279
- package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-forget/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-garbage-collect/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-kill/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-log/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-purge/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +16 -8
- package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-routines/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-save/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +3 -3
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-uninstall/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +1 -1
- package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +1 -1
- package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +4 -4
- package/pipeline/skills/shared/external/agentflow/SKILL.md +1 -1
- package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/android_ui_verification/SKILL.md +1 -1
- package/pipeline/skills/shared/external/api-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +1 -1
- package/pipeline/skills/shared/external/app-store-changelog/SKILL.md +1 -1
- package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
- package/pipeline/skills/shared/external/backlog/SKILL.md +12 -12
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -1
- package/pipeline/skills/shared/external/context-compression/SKILL.md +1 -1
- package/pipeline/skills/shared/external/council/SKILL.md +3 -3
- package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -1
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +1 -1
- package/pipeline/skills/shared/external/docker-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +1 -1
- package/pipeline/skills/shared/external/firebase/SKILL.md +1 -1
- package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +1 -1
- package/pipeline/skills/shared/external/help-skills/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-content/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-status/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-system/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-foundations/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-inputs/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-platforms/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-technologies/SKILL.md +1 -1
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -1
- package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -1
- package/pipeline/skills/shared/external/ios-debugger-agent/SKILL.md +1 -1
- package/pipeline/skills/shared/external/ios-developer/SKILL.md +1 -1
- package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/macos-menubar-tuist-app/SKILL.md +1 -1
- package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +1 -1
- package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +1 -1
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -1
- package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/observability-engineer/SKILL.md +1 -1
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -1
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -1
- package/pipeline/skills/shared/external/search-first/SKILL.md +2 -2
- package/pipeline/skills/shared/external/skill-creator/SKILL.md +12 -12
- package/pipeline/skills/shared/external/skill-creator/audit.md +21 -21
- package/pipeline/skills/shared/external/skill-creator/checklist.md +3 -3
- package/pipeline/skills/shared/external/skill-creator/examples.md +10 -10
- package/pipeline/skills/shared/external/skill-creator/label-check.md +17 -17
- package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +86 -50
- package/pipeline/skills/shared/external/skill-creator/template.md +9 -9
- package/pipeline/skills/shared/external/swift-concurrency-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/swiftui-performance-audit/SKILL.md +1 -1
- package/pipeline/skills/shared/external/swiftui-ui-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/swiftui-view-refactor/SKILL.md +1 -1
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -1
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -1
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -1
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -1
- package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -1
- package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -1
- package/pipeline/skills/shared/external/xcode-build-benchmark/schemas/build-benchmark.schema.json +9 -49
- package/pipeline/skills/skills-index.md +57 -57
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-1-analysis.json +0 -25
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-2-plan.json +0 -30
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/task.json +0 -12
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-2-plan.json +0 -43
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-review.json +0 -35
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-triage.json +0 -35
- package/pipeline/eval/golden-tasks/02-android-feature-compose/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/02-android-feature-compose/task.json +0 -12
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/task.json +0 -12
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-2-plan.json +0 -40
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/task.json +0 -12
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-review.json +0 -28
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-triage.json +0 -27
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/task.json +0 -12
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-2-plan.json +0 -41
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-review.json +0 -12
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-triage.json +0 -6
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/task.json +0 -12
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-review.json +0 -28
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-triage.json +0 -27
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/task.json +0 -12
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-1-analysis.json +0 -25
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-2-plan.json +0 -31
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-review.json +0 -12
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-triage.json +0 -18
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/task.json +0 -12
- package/pipeline/eval/golden-tasks/README.md +0 -65
- package/pipeline/eval/intent-cases.json +0 -40
- package/pipeline/eval/run-metrics-fixture.json +0 -46
- package/pipeline/eval/triage/01-empty-findings/expected.json +0 -6
- package/pipeline/eval/triage/01-empty-findings/input.json +0 -5
- package/pipeline/eval/triage/01-empty-findings/notes.md +0 -7
- package/pipeline/eval/triage/02-real-blocker/expected.json +0 -15
- package/pipeline/eval/triage/02-real-blocker/input.json +0 -14
- package/pipeline/eval/triage/02-real-blocker/notes.md +0 -7
- package/pipeline/eval/triage/03-out-of-scope-defer/expected.json +0 -18
- package/pipeline/eval/triage/03-out-of-scope-defer/input.json +0 -14
- package/pipeline/eval/triage/03-out-of-scope-defer/notes.md +0 -10
- package/pipeline/eval/triage/04-false-positive-reject/expected.json +0 -18
- package/pipeline/eval/triage/04-false-positive-reject/input.json +0 -14
- package/pipeline/eval/triage/04-false-positive-reject/notes.md +0 -10
- package/pipeline/eval/triage/05-mixed-classification/expected.json +0 -43
- package/pipeline/eval/triage/05-mixed-classification/input.json +0 -38
- package/pipeline/eval/triage/05-mixed-classification/notes.md +0 -17
- package/pipeline/eval/triage/06-severity-mismatch/expected.json +0 -15
- package/pipeline/eval/triage/06-severity-mismatch/input.json +0 -14
- package/pipeline/eval/triage/06-severity-mismatch/notes.md +0 -9
- package/pipeline/eval/triage/07-duplicate-reviewers/expected.json +0 -27
- package/pipeline/eval/triage/07-duplicate-reviewers/input.json +0 -22
- package/pipeline/eval/triage/07-duplicate-reviewers/notes.md +0 -9
- package/pipeline/eval/triage/08-style-misclassified/expected.json +0 -18
- package/pipeline/eval/triage/08-style-misclassified/input.json +0 -14
- package/pipeline/eval/triage/08-style-misclassified/notes.md +0 -9
- package/pipeline/eval/triage/09-cascading-finding/expected.json +0 -23
- package/pipeline/eval/triage/09-cascading-finding/input.json +0 -22
- package/pipeline/eval/triage/09-cascading-finding/notes.md +0 -9
- package/pipeline/eval/triage/10-deferred-crossref/expected.json +0 -18
- package/pipeline/eval/triage/10-deferred-crossref/input.json +0 -14
- package/pipeline/eval/triage/10-deferred-crossref/notes.md +0 -9
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/expected.json +0 -27
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/input.json +0 -22
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/notes.md +0 -14
- package/pipeline/eval/triage/README.md +0 -54
- package/pipeline/scripts/benchmark-phase-0.sh +0 -128
- package/pipeline/scripts/check-md-links.mjs +0 -84
- package/pipeline/scripts/eval-golden-tasks-live.mjs +0 -297
- package/pipeline/scripts/eval-golden-tasks.mjs +0 -212
- package/pipeline/scripts/eval-intent.mjs +0 -103
- package/pipeline/scripts/eval-mine-corpus.mjs +0 -201
- package/pipeline/scripts/eval-triage.mjs +0 -171
- package/pipeline/scripts/fixtures/diff-risk-android.diff +0 -40
- package/pipeline/scripts/fixtures/diff-risk-ios.diff +0 -48
- package/pipeline/scripts/fixtures/diff-risk-test-removal.diff +0 -40
- package/pipeline/scripts/fixtures/install-layout.tsv +0 -19
- package/pipeline/scripts/fixtures/pack-expected-count.txt +0 -1
- package/pipeline/scripts/fixtures/test-gap-node.diff +0 -30
- package/pipeline/scripts/fixtures/test-gap-python.diff +0 -32
- package/pipeline/scripts/lint-mcp-refs.mjs +0 -207
- package/pipeline/scripts/lint-skills.mjs +0 -143
- package/pipeline/scripts/run-smokes.mjs +0 -76
- package/pipeline/scripts/smoke-add-detail.sh +0 -137
- package/pipeline/scripts/smoke-agent-guard.sh +0 -74
- package/pipeline/scripts/smoke-agent-log-cost.sh +0 -262
- package/pipeline/scripts/smoke-agent-model-routing.sh +0 -87
- package/pipeline/scripts/smoke-ask-choice.sh +0 -42
- package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +0 -36
- package/pipeline/scripts/smoke-bitbucket-contract.sh +0 -255
- package/pipeline/scripts/smoke-changelog-version.sh +0 -47
- package/pipeline/scripts/smoke-channels-approval-gate.sh +0 -60
- package/pipeline/scripts/smoke-channels-flow.sh +0 -130
- package/pipeline/scripts/smoke-ci-workflows.sh +0 -88
- package/pipeline/scripts/smoke-clarify.sh +0 -148
- package/pipeline/scripts/smoke-command-inventory.sh +0 -81
- package/pipeline/scripts/smoke-commands-skills-parity.sh +0 -87
- package/pipeline/scripts/smoke-community-gates.sh +0 -75
- package/pipeline/scripts/smoke-compliance-skills.sh +0 -119
- package/pipeline/scripts/smoke-config-hygiene.sh +0 -58
- package/pipeline/scripts/smoke-cost-budget.sh +0 -70
- package/pipeline/scripts/smoke-cost-summary.sh +0 -139
- package/pipeline/scripts/smoke-cross-phase-cohesion.sh +0 -128
- package/pipeline/scripts/smoke-description-tr.sh +0 -82
- package/pipeline/scripts/smoke-dev-critic.sh +0 -144
- package/pipeline/scripts/smoke-diff-explain.sh +0 -147
- package/pipeline/scripts/smoke-diff-risk.sh +0 -190
- package/pipeline/scripts/smoke-dynamic-skill-loading.sh +0 -160
- package/pipeline/scripts/smoke-eval-live.sh +0 -136
- package/pipeline/scripts/smoke-evidence-gate.sh +0 -93
- package/pipeline/scripts/smoke-extract-conventions.sh +0 -163
- package/pipeline/scripts/smoke-fetchers-offline.sh +0 -448
- package/pipeline/scripts/smoke-figma-dispatch.sh +0 -112
- package/pipeline/scripts/smoke-gate-hooks.sh +0 -74
- package/pipeline/scripts/smoke-gc-tmp.sh +0 -130
- package/pipeline/scripts/smoke-gc-worktrees.sh +0 -125
- package/pipeline/scripts/smoke-generate-issue.sh +0 -120
- package/pipeline/scripts/smoke-handoff-contract.sh +0 -92
- package/pipeline/scripts/smoke-identity-isolation.sh +0 -70
- package/pipeline/scripts/smoke-install-layout.sh +0 -248
- package/pipeline/scripts/smoke-intent-guard.sh +0 -86
- package/pipeline/scripts/smoke-issue-comment-template.sh +0 -86
- package/pipeline/scripts/smoke-issue-jira-triad.sh +0 -120
- package/pipeline/scripts/smoke-keychain.sh +0 -158
- package/pipeline/scripts/smoke-language-axis.sh +0 -109
- package/pipeline/scripts/smoke-learning-curve.sh +0 -61
- package/pipeline/scripts/smoke-learnings-ledger.sh +0 -86
- package/pipeline/scripts/smoke-lib-scripts.sh +0 -448
- package/pipeline/scripts/smoke-mcp-gate.sh +0 -68
- package/pipeline/scripts/smoke-md-links.sh +0 -8
- package/pipeline/scripts/smoke-md2confluence.sh +0 -126
- package/pipeline/scripts/smoke-metrics-cache-ratio.sh +0 -72
- package/pipeline/scripts/smoke-migrate-state.sh +0 -102
- package/pipeline/scripts/smoke-mode-dispatch-drift.sh +0 -161
- package/pipeline/scripts/smoke-model-fallback.sh +0 -89
- package/pipeline/scripts/smoke-multi-repo-integration.sh +0 -116
- package/pipeline/scripts/smoke-multi-repo-worktree.sh +0 -61
- package/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh +0 -115
- package/pipeline/scripts/smoke-no-token-prompt.sh +0 -85
- package/pipeline/scripts/smoke-pack-contents.sh +0 -140
- package/pipeline/scripts/smoke-pat-audit.sh +0 -128
- package/pipeline/scripts/smoke-per-repo-memory.sh +0 -156
- package/pipeline/scripts/smoke-phase-0-multi-repo.sh +0 -170
- package/pipeline/scripts/smoke-phase-6-multi.sh +0 -79
- package/pipeline/scripts/smoke-phase-banner.sh +0 -101
- package/pipeline/scripts/smoke-phase-tracker.sh +0 -324
- package/pipeline/scripts/smoke-phase0-bridge-contract.sh +0 -241
- package/pipeline/scripts/smoke-phase4-gates.sh +0 -45
- package/pipeline/scripts/smoke-phase4-triage.sh +0 -229
- package/pipeline/scripts/smoke-plan-approval-gate.sh +0 -71
- package/pipeline/scripts/smoke-plan-safety.sh +0 -139
- package/pipeline/scripts/smoke-plan-todos.sh +0 -196
- package/pipeline/scripts/smoke-plugin-validate.sh +0 -64
- package/pipeline/scripts/smoke-pr-review-actions.sh +0 -152
- package/pipeline/scripts/smoke-pre-commit.sh +0 -170
- package/pipeline/scripts/smoke-pref-migration.sh +0 -226
- package/pipeline/scripts/smoke-prefs-language.sh +0 -134
- package/pipeline/scripts/smoke-progress-contract.sh +0 -127
- package/pipeline/scripts/smoke-prune-logs.sh +0 -137
- package/pipeline/scripts/smoke-purge.sh +0 -138
- package/pipeline/scripts/smoke-push-retry.sh +0 -75
- package/pipeline/scripts/smoke-repo-map.sh +0 -300
- package/pipeline/scripts/smoke-review-readiness.sh +0 -92
- package/pipeline/scripts/smoke-review-watch.sh +0 -146
- package/pipeline/scripts/smoke-routines.sh +0 -84
- package/pipeline/scripts/smoke-run-aggregator.sh +0 -216
- package/pipeline/scripts/smoke-run-metrics.sh +0 -50
- package/pipeline/scripts/smoke-search.sh +0 -187
- package/pipeline/scripts/smoke-shadow-git.sh +0 -224
- package/pipeline/scripts/smoke-skill-authoring.sh +0 -137
- package/pipeline/scripts/smoke-skill-language.sh +0 -83
- package/pipeline/scripts/smoke-skill-manifest.sh +0 -138
- package/pipeline/scripts/smoke-skill-scan.sh +0 -198
- package/pipeline/scripts/smoke-source-parity.sh +0 -85
- package/pipeline/scripts/smoke-subagent-validators.sh +0 -108
- package/pipeline/scripts/smoke-sync-parity.sh +0 -92
- package/pipeline/scripts/smoke-tasklist-ordering.sh +0 -112
- package/pipeline/scripts/smoke-telemetry.sh +0 -147
- package/pipeline/scripts/smoke-test-gap.sh +0 -183
- package/pipeline/scripts/smoke-token-budget.sh +0 -67
- package/pipeline/scripts/smoke-token-preflight.sh +0 -82
- package/pipeline/scripts/smoke-tracker-contract.sh +0 -191
- package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +0 -73
- package/pipeline/scripts/smoke-triage-memory.sh +0 -174
- package/pipeline/scripts/smoke-update-check.sh +0 -135
- package/pipeline/scripts/smoke-url-enrichment.sh +0 -70
- package/pipeline/scripts/smoke-validate-analysis-doc.sh +0 -161
- package/pipeline/scripts/smoke-validator-contradiction.sh +0 -67
- package/pipeline/scripts/smoke-validator-gates.sh +0 -164
- package/pipeline/scripts/smoke-vercel-deploy-redact.sh +0 -129
- package/pipeline/scripts/smoke-verify-by-test.sh +0 -148
- package/pipeline/scripts/smoke-wiki-integration.sh +0 -122
- package/pipeline/scripts/smoke-work-summary.sh +0 -163
- package/pipeline/scripts/smoke-workflow-audit.sh +0 -69
- package/pipeline/scripts/smoke-worktree-path-convention.sh +0 -86
- package/pipeline/scripts/smoke-wrapper-preservation.sh +0 -68
- package/pipeline/scripts/smoke-write-state.sh +0 -115
- package/pipeline/scripts/sync-parity-check.sh +0 -135
- package/pipeline/scripts/test-gap-rules/android.json +0 -25
- package/pipeline/scripts/test-gap-rules/ios.json +0 -29
- package/pipeline/scripts/test-gap-rules/node.json +0 -17
- package/pipeline/scripts/test-gap-rules/python.json +0 -19
- package/pipeline/scripts/validate-schemas.mjs +0 -88
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// lint-mcp-refs.mjs - every mcp__<server>__<tool> reference must be callable.
|
|
3
|
-
//
|
|
4
|
-
// A host composes an MCP tool name as mcp__<registered-server-name>__<tool>.
|
|
5
|
-
// The pipeline had 100 references to `mcp__dev_toolkit__*` while the server is
|
|
6
|
-
// registered as `dev-toolkit`, so the real names are `mcp__dev-toolkit__*`.
|
|
7
|
-
// Nothing caught it: prose is not executed, and a wrong name inside an
|
|
8
|
-
// `allowed-tools:` allowlist silently denies the tool instead of erroring.
|
|
9
|
-
//
|
|
10
|
-
// Checks (errors - fail the gate):
|
|
11
|
-
// 1. the server segment must be a known registered server name
|
|
12
|
-
// 2. the known-bad snake_case spellings are called out with their fix
|
|
13
|
-
//
|
|
14
|
-
// Optional deep check (advisory - never fails):
|
|
15
|
-
// 3. when the companion dev-toolkit repo resolves locally (via
|
|
16
|
-
// prefs.global.devToolkit.localPath, or the mcpServers registration in
|
|
17
|
-
// ~/.claude.json / ~/.claude/settings.json), handshake it over stdio and
|
|
18
|
-
// confirm every referenced dev-toolkit tool actually exists in tools/list.
|
|
19
|
-
// CI has no local checkout, so an unresolvable repo is a skip, not a fail.
|
|
20
|
-
//
|
|
21
|
-
// Zero-dependency. Runs inside `npm test` and ci-lite.
|
|
22
|
-
//
|
|
23
|
-
// Usage: node pipeline/scripts/lint-mcp-refs.mjs [--strict-tools]
|
|
24
|
-
// Exit: 0 clean, 1 unknown server segment (or a missing tool with --strict-tools).
|
|
25
|
-
|
|
26
|
-
import { readFileSync, existsSync, readdirSync, statSync } from "node:fs";
|
|
27
|
-
import { homedir } from "node:os";
|
|
28
|
-
import { dirname, join, resolve } from "node:path";
|
|
29
|
-
import { fileURLToPath } from "node:url";
|
|
30
|
-
import { spawnSync } from "node:child_process";
|
|
31
|
-
|
|
32
|
-
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
33
|
-
const strictTools = process.argv.includes("--strict-tools");
|
|
34
|
-
|
|
35
|
-
// Servers the pipeline is allowed to reference. The key is the registered
|
|
36
|
-
// server name as it appears in an mcpServers config, which is what the host
|
|
37
|
-
// interpolates into the tool name.
|
|
38
|
-
const KNOWN_SERVERS = {
|
|
39
|
-
"dev-toolkit": "companion device/browser toolkit (@mmerterden/dev-toolkit-mcp)",
|
|
40
|
-
claude_ai_Figma: "Figma MCP (analysis phase only)",
|
|
41
|
-
XcodeBuildMCP: "optional third-party Xcode build server",
|
|
42
|
-
};
|
|
43
|
-
// Spellings that look right but cannot resolve to a registered server.
|
|
44
|
-
const KNOWN_BAD = { dev_toolkit: "dev-toolkit", "claude-ai-Figma": "claude_ai_Figma" };
|
|
45
|
-
|
|
46
|
-
const SCAN_DIRS = ["pipeline/commands", "pipeline/skills", "pipeline/multi-agent-refs"];
|
|
47
|
-
const SCAN_EXT = /\.(md|sh|mjs|json)$/;
|
|
48
|
-
const PRUNE = new Set(["node_modules", ".git", ".worktrees", "Pods", ".build", "DerivedData", ".next"]);
|
|
49
|
-
|
|
50
|
-
function walk(dir, out = []) {
|
|
51
|
-
let entries;
|
|
52
|
-
try {
|
|
53
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
54
|
-
} catch {
|
|
55
|
-
return out;
|
|
56
|
-
}
|
|
57
|
-
for (const e of entries) {
|
|
58
|
-
if (PRUNE.has(e.name)) continue;
|
|
59
|
-
const p = join(dir, e.name);
|
|
60
|
-
if (e.isDirectory()) walk(p, out);
|
|
61
|
-
else if (SCAN_EXT.test(e.name)) out.push(p);
|
|
62
|
-
}
|
|
63
|
-
return out;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const files = SCAN_DIRS.flatMap((d) => walk(join(repoRoot, d)));
|
|
67
|
-
const REF = /mcp__([A-Za-z0-9_-]+?)__([a-z0-9_]+)/g;
|
|
68
|
-
|
|
69
|
-
const errors = [];
|
|
70
|
-
const refs = new Map(); // server -> Map(tool -> Set(file:line))
|
|
71
|
-
|
|
72
|
-
for (const file of files) {
|
|
73
|
-
const rel = file.slice(repoRoot.length + 1);
|
|
74
|
-
const lines = readFileSync(file, "utf8").split("\n");
|
|
75
|
-
lines.forEach((line, i) => {
|
|
76
|
-
for (const m of line.matchAll(REF)) {
|
|
77
|
-
const [, server, tool] = m;
|
|
78
|
-
// Prose writes families as mcp__dev-toolkit__ios_* - a wildcard, not a tool.
|
|
79
|
-
const next = line[m.index + m[0].length];
|
|
80
|
-
if (next === "*" || tool.endsWith("_")) continue;
|
|
81
|
-
const where = `${rel}:${i + 1}`;
|
|
82
|
-
if (!refs.has(server)) refs.set(server, new Map());
|
|
83
|
-
const tools = refs.get(server);
|
|
84
|
-
if (!tools.has(tool)) tools.set(tool, new Set());
|
|
85
|
-
tools.get(tool).add(where);
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
for (const [server, tools] of refs) {
|
|
91
|
-
if (server in KNOWN_SERVERS) continue;
|
|
92
|
-
const fix = KNOWN_BAD[server];
|
|
93
|
-
const sample = [...tools.values()][0].values().next().value;
|
|
94
|
-
errors.push(
|
|
95
|
-
fix
|
|
96
|
-
? `unknown MCP server segment "${server}" (${tools.size} tool refs, e.g. ${sample}) - the registered server is "${fix}", so the prefix must be mcp__${fix}__`
|
|
97
|
-
: `unknown MCP server segment "${server}" (${tools.size} tool refs, e.g. ${sample}) - add it to KNOWN_SERVERS in lint-mcp-refs.mjs if this server is expected, or fix the prefix to match the registered server name`,
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// ---- optional deep check: do the referenced dev-toolkit tools exist? --------
|
|
102
|
-
|
|
103
|
-
function resolveToolkitRepo() {
|
|
104
|
-
const prefsPath = join(homedir(), ".claude", "multi-agent-preferences.json");
|
|
105
|
-
if (existsSync(prefsPath)) {
|
|
106
|
-
try {
|
|
107
|
-
const dt = JSON.parse(readFileSync(prefsPath, "utf8"))?.global?.devToolkit;
|
|
108
|
-
if (dt && dt.enabled === false) return null;
|
|
109
|
-
if (dt?.localPath) {
|
|
110
|
-
const p = dt.localPath.replace(/^~|\$HOME/, homedir());
|
|
111
|
-
if (existsSync(join(p, "package.json"))) return p;
|
|
112
|
-
}
|
|
113
|
-
} catch {
|
|
114
|
-
/* fall through to registration lookup */
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
const candidates = [];
|
|
118
|
-
const collect = (obj) => {
|
|
119
|
-
for (const [name, cfg] of Object.entries(obj || {})) {
|
|
120
|
-
const arg = Array.isArray(cfg?.args) ? cfg.args.find((a) => /\.(mjs|js)$/.test(a)) : null;
|
|
121
|
-
if (name.includes("dev") && arg) candidates.push(dirname(resolve(arg)));
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
for (const cfgPath of [join(homedir(), ".claude.json"), join(homedir(), ".claude", "settings.json")]) {
|
|
125
|
-
if (!existsSync(cfgPath)) continue;
|
|
126
|
-
try {
|
|
127
|
-
const json = JSON.parse(readFileSync(cfgPath, "utf8"));
|
|
128
|
-
collect(json.mcpServers);
|
|
129
|
-
for (const proj of Object.values(json.projects || {})) collect(proj.mcpServers);
|
|
130
|
-
} catch {
|
|
131
|
-
/* ignore unreadable config */
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
for (const c of candidates) {
|
|
135
|
-
try {
|
|
136
|
-
const pkg = JSON.parse(readFileSync(join(c, "package.json"), "utf8"));
|
|
137
|
-
if (pkg.dependencies?.["@modelcontextprotocol/sdk"] && statSync(join(c, ".git"))) return c;
|
|
138
|
-
} catch {
|
|
139
|
-
/* not the toolkit */
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function listTools(repo) {
|
|
146
|
-
const frames =
|
|
147
|
-
[
|
|
148
|
-
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"lint-mcp-refs","version":"1"}}}',
|
|
149
|
-
'{"jsonrpc":"2.0","method":"notifications/initialized"}',
|
|
150
|
-
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}',
|
|
151
|
-
].join("\n") + "\n";
|
|
152
|
-
const r = spawnSync(process.execPath, ["index.js"], {
|
|
153
|
-
cwd: repo,
|
|
154
|
-
input: frames,
|
|
155
|
-
encoding: "utf8",
|
|
156
|
-
timeout: 20000,
|
|
157
|
-
maxBuffer: 32 * 1024 * 1024,
|
|
158
|
-
});
|
|
159
|
-
if (r.error || !r.stdout) return null;
|
|
160
|
-
for (const line of r.stdout.split("\n")) {
|
|
161
|
-
if (!line.trim()) continue;
|
|
162
|
-
try {
|
|
163
|
-
const msg = JSON.parse(line);
|
|
164
|
-
if (msg.id === 2 && msg.result?.tools) return msg.result.tools.map((t) => t.name);
|
|
165
|
-
} catch {
|
|
166
|
-
/* not a frame */
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const referencedToolkitTools = refs.get("dev-toolkit");
|
|
173
|
-
let deep = "skipped (no local dev-toolkit checkout)";
|
|
174
|
-
if (referencedToolkitTools) {
|
|
175
|
-
const repo = resolveToolkitRepo();
|
|
176
|
-
const served = repo ? listTools(repo) : null;
|
|
177
|
-
if (!served) {
|
|
178
|
-
deep = repo ? `skipped (${repo} did not answer tools/list)` : deep;
|
|
179
|
-
} else {
|
|
180
|
-
const missing = [...referencedToolkitTools.keys()].filter((t) => !served.includes(t));
|
|
181
|
-
if (missing.length === 0) {
|
|
182
|
-
deep = `all ${referencedToolkitTools.size} referenced tools exist in the live server (${served.length} served)`;
|
|
183
|
-
} else {
|
|
184
|
-
const msg = `dev-toolkit tools referenced but not served (${served.length} served): ${missing
|
|
185
|
-
.map((t) => `${t} [${[...referencedToolkitTools.get(t)][0]}]`)
|
|
186
|
-
.join(", ")}`;
|
|
187
|
-
deep = `${missing.length} referenced tool(s) not served`;
|
|
188
|
-
if (strictTools) errors.push(msg);
|
|
189
|
-
else console.warn(` WARNING - ${msg}`);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const totalRefs = [...refs.values()].reduce((n, tools) => n + tools.size, 0);
|
|
195
|
-
console.log(
|
|
196
|
-
`lint-mcp-refs: ${totalRefs} distinct tool references across ${refs.size} server(s) in ${files.length} files`,
|
|
197
|
-
);
|
|
198
|
-
for (const [server, tools] of refs) {
|
|
199
|
-
console.log(` ${server in KNOWN_SERVERS ? "✓" : "✗"} mcp__${server}__* : ${tools.size} tools`);
|
|
200
|
-
}
|
|
201
|
-
console.log(` tool-existence check: ${deep}`);
|
|
202
|
-
|
|
203
|
-
if (errors.length > 0) {
|
|
204
|
-
console.error(`lint-mcp-refs: ${errors.length} error(s)`);
|
|
205
|
-
for (const e of errors) console.error(" " + e);
|
|
206
|
-
process.exit(1);
|
|
207
|
-
}
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Frontmatter linter for every SKILL.md shipped by the pipeline.
|
|
3
|
-
//
|
|
4
|
-
// Until v10.0.0 the shipped SKILL.md files were counted by smokes but never
|
|
5
|
-
// content-checked, so a skill could ship without the frontmatter that
|
|
6
|
-
// Claude Code / Copilot CLI need to register it. This gate enforces the
|
|
7
|
-
// minimal contract; it is zero-dependency (line-based YAML subset) so it
|
|
8
|
-
// runs inside `npm test` and ci-lite without installs.
|
|
9
|
-
//
|
|
10
|
-
// Contract per SKILL.md (errors - fail the gate):
|
|
11
|
-
// - frontmatter block present (--- ... ---) starting at line 1
|
|
12
|
-
// - name: required, lowercase kebab-case (underscores tolerated for
|
|
13
|
-
// legacy skills already published under a snake_case name)
|
|
14
|
-
// - description: required, non-empty, single line, <= 1024 chars
|
|
15
|
-
// - user-invocable: when present, must be true or false
|
|
16
|
-
// - platform: when present, must be ios | android | common | all
|
|
17
|
-
//
|
|
18
|
-
// Spec-conformance signals (warnings - never fail the gate):
|
|
19
|
-
// - name should match the agent-skills spec pattern
|
|
20
|
-
// (lowercase letters/digits/hyphens, <= 64 chars)
|
|
21
|
-
// - description should carry a routing/trigger clause ("use when ...")
|
|
22
|
-
// so the model can decide when to load the skill
|
|
23
|
-
// - a body over ~500 lines with no companion-file reference should be
|
|
24
|
-
// split into references/ files loaded on demand
|
|
25
|
-
|
|
26
|
-
import { readFileSync } from "node:fs";
|
|
27
|
-
import { execSync } from "node:child_process";
|
|
28
|
-
import { dirname, join } from "node:path";
|
|
29
|
-
import { fileURLToPath } from "node:url";
|
|
30
|
-
|
|
31
|
-
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
32
|
-
// Scope to pipeline/skills only. Slash commands under pipeline/commands also use
|
|
33
|
-
// the <name>/SKILL.md layout now, but they are command definitions
|
|
34
|
-
// (description + allowed-tools, name derived from the dir), NOT skills, so the
|
|
35
|
-
// skill frontmatter contract (required `name:`) does not apply to them.
|
|
36
|
-
const files = execSync("find pipeline/skills -name SKILL.md", { cwd: repoRoot, encoding: "utf-8" })
|
|
37
|
-
.trim()
|
|
38
|
-
.split("\n")
|
|
39
|
-
.filter(Boolean)
|
|
40
|
-
.sort();
|
|
41
|
-
|
|
42
|
-
const errors = [];
|
|
43
|
-
const fail = (file, msg) => errors.push(`${file}: ${msg}`);
|
|
44
|
-
// Non-fatal quality signals (a terse description routes poorly). Warnings never
|
|
45
|
-
// fail the gate; they surface skills whose frontmatter could trigger better.
|
|
46
|
-
const warnings = [];
|
|
47
|
-
const warn = (file, msg) => warnings.push(`${file}: ${msg}`);
|
|
48
|
-
|
|
49
|
-
for (const file of files) {
|
|
50
|
-
const raw = readFileSync(join(repoRoot, file), "utf-8");
|
|
51
|
-
|
|
52
|
-
if (!raw.startsWith("---\n")) {
|
|
53
|
-
fail(file, "missing frontmatter block at line 1");
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
const end = raw.indexOf("\n---", 4);
|
|
57
|
-
if (end === -1) {
|
|
58
|
-
fail(file, "frontmatter block never closed");
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const fields = {};
|
|
63
|
-
for (const line of raw.slice(4, end).split("\n")) {
|
|
64
|
-
const m = line.match(/^([a-zA-Z][a-zA-Z0-9-]*):\s*(.*)$/);
|
|
65
|
-
if (m) fields[m[1]] = m[2].replace(/^["']|["']$/g, "").trim();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (!fields.name) {
|
|
69
|
-
fail(file, "missing required field: name");
|
|
70
|
-
} else if (!/^[a-z0-9]+([-_][a-z0-9]+)*$/.test(fields.name)) {
|
|
71
|
-
fail(file, `name is not lowercase kebab/snake-case: "${fields.name}"`);
|
|
72
|
-
} else {
|
|
73
|
-
// Spec conformance (warn-only): the agent-skills spec allows lowercase
|
|
74
|
-
// letters, digits, and hyphens, max 64 chars. Snake_case passes the
|
|
75
|
-
// legacy error check above but is out of spec.
|
|
76
|
-
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(fields.name)) {
|
|
77
|
-
warn(file, `name is outside the agent-skills spec pattern (hyphens only): "${fields.name}"`);
|
|
78
|
-
}
|
|
79
|
-
if (fields.name.length > 64) {
|
|
80
|
-
warn(file, `name exceeds the spec's 64-char limit (${fields.name.length})`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (!("description" in fields)) {
|
|
85
|
-
fail(file, "missing required field: description");
|
|
86
|
-
} else if (fields.description.length === 0) {
|
|
87
|
-
fail(file, "description is empty");
|
|
88
|
-
} else if (fields.description.length > 1024) {
|
|
89
|
-
fail(file, `description exceeds 1024 chars (${fields.description.length})`);
|
|
90
|
-
} else {
|
|
91
|
-
// Routing quality: a description of a few words rarely says what the skill
|
|
92
|
-
// does AND when to use it, so the model cannot decide to trigger it.
|
|
93
|
-
// Skip YAML block scalars (`description: |` / `>`): this line-based parser
|
|
94
|
-
// only sees the indicator, not the real multi-line text, so a word count
|
|
95
|
-
// would be a false positive.
|
|
96
|
-
const desc = fields.description;
|
|
97
|
-
const isBlockScalar = /^[|>][+-]?\d*$/.test(desc);
|
|
98
|
-
const words = desc.split(/\s+/).filter(Boolean).length;
|
|
99
|
-
if (!isBlockScalar && words < 6) {
|
|
100
|
-
warn(file, `description is terse (${words} words) - say what it does AND when to use it`);
|
|
101
|
-
}
|
|
102
|
-
// Routing quality (warn-only): without a trigger clause the model has no
|
|
103
|
-
// signal for WHEN to load the skill. Heuristic phrase match; block
|
|
104
|
-
// scalars are skipped for the same parser reason as above.
|
|
105
|
-
const ROUTING_RE = /\b(use (this|it|when|for|whenever)|when (the|a|an|you|asked|working|building|implementing|writing|reviewing)|triggers?|invoked?( when| for)?|applies when|activated?)\b/i;
|
|
106
|
-
if (!isBlockScalar && !ROUTING_RE.test(desc)) {
|
|
107
|
-
warn(file, "description has no routing/trigger clause (e.g. \"use when ...\")");
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Body size (warn-only): the agent-skills guidance is to keep SKILL.md
|
|
112
|
-
// lean and push detail into companion files loaded on demand. A ~500+
|
|
113
|
-
// line body that references no companion file loads whole every time.
|
|
114
|
-
const body = raw.slice(end + 4);
|
|
115
|
-
const bodyLines = body.split("\n").length;
|
|
116
|
-
if (bodyLines > 500) {
|
|
117
|
-
const referencesCompanion =
|
|
118
|
-
/\]\((?!https?:|mailto:|#)[^)]+\)/.test(body) || /`[^`\n]*\/[^`\n]*\.(md|sh|mjs|json|py|txt)`/.test(body);
|
|
119
|
-
if (!referencesCompanion) {
|
|
120
|
-
warn(file, `body is ${bodyLines} lines with no companion-file reference - consider splitting into references/`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if ("user-invocable" in fields && !["true", "false"].includes(fields["user-invocable"])) {
|
|
125
|
-
fail(file, `user-invocable must be true|false, got "${fields["user-invocable"]}"`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if ("platform" in fields && !["ios", "android", "common", "all"].includes(fields.platform)) {
|
|
129
|
-
fail(file, `platform must be ios|android|common|all, got "${fields.platform}"`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (warnings.length > 0) {
|
|
134
|
-
console.warn(`lint-skills: ${warnings.length} warning(s) (non-fatal):`);
|
|
135
|
-
for (const w of warnings) console.warn(" " + w);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (errors.length > 0) {
|
|
139
|
-
console.error(`lint-skills: ${errors.length} error(s) across ${files.length} SKILL.md files`);
|
|
140
|
-
for (const e of errors) console.error(" " + e);
|
|
141
|
-
process.exit(1);
|
|
142
|
-
}
|
|
143
|
-
console.log(`lint-skills: ${files.length} SKILL.md files OK`);
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Structured runner for pipeline/scripts/smoke-*.sh.
|
|
3
|
-
//
|
|
4
|
-
// Replaces the bare `for f in smoke-*.sh; do bash "$f" || exit 1; done`
|
|
5
|
-
// loop with per-suite timeouts, timing, and a summary, so a hung smoke
|
|
6
|
-
// (network wait, interactive prompt) can never block `npm test` or CI
|
|
7
|
-
// indefinitely.
|
|
8
|
-
//
|
|
9
|
-
// Env:
|
|
10
|
-
// SMOKE_TIMEOUT_SECONDS per-suite timeout (default 180)
|
|
11
|
-
// SMOKE_NO_BAIL=1 run every suite and summarize instead of
|
|
12
|
-
// stopping at the first failure
|
|
13
|
-
// Args:
|
|
14
|
-
// optional substring filters: `node run-smokes.mjs adapters keychain`
|
|
15
|
-
// runs only suites whose filename contains one of the terms.
|
|
16
|
-
|
|
17
|
-
import { spawnSync } from "node:child_process";
|
|
18
|
-
import { readdirSync } from "node:fs";
|
|
19
|
-
import { dirname, join } from "node:path";
|
|
20
|
-
import { fileURLToPath } from "node:url";
|
|
21
|
-
|
|
22
|
-
const scriptsDir = dirname(fileURLToPath(import.meta.url));
|
|
23
|
-
const timeoutSec = Number(process.env.SMOKE_TIMEOUT_SECONDS || 180);
|
|
24
|
-
const bail = process.env.SMOKE_NO_BAIL !== "1";
|
|
25
|
-
const filters = process.argv.slice(2);
|
|
26
|
-
|
|
27
|
-
const suites = readdirSync(scriptsDir)
|
|
28
|
-
.filter((f) => f.startsWith("smoke-") && f.endsWith(".sh"))
|
|
29
|
-
.filter((f) => filters.length === 0 || filters.some((t) => f.includes(t)))
|
|
30
|
-
.sort();
|
|
31
|
-
|
|
32
|
-
if (suites.length === 0) {
|
|
33
|
-
console.error("run-smokes: no smoke suites matched");
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const failures = [];
|
|
38
|
-
let ran = 0;
|
|
39
|
-
const startedAt = Date.now();
|
|
40
|
-
|
|
41
|
-
for (const suite of suites) {
|
|
42
|
-
ran++;
|
|
43
|
-
const t0 = Date.now();
|
|
44
|
-
const res = spawnSync("bash", [join(scriptsDir, suite)], {
|
|
45
|
-
encoding: "utf-8",
|
|
46
|
-
timeout: timeoutSec * 1000,
|
|
47
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
48
|
-
});
|
|
49
|
-
const secs = ((Date.now() - t0) / 1000).toFixed(1);
|
|
50
|
-
|
|
51
|
-
const timedOut = res.error && res.error.code === "ETIMEDOUT";
|
|
52
|
-
const ok = !timedOut && res.status === 0;
|
|
53
|
-
|
|
54
|
-
if (ok) {
|
|
55
|
-
console.log(`ok ${suite} (${secs}s)`);
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const reason = timedOut ? `timeout after ${timeoutSec}s` : `exit ${res.status}`;
|
|
60
|
-
console.error(`FAIL ${suite} (${reason}, ${secs}s)`);
|
|
61
|
-
if (res.stdout) console.error(res.stdout.split("\n").slice(-30).join("\n"));
|
|
62
|
-
if (res.stderr) console.error(res.stderr.split("\n").slice(-30).join("\n"));
|
|
63
|
-
failures.push(suite);
|
|
64
|
-
if (bail) break;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const totalSecs = ((Date.now() - startedAt) / 1000).toFixed(1);
|
|
68
|
-
const skipped = suites.length - ran;
|
|
69
|
-
console.log(
|
|
70
|
-
`run-smokes: ${ran - failures.length}/${suites.length} passed in ${totalSecs}s` +
|
|
71
|
-
(skipped > 0 ? ` (${skipped} not run after bail)` : ""),
|
|
72
|
-
);
|
|
73
|
-
if (failures.length > 0) {
|
|
74
|
-
console.error(`run-smokes: failed -> ${failures.join(", ")}`);
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-add-detail.sh - contract check for the `add-detail` command.
|
|
3
|
-
#
|
|
4
|
-
# Does NOT hit real Bitbucket / Jira / GitHub. Instead:
|
|
5
|
-
# 1. Generates a fake PR diff and body fixture.
|
|
6
|
-
# 2. Runs the body-file + jq --rawfile + curl --data-binary pipeline
|
|
7
|
-
# against a local `jq` / `python -c` simulator that checks the
|
|
8
|
-
# resulting JSON payload round-trips with:
|
|
9
|
-
# - real newlines (no literal "\n")
|
|
10
|
-
# - no HTML entities (& / < / " / ')
|
|
11
|
-
# - required Bitbucket PUT fields present: reviewers, fromRef, toRef, draft
|
|
12
|
-
# 3. Returns non-zero if any contract is violated.
|
|
13
|
-
#
|
|
14
|
-
# Intended as a CI guard so regressions like the "23 reviewers wiped" incident
|
|
15
|
-
# cannot land unnoticed.
|
|
16
|
-
|
|
17
|
-
set -euo pipefail
|
|
18
|
-
|
|
19
|
-
TMP="$(mktemp -d -t multi-agent-smoke-XXXX)" || { echo "FATAL: mktemp failed" >&2; exit 1; }
|
|
20
|
-
trap 'rm -rf "$TMP"' EXIT
|
|
21
|
-
|
|
22
|
-
PASS=0
|
|
23
|
-
FAIL=0
|
|
24
|
-
|
|
25
|
-
pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
|
|
26
|
-
fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
27
|
-
|
|
28
|
-
need() {
|
|
29
|
-
command -v "$1" >/dev/null 2>&1 || {
|
|
30
|
-
echo "error: $1 is required" >&2
|
|
31
|
-
exit 2
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
need jq
|
|
35
|
-
need python3
|
|
36
|
-
|
|
37
|
-
echo "→ Writing fixture body with newlines, '&' and Turkish chars"
|
|
38
|
-
cat > "$TMP/body.md" <<'EOF'
|
|
39
|
-
## Özet
|
|
40
|
-
|
|
41
|
-
- Kök neden: UILabel'e `adjustsFontSizeToFitWidth` eklenmemiş
|
|
42
|
-
- Çözüm: `minimumScaleFactor = 0.7` ile ölçek desteği
|
|
43
|
-
- Etki: İspanyolca & Almanca'da taşma düzeliyor
|
|
44
|
-
|
|
45
|
-
## Test Senaryoları
|
|
46
|
-
|
|
47
|
-
| # | Ön koşul | Adımlar | Beklenen |
|
|
48
|
-
|---|----------|---------|----------|
|
|
49
|
-
| 1 | Dil = ES | Check-in Summary aç | Şehir isimleri sığar |
|
|
50
|
-
EOF
|
|
51
|
-
|
|
52
|
-
echo "→ Building payload with jq --rawfile"
|
|
53
|
-
jq -n \
|
|
54
|
-
--rawfile body "$TMP/body.md" \
|
|
55
|
-
--arg title 'fix(checkin): Spanish overflow [PROJ-12345]' \
|
|
56
|
-
'{
|
|
57
|
-
title: $title,
|
|
58
|
-
description: $body,
|
|
59
|
-
fromRef: { id: "refs/heads/bugfix/PROJ-12345", repository: { slug: "my-app", project: { key: "APP" } } },
|
|
60
|
-
toRef: { id: "refs/heads/develop", repository: { slug: "my-app", project: { key: "APP" } } },
|
|
61
|
-
reviewers: [{ user: { name: "alice" } }, { user: { name: "bob" } }],
|
|
62
|
-
draft: false,
|
|
63
|
-
state: "OPEN", open: true, closed: false, locked: false
|
|
64
|
-
}' > "$TMP/payload.json"
|
|
65
|
-
|
|
66
|
-
echo "→ Contract checks"
|
|
67
|
-
|
|
68
|
-
# Contract 1: payload is valid JSON.
|
|
69
|
-
if jq -e . "$TMP/payload.json" >/dev/null 2>&1; then
|
|
70
|
-
pass "payload is valid JSON"
|
|
71
|
-
else
|
|
72
|
-
fail "payload is not valid JSON"
|
|
73
|
-
fi
|
|
74
|
-
|
|
75
|
-
# Contract 2: description field round-trips with real newlines (not literal \n).
|
|
76
|
-
DESC="$(jq -r .description "$TMP/payload.json")"
|
|
77
|
-
if [[ "$DESC" == *$'\n'* ]]; then
|
|
78
|
-
pass "description contains real newlines"
|
|
79
|
-
else
|
|
80
|
-
fail "description is single-line - newlines were escaped"
|
|
81
|
-
fi
|
|
82
|
-
|
|
83
|
-
# Contract 3: no literal backslash-n sequence survived.
|
|
84
|
-
if grep -q '\\n' <<< "$DESC"; then
|
|
85
|
-
fail "literal \\n escaped into description"
|
|
86
|
-
else
|
|
87
|
-
pass "no literal \\n in description"
|
|
88
|
-
fi
|
|
89
|
-
|
|
90
|
-
# Contract 4: no HTML entities in title or description.
|
|
91
|
-
for field in title description; do
|
|
92
|
-
VALUE="$(jq -r --arg f "$field" '.[$f]' "$TMP/payload.json")"
|
|
93
|
-
if grep -Eq '&(amp|lt|gt|quot|#39);' <<< "$VALUE"; then
|
|
94
|
-
fail "$field contains HTML entities"
|
|
95
|
-
else
|
|
96
|
-
pass "$field has no HTML entities"
|
|
97
|
-
fi
|
|
98
|
-
done
|
|
99
|
-
|
|
100
|
-
# Contract 5: UTF-8 survives end-to-end (checks 'Ö', 'İ', 'ç').
|
|
101
|
-
for glyph in 'Ö' 'İ' 'ç'; do
|
|
102
|
-
if grep -q "$glyph" <<< "$DESC"; then
|
|
103
|
-
pass "UTF-8 glyph '$glyph' preserved"
|
|
104
|
-
else
|
|
105
|
-
fail "UTF-8 glyph '$glyph' missing from description"
|
|
106
|
-
fi
|
|
107
|
-
done
|
|
108
|
-
|
|
109
|
-
# Contract 6: Bitbucket PUT contract - all of reviewers/fromRef/toRef/draft present.
|
|
110
|
-
for field in reviewers fromRef toRef draft; do
|
|
111
|
-
if jq -e "has(\"$field\")" "$TMP/payload.json" >/dev/null; then
|
|
112
|
-
pass "payload has .$field"
|
|
113
|
-
else
|
|
114
|
-
fail "payload missing .$field (Bitbucket will wipe it)"
|
|
115
|
-
fi
|
|
116
|
-
done
|
|
117
|
-
|
|
118
|
-
# Contract 7: reviewers is a non-empty array for this fixture.
|
|
119
|
-
LEN="$(jq '.reviewers | length' "$TMP/payload.json")"
|
|
120
|
-
if [ "$LEN" -gt 0 ]; then
|
|
121
|
-
pass "reviewers array preserved ($LEN entries)"
|
|
122
|
-
else
|
|
123
|
-
fail "reviewers array is empty - would wipe defaults"
|
|
124
|
-
fi
|
|
125
|
-
|
|
126
|
-
# Contract 8: fromRef / toRef are distinct (PR to self is rejected by Bitbucket).
|
|
127
|
-
FROM="$(jq -r .fromRef.id "$TMP/payload.json")"
|
|
128
|
-
TO="$(jq -r .toRef.id "$TMP/payload.json")"
|
|
129
|
-
if [ "$FROM" != "$TO" ]; then
|
|
130
|
-
pass "fromRef ($FROM) ≠ toRef ($TO)"
|
|
131
|
-
else
|
|
132
|
-
fail "fromRef == toRef - invalid PR"
|
|
133
|
-
fi
|
|
134
|
-
|
|
135
|
-
echo
|
|
136
|
-
echo "══ add-detail smoke: $PASS passed, $FAIL failed ══"
|
|
137
|
-
[ "$FAIL" -eq 0 ]
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-agent-guard.sh - behavioral contract for the agent-guard PreToolUse hook.
|
|
3
|
-
#
|
|
4
|
-
# Verifies the guard blocks (exit 2) AI-attribution commits and force-push to a
|
|
5
|
-
# protected branch, allows (exit 0) everything else, fails open on bad input, and
|
|
6
|
-
# NEVER executes the inspected command (injection safety).
|
|
7
|
-
#
|
|
8
|
-
# Runs the guard from the repo copy in a throwaway git repo whose branch we set,
|
|
9
|
-
# so the "bare push on a protected branch" path is exercised deterministically.
|
|
10
|
-
#
|
|
11
|
-
# Exit 0 = all pass, 1 = any failure.
|
|
12
|
-
|
|
13
|
-
set -uo pipefail
|
|
14
|
-
|
|
15
|
-
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
16
|
-
GUARD="$ROOT/pipeline/scripts/agent-guard.sh"
|
|
17
|
-
command -v python3 >/dev/null 2>&1 || { echo "error: python3 required" >&2; exit 127; }
|
|
18
|
-
[ -f "$GUARD" ] || { echo "FAIL: agent-guard.sh missing at $GUARD" >&2; exit 1; }
|
|
19
|
-
[ -f "$ROOT/pipeline/scripts/agent-guard.py" ] || { echo "FAIL: agent-guard.py missing" >&2; exit 1; }
|
|
20
|
-
|
|
21
|
-
PASS=0; FAIL=0
|
|
22
|
-
pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
|
|
23
|
-
fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
24
|
-
|
|
25
|
-
# throwaway repo on a protected branch so `git rev-parse` inside the guard = main
|
|
26
|
-
WORK="$(mktemp -d)"
|
|
27
|
-
trap 'rm -rf "$WORK"' EXIT
|
|
28
|
-
# init on main WITH a commit so `git rev-parse --abbrev-ref HEAD` resolves to main
|
|
29
|
-
# (an unborn branch reports HEAD, which would not exercise the bare-push path)
|
|
30
|
-
( cd "$WORK" \
|
|
31
|
-
&& git init -q \
|
|
32
|
-
&& git symbolic-ref HEAD refs/heads/main \
|
|
33
|
-
&& git -c user.email=t@t -c user.name=t commit -q --allow-empty -m init ) 2>/dev/null
|
|
34
|
-
|
|
35
|
-
J() { python3 -c 'import json,sys; print(json.dumps({"tool_input":{"command":sys.argv[1]}}))' "$1"; }
|
|
36
|
-
run() { # label expected-exit command-string
|
|
37
|
-
local got
|
|
38
|
-
( cd "$WORK" && printf '%s' "$(J "$3")" | bash "$GUARD" >/dev/null 2>&1 )
|
|
39
|
-
got=$?
|
|
40
|
-
if [ "$got" = "$2" ]; then pass "[$got] $1"; else fail "expected $2 got $got: $1"; fi
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
echo "→ blocks (exit 2)"
|
|
44
|
-
run "commit Co-Authored-By: Claude" 2 'git commit -m "x
|
|
45
|
-
|
|
46
|
-
Co-Authored-By: Claude <noreply@anthropic.com>"'
|
|
47
|
-
run "commit robot-emoji" 2 'git commit -m "x
|
|
48
|
-
|
|
49
|
-
🤖 Generated with Claude Code"'
|
|
50
|
-
run "push --force origin main" 2 'git push --force origin main'
|
|
51
|
-
run "push -f origin develop" 2 'git push -f origin develop'
|
|
52
|
-
run "push --force-with-lease master" 2 'git push --force-with-lease origin master'
|
|
53
|
-
run "bare push -f on main" 2 'git push -f'
|
|
54
|
-
|
|
55
|
-
echo "→ allows (exit 0)"
|
|
56
|
-
run "normal commit" 0 'git commit -m "feat: normal"'
|
|
57
|
-
run "push no force" 0 'git push origin main'
|
|
58
|
-
run "force push feature branch" 0 'git push --force origin feature/DEMO-1'
|
|
59
|
-
run "force push tag" 0 'git push -f origin v1.2.3'
|
|
60
|
-
run "non-git command" 0 'ls -la /tmp'
|
|
61
|
-
run "empty command" 0 ''
|
|
62
|
-
|
|
63
|
-
echo "→ fail-open on bad input"
|
|
64
|
-
( cd "$WORK" && printf 'not-json{' | bash "$GUARD" >/dev/null 2>&1 ); [ $? -eq 0 ] && pass "malformed json -> allow" || fail "malformed json blocked"
|
|
65
|
-
( cd "$WORK" && printf '' | bash "$GUARD" >/dev/null 2>&1 ); [ $? -eq 0 ] && pass "empty stdin -> allow" || fail "empty stdin blocked"
|
|
66
|
-
|
|
67
|
-
echo "→ injection safety (inspected command must NOT execute)"
|
|
68
|
-
SENT="$WORK/sentinel"; : > "$SENT"
|
|
69
|
-
( cd "$WORK" && printf '%s' "$(J "git commit -m \"\$(rm -f '$SENT')\"")" | bash "$GUARD" >/dev/null 2>&1 )
|
|
70
|
-
[ -f "$SENT" ] && pass "sentinel survived (no execution)" || fail "sentinel deleted - command executed"
|
|
71
|
-
|
|
72
|
-
echo ""
|
|
73
|
-
echo "══ agent-guard smoke: $PASS passed, $FAIL failed ══"
|
|
74
|
-
[ "$FAIL" -eq 0 ]
|