@mmerterden/multi-agent-pipeline 20.34.0 → 20.36.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.
Files changed (335) hide show
  1. package/CHANGELOG.md +69 -1
  2. package/README.md +7 -5
  3. package/README.tr.md +6 -5
  4. package/docs/architecture.md +3 -3
  5. package/docs/ecosystem.md +5 -5
  6. package/docs/facts.json +4 -4
  7. package/install/catalog-history.json +1 -1
  8. package/install/claude.mjs +16 -1
  9. package/install/templates/claude-hooks.json +23 -1
  10. package/manifest.json +338 -305
  11. package/package.json +5 -1
  12. package/pipeline/agents/bulk-reader.md +3 -0
  13. package/pipeline/commands/multi-agent/channels/SKILL.md +1 -1
  14. package/pipeline/commands/multi-agent/office-on/SKILL.md +7 -3
  15. package/pipeline/commands/multi-agent/office-pair/SKILL.md +66 -0
  16. package/pipeline/commands/multi-agent/office-status/SKILL.md +3 -2
  17. package/pipeline/commands/multi-agent/sync/SKILL.md +3 -3
  18. package/pipeline/contract/CHANGELOG.md +9 -0
  19. package/pipeline/contract/README.md +1 -0
  20. package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
  21. package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
  22. package/pipeline/contract/fixtures/error-unsigned.json +1 -1
  23. package/pipeline/contract/manifest.json +14 -1
  24. package/pipeline/contract/types/index.d.ts +24 -2
  25. package/pipeline/lib/json-file-lock.mjs +61 -15
  26. package/pipeline/lib/office-remote.mjs +228 -0
  27. package/pipeline/lib/qr-code.mjs +386 -0
  28. package/pipeline/lib/repo-hygiene.sh +1 -0
  29. package/pipeline/lib/run-completion.mjs +17 -0
  30. package/pipeline/multi-agent-refs/channels/issue-comment.md +1 -1
  31. package/pipeline/multi-agent-refs/cross-cli-contract.md +4 -4
  32. package/pipeline/multi-agent-refs/features/office-remote-protocol.md +166 -0
  33. package/pipeline/multi-agent-refs/features/office-threat-model.md +1 -1
  34. package/pipeline/multi-agent-refs/features/scope-check.md +16 -1
  35. package/pipeline/multi-agent-refs/features/verify-by-test.md +10 -0
  36. package/pipeline/multi-agent-refs/help/en.md +1 -0
  37. package/pipeline/multi-agent-refs/help/tr.md +1 -0
  38. package/pipeline/multi-agent-refs/keychain.md +7 -0
  39. package/pipeline/multi-agent-refs/phases/phase-0-init.md +6 -2
  40. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +4 -6
  41. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +9 -12
  42. package/pipeline/multi-agent-refs/phases/phase-3-review.md +9 -8
  43. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +1 -4
  44. package/pipeline/multi-agent-refs/rules.md +3 -2
  45. package/pipeline/multi-agent-refs/tracker-contract.md +1 -1
  46. package/pipeline/office/NOTICE.md +4 -1
  47. package/pipeline/office/assets/char_12.png +0 -0
  48. package/pipeline/office/assets/char_13.png +0 -0
  49. package/pipeline/office/assets/char_14.png +0 -0
  50. package/pipeline/office/assets/char_15.png +0 -0
  51. package/pipeline/office/assets/char_16.png +0 -0
  52. package/pipeline/office/assets/char_17.png +0 -0
  53. package/pipeline/office/assets/char_18.png +0 -0
  54. package/pipeline/office/assets/char_19.png +0 -0
  55. package/pipeline/office/assets/char_20.png +0 -0
  56. package/pipeline/office/assets/char_21.png +0 -0
  57. package/pipeline/office/assets/char_22.png +0 -0
  58. package/pipeline/office/assets/char_23.png +0 -0
  59. package/pipeline/office/assets/char_24.png +0 -0
  60. package/pipeline/office/assets/char_25.png +0 -0
  61. package/pipeline/office/assets/char_26.png +0 -0
  62. package/pipeline/office/assets/char_27.png +0 -0
  63. package/pipeline/office/assets/char_28.png +0 -0
  64. package/pipeline/office/assets/char_29.png +0 -0
  65. package/pipeline/office/assets/char_30.png +0 -0
  66. package/pipeline/office/index.html +293 -49
  67. package/pipeline/schemas/agent-state.schema.json +18 -0
  68. package/pipeline/schemas/planning-output.schema.json +18 -0
  69. package/pipeline/schemas/pr-reach.schema.json +55 -0
  70. package/pipeline/schemas/prefs.schema.json +13 -7
  71. package/pipeline/schemas/triage-output.schema.json +12 -1
  72. package/pipeline/schemas/unattended-policy.json +2 -1
  73. package/pipeline/scripts/_guard_rules.py +449 -1
  74. package/pipeline/scripts/_test-gaming.mjs +36 -0
  75. package/pipeline/scripts/agent-guard.py +79 -19
  76. package/pipeline/scripts/anonymize-findings.mjs +99 -0
  77. package/pipeline/scripts/answer-check.mjs +113 -0
  78. package/pipeline/scripts/answer-check.sh +20 -0
  79. package/pipeline/scripts/autopilot-publish.mjs +9 -2
  80. package/pipeline/scripts/contract-server.mjs +35 -1
  81. package/pipeline/scripts/gate-ledger.mjs +144 -21
  82. package/pipeline/scripts/office-build.mjs +276 -5
  83. package/pipeline/scripts/office-control.mjs +70 -10
  84. package/pipeline/scripts/office-pair.mjs +438 -0
  85. package/pipeline/scripts/office-remote-client.mjs +463 -0
  86. package/pipeline/scripts/phase-tracker.sh +8 -0
  87. package/pipeline/scripts/phase0-exit-gate.mjs +40 -37
  88. package/pipeline/scripts/phone-devices.mjs +9 -1
  89. package/pipeline/scripts/pre-commit-check.sh +28 -2
  90. package/pipeline/scripts/pre-push-check.sh +6 -12
  91. package/pipeline/scripts/rework-base.mjs +87 -0
  92. package/pipeline/scripts/run-commit-check.mjs +104 -0
  93. package/pipeline/scripts/run-scope-guard.mjs +58 -3
  94. package/pipeline/scripts/run-scope-guard.sh +13 -1
  95. package/pipeline/scripts/scope-check-gate.mjs +70 -6
  96. package/pipeline/scripts/task-mirror.mjs +106 -15
  97. package/pipeline/scripts/task-mirror.sh +3 -2
  98. package/pipeline/scripts/test-stability-gate.mjs +112 -0
  99. package/pipeline/scripts/update-check.sh +7 -1
  100. package/pipeline/scripts/update-issue-progress.sh +73 -8
  101. package/pipeline/scripts/validate-planning.mjs +105 -1
  102. package/pipeline/scripts/validate-triage.mjs +42 -2
  103. package/pipeline/scripts/work-items.mjs +119 -1
  104. package/pipeline/scripts/worktree-prepare-all.mjs +200 -0
  105. package/pipeline/scripts/worktree-prepare.sh +40 -4
  106. package/pipeline/scripts/write-state.mjs +67 -24
  107. package/pipeline/skills/.skill-manifest.json +12 -8
  108. package/pipeline/skills/.skills-index.json +15 -4
  109. package/pipeline/skills/shared/README.md +4 -3
  110. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +1 -1
  111. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +6 -6
  112. package/pipeline/skills/shared/core/google-play-compliance/references/rules.yml +1 -1
  113. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
  114. package/pipeline/skills/shared/core/multi-agent-office-pair/SKILL.md +19 -0
  115. package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +2 -2
  116. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +3 -3
  117. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +14 -0
  118. package/pipeline/skills/shared/external/android-architecture/references/patterns.md +6 -0
  119. package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +10 -0
  120. package/pipeline/skills/shared/external/android-datastore/references/patterns.md +11 -0
  121. package/pipeline/skills/shared/external/android-design-tokens-codegen/references/patterns.md +8 -0
  122. package/pipeline/skills/shared/external/android-jetpack-compose-expert/references/patterns.md +6 -0
  123. package/pipeline/skills/shared/external/android-mvi-viewmodel/references/patterns.md +10 -0
  124. package/pipeline/skills/shared/external/android-performance/references/patterns.md +13 -0
  125. package/pipeline/skills/shared/external/android-security/references/patterns.md +16 -0
  126. package/pipeline/skills/shared/external/api-patterns/references/contract-details.md +9 -0
  127. package/pipeline/skills/shared/external/api-security-best-practices/references/abuse-controls.md +12 -0
  128. package/pipeline/skills/shared/external/api-security-best-practices/references/identity-and-access.md +13 -0
  129. package/pipeline/skills/shared/external/api-security-best-practices/references/request-handling.md +12 -0
  130. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +9 -0
  131. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +8 -0
  132. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +10 -0
  133. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +21 -0
  134. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +9 -0
  135. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +7 -0
  136. package/pipeline/skills/shared/external/app-store-review/SKILL.md +2 -2
  137. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +10 -0
  138. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +10 -1
  139. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +22 -0
  140. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +17 -0
  141. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +22 -0
  142. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +17 -0
  143. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +9 -0
  144. package/pipeline/skills/shared/external/authentication/references/passkeys.md +11 -0
  145. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +13 -0
  146. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +9 -0
  147. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +10 -0
  148. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +15 -0
  149. package/pipeline/skills/shared/external/compose-components/references/patterns.md +7 -0
  150. package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +7 -0
  151. package/pipeline/skills/shared/external/compose-testing/references/patterns.md +15 -0
  152. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +9 -0
  153. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +11 -0
  154. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +10 -0
  155. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +13 -0
  156. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +20 -0
  157. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +16 -0
  158. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +16 -0
  159. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +14 -0
  160. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +13 -0
  161. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +10 -0
  162. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +10 -0
  163. package/pipeline/skills/shared/external/fastapi-pro/references/app-structure.md +10 -0
  164. package/pipeline/skills/shared/external/gradle-kotlin-dsl/references/patterns.md +17 -0
  165. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +12 -0
  166. package/pipeline/skills/shared/external/hig-app-icons/references/icon-workflow.md +10 -0
  167. package/pipeline/skills/shared/external/hig-components-content/references/content-views.md +10 -0
  168. package/pipeline/skills/shared/external/hig-components-layout/references/bars-and-tabs.md +11 -0
  169. package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +13 -0
  170. package/pipeline/skills/shared/external/hig-foundations/references/type-color-spacing.md +13 -0
  171. package/pipeline/skills/shared/external/hig-patterns/references/onboarding-and-states.md +11 -0
  172. package/pipeline/skills/shared/external/hig-platforms/references/ipad-layout.md +12 -0
  173. package/pipeline/skills/shared/external/hig-technologies/references/technologies.md +16 -0
  174. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +9 -0
  175. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +18 -0
  176. package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md +9 -0
  177. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +9 -0
  178. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +7 -0
  179. package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +13 -0
  180. package/pipeline/skills/shared/external/ios-design-review/references/appearance-matrix.md +9 -0
  181. package/pipeline/skills/shared/external/ios-design-review/references/checklist.md +15 -0
  182. package/pipeline/skills/shared/external/ios-haptics/references/core-haptics.md +11 -0
  183. package/pipeline/skills/shared/external/ios-iphone-duo/references/api-map.md +11 -0
  184. package/pipeline/skills/shared/external/ios-iphone-duo/references/fixed-assumptions.md +15 -0
  185. package/pipeline/skills/shared/external/ios-iphone-duo/references/poses-and-panes.md +11 -0
  186. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +18 -0
  187. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +19 -0
  188. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +15 -0
  189. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +7 -0
  190. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +8 -0
  191. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +12 -0
  192. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +17 -0
  193. package/pipeline/skills/shared/external/ios-security/references/app-review-guidelines.md +8 -0
  194. package/pipeline/skills/shared/external/ios-security/references/cryptokit-advanced.md +14 -0
  195. package/pipeline/skills/shared/external/ios-security/references/file-storage-patterns.md +8 -0
  196. package/pipeline/skills/shared/external/ios-security/references/privacy-manifest.md +9 -0
  197. package/pipeline/skills/shared/external/ios-sf-symbols/references/effects-and-custom-symbols.md +9 -0
  198. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +13 -0
  199. package/pipeline/skills/shared/external/kotlin-coroutines-expert/references/patterns.md +8 -0
  200. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +21 -0
  201. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +9 -0
  202. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +12 -0
  203. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +16 -0
  204. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +21 -0
  205. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +10 -0
  206. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +9 -0
  207. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +12 -0
  208. package/pipeline/skills/shared/external/nodejs-backend-patterns/references/runtime-patterns.md +11 -0
  209. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +11 -0
  210. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +18 -0
  211. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +11 -0
  212. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +11 -0
  213. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +15 -0
  214. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +14 -0
  215. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +14 -0
  216. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +14 -0
  217. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +14 -0
  218. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +10 -0
  219. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +12 -0
  220. package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +14 -0
  221. package/pipeline/skills/shared/external/room-database/references/patterns.md +16 -0
  222. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +10 -0
  223. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +10 -0
  224. package/pipeline/skills/shared/external/spm-build-analysis/references/build-optimization-sources.md +14 -0
  225. package/pipeline/skills/shared/external/spm-build-analysis/references/spm-analysis-checks.md +19 -0
  226. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +13 -0
  227. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +31 -0
  228. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +8 -0
  229. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +9 -0
  230. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +10 -0
  231. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +8 -0
  232. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +24 -0
  233. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +8 -0
  234. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +14 -0
  235. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +12 -0
  236. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +10 -0
  237. package/pipeline/skills/shared/external/swift-concurrency-expert/references/swift-6-2-concurrency.md +8 -0
  238. package/pipeline/skills/shared/external/swift-concurrency-pro/references/actors.md +10 -0
  239. package/pipeline/skills/shared/external/swift-concurrency-pro/references/cancellation.md +7 -0
  240. package/pipeline/skills/shared/external/swift-concurrency-pro/references/interop.md +10 -0
  241. package/pipeline/skills/shared/external/swift-concurrency-pro/references/new-features.md +11 -0
  242. package/pipeline/skills/shared/external/swift-concurrency-pro/references/structured.md +9 -0
  243. package/pipeline/skills/shared/external/swift-concurrency-pro/references/testing.md +13 -0
  244. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +7 -0
  245. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +14 -0
  246. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +16 -0
  247. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +17 -0
  248. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +19 -0
  249. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +16 -0
  250. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +19 -0
  251. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +18 -0
  252. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +16 -0
  253. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +17 -0
  254. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +17 -0
  255. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +17 -0
  256. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +18 -0
  257. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +21 -0
  258. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +16 -0
  259. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +16 -0
  260. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +9 -0
  261. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +24 -0
  262. package/pipeline/skills/shared/external/swift-testing-pro/references/async-tests.md +9 -0
  263. package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +11 -0
  264. package/pipeline/skills/shared/external/swift-testing-pro/references/writing-better-tests.md +13 -0
  265. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +8 -0
  266. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +17 -0
  267. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +12 -0
  268. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +11 -0
  269. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +7 -0
  270. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +13 -0
  271. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +24 -0
  272. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +12 -0
  273. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +15 -0
  274. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +15 -0
  275. package/pipeline/skills/shared/external/swiftui-animation/references/scroll-effects.md +12 -0
  276. package/pipeline/skills/shared/external/swiftui-expert-skill/references/accessibility-patterns.md +11 -0
  277. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +7 -0
  278. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +13 -0
  279. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +9 -0
  280. package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts-accessibility.md +9 -0
  281. package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts.md +17 -0
  282. package/pipeline/skills/shared/external/swiftui-expert-skill/references/image-optimization.md +9 -0
  283. package/pipeline/skills/shared/external/swiftui-expert-skill/references/latest-apis.md +10 -0
  284. package/pipeline/skills/shared/external/swiftui-expert-skill/references/layout-best-practices.md +12 -0
  285. package/pipeline/skills/shared/external/swiftui-expert-skill/references/list-patterns.md +12 -0
  286. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-scenes.md +13 -0
  287. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-views.md +12 -0
  288. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-window-styling.md +12 -0
  289. package/pipeline/skills/shared/external/swiftui-expert-skill/references/performance-patterns.md +7 -0
  290. package/pipeline/skills/shared/external/swiftui-expert-skill/references/scroll-patterns.md +9 -0
  291. package/pipeline/skills/shared/external/swiftui-expert-skill/references/sheet-navigation-patterns.md +10 -0
  292. package/pipeline/skills/shared/external/swiftui-expert-skill/references/state-management.md +18 -0
  293. package/pipeline/skills/shared/external/swiftui-expert-skill/references/view-structure.md +16 -0
  294. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +11 -0
  295. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +8 -0
  296. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +14 -0
  297. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +9 -0
  298. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +22 -0
  299. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/uikit-glass.md +14 -0
  300. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +10 -0
  301. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +10 -0
  302. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +13 -0
  303. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +10 -0
  304. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +14 -0
  305. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +30 -0
  306. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +16 -0
  307. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +11 -0
  308. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +11 -0
  309. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +10 -0
  310. package/pipeline/skills/shared/external/swiftui-performance-audit/references/code-smells.md +8 -0
  311. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/app-wiring.md +12 -0
  312. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/menu-bar.md +10 -0
  313. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/navigationstack.md +11 -0
  314. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/scroll-reveal.md +12 -0
  315. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/sheets.md +14 -0
  316. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/tabview.md +10 -0
  317. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +10 -0
  318. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +13 -0
  319. package/pipeline/skills/shared/external/swiftui-view-refactor/references/mv-patterns.md +12 -0
  320. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +8 -0
  321. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +8 -0
  322. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +14 -0
  323. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +17 -0
  324. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +11 -0
  325. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +11 -0
  326. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +27 -0
  327. package/pipeline/skills/shared/external/xcode-build-fixer/references/build-settings-best-practices.md +9 -0
  328. package/pipeline/skills/shared/external/xcode-build-fixer/references/fix-patterns.md +7 -0
  329. package/pipeline/skills/shared/external/xcode-build-orchestrator/references/build-settings-best-practices.md +9 -0
  330. package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/build-optimization-sources.md +14 -0
  331. package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/code-compilation-checks.md +8 -0
  332. package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-optimization-sources.md +14 -0
  333. package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-settings-best-practices.md +9 -0
  334. package/pipeline/skills/shared/external/xcode-project-analyzer/references/project-audit-checks.md +13 -0
  335. package/pipeline/skills/skills-index.md +2 -1
