buildanything 1.7.1 → 2.0.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/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +9 -3
- package/CHANGELOG.md +112 -0
- package/README.md +2 -2
- package/agents/a11y-architect.md +166 -0
- package/agents/business-model.md +80 -29
- package/agents/code-architect.md +75 -0
- package/agents/code-reviewer.md +255 -0
- package/agents/code-simplifier.md +64 -0
- package/agents/design-brand-guardian.md +293 -53
- package/agents/design-critic.md +139 -0
- package/agents/design-inclusive-visuals-specialist.md +6 -19
- package/agents/design-ui-designer.md +335 -56
- package/agents/design-ux-architect.md +403 -55
- package/agents/design-ux-researcher.md +264 -49
- package/agents/engineering-ai-engineer.md +26 -36
- package/agents/engineering-backend-architect.md +185 -36
- package/agents/engineering-data-engineer.md +225 -43
- package/agents/engineering-devops-automator.md +227 -74
- package/agents/engineering-frontend-developer.md +210 -34
- package/agents/engineering-mobile-app-builder.md +6 -1
- package/agents/engineering-rapid-prototyper.md +30 -9
- package/agents/engineering-security-engineer.md +263 -61
- package/agents/engineering-senior-developer.md +128 -19
- package/agents/engineering-sre.md +84 -0
- package/agents/engineering-technical-writer.md +285 -41
- package/agents/feature-intel.md +110 -0
- package/agents/ios-app-review-guardian.md +66 -0
- package/agents/ios-foundation-models-specialist.md +64 -0
- package/agents/ios-storekit-specialist.md +59 -0
- package/agents/ios-swift-architect.md +129 -0
- package/agents/ios-swift-search.md +137 -0
- package/agents/ios-swift-ui-design.md +136 -0
- package/agents/marketing-app-store-optimizer.md +246 -64
- package/agents/planner.md +216 -0
- package/agents/pr-test-analyzer.md +63 -0
- package/agents/product-feedback-synthesizer.md +8 -2
- package/agents/refactor-cleaner.md +102 -0
- package/agents/security-reviewer.md +128 -0
- package/agents/silent-failure-hunter.md +54 -0
- package/agents/swift-build-resolver.md +119 -0
- package/agents/swift-reviewer.md +112 -0
- package/agents/tech-feasibility.md +21 -1
- package/agents/testing-api-tester.md +236 -59
- package/agents/testing-evidence-collector.md +26 -1
- package/agents/testing-performance-benchmarker.md +21 -1
- package/agents/testing-reality-checker.md +6 -1
- package/agents/visual-research.md +116 -0
- package/bin/adapters/cycle-counter-tool.ts +155 -0
- package/bin/adapters/scribe-tool.ts +71 -0
- package/bin/adapters/state-save-tool.ts +130 -0
- package/bin/adapters/write-lease-tool.ts +127 -0
- package/bin/buildanything-runtime.js +15 -0
- package/bin/buildanything-runtime.ts +328 -0
- package/bin/setup.js +83 -8
- package/commands/add-feature.md +2 -0
- package/commands/build.md +752 -332
- package/commands/fix.md +65 -0
- package/commands/self-check.md +121 -0
- package/commands/setup.md +114 -0
- package/commands/ux-review.md +63 -0
- package/commands/verify.md +69 -0
- package/docs/migration/agents.yaml +729 -0
- package/docs/migration/phase-graph.yaml +1088 -0
- package/docs/migration/sdk-host-compat.md +18 -0
- package/hooks/compile-writer-owner-cache.ts +171 -0
- package/hooks/hooks.json +36 -0
- package/hooks/pre-tool-use +19 -0
- package/hooks/pre-tool-use.ts +776 -0
- package/hooks/record-mode-transitions.ts +178 -0
- package/hooks/session-start +89 -2
- package/hooks/subagent-start +17 -0
- package/hooks/subagent-start.ts +471 -0
- package/hooks/subagent-stop +17 -0
- package/hooks/subagent-stop.ts +153 -0
- package/package.json +28 -5
- package/protocols/architecture-schema.md +171 -0
- package/protocols/build-fix.md +52 -0
- package/protocols/cleanup.md +54 -0
- package/protocols/decision-log.md +131 -0
- package/protocols/eval-harness.md +61 -0
- package/protocols/fake-data-detector.md +64 -0
- package/protocols/ios-context.md +234 -0
- package/protocols/ios-frameworks-map.md +323 -0
- package/protocols/ios-phase-branches.md +337 -0
- package/protocols/ios-preflight.md +27 -0
- package/protocols/launch-readiness.md +258 -0
- package/protocols/metric-loop.md +153 -0
- package/protocols/smoke-test.md +118 -0
- package/protocols/state-schema.json +388 -0
- package/protocols/state-schema.md +172 -0
- package/protocols/verify.md +127 -0
- package/protocols/visual-dna.md +185 -0
- package/protocols/web-phase-branches.md +351 -0
- package/skills/ios/_VENDORED.md +62 -0
- package/skills/ios/activitykit/LICENSE +131 -0
- package/skills/ios/activitykit/SKILL.md +505 -0
- package/skills/ios/activitykit/references/activitykit-patterns.md +868 -0
- package/skills/ios/app-intents/LICENSE +131 -0
- package/skills/ios/app-intents/SKILL.md +494 -0
- package/skills/ios/app-intents/references/appintents-advanced.md +1076 -0
- package/skills/ios/app-store-connect-metadata/SKILL.md +148 -0
- package/skills/ios/apple-on-device-ai/LICENSE +131 -0
- package/skills/ios/apple-on-device-ai/SKILL.md +505 -0
- package/skills/ios/apple-on-device-ai/references/coreml-conversion.md +425 -0
- package/skills/ios/apple-on-device-ai/references/coreml-optimization.md +344 -0
- package/skills/ios/apple-on-device-ai/references/foundation-models.md +508 -0
- package/skills/ios/apple-on-device-ai/references/mlx-swift.md +285 -0
- package/skills/ios/asc-privacy-manifest/SKILL.md +350 -0
- package/skills/ios/hig-components-content/SKILL.md +86 -0
- package/skills/ios/hig-components-content/references/activity-views.md +79 -0
- package/skills/ios/hig-components-content/references/charts.md +180 -0
- package/skills/ios/hig-components-content/references/collections.md +48 -0
- package/skills/ios/hig-components-content/references/color-wells.md +42 -0
- package/skills/ios/hig-components-content/references/image-views.md +82 -0
- package/skills/ios/hig-components-content/references/image-wells.md +34 -0
- package/skills/ios/hig-components-content/references/lockups.md +78 -0
- package/skills/ios/hig-components-content/references/web-views.md +36 -0
- package/skills/ios/hig-components-controls/SKILL.md +88 -0
- package/skills/ios/hig-components-controls/references/combo-boxes.md +40 -0
- package/skills/ios/hig-components-controls/references/controls.md +112 -0
- package/skills/ios/hig-components-controls/references/gauges.md +74 -0
- package/skills/ios/hig-components-controls/references/labels.md +92 -0
- package/skills/ios/hig-components-controls/references/pickers.md +128 -0
- package/skills/ios/hig-components-controls/references/rating-indicators.md +38 -0
- package/skills/ios/hig-components-controls/references/segmented-controls.md +94 -0
- package/skills/ios/hig-components-controls/references/sliders.md +92 -0
- package/skills/ios/hig-components-controls/references/steppers.md +40 -0
- package/skills/ios/hig-components-controls/references/text-fields.md +88 -0
- package/skills/ios/hig-components-controls/references/text-views.md +56 -0
- package/skills/ios/hig-components-controls/references/toggles.md +127 -0
- package/skills/ios/hig-components-controls/references/token-fields.md +48 -0
- package/skills/ios/hig-components-controls/references/virtual-keyboards.md +156 -0
- package/skills/ios/hig-components-dialogs/SKILL.md +76 -0
- package/skills/ios/hig-components-dialogs/references/action-sheets.md +74 -0
- package/skills/ios/hig-components-dialogs/references/alerts.md +158 -0
- package/skills/ios/hig-components-dialogs/references/digit-entry-views.md +32 -0
- package/skills/ios/hig-components-dialogs/references/popovers.md +81 -0
- package/skills/ios/hig-components-dialogs/references/sheets.md +157 -0
- package/skills/ios/hig-components-layout/SKILL.md +99 -0
- package/skills/ios/hig-components-layout/references/boxes.md +48 -0
- package/skills/ios/hig-components-layout/references/column-views.md +44 -0
- package/skills/ios/hig-components-layout/references/lists-and-tables.md +99 -0
- package/skills/ios/hig-components-layout/references/ornaments.md +56 -0
- package/skills/ios/hig-components-layout/references/outline-views.md +64 -0
- package/skills/ios/hig-components-layout/references/panels.md +75 -0
- package/skills/ios/hig-components-layout/references/scroll-views.md +123 -0
- package/skills/ios/hig-components-layout/references/sidebars.md +109 -0
- package/skills/ios/hig-components-layout/references/split-views.md +110 -0
- package/skills/ios/hig-components-layout/references/tab-bars.md +173 -0
- package/skills/ios/hig-components-layout/references/tab-views.md +68 -0
- package/skills/ios/hig-components-layout/references/windows.md +188 -0
- package/skills/ios/hig-components-menus/SKILL.md +81 -0
- package/skills/ios/hig-components-menus/references/action-button.md +61 -0
- package/skills/ios/hig-components-menus/references/buttons.md +261 -0
- package/skills/ios/hig-components-menus/references/context-menus.md +105 -0
- package/skills/ios/hig-components-menus/references/disclosure-controls.md +84 -0
- package/skills/ios/hig-components-menus/references/dock-menus.md +40 -0
- package/skills/ios/hig-components-menus/references/edit-menus.md +88 -0
- package/skills/ios/hig-components-menus/references/menus.md +171 -0
- package/skills/ios/hig-components-menus/references/pop-up-buttons.md +70 -0
- package/skills/ios/hig-components-menus/references/pull-down-buttons.md +77 -0
- package/skills/ios/hig-components-menus/references/the-menu-bar.md +303 -0
- package/skills/ios/hig-components-menus/references/toolbars.md +256 -0
- package/skills/ios/hig-components-search/SKILL.md +68 -0
- package/skills/ios/hig-components-search/references/page-controls.md +120 -0
- package/skills/ios/hig-components-search/references/path-controls.md +40 -0
- package/skills/ios/hig-components-search/references/search-fields.md +189 -0
- package/skills/ios/hig-components-status/SKILL.md +80 -0
- package/skills/ios/hig-components-status/references/activity-rings.md +105 -0
- package/skills/ios/hig-components-status/references/progress-indicators.md +116 -0
- package/skills/ios/hig-components-status/references/status-bars.md +38 -0
- package/skills/ios/hig-components-system/SKILL.md +88 -0
- package/skills/ios/hig-components-system/references/app-clips.md +387 -0
- package/skills/ios/hig-components-system/references/app-shortcuts.md +114 -0
- package/skills/ios/hig-components-system/references/complications.md +425 -0
- package/skills/ios/hig-components-system/references/home-screen-quick-actions.md +42 -0
- package/skills/ios/hig-components-system/references/live-activities.md +442 -0
- package/skills/ios/hig-components-system/references/notifications.md +153 -0
- package/skills/ios/hig-components-system/references/top-shelf.md +135 -0
- package/skills/ios/hig-components-system/references/watch-faces.md +40 -0
- package/skills/ios/hig-components-system/references/widgets.md +517 -0
- package/skills/ios/hig-foundations/SKILL.md +98 -0
- package/skills/ios/hig-foundations/references/accessibility.md +291 -0
- package/skills/ios/hig-foundations/references/app-icons.md +210 -0
- package/skills/ios/hig-foundations/references/branding.md +44 -0
- package/skills/ios/hig-foundations/references/color.md +274 -0
- package/skills/ios/hig-foundations/references/dark-mode.md +116 -0
- package/skills/ios/hig-foundations/references/icons.md +263 -0
- package/skills/ios/hig-foundations/references/images.md +176 -0
- package/skills/ios/hig-foundations/references/immersive-experiences.md +174 -0
- package/skills/ios/hig-foundations/references/inclusion.md +189 -0
- package/skills/ios/hig-foundations/references/layout.md +425 -0
- package/skills/ios/hig-foundations/references/materials.md +238 -0
- package/skills/ios/hig-foundations/references/motion.md +103 -0
- package/skills/ios/hig-foundations/references/privacy.md +231 -0
- package/skills/ios/hig-foundations/references/right-to-left.md +206 -0
- package/skills/ios/hig-foundations/references/sf-symbols.md +310 -0
- package/skills/ios/hig-foundations/references/spatial-layout.md +142 -0
- package/skills/ios/hig-foundations/references/typography.md +1146 -0
- package/skills/ios/hig-foundations/references/writing.md +91 -0
- package/skills/ios/hig-inputs/SKILL.md +94 -0
- package/skills/ios/hig-inputs/references/apple-pencil-and-scribble.md +148 -0
- package/skills/ios/hig-inputs/references/camera-control.md +107 -0
- package/skills/ios/hig-inputs/references/digital-crown.md +83 -0
- package/skills/ios/hig-inputs/references/eyes.md +120 -0
- package/skills/ios/hig-inputs/references/focus-and-selection.md +120 -0
- package/skills/ios/hig-inputs/references/game-controls.md +156 -0
- package/skills/ios/hig-inputs/references/gestures.md +208 -0
- package/skills/ios/hig-inputs/references/gyro-and-accelerometer.md +40 -0
- package/skills/ios/hig-inputs/references/keyboards.md +234 -0
- package/skills/ios/hig-inputs/references/nearby-interactions.md +70 -0
- package/skills/ios/hig-inputs/references/pointing-devices.md +237 -0
- package/skills/ios/hig-inputs/references/remotes.md +67 -0
- package/skills/ios/hig-inputs/references/spatial-interactions.md +70 -0
- package/skills/ios/hig-patterns/SKILL.md +104 -0
- package/skills/ios/hig-patterns/references/charting-data.md +81 -0
- package/skills/ios/hig-patterns/references/collaboration-and-sharing.md +86 -0
- package/skills/ios/hig-patterns/references/drag-and-drop.md +134 -0
- package/skills/ios/hig-patterns/references/entering-data.md +69 -0
- package/skills/ios/hig-patterns/references/feedback.md +67 -0
- package/skills/ios/hig-patterns/references/file-management.md +135 -0
- package/skills/ios/hig-patterns/references/going-full-screen.md +79 -0
- package/skills/ios/hig-patterns/references/launching.md +81 -0
- package/skills/ios/hig-patterns/references/live-viewing-apps.md +79 -0
- package/skills/ios/hig-patterns/references/loading.md +59 -0
- package/skills/ios/hig-patterns/references/managing-accounts.md +107 -0
- package/skills/ios/hig-patterns/references/managing-notifications.md +99 -0
- package/skills/ios/hig-patterns/references/modality.md +82 -0
- package/skills/ios/hig-patterns/references/multitasking.md +131 -0
- package/skills/ios/hig-patterns/references/offering-help.md +117 -0
- package/skills/ios/hig-patterns/references/onboarding.md +69 -0
- package/skills/ios/hig-patterns/references/playing-audio.md +124 -0
- package/skills/ios/hig-patterns/references/playing-haptics.md +280 -0
- package/skills/ios/hig-patterns/references/playing-video.md +180 -0
- package/skills/ios/hig-patterns/references/printing.md +50 -0
- package/skills/ios/hig-patterns/references/ratings-and-reviews.md +48 -0
- package/skills/ios/hig-patterns/references/searching.md +70 -0
- package/skills/ios/hig-patterns/references/settings.md +84 -0
- package/skills/ios/hig-patterns/references/undo-and-redo.md +58 -0
- package/skills/ios/hig-patterns/references/workouts.md +76 -0
- package/skills/ios/hig-platforms/SKILL.md +84 -0
- package/skills/ios/hig-platforms/references/designing-for-games.md +159 -0
- package/skills/ios/hig-platforms/references/designing-for-ios.md +66 -0
- package/skills/ios/hig-platforms/references/designing-for-ipados.md +64 -0
- package/skills/ios/hig-platforms/references/designing-for-macos.md +70 -0
- package/skills/ios/hig-platforms/references/designing-for-tvos.md +68 -0
- package/skills/ios/hig-platforms/references/designing-for-visionos.md +85 -0
- package/skills/ios/hig-platforms/references/designing-for-watchos.md +74 -0
- package/skills/ios/hig-project-context/SKILL.md +133 -0
- package/skills/ios/hig-technologies/SKILL.md +107 -0
- package/skills/ios/hig-technologies/references/airplay.md +125 -0
- package/skills/ios/hig-technologies/references/always-on.md +62 -0
- package/skills/ios/hig-technologies/references/apple-pay.md +441 -0
- package/skills/ios/hig-technologies/references/augmented-reality.md +247 -0
- package/skills/ios/hig-technologies/references/carekit.md +224 -0
- package/skills/ios/hig-technologies/references/carplay.md +119 -0
- package/skills/ios/hig-technologies/references/game-center.md +343 -0
- package/skills/ios/hig-technologies/references/generative-ai.md +110 -0
- package/skills/ios/hig-technologies/references/healthkit.md +120 -0
- package/skills/ios/hig-technologies/references/homekit.md +343 -0
- package/skills/ios/hig-technologies/references/icloud.md +52 -0
- package/skills/ios/hig-technologies/references/id-verifier.md +73 -0
- package/skills/ios/hig-technologies/references/imessage-apps-and-stickers.md +105 -0
- package/skills/ios/hig-technologies/references/in-app-purchase.md +263 -0
- package/skills/ios/hig-technologies/references/live-photos.md +54 -0
- package/skills/ios/hig-technologies/references/mac-catalyst.md +216 -0
- package/skills/ios/hig-technologies/references/machine-learning.md +394 -0
- package/skills/ios/hig-technologies/references/maps.md +221 -0
- package/skills/ios/hig-technologies/references/nfc.md +51 -0
- package/skills/ios/hig-technologies/references/photo-editing.md +40 -0
- package/skills/ios/hig-technologies/references/researchkit.md +134 -0
- package/skills/ios/hig-technologies/references/shareplay.md +142 -0
- package/skills/ios/hig-technologies/references/shazamkit.md +47 -0
- package/skills/ios/hig-technologies/references/sign-in-with-apple.md +288 -0
- package/skills/ios/hig-technologies/references/siri.md +523 -0
- package/skills/ios/hig-technologies/references/tap-to-pay-on-iphone.md +208 -0
- package/skills/ios/hig-technologies/references/voiceover.md +90 -0
- package/skills/ios/hig-technologies/references/wallet.md +420 -0
- package/skills/ios/ios-26-platform/SKILL.md +53 -0
- package/skills/ios/ios-26-platform/references/automatic-adoption.md +161 -0
- package/skills/ios/ios-26-platform/references/backward-compat.md +238 -0
- package/skills/ios/ios-26-platform/references/liquid-glass.md +255 -0
- package/skills/ios/ios-26-platform/references/swiftui-apis.md +277 -0
- package/skills/ios/ios-26-platform/references/toolbar-navigation.md +250 -0
- package/skills/ios/ios-bootstrap/SKILL.md +107 -0
- package/skills/ios/ios-bootstrap/references/apple-docs-mcp-config.md +28 -0
- package/skills/ios/ios-bootstrap/references/new-project-dialog.md +41 -0
- package/skills/ios/ios-bootstrap/references/xcode-mcp-config.md +29 -0
- package/skills/ios/ios-debugger-agent/LICENSE +21 -0
- package/skills/ios/ios-debugger-agent/SKILL.md +58 -0
- package/skills/ios/ios-debugger-agent/agents/openai.yaml +4 -0
- package/skills/ios/ios-entitlements-generator/SKILL.md +47 -0
- package/skills/ios/ios-info-plist-hardening/SKILL.md +130 -0
- package/skills/ios/ios-maestro-flow-author/SKILL.md +68 -0
- package/skills/ios/ios-maestro-flow-author/references/input-and-scroll.yaml +17 -0
- package/skills/ios/ios-maestro-flow-author/references/modal-and-dismiss.yaml +14 -0
- package/skills/ios/ios-maestro-flow-author/references/onboarding-flow.yaml +16 -0
- package/skills/ios/ios-maestro-flow-author/references/tab-navigation.yaml +13 -0
- package/skills/ios/ios-maestro-flow-author/references/tap-and-assert.yaml +9 -0
- package/skills/ios/swift-accessibility/LICENSE +21 -0
- package/skills/ios/swift-accessibility/SKILL.md +371 -0
- package/skills/ios/swift-accessibility/examples/before-after-appkit.md +446 -0
- package/skills/ios/swift-accessibility/examples/before-after-swiftui.md +441 -0
- package/skills/ios/swift-accessibility/examples/before-after-uikit.md +464 -0
- package/skills/ios/swift-accessibility/references/assistive-access.md +441 -0
- package/skills/ios/swift-accessibility/references/display-settings.md +491 -0
- package/skills/ios/swift-accessibility/references/dynamic-type.md +420 -0
- package/skills/ios/swift-accessibility/references/media-accessibility.md +421 -0
- package/skills/ios/swift-accessibility/references/motor-input.md +393 -0
- package/skills/ios/swift-accessibility/references/nutrition-labels.md +362 -0
- package/skills/ios/swift-accessibility/references/platform-specifics.md +515 -0
- package/skills/ios/swift-accessibility/references/semantic-structure.md +585 -0
- package/skills/ios/swift-accessibility/references/testing-auditing.md +507 -0
- package/skills/ios/swift-accessibility/references/voice-control.md +317 -0
- package/skills/ios/swift-accessibility/references/voiceover-swiftui.md +584 -0
- package/skills/ios/swift-accessibility/references/voiceover-uikit.md +519 -0
- package/skills/ios/swift-accessibility/references/wcag-mapping.md +167 -0
- package/skills/ios/swift-accessibility/resources/audit-template.swift +128 -0
- package/skills/ios/swift-accessibility/resources/qa-checklist.md +258 -0
- package/skills/ios/swift-actor-persistence/SKILL.md +143 -0
- package/skills/ios/swift-concurrency/LICENSE +21 -0
- package/skills/ios/swift-concurrency/SKILL.md +171 -0
- package/skills/ios/swift-concurrency/references/_index.md +50 -0
- package/skills/ios/swift-concurrency/references/actors.md +660 -0
- package/skills/ios/swift-concurrency/references/async-algorithms.md +847 -0
- package/skills/ios/swift-concurrency/references/async-await-basics.md +266 -0
- package/skills/ios/swift-concurrency/references/async-sequences.md +710 -0
- package/skills/ios/swift-concurrency/references/core-data.md +560 -0
- package/skills/ios/swift-concurrency/references/glossary.md +135 -0
- package/skills/ios/swift-concurrency/references/linting.md +155 -0
- package/skills/ios/swift-concurrency/references/memory-management.md +569 -0
- package/skills/ios/swift-concurrency/references/migration.md +1104 -0
- package/skills/ios/swift-concurrency/references/performance.md +593 -0
- package/skills/ios/swift-concurrency/references/sendable.md +598 -0
- package/skills/ios/swift-concurrency/references/tasks.md +636 -0
- package/skills/ios/swift-concurrency/references/testing.md +592 -0
- package/skills/ios/swift-concurrency/references/threading.md +495 -0
- package/skills/ios/swift-concurrency-6-2/SKILL.md +216 -0
- package/skills/ios/swift-protocol-di-testing/SKILL.md +190 -0
- package/skills/ios/swift-security-expert/LICENSE +21 -0
- package/skills/ios/swift-security-expert/SKILL.md +470 -0
- package/skills/ios/swift-security-expert/references/biometric-authentication.md +565 -0
- package/skills/ios/swift-security-expert/references/certificate-trust.md +592 -0
- package/skills/ios/swift-security-expert/references/common-anti-patterns.md +690 -0
- package/skills/ios/swift-security-expert/references/compliance-owasp-mapping.md +537 -0
- package/skills/ios/swift-security-expert/references/credential-storage-patterns.md +721 -0
- package/skills/ios/swift-security-expert/references/cryptokit-public-key.md +505 -0
- package/skills/ios/swift-security-expert/references/cryptokit-symmetric.md +497 -0
- package/skills/ios/swift-security-expert/references/keychain-access-control.md +508 -0
- package/skills/ios/swift-security-expert/references/keychain-fundamentals.md +596 -0
- package/skills/ios/swift-security-expert/references/keychain-item-classes.md +476 -0
- package/skills/ios/swift-security-expert/references/keychain-sharing.md +458 -0
- package/skills/ios/swift-security-expert/references/migration-legacy-stores.md +727 -0
- package/skills/ios/swift-security-expert/references/secure-enclave.md +539 -0
- package/skills/ios/swift-security-expert/references/testing-security-code.md +781 -0
- package/skills/ios/swift-testing-expert/LICENSE +21 -0
- package/skills/ios/swift-testing-expert/SKILL.md +79 -0
- package/skills/ios/swift-testing-expert/references/_index.md +12 -0
- package/skills/ios/swift-testing-expert/references/async-testing-and-waiting.md +127 -0
- package/skills/ios/swift-testing-expert/references/expectations.md +145 -0
- package/skills/ios/swift-testing-expert/references/fundamentals.md +141 -0
- package/skills/ios/swift-testing-expert/references/migration-from-xctest.md +127 -0
- package/skills/ios/swift-testing-expert/references/parallelization-and-isolation.md +95 -0
- package/skills/ios/swift-testing-expert/references/parameterized-testing.md +284 -0
- package/skills/ios/swift-testing-expert/references/performance-and-best-practices.md +187 -0
- package/skills/ios/swift-testing-expert/references/traits-and-tags.md +114 -0
- package/skills/ios/swift-testing-expert/references/xcode-workflows.md +70 -0
- package/skills/ios/swiftdata-pro/LICENSE +21 -0
- package/skills/ios/swiftdata-pro/SKILL.md +102 -0
- package/skills/ios/swiftdata-pro/agents/openai.yaml +10 -0
- package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.png +0 -0
- package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.svg +29 -0
- package/skills/ios/swiftdata-pro/references/class-inheritance.md +104 -0
- package/skills/ios/swiftdata-pro/references/cloudkit.md +10 -0
- package/skills/ios/swiftdata-pro/references/core-rules.md +20 -0
- package/skills/ios/swiftdata-pro/references/indexing.md +27 -0
- package/skills/ios/swiftdata-pro/references/predicates.md +73 -0
- package/skills/ios/swiftui-design-principles/AGENTS.md +21 -0
- package/skills/ios/swiftui-design-principles/LICENSE +21 -0
- package/skills/ios/swiftui-design-principles/README.md +41 -0
- package/skills/ios/swiftui-design-principles/SKILL.md +605 -0
- package/skills/ios/swiftui-design-principles/metadata.json +10 -0
- package/skills/ios/swiftui-design-tokens/SKILL.md +475 -0
- package/skills/ios/swiftui-liquid-glass/LICENSE +21 -0
- package/skills/ios/swiftui-liquid-glass/SKILL.md +95 -0
- package/skills/ios/swiftui-liquid-glass/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-liquid-glass/references/liquid-glass.md +280 -0
- package/skills/ios/swiftui-performance-audit/LICENSE +21 -0
- package/skills/ios/swiftui-performance-audit/SKILL.md +111 -0
- package/skills/ios/swiftui-performance-audit/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-performance-audit/references/code-smells.md +150 -0
- package/skills/ios/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
- package/skills/ios/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
- package/skills/ios/swiftui-performance-audit/references/profiling-intake.md +44 -0
- package/skills/ios/swiftui-performance-audit/references/report-template.md +47 -0
- package/skills/ios/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
- package/skills/ios/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
- package/skills/ios/swiftui-pro/LICENSE +21 -0
- package/skills/ios/swiftui-pro/SKILL.md +108 -0
- package/skills/ios/swiftui-pro/agents/openai.yaml +10 -0
- package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
- package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
- package/skills/ios/swiftui-pro/references/accessibility.md +13 -0
- package/skills/ios/swiftui-pro/references/api.md +39 -0
- package/skills/ios/swiftui-pro/references/data.md +43 -0
- package/skills/ios/swiftui-pro/references/design.md +31 -0
- package/skills/ios/swiftui-pro/references/hygiene.md +9 -0
- package/skills/ios/swiftui-pro/references/navigation.md +14 -0
- package/skills/ios/swiftui-pro/references/performance.md +46 -0
- package/skills/ios/swiftui-pro/references/swift.md +56 -0
- package/skills/ios/swiftui-pro/references/views.md +35 -0
- package/skills/ios/swiftui-ui-patterns/LICENSE +21 -0
- package/skills/ios/swiftui-ui-patterns/SKILL.md +100 -0
- package/skills/ios/swiftui-ui-patterns/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-ui-patterns/references/app-wiring.md +201 -0
- package/skills/ios/swiftui-ui-patterns/references/async-state.md +96 -0
- package/skills/ios/swiftui-ui-patterns/references/components-index.md +50 -0
- package/skills/ios/swiftui-ui-patterns/references/controls.md +57 -0
- package/skills/ios/swiftui-ui-patterns/references/deeplinks.md +66 -0
- package/skills/ios/swiftui-ui-patterns/references/focus.md +90 -0
- package/skills/ios/swiftui-ui-patterns/references/form.md +97 -0
- package/skills/ios/swiftui-ui-patterns/references/grids.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/haptics.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/input-toolbar.md +51 -0
- package/skills/ios/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
- package/skills/ios/swiftui-ui-patterns/references/list.md +86 -0
- package/skills/ios/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
- package/skills/ios/swiftui-ui-patterns/references/macos-settings.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/matched-transitions.md +59 -0
- package/skills/ios/swiftui-ui-patterns/references/media.md +73 -0
- package/skills/ios/swiftui-ui-patterns/references/menu-bar.md +101 -0
- package/skills/ios/swiftui-ui-patterns/references/navigationstack.md +159 -0
- package/skills/ios/swiftui-ui-patterns/references/overlay.md +45 -0
- package/skills/ios/swiftui-ui-patterns/references/performance.md +62 -0
- package/skills/ios/swiftui-ui-patterns/references/previews.md +48 -0
- package/skills/ios/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
- package/skills/ios/swiftui-ui-patterns/references/scrollview.md +87 -0
- package/skills/ios/swiftui-ui-patterns/references/searchable.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/sheets.md +155 -0
- package/skills/ios/swiftui-ui-patterns/references/split-views.md +72 -0
- package/skills/ios/swiftui-ui-patterns/references/tabview.md +114 -0
- package/skills/ios/swiftui-ui-patterns/references/theming.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/title-menus.md +93 -0
- package/skills/ios/swiftui-ui-patterns/references/top-bar.md +49 -0
- package/skills/ios/swiftui-view-refactor/LICENSE +21 -0
- package/skills/ios/swiftui-view-refactor/SKILL.md +207 -0
- package/skills/ios/swiftui-view-refactor/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-view-refactor/references/mv-patterns.md +161 -0
- package/skills/ios/widgetkit/LICENSE +131 -0
- package/skills/ios/widgetkit/SKILL.md +502 -0
- package/skills/ios/widgetkit/references/widgetkit-advanced.md +871 -0
- package/skills/ios/writing-for-interfaces/SKILL.md +75 -0
- package/skills/web/accessibility/SKILL.md +146 -0
- package/skills/web/aceternity-ui/SKILL.md +719 -0
- package/skills/web/aceternity-ui/metadata.json +10 -0
- package/skills/web/api-design/SKILL.md +523 -0
- package/skills/web/chart-accessibility/SKILL.md +332 -0
- package/skills/web/composition-patterns/AGENTS.md +946 -0
- package/skills/web/composition-patterns/README.md +60 -0
- package/skills/web/composition-patterns/SKILL.md +89 -0
- package/skills/web/composition-patterns/metadata.json +11 -0
- package/skills/web/composition-patterns/rules/_sections.md +29 -0
- package/skills/web/composition-patterns/rules/_template.md +24 -0
- package/skills/web/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/skills/web/composition-patterns/rules/architecture-compound-components.md +112 -0
- package/skills/web/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/skills/web/composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/skills/web/composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/skills/web/composition-patterns/rules/state-context-interface.md +191 -0
- package/skills/web/composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/skills/web/composition-patterns/rules/state-lift-state.md +125 -0
- package/skills/web/cost-aware-llm-pipeline/SKILL.md +183 -0
- package/skills/web/database-migrations/SKILL.md +429 -0
- package/skills/web/deployment-patterns/SKILL.md +427 -0
- package/skills/web/docker-patterns/SKILL.md +364 -0
- package/skills/web/e2e-testing/SKILL.md +326 -0
- package/skills/web/lighthouse-ci/SKILL.md +361 -0
- package/skills/web/mcp-server-patterns/SKILL.md +69 -0
- package/skills/web/next-best-practices/SKILL.md +153 -0
- package/skills/web/next-best-practices/async-patterns.md +87 -0
- package/skills/web/next-best-practices/bundling.md +180 -0
- package/skills/web/next-best-practices/data-patterns.md +297 -0
- package/skills/web/next-best-practices/debug-tricks.md +105 -0
- package/skills/web/next-best-practices/directives.md +73 -0
- package/skills/web/next-best-practices/error-handling.md +227 -0
- package/skills/web/next-best-practices/file-conventions.md +140 -0
- package/skills/web/next-best-practices/font.md +245 -0
- package/skills/web/next-best-practices/functions.md +108 -0
- package/skills/web/next-best-practices/hydration-error.md +91 -0
- package/skills/web/next-best-practices/image.md +173 -0
- package/skills/web/next-best-practices/metadata.md +301 -0
- package/skills/web/next-best-practices/parallel-routes.md +287 -0
- package/skills/web/next-best-practices/route-handlers.md +146 -0
- package/skills/web/next-best-practices/rsc-boundaries.md +159 -0
- package/skills/web/next-best-practices/runtime-selection.md +39 -0
- package/skills/web/next-best-practices/scripts.md +141 -0
- package/skills/web/next-best-practices/self-hosting.md +371 -0
- package/skills/web/next-best-practices/suspense-boundaries.md +67 -0
- package/skills/web/next-cache-components/SKILL.md +411 -0
- package/skills/web/postgres-best-practices/SKILL.md +14 -0
- package/skills/web/postgres-best-practices/references/schema-design.md +9 -0
- package/skills/web/react-best-practices/AGENTS.md +3810 -0
- package/skills/web/react-best-practices/README.md +123 -0
- package/skills/web/react-best-practices/SKILL.md +149 -0
- package/skills/web/react-best-practices/metadata.json +15 -0
- package/skills/web/react-best-practices/rules/_sections.md +46 -0
- package/skills/web/react-best-practices/rules/_template.md +28 -0
- package/skills/web/react-best-practices/rules/advanced-effect-event-deps.md +56 -0
- package/skills/web/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/skills/web/react-best-practices/rules/advanced-init-once.md +42 -0
- package/skills/web/react-best-practices/rules/advanced-use-latest.md +39 -0
- package/skills/web/react-best-practices/rules/async-api-routes.md +38 -0
- package/skills/web/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
- package/skills/web/react-best-practices/rules/async-defer-await.md +82 -0
- package/skills/web/react-best-practices/rules/async-dependencies.md +51 -0
- package/skills/web/react-best-practices/rules/async-parallel.md +28 -0
- package/skills/web/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/skills/web/react-best-practices/rules/bundle-analyzable-paths.md +63 -0
- package/skills/web/react-best-practices/rules/bundle-barrel-imports.md +60 -0
- package/skills/web/react-best-practices/rules/bundle-conditional.md +31 -0
- package/skills/web/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/skills/web/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/skills/web/react-best-practices/rules/bundle-preload.md +50 -0
- package/skills/web/react-best-practices/rules/client-event-listeners.md +74 -0
- package/skills/web/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/skills/web/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/skills/web/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/skills/web/react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/skills/web/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/skills/web/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/skills/web/react-best-practices/rules/js-cache-storage.md +70 -0
- package/skills/web/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/skills/web/react-best-practices/rules/js-early-exit.md +50 -0
- package/skills/web/react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/skills/web/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/skills/web/react-best-practices/rules/js-index-maps.md +37 -0
- package/skills/web/react-best-practices/rules/js-length-check-first.md +49 -0
- package/skills/web/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/skills/web/react-best-practices/rules/js-request-idle-callback.md +105 -0
- package/skills/web/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/skills/web/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/skills/web/react-best-practices/rules/rendering-activity.md +26 -0
- package/skills/web/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/skills/web/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/skills/web/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/skills/web/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/skills/web/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/skills/web/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/skills/web/react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/skills/web/react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/skills/web/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/skills/web/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/skills/web/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/skills/web/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/skills/web/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/skills/web/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/skills/web/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/skills/web/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/skills/web/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/skills/web/react-best-practices/rules/rerender-memo.md +44 -0
- package/skills/web/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/skills/web/react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/skills/web/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/skills/web/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/skills/web/react-best-practices/rules/rerender-transitions.md +40 -0
- package/skills/web/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/skills/web/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/skills/web/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/skills/web/react-best-practices/rules/server-auth-actions.md +96 -0
- package/skills/web/react-best-practices/rules/server-cache-lru.md +41 -0
- package/skills/web/react-best-practices/rules/server-cache-react.md +76 -0
- package/skills/web/react-best-practices/rules/server-dedup-props.md +65 -0
- package/skills/web/react-best-practices/rules/server-hoist-static-io.md +149 -0
- package/skills/web/react-best-practices/rules/server-no-shared-module-state.md +50 -0
- package/skills/web/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/skills/web/react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
- package/skills/web/react-best-practices/rules/server-serialization.md +38 -0
- package/skills/web/seo/SKILL.md +154 -0
- package/skills/web/web-design-guidelines/SKILL.md +39 -0
- package/skills/web/zap-scan-config/SKILL.md +444 -0
- package/skills/web/zap-scan-config/assets/.gitkeep +9 -0
- package/skills/web/zap-scan-config/assets/github_action.yml +207 -0
- package/skills/web/zap-scan-config/assets/gitlab_ci.yml +226 -0
- package/skills/web/zap-scan-config/assets/zap_automation.yaml +196 -0
- package/skills/web/zap-scan-config/assets/zap_context.xml +192 -0
- package/skills/web/zap-scan-config/references/EXAMPLE.md +40 -0
- package/skills/web/zap-scan-config/references/api_testing_guide.md +475 -0
- package/skills/web/zap-scan-config/references/authentication_guide.md +431 -0
- package/skills/web/zap-scan-config/references/false_positive_handling.md +427 -0
- package/skills/web/zap-scan-config/references/owasp_mapping.md +255 -0
- package/src/lrr/aggregator.ts +80 -0
- package/src/orchestrator/hooks/context-header.ts +95 -0
- package/src/orchestrator/hooks/token-accounting-emitter.ts +77 -0
- package/src/orchestrator/hooks/token-accounting.ts +101 -0
- package/src/orchestrator/mcp/cycle-counter.ts +129 -0
- package/src/orchestrator/mcp/scribe.ts +283 -0
- package/src/orchestrator/mcp/state-save.ts +149 -0
- package/src/orchestrator/mcp/write-lease.ts +167 -0
- package/src/orchestrator/phase4-shared-context.ts +41 -0
- package/src/orchestrator/schemas/backward-edge.ts +46 -0
- package/agents/agentic-identity-trust.md +0 -121
- package/agents/data-consolidation-agent.md +0 -39
- package/agents/design-image-prompt-engineer.md +0 -105
- package/agents/design-visual-storyteller.md +0 -147
- package/agents/design-whimsy-injector.md +0 -89
- package/agents/engineering-autonomous-optimization-architect.md +0 -105
- package/agents/market-intel.md +0 -35
- package/agents/marketing-instagram-curator.md +0 -111
- package/agents/marketing-reddit-community-builder.md +0 -121
- package/agents/marketing-social-media-strategist.md +0 -74
- package/agents/marketing-tiktok-strategist.md +0 -123
- package/agents/marketing-twitter-engager.md +0 -124
- package/agents/marketing-wechat-official-account.md +0 -143
- package/agents/marketing-xiaohongshu-specialist.md +0 -136
- package/agents/marketing-zhihu-strategist.md +0 -160
- package/agents/product-behavioral-nudge-engine.md +0 -78
- package/agents/project-management-experiment-tracker.md +0 -102
- package/agents/report-distribution-agent.md +0 -43
- package/agents/risk-analysis.md +0 -45
- package/agents/sales-data-extraction-agent.md +0 -46
- package/agents/specialized-cultural-intelligence-strategist.md +0 -65
- package/agents/specialized-developer-advocate.md +0 -146
- package/agents/support-analytics-reporter.md +0 -133
- package/agents/support-executive-summary-generator.md +0 -64
- package/agents/support-finance-tracker.md +0 -145
- package/agents/support-legal-compliance-checker.md +0 -129
- package/agents/support-support-responder.md +0 -91
- package/agents/testing-accessibility-auditor.md +0 -110
- package/agents/testing-test-results-analyzer.md +0 -97
- package/agents/testing-tool-evaluator.md +0 -76
- package/agents/testing-workflow-optimizer.md +0 -99
- package/agents/user-research.md +0 -40
|
@@ -0,0 +1,776 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/*
|
|
3
|
+
* buildanything: PreToolUse writer-owner hook handler (tasks 2.1.1–2.1.3, 2.2.2, 2.4.1, 2.4.2, 3.3.1, 5.2.1).
|
|
4
|
+
*
|
|
5
|
+
* Reads a Claude Code tool-call JSON from stdin, consults the writer-owner
|
|
6
|
+
* table in docs/migration/phase-graph.yaml, and denies Write|Edit|MultiEdit
|
|
7
|
+
* when the current phase (from docs/plans/.build-state.json) does not match
|
|
8
|
+
* the owning phase for file_path.
|
|
9
|
+
*
|
|
10
|
+
* Task 2.1.3 adds default-deny for paths NOT in the writer-owner table when
|
|
11
|
+
* they fall under PROTECTED_PREFIXES (docs/plans/, docs/migration/, commands/,
|
|
12
|
+
* agents/, protocols/, hooks/, src/orchestrator/, .claude-plugin/, CLAUDE.md).
|
|
13
|
+
* Phase-scratch globs (phase_internal_scratch.path_glob from phase-graph.yaml,
|
|
14
|
+
* surfaced as cache.scratch_globs) are exempt from default-deny. Source code
|
|
15
|
+
* under the user's project (outside the protected prefix set) is allowed.
|
|
16
|
+
*
|
|
17
|
+
* Task 2.4.1 layers a task-level write-lease check on top of the phase-level
|
|
18
|
+
* decision. After the phase-level check returns ALLOW (either via a matching
|
|
19
|
+
* writer-owner entry or by falling outside PROTECTED_PREFIXES), the handler
|
|
20
|
+
* consults docs/plans/.build-state.json.active_write_leases[] and denies when
|
|
21
|
+
* the calling task_id lacks a lease covering file_path.
|
|
22
|
+
*
|
|
23
|
+
* Task 2.4.2 tightens task_id derivation to STRICT mode: task_id comes ONLY
|
|
24
|
+
* from stdin `parent_tool_use_id`, which per SDK subagent propagation
|
|
25
|
+
* identifies the parent Agent dispatch. The transitional 2.4.1 fallbacks
|
|
26
|
+
* (`tool_use_id`, env `BUILDANYTHING_TASK_ID`) are removed because
|
|
27
|
+
* `tool_use_id` is per-tool-invocation (too granular — the writer-owner
|
|
28
|
+
* table encodes per-AGENT permissions) and env vars are forgeable / leaky.
|
|
29
|
+
* When `parent_tool_use_id` is absent (main orchestrator context), task_id
|
|
30
|
+
* is null and the lease check short-circuits to allow (same as 2.4.1's
|
|
31
|
+
* "no task_id known" branch).
|
|
32
|
+
*
|
|
33
|
+
* Task 3.3.1 adds a HARD-DENY branch for raw Write|Edit|MultiEdit targeting
|
|
34
|
+
* docs/plans/.build-state.json. All mutations to that file must route through
|
|
35
|
+
* the `state_save` MCP tool (src/orchestrator/mcp/state-save.ts) which owns
|
|
36
|
+
* atomic write semantics + SHA-256 integrity. The lease mechanism from 2.4.1
|
|
37
|
+
* does NOT override this deny — the rule is "only state_save writes the state
|
|
38
|
+
* file, period". MCP tool calls are not Write/Edit and therefore never enter
|
|
39
|
+
* this hook. Emergency rollback via BUILDANYTHING_ALLOW_RAW_STATE_WRITES=on
|
|
40
|
+
* downgrades to warn (off by default).
|
|
41
|
+
*
|
|
42
|
+
* Task 5.2.1 extends writer-owner enforcement to agent-role owners. When the
|
|
43
|
+
* matched artifact's writers list contains tokens that are neither phases
|
|
44
|
+
* (`phase-N`) nor the known non-agent pseudos (orchestrator,
|
|
45
|
+
* orchestrator-scribe, every-phase, auto-rendered-view, all-subagents-auto),
|
|
46
|
+
* those tokens are treated as `subagent_type` names. The hook reads the
|
|
47
|
+
* subagent_type that subagent-start staged to
|
|
48
|
+
* `.buildanything/subagent-start-cache/<parent_tool_use_id>.json` and denies
|
|
49
|
+
* when the staged subagent_type is not in the writers list. This restricts
|
|
50
|
+
* `lrr/*.json` to the 5 LRR chapter subagents (the "chapter-judge" role) —
|
|
51
|
+
* aggregator + orchestrator are NOT writers of chapter files.
|
|
52
|
+
*
|
|
53
|
+
* Exit codes per Claude Code PreToolUse protocol:
|
|
54
|
+
* 0 — allow
|
|
55
|
+
* 2 — deny (stderr shown to Claude)
|
|
56
|
+
*
|
|
57
|
+
* Rollback:
|
|
58
|
+
* BUILDANYTHING_ENFORCE_WRITER_OWNER=false → ALL denies (writer-owner AND
|
|
59
|
+
* lease) downgrade to stdout warnings and exit 0.
|
|
60
|
+
* BUILDANYTHING_ENFORCE_WRITE_LEASE=false → only the lease check downgrades
|
|
61
|
+
* to stdout warning; writer-owner denies still block.
|
|
62
|
+
* BUILDANYTHING_SCRIBE_SINGLE_WRITER=off → only the decisions.jsonl
|
|
63
|
+
* writer-owner deny downgrades to stdout warning (task 2.2.2). Every
|
|
64
|
+
* other artifact still blocks on mismatch. Use this flag to regress to
|
|
65
|
+
* dual-write mode if the scribe MCP pipeline misbehaves in production
|
|
66
|
+
* without disabling the rest of the writer-owner table.
|
|
67
|
+
* BUILDANYTHING_ALLOW_RAW_STATE_WRITES=on → 3.3.1 hard-deny on raw
|
|
68
|
+
* Write|Edit targeting docs/plans/.build-state.json downgrades to warn.
|
|
69
|
+
* Default OFF. Emergency debugging only — normal operation MUST route
|
|
70
|
+
* state mutations through the state_save MCP.
|
|
71
|
+
* BUILDANYTHING_STRICT_TASK_ID=off → restore 2.4.1 env fallback: when
|
|
72
|
+
* stdin lacks `parent_tool_use_id`, consult env BUILDANYTHING_TASK_ID
|
|
73
|
+
* before giving up. Default is on (strict, SDK-only). The `tool_use_id`
|
|
74
|
+
* fallback is NOT restored by this flag — it was always wrong.
|
|
75
|
+
*
|
|
76
|
+
* Task 2.1.2: boot-compiled cache at .buildanything/writer-owner.json is the
|
|
77
|
+
* fast path. Cache misses (missing / corrupt / stale-mtime) fall back to live
|
|
78
|
+
* YAML parse with a single stderr warning.
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
import { readFileSync, realpathSync, statSync } from "node:fs";
|
|
82
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
83
|
+
import process from "node:process";
|
|
84
|
+
import { parse as parseYaml } from "yaml";
|
|
85
|
+
|
|
86
|
+
const WATCHED_TOOLS = new Set(["Write", "Edit", "MultiEdit"]);
|
|
87
|
+
const ENFORCE_ENV = "BUILDANYTHING_ENFORCE_WRITER_OWNER";
|
|
88
|
+
const ENFORCE_LEASE_ENV = "BUILDANYTHING_ENFORCE_WRITE_LEASE";
|
|
89
|
+
const SCRIBE_SINGLE_WRITER_ENV = "BUILDANYTHING_SCRIBE_SINGLE_WRITER";
|
|
90
|
+
const DECISIONS_JSONL_PATH = "docs/plans/decisions.jsonl";
|
|
91
|
+
// Task 3.3.1: raw Write|Edit on .build-state.json is unconditionally denied;
|
|
92
|
+
// this flag downgrades the deny to warn. Default OFF — emergency use only.
|
|
93
|
+
const ALLOW_RAW_STATE_WRITES_ENV = "BUILDANYTHING_ALLOW_RAW_STATE_WRITES";
|
|
94
|
+
const BUILD_STATE_PATH = "docs/plans/.build-state.json";
|
|
95
|
+
const TASK_ID_ENV = "BUILDANYTHING_TASK_ID";
|
|
96
|
+
// Task 2.4.2: default ON (strict, parent_tool_use_id only). Set to "off" to
|
|
97
|
+
// restore 2.4.1's env-var fallback. The per-invocation `tool_use_id` fallback
|
|
98
|
+
// is unconditionally removed — no flag re-enables it.
|
|
99
|
+
const STRICT_TASK_ID_ENV = "BUILDANYTHING_STRICT_TASK_ID";
|
|
100
|
+
|
|
101
|
+
// Claude Code PreToolUse stdin shape (only the fields we consume).
|
|
102
|
+
interface ToolInput {
|
|
103
|
+
file_path?: string;
|
|
104
|
+
}
|
|
105
|
+
interface ToolCall {
|
|
106
|
+
tool_name?: string;
|
|
107
|
+
tool_input?: ToolInput;
|
|
108
|
+
// Per SDK subagent propagation, `parent_tool_use_id` identifies the parent
|
|
109
|
+
// Agent dispatch that owns this tool call. 2.4.2 relies on this field
|
|
110
|
+
// exclusively (see `deriveTaskId`). Absent in main-orchestrator context.
|
|
111
|
+
parent_tool_use_id?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Persisted lease shape per MIGRATION-PLAN-FINAL §4 A5:
|
|
115
|
+
// `.build-state.json.active_write_leases[] = {task_id, file_paths[], ...}`.
|
|
116
|
+
// We also accept the in-memory shape from write-lease.ts (`{holder, paths[]}`)
|
|
117
|
+
// for robustness during cutover.
|
|
118
|
+
interface PersistedLease {
|
|
119
|
+
task_id?: string;
|
|
120
|
+
file_paths?: string[];
|
|
121
|
+
holder?: string;
|
|
122
|
+
paths?: string[];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
interface NormalizedLease {
|
|
126
|
+
task_id: string;
|
|
127
|
+
file_paths: string[];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
interface ArtifactEntry {
|
|
131
|
+
path: string;
|
|
132
|
+
writer?: string;
|
|
133
|
+
writers?: string[];
|
|
134
|
+
// Populated when the entry comes from the boot-compiled cache.
|
|
135
|
+
regex?: string;
|
|
136
|
+
is_glob?: boolean;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface PhaseScratch {
|
|
140
|
+
path_glob?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
interface PhaseGraph {
|
|
144
|
+
artifacts?: ArtifactEntry[];
|
|
145
|
+
phase_internal_scratch?: PhaseScratch;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
interface CachedArtifact {
|
|
149
|
+
path: string;
|
|
150
|
+
writers: string[];
|
|
151
|
+
is_glob: boolean;
|
|
152
|
+
regex?: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
interface CachedScratchGlob {
|
|
156
|
+
glob: string;
|
|
157
|
+
regex: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
interface WriterOwnerCache {
|
|
161
|
+
version: number;
|
|
162
|
+
source_sha: string;
|
|
163
|
+
source_mtime: number;
|
|
164
|
+
compiled_at: string;
|
|
165
|
+
artifacts: CachedArtifact[];
|
|
166
|
+
// Task 2.1.3 additive field. Older caches may omit it.
|
|
167
|
+
scratch_globs?: CachedScratchGlob[];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Paths under these prefixes are subject to default-deny when absent from the
|
|
171
|
+
// writer-owner table and not a scratch glob. Everything outside this set is
|
|
172
|
+
// assumed to be user-project source code and allowed through.
|
|
173
|
+
const PROTECTED_PREFIXES = [
|
|
174
|
+
"docs/plans/",
|
|
175
|
+
"docs/migration/",
|
|
176
|
+
".claude-plugin/",
|
|
177
|
+
"commands/",
|
|
178
|
+
"agents/",
|
|
179
|
+
"protocols/",
|
|
180
|
+
"hooks/",
|
|
181
|
+
"src/orchestrator/",
|
|
182
|
+
];
|
|
183
|
+
const PROTECTED_FILES = new Set(["CLAUDE.md"]);
|
|
184
|
+
|
|
185
|
+
const CACHE_VERSION = 1;
|
|
186
|
+
const CACHE_REL_PATH = ".buildanything/writer-owner.json";
|
|
187
|
+
const SUBAGENT_START_CACHE_DIR_REL = ".buildanything/subagent-start-cache";
|
|
188
|
+
|
|
189
|
+
// Tokens in `writers:` / `writer:` that are NOT agent-role names. Anything
|
|
190
|
+
// outside this set AND not matching /^phase--?\d+$/ is treated as a
|
|
191
|
+
// subagent_type (task 5.2.1).
|
|
192
|
+
const NON_AGENT_OWNER_PSEUDOS = new Set([
|
|
193
|
+
"orchestrator",
|
|
194
|
+
"orchestrator-scribe",
|
|
195
|
+
"every-phase",
|
|
196
|
+
"auto-rendered-view",
|
|
197
|
+
"all-subagents-auto",
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
interface BuildState {
|
|
201
|
+
current_phase?: string | number;
|
|
202
|
+
phase?: string | number;
|
|
203
|
+
active_write_leases?: PersistedLease[];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function readStdin(): string {
|
|
207
|
+
try {
|
|
208
|
+
return readFileSync(0, "utf8");
|
|
209
|
+
} catch {
|
|
210
|
+
return "";
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function pluginRoot(): string {
|
|
215
|
+
// Prefer env injected by Claude Code; fall back to walking up from __dirname.
|
|
216
|
+
const fromEnv = process.env.CLAUDE_PLUGIN_ROOT;
|
|
217
|
+
if (fromEnv) return fromEnv;
|
|
218
|
+
return resolve(__dirname, "..");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function projectRoot(): string {
|
|
222
|
+
// User's project cwd — where docs/plans/.build-state.json lives. The plugin
|
|
223
|
+
// repo itself also contains a phase-graph.yaml, but not a build-state.
|
|
224
|
+
return process.cwd();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function globToRegex(pattern: string): RegExp {
|
|
228
|
+
// Simple glob: escape regex metachars except `*`, translate `*` → `[^/]*`,
|
|
229
|
+
// `**` → `.*`, `{a,b}` → `(?:a|b)`. Anchored on both sides.
|
|
230
|
+
let out = "";
|
|
231
|
+
for (let i = 0; i < pattern.length; i += 1) {
|
|
232
|
+
const ch = pattern[i];
|
|
233
|
+
if (ch === "*") {
|
|
234
|
+
if (pattern[i + 1] === "*") {
|
|
235
|
+
out += ".*";
|
|
236
|
+
i += 1;
|
|
237
|
+
} else {
|
|
238
|
+
out += "[^/]*";
|
|
239
|
+
}
|
|
240
|
+
} else if (ch === "{") {
|
|
241
|
+
const close = pattern.indexOf("}", i);
|
|
242
|
+
if (close === -1) {
|
|
243
|
+
out += "\\{";
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const parts = pattern.slice(i + 1, close).split(",").map((p) =>
|
|
247
|
+
p.replace(/[.+?^${}()|[\]\\]/g, (m) => `\\${m}`),
|
|
248
|
+
);
|
|
249
|
+
out += `(?:${parts.join("|")})`;
|
|
250
|
+
i = close;
|
|
251
|
+
} else if (/[.+?^${}()|[\]\\]/.test(ch)) {
|
|
252
|
+
out += `\\${ch}`;
|
|
253
|
+
} else {
|
|
254
|
+
out += ch;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return new RegExp(`^${out}$`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function findArtifact(filePath: string, artifacts: ArtifactEntry[]): ArtifactEntry | null {
|
|
261
|
+
// Exact match first, then glob match. Exact wins if both are present.
|
|
262
|
+
const exact = artifacts.find((a) => a.path === filePath);
|
|
263
|
+
if (exact) return exact;
|
|
264
|
+
for (const a of artifacts) {
|
|
265
|
+
const isGlob = a.is_glob ?? (a.path.includes("*") || a.path.includes("["));
|
|
266
|
+
if (!isGlob) continue;
|
|
267
|
+
let re: RegExp;
|
|
268
|
+
if (a.regex) {
|
|
269
|
+
re = new RegExp(a.regex);
|
|
270
|
+
} else {
|
|
271
|
+
// Treat `[task-id]` placeholder in path-with-id entries as `*` for matching.
|
|
272
|
+
const normalized = a.path.replace(/\[[^\]]+\]/g, "*");
|
|
273
|
+
re = globToRegex(normalized);
|
|
274
|
+
}
|
|
275
|
+
if (re.test(filePath)) return a;
|
|
276
|
+
}
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function normalizePhase(raw: unknown): string | null {
|
|
281
|
+
if (raw === undefined || raw === null) return null;
|
|
282
|
+
const s = String(raw).trim();
|
|
283
|
+
if (!s) return null;
|
|
284
|
+
// Accept "phase-2", "2", "P2". Normalize to "phase-N". Must match the
|
|
285
|
+
// whole string; otherwise agent names containing digits (e.g. "a11y-architect")
|
|
286
|
+
// or compound pseudos like "phase-6-aggregator" get mis-classified as phases
|
|
287
|
+
// (task 5.2.1: the latter was a latent bug surfaced by agent-role writers
|
|
288
|
+
// landing in the phase-graph).
|
|
289
|
+
const m = s.match(/^(?:phase-?|P)?(-?\d+)$/i);
|
|
290
|
+
if (!m) return null;
|
|
291
|
+
return `phase-${m[1]}`;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function ownerPhases(entry: ArtifactEntry): string[] {
|
|
295
|
+
const raw: string[] = [];
|
|
296
|
+
if (entry.writer) raw.push(entry.writer);
|
|
297
|
+
if (entry.writers) raw.push(...entry.writers);
|
|
298
|
+
return raw.map((w) => String(w).trim()).filter(Boolean);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
interface LoadedState {
|
|
302
|
+
phase: string | null;
|
|
303
|
+
leases: NormalizedLease[];
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function normalizeLease(raw: PersistedLease): NormalizedLease | null {
|
|
307
|
+
const taskId = raw.task_id ?? raw.holder;
|
|
308
|
+
const paths = raw.file_paths ?? raw.paths;
|
|
309
|
+
if (typeof taskId !== "string" || !taskId.trim()) return null;
|
|
310
|
+
if (!Array.isArray(paths)) return null;
|
|
311
|
+
const cleaned = paths
|
|
312
|
+
.filter((p): p is string => typeof p === "string" && p.length > 0);
|
|
313
|
+
if (cleaned.length === 0) return null;
|
|
314
|
+
return { task_id: taskId, file_paths: cleaned };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function loadBuildState(projectDir: string): LoadedState {
|
|
318
|
+
const path = resolve(projectDir, "docs/plans/.build-state.json");
|
|
319
|
+
let text: string;
|
|
320
|
+
try {
|
|
321
|
+
text = readFileSync(path, "utf8");
|
|
322
|
+
} catch {
|
|
323
|
+
return { phase: null, leases: [] };
|
|
324
|
+
}
|
|
325
|
+
let state: BuildState;
|
|
326
|
+
try {
|
|
327
|
+
state = JSON.parse(text) as BuildState;
|
|
328
|
+
} catch {
|
|
329
|
+
return { phase: null, leases: [] };
|
|
330
|
+
}
|
|
331
|
+
const rawLeases = Array.isArray(state.active_write_leases)
|
|
332
|
+
? state.active_write_leases
|
|
333
|
+
: [];
|
|
334
|
+
const leases: NormalizedLease[] = [];
|
|
335
|
+
for (const r of rawLeases) {
|
|
336
|
+
const n = normalizeLease(r);
|
|
337
|
+
if (n) leases.push(n);
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
phase: normalizePhase(state.current_phase ?? state.phase),
|
|
341
|
+
leases,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function phaseGraphPath(pluginDir: string): string {
|
|
346
|
+
return resolve(pluginDir, "docs/migration/phase-graph.yaml");
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function cachePath(projectDir: string): string {
|
|
350
|
+
return resolve(projectDir, CACHE_REL_PATH);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
interface LoadedTable {
|
|
354
|
+
artifacts: ArtifactEntry[];
|
|
355
|
+
scratchRegexes: RegExp[];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function cachedToEntry(c: CachedArtifact): ArtifactEntry {
|
|
359
|
+
return { path: c.path, writers: c.writers, is_glob: c.is_glob, regex: c.regex };
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function loadFromCache(pluginDir: string, projectDir: string): LoadedTable | null {
|
|
363
|
+
// Cache hit requires: file exists, parses, version matches, and the source
|
|
364
|
+
// YAML's current mtime matches cache.source_mtime. Any mismatch is a miss.
|
|
365
|
+
const cPath = cachePath(projectDir);
|
|
366
|
+
let text: string;
|
|
367
|
+
try {
|
|
368
|
+
text = readFileSync(cPath, "utf8");
|
|
369
|
+
} catch {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
let cache: WriterOwnerCache;
|
|
373
|
+
try {
|
|
374
|
+
cache = JSON.parse(text) as WriterOwnerCache;
|
|
375
|
+
} catch {
|
|
376
|
+
process.stderr.write(
|
|
377
|
+
"buildanything: writer-owner cache malformed; parsing YAML live\n",
|
|
378
|
+
);
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
if (cache.version !== CACHE_VERSION || !Array.isArray(cache.artifacts)) {
|
|
382
|
+
process.stderr.write(
|
|
383
|
+
"buildanything: writer-owner cache schema mismatch; parsing YAML live\n",
|
|
384
|
+
);
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
try {
|
|
388
|
+
const srcMtime = Math.floor(statSync(phaseGraphPath(pluginDir)).mtimeMs);
|
|
389
|
+
if (srcMtime !== cache.source_mtime) {
|
|
390
|
+
process.stderr.write(
|
|
391
|
+
"buildanything: writer-owner cache stale; parsing YAML live\n",
|
|
392
|
+
);
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
} catch {
|
|
396
|
+
// Source YAML missing — fall through to live parse (which will also fail
|
|
397
|
+
// gracefully and treat the table as empty).
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
const scratchRegexes: RegExp[] = (cache.scratch_globs ?? []).map((g) => {
|
|
401
|
+
try {
|
|
402
|
+
return new RegExp(g.regex);
|
|
403
|
+
} catch {
|
|
404
|
+
return globToRegex(g.glob);
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
return {
|
|
408
|
+
artifacts: cache.artifacts.map(cachedToEntry),
|
|
409
|
+
scratchRegexes,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function loadFromYaml(pluginDir: string): LoadedTable {
|
|
414
|
+
const text = readFileSync(phaseGraphPath(pluginDir), "utf8");
|
|
415
|
+
const doc = parseYaml(text) as PhaseGraph;
|
|
416
|
+
const artifacts = Array.isArray(doc?.artifacts) ? doc.artifacts : [];
|
|
417
|
+
const scratchRegexes: RegExp[] = [];
|
|
418
|
+
const scratchGlob = doc?.phase_internal_scratch?.path_glob;
|
|
419
|
+
if (typeof scratchGlob === "string" && scratchGlob.trim()) {
|
|
420
|
+
scratchRegexes.push(globToRegex(scratchGlob));
|
|
421
|
+
}
|
|
422
|
+
return { artifacts, scratchRegexes };
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function loadArtifacts(pluginDir: string, projectDir: string): LoadedTable {
|
|
426
|
+
const cached = loadFromCache(pluginDir, projectDir);
|
|
427
|
+
if (cached) return cached;
|
|
428
|
+
// Live parse fallback. Do NOT rewrite the cache — that is session-start's job.
|
|
429
|
+
return loadFromYaml(pluginDir);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function isProtectedPath(relPath: string): boolean {
|
|
433
|
+
if (PROTECTED_FILES.has(relPath)) return true;
|
|
434
|
+
return PROTECTED_PREFIXES.some((p) => relPath.startsWith(p));
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function matchesScratch(relPath: string, scratchRegexes: RegExp[]): boolean {
|
|
438
|
+
return scratchRegexes.some((re) => re.test(relPath));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function enforceMode(): "deny" | "warn" {
|
|
442
|
+
return process.env[ENFORCE_ENV] === "false" ? "warn" : "deny";
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Task 2.2.2: scoped rollback for decisions.jsonl writer-owner enforcement.
|
|
446
|
+
// Returns true when the candidate path set includes docs/plans/decisions.jsonl
|
|
447
|
+
// AND the operator has set the scoped flag off. Lets users regress to Stage 1
|
|
448
|
+
// dual-write without disabling the rest of the writer-owner table.
|
|
449
|
+
function decisionsJsonlDowngrade(candidates: Iterable<string>): boolean {
|
|
450
|
+
if (process.env[SCRIBE_SINGLE_WRITER_ENV] !== "off") return false;
|
|
451
|
+
for (const c of candidates) if (c === DECISIONS_JSONL_PATH) return true;
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function leaseEnforceMode(): "deny" | "warn" {
|
|
456
|
+
// Either rollback flag downgrades the lease decision. The writer-owner
|
|
457
|
+
// flag is the umbrella "turn off all enforcement" switch; the lease flag
|
|
458
|
+
// is the targeted "keep writer-owner, soften only the lease layer" switch.
|
|
459
|
+
if (process.env[ENFORCE_ENV] === "false") return "warn";
|
|
460
|
+
if (process.env[ENFORCE_LEASE_ENV] === "false") return "warn";
|
|
461
|
+
return "deny";
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
interface StagedSubagent {
|
|
465
|
+
subagent_type?: string | null;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function readStagedSubagentType(projectDir: string, taskId: string): string | null {
|
|
469
|
+
// The SubagentStart hook (hooks/subagent-start.ts) writes a staging file
|
|
470
|
+
// keyed by parent_tool_use_id. We read only the subagent_type field. Any
|
|
471
|
+
// filesystem / parse error yields null (caller falls through — agent-role
|
|
472
|
+
// enforcement is best-effort; missing staging data never blocks a write).
|
|
473
|
+
const path = resolve(projectDir, SUBAGENT_START_CACHE_DIR_REL, `${taskId}.json`);
|
|
474
|
+
let text: string;
|
|
475
|
+
try {
|
|
476
|
+
text = readFileSync(path, "utf8");
|
|
477
|
+
} catch {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
try {
|
|
481
|
+
const parsed = JSON.parse(text) as StagedSubagent;
|
|
482
|
+
const t = parsed?.subagent_type;
|
|
483
|
+
return typeof t === "string" && t.length > 0 ? t : null;
|
|
484
|
+
} catch {
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function agentRoleOwners(owners: string[]): string[] {
|
|
490
|
+
// Owners that are neither phases nor known non-agent pseudos. The caller
|
|
491
|
+
// has already normalized phase-like strings to `phase-N`.
|
|
492
|
+
return owners.filter(
|
|
493
|
+
(o) => !/^phase--?\d+$/.test(o) && !NON_AGENT_OWNER_PSEUDOS.has(o),
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function deriveTaskId(call: ToolCall): string | null {
|
|
498
|
+
// Task 2.4.2: STRICT by default. The writer-owner/lease table encodes
|
|
499
|
+
// per-AGENT permissions, and `parent_tool_use_id` is the SDK-propagated
|
|
500
|
+
// identifier of the parent Agent dispatch. `tool_use_id` (per-invocation)
|
|
501
|
+
// is too granular and env vars are forgeable; both were transitional
|
|
502
|
+
// 2.4.1 fallbacks. Absence of `parent_tool_use_id` is the legitimate
|
|
503
|
+
// main-orchestrator case and returns null (callers short-circuit).
|
|
504
|
+
const fromCall = call.parent_tool_use_id;
|
|
505
|
+
if (typeof fromCall === "string" && fromCall.trim()) return fromCall.trim();
|
|
506
|
+
|
|
507
|
+
// Rollback path only: restores the 2.4.1 env-var fallback. Unset or any
|
|
508
|
+
// value other than "off" keeps strict mode. The `tool_use_id` fallback
|
|
509
|
+
// is NOT restored by this flag.
|
|
510
|
+
if (process.env[STRICT_TASK_ID_ENV] === "off") {
|
|
511
|
+
const fromEnv = process.env[TASK_ID_ENV];
|
|
512
|
+
if (typeof fromEnv === "string" && fromEnv.trim()) return fromEnv.trim();
|
|
513
|
+
}
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function leaseCoversPath(lease: NormalizedLease, candidates: Iterable<string>): boolean {
|
|
518
|
+
const candidateList = [...candidates];
|
|
519
|
+
for (const entry of lease.file_paths) {
|
|
520
|
+
const isGlob = entry.includes("*") || /\[[^\]]+\]/.test(entry);
|
|
521
|
+
if (!isGlob) {
|
|
522
|
+
if (candidateList.includes(entry)) return true;
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
const normalized = entry.replace(/\[[^\]]+\]/g, "*");
|
|
526
|
+
const re = globToRegex(normalized);
|
|
527
|
+
if (candidateList.some((c) => re.test(c))) return true;
|
|
528
|
+
}
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
interface LeaseDecision {
|
|
533
|
+
kind: "allow" | "deny" | "warn";
|
|
534
|
+
message?: string;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function evaluateLease(
|
|
538
|
+
taskId: string | null,
|
|
539
|
+
leases: NormalizedLease[],
|
|
540
|
+
toolName: string,
|
|
541
|
+
displayPath: string,
|
|
542
|
+
candidates: Iterable<string>,
|
|
543
|
+
): LeaseDecision {
|
|
544
|
+
// No task_id known → main-orchestrator context (no parent_tool_use_id on
|
|
545
|
+
// stdin) or 2.4.1-rollback env-unset. Lease layer intentionally no-ops;
|
|
546
|
+
// the writer-owner layer still ran above. This preserves Phase 0/1/2
|
|
547
|
+
// behavior and the main orchestrator case.
|
|
548
|
+
if (!taskId) return { kind: "allow" };
|
|
549
|
+
|
|
550
|
+
if (leases.length === 0) {
|
|
551
|
+
// TODO(phase-4-cutover): once Phase 4 orchestrator reliably acquires
|
|
552
|
+
// leases before dispatching implementers, flip this to a hard deny.
|
|
553
|
+
// For now, warn + allow so the system doesn't break during cutover
|
|
554
|
+
// from Stage 1 to Stage 2.
|
|
555
|
+
return {
|
|
556
|
+
kind: "warn",
|
|
557
|
+
message: `buildanything: write-lease WARNING on ${toolName} ${displayPath} — task_id '${taskId}' has no active leases in .build-state.json; lease acquisition will be required once Phase 4 implementer dispatches are fully cut over`,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const mine = leases.find((l) => l.task_id === taskId);
|
|
562
|
+
if (!mine) {
|
|
563
|
+
return {
|
|
564
|
+
kind: "deny",
|
|
565
|
+
message: `buildanything: write lease required — current task_id '${taskId}' has no active lease covering ${displayPath}`,
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (!leaseCoversPath(mine, candidates)) {
|
|
570
|
+
return {
|
|
571
|
+
kind: "deny",
|
|
572
|
+
message: `buildanything: write lease for task '${taskId}' does not include path ${displayPath}`,
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return { kind: "allow" };
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function applyLeaseDecision(decision: LeaseDecision): number {
|
|
580
|
+
if (decision.kind === "allow") return 0;
|
|
581
|
+
if (decision.kind === "warn") {
|
|
582
|
+
if (decision.message) process.stdout.write(`${decision.message}\n`);
|
|
583
|
+
return 0;
|
|
584
|
+
}
|
|
585
|
+
// deny
|
|
586
|
+
if (leaseEnforceMode() === "warn") {
|
|
587
|
+
if (decision.message) process.stdout.write(`WARNING: ${decision.message}\n`);
|
|
588
|
+
return 0;
|
|
589
|
+
}
|
|
590
|
+
if (decision.message) process.stderr.write(`${decision.message}\n`);
|
|
591
|
+
return 2;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function main(): number {
|
|
595
|
+
const raw = readStdin();
|
|
596
|
+
if (!raw.trim()) return 0;
|
|
597
|
+
|
|
598
|
+
let call: ToolCall;
|
|
599
|
+
try {
|
|
600
|
+
call = JSON.parse(raw) as ToolCall;
|
|
601
|
+
} catch {
|
|
602
|
+
// Malformed stdin is not this hook's problem — fail open.
|
|
603
|
+
return 0;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
const toolName = call.tool_name ?? "";
|
|
607
|
+
if (!WATCHED_TOOLS.has(toolName)) return 0;
|
|
608
|
+
|
|
609
|
+
const filePath = call.tool_input?.file_path;
|
|
610
|
+
if (!filePath) return 0;
|
|
611
|
+
|
|
612
|
+
const plugin = pluginRoot();
|
|
613
|
+
const project = projectRoot();
|
|
614
|
+
|
|
615
|
+
let table: LoadedTable;
|
|
616
|
+
try {
|
|
617
|
+
table = loadArtifacts(plugin, project);
|
|
618
|
+
} catch {
|
|
619
|
+
// Cache missing AND phase-graph.yaml unreadable — fail open rather than
|
|
620
|
+
// brick every write. A compiler/YAML failure is a buildanything bug, not
|
|
621
|
+
// a reason to block the user's work.
|
|
622
|
+
return 0;
|
|
623
|
+
}
|
|
624
|
+
const { artifacts, scratchRegexes } = table;
|
|
625
|
+
|
|
626
|
+
// Match file_path against the path being written. The writer-owner table
|
|
627
|
+
// lists paths relative to project root; stdin paths may be absolute. Build
|
|
628
|
+
// a candidate set: raw, relative-to-cwd, relative-to-realpath(cwd). Also
|
|
629
|
+
// probe the filePath's realpath when possible so /tmp vs /private/tmp on
|
|
630
|
+
// macOS resolves consistently.
|
|
631
|
+
const relCandidates = new Set<string>([filePath]);
|
|
632
|
+
if (isAbsolute(filePath)) {
|
|
633
|
+
const projectCandidates = new Set<string>([project]);
|
|
634
|
+
try { projectCandidates.add(realpathSync(project)); } catch { /* ignore */ }
|
|
635
|
+
const absCandidates = new Set<string>([filePath]);
|
|
636
|
+
try {
|
|
637
|
+
// filePath may not exist yet (pre-write); realpath the parent dir.
|
|
638
|
+
const parent = resolve(filePath, "..");
|
|
639
|
+
absCandidates.add(resolve(realpathSync(parent), filePath.slice(parent.length + 1)));
|
|
640
|
+
} catch { /* ignore */ }
|
|
641
|
+
for (const p of projectCandidates) {
|
|
642
|
+
for (const f of absCandidates) {
|
|
643
|
+
relCandidates.add(relative(p, f));
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Task 3.3.1: raw Write|Edit|MultiEdit on docs/plans/.build-state.json is
|
|
649
|
+
// unconditionally denied — all state mutations must route through the
|
|
650
|
+
// `state_save` MCP tool. This runs BEFORE the writer-owner table lookup
|
|
651
|
+
// and lease check because the rule is absolute: no lease and no phase
|
|
652
|
+
// ownership overrides it. The state_save MCP call itself is not a Write
|
|
653
|
+
// tool call, so PreToolUse never fires on it — no false positive on the
|
|
654
|
+
// legitimate writer. We match on `relCandidates` (covers raw + absolute-
|
|
655
|
+
// resolved-to-project forms) plus a fresh cwd-relative normalization of
|
|
656
|
+
// filePath to catch the `./docs/plans/.build-state.json` form that
|
|
657
|
+
// `relCandidates` does not otherwise normalize for relative inputs.
|
|
658
|
+
const buildStateMatched =
|
|
659
|
+
relCandidates.has(BUILD_STATE_PATH) ||
|
|
660
|
+
relative(project, resolve(project, filePath)) === BUILD_STATE_PATH;
|
|
661
|
+
if (buildStateMatched) {
|
|
662
|
+
const msg = `buildanything: raw ${toolName} on ${BUILD_STATE_PATH} denied. Use the state_save MCP tool instead. Set ${ALLOW_RAW_STATE_WRITES_ENV}=on to downgrade to warn.`;
|
|
663
|
+
if (process.env[ALLOW_RAW_STATE_WRITES_ENV] === "on") {
|
|
664
|
+
process.stdout.write(`WARNING: ${msg}\n`);
|
|
665
|
+
return 0;
|
|
666
|
+
}
|
|
667
|
+
process.stderr.write(`${msg}\n`);
|
|
668
|
+
return 2;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
let hit: ArtifactEntry | null = null;
|
|
672
|
+
let hitPath = filePath;
|
|
673
|
+
for (const candidate of relCandidates) {
|
|
674
|
+
hit = findArtifact(candidate, artifacts);
|
|
675
|
+
if (hit) {
|
|
676
|
+
hitPath = candidate;
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const { phase: currentPhase, leases } = loadBuildState(project);
|
|
682
|
+
const taskId = deriveTaskId(call);
|
|
683
|
+
|
|
684
|
+
if (!hit) {
|
|
685
|
+
// Task 2.1.3: default-deny for unknown paths under PROTECTED_PREFIXES.
|
|
686
|
+
// Exemptions: scratch globs (phase_internal_scratch) and any path outside
|
|
687
|
+
// the protected prefix set (user-project source code).
|
|
688
|
+
const scratchMatched = [...relCandidates].some((c) =>
|
|
689
|
+
matchesScratch(c, scratchRegexes),
|
|
690
|
+
);
|
|
691
|
+
if (scratchMatched) {
|
|
692
|
+
return applyLeaseDecision(
|
|
693
|
+
evaluateLease(taskId, leases, toolName, filePath, relCandidates),
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const underProtected = [...relCandidates].some((c) => isProtectedPath(c));
|
|
698
|
+
if (!underProtected) {
|
|
699
|
+
return applyLeaseDecision(
|
|
700
|
+
evaluateLease(taskId, leases, toolName, filePath, relCandidates),
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const denyPath = [...relCandidates].find((c) => isProtectedPath(c)) ?? filePath;
|
|
705
|
+
const msg = `buildanything: writer-owner hook denied ${toolName} on ${denyPath} — path not in writer-owner table. Please add an entry to docs/migration/phase-graph.yaml or route the write through the scribe_decision MCP.`;
|
|
706
|
+
|
|
707
|
+
if (enforceMode() === "warn" || decisionsJsonlDowngrade(relCandidates)) {
|
|
708
|
+
process.stdout.write(`WARNING: ${msg}\n`);
|
|
709
|
+
return 0;
|
|
710
|
+
}
|
|
711
|
+
process.stderr.write(`${msg}\n`);
|
|
712
|
+
return 2;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// Boot-time race: no state file yet. Fail open on phase-mismatch check so
|
|
716
|
+
// Phase 1's first write isn't blocked before .build-state.json exists.
|
|
717
|
+
if (!currentPhase) return 0;
|
|
718
|
+
|
|
719
|
+
const owners = ownerPhases(hit).map((w) => normalizePhase(w) ?? w);
|
|
720
|
+
if (owners.length === 0) {
|
|
721
|
+
return applyLeaseDecision(
|
|
722
|
+
evaluateLease(taskId, leases, toolName, hitPath, relCandidates),
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// Task 5.2.1: agent-role enforcement. When the artifact's writers list
|
|
727
|
+
// contains subagent_type names (e.g. chapter-judge subagents for
|
|
728
|
+
// lrr/*.json), deny when the calling subagent's staged subagent_type is
|
|
729
|
+
// not in that list. Best-effort: if no task_id or no staged cache file
|
|
730
|
+
// exists, fall through (matches lease semantics — main orchestrator has
|
|
731
|
+
// no parent_tool_use_id; writer-owner still enforces phases).
|
|
732
|
+
const agentOwners = agentRoleOwners(owners);
|
|
733
|
+
if (agentOwners.length > 0 && taskId) {
|
|
734
|
+
const stagedType = readStagedSubagentType(project, taskId);
|
|
735
|
+
if (stagedType && !agentOwners.includes(stagedType)) {
|
|
736
|
+
const ownerStr = agentOwners.join(" | ");
|
|
737
|
+
const msg = `buildanything: writer-owner hook denied ${toolName} on ${hitPath} — subagent '${stagedType}' is not an owner; writer(s): ${ownerStr}`;
|
|
738
|
+
if (enforceMode() === "warn" || decisionsJsonlDowngrade(relCandidates)) {
|
|
739
|
+
process.stdout.write(`WARNING: ${msg}\n`);
|
|
740
|
+
return 0;
|
|
741
|
+
}
|
|
742
|
+
process.stderr.write(`${msg}\n`);
|
|
743
|
+
return 2;
|
|
744
|
+
}
|
|
745
|
+
// stagedType match OR staging file missing → fall through to phase / lease.
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// Non-phase owners (e.g. "orchestrator", "orchestrator-scribe",
|
|
749
|
+
// "every-phase", "auto-rendered-view") are out of scope for this phase-vs-
|
|
750
|
+
// phase check. Leave them to task 2.2.x / 2.4.x.
|
|
751
|
+
const phaseOwners = owners.filter((o) => /^phase--?\d+$/.test(o));
|
|
752
|
+
if (phaseOwners.length === 0) {
|
|
753
|
+
return applyLeaseDecision(
|
|
754
|
+
evaluateLease(taskId, leases, toolName, hitPath, relCandidates),
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if (phaseOwners.includes(currentPhase)) {
|
|
759
|
+
return applyLeaseDecision(
|
|
760
|
+
evaluateLease(taskId, leases, toolName, hitPath, relCandidates),
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
const ownerStr = phaseOwners.join(" | ");
|
|
765
|
+
const msg = `buildanything: writer-owner hook denied ${toolName} on ${hitPath} — current phase ${currentPhase}, path owned by ${ownerStr}`;
|
|
766
|
+
|
|
767
|
+
if (enforceMode() === "warn" || decisionsJsonlDowngrade(relCandidates)) {
|
|
768
|
+
process.stdout.write(`WARNING: ${msg}\n`);
|
|
769
|
+
return 0;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
process.stderr.write(`${msg}\n`);
|
|
773
|
+
return 2;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
process.exit(main());
|