@mmerterden/multi-agent-pipeline 20.34.0 → 20.35.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 +55 -1
- package/README.md +1 -0
- package/docs/facts.json +2 -2
- package/install/catalog-history.json +1 -1
- package/install/claude.mjs +16 -1
- package/install/templates/claude-hooks.json +23 -1
- package/manifest.json +324 -293
- package/package.json +1 -1
- package/pipeline/agents/bulk-reader.md +3 -0
- package/pipeline/commands/multi-agent/channels/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/office-on/SKILL.md +7 -3
- package/pipeline/commands/multi-agent/office-status/SKILL.md +3 -2
- package/pipeline/contract/CHANGELOG.md +9 -0
- package/pipeline/contract/README.md +1 -0
- package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
- package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
- package/pipeline/contract/fixtures/error-unsigned.json +1 -1
- package/pipeline/contract/manifest.json +14 -1
- package/pipeline/contract/types/index.d.ts +24 -2
- package/pipeline/lib/json-file-lock.mjs +61 -15
- package/pipeline/lib/office-remote.mjs +228 -0
- package/pipeline/lib/qr-code.mjs +360 -0
- package/pipeline/lib/repo-hygiene.sh +1 -0
- package/pipeline/lib/run-completion.mjs +17 -0
- package/pipeline/multi-agent-refs/channels/issue-comment.md +1 -1
- package/pipeline/multi-agent-refs/features/office-remote-protocol.md +166 -0
- package/pipeline/multi-agent-refs/features/office-threat-model.md +1 -1
- package/pipeline/multi-agent-refs/features/scope-check.md +16 -1
- package/pipeline/multi-agent-refs/features/verify-by-test.md +10 -0
- package/pipeline/multi-agent-refs/keychain.md +7 -0
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +6 -2
- package/pipeline/multi-agent-refs/phases/phase-1-plan.md +4 -6
- package/pipeline/multi-agent-refs/phases/phase-2-dev.md +9 -12
- package/pipeline/multi-agent-refs/phases/phase-3-review.md +9 -8
- package/pipeline/multi-agent-refs/phases/phase-4-commit.md +1 -4
- package/pipeline/multi-agent-refs/rules.md +3 -2
- package/pipeline/multi-agent-refs/tracker-contract.md +1 -1
- package/pipeline/office/NOTICE.md +4 -1
- package/pipeline/office/assets/char_12.png +0 -0
- package/pipeline/office/assets/char_13.png +0 -0
- package/pipeline/office/assets/char_14.png +0 -0
- package/pipeline/office/assets/char_15.png +0 -0
- package/pipeline/office/assets/char_16.png +0 -0
- package/pipeline/office/assets/char_17.png +0 -0
- package/pipeline/office/assets/char_18.png +0 -0
- package/pipeline/office/assets/char_19.png +0 -0
- package/pipeline/office/assets/char_20.png +0 -0
- package/pipeline/office/assets/char_21.png +0 -0
- package/pipeline/office/assets/char_22.png +0 -0
- package/pipeline/office/assets/char_23.png +0 -0
- package/pipeline/office/assets/char_24.png +0 -0
- package/pipeline/office/assets/char_25.png +0 -0
- package/pipeline/office/assets/char_26.png +0 -0
- package/pipeline/office/assets/char_27.png +0 -0
- package/pipeline/office/assets/char_28.png +0 -0
- package/pipeline/office/assets/char_29.png +0 -0
- package/pipeline/office/assets/char_30.png +0 -0
- package/pipeline/office/index.html +293 -49
- package/pipeline/schemas/agent-state.schema.json +18 -0
- package/pipeline/schemas/planning-output.schema.json +18 -0
- package/pipeline/schemas/pr-reach.schema.json +55 -0
- package/pipeline/schemas/prefs.schema.json +13 -7
- package/pipeline/schemas/triage-output.schema.json +12 -1
- package/pipeline/schemas/unattended-policy.json +2 -1
- package/pipeline/scripts/_guard_rules.py +449 -1
- package/pipeline/scripts/_test-gaming.mjs +36 -0
- package/pipeline/scripts/agent-guard.py +79 -19
- package/pipeline/scripts/anonymize-findings.mjs +99 -0
- package/pipeline/scripts/answer-check.mjs +113 -0
- package/pipeline/scripts/answer-check.sh +20 -0
- package/pipeline/scripts/autopilot-publish.mjs +9 -2
- package/pipeline/scripts/contract-server.mjs +35 -1
- package/pipeline/scripts/gate-ledger.mjs +144 -21
- package/pipeline/scripts/office-build.mjs +276 -5
- package/pipeline/scripts/office-control.mjs +70 -10
- package/pipeline/scripts/office-pair.mjs +407 -0
- package/pipeline/scripts/office-remote-client.mjs +463 -0
- package/pipeline/scripts/phase-tracker.sh +8 -0
- package/pipeline/scripts/phase0-exit-gate.mjs +40 -37
- package/pipeline/scripts/phone-devices.mjs +9 -1
- package/pipeline/scripts/pre-commit-check.sh +28 -2
- package/pipeline/scripts/pre-push-check.sh +6 -12
- package/pipeline/scripts/rework-base.mjs +87 -0
- package/pipeline/scripts/run-commit-check.mjs +104 -0
- package/pipeline/scripts/run-scope-guard.mjs +58 -3
- package/pipeline/scripts/run-scope-guard.sh +13 -1
- package/pipeline/scripts/scope-check-gate.mjs +70 -6
- package/pipeline/scripts/task-mirror.mjs +106 -15
- package/pipeline/scripts/task-mirror.sh +3 -2
- package/pipeline/scripts/test-stability-gate.mjs +112 -0
- package/pipeline/scripts/update-check.sh +7 -1
- package/pipeline/scripts/update-issue-progress.sh +73 -8
- package/pipeline/scripts/validate-planning.mjs +105 -1
- package/pipeline/scripts/validate-triage.mjs +42 -2
- package/pipeline/scripts/work-items.mjs +119 -1
- package/pipeline/scripts/worktree-prepare-all.mjs +200 -0
- package/pipeline/scripts/worktree-prepare.sh +40 -4
- package/pipeline/scripts/write-state.mjs +67 -24
- package/pipeline/skills/.skill-manifest.json +5 -5
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +5 -5
- package/pipeline/skills/shared/core/google-play-compliance/references/rules.yml +1 -1
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +2 -2
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +14 -0
- package/pipeline/skills/shared/external/android-architecture/references/patterns.md +6 -0
- package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +10 -0
- package/pipeline/skills/shared/external/android-datastore/references/patterns.md +11 -0
- package/pipeline/skills/shared/external/android-design-tokens-codegen/references/patterns.md +8 -0
- package/pipeline/skills/shared/external/android-jetpack-compose-expert/references/patterns.md +6 -0
- package/pipeline/skills/shared/external/android-mvi-viewmodel/references/patterns.md +10 -0
- package/pipeline/skills/shared/external/android-performance/references/patterns.md +13 -0
- package/pipeline/skills/shared/external/android-security/references/patterns.md +16 -0
- package/pipeline/skills/shared/external/api-patterns/references/contract-details.md +9 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/abuse-controls.md +12 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/identity-and-access.md +13 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/request-handling.md +12 -0
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +9 -0
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +8 -0
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +10 -0
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +21 -0
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +9 -0
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +7 -0
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +2 -2
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +10 -0
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +10 -1
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +22 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +17 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +22 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +17 -0
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +9 -0
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +11 -0
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +13 -0
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +9 -0
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +10 -0
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +15 -0
- package/pipeline/skills/shared/external/compose-components/references/patterns.md +7 -0
- package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +7 -0
- package/pipeline/skills/shared/external/compose-testing/references/patterns.md +15 -0
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +9 -0
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +11 -0
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +10 -0
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +13 -0
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +20 -0
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +16 -0
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +16 -0
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +14 -0
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +13 -0
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +10 -0
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +10 -0
- package/pipeline/skills/shared/external/fastapi-pro/references/app-structure.md +10 -0
- package/pipeline/skills/shared/external/gradle-kotlin-dsl/references/patterns.md +17 -0
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +12 -0
- package/pipeline/skills/shared/external/hig-app-icons/references/icon-workflow.md +10 -0
- package/pipeline/skills/shared/external/hig-components-content/references/content-views.md +10 -0
- package/pipeline/skills/shared/external/hig-components-layout/references/bars-and-tabs.md +11 -0
- package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +13 -0
- package/pipeline/skills/shared/external/hig-foundations/references/type-color-spacing.md +13 -0
- package/pipeline/skills/shared/external/hig-patterns/references/onboarding-and-states.md +11 -0
- package/pipeline/skills/shared/external/hig-platforms/references/ipad-layout.md +12 -0
- package/pipeline/skills/shared/external/hig-technologies/references/technologies.md +16 -0
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +9 -0
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +18 -0
- package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md +9 -0
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +9 -0
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +7 -0
- package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +13 -0
- package/pipeline/skills/shared/external/ios-design-review/references/appearance-matrix.md +9 -0
- package/pipeline/skills/shared/external/ios-design-review/references/checklist.md +15 -0
- package/pipeline/skills/shared/external/ios-haptics/references/core-haptics.md +11 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/references/api-map.md +11 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/references/fixed-assumptions.md +15 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/references/poses-and-panes.md +11 -0
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +18 -0
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +19 -0
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +15 -0
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +7 -0
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +8 -0
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +12 -0
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +17 -0
- package/pipeline/skills/shared/external/ios-security/references/app-review-guidelines.md +8 -0
- package/pipeline/skills/shared/external/ios-security/references/cryptokit-advanced.md +14 -0
- package/pipeline/skills/shared/external/ios-security/references/file-storage-patterns.md +8 -0
- package/pipeline/skills/shared/external/ios-security/references/privacy-manifest.md +9 -0
- package/pipeline/skills/shared/external/ios-sf-symbols/references/effects-and-custom-symbols.md +9 -0
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +13 -0
- package/pipeline/skills/shared/external/kotlin-coroutines-expert/references/patterns.md +8 -0
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +21 -0
- package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +9 -0
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +12 -0
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +16 -0
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +21 -0
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +10 -0
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +9 -0
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +12 -0
- package/pipeline/skills/shared/external/nodejs-backend-patterns/references/runtime-patterns.md +11 -0
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +11 -0
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +18 -0
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +11 -0
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +11 -0
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +15 -0
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +14 -0
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +14 -0
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +14 -0
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +14 -0
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +10 -0
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +12 -0
- package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +14 -0
- package/pipeline/skills/shared/external/room-database/references/patterns.md +16 -0
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +10 -0
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +10 -0
- package/pipeline/skills/shared/external/spm-build-analysis/references/build-optimization-sources.md +14 -0
- package/pipeline/skills/shared/external/spm-build-analysis/references/spm-analysis-checks.md +19 -0
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +13 -0
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +31 -0
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +8 -0
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +9 -0
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +10 -0
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +8 -0
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +24 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +8 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +14 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +12 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +10 -0
- package/pipeline/skills/shared/external/swift-concurrency-expert/references/swift-6-2-concurrency.md +8 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/actors.md +10 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/cancellation.md +7 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/interop.md +10 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/new-features.md +11 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/structured.md +9 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/testing.md +13 -0
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +7 -0
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +14 -0
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +16 -0
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +17 -0
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +19 -0
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +16 -0
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +19 -0
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +18 -0
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +16 -0
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +17 -0
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +17 -0
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +17 -0
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +18 -0
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +21 -0
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +16 -0
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +16 -0
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +9 -0
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +24 -0
- package/pipeline/skills/shared/external/swift-testing-pro/references/async-tests.md +9 -0
- package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +11 -0
- package/pipeline/skills/shared/external/swift-testing-pro/references/writing-better-tests.md +13 -0
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +8 -0
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +17 -0
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +12 -0
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +11 -0
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +7 -0
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +13 -0
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +24 -0
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +12 -0
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +15 -0
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +15 -0
- package/pipeline/skills/shared/external/swiftui-animation/references/scroll-effects.md +12 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/accessibility-patterns.md +11 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +7 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +13 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +9 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts-accessibility.md +9 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts.md +17 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/image-optimization.md +9 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/latest-apis.md +10 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/layout-best-practices.md +12 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/list-patterns.md +12 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-scenes.md +13 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-views.md +12 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-window-styling.md +12 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/performance-patterns.md +7 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/scroll-patterns.md +9 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/sheet-navigation-patterns.md +10 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/state-management.md +18 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/view-structure.md +16 -0
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +11 -0
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +8 -0
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +14 -0
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +9 -0
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +22 -0
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/uikit-glass.md +14 -0
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +10 -0
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +10 -0
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +13 -0
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +10 -0
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +14 -0
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +30 -0
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +16 -0
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +11 -0
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +11 -0
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +10 -0
- package/pipeline/skills/shared/external/swiftui-performance-audit/references/code-smells.md +8 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/app-wiring.md +12 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/menu-bar.md +10 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/navigationstack.md +11 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/scroll-reveal.md +12 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/sheets.md +14 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/tabview.md +10 -0
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +10 -0
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +13 -0
- package/pipeline/skills/shared/external/swiftui-view-refactor/references/mv-patterns.md +12 -0
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +8 -0
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +8 -0
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +14 -0
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +17 -0
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +11 -0
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +11 -0
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +27 -0
- package/pipeline/skills/shared/external/xcode-build-fixer/references/build-settings-best-practices.md +9 -0
- package/pipeline/skills/shared/external/xcode-build-fixer/references/fix-patterns.md +7 -0
- package/pipeline/skills/shared/external/xcode-build-orchestrator/references/build-settings-best-practices.md +9 -0
- package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/build-optimization-sources.md +14 -0
- package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/code-compilation-checks.md +8 -0
- package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-optimization-sources.md +14 -0
- package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-settings-best-practices.md +9 -0
- package/pipeline/skills/shared/external/xcode-project-analyzer/references/project-audit-checks.md +13 -0
|
@@ -2,19 +2,13 @@
|
|
|
2
2
|
# pre-push-check.sh - primary gate before every push.
|
|
3
3
|
#
|
|
4
4
|
# Purpose:
|
|
5
|
-
# The
|
|
6
|
-
#
|
|
7
|
-
# finished in about four seconds with zero steps recorded, which is a
|
|
8
|
-
# runner/quota block rather than a test failure. Both are now dormant by
|
|
9
|
-
# declaration too (ADR-0011: the push triggers are commented out). So this
|
|
10
|
-
# hook is not a second opinion - it is the only thing standing between a
|
|
11
|
-
# broken commit and npm.
|
|
5
|
+
# The local gate before every push. The repository's CI workflows are dormant
|
|
6
|
+
# (ADR-0011), so this is what stands between a commit and npm.
|
|
12
7
|
#
|
|
13
|
-
# It
|
|
14
|
-
#
|
|
15
|
-
# `for f in smoke-*.sh` loop cannot tell a passing suite from one that exited
|
|
16
|
-
# having asserted nothing; run-smokes.mjs can.
|
|
17
|
-
# it is trusted.
|
|
8
|
+
# It runs the canonical chain, `npm test`, rather than a hand-picked subset:
|
|
9
|
+
# a subset drifts from the chain as steps are added, and a bare
|
|
10
|
+
# `for f in smoke-*.sh` loop cannot tell a passing suite from one that exited
|
|
11
|
+
# 0 having asserted nothing; run-smokes.mjs can.
|
|
18
12
|
#
|
|
19
13
|
# Two modes, because a gate that runs INSIDE the push is a gate that breaks
|
|
20
14
|
# pushes. Git opens the connection to the remote before this hook fires, so a
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @file rework-base.mjs - the tree a rework round started from.
|
|
4
|
+
*
|
|
5
|
+
* scope-check-gate.mjs --rejected compares the rework against the tree the
|
|
6
|
+
* review read, because rejected findings carry that tree's line numbers. Each
|
|
7
|
+
* review round rewrites triage-output.json, so the base is kept per round:
|
|
8
|
+
* `.pipeline/rework-base-<iteration>`. `record` writes HEAD for the round when
|
|
9
|
+
* the round has none yet (a resumed re-entry keeps the base it started from)
|
|
10
|
+
* and prints it; `get` only prints it.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* rework-base.mjs record --worktree <dir> --iteration <N>
|
|
14
|
+
* rework-base.mjs get --worktree <dir> --iteration <N>
|
|
15
|
+
*
|
|
16
|
+
* Exit: 0 printed, 1 no base recorded for the round (get) or HEAD unreadable
|
|
17
|
+
* (record), 64 usage.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { execFileSync } from "node:child_process";
|
|
21
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
|
|
24
|
+
import { invokedDirectly } from "../lib/invoked-directly.mjs";
|
|
25
|
+
|
|
26
|
+
const SHA = /^[0-9a-f]{40,64}$/;
|
|
27
|
+
|
|
28
|
+
export function basePath(worktree, iteration) {
|
|
29
|
+
return join(worktree, ".pipeline", `rework-base-${iteration}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function read(path) {
|
|
33
|
+
if (!existsSync(path)) return null;
|
|
34
|
+
const sha = readFileSync(path, "utf8").trim();
|
|
35
|
+
return SHA.test(sha) ? sha : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function record(worktree, iteration) {
|
|
39
|
+
const path = basePath(worktree, iteration);
|
|
40
|
+
const kept = read(path);
|
|
41
|
+
if (kept) return kept;
|
|
42
|
+
const head = execFileSync("git", ["-C", worktree, "rev-parse", "HEAD"], {
|
|
43
|
+
encoding: "utf8",
|
|
44
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
45
|
+
timeout: 30_000,
|
|
46
|
+
}).trim();
|
|
47
|
+
if (!SHA.test(head)) throw new Error("HEAD is not a commit");
|
|
48
|
+
mkdirSync(join(worktree, ".pipeline"), { recursive: true });
|
|
49
|
+
writeFileSync(path, head + "\n");
|
|
50
|
+
return head;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function get(worktree, iteration) {
|
|
54
|
+
return read(basePath(worktree, iteration));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function main() {
|
|
58
|
+
const [cmd, ...rest] = process.argv.slice(2);
|
|
59
|
+
const opt = (f) => {
|
|
60
|
+
const i = rest.indexOf(f);
|
|
61
|
+
return i >= 0 ? rest[i + 1] : undefined;
|
|
62
|
+
};
|
|
63
|
+
const worktree = opt("--worktree");
|
|
64
|
+
const iteration = Number(opt("--iteration"));
|
|
65
|
+
if (
|
|
66
|
+
!["record", "get"].includes(cmd) ||
|
|
67
|
+
rest.length !== 4 ||
|
|
68
|
+
!worktree ||
|
|
69
|
+
!Number.isInteger(iteration) ||
|
|
70
|
+
iteration < 1
|
|
71
|
+
) {
|
|
72
|
+
console.error("usage: rework-base.mjs record|get --worktree <dir> --iteration <N>");
|
|
73
|
+
return 64;
|
|
74
|
+
}
|
|
75
|
+
let sha;
|
|
76
|
+
try {
|
|
77
|
+
sha = cmd === "record" ? record(worktree, iteration) : get(worktree, iteration);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
console.error(`rework-base: ${err.message}`);
|
|
80
|
+
return 1;
|
|
81
|
+
}
|
|
82
|
+
if (!sha) return 1;
|
|
83
|
+
process.stdout.write(sha + "\n");
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (invokedDirectly(import.meta.url)) process.exit(main());
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @file run-commit-check.mjs - a commit inside a pipeline run is authored by
|
|
4
|
+
* the identity Phase 0 chose for it.
|
|
5
|
+
*
|
|
6
|
+
* Called by pre-commit-check.sh with the hook payload on stdin. For a `git
|
|
7
|
+
* commit` made in the session a live run is bound to, the author git will
|
|
8
|
+
* record (`--author`, `-c user.email=`, else `git var GIT_AUTHOR_IDENT` in the
|
|
9
|
+
* repository) must carry the run's identity email (`projects[].identity` for
|
|
10
|
+
* the repository's worktree, else `identity`). Outside a bound run, for a run
|
|
11
|
+
* that is not live, or with no identity recorded, it says nothing.
|
|
12
|
+
*
|
|
13
|
+
* Usage: run-commit-check.mjs --repo <dir> < hook-payload.json
|
|
14
|
+
* Exit: 0 fine or not judged; 2 the author is not the run's identity.
|
|
15
|
+
*
|
|
16
|
+
* @module pipeline/scripts/run-commit-check
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { spawnSync } from "node:child_process";
|
|
20
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
21
|
+
import { resolve } from "node:path";
|
|
22
|
+
|
|
23
|
+
import { invokedDirectly } from "../lib/invoked-directly.mjs";
|
|
24
|
+
import { findActiveRun } from "./_active-run.mjs";
|
|
25
|
+
|
|
26
|
+
const SESSION_BOUND = new Set(["session", "host-session"]);
|
|
27
|
+
const LIVE = new Set(["in_progress", "awaiting_input"]);
|
|
28
|
+
const COMMIT = /\bgit\b(?:\s+-[cC]\s+\S+|\s+--?[\w-]+(?:=\S+)?)*\s+commit\b/;
|
|
29
|
+
|
|
30
|
+
const real = (p) => {
|
|
31
|
+
try {
|
|
32
|
+
return realpathSync(resolve(p));
|
|
33
|
+
} catch {
|
|
34
|
+
return resolve(p);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** The author email this command makes git record, or null to ask git. */
|
|
39
|
+
export function commandAuthor(command) {
|
|
40
|
+
const author = /--author[=\s]+(['"]?)[^<'"]*<([^>]+)>\1/.exec(command);
|
|
41
|
+
if (author) return author[2].trim();
|
|
42
|
+
const cfg = /-c\s+user\.email=(['"]?)([^\s'"]+)\1/.exec(command);
|
|
43
|
+
return cfg ? cfg[2].trim() : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The identity email the run expects for a commit in `repo`, or null. */
|
|
47
|
+
export function expectedEmail(state, repo) {
|
|
48
|
+
const here = real(repo);
|
|
49
|
+
for (const p of Array.isArray(state.projects) ? state.projects : []) {
|
|
50
|
+
if (p?.worktreePath && real(p.worktreePath) === here && p.identity?.email)
|
|
51
|
+
return p.identity.email;
|
|
52
|
+
}
|
|
53
|
+
return typeof state.identity?.email === "string" && state.identity.email
|
|
54
|
+
? state.identity.email
|
|
55
|
+
: null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function gitAuthorEmail(repo) {
|
|
59
|
+
const r = spawnSync("git", ["-C", repo, "var", "GIT_AUTHOR_IDENT"], {
|
|
60
|
+
encoding: "utf8",
|
|
61
|
+
timeout: 5000,
|
|
62
|
+
});
|
|
63
|
+
return /<([^>]*)>/.exec(r.stdout || "")?.[1]?.trim() || null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The refusal line, or null. */
|
|
67
|
+
export function verdict(payload, repo, env = process.env) {
|
|
68
|
+
const command = payload?.tool_input?.command;
|
|
69
|
+
if (typeof command !== "string" || !COMMIT.test(command)) return null;
|
|
70
|
+
const run = findActiveRun({
|
|
71
|
+
sessionId: typeof payload.session_id === "string" ? payload.session_id : null,
|
|
72
|
+
cwd: typeof payload.cwd === "string" ? payload.cwd : repo,
|
|
73
|
+
env,
|
|
74
|
+
});
|
|
75
|
+
if (!run || !SESSION_BOUND.has(run.matchedBy) || !LIVE.has(run.state?.status)) return null;
|
|
76
|
+
const want = expectedEmail(run.state, repo);
|
|
77
|
+
if (!want) return null;
|
|
78
|
+
const got = commandAuthor(command) || gitAuthorEmail(repo);
|
|
79
|
+
if (!got || got.toLowerCase() === want.toLowerCase()) return null;
|
|
80
|
+
return `BLOCKED: this commit would be authored by ${got}, and run ${run.state.taskId || run.statePath} commits as ${want} (the identity chosen in Phase 0). Set the worktree's user.email to it, or drop the override.`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function main(argv) {
|
|
84
|
+
const i = argv.indexOf("--repo");
|
|
85
|
+
const repo = i >= 0 && argv[i + 1] ? argv[i + 1] : process.cwd();
|
|
86
|
+
let payload;
|
|
87
|
+
try {
|
|
88
|
+
payload = JSON.parse(readFileSync(0, "utf8"));
|
|
89
|
+
} catch {
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
const line = verdict(payload, repo);
|
|
93
|
+
if (!line) return 0;
|
|
94
|
+
process.stderr.write(`${line}\n`);
|
|
95
|
+
return 2;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (invokedDirectly(import.meta.url)) {
|
|
99
|
+
try {
|
|
100
|
+
process.exitCode = main(process.argv.slice(2));
|
|
101
|
+
} catch {
|
|
102
|
+
process.exitCode = 0;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -51,6 +51,12 @@ const FIGMA_TOOL = /^mcp__.*figma/i;
|
|
|
51
51
|
const SESSION_BOUND = new Set(["session", "host-session"]);
|
|
52
52
|
const LIVE = new Set(["in_progress", "awaiting_input"]);
|
|
53
53
|
const FILE_TOOLS = new Set(["Edit", "Write", "NotebookEdit", "MultiEdit"]);
|
|
54
|
+
const STATE_FILES = new Set(["agent-state.json", "tracker-state.json"]);
|
|
55
|
+
const STATE_REDIRECT =
|
|
56
|
+
/(?:>>?|\btee\s+(?:-a\s+)?)\s*['"]?[^\s'"|;&]*(?:agent|tracker)-state\.json\b/;
|
|
57
|
+
const FIGMA_URL = /\bhttps?:\/\/(?:[\w-]+\.)*figma\.com(?=[/:?#\s'"]|$)/i;
|
|
58
|
+
const ADD_ALL = /\bgit\s+(?:-C\s+\S+\s+)?add\s+(?:[^;&|\n]*\s)?(?:-A|--all|\.)(?=$|[\s;&|])/;
|
|
59
|
+
const HTTP_CLIENT = /(?:^|[\s;&|(])(?:curl|wget|http|https|xh)\s/;
|
|
54
60
|
|
|
55
61
|
const deny = (reason) => ({
|
|
56
62
|
hookSpecificOutput: {
|
|
@@ -76,7 +82,39 @@ async function recordMcpCall(statePath, tool, phase) {
|
|
|
76
82
|
});
|
|
77
83
|
}
|
|
78
84
|
|
|
79
|
-
|
|
85
|
+
/** A Figma design read over HTTP: a WebFetch of figma.com, or curl and the like at it. */
|
|
86
|
+
function figmaOverHttp(payload) {
|
|
87
|
+
const ti = payload.tool_input || {};
|
|
88
|
+
if (payload.tool_name === "WebFetch") return typeof ti.url === "string" && FIGMA_URL.test(ti.url);
|
|
89
|
+
if (payload.tool_name !== "Bash" || typeof ti.command !== "string") return false;
|
|
90
|
+
return HTTP_CLIENT.test(ti.command) && FIGMA_URL.test(ti.command);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** An autopilot run answers its own questions: it derives, or it records a halt. */
|
|
94
|
+
function askDecision(run) {
|
|
95
|
+
const s = run.state;
|
|
96
|
+
if (!LIVE.has(s.status) || modeOf(s) !== "autopilot") return null;
|
|
97
|
+
return deny(
|
|
98
|
+
`run ${s.taskId || run.statePath} is on autopilot, which asks nobody. Derive the answer from the analysis, the repo and the preferences, or record a halt (status "paused" with a haltReason) for a decision only the user can make.`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** A write to run state that bypasses write-state.mjs, or null. */
|
|
103
|
+
function stateWriteDecision(payload) {
|
|
104
|
+
const ti = payload.tool_input || {};
|
|
105
|
+
const raw = FILE_TOOLS.has(payload.tool_name) ? ti.file_path || ti.notebook_path : null;
|
|
106
|
+
const byFile = typeof raw === "string" && STATE_FILES.has(basename(raw));
|
|
107
|
+
const byShell =
|
|
108
|
+
payload.tool_name === "Bash" &&
|
|
109
|
+
typeof ti.command === "string" &&
|
|
110
|
+
STATE_REDIRECT.test(ti.command);
|
|
111
|
+
if (!byFile && !byShell) return null;
|
|
112
|
+
return deny(
|
|
113
|
+
'run state is written through write-state.mjs only, which locks the file, merges the patch and refuses unearned phase moves; pipe the patch into node "$HOME/.claude/scripts/write-state.mjs" <path> instead.',
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function figmaDecision(payload, run, { record = true } = {}) {
|
|
80
118
|
const tool = payload.tool_name;
|
|
81
119
|
const state = run.state;
|
|
82
120
|
// A finished, failed or abandoned run governs nothing and records nothing.
|
|
@@ -88,7 +126,7 @@ async function figmaDecision(payload, run) {
|
|
|
88
126
|
`${tool} is a Figma call at Phase ${at} of run ${state.taskId || run.statePath}. The design is read during analysis only (through Phase ${figmaThroughPhase()}); later phases work from the analysis document and the repo's Code Connect mappings. If a variant or value is missing, halt and re-run /multi-agent:analysis.`,
|
|
89
127
|
);
|
|
90
128
|
}
|
|
91
|
-
await recordMcpCall(run.statePath, tool, Math.min(5, Math.max(0, at)));
|
|
129
|
+
if (record) await recordMcpCall(run.statePath, tool, Math.min(5, Math.max(0, at)));
|
|
92
130
|
return null;
|
|
93
131
|
}
|
|
94
132
|
|
|
@@ -152,8 +190,16 @@ function boundaryDecision(payload, run, env) {
|
|
|
152
190
|
export async function decide(payload, env = process.env) {
|
|
153
191
|
if (!payload || typeof payload !== "object") return null;
|
|
154
192
|
const tool = typeof payload.tool_name === "string" ? payload.tool_name : "";
|
|
193
|
+
const stateWrite = stateWriteDecision(payload);
|
|
194
|
+
if (stateWrite) return stateWrite;
|
|
155
195
|
const figma = FIGMA_TOOL.test(tool);
|
|
156
|
-
|
|
196
|
+
const figmaHttp = !figma && figmaOverHttp(payload);
|
|
197
|
+
const asking = tool === "AskUserQuestion";
|
|
198
|
+
const addAll =
|
|
199
|
+
tool === "Bash" &&
|
|
200
|
+
typeof payload.tool_input?.command === "string" &&
|
|
201
|
+
ADD_ALL.test(payload.tool_input.command);
|
|
202
|
+
if (!figma && !figmaHttp && !asking && !addAll && !FILE_TOOLS.has(tool)) return null;
|
|
157
203
|
const run = findActiveRun({
|
|
158
204
|
sessionId: typeof payload.session_id === "string" ? payload.session_id : null,
|
|
159
205
|
cwd: typeof payload.cwd === "string" ? payload.cwd : null,
|
|
@@ -162,6 +208,15 @@ export async function decide(payload, env = process.env) {
|
|
|
162
208
|
// Only a run bound to this session: an old local-mode run matched by its
|
|
163
209
|
// worktree (the project root) must not govern every later session there.
|
|
164
210
|
if (!run || !SESSION_BOUND.has(run.matchedBy)) return null;
|
|
211
|
+
if (asking) return askDecision(run);
|
|
212
|
+
if (addAll) {
|
|
213
|
+
return LIVE.has(run.state.status)
|
|
214
|
+
? deny(
|
|
215
|
+
`run ${run.state.taskId || run.statePath} stages the task's files by name: git add -A, --all and . also take agent artefacts and anything sensitive in the tree.`,
|
|
216
|
+
)
|
|
217
|
+
: null;
|
|
218
|
+
}
|
|
219
|
+
if (figmaHttp) return figmaDecision(payload, run, { record: false });
|
|
165
220
|
return figma ? figmaDecision(payload, run) : boundaryDecision(payload, run, env);
|
|
166
221
|
}
|
|
167
222
|
|
|
@@ -19,5 +19,17 @@ CORE="$HERE/run-scope-guard.mjs"
|
|
|
19
19
|
[ -f "$CORE" ] || exit 0
|
|
20
20
|
command -v node >/dev/null 2>&1 || exit 0
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
# Most Bash and WebFetch calls concern neither Figma nor run state; those end
|
|
23
|
+
# here without starting node.
|
|
24
|
+
INPUT="$(cat)"
|
|
25
|
+
case "$INPUT" in
|
|
26
|
+
*'"tool_name":"Bash"'* | *'"tool_name": "Bash"'* | *'"tool_name":"WebFetch"'* | *'"tool_name": "WebFetch"'*)
|
|
27
|
+
case "$INPUT" in
|
|
28
|
+
*[Ff]igma* | *agent-state.json* | *tracker-state.json* | *" add "*) ;;
|
|
29
|
+
*) exit 0 ;;
|
|
30
|
+
esac
|
|
31
|
+
;;
|
|
32
|
+
esac
|
|
33
|
+
|
|
34
|
+
printf '%s' "$INPUT" | node "$CORE" 2>/dev/null || true
|
|
23
35
|
exit 0
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* git diff --name-only <base>...HEAD | node scope-check-gate.mjs --check .pipeline/scope-check.json --diff-files -
|
|
12
12
|
* node scope-check-gate.mjs --check scope-check.json --diff-files files.txt [--advisory]
|
|
13
13
|
* [--patch <unified diff> | --repo <dir> --patch-range <a>...<b>]
|
|
14
|
+
* [--rejected <triage-output.json> --repo <dir> --rework-base <rev>]
|
|
14
15
|
*
|
|
15
16
|
* Output (stdout, JSON): {ok, justified, unjustified[], unlisted[], notDone, formatOnly[]}
|
|
16
17
|
* unjustified = in the diff, missing from files[] (or with an empty reason)
|
|
@@ -20,6 +21,11 @@
|
|
|
20
21
|
* touch. Reported, never fatal. --patch-range reads that diff
|
|
21
22
|
* with git itself, its output format pinned, so a caller needs
|
|
22
23
|
* no file or process substitution for it.
|
|
24
|
+
* rejectedTouched = with --rejected and --rework-base, findings triage
|
|
25
|
+
* rejected whose lines a rework hunk (rework base against the
|
|
26
|
+
* working tree) changed: Phase 2 acts on accepted findings
|
|
27
|
+
* only, so a rework that edits a rejected finding's lines is
|
|
28
|
+
* "fixing" a finding triage ruled out. Reported, never fatal.
|
|
23
29
|
*
|
|
24
30
|
* Exit codes: 0 every diff file justified (or --advisory), 1 otherwise or on
|
|
25
31
|
* unreadable input, 64 usage.
|
|
@@ -32,7 +38,10 @@ import { normalizeFile } from "./_fingerprint.mjs";
|
|
|
32
38
|
import { parseUnifiedDiff } from "./_unified-diff.mjs";
|
|
33
39
|
|
|
34
40
|
const USAGE =
|
|
35
|
-
"usage: scope-check-gate.mjs --check <scope-check.json> --diff-files <path|-> [--advisory] [--patch <diff> | --repo <dir> --patch-range <a>...<b>]";
|
|
41
|
+
"usage: scope-check-gate.mjs --check <scope-check.json> --diff-files <path|-> [--advisory] [--patch <diff> | --repo <dir> --patch-range <a>...<b>] [--rejected <triage.json> --repo <dir> --rework-base <rev>]";
|
|
42
|
+
|
|
43
|
+
const REV = /^[A-Za-z0-9_./~^@{}-]+$/;
|
|
44
|
+
const NEAR = 2;
|
|
36
45
|
|
|
37
46
|
function parseArgs(argv) {
|
|
38
47
|
const opts = {};
|
|
@@ -44,16 +53,19 @@ function parseArgs(argv) {
|
|
|
44
53
|
else if (a === "--patch") opts.patch = argv[++i];
|
|
45
54
|
else if (a === "--patch-range") opts.patchRange = argv[++i];
|
|
46
55
|
else if (a === "--repo") opts.repo = argv[++i];
|
|
56
|
+
else if (a === "--rejected") opts.rejected = argv[++i];
|
|
57
|
+
else if (a === "--rework-base") opts.reworkBase = argv[++i];
|
|
47
58
|
else return null;
|
|
48
59
|
}
|
|
49
|
-
|
|
50
|
-
return null;
|
|
51
|
-
|
|
60
|
+
for (const rev of [opts.patchRange, opts.reworkBase]) {
|
|
61
|
+
if (rev !== undefined && (!REV.test(rev) || rev.startsWith("-"))) return null;
|
|
62
|
+
}
|
|
63
|
+
if (Boolean(opts.rejected) !== Boolean(opts.reworkBase)) return null;
|
|
52
64
|
return opts.check && opts.diffFiles ? opts : null;
|
|
53
65
|
}
|
|
54
66
|
|
|
55
|
-
/** The diff of a revision range, in the one shape
|
|
56
|
-
function rangeDiff(repo, range) {
|
|
67
|
+
/** The diff of a revision range (or a revision against the working tree), in the one shape the hunk readers take. */
|
|
68
|
+
function rangeDiff(repo, range, unified = []) {
|
|
57
69
|
return execFileSync(
|
|
58
70
|
"git",
|
|
59
71
|
[
|
|
@@ -67,6 +79,7 @@ function rangeDiff(repo, range) {
|
|
|
67
79
|
"--no-textconv",
|
|
68
80
|
"--src-prefix=a/",
|
|
69
81
|
"--dst-prefix=b/",
|
|
82
|
+
...unified,
|
|
70
83
|
range,
|
|
71
84
|
"--",
|
|
72
85
|
],
|
|
@@ -180,6 +193,45 @@ export function formatOnlyHunks(patch) {
|
|
|
180
193
|
return found;
|
|
181
194
|
}
|
|
182
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Rejected findings whose line a hunk of the rework diff changed. The diff
|
|
198
|
+
* runs from the reviewed tree, so a finding's line is read on the hunk's old
|
|
199
|
+
* side: a hunk that removes or rewrites lines within NEAR of it, or inserts
|
|
200
|
+
* next to it, touches it.
|
|
201
|
+
*/
|
|
202
|
+
export function rejectedTouched(triage, patch) {
|
|
203
|
+
const rejected = (Array.isArray(triage?.rejected) ? triage.rejected : [])
|
|
204
|
+
.map((r) => r?.finding)
|
|
205
|
+
.filter((f) => f && typeof f.file === "string" && Number.isInteger(f.line) && f.line > 0);
|
|
206
|
+
if (!rejected.length) return [];
|
|
207
|
+
const ranges = new Map();
|
|
208
|
+
for (const [file, diff] of parseUnifiedDiff(patch)) {
|
|
209
|
+
const list = [];
|
|
210
|
+
for (const h of diff.hunks) {
|
|
211
|
+
const m = /^@@ -(\d+)(?:,(\d+))?/.exec(h.header);
|
|
212
|
+
if (!m) continue;
|
|
213
|
+
const start = Number(m[1]);
|
|
214
|
+
const count = m[2] === undefined ? 1 : Number(m[2]);
|
|
215
|
+
list.push({ from: start - NEAR, to: start + Math.max(count, 1) - 1 + NEAR, hunk: h.header });
|
|
216
|
+
}
|
|
217
|
+
ranges.set(normalizeFile(file), list);
|
|
218
|
+
}
|
|
219
|
+
const out = [];
|
|
220
|
+
for (const f of rejected) {
|
|
221
|
+
const hit = (ranges.get(normalizeFile(f.file)) || []).find(
|
|
222
|
+
(r) => f.line >= r.from && f.line <= r.to,
|
|
223
|
+
);
|
|
224
|
+
if (hit)
|
|
225
|
+
out.push({
|
|
226
|
+
file: normalizeFile(f.file),
|
|
227
|
+
line: f.line,
|
|
228
|
+
issue: f.issue ?? null,
|
|
229
|
+
hunk: hit.hunk,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
|
|
183
235
|
export function evaluate(check, diffFiles) {
|
|
184
236
|
const files = Array.isArray(check?.files) ? check.files : [];
|
|
185
237
|
const reasons = new Map();
|
|
@@ -228,6 +280,18 @@ function main() {
|
|
|
228
280
|
result.patchError = err.message;
|
|
229
281
|
}
|
|
230
282
|
}
|
|
283
|
+
if (opts.rejected) {
|
|
284
|
+
try {
|
|
285
|
+
const triage = JSON.parse(readText(opts.rejected));
|
|
286
|
+
result.rejectedTouched = rejectedTouched(
|
|
287
|
+
triage,
|
|
288
|
+
rangeDiff(opts.repo, opts.reworkBase, ["-U0"]),
|
|
289
|
+
);
|
|
290
|
+
} catch (err) {
|
|
291
|
+
result.rejectedTouched = [];
|
|
292
|
+
result.rejectedError = err.message;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
231
295
|
process.stdout.write(JSON.stringify(result) + "\n");
|
|
232
296
|
return result.ok ? 0 : fail;
|
|
233
297
|
}
|
|
@@ -21,12 +21,21 @@
|
|
|
21
21
|
* is left alone.
|
|
22
22
|
* The task goes under the phase in progress when it was created.
|
|
23
23
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
* Phase tiles: a TaskCreate of "Phase N ..." records its id as the phase's
|
|
25
|
+
* `tasklist_id` (what :resume rebuilds the widget from), replacing the id a
|
|
26
|
+
* previous session left. The widget renders tiles in creation order, so a
|
|
27
|
+
* tile created after a higher-numbered one in the same session shows out of
|
|
28
|
+
* order (tracker-contract.md, "TaskCreate ordering"); the hook says so to the
|
|
29
|
+
* model as additionalContext. Which tiles this session created is kept in
|
|
30
|
+
* `tile-session.json` beside the tracker and starts over with a new session,
|
|
31
|
+
* so tiles a resume recreates are not compared with the old session's.
|
|
32
|
+
*
|
|
33
|
+
* Exits 0 on every path, blocks nothing, and prints only that ordering note. A
|
|
34
|
+
* failure is written to the hook failure log.
|
|
26
35
|
*/
|
|
27
36
|
|
|
28
37
|
import { spawnSync } from "node:child_process";
|
|
29
|
-
import { readFileSync } from "node:fs";
|
|
38
|
+
import { readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
30
39
|
import { dirname, join } from "node:path";
|
|
31
40
|
import { fileURLToPath } from "node:url";
|
|
32
41
|
|
|
@@ -37,6 +46,7 @@ const HOOK = "task-mirror";
|
|
|
37
46
|
const SESSION_BOUND = new Set(["session", "host-session"]);
|
|
38
47
|
const STATUSES = new Set(["pending", "in_progress", "completed"]);
|
|
39
48
|
const TILE = /^Phase \d+\b/;
|
|
49
|
+
const TILE_NO = /^Phase (\d+)\b/;
|
|
40
50
|
const MAX_INPUT = 256 * 1024;
|
|
41
51
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
42
52
|
|
|
@@ -102,6 +112,43 @@ export function mirrorArgs(payload, tracker) {
|
|
|
102
112
|
return null;
|
|
103
113
|
}
|
|
104
114
|
|
|
115
|
+
/**
|
|
116
|
+
* For a phase tile's TaskCreate: the phase it is, the id it got, and the
|
|
117
|
+
* higher-numbered phases whose tiles already exist. Null for anything else.
|
|
118
|
+
*/
|
|
119
|
+
export function tileOf(payload, tracker) {
|
|
120
|
+
if (payload?.tool_name !== "TaskCreate") return null;
|
|
121
|
+
const m = TILE_NO.exec(String(payload.tool_input?.subject ?? "").trim());
|
|
122
|
+
if (!m) return null;
|
|
123
|
+
const n = Number(m[1]);
|
|
124
|
+
const phases = Array.isArray(tracker?.phases) ? tracker.phases : [];
|
|
125
|
+
const phase = phases.find((p) => Number(p.id) === n) ?? null;
|
|
126
|
+
return {
|
|
127
|
+
phase,
|
|
128
|
+
id: createdId(payload.tool_response),
|
|
129
|
+
recorded: phase ? String(phase.meta?.tasklist_id ?? "") : "",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* This session's tile record after creating phase `n`, and the phases whose
|
|
135
|
+
* tiles this session created before it with a higher number.
|
|
136
|
+
*/
|
|
137
|
+
export function sessionTiles(previous, sessionId, n) {
|
|
138
|
+
if (!sessionId) return { record: previous, after: [] };
|
|
139
|
+
const same =
|
|
140
|
+
previous &&
|
|
141
|
+
previous.session === sessionId &&
|
|
142
|
+
previous.tiles &&
|
|
143
|
+
typeof previous.tiles === "object";
|
|
144
|
+
const tiles = same ? { ...previous.tiles } : {};
|
|
145
|
+
const after = Object.keys(tiles)
|
|
146
|
+
.filter((p) => Number(p) > n)
|
|
147
|
+
.sort((a, b) => Number(a) - Number(b));
|
|
148
|
+
tiles[String(n)] = true;
|
|
149
|
+
return { record: { session: sessionId, tiles }, after };
|
|
150
|
+
}
|
|
151
|
+
|
|
105
152
|
export function mirror(payload, env = process.env) {
|
|
106
153
|
if (!payload || !["TaskCreate", "TaskUpdate"].includes(payload.tool_name)) return null;
|
|
107
154
|
const run = findActiveRun({
|
|
@@ -119,26 +166,70 @@ export function mirror(payload, env = process.env) {
|
|
|
119
166
|
} catch {
|
|
120
167
|
return null;
|
|
121
168
|
}
|
|
169
|
+
const tracked = (args) => {
|
|
170
|
+
const r = spawnSync("bash", [join(HERE, "phase-tracker.sh"), ...args], {
|
|
171
|
+
env: {
|
|
172
|
+
...env,
|
|
173
|
+
MULTI_AGENT_TASK_ID: run.state.taskId,
|
|
174
|
+
TRACKER_FILE: trackerPath,
|
|
175
|
+
TRACKER_QUIET: "1",
|
|
176
|
+
},
|
|
177
|
+
encoding: "utf8",
|
|
178
|
+
timeout: 8000,
|
|
179
|
+
});
|
|
180
|
+
if (r.status !== 0) logHookFailure(HOOK, `phase-tracker ${args[0]} exited ${r.status}`, env);
|
|
181
|
+
};
|
|
182
|
+
const tile = tileOf(payload, tracker);
|
|
183
|
+
if (tile) {
|
|
184
|
+
if (!tile.phase) return null;
|
|
185
|
+
if (tile.id && tile.recorded !== tile.id)
|
|
186
|
+
tracked(["meta", String(tile.phase.id), "tasklist_id", tile.id]);
|
|
187
|
+
const sidecar = join(runDirOf(run.statePath), "tile-session.json");
|
|
188
|
+
let previous;
|
|
189
|
+
try {
|
|
190
|
+
previous = JSON.parse(readFileSync(sidecar, "utf8"));
|
|
191
|
+
} catch {
|
|
192
|
+
previous = null;
|
|
193
|
+
}
|
|
194
|
+
const { record, after } = sessionTiles(
|
|
195
|
+
previous,
|
|
196
|
+
String(payload.session_id ?? ""),
|
|
197
|
+
Number(tile.phase.id),
|
|
198
|
+
);
|
|
199
|
+
if (record)
|
|
200
|
+
try {
|
|
201
|
+
const tmp = `${sidecar}.${process.pid}.tmp`;
|
|
202
|
+
writeFileSync(tmp, JSON.stringify(record));
|
|
203
|
+
renameSync(tmp, sidecar);
|
|
204
|
+
} catch (e) {
|
|
205
|
+
logHookFailure(HOOK, `tile record: ${String(e?.message || e).slice(0, 120)}`, env);
|
|
206
|
+
}
|
|
207
|
+
return after.length
|
|
208
|
+
? {
|
|
209
|
+
note:
|
|
210
|
+
`Phase ${tile.phase.id}'s tile was created after the tiles of phase ${after.join(", ")}, ` +
|
|
211
|
+
"so the task widget shows it out of order. Tiles are created in phase-number order before any " +
|
|
212
|
+
'TaskUpdate (tracker-contract.md, "TaskCreate ordering").',
|
|
213
|
+
}
|
|
214
|
+
: null;
|
|
215
|
+
}
|
|
122
216
|
const args = mirrorArgs(payload, tracker);
|
|
123
217
|
if (!args) return null;
|
|
124
|
-
|
|
125
|
-
env: {
|
|
126
|
-
...env,
|
|
127
|
-
MULTI_AGENT_TASK_ID: run.state.taskId,
|
|
128
|
-
TRACKER_FILE: trackerPath,
|
|
129
|
-
TRACKER_QUIET: "1",
|
|
130
|
-
},
|
|
131
|
-
encoding: "utf8",
|
|
132
|
-
timeout: 8000,
|
|
133
|
-
});
|
|
134
|
-
if (r.status !== 0) logHookFailure(HOOK, `phase-tracker task exited ${r.status}`, env);
|
|
218
|
+
tracked(args);
|
|
135
219
|
return args;
|
|
136
220
|
}
|
|
137
221
|
|
|
138
222
|
if (invokedDirectly(import.meta.url)) {
|
|
139
223
|
try {
|
|
140
224
|
const raw = readFileSync(0, "utf8").slice(0, MAX_INPUT);
|
|
141
|
-
mirror(JSON.parse(raw));
|
|
225
|
+
const out = mirror(JSON.parse(raw));
|
|
226
|
+
if (out?.note) {
|
|
227
|
+
process.stdout.write(
|
|
228
|
+
JSON.stringify({
|
|
229
|
+
hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: out.note },
|
|
230
|
+
}) + "\n",
|
|
231
|
+
);
|
|
232
|
+
}
|
|
142
233
|
} catch (e) {
|
|
143
234
|
try {
|
|
144
235
|
logHookFailure(HOOK, String(e?.message || e).slice(0, 200));
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
# logic is in task-mirror.mjs; this wrapper exists because every hook the
|
|
7
7
|
# pipeline registers is a `bash "$HOME/.<host>/scripts/<name>"` command.
|
|
8
8
|
#
|
|
9
|
-
# Exits 0 on every path
|
|
10
|
-
# error means no mirror, never a blocked
|
|
9
|
+
# Exits 0 on every path; its only output is a tile-ordering note for the
|
|
10
|
+
# model. A missing node or any internal error means no mirror, never a blocked
|
|
11
|
+
# tool call.
|
|
11
12
|
|
|
12
13
|
set -u
|
|
13
14
|
|