@@ -0,0 +1,386 @@
1
+ /**
2
+ * qr-code.mjs - a QR Code Model 2 encoder (ISO/IEC 18004), byte mode only.
3
+ *
4
+ * Used by office-pair to show the pairing link on the machine's own screen.
5
+ * Byte mode covers any UTF-8 text; the smallest version that fits the text at
6
+ * the requested error-correction level is chosen, and the mask with the lowest
7
+ * penalty score is applied.
8
+ *
9
+ * Zero runtime dependencies (ADR-0004).
10
+ *
11
+ * @module pipeline/lib/qr-code
12
+ */
13
+
14
+ const ECC_FORMAT_BITS = { L: 1, M: 0, Q: 3, H: 2 };
15
+
16
+ // Per version (index 1..40): error-correction codewords per block, and the
17
+ // number of blocks, for each level (ISO/IEC 18004 table 9).
18
+ const ECC_PER_BLOCK = {
19
+ L: [
20
+ -1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30,
21
+ 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
22
+ ],
23
+ M: [
24
+ -1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28,
25
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
26
+ ],
27
+ Q: [
28
+ -1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30,
29
+ 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30
+ ],
31
+ H: [
32
+ -1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30,
33
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
34
+ ],
35
+ };
36
+ const BLOCKS = {
37
+ L: [
38
+ -1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14,
39
+ 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25,
40
+ ],
41
+ M: [
42
+ -1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23,
43
+ 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49,
44
+ ],
45
+ Q: [
46
+ -1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34,
47
+ 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68,
48
+ ],
49
+ H: [
50
+ -1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35,
51
+ 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81,
52
+ ],
53
+ };
54
+
55
+ /** Modules available for data and error correction in a version. */
56
+ export function rawDataModules(version) {
57
+ let n = (16 * version + 128) * version + 64;
58
+ if (version >= 2) {
59
+ const align = Math.floor(version / 7) + 2;
60
+ n -= (25 * align - 10) * align - 55;
61
+ if (version >= 7) n -= 36;
62
+ }
63
+ return n;
64
+ }
65
+
66
+ /** Data codewords of a version at a level. */
67
+ export function dataCodewords(version, ecc) {
68
+ return (
69
+ Math.floor(rawDataModules(version) / 8) - ECC_PER_BLOCK[ecc][version] * BLOCKS[ecc][version]
70
+ );
71
+ }
72
+
73
+ // GF(256) with the QR polynomial x^8 + x^4 + x^3 + x^2 + 1.
74
+ function gfMul(x, y) {
75
+ let z = 0;
76
+ for (let i = 7; i >= 0; i--) {
77
+ z = (z << 1) ^ ((z >>> 7) * 0x11d);
78
+ z ^= ((y >>> i) & 1) * x;
79
+ }
80
+ return z & 0xff;
81
+ }
82
+
83
+ function rsDivisor(degree) {
84
+ const result = new Array(degree).fill(0);
85
+ result[degree - 1] = 1;
86
+ let root = 1;
87
+ for (let i = 0; i < degree; i++) {
88
+ for (let j = 0; j < degree; j++) {
89
+ result[j] = gfMul(result[j], root);
90
+ if (j + 1 < degree) result[j] ^= result[j + 1];
91
+ }
92
+ root = gfMul(root, 0x02);
93
+ }
94
+ return result;
95
+ }
96
+
97
+ /** Reed-Solomon error-correction codewords for one block. */
98
+ export function reedSolomon(data, degree) {
99
+ const divisor = rsDivisor(degree);
100
+ const result = new Array(degree).fill(0);
101
+ for (const b of data) {
102
+ const factor = b ^ result.shift();
103
+ result.push(0);
104
+ divisor.forEach((coef, i) => {
105
+ result[i] ^= gfMul(coef, factor);
106
+ });
107
+ }
108
+ return result;
109
+ }
110
+
111
+ function alignmentPositions(version) {
112
+ if (version === 1) return [];
113
+ const count = Math.floor(version / 7) + 2;
114
+ const size = version * 4 + 17;
115
+ const step = version === 32 ? 26 : Math.ceil((version * 4 + 4) / (count * 2 - 2)) * 2;
116
+ const out = [6];
117
+ for (let pos = size - 7; out.length < count; pos -= step) out.splice(1, 0, pos);
118
+ return out;
119
+ }
120
+
121
+ function bch(value, bits, poly, polyDegree) {
122
+ let rem = value << polyDegree;
123
+ for (let i = bits + polyDegree - 1; i >= polyDegree; i--) {
124
+ if ((rem >>> i) & 1) rem ^= poly << (i - polyDegree);
125
+ }
126
+ return (value << polyDegree) | rem;
127
+ }
128
+
129
+ /** The 15 format bits for a level and mask, already XORed with the fixed mask. */
130
+ export function formatBits(ecc, mask) {
131
+ return bch((ECC_FORMAT_BITS[ecc] << 3) | mask, 5, 0x537, 10) ^ 0x5412;
132
+ }
133
+
134
+ function codewords(bytes, version, ecc) {
135
+ const capacityBits = dataCodewords(version, ecc) * 8;
136
+ const bits = [];
137
+ const put = (value, length) => {
138
+ for (let i = length - 1; i >= 0; i--) bits.push((value >>> i) & 1);
139
+ };
140
+ put(0b0100, 4);
141
+ put(bytes.length, version < 10 ? 8 : 16);
142
+ for (const b of bytes) put(b, 8);
143
+ put(0, Math.min(4, capacityBits - bits.length));
144
+ put(0, (8 - (bits.length % 8)) % 8);
145
+ for (let pad = 0xec; bits.length < capacityBits; pad ^= 0xec ^ 0x11) put(pad, 8);
146
+ const data = [];
147
+ for (let i = 0; i < bits.length; i += 8) {
148
+ data.push(bits.slice(i, i + 8).reduce((a, b) => (a << 1) | b, 0));
149
+ }
150
+
151
+ const blocks = BLOCKS[ecc][version];
152
+ const eccLen = ECC_PER_BLOCK[ecc][version];
153
+ const raw = Math.floor(rawDataModules(version) / 8);
154
+ const shortBlocks = blocks - (raw % blocks);
155
+ const shortLen = Math.floor(raw / blocks);
156
+ const groups = [];
157
+ for (let i = 0, k = 0; i < blocks; i++) {
158
+ const len = shortLen - eccLen + (i < shortBlocks ? 0 : 1);
159
+ const chunk = data.slice(k, k + len);
160
+ k += len;
161
+ groups.push({ data: chunk, ecc: reedSolomon(chunk, eccLen) });
162
+ }
163
+ const out = [];
164
+ for (let i = 0; i <= shortLen - eccLen; i++) {
165
+ for (const g of groups) if (i < g.data.length) out.push(g.data[i]);
166
+ }
167
+ for (let i = 0; i < eccLen; i++) for (const g of groups) out.push(g.ecc[i]);
168
+ return out;
169
+ }
170
+
171
+ const MASKS = [
172
+ (x, y) => (x + y) % 2 === 0,
173
+ (x, y) => y % 2 === 0,
174
+ (x) => x % 3 === 0,
175
+ (x, y) => (x + y) % 3 === 0,
176
+ (x, y) => (Math.floor(x / 3) + Math.floor(y / 2)) % 2 === 0,
177
+ (x, y) => ((x * y) % 2) + ((x * y) % 3) === 0,
178
+ (x, y) => (((x * y) % 2) + ((x * y) % 3)) % 2 === 0,
179
+ (x, y) => (((x + y) % 2) + ((x * y) % 3)) % 2 === 0,
180
+ ];
181
+
182
+ function baseMatrix(version) {
183
+ const size = version * 4 + 17;
184
+ const dark = Array.from({ length: size }, () => new Array(size).fill(false));
185
+ const fixed = Array.from({ length: size }, () => new Array(size).fill(false));
186
+ const set = (x, y, v) => {
187
+ dark[y][x] = v;
188
+ fixed[y][x] = true;
189
+ };
190
+ for (let i = 0; i < size; i++) {
191
+ set(6, i, i % 2 === 0);
192
+ set(i, 6, i % 2 === 0);
193
+ }
194
+ const finder = (cx, cy) => {
195
+ for (let dy = -4; dy <= 4; dy++) {
196
+ for (let dx = -4; dx <= 4; dx++) {
197
+ const x = cx + dx;
198
+ const y = cy + dy;
199
+ if (x < 0 || y < 0 || x >= size || y >= size) continue;
200
+ const d = Math.max(Math.abs(dx), Math.abs(dy));
201
+ set(x, y, d !== 2 && d !== 4);
202
+ }
203
+ }
204
+ };
205
+ finder(3, 3);
206
+ finder(size - 4, 3);
207
+ finder(3, size - 4);
208
+ const align = alignmentPositions(version);
209
+ for (const ay of align) {
210
+ for (const ax of align) {
211
+ const corner =
212
+ (ax === 6 && ay === 6) || (ax === 6 && ay === size - 7) || (ax === size - 7 && ay === 6);
213
+ if (corner) continue;
214
+ for (let dy = -2; dy <= 2; dy++) {
215
+ for (let dx = -2; dx <= 2; dx++) {
216
+ set(ax + dx, ay + dy, Math.max(Math.abs(dx), Math.abs(dy)) !== 1);
217
+ }
218
+ }
219
+ }
220
+ }
221
+ for (let i = 0; i < 9; i++) {
222
+ fixed[8][i] = fixed[i][8] = true;
223
+ if (i < 8) fixed[8][size - 1 - i] = fixed[size - 1 - i][8] = true;
224
+ }
225
+ set(8, size - 8, true);
226
+ if (version >= 7) {
227
+ const v = bch(version, 6, 0x1f25, 12);
228
+ for (let i = 0; i < 18; i++) {
229
+ const bit = ((v >>> i) & 1) === 1;
230
+ const a = size - 11 + (i % 3);
231
+ const b = Math.floor(i / 3);
232
+ set(a, b, bit);
233
+ set(b, a, bit);
234
+ }
235
+ }
236
+ return { size, dark, fixed };
237
+ }
238
+
239
+ function drawFormat(dark, size, ecc, mask) {
240
+ const f = formatBits(ecc, mask);
241
+ const bit = (i) => ((f >>> i) & 1) === 1;
242
+ for (let i = 0; i <= 5; i++) dark[i][8] = bit(i);
243
+ dark[7][8] = bit(6);
244
+ dark[8][8] = bit(7);
245
+ dark[8][7] = bit(8);
246
+ for (let i = 9; i < 15; i++) dark[8][14 - i] = bit(i);
247
+ for (let i = 0; i < 8; i++) dark[8][size - 1 - i] = bit(i);
248
+ for (let i = 8; i < 15; i++) dark[size - 15 + i][8] = bit(i);
249
+ dark[size - 8][8] = true;
250
+ }
251
+
252
+ function placeData(dark, fixed, size, words) {
253
+ let i = 0;
254
+ const total = words.length * 8;
255
+ for (let right = size - 1; right >= 1; right -= 2) {
256
+ if (right === 6) right = 5;
257
+ for (let vert = 0; vert < size; vert++) {
258
+ for (let j = 0; j < 2; j++) {
259
+ const x = right - j;
260
+ const upward = ((right + 1) & 2) === 0;
261
+ const y = upward ? size - 1 - vert : vert;
262
+ if (fixed[y][x] || i >= total) continue;
263
+ dark[y][x] = ((words[i >>> 3] >>> (7 - (i & 7))) & 1) === 1;
264
+ i++;
265
+ }
266
+ }
267
+ }
268
+ }
269
+
270
+ /** The ISO penalty score of a finished matrix: runs, 2x2 blocks, finder-like patterns, balance. */
271
+ export function penalty(dark) {
272
+ const size = dark.length;
273
+ let score = 0;
274
+ const lineScore = (get) => {
275
+ let s = 0;
276
+ for (let a = 0; a < size; a++) {
277
+ let run = 1;
278
+ for (let b = 1; b <= size; b++) {
279
+ if (b < size && get(a, b) === get(a, b - 1)) {
280
+ run++;
281
+ } else {
282
+ if (run >= 5) s += run - 2;
283
+ run = 1;
284
+ }
285
+ }
286
+ for (let b = 0; b + 10 < size; b++) {
287
+ const seq = Array.from({ length: 11 }, (_, k) => get(a, b + k));
288
+ const p1 = [1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0];
289
+ const p2 = [0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1];
290
+ if (seq.every((v, k) => v === Boolean(p1[k])) || seq.every((v, k) => v === Boolean(p2[k])))
291
+ s += 40;
292
+ }
293
+ }
294
+ return s;
295
+ };
296
+ score += lineScore((a, b) => dark[a][b]);
297
+ score += lineScore((a, b) => dark[b][a]);
298
+ let darkCount = 0;
299
+ for (let y = 0; y < size; y++) {
300
+ for (let x = 0; x < size; x++) {
301
+ if (dark[y][x]) darkCount++;
302
+ if (
303
+ y + 1 < size &&
304
+ x + 1 < size &&
305
+ dark[y][x] === dark[y][x + 1] &&
306
+ dark[y][x] === dark[y + 1][x] &&
307
+ dark[y][x] === dark[y + 1][x + 1]
308
+ )
309
+ score += 3;
310
+ }
311
+ }
312
+ const total = size * size;
313
+ score += (Math.ceil(Math.abs(darkCount * 20 - total * 10) / total) - 1) * 10;
314
+ return score;
315
+ }
316
+
317
+ /**
318
+ * Encode text as a QR code.
319
+ *
320
+ * @param {string} text
321
+ * @param {{ecc?: "L"|"M"|"Q"|"H", mask?: number}} [opts] mask forces one of 0..7
322
+ * @returns {{version: number, size: number, ecc: string, mask: number, modules: boolean[][]}}
323
+ */
324
+ export function encodeQr(text, { ecc = "M", mask } = {}) {
325
+ if (!ECC_PER_BLOCK[ecc]) throw new Error(`unknown error-correction level ${ecc}`);
326
+ const bytes = [...Buffer.from(String(text), "utf8")];
327
+ let version = 1;
328
+ const fits = (v) => 4 + (v < 10 ? 8 : 16) + bytes.length * 8 <= dataCodewords(v, ecc) * 8;
329
+ while (version <= 40 && !fits(version)) version++;
330
+ if (version > 40) throw new Error("text is too long for a QR code");
331
+ const words = codewords(bytes, version, ecc);
332
+ const { size, dark, fixed } = baseMatrix(version);
333
+ placeData(dark, fixed, size, words);
334
+ const candidates = Number.isInteger(mask) ? [mask] : [0, 1, 2, 3, 4, 5, 6, 7];
335
+ let best = null;
336
+ for (const m of candidates) {
337
+ const grid = dark.map((row, y) => row.map((v, x) => (fixed[y][x] ? v : v !== MASKS[m](x, y))));
338
+ drawFormat(grid, size, ecc, m);
339
+ const score = penalty(grid);
340
+ if (!best || score < best.score) best = { score, grid, mask: m };
341
+ }
342
+ return { version, size, ecc, mask: best.mask, modules: best.grid };
343
+ }
344
+
345
+ /** The code as one SVG path (one unit per module, a four-module quiet zone). */
346
+ export function qrSvg(qr, { scale = 6 } = {}) {
347
+ const q = 4;
348
+ const n = qr.size + q * 2;
349
+ let d = "";
350
+ qr.modules.forEach((row, y) =>
351
+ row.forEach((on, x) => {
352
+ if (on) d += `M${x + q} ${y + q}h1v1h-1z`;
353
+ }),
354
+ );
355
+ return (
356
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${n} ${n}" width="${n * scale}" ` +
357
+ `height="${n * scale}" shape-rendering="crispEdges"><rect width="${n}" height="${n}" ` +
358
+ `fill="#fff"/><path d="${d}" fill="#000"/></svg>`
359
+ );
360
+ }
361
+
362
+ /**
363
+ * The code as terminal text, two module rows per line in half blocks. Light
364
+ * modules are drawn (a dark terminal shows them white), with a two-module quiet
365
+ * zone.
366
+ */
367
+ export function qrText(qr) {
368
+ const q = 2;
369
+ const n = qr.size + q * 2;
370
+ const light = (x, y) => {
371
+ const mx = x - q;
372
+ const my = y - q;
373
+ return !(mx >= 0 && my >= 0 && mx < qr.size && my < qr.size && qr.modules[my][mx]);
374
+ };
375
+ const lines = [];
376
+ for (let y = 0; y < n; y += 2) {
377
+ let line = "";
378
+ for (let x = 0; x < n; x++) {
379
+ const top = light(x, y);
380
+ const bottom = y + 1 < n ? light(x, y + 1) : false;
381
+ line += top && bottom ? "█" : top ? "▀" : bottom ? "▄" : " ";
382
+ }
383
+ lines.push(line);
384
+ }
385
+ return lines.join("\n");
386
+ }
@@ -41,6 +41,7 @@ MA_HYGIENE_END='# <<< multi-agent pipeline (managed) <<<'
41
41
  ma_hygiene_patterns() {
42
42
  cat <<'PATTERNS'
43
43
  .worktrees/
44
+ .DerivedData/
44
45
  .pipeline/
45
46
  .multi-agent/
46
47
  triage-output.json
@@ -11,6 +11,8 @@
11
11
  * `commit.sha`, a `pr`, a runner `publish` record, a `projects[].commit`
12
12
  * or `projects[].pr`, or an explicit no-commit decision
13
13
  * (`phases["4"].status: "skipped"` with a `notes` reason).
14
+ * A repo commit counts as delivered once its push outcome is recorded
15
+ * (`projects[].pushStatus`) or it has a pull request.
14
16
  * 3. The plan's coverage was judged, unless the mode declares
15
17
  * `commits: false`: a `plan-coverage` entry in `gates[]` with a passing
16
18
  * verdict. plan-coverage-gate.mjs records it attended and unattended.
@@ -101,6 +103,16 @@ export function phase4Evidence(state) {
101
103
  return Boolean(p4 && p4.status === "skipped" && typeof p4.notes === "string" && p4.notes.trim());
102
104
  }
103
105
 
106
+ const PUSH_RECORDED = new Set(["pushed", "rebase-pushed", "skipped"]);
107
+
108
+ /** Repos whose commit has neither a recorded push outcome nor a pull request. */
109
+ export function unpushedRepos(state) {
110
+ const projects = Array.isArray(state.projects) ? state.projects : [];
111
+ return projects
112
+ .filter((p) => p && hasSha(p.commit) && !hasPr(p.pr) && !PUSH_RECORDED.has(p.pushStatus))
113
+ .map((p, i) => p.project || p.name || `#${i + 1}`);
114
+ }
115
+
104
116
  /** True when a passing plan-coverage verdict is in the gate ledger. */
105
117
  export function planCoverageRecorded(state) {
106
118
  const gates = Array.isArray(state.gates) ? state.gates : [];
@@ -138,6 +150,11 @@ export function completionGaps(state) {
138
150
  'no Phase 4 evidence: record commit.sha (or pr), or the no-commit decision as phases["4"] = {status: "skipped", notes: "<why>"}',
139
151
  );
140
152
  }
153
+ for (const name of unpushedRepos(state)) {
154
+ gaps.push(
155
+ `repo ${name} has a commit with no recorded push: set projects[].pushStatus to "pushed" or "rebase-pushed", or "skipped" when the push is left to the user (Phase 4 push policy)`,
156
+ );
157
+ }
141
158
  if (!planCoverageRecorded(state)) {
142
159
  gaps.push(
143
160
  "no passing plan-coverage verdict in gates[]: run plan-coverage-gate.mjs --state <agent-state.json> (Phase 4 Step 0a)",
@@ -132,7 +132,7 @@ gh issue comment "$ISSUE_NUMBER" --repo "$ORG/$REPO" --body-file /tmp/channels-$
132
132
  bash $HOME/.claude/scripts/update-issue-progress.sh "$TASK_ID"
133
133
  ```
134
134
 
135
- If either call fails, the channel adapter MUST NOT mark the issue channel as completed in `agent-state.json`. The user sees a clear error and can retry.
135
+ If either call fails, the channel adapter MUST NOT mark the issue channel as completed in `agent-state.json`. The user sees a clear error and can retry. The order is enforced: `update-issue-progress.sh` exits 5 and leaves the body unchanged unless the authenticated account has a comment on the issue since `state.startedAt` that carries the `Ref: #N` footer.
136
136
 
137
137
  ## Drift detection
138
138
 
@@ -1,7 +1,7 @@
1
1
  # Cross-CLI Contract (Claude Code · Copilot CLI · Codex CLI)
2
2
 
3
3
  <!-- toc -->
4
- - [1. Command Inventory (69 commands)](#1-command-inventory-69-commands)
4
+ - [1. Command Inventory (70 commands)](#1-command-inventory-70-commands)
5
5
  - [2. Canonical Placeholder Vocabulary](#2-canonical-placeholder-vocabulary)
6
6
  - [2.6 Intentional structural divergence - thin dispatcher vs inlined orchestrator](#26-intentional-structural-divergence---thin-dispatcher-vs-inlined-orchestrator)
7
7
  - [2.7 One command, three different meanings: `/multi-agent:model`](#27-one-command-three-different-meanings-multi-agentmodel)
@@ -20,14 +20,14 @@
20
20
 
21
21
  ---
22
22
 
23
- ## 1. Command Inventory (69 commands)
23
+ ## 1. Command Inventory (70 commands)
24
24
 
25
25
  ```
26
26
  analysis, analysis-jira, analysis-resolve, autopilot, autopilot-off,
27
27
  autopilot-on, autopilot-status, bug-bash, build-optimize, channels, complaint-analysis,
28
28
  create-jira, design-check, diff-explain, doctor, estimate, feedback, forget,
29
29
  garbage-collect, graph, help, ios-coding-standard, issue, jira, kill,
30
- language, log, manual-test, model, office-off, office-on, office-status,
30
+ language, log, manual-test, model, office-off, office-on, office-pair, office-status,
31
31
  postmortem, prune-logs,
32
32
  prune-prompts, purge, refactor, research, resume, review,
33
33
  review-analysis, review-issue, review-jira, route-off, route-on,
@@ -43,7 +43,7 @@ Categories:
43
43
  - **Issue generator** (one-shot, no worktree, asks type Task/Bug/Story, hard approval gate before create): `create-jira`
44
44
  - **Pipeline entries**: `autopilot` (plus the bare `/multi-agent` in the dispatcher). The phase set is a property of the command, and every mode runs its whole set.
45
45
  - **Tail path** (the pipeline tail over work already on a branch, no run behind it): `resume`
46
- - **Ops commands** (one-shot, no worktree): `status`, `log`, `kill`, `steer`, `purge`, `uninstall`, `resume`, `review`, `review-jira`, `review-issue`, `analysis`, `analysis-resolve`, `research`, `scaffold`, `complaint-analysis`, `bug-bash`, `test-gen`, `build-optimize`, `channels`, `scan`, `search`, `diff-explain`, `garbage-collect`, `graph`, `postmortem`, `prune-logs`, `prune-prompts`, `serve`, `office-on`, `office-off`, `office-status`
46
+ - **Ops commands** (one-shot, no worktree): `status`, `log`, `kill`, `steer`, `purge`, `uninstall`, `resume`, `review`, `review-jira`, `review-issue`, `analysis`, `analysis-resolve`, `research`, `scaffold`, `complaint-analysis`, `bug-bash`, `test-gen`, `build-optimize`, `channels`, `scan`, `search`, `diff-explain`, `garbage-collect`, `graph`, `postmortem`, `prune-logs`, `prune-prompts`, `serve`, `office-on`, `office-off`, `office-pair`, `office-status`
47
47
  - **Local audits** (worktree only to build; no commit, push, PR or channels): `design-check`, `testflight-validation`, `ios-coding-standard`. `testflight-validation` additionally never invokes `altool --upload-app` - a validation run must not be able to ship a build by accident.
48
48
  - **Meta-ops**: `setup`, `sync`, `update`, `help`, `refactor`, `test`, `stack`, `manual-test`, `language`
49
49
  - **Routines** (user-defined routine registry; the routines they create are local-only and never synced): `save`, `routines`, `forget`
@@ -0,0 +1,166 @@
1
+ # Office remote protocol
2
+
3
+ <!-- toc -->
4
+ - [Parts and trust](#parts-and-trust)
5
+ - [Account](#account)
6
+ - [Pairing](#pairing)
7
+ - [Transport](#transport)
8
+ - [Passkey tiers](#passkey-tiers)
9
+ - [Audit](#audit)
10
+ - [Decisions](#decisions)
11
+ - [To verify on a real phone](#to-verify-on-a-real-phone)
12
+ <!-- /toc -->
13
+
14
+ How a phone reaches the phone routes of the local contract server through a
15
+ relay on the user's site, without the machine opening a port and without the
16
+ relay or the site being able to read a payload or act on their own.
17
+
18
+ The request layer exists and is not changed here: every phone request is signed
19
+ with the device's Ed25519 key (`MA-PHONE-SIG-1`), and `_phone-auth.mjs` checks
20
+ the device, the window, the nonce, the scopes and writes the audit line
21
+ (`phone-api.md`). This document adds what leaving the machine requires: an
22
+ end-to-end layer around those requests, the relay that carries it, pairing that
23
+ enrols the device through the existing registry, and a passkey on top of the
24
+ device signature for the routes that change something.
25
+
26
+ ## Parts and trust
27
+
28
+ | Part | Runs | Holds | Trusted for |
29
+ |---|---|---|---|
30
+ | Phone page | the phone browser, on the site origin (behind the owner session) | the device signing key and the session key pair (non-extractable WebCrypto), a passkey | showing data, asking the user, signing |
31
+ | Site + relay | the user's site (serverless functions + database) | the owner's account, pairing records, ciphertext queues | delivering ciphertext, nothing else |
32
+ | Machine client | the user's machine, started with the office | the machine key pair, the audit of remote frames | decrypting, checking the passkey tier, forwarding to the local server |
33
+ | Local server | the user's machine, 127.0.0.1 only | the device registry, nonces, audit | verifying the device signature and executing phone routes, as today |
34
+
35
+ The relay and its database are treated as compromised: they can drop, delay,
36
+ replay and reorder frames. None of that lets them read a frame, forge a device
37
+ signature or pass the passkey check. The phone page itself is the trust anchor
38
+ on the phone side, as for any web app holding keys: a site that serves a
39
+ modified page can act as the phone. The page therefore loads no third-party
40
+ script, runs under a strict CSP and keeps its keys non-extractable, and the
41
+ machine still applies every check of its own (tiers, routes, counters).
42
+
43
+ ## Account
44
+
45
+ The first release serves the owner's account only and reuses the site's
46
+ existing owner session rather than adding a second login: the phone page and
47
+ every phone relay route require it, and state-changing routes also require a
48
+ same-origin request. The session only lets the phone use the relay for its
49
+ own pairings; it never authorizes an action on the machine, which still
50
+ checks the device signature, the passkey tier and the route itself. Invited
51
+ accounts are a later step.
52
+
53
+ ## Pairing
54
+
55
+ `office-pair.mjs` runs the machine side; every subcommand refuses an
56
+ unattended run. The user runs it in their own terminal (`! node
57
+ ~/.claude/scripts/office-pair.mjs start`), so nothing it shows lands in a
58
+ transcript.
59
+
60
+ 1. `start [--relay <origin>]` creates the machine key pair (P-256) on first
61
+ use and a relay token (256 bits) kept in `<phone dir>/remote.json` (0600),
62
+ a pairing id and a one-time secret (128 bits, valid 10 minutes). The relay
63
+ origin comes from `prefs.global.office.remote.relayUrl` or the flag, never
64
+ from the code; it is https, or http on the loopback. The relay receives the
65
+ pairing id, the machine public key, the expiry and the SHA-256 of the relay
66
+ token, never the token itself.
67
+ 2. The QR code is drawn by the dependency-free encoder in `lib/qr-code.mjs`
68
+ into a 0600 page (`default-src 'none'`) in a 0700 directory and opened in
69
+ the local browser; the secret is never printed. It holds
70
+ `<origin>/office/pair#p=<pairing id>&s=<secret>&k=<machine public key>`.
71
+ A browser never sends the fragment to a server, so the relay neither sees
72
+ the secret nor can swap the machine key.
73
+ 3. The phone, signed in, scans it, creates its Ed25519 device key and its P-256
74
+ session key, registers a passkey (WebAuthn `create`, user verification
75
+ required) and sends, sealed with a key that mixes the ECDH secret with the
76
+ pairing secret: the device name and public key, and the passkey credential
77
+ id, public key (`getPublicKey()`, SPKI), rp id and origin.
78
+ 4. `wait` polls the relay with the token, opens the message, refuses a passkey
79
+ whose origin is not the relay origin or whose rp id is not its host, and
80
+ prints the six-digit code derived from both public keys and a fingerprint
81
+ of the machine key. The phone shows the same code and fingerprint, and
82
+ accepts the pairing only after its user confirms on the phone that both
83
+ match, so a link someone else registered cannot pair the phone with their
84
+ machine.
85
+ 5. `confirm <code>` takes the code as the phone shows it. A mismatch drops the
86
+ pairing (one attempt). A match enrols the device with
87
+ `phone-devices.mjs add` (scopes `read,answer`; `launch` stays a separate
88
+ local switch), stores the session key and passkey beside it, and tells the
89
+ relay to start delivering for the pairing. That call carries the first
90
+ machine-to-phone frame (counter 1), sealed with the pairing's keys, holding
91
+ the device id the phone signs its requests with; the relay cannot alter it.
92
+ 6. Revoke: `phone-devices.mjs revoke <id>` on the machine also drops the
93
+ device's remote keys; the site can stop delivering for a pairing. Either
94
+ alone ends control.
95
+
96
+ ## Transport
97
+
98
+ - The machine client polls the relay over HTTPS (long poll, 25 s) and posts
99
+ responses. No inbound port, no tunnel, no funnel.
100
+ - A frame is `{method, target, headers, body}` (request) or
101
+ `{status, headers, body}` (response), as `phone-api.md` specifies, sealed
102
+ with AES-256-GCM. The keys come from ECDH between the phone session key and
103
+ the machine key, through HKDF-SHA-256 with the pairing id as salt, one key
104
+ per direction; the nonce is a per-direction counter, and a frame whose
105
+ counter does not exceed the last one accepted is dropped.
106
+ - The relay stores `{pairingId, direction, counter, ciphertext, expiresAt}` in
107
+ a table only the service role reaches, and deletes a row on delivery or
108
+ after 10 minutes.
109
+ - The machine client takes a target only in the canonical form the local
110
+ server routes on (no encoded dot segment, backslash or fragment), so the
111
+ tier and the route prefix are judged on the path the server executes. Only
112
+ one client runs at a time (a lock file), and each poll writes its counters
113
+ onto the config as it is on disk, so a revoke made meanwhile is kept.
114
+ - The machine client forwards only targets under `/v1/phone/`, never adds an
115
+ `Authorization` header, and rewrites `Host` to the loopback address; the
116
+ local server's own checks then apply unchanged.
117
+ - Remote is off by default: the client runs only while the office is on and
118
+ a device is paired; `office-off` stops it.
119
+
120
+ ## Passkey tiers
121
+
122
+ The device signature proves which phone sent a request; the passkey proves the
123
+ user was holding it. Each phone route carries a tier from the route table:
124
+
125
+ | Tier | Phone routes | Passkey |
126
+ |---|---|---|
127
+ | read | `GET /v1/phone/runs`, `GET /v1/phone/runs/{id}` | one at session open; the session locks after 5 minutes idle |
128
+ | low | `POST /v1/phone/runs/{id}/answer`, a canned steer | within 2 minutes of the last passkey (configurable down to every time) |
129
+ | high | `POST /v1/phone/launch` and any later route that changes code or writes to a host | every time, bound to the request |
130
+
131
+ For a passkey-bound request the phone sends `X-MA-Passkey` with a WebAuthn
132
+ assertion whose challenge is SHA-256 of the request's canonical signed text.
133
+ The machine verifies, before the request reaches the local server: the rp id
134
+ hash and origin match the paired ones, the user verified flag is set, the
135
+ signature verifies with the paired credential key, and the sign count (when the
136
+ authenticator keeps one) has grown. Steering from the phone is a canned
137
+ template chosen by id and expanded on the machine; free-text steering, widening
138
+ actions (autopilot on, stack, routine delete, host permissions) and merges stay
139
+ desktop routes.
140
+
141
+ ## Audit
142
+
143
+ The local server keeps its per-request audit as today. The machine client adds
144
+ `<phone dir>/remote-frames.jsonl` (0600): time, device, counter,
145
+ target path without query, tier, passkey verdict and the reason of a drop.
146
+ Never a body, a header value or a signature.
147
+
148
+ ## Decisions
149
+
150
+ 1. **The machine client ships in the public pipeline**, inert until
151
+ `office-pair` pairs a device. The relay URL comes from prefs or the pairing
152
+ QR, never from the code.
153
+ 2. **Steering from the phone takes canned templates only**, chosen by id and
154
+ expanded on the machine.
155
+ 3. **The first release serves the owner's account only**; invites follow as a
156
+ separate step.
157
+ 4. **The relay queue is a short-lived table in the site's database**: ciphertext
158
+ only, deleted on delivery or after 10 minutes, service role only.
159
+
160
+ ## To verify on a real phone
161
+
162
+ WebCrypto Ed25519 and non-extractable P-256 keys in the phone's browser,
163
+ `getPublicKey()` on the passkey registration response, and the long-poll
164
+ interval against the site's function time limit. The design falls back to
165
+ P-256 ECDSA for the device key if Ed25519 is missing, which `_phone-auth.mjs`
166
+ would then accept as a second key type.
@@ -66,7 +66,7 @@ not mitigated here.
66
66
  | T20 | A learned statement carrying a secret or a machine path to the page | `GET /v1/memory` returns counts, the code graph's age and size, and the five newest learning statements per repository, each passed through the transcript redactor (provider credential rules, paths and addresses) and cut to 120 characters. Repository names are matched against a safe character set before any file is opened. Read only; `local-only` | `office-routes.test.mjs` "GET /v1/memory reports every shelf, redacted" |
67
67
  | T21 | Machine details through the resource panel | `GET /v1/system` returns numbers only (cores, load, memory, free disk, process counts, log folder size) and two fixed warnings; no path, process argument, user or host name. The schema allows nothing else. The measurement runs as child processes off the event loop, concurrent requests share one measurement, and the log folder size is reused for a minute. Readable from a phone, since it names nothing | `office-routes.test.mjs` "GET /v1/system returns numbers only: no path, user or host name" |
68
68
  | T22 | Old run logs deleted wider than asked, or a live run's log removed | `POST /v1/prune-logs` takes only an age from a fixed set (7, 30 or 90 days) and runs `prune-logs.sh`, the script `/multi-agent:prune-logs` runs: the dry run is the preview; the confirmed step lists the folders again, refuses with `changed` when they differ, and then prunes only the previewed list through `--only-from`, under a token bound to that age and list. The script never touches the audit trail, the metrics corpus or the task counters, refuses a log root that is `/`, the home folder or a git checkout, and spares a folder written in the last minutes and a run that can still be resumed. Tagged `local-only`/`high` | `office-routes.test.mjs` "POST /v1/prune-logs previews, then prunes only on the token" |
69
- | T23 | Pull request listing exposing a token, or the native task mirror writing into another run | Each GitHub account gh is logged in to is asked with `gh auth token` for that host and login; the token travels in `GH_TOKEN` (and `GH_ENTERPRISE_TOKEN` for an Enterprise Server host) for the child process only, never on a command line, in a response or a log. Bitbucket Server is read over https only (a plain-http host is refused unless it is the loopback), redirects are not followed, and the token, read from the credential store with a deadline, travels in the `Authorization` header only; an unreachable host is a reason on that source, not a failure of the list. A redacted answer drops URLs and logins, numbering the accounts instead. The task-mirror hook (PostToolUse on TaskCreate and TaskUpdate) writes only into the run whose recorded session is the calling session, skips phase tiles, never prints, and exits 0 on any input it cannot read | `work-items-prs.test.mjs`, `task-mirror.test.mjs` |
69
+ | T23 | Pull request listing exposing a token, or the native task mirror writing into another run | Each GitHub account gh is logged in to is asked with `gh auth token` for that host and login; the token travels in `GH_TOKEN` (and `GH_ENTERPRISE_TOKEN` for an Enterprise Server host) for the child process only, never on a command line, in a response or a log. Bitbucket Server is read over https only (a plain-http host is refused unless it is the loopback), redirects are not followed, and the token, read from the credential store with a deadline, travels in the `Authorization` header only; an unreachable host is a reason on that source, not a failure of the list. `GET /v1/prs/reach` (local-only) asks gh for the changed file paths of at most 20 GitHub pull requests, three at a time, each with its account's token in the environment; it asks nothing for a repository whose name is not `owner/repo` or that has no graph under `~/.claude/knowledge/<repo name>/`. The paths serve only as keys into the local graph: nothing is read from disk by them and none is returned, only counts. The graph is parsed in a child process, so no other route waits behind it. A redacted answer drops URLs and logins, numbering the accounts instead. The task-mirror hook (PostToolUse on TaskCreate and TaskUpdate) writes only into the run whose recorded session is the calling session, skips phase tiles, never prints, and exits 0 on any input it cannot read | `work-items-prs.test.mjs`, `task-mirror.test.mjs`, `pr-reach.test.mjs`, `office-work.test.mjs` "GET /v1/prs/reach says which pull requests have no graph to measure against" |
70
70
  | T24 | The panel changing a preference wider than the terminal would, or posting addresses to a phone | `POST /v1/settings` takes one of three settings and one of its two values, previews the change and runs, under a token bound to that setting and value, what `/multi-agent:language`, `/multi-agent:model` or `/multi-agent:route-on`/`route-off` runs; route on passes the saved strategy back so only the switch changes, nothing the panel sends can add rules or a free provider, and two changes never run at once. Tagged `local-only`/`high`. Report links: `channelResults` URLs must be https, travel only in the run record's `channelUrls` (local, dropped from a redacted answer) and are shown as text, never followed | `office-routes.test.mjs` "POST /v1/settings changes a switch only on the token, through its script", `runs-index.test.mjs` |
71
71
 
72
72
  ## Residual risks