@mmerterden/multi-agent-pipeline 20.34.0 → 20.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -1
- package/README.md +1 -0
- package/docs/facts.json +2 -2
- package/install/catalog-history.json +1 -1
- package/install/claude.mjs +16 -1
- package/install/templates/claude-hooks.json +23 -1
- package/manifest.json +324 -293
- package/package.json +1 -1
- package/pipeline/agents/bulk-reader.md +3 -0
- package/pipeline/commands/multi-agent/channels/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/office-on/SKILL.md +7 -3
- package/pipeline/commands/multi-agent/office-status/SKILL.md +3 -2
- package/pipeline/contract/CHANGELOG.md +9 -0
- package/pipeline/contract/README.md +1 -0
- package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
- package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
- package/pipeline/contract/fixtures/error-unsigned.json +1 -1
- package/pipeline/contract/manifest.json +14 -1
- package/pipeline/contract/types/index.d.ts +24 -2
- package/pipeline/lib/json-file-lock.mjs +61 -15
- package/pipeline/lib/office-remote.mjs +228 -0
- package/pipeline/lib/qr-code.mjs +360 -0
- package/pipeline/lib/repo-hygiene.sh +1 -0
- package/pipeline/lib/run-completion.mjs +17 -0
- package/pipeline/multi-agent-refs/channels/issue-comment.md +1 -1
- package/pipeline/multi-agent-refs/features/office-remote-protocol.md +166 -0
- package/pipeline/multi-agent-refs/features/office-threat-model.md +1 -1
- package/pipeline/multi-agent-refs/features/scope-check.md +16 -1
- package/pipeline/multi-agent-refs/features/verify-by-test.md +10 -0
- package/pipeline/multi-agent-refs/keychain.md +7 -0
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +6 -2
- package/pipeline/multi-agent-refs/phases/phase-1-plan.md +4 -6
- package/pipeline/multi-agent-refs/phases/phase-2-dev.md +9 -12
- package/pipeline/multi-agent-refs/phases/phase-3-review.md +9 -8
- package/pipeline/multi-agent-refs/phases/phase-4-commit.md +1 -4
- package/pipeline/multi-agent-refs/rules.md +3 -2
- package/pipeline/multi-agent-refs/tracker-contract.md +1 -1
- package/pipeline/office/NOTICE.md +4 -1
- package/pipeline/office/assets/char_12.png +0 -0
- package/pipeline/office/assets/char_13.png +0 -0
- package/pipeline/office/assets/char_14.png +0 -0
- package/pipeline/office/assets/char_15.png +0 -0
- package/pipeline/office/assets/char_16.png +0 -0
- package/pipeline/office/assets/char_17.png +0 -0
- package/pipeline/office/assets/char_18.png +0 -0
- package/pipeline/office/assets/char_19.png +0 -0
- package/pipeline/office/assets/char_20.png +0 -0
- package/pipeline/office/assets/char_21.png +0 -0
- package/pipeline/office/assets/char_22.png +0 -0
- package/pipeline/office/assets/char_23.png +0 -0
- package/pipeline/office/assets/char_24.png +0 -0
- package/pipeline/office/assets/char_25.png +0 -0
- package/pipeline/office/assets/char_26.png +0 -0
- package/pipeline/office/assets/char_27.png +0 -0
- package/pipeline/office/assets/char_28.png +0 -0
- package/pipeline/office/assets/char_29.png +0 -0
- package/pipeline/office/assets/char_30.png +0 -0
- package/pipeline/office/index.html +293 -49
- package/pipeline/schemas/agent-state.schema.json +18 -0
- package/pipeline/schemas/planning-output.schema.json +18 -0
- package/pipeline/schemas/pr-reach.schema.json +55 -0
- package/pipeline/schemas/prefs.schema.json +13 -7
- package/pipeline/schemas/triage-output.schema.json +12 -1
- package/pipeline/schemas/unattended-policy.json +2 -1
- package/pipeline/scripts/_guard_rules.py +449 -1
- package/pipeline/scripts/_test-gaming.mjs +36 -0
- package/pipeline/scripts/agent-guard.py +79 -19
- package/pipeline/scripts/anonymize-findings.mjs +99 -0
- package/pipeline/scripts/answer-check.mjs +113 -0
- package/pipeline/scripts/answer-check.sh +20 -0
- package/pipeline/scripts/autopilot-publish.mjs +9 -2
- package/pipeline/scripts/contract-server.mjs +35 -1
- package/pipeline/scripts/gate-ledger.mjs +144 -21
- package/pipeline/scripts/office-build.mjs +276 -5
- package/pipeline/scripts/office-control.mjs +70 -10
- package/pipeline/scripts/office-pair.mjs +407 -0
- package/pipeline/scripts/office-remote-client.mjs +463 -0
- package/pipeline/scripts/phase-tracker.sh +8 -0
- package/pipeline/scripts/phase0-exit-gate.mjs +40 -37
- package/pipeline/scripts/phone-devices.mjs +9 -1
- package/pipeline/scripts/pre-commit-check.sh +28 -2
- package/pipeline/scripts/pre-push-check.sh +6 -12
- package/pipeline/scripts/rework-base.mjs +87 -0
- package/pipeline/scripts/run-commit-check.mjs +104 -0
- package/pipeline/scripts/run-scope-guard.mjs +58 -3
- package/pipeline/scripts/run-scope-guard.sh +13 -1
- package/pipeline/scripts/scope-check-gate.mjs +70 -6
- package/pipeline/scripts/task-mirror.mjs +106 -15
- package/pipeline/scripts/task-mirror.sh +3 -2
- package/pipeline/scripts/test-stability-gate.mjs +112 -0
- package/pipeline/scripts/update-check.sh +7 -1
- package/pipeline/scripts/update-issue-progress.sh +73 -8
- package/pipeline/scripts/validate-planning.mjs +105 -1
- package/pipeline/scripts/validate-triage.mjs +42 -2
- package/pipeline/scripts/work-items.mjs +119 -1
- package/pipeline/scripts/worktree-prepare-all.mjs +200 -0
- package/pipeline/scripts/worktree-prepare.sh +40 -4
- package/pipeline/scripts/write-state.mjs +67 -24
- package/pipeline/skills/.skill-manifest.json +5 -5
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +5 -5
- package/pipeline/skills/shared/core/google-play-compliance/references/rules.yml +1 -1
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +2 -2
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +14 -0
- package/pipeline/skills/shared/external/android-architecture/references/patterns.md +6 -0
- package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +10 -0
- package/pipeline/skills/shared/external/android-datastore/references/patterns.md +11 -0
- package/pipeline/skills/shared/external/android-design-tokens-codegen/references/patterns.md +8 -0
- package/pipeline/skills/shared/external/android-jetpack-compose-expert/references/patterns.md +6 -0
- package/pipeline/skills/shared/external/android-mvi-viewmodel/references/patterns.md +10 -0
- package/pipeline/skills/shared/external/android-performance/references/patterns.md +13 -0
- package/pipeline/skills/shared/external/android-security/references/patterns.md +16 -0
- package/pipeline/skills/shared/external/api-patterns/references/contract-details.md +9 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/abuse-controls.md +12 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/identity-and-access.md +13 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/request-handling.md +12 -0
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +9 -0
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +8 -0
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +10 -0
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +21 -0
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +9 -0
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +7 -0
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +2 -2
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +10 -0
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +10 -1
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +22 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +17 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +22 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +17 -0
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +9 -0
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +11 -0
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +13 -0
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +9 -0
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +10 -0
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +15 -0
- package/pipeline/skills/shared/external/compose-components/references/patterns.md +7 -0
- package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +7 -0
- package/pipeline/skills/shared/external/compose-testing/references/patterns.md +15 -0
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +9 -0
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +11 -0
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +10 -0
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +13 -0
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +20 -0
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +16 -0
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +16 -0
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +14 -0
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +13 -0
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +10 -0
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +10 -0
- package/pipeline/skills/shared/external/fastapi-pro/references/app-structure.md +10 -0
- package/pipeline/skills/shared/external/gradle-kotlin-dsl/references/patterns.md +17 -0
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +12 -0
- package/pipeline/skills/shared/external/hig-app-icons/references/icon-workflow.md +10 -0
- package/pipeline/skills/shared/external/hig-components-content/references/content-views.md +10 -0
- package/pipeline/skills/shared/external/hig-components-layout/references/bars-and-tabs.md +11 -0
- package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +13 -0
- package/pipeline/skills/shared/external/hig-foundations/references/type-color-spacing.md +13 -0
- package/pipeline/skills/shared/external/hig-patterns/references/onboarding-and-states.md +11 -0
- package/pipeline/skills/shared/external/hig-platforms/references/ipad-layout.md +12 -0
- package/pipeline/skills/shared/external/hig-technologies/references/technologies.md +16 -0
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +9 -0
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +18 -0
- package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md +9 -0
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +9 -0
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +7 -0
- package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +13 -0
- package/pipeline/skills/shared/external/ios-design-review/references/appearance-matrix.md +9 -0
- package/pipeline/skills/shared/external/ios-design-review/references/checklist.md +15 -0
- package/pipeline/skills/shared/external/ios-haptics/references/core-haptics.md +11 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/references/api-map.md +11 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/references/fixed-assumptions.md +15 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/references/poses-and-panes.md +11 -0
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +18 -0
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +19 -0
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +15 -0
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +7 -0
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +8 -0
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +12 -0
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +17 -0
- package/pipeline/skills/shared/external/ios-security/references/app-review-guidelines.md +8 -0
- package/pipeline/skills/shared/external/ios-security/references/cryptokit-advanced.md +14 -0
- package/pipeline/skills/shared/external/ios-security/references/file-storage-patterns.md +8 -0
- package/pipeline/skills/shared/external/ios-security/references/privacy-manifest.md +9 -0
- package/pipeline/skills/shared/external/ios-sf-symbols/references/effects-and-custom-symbols.md +9 -0
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +13 -0
- package/pipeline/skills/shared/external/kotlin-coroutines-expert/references/patterns.md +8 -0
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +21 -0
- package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +9 -0
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +12 -0
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +16 -0
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +21 -0
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +10 -0
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +9 -0
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +12 -0
- package/pipeline/skills/shared/external/nodejs-backend-patterns/references/runtime-patterns.md +11 -0
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +11 -0
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +18 -0
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +11 -0
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +11 -0
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +15 -0
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +14 -0
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +14 -0
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +14 -0
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +14 -0
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +10 -0
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +12 -0
- package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +14 -0
- package/pipeline/skills/shared/external/room-database/references/patterns.md +16 -0
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +10 -0
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +10 -0
- package/pipeline/skills/shared/external/spm-build-analysis/references/build-optimization-sources.md +14 -0
- package/pipeline/skills/shared/external/spm-build-analysis/references/spm-analysis-checks.md +19 -0
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +13 -0
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +31 -0
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +8 -0
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +9 -0
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +10 -0
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +8 -0
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +24 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +8 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +14 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +12 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +10 -0
- package/pipeline/skills/shared/external/swift-concurrency-expert/references/swift-6-2-concurrency.md +8 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/actors.md +10 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/cancellation.md +7 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/interop.md +10 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/new-features.md +11 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/structured.md +9 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/testing.md +13 -0
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +7 -0
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +14 -0
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +16 -0
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +17 -0
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +19 -0
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +16 -0
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +19 -0
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +18 -0
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +16 -0
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +17 -0
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +17 -0
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +17 -0
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +18 -0
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +21 -0
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +16 -0
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +16 -0
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +9 -0
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +24 -0
- package/pipeline/skills/shared/external/swift-testing-pro/references/async-tests.md +9 -0
- package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +11 -0
- package/pipeline/skills/shared/external/swift-testing-pro/references/writing-better-tests.md +13 -0
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +8 -0
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +17 -0
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +12 -0
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +11 -0
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +7 -0
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +13 -0
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +24 -0
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +12 -0
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +15 -0
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +15 -0
- package/pipeline/skills/shared/external/swiftui-animation/references/scroll-effects.md +12 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/accessibility-patterns.md +11 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +7 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +13 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +9 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts-accessibility.md +9 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts.md +17 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/image-optimization.md +9 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/latest-apis.md +10 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/layout-best-practices.md +12 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/list-patterns.md +12 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-scenes.md +13 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-views.md +12 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-window-styling.md +12 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/performance-patterns.md +7 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/scroll-patterns.md +9 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/sheet-navigation-patterns.md +10 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/state-management.md +18 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/view-structure.md +16 -0
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +11 -0
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +8 -0
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +14 -0
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +9 -0
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +22 -0
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/uikit-glass.md +14 -0
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +10 -0
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +10 -0
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +13 -0
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +10 -0
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +14 -0
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +30 -0
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +16 -0
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +11 -0
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +11 -0
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +10 -0
- package/pipeline/skills/shared/external/swiftui-performance-audit/references/code-smells.md +8 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/app-wiring.md +12 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/menu-bar.md +10 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/navigationstack.md +11 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/scroll-reveal.md +12 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/sheets.md +14 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/tabview.md +10 -0
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +10 -0
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +13 -0
- package/pipeline/skills/shared/external/swiftui-view-refactor/references/mv-patterns.md +12 -0
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +8 -0
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +8 -0
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +14 -0
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +17 -0
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +11 -0
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +11 -0
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +27 -0
- package/pipeline/skills/shared/external/xcode-build-fixer/references/build-settings-best-practices.md +9 -0
- package/pipeline/skills/shared/external/xcode-build-fixer/references/fix-patterns.md +7 -0
- package/pipeline/skills/shared/external/xcode-build-orchestrator/references/build-settings-best-practices.md +9 -0
- package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/build-optimization-sources.md +14 -0
- package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/code-compilation-checks.md +8 -0
- package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-optimization-sources.md +14 -0
- package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-settings-best-practices.md +9 -0
- package/pipeline/skills/shared/external/xcode-project-analyzer/references/project-audit-checks.md +13 -0
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
* pipeline/office/index.html: the list of sprite names in pipeline/office/assets
|
|
10
10
|
* (served by contract-server.mjs at /office/assets/), the strings as a JSON
|
|
11
11
|
* literal, the two code files in order inside one IIFE. It also renders the
|
|
12
|
-
* pixel grids in pipeline/office-art into PNG sprites in pipeline/office/assets
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* pixel grids in pipeline/office-art into PNG sprites in pipeline/office/assets,
|
|
13
|
+
* recolors the character sheets into the company's employees
|
|
14
|
+
* (office-art/employees.json) and writes pipeline/office/toolkit.json, the
|
|
15
|
+
* toolkit version shipped with the page; --check holds all of them to their
|
|
16
|
+
* sources.
|
|
15
17
|
*
|
|
16
18
|
* node pipeline/scripts/office-build.mjs write the page
|
|
17
19
|
* node pipeline/scripts/office-build.mjs --check exit 1 when the page on
|
|
@@ -21,7 +23,7 @@
|
|
|
21
23
|
|
|
22
24
|
import { createHash } from "node:crypto";
|
|
23
25
|
import { readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
24
|
-
import { deflateSync } from "node:zlib";
|
|
26
|
+
import { deflateSync, inflateSync } from "node:zlib";
|
|
25
27
|
import { basename, join } from "node:path";
|
|
26
28
|
import { pathToFileURL } from "node:url";
|
|
27
29
|
|
|
@@ -78,6 +80,272 @@ export function encodePng(width, height, rgba) {
|
|
|
78
80
|
]);
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
/**
|
|
84
|
+
* An 8-bit RGBA PNG decoded to {width, height, rgba}. Only that colour type is
|
|
85
|
+
* read (every sheet in office/assets is one); anything else throws.
|
|
86
|
+
*/
|
|
87
|
+
export function decodePng(buf) {
|
|
88
|
+
if (buf.subarray(0, 8).toString("hex") !== PNG_SIGNATURE) throw new Error("not a PNG file");
|
|
89
|
+
let o = 8;
|
|
90
|
+
let width = 0;
|
|
91
|
+
let height = 0;
|
|
92
|
+
const idat = [];
|
|
93
|
+
while (o + 8 <= buf.length) {
|
|
94
|
+
const len = buf.readUInt32BE(o);
|
|
95
|
+
const type = buf.toString("ascii", o + 4, o + 8);
|
|
96
|
+
const data = buf.subarray(o + 8, o + 8 + len);
|
|
97
|
+
if (type === "IHDR") {
|
|
98
|
+
width = data.readUInt32BE(0);
|
|
99
|
+
height = data.readUInt32BE(4);
|
|
100
|
+
if (data[8] !== 8 || data[9] !== 6 || data[12] !== 0)
|
|
101
|
+
throw new Error("only 8-bit RGBA, non-interlaced PNGs are read");
|
|
102
|
+
} else if (type === "IDAT") idat.push(data);
|
|
103
|
+
else if (type === "IEND") break;
|
|
104
|
+
o += 12 + len;
|
|
105
|
+
}
|
|
106
|
+
if (!width || !height) throw new Error("PNG has no IHDR");
|
|
107
|
+
const raw = inflateSync(Buffer.concat(idat));
|
|
108
|
+
const stride = width * 4;
|
|
109
|
+
if (raw.length !== (stride + 1) * height) throw new Error("PNG data has the wrong length");
|
|
110
|
+
const rgba = Buffer.alloc(stride * height);
|
|
111
|
+
for (let y = 0; y < height; y++) {
|
|
112
|
+
const filter = raw[y * (stride + 1)];
|
|
113
|
+
const line = y * (stride + 1) + 1;
|
|
114
|
+
for (let x = 0; x < stride; x++) {
|
|
115
|
+
const a = x >= 4 ? rgba[y * stride + x - 4] : 0;
|
|
116
|
+
const b = y > 0 ? rgba[(y - 1) * stride + x] : 0;
|
|
117
|
+
const c = x >= 4 && y > 0 ? rgba[(y - 1) * stride + x - 4] : 0;
|
|
118
|
+
let v = raw[line + x];
|
|
119
|
+
if (filter === 1) v += a;
|
|
120
|
+
else if (filter === 2) v += b;
|
|
121
|
+
else if (filter === 3) v += (a + b) >> 1;
|
|
122
|
+
else if (filter === 4) {
|
|
123
|
+
const p = a + b - c;
|
|
124
|
+
const pa = Math.abs(p - a);
|
|
125
|
+
const pb = Math.abs(p - b);
|
|
126
|
+
const pc = Math.abs(p - c);
|
|
127
|
+
v += pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
|
|
128
|
+
} else if (filter !== 0) throw new Error(`unknown PNG filter ${filter}`);
|
|
129
|
+
rgba[y * stride + x] = v & 255;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return { width, height, rgba };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// --- the employees -----------------------------------------------------------
|
|
136
|
+
// office-art/employees.json gives each person of the company a base character
|
|
137
|
+
// sheet and four colours. A sheet's colours are sorted into parts by where
|
|
138
|
+
// they sit in the 16x32 frame (hair above the face, the shirt below it, then
|
|
139
|
+
// trousers and shoes) and skin by the face's tone; the near-black outline,
|
|
140
|
+
// white, the eyes and the shoes keep their colours. Each part moves to the
|
|
141
|
+
// person's colour with its shading kept.
|
|
142
|
+
|
|
143
|
+
const FRAME_H = 32;
|
|
144
|
+
const BANDS = [
|
|
145
|
+
["hair", 0, 13],
|
|
146
|
+
["face", 13, 18],
|
|
147
|
+
["top", 18, 25],
|
|
148
|
+
["bottom", 25, 28],
|
|
149
|
+
["shoes", 28, 32],
|
|
150
|
+
];
|
|
151
|
+
const PARTS = ["hair", "skin", "top", "bottom"];
|
|
152
|
+
const SKIN_NEAR = 48;
|
|
153
|
+
|
|
154
|
+
const channels = (hex) => [0, 2, 4].map((i) => parseInt(hex.slice(i, i + 2), 16));
|
|
155
|
+
|
|
156
|
+
function hsl(r, g, b) {
|
|
157
|
+
const [R, G, B] = [r / 255, g / 255, b / 255];
|
|
158
|
+
const max = Math.max(R, G, B);
|
|
159
|
+
const min = Math.min(R, G, B);
|
|
160
|
+
const l = (max + min) / 2;
|
|
161
|
+
const d = max - min;
|
|
162
|
+
if (!d) return [0, 0, l];
|
|
163
|
+
const s = d / (1 - Math.abs(2 * l - 1));
|
|
164
|
+
const h = max === R ? ((G - B) / d + 6) % 6 : max === G ? (B - R) / d + 2 : (R - G) / d + 4;
|
|
165
|
+
return [h * 60, s, l];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function rgbOf(h, s, l) {
|
|
169
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
170
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
171
|
+
const m = l - c / 2;
|
|
172
|
+
const sector = [
|
|
173
|
+
[c, x, 0],
|
|
174
|
+
[x, c, 0],
|
|
175
|
+
[0, c, x],
|
|
176
|
+
[0, x, c],
|
|
177
|
+
[x, 0, c],
|
|
178
|
+
[c, 0, x],
|
|
179
|
+
][Math.min(5, Math.floor(h / 60))];
|
|
180
|
+
return sector.map((v) => Math.round(Math.min(1, Math.max(0, v + m)) * 255));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** The band of a sheet row: hair, face, top, bottom or shoes, by its place in the 32-row frame. */
|
|
184
|
+
export function bandOf(y) {
|
|
185
|
+
return BANDS.find(([, a, b]) => y % FRAME_H >= a && y % FRAME_H < b)[0];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* The middle of the face where it faces the viewer: the first row of frames
|
|
190
|
+
* (the front view; the second shows the back of the head), the face rows,
|
|
191
|
+
* away from the sides long hair covers and the collar rises into.
|
|
192
|
+
*/
|
|
193
|
+
export function inFaceCentre(x, y) {
|
|
194
|
+
return y >= 13 && y < 18 && x % 16 >= 5 && x % 16 <= 10;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The part of every pixel of a sheet: hair, skin, top, bottom, keep, or null
|
|
199
|
+
* for a transparent one. A pixel is judged by its colour and its row:
|
|
200
|
+
* - near-black outline and white keep their colour everywhere;
|
|
201
|
+
* - the face's tone is skin wherever it is (neck, arms, hands, legs);
|
|
202
|
+
* - the hair rows are hair, the dark strand lines inside it included;
|
|
203
|
+
* - the face rows are skin, hair (a colour of the hair rows: long hair at the
|
|
204
|
+
* sides) or kept (eyes, mouth, the face's outline), never clothing;
|
|
205
|
+
* - the body rows are the top, except a hair colour (a braid over the
|
|
206
|
+
* shoulder), the leg rows are the bottom, and the shoes keep theirs.
|
|
207
|
+
*/
|
|
208
|
+
export function pixelParts(sheet) {
|
|
209
|
+
const { width, height, rgba } = sheet;
|
|
210
|
+
const seen = new Map();
|
|
211
|
+
for (let y = 0; y < height; y++) {
|
|
212
|
+
const band = bandOf(y);
|
|
213
|
+
for (let x = 0; x < width; x++) {
|
|
214
|
+
const o = (y * width + x) * 4;
|
|
215
|
+
if (!rgba[o + 3]) continue;
|
|
216
|
+
const hex = rgba.subarray(o, o + 3).toString("hex");
|
|
217
|
+
const counts = seen.get(hex) || {
|
|
218
|
+
hair: 0,
|
|
219
|
+
face: 0,
|
|
220
|
+
top: 0,
|
|
221
|
+
bottom: 0,
|
|
222
|
+
shoes: 0,
|
|
223
|
+
centre: 0,
|
|
224
|
+
};
|
|
225
|
+
counts[band]++;
|
|
226
|
+
if (inFaceCentre(x, y)) counts.centre++;
|
|
227
|
+
seen.set(hex, counts);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const info = new Map();
|
|
231
|
+
for (const [hex, counts] of seen) {
|
|
232
|
+
const rgb = channels(hex);
|
|
233
|
+
const [h, s, l] = hsl(...rgb);
|
|
234
|
+
const n = counts.hair + counts.face + counts.top + counts.bottom + counts.shoes;
|
|
235
|
+
const [band] = BANDS.map(([name]) => [name, counts[name]]).sort((p, q) => q[1] - p[1])[0];
|
|
236
|
+
info.set(hex, {
|
|
237
|
+
rgb,
|
|
238
|
+
counts,
|
|
239
|
+
n,
|
|
240
|
+
band,
|
|
241
|
+
fixed: Math.max(...rgb) < 0x18 || Math.min(...rgb) > 0xf8,
|
|
242
|
+
skinLike: h >= 8 && h <= 45 && s >= 0.25 && l >= 0.15,
|
|
243
|
+
h,
|
|
244
|
+
s,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
// The face is the skin-like colours of its middle, where no hair hangs, that
|
|
248
|
+
// the hair rows use less (a forehead under the fringe counts). Skin elsewhere
|
|
249
|
+
// is a colour the hair rows never use that sits next to a face colour (the
|
|
250
|
+
// shading of arms and hands); a hair highlight of a skin-like hue lives in the
|
|
251
|
+
// hair rows, and a clothing colour of a skin-like hue is far from the face or
|
|
252
|
+
// lives below the waist. Hair never reaches the middle of the face.
|
|
253
|
+
const faceTones = [...info.values()].filter(
|
|
254
|
+
(c) => c.skinLike && c.counts.centre > 0 && c.counts.centre * 2 >= c.counts.hair,
|
|
255
|
+
);
|
|
256
|
+
const near = (c, f) =>
|
|
257
|
+
Math.abs(c.s - f.s) <= 0.3 && c.rgb.every((v, i) => Math.abs(v - f.rgb[i]) <= SKIN_NEAR);
|
|
258
|
+
const skin = (c) =>
|
|
259
|
+
faceTones.includes(c) ||
|
|
260
|
+
(c.skinLike && c.counts.hair === 0 && c.band !== "bottom" && faceTones.some((f) => near(c, f)));
|
|
261
|
+
const hairColour = (c) => c.counts.hair > 0 && c.counts.hair * 2 >= c.n - c.counts.hair;
|
|
262
|
+
const out = new Array(width * height).fill(null);
|
|
263
|
+
for (let y = 0; y < height; y++) {
|
|
264
|
+
const band = bandOf(y);
|
|
265
|
+
for (let x = 0; x < width; x++) {
|
|
266
|
+
const o = (y * width + x) * 4;
|
|
267
|
+
if (!rgba[o + 3]) continue;
|
|
268
|
+
const c = info.get(rgba.subarray(o, o + 3).toString("hex"));
|
|
269
|
+
let part;
|
|
270
|
+
if (c.fixed) part = "keep";
|
|
271
|
+
else if (skin(c)) part = "skin";
|
|
272
|
+
else if (band === "hair") part = "hair";
|
|
273
|
+
else if (band === "face") part = hairColour(c) && !inFaceCentre(x, y) ? "hair" : "keep";
|
|
274
|
+
else if (band === "top") part = c.band === "hair" ? "hair" : "top";
|
|
275
|
+
else if (band === "bottom") part = "bottom";
|
|
276
|
+
else part = "keep";
|
|
277
|
+
out[y * width + x] = part;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** A part's target lightness for a source lightness: a monotone ramp around the part's mean. */
|
|
284
|
+
function rampOf(l, mean, target) {
|
|
285
|
+
if (l <= mean) return target * (l / mean);
|
|
286
|
+
const top = Math.min(0.92, target + 0.35);
|
|
287
|
+
return target + (top - target) * ((l - mean) / (1 - mean));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* A character sheet in one person's colours: {hair, skin, top, bottom} as
|
|
292
|
+
* "rrggbb". Returns {width, height, rgba, parts}, parts being pixelParts.
|
|
293
|
+
*/
|
|
294
|
+
export function recolorSheet(sheet, colours) {
|
|
295
|
+
const parts = pixelParts(sheet);
|
|
296
|
+
const { rgba } = sheet;
|
|
297
|
+
const sum = {};
|
|
298
|
+
const n = {};
|
|
299
|
+
for (let i = 0; i < parts.length; i++) {
|
|
300
|
+
const p = parts[i];
|
|
301
|
+
if (!PARTS.includes(p)) continue;
|
|
302
|
+
const [, , l] = hsl(rgba[i * 4], rgba[i * 4 + 1], rgba[i * 4 + 2]);
|
|
303
|
+
sum[p] = (sum[p] || 0) + l;
|
|
304
|
+
n[p] = (n[p] || 0) + 1;
|
|
305
|
+
}
|
|
306
|
+
const targets = {};
|
|
307
|
+
for (const p of PARTS) {
|
|
308
|
+
if (!n[p]) continue;
|
|
309
|
+
if (!/^[0-9a-f]{6}$/i.test(colours[p] || "")) throw new Error(`no ${p} colour`);
|
|
310
|
+
targets[p] = hsl(...channels(colours[p]));
|
|
311
|
+
}
|
|
312
|
+
const out = Buffer.from(rgba);
|
|
313
|
+
const memo = new Map();
|
|
314
|
+
for (let i = 0; i < parts.length; i++) {
|
|
315
|
+
const p = parts[i];
|
|
316
|
+
if (!PARTS.includes(p)) continue;
|
|
317
|
+
const o = i * 4;
|
|
318
|
+
const key = p + rgba.subarray(o, o + 3).toString("hex");
|
|
319
|
+
let to = memo.get(key);
|
|
320
|
+
if (!to) {
|
|
321
|
+
const [th, ts, tl] = targets[p];
|
|
322
|
+
const [, , l] = hsl(rgba[o], rgba[o + 1], rgba[o + 2]);
|
|
323
|
+
to = rgbOf(th, ts, Math.min(0.94, Math.max(0.03, rampOf(l, sum[p] / n[p], tl))));
|
|
324
|
+
memo.set(key, to);
|
|
325
|
+
}
|
|
326
|
+
[out[o], out[o + 1], out[o + 2]] = to;
|
|
327
|
+
}
|
|
328
|
+
return { width: sheet.width, height: sheet.height, rgba: out, parts };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** The employees as the page reads them, and their sheets: "char_<slot>" -> PNG bytes. */
|
|
332
|
+
export function employees() {
|
|
333
|
+
const { employees: list } = JSON.parse(readFileSync(join(ART, "employees.json"), "utf8"));
|
|
334
|
+
const sprites = new Map();
|
|
335
|
+
const slots = new Set();
|
|
336
|
+
const people = list.map((e) => {
|
|
337
|
+
if (!Number.isInteger(e.slot) || e.slot < 12 || e.slot > 99 || slots.has(e.slot))
|
|
338
|
+
throw new Error(`${e.role}: slot must be a unique integer from 12 to 99`);
|
|
339
|
+
if (!Number.isInteger(e.base) || e.base < 0 || e.base > 5)
|
|
340
|
+
throw new Error(`${e.role}: base is a character sheet from 0 to 5`);
|
|
341
|
+
slots.add(e.slot);
|
|
342
|
+
const sheet = recolorSheet(decodePng(readFileSync(join(ASSETS, `char_${e.base}.png`))), e);
|
|
343
|
+
sprites.set(`char_${e.slot}`, encodePng(sheet.width, sheet.height, sheet.rgba));
|
|
344
|
+
return { role: e.role, name: e.name, agent: e.agent, tier: e.tier, slot: e.slot };
|
|
345
|
+
});
|
|
346
|
+
return { people, sprites };
|
|
347
|
+
}
|
|
348
|
+
|
|
81
349
|
/** One art file as a PNG; throws on a size line, row length or letter that does not fit. */
|
|
82
350
|
export function renderArt(text, colors, name = "sprite") {
|
|
83
351
|
const lines = text.replace(/\r/g, "").trimEnd().split("\n");
|
|
@@ -150,11 +418,13 @@ export function build() {
|
|
|
150
418
|
const read = (f) => readFileSync(join(SRC, f), "utf8");
|
|
151
419
|
const strings = JSON.parse(read("strings.json"));
|
|
152
420
|
const sprites = atlas();
|
|
421
|
+
const staff = employees();
|
|
153
422
|
const script = [
|
|
154
423
|
'"use strict";',
|
|
155
424
|
"(() => {",
|
|
156
425
|
`const ATLAS = ${JSON.stringify(sprites)};`,
|
|
157
426
|
`const STRINGS = ${JSON.stringify(strings)};`,
|
|
427
|
+
`const EMPLOYEES = ${JSON.stringify(staff.people)};`,
|
|
158
428
|
'const spriteUrl = (name) => "/office/assets/" + name + ".png";',
|
|
159
429
|
read("world.js").trim(),
|
|
160
430
|
read("app.js").trim(),
|
|
@@ -166,6 +436,7 @@ export function build() {
|
|
|
166
436
|
const hash = createHash("sha256").update(script).update(style).update(read("shell.html"));
|
|
167
437
|
// Redrawn sprites change the page too, though no source text did.
|
|
168
438
|
for (const [name, png] of artSprites()) hash.update(name).update(png);
|
|
439
|
+
for (const [name, png] of staff.sprites) hash.update(name).update(png);
|
|
169
440
|
const build = hash.digest("hex").slice(0, 12);
|
|
170
441
|
// Text that would close the inline element early, or open an HTML comment
|
|
171
442
|
// inside it, turns source into markup.
|
|
@@ -181,7 +452,7 @@ export function build() {
|
|
|
181
452
|
|
|
182
453
|
const isMain = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
183
454
|
if (isMain) {
|
|
184
|
-
const art = artSprites();
|
|
455
|
+
const art = new Map([...artSprites(), ...employees().sprites]);
|
|
185
456
|
const check = process.argv.includes("--check");
|
|
186
457
|
if (!check) for (const [name, png] of art) writeFileSync(join(ASSETS, `${name}.png`), png);
|
|
187
458
|
const html = build();
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
* status running or not, the URL without the token, the idle
|
|
24
24
|
* limit, remote access, the paired devices the phone
|
|
25
25
|
* registry lists, and the last panel actions from
|
|
26
|
-
* office-actions.jsonl. Remote access is "
|
|
27
|
-
*
|
|
28
|
-
* prefs.global.office.remote
|
|
29
|
-
*
|
|
30
|
-
*
|
|
26
|
+
* office-actions.jsonl. Remote access is "off" until
|
|
27
|
+
* office-pair pairs a phone, "configured" when
|
|
28
|
+
* prefs.global.office.remote is set but nothing is paired,
|
|
29
|
+
* and "paired" once a device is; while paired, office-on
|
|
30
|
+
* starts office-remote-client.mjs beside the server.
|
|
31
31
|
*
|
|
32
32
|
* State: one 0600 file, ~/.claude/logs/multi-agent/office/office.json, with
|
|
33
33
|
* the pid, port and token file of the server this script started. The server
|
|
@@ -58,6 +58,7 @@ import { homedir } from "node:os";
|
|
|
58
58
|
import { dirname, join } from "node:path";
|
|
59
59
|
|
|
60
60
|
import { codeDigest } from "../lib/code-digest.mjs";
|
|
61
|
+
import { phoneDir } from "./_phone-auth.mjs";
|
|
61
62
|
import { fileURLToPath } from "node:url";
|
|
62
63
|
|
|
63
64
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
@@ -72,6 +73,7 @@ const START_SKEW_MS = 120 * 1000;
|
|
|
72
73
|
const home = (env) => env.HOME || homedir();
|
|
73
74
|
const stateDir = (env) => join(home(env), ".claude", "logs", "multi-agent", "office");
|
|
74
75
|
const stateFile = (env) => join(stateDir(env), "office.json");
|
|
76
|
+
const clientFile = (env) => join(stateDir(env), "remote-client.json");
|
|
75
77
|
const actionsLog = (env) =>
|
|
76
78
|
join(home(env), ".claude", "logs", "multi-agent", "office-actions.jsonl");
|
|
77
79
|
|
|
@@ -86,16 +88,22 @@ const readJson = (p) => {
|
|
|
86
88
|
/** The panel page: next to this scripts directory, in a checkout and in the install. */
|
|
87
89
|
export const pagePath = () => join(HERE, "..", "office", "index.html");
|
|
88
90
|
|
|
89
|
-
/** Whether a
|
|
91
|
+
/** Whether a device is paired for remote use (office-pair wrote the remote config). */
|
|
92
|
+
export const remotePaired = (env = process.env) => existsSync(join(phoneDir(env), "remote.json"));
|
|
93
|
+
|
|
94
|
+
/** Whether the remote layer is configured: a paired device, or a prefs block. */
|
|
90
95
|
export function remoteLayer(env = process.env) {
|
|
96
|
+
if (remotePaired(env)) return true;
|
|
91
97
|
const prefs = readJson(join(home(env), ".claude", "multi-agent-preferences.json"));
|
|
92
98
|
const block = prefs?.global?.office?.remote;
|
|
93
|
-
|
|
94
|
-
return existsSync(join(home(env), ".claude", "office-remote"));
|
|
99
|
+
return Boolean(block && typeof block === "object" && !Array.isArray(block));
|
|
95
100
|
}
|
|
96
101
|
|
|
97
|
-
const remoteText = (env) =>
|
|
98
|
-
|
|
102
|
+
const remoteText = (env) => {
|
|
103
|
+
if (remotePaired(env)) return "paired: the remote client runs while the office is on";
|
|
104
|
+
if (remoteLayer(env)) return "configured, no phone paired (office-pair start)";
|
|
105
|
+
return "off: no phone paired";
|
|
106
|
+
};
|
|
99
107
|
|
|
100
108
|
/** The contract server: beside this script, else the install. */
|
|
101
109
|
export function serverScript(env = process.env) {
|
|
@@ -127,6 +135,55 @@ function psField(pid, field) {
|
|
|
127
135
|
return r.status === 0 ? r.stdout.trim() : "";
|
|
128
136
|
}
|
|
129
137
|
|
|
138
|
+
/** The remote client this script started, if it is still running. */
|
|
139
|
+
function remoteClient(env) {
|
|
140
|
+
const c = readJson(clientFile(env));
|
|
141
|
+
if (!c || !Number.isInteger(c.pid) || !alive(c.pid)) return null;
|
|
142
|
+
return psField(c.pid, "command").includes("office-remote-client.mjs") ? c : null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Start office-remote-client.mjs beside the server when a device is paired,
|
|
147
|
+
* or keep the one already running. Without a pairing nothing starts, so
|
|
148
|
+
* remote stays off by default; the client also stops by itself once the
|
|
149
|
+
* server is gone.
|
|
150
|
+
*/
|
|
151
|
+
export function startRemoteClient(env = process.env) {
|
|
152
|
+
if (!remotePaired(env)) return false;
|
|
153
|
+
if (remoteClient(env)) return true;
|
|
154
|
+
const script = join(HERE, "office-remote-client.mjs");
|
|
155
|
+
if (!existsSync(script)) return false;
|
|
156
|
+
const child = spawn(process.execPath, [script, "loop"], {
|
|
157
|
+
env,
|
|
158
|
+
detached: true,
|
|
159
|
+
stdio: "ignore",
|
|
160
|
+
});
|
|
161
|
+
child.unref();
|
|
162
|
+
mkdirSync(stateDir(env), { recursive: true, mode: 0o700 });
|
|
163
|
+
writeFileSync(
|
|
164
|
+
clientFile(env),
|
|
165
|
+
JSON.stringify({ pid: child.pid, startedAt: new Date().toISOString() }) + "\n",
|
|
166
|
+
{ mode: 0o600 },
|
|
167
|
+
);
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Stop the remote client this script started. */
|
|
172
|
+
export function stopRemoteClient(env = process.env) {
|
|
173
|
+
const c = remoteClient(env);
|
|
174
|
+
let stopped = false;
|
|
175
|
+
if (c) {
|
|
176
|
+
try {
|
|
177
|
+
process.kill(c.pid, "SIGTERM");
|
|
178
|
+
stopped = true;
|
|
179
|
+
} catch {
|
|
180
|
+
stopped = false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
rmSync(clientFile(env), { force: true });
|
|
184
|
+
return stopped;
|
|
185
|
+
}
|
|
186
|
+
|
|
130
187
|
/**
|
|
131
188
|
* Whether `pid` is still the server a state record names: its token file
|
|
132
189
|
* names the same pid, the process runs contract-server.mjs, and it started
|
|
@@ -313,6 +370,7 @@ export async function officeOn({ env = process.env, open = true } = {}) {
|
|
|
313
370
|
if (!s) throw new Error("the server stopped right after it started");
|
|
314
371
|
}
|
|
315
372
|
const page = `${s.url}/office`;
|
|
373
|
+
const remote = startRemoteClient(env);
|
|
316
374
|
const opened = open ? openBrowser(`${page}#code=${await panelCode(s)}`, env) : false;
|
|
317
375
|
return {
|
|
318
376
|
running: true,
|
|
@@ -321,12 +379,14 @@ export async function officeOn({ env = process.env, open = true } = {}) {
|
|
|
321
379
|
url: page,
|
|
322
380
|
pid: s.pid,
|
|
323
381
|
idleMinutes: s.idleMinutes,
|
|
382
|
+
remote,
|
|
324
383
|
opened,
|
|
325
384
|
};
|
|
326
385
|
}
|
|
327
386
|
|
|
328
387
|
/** Stop the server this script started. */
|
|
329
388
|
export function officeOff({ env = process.env } = {}) {
|
|
389
|
+
stopRemoteClient(env);
|
|
330
390
|
const s = readJson(stateFile(env));
|
|
331
391
|
let stopped = false;
|
|
332
392
|
if (isOurServer(s, s ? readJson(s.tokenFile ?? "") : null)) {
|