@mmerterden/multi-agent-pipeline 20.33.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 +98 -1
- package/README.md +3 -1
- package/README.tr.md +2 -1
- package/docs/adr/0018-cross-repo-links-not-a-global-graph.md +43 -0
- package/docs/adr/README.md +2 -1
- package/docs/facts.json +5 -4
- package/docs/features.md +1 -1
- package/install/_dev-only-files.mjs +1 -0
- package/install/catalog-history.json +1 -1
- package/install/claude.mjs +24 -2
- package/install/templates/claude-hooks.json +34 -1
- package/manifest.json +393 -339
- package/package.json +3 -1
- package/pipeline/agents/bulk-reader.md +3 -0
- package/pipeline/agents/code-reviewer.md +9 -0
- package/pipeline/agents/explorer.md +4 -0
- package/pipeline/agents/plan-critic.md +4 -2
- package/pipeline/commands/multi-agent/SKILL.md +17 -136
- package/pipeline/commands/multi-agent/channels/SKILL.md +16 -298
- package/pipeline/commands/multi-agent/graph/SKILL.md +5 -2
- package/pipeline/commands/multi-agent/language/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/office-on/SKILL.md +7 -3
- package/pipeline/commands/multi-agent/office-status/SKILL.md +3 -2
- package/pipeline/commands/multi-agent/resume/SKILL.md +8 -22
- package/pipeline/commands/multi-agent/review/SKILL.md +7 -131
- package/pipeline/commands/multi-agent/setup/SKILL.md +19 -224
- package/pipeline/contract/CHANGELOG.md +9 -0
- package/pipeline/contract/README.md +6 -2
- 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/generator.md +146 -0
- package/pipeline/multi-agent-refs/channels/issue-comment.md +1 -1
- package/pipeline/multi-agent-refs/channels/menu.md +55 -0
- package/pipeline/multi-agent-refs/channels/multi-repo.md +6 -0
- package/pipeline/multi-agent-refs/channels/preview.md +25 -0
- package/pipeline/multi-agent-refs/channels/reference.md +98 -0
- package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +1 -1
- package/pipeline/multi-agent-refs/features/code-graph.md +27 -0
- package/pipeline/multi-agent-refs/features/office-remote-protocol.md +166 -0
- package/pipeline/multi-agent-refs/features/office-threat-model.md +2 -2
- package/pipeline/multi-agent-refs/features/plan-critic.md +5 -2
- package/pipeline/multi-agent-refs/features/review-delta.md +4 -1
- package/pipeline/multi-agent-refs/features/scope-check.md +25 -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/knowledge.md +9 -0
- package/pipeline/multi-agent-refs/orchestrator/dispatcher.md +163 -0
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +6 -2
- package/pipeline/multi-agent-refs/phases/phase-1-plan.md +8 -10
- package/pipeline/multi-agent-refs/phases/phase-2-dev.md +18 -20
- package/pipeline/multi-agent-refs/phases/phase-3-review.md +20 -14
- package/pipeline/multi-agent-refs/phases/phase-4-commit.md +8 -7
- package/pipeline/multi-agent-refs/phases/phase-5-report.md +6 -3
- package/pipeline/multi-agent-refs/picker-contract.md +1 -1
- package/pipeline/multi-agent-refs/resume/details.md +35 -0
- package/pipeline/multi-agent-refs/review/inputs.md +87 -0
- package/pipeline/multi-agent-refs/review/module-guides.md +25 -0
- package/pipeline/multi-agent-refs/review/valven.md +42 -0
- package/pipeline/multi-agent-refs/rules.md +3 -2
- package/pipeline/multi-agent-refs/setup/accounts.md +27 -0
- package/pipeline/multi-agent-refs/setup/app-store-connect.md +5 -0
- package/pipeline/multi-agent-refs/setup/firebase.md +5 -0
- package/pipeline/multi-agent-refs/setup/google-play.md +4 -0
- package/pipeline/multi-agent-refs/setup/hooks-and-plugins.md +21 -0
- package/pipeline/multi-agent-refs/setup/language.md +46 -0
- package/pipeline/multi-agent-refs/setup/multi-repo-group.md +51 -0
- package/pipeline/multi-agent-refs/setup/summaries.md +63 -0
- package/pipeline/multi-agent-refs/setup/token-save-flow.md +51 -0
- 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 +304 -55
- package/pipeline/office/toolkit.json +1 -1
- package/pipeline/rules/response-shape.md +6 -7
- package/pipeline/schemas/agent-state.schema.json +36 -0
- package/pipeline/schemas/phases.json +4 -4
- package/pipeline/schemas/plan-critique.schema.json +9 -2
- package/pipeline/schemas/planning-output.schema.json +43 -2
- package/pipeline/schemas/pr-reach.schema.json +55 -0
- package/pipeline/schemas/prefs.schema.json +37 -8
- package/pipeline/schemas/reviewer-output.schema.json +20 -2
- package/pipeline/schemas/token-budget.json +4 -4
- package/pipeline/schemas/triage-output.schema.json +32 -3
- package/pipeline/schemas/unattended-policy.json +2 -1
- package/pipeline/scripts/_graph-analysis.mjs +270 -0
- package/pipeline/scripts/_guard_rules.py +449 -1
- package/pipeline/scripts/_memory-policy.mjs +165 -0
- package/pipeline/scripts/_secret-scan.mjs +105 -0
- package/pipeline/scripts/_test-gaming.mjs +36 -0
- package/pipeline/scripts/_unified-diff.mjs +2 -2
- 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/capture-flush.sh +12 -2
- package/pipeline/scripts/check-read-size.sh +9 -0
- package/pipeline/scripts/code-graph-rules/android.json +8 -3
- package/pipeline/scripts/contract-server.mjs +71 -16
- package/pipeline/scripts/debt-ledger.mjs +323 -0
- package/pipeline/scripts/file-context.mjs +261 -0
- package/pipeline/scripts/file-context.sh +20 -0
- package/pipeline/scripts/gate-ledger.mjs +144 -21
- package/pipeline/scripts/graph-links.mjs +230 -0
- package/pipeline/scripts/graph-query.mjs +133 -1
- package/pipeline/scripts/graph-report.mjs +65 -0
- package/pipeline/scripts/learn-from-transcripts.mjs +12 -48
- package/pipeline/scripts/learnings-ledger.mjs +143 -24
- 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/plan-critique-gate.mjs +11 -2
- 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/rework-rulings.mjs +106 -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 +213 -2
- 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/triage-memory.mjs +64 -37
- package/pipeline/scripts/update-check.sh +7 -1
- package/pipeline/scripts/update-issue-progress.sh +73 -8
- package/pipeline/scripts/validate-planning.mjs +172 -4
- package/pipeline/scripts/validate-reviewer.mjs +20 -0
- package/pipeline/scripts/validate-triage.mjs +149 -4
- 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 +9 -9
- 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-graph/SKILL.md +5 -2
- package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +4 -4
- package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +2 -0
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +4 -4
- 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
|
@@ -11,6 +11,17 @@
|
|
|
11
11
|
// Usage:
|
|
12
12
|
// node pipeline/scripts/validate-triage.mjs path/to/triage.json
|
|
13
13
|
// cat triage.json | node pipeline/scripts/validate-triage.mjs -
|
|
14
|
+
// [--reviewers a.json,b.json] # every reviewer cannotVerify row needs a triage row
|
|
15
|
+
// [--reviewers-dir <dir>] # same, for every reviewer-*.json in <dir>
|
|
16
|
+
// [--anonymized] # the verdict was judged on anonymized input:
|
|
17
|
+
// # no finding may carry `reviewer`, labels only
|
|
18
|
+
//
|
|
19
|
+
// An accepted finding names its source as `reviewer` (a model family),
|
|
20
|
+
// `foundBy` (an anonymized "Source A" label, or "security-auditor") or `tag`
|
|
21
|
+
// (a deterministic gate finding). Triage judges anonymized findings and
|
|
22
|
+
// carries `foundBy` through; anonymize-findings.mjs --restore writes the
|
|
23
|
+
// family back afterwards, so a `reviewer` in the verdict itself means the
|
|
24
|
+
// judge was told whose findings it graded.
|
|
14
25
|
//
|
|
15
26
|
// Exit codes:
|
|
16
27
|
// 0 - valid, safe to act on
|
|
@@ -21,9 +32,12 @@
|
|
|
21
32
|
// Output: JSON result to stdout with shape:
|
|
22
33
|
// {"ok": bool, "code": N, "errors": [...], "warnings": [...], "corrected": {...}|null}
|
|
23
34
|
|
|
24
|
-
import { readFileSync } from "node:fs";
|
|
35
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
36
|
+
import { join } from "node:path";
|
|
25
37
|
|
|
26
38
|
const ALLOWED_REVIEWERS = new Set(["fable", "opus", "sonnet", "gpt"]);
|
|
39
|
+
const FOUND_BY_RE = /^(Source [A-Z]|security-auditor)$/;
|
|
40
|
+
const ANONYMIZED = process.argv.includes("--anonymized");
|
|
27
41
|
const ALLOWED_SEVERITIES = new Set(["blocking", "important", "suggestion"]);
|
|
28
42
|
const ALLOWED_CONSENSUS_VERDICTS = new Set([
|
|
29
43
|
"unanimous-pass",
|
|
@@ -35,9 +49,72 @@ const ALLOWED_VERIFICATION_RESULTS = new Set(["confirmed", "not-reproduced", "in
|
|
|
35
49
|
const FINGERPRINT_RE = /^F:[0-9a-f]{8}$/;
|
|
36
50
|
const OVER_REJECT_THRESHOLD = 0.8;
|
|
37
51
|
const OVER_REJECT_MIN_FINDINGS = 5;
|
|
52
|
+
const CANNOT_VERIFY_RESOLUTIONS = new Set(["verified", "finding", "out-of-scope"]);
|
|
53
|
+
// A path (any run without spaces, quotes or parentheses that holds a letter),
|
|
54
|
+
// then :line. A time of day has no letter, so it is not one.
|
|
55
|
+
const FILE_LINE = /(?:^|[\s(`'"[])([^\s:`'"()]*\p{L}[^\s:`'"()]*):(\d+)\b/gu;
|
|
56
|
+
const fileLines = (text) => [...String(text).matchAll(FILE_LINE)].map((m) => `${m[1]}:${m[2]}`);
|
|
57
|
+
const PATH_EDGE = /[^\s(`'",;]/u;
|
|
58
|
+
/** True when `text` names `at` (file:line) as a whole token, not inside a longer path. */
|
|
59
|
+
function names(text, at) {
|
|
60
|
+
const t = String(text);
|
|
61
|
+
for (let i = t.indexOf(at); i !== -1; i = t.indexOf(at, i + 1)) {
|
|
62
|
+
const before = i === 0 ? "" : t[i - 1];
|
|
63
|
+
const after = t[i + at.length] ?? "";
|
|
64
|
+
if ((!before || !PATH_EDGE.test(before)) && !/\d/.test(after)) return true;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
const normRequirement = (s) => String(s).toLowerCase().replace(/\s+/g, " ").trim();
|
|
69
|
+
|
|
70
|
+
/** Reviewer files named by --reviewers, or found by --reviewers-dir; null when neither is given. */
|
|
71
|
+
function reviewerFiles() {
|
|
72
|
+
const listAt = process.argv.indexOf("--reviewers");
|
|
73
|
+
const dirAt = process.argv.indexOf("--reviewers-dir");
|
|
74
|
+
if (listAt === -1 && dirAt === -1) return null;
|
|
75
|
+
const files = [];
|
|
76
|
+
if (listAt !== -1) {
|
|
77
|
+
const value = process.argv[listAt + 1];
|
|
78
|
+
if (!value || value.startsWith("--"))
|
|
79
|
+
throw new Error("--reviewers needs a comma-separated list");
|
|
80
|
+
files.push(...value.split(",").filter(Boolean));
|
|
81
|
+
}
|
|
82
|
+
if (dirAt !== -1) {
|
|
83
|
+
const dir = process.argv[dirAt + 1];
|
|
84
|
+
if (!dir || dir.startsWith("--")) throw new Error("--reviewers-dir needs a folder");
|
|
85
|
+
for (const name of readdirSync(dir).sort()) {
|
|
86
|
+
if (/^reviewer-.*\.json$/.test(name)) files.push(join(dir, name));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return files;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Requirements the reviewers could not verify from the diff, from their files. */
|
|
93
|
+
function reviewerRequirements() {
|
|
94
|
+
const files = reviewerFiles();
|
|
95
|
+
if (files === null) return null;
|
|
96
|
+
const out = new Set();
|
|
97
|
+
for (const p of files) {
|
|
98
|
+
const doc = JSON.parse(readFileSync(p, "utf-8"));
|
|
99
|
+
for (const row of doc?.cannotVerify ?? []) {
|
|
100
|
+
if (typeof row?.requirement === "string") out.add(normRequirement(row.requirement));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** The input path: the first argument that is neither a flag nor a flag's value. */
|
|
107
|
+
function inputArg() {
|
|
108
|
+
const args = process.argv.slice(2);
|
|
109
|
+
for (let i = 0; i < args.length; i++) {
|
|
110
|
+
if (args[i] === "--reviewers" || args[i] === "--reviewers-dir") i++;
|
|
111
|
+
else if (!args[i].startsWith("--")) return args[i];
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
38
115
|
|
|
39
116
|
function readInput() {
|
|
40
|
-
const arg =
|
|
117
|
+
const arg = inputArg();
|
|
41
118
|
if (!arg) {
|
|
42
119
|
console.error("error: provide a path or '-' for STDIN");
|
|
43
120
|
process.exit(64); // EX_USAGE
|
|
@@ -108,11 +185,33 @@ function validateVerification(v, label, errors) {
|
|
|
108
185
|
}
|
|
109
186
|
}
|
|
110
187
|
|
|
188
|
+
/** The finding's source kind, or null when none is valid. */
|
|
189
|
+
function sourceOf(f) {
|
|
190
|
+
if (f?.foundBy !== undefined) return FOUND_BY_RE.test(String(f.foundBy)) ? "label" : null;
|
|
191
|
+
if (typeof f?.tag === "string" && f.tag) return "tag";
|
|
192
|
+
return ALLOWED_REVIEWERS.has(f?.reviewer) ? "reviewer" : null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** With --anonymized: every finding that names a reviewer, by list and index. */
|
|
196
|
+
function identityLeaks(parsed) {
|
|
197
|
+
const out = [];
|
|
198
|
+
(parsed.accepted || []).forEach((f, i) => {
|
|
199
|
+
if (f && Object.hasOwn(f, "reviewer")) out.push(`accepted[${i}]`);
|
|
200
|
+
});
|
|
201
|
+
for (const key of ["deferred", "rejected"]) {
|
|
202
|
+
(parsed[key] || []).forEach((e, i) => {
|
|
203
|
+
if (e?.finding && Object.hasOwn(e.finding, "reviewer")) out.push(`${key}[${i}].finding`);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return out;
|
|
207
|
+
}
|
|
208
|
+
|
|
111
209
|
function validateAccepted(f, i, errors) {
|
|
112
210
|
validateRawFinding(f, `accepted[${i}]`, errors);
|
|
113
|
-
|
|
211
|
+
const source = sourceOf(f);
|
|
212
|
+
if (source === null) {
|
|
114
213
|
errors.push(
|
|
115
|
-
`accepted[${i}]:
|
|
214
|
+
`accepted[${i}]: name the source as foundBy ("Source A".."Source Z" or "security-auditor"), tag, or reviewer fable|opus|sonnet|gpt (got reviewer ${JSON.stringify(f?.reviewer)}, foundBy ${JSON.stringify(f?.foundBy)})`,
|
|
116
215
|
);
|
|
117
216
|
}
|
|
118
217
|
if (typeof f.fix !== "string" || f.fix.length < 4) {
|
|
@@ -213,6 +312,52 @@ function validate(raw) {
|
|
|
213
312
|
parsed.accepted.forEach((f, i) => validateAccepted(f, i, errors));
|
|
214
313
|
parsed.deferred.forEach((f, i) => validateClassified(f, "deferred", i, errors));
|
|
215
314
|
parsed.rejected.forEach((f, i) => validateClassified(f, "rejected", i, errors));
|
|
315
|
+
if (ANONYMIZED) {
|
|
316
|
+
for (const at of identityLeaks(parsed)) {
|
|
317
|
+
errors.push(
|
|
318
|
+
`${at}: carries reviewer in an anonymized verdict; carry foundBy through, the family is restored after validation`,
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Every requirement a reviewer could not verify from the diff is settled here:
|
|
324
|
+
// checked in the unchanged code (verified), turned into a finding, or ruled
|
|
325
|
+
// out of scope, each with its evidence.
|
|
326
|
+
if (parsed.cannotVerify !== undefined && !Array.isArray(parsed.cannotVerify)) {
|
|
327
|
+
errors.push("cannotVerify must be an array when present");
|
|
328
|
+
}
|
|
329
|
+
const settled = new Set();
|
|
330
|
+
const list = (x) => (Array.isArray(x) ? x : []);
|
|
331
|
+
const kept = [...list(parsed.accepted), ...list(parsed.deferred).map((d) => d?.finding)]
|
|
332
|
+
.filter((f) => typeof f?.file === "string" && Number.isInteger(f?.line))
|
|
333
|
+
.map((f) => `${f.file}:${f.line}`);
|
|
334
|
+
(Array.isArray(parsed.cannotVerify) ? parsed.cannotVerify : []).forEach((row, i) => {
|
|
335
|
+
const label = `cannotVerify[${i}]`;
|
|
336
|
+
if (typeof row?.requirement !== "string" || row.requirement.trim().length < 4) {
|
|
337
|
+
errors.push(`${label}: requirement must be a string >= 4 chars`);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
if (!CANNOT_VERIFY_RESOLUTIONS.has(row.resolution)) {
|
|
341
|
+
errors.push(`${label}: resolution must be verified|finding|out-of-scope`);
|
|
342
|
+
}
|
|
343
|
+
if (typeof row.evidence !== "string" || row.evidence.trim().length < 4) {
|
|
344
|
+
errors.push(`${label}: evidence must say how it was settled`);
|
|
345
|
+
} else if (row.resolution === "verified" && !fileLines(row.evidence).length) {
|
|
346
|
+
errors.push(`${label}: a verified requirement cites the file:line it read`);
|
|
347
|
+
} else if (row.resolution === "finding" && !kept.some((at) => names(row.evidence, at))) {
|
|
348
|
+
errors.push(`${label}: evidence names no accepted or deferred finding (cite its file:line)`);
|
|
349
|
+
}
|
|
350
|
+
settled.add(normRequirement(row.requirement));
|
|
351
|
+
});
|
|
352
|
+
let required;
|
|
353
|
+
try {
|
|
354
|
+
required = reviewerRequirements();
|
|
355
|
+
} catch (err) {
|
|
356
|
+
errors.push(`--reviewers unreadable: ${err.message}`);
|
|
357
|
+
}
|
|
358
|
+
for (const req of required ?? []) {
|
|
359
|
+
if (!settled.has(req)) errors.push(`cannotVerify: reviewer requirement not settled: "${req}"`);
|
|
360
|
+
}
|
|
216
361
|
|
|
217
362
|
// Optional consensus block (v3.1.0) + cross-check against the actual verdict
|
|
218
363
|
// so the block is not merely decorative.
|
|
@@ -7,6 +7,13 @@
|
|
|
7
7
|
* /multi-agent:jira, /multi-agent:issue and Phase 0 run;
|
|
8
8
|
* title, type, status, score, blockers, warnings, summary
|
|
9
9
|
* and a description excerpt, scrubbed
|
|
10
|
+
* reach how far each open GitHub pull request reaches: its
|
|
11
|
+
* changed files (`gh pr view --json files`) looked up in
|
|
12
|
+
* the repository's own code graph, and the files that
|
|
13
|
+
* depend on them counted (graph-affected, depth 2). A
|
|
14
|
+
* repository without a graph, a Bitbucket pull request or
|
|
15
|
+
* a name that is not owner/repo is listed with its reason
|
|
16
|
+
* and costs no gh call
|
|
10
17
|
* prs open pull requests on GitHub where you are asked to
|
|
11
18
|
* review or are the author (`gh search prs`, the queries
|
|
12
19
|
* /multi-agent:review lists). Bitbucket is reported as a
|
|
@@ -34,6 +41,7 @@
|
|
|
34
41
|
* Usage:
|
|
35
42
|
* work-items.mjs maturity <key>
|
|
36
43
|
* work-items.mjs prs
|
|
44
|
+
* work-items.mjs reach
|
|
37
45
|
* work-items.mjs verdict <task-id> [--post]
|
|
38
46
|
*
|
|
39
47
|
* MA_GH_BIN stands in for gh, MA_ISSUE_FETCHER and MA_POST_PR_REVIEW for the
|
|
@@ -57,6 +65,8 @@ import { runMain } from "../lib/fatal.mjs";
|
|
|
57
65
|
import { invokedDirectly } from "../lib/invoked-directly.mjs";
|
|
58
66
|
import { resolveTokenAsync } from "../lib/credential-resolve.mjs";
|
|
59
67
|
import { homedir } from "node:os";
|
|
68
|
+
import { affected } from "./graph-affected.mjs";
|
|
69
|
+
import { loadGraph } from "./graph-query.mjs";
|
|
60
70
|
|
|
61
71
|
export const CONTRACT_VERSION = "1.1.0";
|
|
62
72
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
@@ -349,6 +359,113 @@ export async function pullRequests({ env = process.env, redact = false } = {}) {
|
|
|
349
359
|
return { contractVersion: CONTRACT_VERSION, items, sources };
|
|
350
360
|
}
|
|
351
361
|
|
|
362
|
+
const REACH_MAX = 20;
|
|
363
|
+
const REACH_PARALLEL = 3;
|
|
364
|
+
const REACH_DEPTH = 2;
|
|
365
|
+
const FILES_MAX = 300;
|
|
366
|
+
const NAME_PART = /^(?!\.{1,2}$)[A-Za-z0-9._-]{1,100}$/;
|
|
367
|
+
|
|
368
|
+
/** owner/repo with two plain parts; anything else never reaches gh. */
|
|
369
|
+
function plainRepo(repo) {
|
|
370
|
+
const parts = String(repo).split("/");
|
|
371
|
+
return parts.length === 2 && parts.every((x) => NAME_PART.test(x));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async function inBatches(items, size, fn) {
|
|
375
|
+
const out = [];
|
|
376
|
+
for (let i = 0; i < items.length; i += size) {
|
|
377
|
+
out.push(...(await Promise.all(items.slice(i, i + size).map(fn))));
|
|
378
|
+
}
|
|
379
|
+
return out;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** The changed file paths of one GitHub pull request, or null when gh could not say. */
|
|
383
|
+
async function changedFiles(env, repo, number) {
|
|
384
|
+
const gh = env.MA_GH_BIN || "gh";
|
|
385
|
+
const r = await spawnAsync(
|
|
386
|
+
gh,
|
|
387
|
+
["pr", "view", String(number), "--repo", repo, "--json", "files"],
|
|
388
|
+
{
|
|
389
|
+
env,
|
|
390
|
+
timeoutMs: TIMEOUT_MS,
|
|
391
|
+
},
|
|
392
|
+
);
|
|
393
|
+
if (r.error || r.status !== 0) return null;
|
|
394
|
+
try {
|
|
395
|
+
const files = JSON.parse(r.stdout)?.files;
|
|
396
|
+
return (Array.isArray(files) ? files : [])
|
|
397
|
+
.map((f) => f?.path)
|
|
398
|
+
.filter((path) => typeof path === "string" && path.length <= 1024)
|
|
399
|
+
.slice(0, FILES_MAX);
|
|
400
|
+
} catch {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* How far each open pull request reaches, widest first. Changed paths are
|
|
407
|
+
* used only as keys into the local graph: nothing is read from disk by them
|
|
408
|
+
* and none is echoed back, only counts.
|
|
409
|
+
*/
|
|
410
|
+
export async function prReach({ env = process.env, list = null } = {}) {
|
|
411
|
+
const prs = list ?? (await pullRequests({ env }));
|
|
412
|
+
const envByLogin = new Map();
|
|
413
|
+
for (const a of await ghAccounts(env)) {
|
|
414
|
+
const aEnv = await accountEnv(env, a);
|
|
415
|
+
if (aEnv) envByLogin.set(a.login, aEnv);
|
|
416
|
+
}
|
|
417
|
+
const knowledge = join(env.HOME || homedir(), ".claude", "knowledge");
|
|
418
|
+
const graphs = new Map();
|
|
419
|
+
const graphFor = (project) => {
|
|
420
|
+
if (!graphs.has(project)) {
|
|
421
|
+
const path = join(knowledge, project, "code-graph.json");
|
|
422
|
+
let graph;
|
|
423
|
+
try {
|
|
424
|
+
graph = existsSync(path) ? loadGraph(path) : null;
|
|
425
|
+
} catch {
|
|
426
|
+
graph = null;
|
|
427
|
+
}
|
|
428
|
+
graphs.set(project, graph);
|
|
429
|
+
}
|
|
430
|
+
return graphs.get(project);
|
|
431
|
+
};
|
|
432
|
+
const candidates = prs.items
|
|
433
|
+
.filter((p) => p.source !== "github" || plainRepo(p.repo))
|
|
434
|
+
.slice(0, REACH_MAX);
|
|
435
|
+
const rows = await inBatches(candidates, REACH_PARALLEL, async (p) => {
|
|
436
|
+
const row = { source: p.source, repo: p.repo, number: p.number, project: null };
|
|
437
|
+
if (p.source !== "github") return { ...row, reach: null, reason: "not measured off GitHub" };
|
|
438
|
+
const project = p.repo.split("/")[1];
|
|
439
|
+
const graph = graphFor(project);
|
|
440
|
+
if (!graph) return { ...row, reach: null, reason: `no code graph for ${project}` };
|
|
441
|
+
const files = await changedFiles(envByLogin.get(p.account) ?? env, p.repo, p.number);
|
|
442
|
+
if (!files) return { ...row, project, reach: null, reason: "gh could not list its files" };
|
|
443
|
+
const ids = new Set(graph.nodes.filter((n) => n.kind === "file").map((n) => n.id));
|
|
444
|
+
const seeds = files.map((f) => `file:${f}`).filter((id) => ids.has(id));
|
|
445
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
446
|
+
const dependents = seeds.length
|
|
447
|
+
? affected({ graph, seeds: seeds.map((id) => byId.get(id)), depth: REACH_DEPTH })
|
|
448
|
+
: [];
|
|
449
|
+
const reachedFiles = new Set(
|
|
450
|
+
dependents.filter((d) => d.node?.kind === "file").map((d) => d.id),
|
|
451
|
+
);
|
|
452
|
+
return {
|
|
453
|
+
...row,
|
|
454
|
+
project,
|
|
455
|
+
changed: files.length,
|
|
456
|
+
inGraph: seeds.length,
|
|
457
|
+
reach: reachedFiles.size,
|
|
458
|
+
};
|
|
459
|
+
});
|
|
460
|
+
const updated = new Map(prs.items.map((p) => [`${p.repo}#${p.number}`, p.updatedAt ?? ""]));
|
|
461
|
+
rows.sort(
|
|
462
|
+
(a, b) =>
|
|
463
|
+
(b.reach ?? -1) - (a.reach ?? -1) ||
|
|
464
|
+
(updated.get(`${a.repo}#${a.number}`) < updated.get(`${b.repo}#${b.number}`) ? 1 : -1),
|
|
465
|
+
);
|
|
466
|
+
return { contractVersion: CONTRACT_VERSION, depth: REACH_DEPTH, items: rows };
|
|
467
|
+
}
|
|
468
|
+
|
|
352
469
|
function stateOf(taskId) {
|
|
353
470
|
const { found, records } = selectRuns({ taskId });
|
|
354
471
|
if (!found || !records.length) throw new WorkError("no-run", "no such run");
|
|
@@ -449,11 +566,12 @@ async function main(argv) {
|
|
|
449
566
|
let out;
|
|
450
567
|
if (cmd === "maturity" && arg) out = await issueMaturity(arg);
|
|
451
568
|
else if (cmd === "prs" && !arg) out = await pullRequests();
|
|
569
|
+
else if (cmd === "reach" && !arg) out = await prReach();
|
|
452
570
|
else if (cmd === "verdict" && arg && (flag === undefined || flag === "--post")) {
|
|
453
571
|
out = flag ? await postReviewVerdict(arg) : reviewVerdictPreview(arg);
|
|
454
572
|
} else {
|
|
455
573
|
process.stderr.write(
|
|
456
|
-
"usage: work-items.mjs maturity <key> | prs | verdict <task-id> [--post]\n",
|
|
574
|
+
"usage: work-items.mjs maturity <key> | prs | reach | verdict <task-id> [--post]\n",
|
|
457
575
|
);
|
|
458
576
|
return 2;
|
|
459
577
|
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @file worktree-prepare-all.mjs - the multi-repo worktree setup as one
|
|
4
|
+
* step: every repo gets its worktree, or none keeps one this step created.
|
|
5
|
+
*
|
|
6
|
+
* Phase 0 creates one worktree per repo of a multi-repo task. When the third
|
|
7
|
+
* repo's add fails, the first two must not stay behind on a half-made task.
|
|
8
|
+
* This runs worktree-prepare.sh serially in the given order and, on the first
|
|
9
|
+
* failure, rolls back what this call created, newest first:
|
|
10
|
+
*
|
|
11
|
+
* - the worktree is removed without --force, so a worktree holding changes
|
|
12
|
+
* is kept and reported instead of discarded;
|
|
13
|
+
* - the branch is deleted only when worktree-prepare created it here
|
|
14
|
+
* (newBranch) and it still points at origin/<base>, so a branch that
|
|
15
|
+
* existed before, or gained a commit, is never deleted.
|
|
16
|
+
*
|
|
17
|
+
* A worktree that was reused (it existed before the call) is never touched.
|
|
18
|
+
*
|
|
19
|
+
* Usage:
|
|
20
|
+
* worktree-prepare-all.mjs --repos <repos.json>
|
|
21
|
+
* repos.json: [{project, path, branch, base, name?, email?}, ...]
|
|
22
|
+
*
|
|
23
|
+
* Output (stdout, JSON): {ok, results[], failed, rolledBack[], kept[]}
|
|
24
|
+
* results each repo's worktree-prepare.sh output, in order, up to the failure
|
|
25
|
+
* failed the failing repo's output, or null
|
|
26
|
+
* rolledBack [{project, worktreePath, branchDeleted}]
|
|
27
|
+
* kept [{project, worktreePath, reason}] what the rollback left in place
|
|
28
|
+
*
|
|
29
|
+
* Exit: 0 every repo ready, 1 a repo failed (rollback done; see kept[]),
|
|
30
|
+
* 2 usage or a repos file that does not parse.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
34
|
+
import { readFileSync } from "node:fs";
|
|
35
|
+
import { dirname, join } from "node:path";
|
|
36
|
+
import { fileURLToPath } from "node:url";
|
|
37
|
+
|
|
38
|
+
import { invokedDirectly } from "../lib/invoked-directly.mjs";
|
|
39
|
+
|
|
40
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
41
|
+
const PREPARE = join(HERE, "worktree-prepare.sh");
|
|
42
|
+
|
|
43
|
+
const git = (cwd, ...args) =>
|
|
44
|
+
execFileSync("git", ["-C", cwd, ...args], {
|
|
45
|
+
encoding: "utf8",
|
|
46
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
47
|
+
timeout: 60_000,
|
|
48
|
+
}).trim();
|
|
49
|
+
|
|
50
|
+
function prepareOne(repo, prepare) {
|
|
51
|
+
try {
|
|
52
|
+
return prepareOnce(repo, prepare);
|
|
53
|
+
} catch (err) {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
out: {
|
|
57
|
+
project: repo.project,
|
|
58
|
+
worktreePath: repo.path,
|
|
59
|
+
branch: repo.branch,
|
|
60
|
+
baseBranch: repo.base,
|
|
61
|
+
action: "failed",
|
|
62
|
+
error: String(err?.message || err).slice(0, 500),
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function prepareOnce(repo, prepare) {
|
|
69
|
+
const args = [
|
|
70
|
+
prepare,
|
|
71
|
+
"--project",
|
|
72
|
+
repo.project,
|
|
73
|
+
"--path",
|
|
74
|
+
repo.path,
|
|
75
|
+
"--branch",
|
|
76
|
+
repo.branch,
|
|
77
|
+
"--base",
|
|
78
|
+
repo.base,
|
|
79
|
+
];
|
|
80
|
+
if (repo.name) args.push("--name", repo.name);
|
|
81
|
+
if (repo.email) args.push("--email", repo.email);
|
|
82
|
+
const r = spawnSync("bash", args, { encoding: "utf8", timeout: 300_000 });
|
|
83
|
+
let out;
|
|
84
|
+
try {
|
|
85
|
+
out = JSON.parse(r.stdout);
|
|
86
|
+
} catch {
|
|
87
|
+
out = null;
|
|
88
|
+
}
|
|
89
|
+
if (r.status === 0 && out && (out.action === "created" || out.action === "reused"))
|
|
90
|
+
return { ok: true, out };
|
|
91
|
+
return {
|
|
92
|
+
ok: false,
|
|
93
|
+
out: out ?? {
|
|
94
|
+
project: repo.project,
|
|
95
|
+
worktreePath: repo.path,
|
|
96
|
+
branch: repo.branch,
|
|
97
|
+
baseBranch: repo.base,
|
|
98
|
+
action: "failed",
|
|
99
|
+
error: (r.stderr || r.error?.message || `exit ${r.status}`).trim().slice(0, 500),
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Undo one worktree this call created. Returns a rolledBack or kept record. */
|
|
105
|
+
export function rollbackOne(res) {
|
|
106
|
+
const where = { project: res.project, worktreePath: res.worktreePath };
|
|
107
|
+
try {
|
|
108
|
+
git(res.project, "worktree", "remove", res.worktreePath);
|
|
109
|
+
} catch (err) {
|
|
110
|
+
return {
|
|
111
|
+
kept: {
|
|
112
|
+
...where,
|
|
113
|
+
reason: `worktree not removed: ${String(err.stderr || err.message)
|
|
114
|
+
.trim()
|
|
115
|
+
.slice(0, 200)}`,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
let branchDeleted = false;
|
|
120
|
+
if (res.newBranch === true) {
|
|
121
|
+
try {
|
|
122
|
+
const tip = git(res.project, "rev-parse", "--verify", `refs/heads/${res.branch}`);
|
|
123
|
+
const base = git(
|
|
124
|
+
res.project,
|
|
125
|
+
"rev-parse",
|
|
126
|
+
"--verify",
|
|
127
|
+
`refs/remotes/origin/${res.baseBranch}`,
|
|
128
|
+
);
|
|
129
|
+
if (tip === base) {
|
|
130
|
+
git(res.project, "branch", "-D", "--", res.branch);
|
|
131
|
+
branchDeleted = true;
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
branchDeleted = false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return { rolledBack: { ...where, branchDeleted } };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function prepareAll(repos, { prepare = PREPARE } = {}) {
|
|
141
|
+
const results = [];
|
|
142
|
+
for (const repo of repos) {
|
|
143
|
+
const r = prepareOne(repo, prepare);
|
|
144
|
+
if (r.ok) {
|
|
145
|
+
results.push(r.out);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const rolledBack = [];
|
|
149
|
+
const kept = [];
|
|
150
|
+
for (const done of [...results].reverse()) {
|
|
151
|
+
if (done.action !== "created") continue;
|
|
152
|
+
const u = rollbackOne(done);
|
|
153
|
+
if (u.rolledBack) rolledBack.push(u.rolledBack);
|
|
154
|
+
else kept.push(u.kept);
|
|
155
|
+
}
|
|
156
|
+
return { ok: false, results, failed: r.out, rolledBack, kept };
|
|
157
|
+
}
|
|
158
|
+
return { ok: true, results, failed: null, rolledBack: [], kept: [] };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const FIELD = /^[^\0\n]+$/;
|
|
162
|
+
|
|
163
|
+
export function readRepos(text) {
|
|
164
|
+
const list = JSON.parse(text);
|
|
165
|
+
if (!Array.isArray(list) || !list.length) throw new Error("repos must be a non-empty array");
|
|
166
|
+
for (const r of list) {
|
|
167
|
+
for (const k of ["project", "path", "branch", "base"]) {
|
|
168
|
+
if (typeof r?.[k] !== "string" || !FIELD.test(r[k]))
|
|
169
|
+
throw new Error(`each repo needs a string ${k}`);
|
|
170
|
+
}
|
|
171
|
+
for (const k of ["branch", "base"])
|
|
172
|
+
if (r[k].startsWith("-")) throw new Error(`${k} must not start with -`);
|
|
173
|
+
for (const k of ["name", "email"]) {
|
|
174
|
+
if (r[k] !== undefined && (typeof r[k] !== "string" || !FIELD.test(r[k]))) {
|
|
175
|
+
throw new Error(`${k} must be a string when given`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return list;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function main() {
|
|
183
|
+
const i = process.argv.indexOf("--repos");
|
|
184
|
+
if (i < 0 || !process.argv[i + 1] || process.argv.length !== 4) {
|
|
185
|
+
console.error("usage: worktree-prepare-all.mjs --repos <repos.json>");
|
|
186
|
+
return 2;
|
|
187
|
+
}
|
|
188
|
+
let repos;
|
|
189
|
+
try {
|
|
190
|
+
repos = readRepos(readFileSync(process.argv[i + 1], "utf8"));
|
|
191
|
+
} catch (err) {
|
|
192
|
+
console.error(`worktree-prepare-all: ${err.message}`);
|
|
193
|
+
return 2;
|
|
194
|
+
}
|
|
195
|
+
const out = prepareAll(repos);
|
|
196
|
+
process.stdout.write(JSON.stringify(out, null, 2) + "\n");
|
|
197
|
+
return out.ok ? 0 : 1;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (invokedDirectly(import.meta.url)) process.exit(main());
|
|
@@ -27,10 +27,13 @@
|
|
|
27
27
|
# [--email <git user.email>]
|
|
28
28
|
#
|
|
29
29
|
# Output (stdout, JSON): {project, worktreePath, branch, baseBranch,
|
|
30
|
-
# action: created|reused|failed, pulled: true|false|null, error}
|
|
30
|
+
# action: created|reused|failed, pulled: true|false|null, newBranch, error}
|
|
31
|
+
# newBranch is true only when this call created the branch from origin/<base>,
|
|
32
|
+
# which is what tells worktree-prepare-all.mjs a rollback may delete it.
|
|
31
33
|
#
|
|
32
34
|
# Exit: 0 ready, 1 the add failed (nothing was created; the caller runs its
|
|
33
|
-
# rollback / collision flow), 2 usage
|
|
35
|
+
# rollback / collision flow), 2 usage, a path outside <project>/.worktrees/,
|
|
36
|
+
# or a new branch outside the naming convention.
|
|
34
37
|
set -uo pipefail
|
|
35
38
|
|
|
36
39
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -59,11 +62,43 @@ git -C "$PROJECT" rev-parse --git-dir >/dev/null 2>&1 || {
|
|
|
59
62
|
exit 2
|
|
60
63
|
}
|
|
61
64
|
|
|
65
|
+
# The worktree lives in the project's own .worktrees/ directory, compared on
|
|
66
|
+
# physical paths so a symlink cannot place it elsewhere.
|
|
67
|
+
physical() { # nearest existing ancestor resolved, the rest appended
|
|
68
|
+
local p="$1" tail=""
|
|
69
|
+
while [ ! -e "$p" ] && [ "$p" != "/" ] && [ "$p" != "." ]; do
|
|
70
|
+
tail="/$(basename "$p")$tail"
|
|
71
|
+
p="$(dirname "$p")"
|
|
72
|
+
done
|
|
73
|
+
printf '%s%s' "$(cd "$p" 2>/dev/null && pwd -P)" "$tail"
|
|
74
|
+
}
|
|
75
|
+
WT_ROOT="$(physical "$PROJECT")/.worktrees/"
|
|
76
|
+
case "$(physical "$WT")/" in
|
|
77
|
+
"$WT_ROOT"?*/) ;;
|
|
78
|
+
*)
|
|
79
|
+
echo "worktree-prepare: --path must be inside $PROJECT/.worktrees/ (got $WT)" >&2
|
|
80
|
+
exit 2
|
|
81
|
+
;;
|
|
82
|
+
esac
|
|
83
|
+
# A branch this call creates follows the naming convention; an existing one
|
|
84
|
+
# is re-entered as it is.
|
|
85
|
+
if ! git -C "$PROJECT" show-ref --verify --quiet "refs/heads/$BRANCH" &&
|
|
86
|
+
! git -C "$PROJECT" show-ref --verify --quiet "refs/remotes/origin/$BRANCH"; then
|
|
87
|
+
case "$BRANCH" in
|
|
88
|
+
feature/?* | bugfix/?* | hotfix/?* | release/?*) ;;
|
|
89
|
+
*)
|
|
90
|
+
echo "worktree-prepare: a new branch is named feature/<key>-<desc>, bugfix/<key>-<desc>, hotfix/<desc> or release/<version> (got $BRANCH)" >&2
|
|
91
|
+
exit 2
|
|
92
|
+
;;
|
|
93
|
+
esac
|
|
94
|
+
fi
|
|
95
|
+
NEW_BRANCH=false
|
|
62
96
|
report() { # $1 action, $2 pulled (true|false|null), $3 error
|
|
63
97
|
jq -n --arg project "$PROJECT" --arg wt "$WT" --arg branch "$BRANCH" --arg base "$BASE" \
|
|
64
|
-
--arg action "$1" --argjson pulled "$2" --arg error "$3" \
|
|
98
|
+
--arg action "$1" --argjson pulled "$2" --argjson newBranch "$NEW_BRANCH" --arg error "$3" \
|
|
65
99
|
'{project: $project, worktreePath: $wt, branch: $branch, baseBranch: $base,
|
|
66
|
-
action: $action, pulled: $pulled,
|
|
100
|
+
action: $action, pulled: $pulled, newBranch: $newBranch,
|
|
101
|
+
error: (if $error == "" then null else $error end)}'
|
|
67
102
|
}
|
|
68
103
|
|
|
69
104
|
# Physical path of $1, for comparing paths that may go through a symlink.
|
|
@@ -122,5 +157,6 @@ if ! err=$(git -C "$PROJECT" worktree add -q "${add_args[@]}" 2>&1); then
|
|
|
122
157
|
report failed null "$err"
|
|
123
158
|
exit 1
|
|
124
159
|
fi
|
|
160
|
+
[ "${add_args[1]}" = "-b" ] && NEW_BRANCH=true
|
|
125
161
|
set_identity
|
|
126
162
|
report created null ""
|