@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
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @file graph-links.mjs - which files of one repository import another.
|
|
4
|
+
*
|
|
5
|
+
* Each repository keeps its own code graph (ADR-0010); a run that touches a
|
|
6
|
+
* main repository and its libraries needs to know where they meet. This joins
|
|
7
|
+
* graphs without merging them: a repository's import of a name it does not
|
|
8
|
+
* declare (a `module:` node) is matched against the names other repositories
|
|
9
|
+
* export, and every match becomes one link row.
|
|
10
|
+
*
|
|
11
|
+
* What a repository exports, by stack:
|
|
12
|
+
* node the `name` in package.json
|
|
13
|
+
* python top-level package directories (they hold __init__.py)
|
|
14
|
+
* ios target directories under Sources/
|
|
15
|
+
* go the module path in go.mod
|
|
16
|
+
* android top-level, non-nested type and composable declarations, since a
|
|
17
|
+
* Kotlin import names a class
|
|
18
|
+
* any the repository directory name, when the stack names nothing
|
|
19
|
+
*
|
|
20
|
+
* A Kotlin import keeps only its last segment in the graph, and names like
|
|
21
|
+
* `Text` or `Result` exist in many packages. An android link therefore holds
|
|
22
|
+
* only the importing files whose `import <package>.<Name>` names the package
|
|
23
|
+
* the exporting file declares; both repositories are read from disk for it.
|
|
24
|
+
*
|
|
25
|
+
* Usage:
|
|
26
|
+
* graph-links.mjs build --projects <a,b,...> [--out <path>]
|
|
27
|
+
* graph-links.mjs affected --project <b> [--name <module-or-symbol>] [--links <path>]
|
|
28
|
+
*
|
|
29
|
+
* Projects are directory names under ~/.claude/knowledge/. `build` writes
|
|
30
|
+
* ~/.claude/knowledge/_links.json unless --out says otherwise. `affected`
|
|
31
|
+
* lists the files in other repositories that import project b (or one of
|
|
32
|
+
* its names), so a change there can be followed across.
|
|
33
|
+
*
|
|
34
|
+
* Exit: 0 done · 1 a graph or the link file is missing · 64 usage
|
|
35
|
+
*
|
|
36
|
+
* @module pipeline/scripts/graph-links
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
40
|
+
import { homedir } from "node:os";
|
|
41
|
+
import { basename, join, resolve } from "node:path";
|
|
42
|
+
|
|
43
|
+
import { loadGraph } from "./graph-query.mjs";
|
|
44
|
+
import { invokedDirectly } from "../lib/invoked-directly.mjs";
|
|
45
|
+
|
|
46
|
+
const KNOWLEDGE = () => join(homedir(), ".claude", "knowledge");
|
|
47
|
+
const PROJECT_RE = /^[A-Za-z0-9._-]+$/;
|
|
48
|
+
const ANDROID_KINDS = new Set(["class", "object", "interface", "enum", "composable", "typealias"]);
|
|
49
|
+
|
|
50
|
+
function readText(p) {
|
|
51
|
+
try {
|
|
52
|
+
return readFileSync(p, "utf8");
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function dirs(p) {
|
|
59
|
+
try {
|
|
60
|
+
return readdirSync(p, { withFileTypes: true })
|
|
61
|
+
.filter((d) => d.isDirectory() && !d.name.startsWith("."))
|
|
62
|
+
.map((d) => d.name);
|
|
63
|
+
} catch {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** name -> what it points at in this repository (a symbol id, or null for the repo). */
|
|
69
|
+
export function exportsOf(graph) {
|
|
70
|
+
const out = new Map();
|
|
71
|
+
const root = graph.root || "";
|
|
72
|
+
if (graph.stack === "node") {
|
|
73
|
+
const pkg = readText(join(root, "package.json"));
|
|
74
|
+
try {
|
|
75
|
+
const name = JSON.parse(pkg ?? "null")?.name;
|
|
76
|
+
if (typeof name === "string" && name) out.set(name, null);
|
|
77
|
+
} catch {
|
|
78
|
+
/* no package name */
|
|
79
|
+
}
|
|
80
|
+
} else if (graph.stack === "python") {
|
|
81
|
+
for (const d of dirs(root)) if (existsSync(join(root, d, "__init__.py"))) out.set(d, null);
|
|
82
|
+
} else if (graph.stack === "ios") {
|
|
83
|
+
for (const d of dirs(join(root, "Sources"))) out.set(d, null);
|
|
84
|
+
} else if (graph.stack === "go") {
|
|
85
|
+
const mod = /^module\s+(\S+)/m.exec(readText(join(root, "go.mod")) ?? "");
|
|
86
|
+
if (mod) out.set(mod[1], null);
|
|
87
|
+
} else if (graph.stack === "android") {
|
|
88
|
+
for (const n of graph.nodes) {
|
|
89
|
+
if (n.kind === "symbol" && !n.nested && ANDROID_KINDS.has(n.symbolKind) && !out.has(n.name))
|
|
90
|
+
out.set(n.name, n.id);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!out.size && root) out.set(basename(root), null);
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The package a Kotlin symbol's file declares, or null. */
|
|
98
|
+
function kotlinPackage(root, symbolId, cache) {
|
|
99
|
+
const rel = symbolId.slice("sym:".length, symbolId.lastIndexOf("#"));
|
|
100
|
+
const key = `${root}\0${rel}`;
|
|
101
|
+
if (!cache.has(key)) {
|
|
102
|
+
const text = rel.includes("..") ? null : readText(join(root, rel));
|
|
103
|
+
cache.set(key, /^\s*package\s+([\w.]+)/m.exec(text ?? "")?.[1] ?? null);
|
|
104
|
+
}
|
|
105
|
+
return cache.get(key);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** True when the file imports `name` from exactly that package. */
|
|
109
|
+
function importsFrom(root, rel, pkg, name) {
|
|
110
|
+
if (rel.includes("..")) return false;
|
|
111
|
+
const text = readText(join(root, rel)) ?? "";
|
|
112
|
+
const esc = (v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
113
|
+
return new RegExp(`^\\s*import\\s+${esc(pkg)}\\.${esc(name)}(?![\\w])`, "m").test(text);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** The names an import could be asking for: itself, and its package prefix. */
|
|
117
|
+
export function candidates(moduleName) {
|
|
118
|
+
const m = String(moduleName);
|
|
119
|
+
const set = new Set([m]);
|
|
120
|
+
if (m.startsWith("@")) set.add(m.split("/").slice(0, 2).join("/"));
|
|
121
|
+
else set.add(m.split(/[/.]/)[0]);
|
|
122
|
+
return [...set];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Link rows between every pair of the given graphs. */
|
|
126
|
+
export function link(graphs) {
|
|
127
|
+
const exported = graphs.map((g) => ({
|
|
128
|
+
project: g.project,
|
|
129
|
+
graph: g.graph,
|
|
130
|
+
names: exportsOf(g.graph),
|
|
131
|
+
}));
|
|
132
|
+
const packages = new Map();
|
|
133
|
+
const rows = [];
|
|
134
|
+
for (const { project, graph } of graphs) {
|
|
135
|
+
const importers = new Map();
|
|
136
|
+
for (const e of graph.edges) {
|
|
137
|
+
if (e.kind !== "imports" || !e.to.startsWith("module:")) continue;
|
|
138
|
+
if (!importers.has(e.to)) importers.set(e.to, []);
|
|
139
|
+
importers.get(e.to).push(e.from.replace(/^file:/, ""));
|
|
140
|
+
}
|
|
141
|
+
for (const [mid, files] of importers) {
|
|
142
|
+
const name = mid.slice("module:".length);
|
|
143
|
+
for (const other of exported) {
|
|
144
|
+
if (other.project === project) continue;
|
|
145
|
+
const hit = candidates(name).find((c) => other.names.has(c));
|
|
146
|
+
if (hit === undefined) continue;
|
|
147
|
+
const target = other.names.get(hit);
|
|
148
|
+
let kept = [...new Set(files)].sort();
|
|
149
|
+
if (other.graph.stack === "android" && target) {
|
|
150
|
+
const pkg = kotlinPackage(other.graph.root || "", target, packages);
|
|
151
|
+
kept = pkg ? kept.filter((f) => importsFrom(graph.root || "", f, pkg, hit)) : [];
|
|
152
|
+
}
|
|
153
|
+
if (!kept.length) continue;
|
|
154
|
+
rows.push({ from: project, module: name, to: other.project, target, files: kept });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return rows.sort(
|
|
159
|
+
(a, b) =>
|
|
160
|
+
a.to.localeCompare(b.to) || a.from.localeCompare(b.from) || a.module.localeCompare(b.module),
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function graphPath(project) {
|
|
165
|
+
if (!PROJECT_RE.test(project) || project === "." || project === "..")
|
|
166
|
+
throw new Error(`not a project name: ${project}`);
|
|
167
|
+
return join(KNOWLEDGE(), project, "code-graph.json");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function flag(argv, name) {
|
|
171
|
+
const i = argv.indexOf(`--${name}`);
|
|
172
|
+
return i >= 0 ? argv[i + 1] : undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function main(argv) {
|
|
176
|
+
const sub = argv[0];
|
|
177
|
+
const linksPath = resolve(
|
|
178
|
+
flag(argv, "links") || flag(argv, "out") || join(KNOWLEDGE(), "_links.json"),
|
|
179
|
+
);
|
|
180
|
+
if (sub === "build") {
|
|
181
|
+
const projects = (flag(argv, "projects") || "").split(",").filter(Boolean);
|
|
182
|
+
if (projects.length < 2) return usage();
|
|
183
|
+
let graphs;
|
|
184
|
+
try {
|
|
185
|
+
graphs = projects.map((project) => ({ project, graph: loadGraph(graphPath(project)) }));
|
|
186
|
+
} catch (e) {
|
|
187
|
+
process.stderr.write(`graph-links: ${e.message}\n`);
|
|
188
|
+
return 1;
|
|
189
|
+
}
|
|
190
|
+
const rows = link(graphs);
|
|
191
|
+
const doc = { v: 1, builtAt: new Date().toISOString(), projects, links: rows };
|
|
192
|
+
writeFileSync(linksPath, JSON.stringify(doc, null, 2) + "\n", { mode: 0o600 });
|
|
193
|
+
process.stdout.write(JSON.stringify({ out: linksPath, projects, links: rows.length }) + "\n");
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
196
|
+
if (sub === "affected") {
|
|
197
|
+
const project = flag(argv, "project");
|
|
198
|
+
if (!project) return usage();
|
|
199
|
+
const doc = readText(linksPath);
|
|
200
|
+
if (!doc) {
|
|
201
|
+
process.stderr.write(`graph-links: no link file at ${linksPath}; run build first\n`);
|
|
202
|
+
return 1;
|
|
203
|
+
}
|
|
204
|
+
const name = flag(argv, "name");
|
|
205
|
+
const hits = JSON.parse(doc).links.filter(
|
|
206
|
+
(r) =>
|
|
207
|
+
r.to === project &&
|
|
208
|
+
(!name ||
|
|
209
|
+
r.module === name ||
|
|
210
|
+
r.target?.endsWith(`#${name}`) ||
|
|
211
|
+
candidates(r.module).includes(name)),
|
|
212
|
+
);
|
|
213
|
+
process.stdout.write(
|
|
214
|
+
JSON.stringify({ project, name: name ?? null, importers: hits }, null, 2) + "\n",
|
|
215
|
+
);
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
return usage();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function usage() {
|
|
222
|
+
process.stderr.write(
|
|
223
|
+
"usage: graph-links.mjs build --projects <a,b,...> [--out <path>] | affected --project <b> [--name <n>] [--links <path>]\n",
|
|
224
|
+
);
|
|
225
|
+
return 64;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (invokedDirectly(import.meta.url)) {
|
|
229
|
+
process.exitCode = main(process.argv.slice(2));
|
|
230
|
+
}
|
|
@@ -18,9 +18,16 @@
|
|
|
18
18
|
* --seeds N How many ranked seeds to expand. Default: 8
|
|
19
19
|
* --json Emit JSON instead of text
|
|
20
20
|
*
|
|
21
|
+
* graph-query --path <from> <to> [--max-depth N] [--directed] [--kind K,...]
|
|
22
|
+
* Shortest path between two symbols or files, by node id or
|
|
23
|
+
* by name. A name that matches more than one node is refused
|
|
24
|
+
* with the candidates, never guessed; a name that matches
|
|
25
|
+
* none gets its nearest names.
|
|
26
|
+
*
|
|
21
27
|
* Exit codes:
|
|
22
|
-
* 0 - answered (possibly empty)
|
|
28
|
+
* 0 - answered (possibly empty, or no path within the depth)
|
|
23
29
|
* 1 - graph missing or unreadable
|
|
30
|
+
* 3 - a --path endpoint is ambiguous or unknown
|
|
24
31
|
* 64 - usage error
|
|
25
32
|
*
|
|
26
33
|
* @module pipeline/scripts/graph-query
|
|
@@ -93,6 +100,72 @@ export function rankNodes(graph, question) {
|
|
|
93
100
|
.map((s) => ({ node: graph.nodes[s.index], score: s.score }));
|
|
94
101
|
}
|
|
95
102
|
|
|
103
|
+
const CANDIDATES = 5;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* One node for a name or id. Exact id first, then exact name, then the name
|
|
107
|
+
* case-insensitively. More than one match is refused with the candidates; no
|
|
108
|
+
* match returns the nearest names by the same ranking the query uses.
|
|
109
|
+
*
|
|
110
|
+
* @returns {{node: object} | {ambiguous: object[]} | {unknown: object[]}}
|
|
111
|
+
*/
|
|
112
|
+
export function resolveNode(graph, ref) {
|
|
113
|
+
const byId = graph.nodes.find((n) => n.id === ref);
|
|
114
|
+
if (byId) return { node: byId };
|
|
115
|
+
let matches = graph.nodes.filter((n) => n.name === ref);
|
|
116
|
+
if (!matches.length) {
|
|
117
|
+
const lower = String(ref).toLowerCase();
|
|
118
|
+
matches = graph.nodes.filter((n) => (n.name || "").toLowerCase() === lower);
|
|
119
|
+
}
|
|
120
|
+
if (matches.length === 1) return { node: matches[0] };
|
|
121
|
+
if (matches.length > 1) return { ambiguous: matches.slice(0, CANDIDATES) };
|
|
122
|
+
return {
|
|
123
|
+
unknown: rankNodes(graph, String(ref))
|
|
124
|
+
.slice(0, CANDIDATES)
|
|
125
|
+
.map((r) => r.node),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Breadth-first shortest path. Undirected unless `directed`, so a subclass
|
|
131
|
+
* reaches its base and the base reaches the subclass. External module nodes
|
|
132
|
+
* are endpoints only.
|
|
133
|
+
*
|
|
134
|
+
* @returns {{path: Array<{node: object, via: string|null}>} | {path: null}}
|
|
135
|
+
*/
|
|
136
|
+
export function shortestPath(
|
|
137
|
+
graph,
|
|
138
|
+
fromId,
|
|
139
|
+
toId,
|
|
140
|
+
{ maxDepth = 8, directed = false, kinds = null } = {},
|
|
141
|
+
) {
|
|
142
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
143
|
+
const adj = adjacency(graph);
|
|
144
|
+
const prev = new Map([[fromId, null]]);
|
|
145
|
+
let frontier = [fromId];
|
|
146
|
+
for (let d = 0; d < maxDepth && frontier.length && !prev.has(toId); d++) {
|
|
147
|
+
const next = [];
|
|
148
|
+
for (const id of frontier) {
|
|
149
|
+
for (const n of adj.get(id) || []) {
|
|
150
|
+
if (directed && n.dir !== "out") continue;
|
|
151
|
+
if (kinds && !kinds.includes(n.kind)) continue;
|
|
152
|
+
if (prev.has(n.id)) continue;
|
|
153
|
+
prev.set(n.id, { from: id, via: `${n.kind}/${n.dir}` });
|
|
154
|
+
// An external module joins every file that imports it, so it ends a
|
|
155
|
+
// path but is never walked through.
|
|
156
|
+
if (byId.get(n.id)?.kind !== "module" || n.id === toId) next.push(n.id);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
frontier = next;
|
|
160
|
+
}
|
|
161
|
+
if (!prev.has(toId)) return { path: null };
|
|
162
|
+
const path = [];
|
|
163
|
+
for (let id = toId; id !== null; id = prev.get(id)?.from ?? null) {
|
|
164
|
+
path.unshift({ node: byId.get(id), via: prev.get(id)?.via ?? null });
|
|
165
|
+
}
|
|
166
|
+
return { path };
|
|
167
|
+
}
|
|
168
|
+
|
|
96
169
|
function renderNode(node) {
|
|
97
170
|
const where = node.path ? `${node.path}${node.line ? `:${node.line}` : ""}` : "-";
|
|
98
171
|
const kind = node.symbolKind || node.kind;
|
|
@@ -157,8 +230,67 @@ export function traverse({ graph, question, budget, depth, seedCount }) {
|
|
|
157
230
|
return { seeds, hits, truncated, tokens };
|
|
158
231
|
}
|
|
159
232
|
|
|
233
|
+
function pathMain(flags, positional, graph) {
|
|
234
|
+
if (positional.length !== 2) {
|
|
235
|
+
process.stderr.write(
|
|
236
|
+
"graph-query: --path needs two endpoints, e.g. --path LoginView AuthService\n",
|
|
237
|
+
);
|
|
238
|
+
return 64;
|
|
239
|
+
}
|
|
240
|
+
const ends = positional.map((ref) => ({ ref, ...resolveNode(graph, ref) }));
|
|
241
|
+
const bad = ends.filter((e) => !e.node);
|
|
242
|
+
if (bad.length) {
|
|
243
|
+
const doc = bad.map((e) => ({
|
|
244
|
+
ref: e.ref,
|
|
245
|
+
reason: e.ambiguous ? "ambiguous" : "unknown",
|
|
246
|
+
candidates: (e.ambiguous || e.unknown).map((n) => ({ id: n.id, line: renderNode(n) })),
|
|
247
|
+
}));
|
|
248
|
+
if (flags.json === true) process.stdout.write(JSON.stringify({ refused: doc }, null, 2) + "\n");
|
|
249
|
+
else
|
|
250
|
+
for (const d of doc) {
|
|
251
|
+
process.stdout.write(`${d.ref}: ${d.reason}; pass one of these ids:\n`);
|
|
252
|
+
for (const c of d.candidates) process.stdout.write(` ${c.id} ${c.line}\n`);
|
|
253
|
+
}
|
|
254
|
+
return 3;
|
|
255
|
+
}
|
|
256
|
+
const maxDepth = Math.min(Number(flags["max-depth"]) > 0 ? Number(flags["max-depth"]) : 8, 12);
|
|
257
|
+
const kinds = typeof flags.kind === "string" ? flags.kind.split(",") : null;
|
|
258
|
+
const { path } = shortestPath(graph, ends[0].node.id, ends[1].node.id, {
|
|
259
|
+
maxDepth,
|
|
260
|
+
directed: flags.directed === true,
|
|
261
|
+
kinds,
|
|
262
|
+
});
|
|
263
|
+
if (flags.json === true) {
|
|
264
|
+
const hops =
|
|
265
|
+
path?.map((h) => ({ id: h.node.id, line: renderNode(h.node), via: h.via })) ?? null;
|
|
266
|
+
process.stdout.write(
|
|
267
|
+
JSON.stringify({ from: ends[0].node.id, to: ends[1].node.id, path: hops }, null, 2) + "\n",
|
|
268
|
+
);
|
|
269
|
+
return 0;
|
|
270
|
+
}
|
|
271
|
+
if (!path) {
|
|
272
|
+
process.stdout.write(`no path within ${maxDepth} hops\n`);
|
|
273
|
+
return 0;
|
|
274
|
+
}
|
|
275
|
+
path.forEach((h, i) => process.stdout.write(`${printedLine(i, renderNode(h.node), h.via)}\n`));
|
|
276
|
+
return 0;
|
|
277
|
+
}
|
|
278
|
+
|
|
160
279
|
function main() {
|
|
161
280
|
const { flags, positional } = parseFlags(process.argv.slice(2));
|
|
281
|
+
if (flags.path !== undefined) {
|
|
282
|
+
// parseFlags reads the first endpoint as the flag's value.
|
|
283
|
+
const ends = typeof flags.path === "string" ? [flags.path, ...positional] : positional;
|
|
284
|
+
const path = flags.graph && flags.graph !== true ? resolve(flags.graph) : defaultGraphPath();
|
|
285
|
+
let graph;
|
|
286
|
+
try {
|
|
287
|
+
graph = loadGraph(path);
|
|
288
|
+
} catch (e) {
|
|
289
|
+
process.stderr.write(`graph-query: ${e.message}\n`);
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
process.exit(pathMain(flags, ends, graph));
|
|
293
|
+
}
|
|
162
294
|
const question = positional.join(" ").trim();
|
|
163
295
|
if (!question) {
|
|
164
296
|
process.stderr.write('graph-query: a question is required, e.g. graph-query "flight search"\n');
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
* --out <path> Report path. Default: GRAPH_REPORT.md beside the graph
|
|
18
18
|
* --top N Rows per ranked section. Default: 15
|
|
19
19
|
* --stdout Print instead of writing
|
|
20
|
+
* --diff <path> Compare with another build of the same repository (the
|
|
21
|
+
* older one) and print what changed as JSON, writing nothing.
|
|
20
22
|
* --status Print one header line and exit, writing nothing. This is
|
|
21
23
|
* what `/multi-agent:graph status` runs: the graph is a
|
|
22
24
|
* 22MB JSON on a large repo, so "read the header yourself"
|
|
@@ -35,6 +37,7 @@ import { join, dirname, resolve } from "node:path";
|
|
|
35
37
|
import { parseFlags } from "./graph-build.mjs";
|
|
36
38
|
import { loadGraph, defaultGraphPath } from "./graph-query.mjs";
|
|
37
39
|
import { loadRules } from "./_code-graph.mjs";
|
|
40
|
+
import { clusters, graphDiff, importCycles, questions } from "./_graph-analysis.mjs";
|
|
38
41
|
|
|
39
42
|
/**
|
|
40
43
|
* Top-level source directory of a path, used as the module bucket.
|
|
@@ -223,6 +226,49 @@ export function render(graph, top) {
|
|
|
223
226
|
}
|
|
224
227
|
L.push("");
|
|
225
228
|
|
|
229
|
+
const cycles = importCycles(graph);
|
|
230
|
+
L.push(`## Import cycles`);
|
|
231
|
+
L.push("");
|
|
232
|
+
if (!cycles.length) {
|
|
233
|
+
L.push(`None: no files import each other, directly or around a loop.`);
|
|
234
|
+
} else {
|
|
235
|
+
L.push(
|
|
236
|
+
`${cycles.length} group(s) of files that import each other. A change to one reaches all of them.`,
|
|
237
|
+
);
|
|
238
|
+
L.push("");
|
|
239
|
+
for (const c of cycles.slice(0, top)) {
|
|
240
|
+
const shown = c
|
|
241
|
+
.slice(0, 6)
|
|
242
|
+
.map((f) => `\`${f.replace(/^file:/, "")}\``)
|
|
243
|
+
.join(", ");
|
|
244
|
+
L.push(`- ${c.length} files: ${shown}${c.length > 6 ? `, ... ${c.length - 6} more` : ""}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
L.push("");
|
|
248
|
+
|
|
249
|
+
const groups = clusters(graph);
|
|
250
|
+
L.push(`## Clusters`);
|
|
251
|
+
L.push("");
|
|
252
|
+
L.push(
|
|
253
|
+
`Files more connected to each other than to the rest (label propagation over imports and references). ` +
|
|
254
|
+
`Cohesion is the share of a cluster's edges that stay inside it.`,
|
|
255
|
+
);
|
|
256
|
+
L.push("");
|
|
257
|
+
L.push(`| Files | Cohesion | Around | Where |`);
|
|
258
|
+
L.push(`|---|---|---|---|`);
|
|
259
|
+
for (const g of groups.slice(0, top)) {
|
|
260
|
+
L.push(`| ${g.files.length} | ${g.cohesion} | \`${g.id}\` | ${g.dirs} |`);
|
|
261
|
+
}
|
|
262
|
+
L.push("");
|
|
263
|
+
|
|
264
|
+
const asks = questions(graph, { hubs: s.hubs, cycles, groups });
|
|
265
|
+
if (asks.length) {
|
|
266
|
+
L.push(`## Questions worth asking`);
|
|
267
|
+
L.push("");
|
|
268
|
+
for (const q of asks) L.push(`- ${q}`);
|
|
269
|
+
L.push("");
|
|
270
|
+
}
|
|
271
|
+
|
|
226
272
|
L.push(`## Modules`);
|
|
227
273
|
L.push("");
|
|
228
274
|
L.push(`| Module | Files | Symbols | Test files |`);
|
|
@@ -358,7 +404,14 @@ export function statusLine(graph, path) {
|
|
|
358
404
|
);
|
|
359
405
|
}
|
|
360
406
|
|
|
407
|
+
const USAGE =
|
|
408
|
+
"usage: graph-report.mjs [--graph <path>] [--out <path>] [--top N] [--stdout] [--diff <older-graph>] [--status]\n";
|
|
409
|
+
|
|
361
410
|
function main() {
|
|
411
|
+
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
412
|
+
process.stdout.write(USAGE);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
362
415
|
const { flags } = parseFlags(process.argv.slice(2));
|
|
363
416
|
const path = flags.graph && flags.graph !== true ? resolve(flags.graph) : defaultGraphPath();
|
|
364
417
|
|
|
@@ -375,6 +428,18 @@ function main() {
|
|
|
375
428
|
return;
|
|
376
429
|
}
|
|
377
430
|
|
|
431
|
+
if (flags.diff && flags.diff !== true) {
|
|
432
|
+
let before;
|
|
433
|
+
try {
|
|
434
|
+
before = loadGraph(resolve(flags.diff));
|
|
435
|
+
} catch (e) {
|
|
436
|
+
process.stderr.write(`graph-report: ${e.message}\n`);
|
|
437
|
+
process.exit(1);
|
|
438
|
+
}
|
|
439
|
+
process.stdout.write(JSON.stringify(graphDiff(before, graph), null, 2) + "\n");
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
|
|
378
443
|
const top = Number(flags.top) > 0 ? Number(flags.top) : 15;
|
|
379
444
|
const markdown = render(graph, top);
|
|
380
445
|
|
|
@@ -53,20 +53,14 @@
|
|
|
53
53
|
* Exit: 0 ok (including "nothing found") | 1 usage
|
|
54
54
|
*/
|
|
55
55
|
|
|
56
|
-
import {
|
|
57
|
-
existsSync,
|
|
58
|
-
mkdtempSync,
|
|
59
|
-
readFileSync,
|
|
60
|
-
readdirSync,
|
|
61
|
-
rmSync,
|
|
62
|
-
statSync,
|
|
63
|
-
writeFileSync,
|
|
64
|
-
} from "node:fs";
|
|
56
|
+
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
65
57
|
import { execFileSync } from "node:child_process";
|
|
66
58
|
import { basename, dirname, join } from "node:path";
|
|
67
|
-
import { homedir
|
|
59
|
+
import { homedir } from "node:os";
|
|
68
60
|
import { fileURLToPath } from "node:url";
|
|
69
61
|
import { runMain } from "../lib/fatal.mjs";
|
|
62
|
+
import { memoryExcluded } from "./_memory-policy.mjs";
|
|
63
|
+
import { dropSecrets } from "./_secret-scan.mjs";
|
|
70
64
|
|
|
71
65
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
72
66
|
const argv = process.argv.slice(2);
|
|
@@ -382,41 +376,6 @@ function mine(events) {
|
|
|
382
376
|
return found;
|
|
383
377
|
}
|
|
384
378
|
|
|
385
|
-
/* ----------------------------------------------------------------- privacy */
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* The single-source secret check. Candidates go to a temp file and through
|
|
389
|
-
* pre-commit-check.sh, which owns the patterns; a hit is dropped, never written.
|
|
390
|
-
*/
|
|
391
|
-
function dropSecrets(items) {
|
|
392
|
-
const scanner = existsSync(join(HERE, "pre-commit-check.sh"))
|
|
393
|
-
? join(HERE, "pre-commit-check.sh")
|
|
394
|
-
: join(homedir(), ".claude", "scripts", "pre-commit-check.sh");
|
|
395
|
-
if (!existsSync(scanner)) {
|
|
396
|
-
return { kept: items, dropped: 0, scanned: false };
|
|
397
|
-
}
|
|
398
|
-
const dir = mkdtempSync(join(tmpdir(), "lft-"));
|
|
399
|
-
const kept = [];
|
|
400
|
-
let dropped = 0;
|
|
401
|
-
try {
|
|
402
|
-
for (const it of items) {
|
|
403
|
-
const f = join(dir, "candidate.txt");
|
|
404
|
-
writeFileSync(f, `${it.statement}\n${it.diagnosis || ""}\n${it.scope}\n`);
|
|
405
|
-
let clean = true;
|
|
406
|
-
try {
|
|
407
|
-
execFileSync("bash", [scanner, "--scan-file", f], { stdio: "ignore" });
|
|
408
|
-
} catch {
|
|
409
|
-
clean = false;
|
|
410
|
-
}
|
|
411
|
-
if (clean) kept.push(it);
|
|
412
|
-
else dropped++;
|
|
413
|
-
}
|
|
414
|
-
} finally {
|
|
415
|
-
rmSync(dir, { recursive: true, force: true });
|
|
416
|
-
}
|
|
417
|
-
return { kept, dropped, scanned: true };
|
|
418
|
-
}
|
|
419
|
-
|
|
420
379
|
/* -------------------------------------------------------------------- main */
|
|
421
380
|
|
|
422
381
|
function main() {
|
|
@@ -462,12 +421,16 @@ function main() {
|
|
|
462
421
|
uniq.push(it);
|
|
463
422
|
}
|
|
464
423
|
|
|
465
|
-
const { kept, dropped, scanned } = dropSecrets(
|
|
424
|
+
const { kept, dropped, scanned } = dropSecrets(
|
|
425
|
+
uniq,
|
|
426
|
+
(it) => `${it.statement}\n${it.diagnosis || ""}\n${it.scope}`,
|
|
427
|
+
);
|
|
466
428
|
const slug = repoSlug();
|
|
429
|
+
const excluded = memoryExcluded(slug);
|
|
467
430
|
|
|
468
431
|
let written = 0;
|
|
469
432
|
let deduped = 0;
|
|
470
|
-
if (APPLY && kept.length) {
|
|
433
|
+
if (APPLY && kept.length && !excluded) {
|
|
471
434
|
const ledger = existsSync(join(HERE, "learnings-ledger.mjs"))
|
|
472
435
|
? join(HERE, "learnings-ledger.mjs")
|
|
473
436
|
: join(homedir(), ".claude", "scripts", "learnings-ledger.mjs");
|
|
@@ -572,7 +535,7 @@ function main() {
|
|
|
572
535
|
}
|
|
573
536
|
|
|
574
537
|
let localFile = null;
|
|
575
|
-
if (APPLY && kept.length) localFile = writeLocalBlock(kept);
|
|
538
|
+
if (APPLY && kept.length && !excluded) localFile = writeLocalBlock(kept);
|
|
576
539
|
|
|
577
540
|
// Record how many lessons the machine found, kept apart from the ones a model
|
|
578
541
|
// proposed. Pooling them lets a "lessons learned" number climb while the half
|
|
@@ -595,6 +558,7 @@ function main() {
|
|
|
595
558
|
const result = {
|
|
596
559
|
status: "ok",
|
|
597
560
|
repo: slug,
|
|
561
|
+
excluded,
|
|
598
562
|
transcripts: files.length,
|
|
599
563
|
toolResultsExamined: examined,
|
|
600
564
|
candidates: uniq.length,
|