@productbrain/cli 0.1.0-beta.1 → 0.1.0-beta.100
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/README.md +127 -0
- package/dist/__tests__/adapters.test.d.ts +2 -0
- package/dist/__tests__/adapters.test.d.ts.map +1 -0
- package/dist/__tests__/adapters.test.js +417 -0
- package/dist/__tests__/adapters.test.js.map +1 -0
- package/dist/__tests__/audit.test.d.ts +2 -0
- package/dist/__tests__/audit.test.d.ts.map +1 -0
- package/dist/__tests__/audit.test.js +394 -0
- package/dist/__tests__/audit.test.js.map +1 -0
- package/dist/__tests__/authority-domains.test.d.ts +2 -0
- package/dist/__tests__/authority-domains.test.d.ts.map +1 -0
- package/dist/__tests__/authority-domains.test.js +48 -0
- package/dist/__tests__/authority-domains.test.js.map +1 -0
- package/dist/__tests__/batch-transformations.test.d.ts +2 -0
- package/dist/__tests__/batch-transformations.test.d.ts.map +1 -0
- package/dist/__tests__/batch-transformations.test.js +263 -0
- package/dist/__tests__/batch-transformations.test.js.map +1 -0
- package/dist/__tests__/capture.test.d.ts +2 -0
- package/dist/__tests__/capture.test.d.ts.map +1 -0
- package/dist/__tests__/capture.test.js +377 -0
- package/dist/__tests__/capture.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +8 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +296 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/constants.test.d.ts +2 -0
- package/dist/__tests__/constants.test.d.ts.map +1 -0
- package/dist/__tests__/constants.test.js +141 -0
- package/dist/__tests__/constants.test.js.map +1 -0
- package/dist/__tests__/constellation.test.d.ts +2 -0
- package/dist/__tests__/constellation.test.d.ts.map +1 -0
- package/dist/__tests__/constellation.test.js +254 -0
- package/dist/__tests__/constellation.test.js.map +1 -0
- package/dist/__tests__/context-strategy.test.d.ts +2 -0
- package/dist/__tests__/context-strategy.test.d.ts.map +1 -0
- package/dist/__tests__/context-strategy.test.js +79 -0
- package/dist/__tests__/context-strategy.test.js.map +1 -0
- package/dist/__tests__/envelope-contract.test.d.ts +15 -0
- package/dist/__tests__/envelope-contract.test.d.ts.map +1 -0
- package/dist/__tests__/envelope-contract.test.js +126 -0
- package/dist/__tests__/envelope-contract.test.js.map +1 -0
- package/dist/__tests__/errors.test.d.ts +2 -0
- package/dist/__tests__/errors.test.d.ts.map +1 -0
- package/dist/__tests__/errors.test.js +117 -0
- package/dist/__tests__/errors.test.js.map +1 -0
- package/dist/__tests__/experiment.test.d.ts +6 -0
- package/dist/__tests__/experiment.test.d.ts.map +1 -0
- package/dist/__tests__/experiment.test.js +69 -0
- package/dist/__tests__/experiment.test.js.map +1 -0
- package/dist/__tests__/fields.test.d.ts +2 -0
- package/dist/__tests__/fields.test.d.ts.map +1 -0
- package/dist/__tests__/fields.test.js +238 -0
- package/dist/__tests__/fields.test.js.map +1 -0
- package/dist/__tests__/glossary.test.d.ts +2 -0
- package/dist/__tests__/glossary.test.d.ts.map +1 -0
- package/dist/__tests__/glossary.test.js +32 -0
- package/dist/__tests__/glossary.test.js.map +1 -0
- package/dist/__tests__/handshake-preview.test.d.ts +2 -0
- package/dist/__tests__/handshake-preview.test.d.ts.map +1 -0
- package/dist/__tests__/handshake-preview.test.js +279 -0
- package/dist/__tests__/handshake-preview.test.js.map +1 -0
- package/dist/__tests__/handshake.test.d.ts +2 -0
- package/dist/__tests__/handshake.test.d.ts.map +1 -0
- package/dist/__tests__/handshake.test.js +196 -0
- package/dist/__tests__/handshake.test.js.map +1 -0
- package/dist/__tests__/hook-intents.test.d.ts +2 -0
- package/dist/__tests__/hook-intents.test.d.ts.map +1 -0
- package/dist/__tests__/hook-intents.test.js +184 -0
- package/dist/__tests__/hook-intents.test.js.map +1 -0
- package/dist/__tests__/ingest.test.d.ts +2 -0
- package/dist/__tests__/ingest.test.d.ts.map +1 -0
- package/dist/__tests__/ingest.test.js +185 -0
- package/dist/__tests__/ingest.test.js.map +1 -0
- package/dist/__tests__/init.test.d.ts +7 -0
- package/dist/__tests__/init.test.d.ts.map +1 -0
- package/dist/__tests__/init.test.js +146 -0
- package/dist/__tests__/init.test.js.map +1 -0
- package/dist/__tests__/login.test.d.ts +2 -0
- package/dist/__tests__/login.test.d.ts.map +1 -0
- package/dist/__tests__/login.test.js +167 -0
- package/dist/__tests__/login.test.js.map +1 -0
- package/dist/__tests__/manifest.test.d.ts +6 -0
- package/dist/__tests__/manifest.test.d.ts.map +1 -0
- package/dist/__tests__/manifest.test.js +138 -0
- package/dist/__tests__/manifest.test.js.map +1 -0
- package/dist/__tests__/method-registry.integration.test.d.ts +6 -0
- package/dist/__tests__/method-registry.integration.test.d.ts.map +1 -0
- package/dist/__tests__/method-registry.integration.test.js +18 -0
- package/dist/__tests__/method-registry.integration.test.js.map +1 -0
- package/dist/__tests__/method-registry.test.d.ts +14 -0
- package/dist/__tests__/method-registry.test.d.ts.map +1 -0
- package/dist/__tests__/method-registry.test.js +134 -0
- package/dist/__tests__/method-registry.test.js.map +1 -0
- package/dist/__tests__/onboarding-path-b.test.d.ts +2 -0
- package/dist/__tests__/onboarding-path-b.test.d.ts.map +1 -0
- package/dist/__tests__/onboarding-path-b.test.js +46 -0
- package/dist/__tests__/onboarding-path-b.test.js.map +1 -0
- package/dist/__tests__/onboarding.test.d.ts +6 -0
- package/dist/__tests__/onboarding.test.d.ts.map +1 -0
- package/dist/__tests__/onboarding.test.js +347 -0
- package/dist/__tests__/onboarding.test.js.map +1 -0
- package/dist/__tests__/orient.test.d.ts +2 -0
- package/dist/__tests__/orient.test.d.ts.map +1 -0
- package/dist/__tests__/orient.test.js +196 -0
- package/dist/__tests__/orient.test.js.map +1 -0
- package/dist/__tests__/personal-layer.test.d.ts +12 -0
- package/dist/__tests__/personal-layer.test.d.ts.map +1 -0
- package/dist/__tests__/personal-layer.test.js +304 -0
- package/dist/__tests__/personal-layer.test.js.map +1 -0
- package/dist/__tests__/profiles.test.d.ts +2 -0
- package/dist/__tests__/profiles.test.d.ts.map +1 -0
- package/dist/__tests__/profiles.test.js +212 -0
- package/dist/__tests__/profiles.test.js.map +1 -0
- package/dist/__tests__/promote.test.d.ts +2 -0
- package/dist/__tests__/promote.test.d.ts.map +1 -0
- package/dist/__tests__/promote.test.js +230 -0
- package/dist/__tests__/promote.test.js.map +1 -0
- package/dist/__tests__/prompts.test.d.ts +6 -0
- package/dist/__tests__/prompts.test.d.ts.map +1 -0
- package/dist/__tests__/prompts.test.js +146 -0
- package/dist/__tests__/prompts.test.js.map +1 -0
- package/dist/__tests__/proposals.test.d.ts +2 -0
- package/dist/__tests__/proposals.test.d.ts.map +1 -0
- package/dist/__tests__/proposals.test.js +167 -0
- package/dist/__tests__/proposals.test.js.map +1 -0
- package/dist/__tests__/relate.test.d.ts +2 -0
- package/dist/__tests__/relate.test.d.ts.map +1 -0
- package/dist/__tests__/relate.test.js +103 -0
- package/dist/__tests__/relate.test.js.map +1 -0
- package/dist/__tests__/repo-detect.test.d.ts +2 -0
- package/dist/__tests__/repo-detect.test.d.ts.map +1 -0
- package/dist/__tests__/repo-detect.test.js +215 -0
- package/dist/__tests__/repo-detect.test.js.map +1 -0
- package/dist/__tests__/runner.test.d.ts +2 -0
- package/dist/__tests__/runner.test.d.ts.map +1 -0
- package/dist/__tests__/runner.test.js +219 -0
- package/dist/__tests__/runner.test.js.map +1 -0
- package/dist/__tests__/session-state-machine.test.d.ts +2 -0
- package/dist/__tests__/session-state-machine.test.d.ts.map +1 -0
- package/dist/__tests__/session-state-machine.test.js +154 -0
- package/dist/__tests__/session-state-machine.test.js.map +1 -0
- package/dist/__tests__/session-touch.test.d.ts +2 -0
- package/dist/__tests__/session-touch.test.d.ts.map +1 -0
- package/dist/__tests__/session-touch.test.js +134 -0
- package/dist/__tests__/session-touch.test.js.map +1 -0
- package/dist/__tests__/session.test.d.ts +2 -0
- package/dist/__tests__/session.test.d.ts.map +1 -0
- package/dist/__tests__/session.test.js +46 -0
- package/dist/__tests__/session.test.js.map +1 -0
- package/dist/__tests__/setup-ingest.test.d.ts +2 -0
- package/dist/__tests__/setup-ingest.test.d.ts.map +1 -0
- package/dist/__tests__/setup-ingest.test.js +55 -0
- package/dist/__tests__/setup-ingest.test.js.map +1 -0
- package/dist/__tests__/setup-resolver.test.d.ts +14 -0
- package/dist/__tests__/setup-resolver.test.d.ts.map +1 -0
- package/dist/__tests__/setup-resolver.test.js +228 -0
- package/dist/__tests__/setup-resolver.test.js.map +1 -0
- package/dist/__tests__/setup.test.d.ts +2 -0
- package/dist/__tests__/setup.test.d.ts.map +1 -0
- package/dist/__tests__/setup.test.js +141 -0
- package/dist/__tests__/setup.test.js.map +1 -0
- package/dist/__tests__/spinner-labels.test.d.ts +2 -0
- package/dist/__tests__/spinner-labels.test.d.ts.map +1 -0
- package/dist/__tests__/spinner-labels.test.js +23 -0
- package/dist/__tests__/spinner-labels.test.js.map +1 -0
- package/dist/__tests__/state.test.d.ts +6 -0
- package/dist/__tests__/state.test.d.ts.map +1 -0
- package/dist/__tests__/state.test.js +97 -0
- package/dist/__tests__/state.test.js.map +1 -0
- package/dist/__tests__/strip.test.d.ts +2 -0
- package/dist/__tests__/strip.test.d.ts.map +1 -0
- package/dist/__tests__/strip.test.js +136 -0
- package/dist/__tests__/strip.test.js.map +1 -0
- package/dist/__tests__/surface-profiles.test.d.ts +2 -0
- package/dist/__tests__/surface-profiles.test.d.ts.map +1 -0
- package/dist/__tests__/surface-profiles.test.js +233 -0
- package/dist/__tests__/surface-profiles.test.js.map +1 -0
- package/dist/__tests__/surfaces.test.d.ts +2 -0
- package/dist/__tests__/surfaces.test.d.ts.map +1 -0
- package/dist/__tests__/surfaces.test.js +46 -0
- package/dist/__tests__/surfaces.test.js.map +1 -0
- package/dist/__tests__/update.test.d.ts +2 -0
- package/dist/__tests__/update.test.d.ts.map +1 -0
- package/dist/__tests__/update.test.js +228 -0
- package/dist/__tests__/update.test.js.map +1 -0
- package/dist/__tests__/workspace.test.d.ts +2 -0
- package/dist/__tests__/workspace.test.d.ts.map +1 -0
- package/dist/__tests__/workspace.test.js +308 -0
- package/dist/__tests__/workspace.test.js.map +1 -0
- package/dist/commands/accept.d.ts +18 -0
- package/dist/commands/accept.d.ts.map +1 -0
- package/dist/commands/accept.js +76 -0
- package/dist/commands/accept.js.map +1 -0
- package/dist/commands/admin/cockpit.d.ts +90 -0
- package/dist/commands/admin/cockpit.d.ts.map +1 -0
- package/dist/commands/admin/cockpit.js +618 -0
- package/dist/commands/admin/cockpit.js.map +1 -0
- package/dist/commands/admin/index.d.ts +21 -0
- package/dist/commands/admin/index.d.ts.map +1 -0
- package/dist/commands/admin/index.js +256 -0
- package/dist/commands/admin/index.js.map +1 -0
- package/dist/commands/admin/inspect.d.ts +30 -0
- package/dist/commands/admin/inspect.d.ts.map +1 -0
- package/dist/commands/admin/inspect.js +555 -0
- package/dist/commands/admin/inspect.js.map +1 -0
- package/dist/commands/admin/inspect.test.d.ts +7 -0
- package/dist/commands/admin/inspect.test.d.ts.map +1 -0
- package/dist/commands/admin/inspect.test.js +90 -0
- package/dist/commands/admin/inspect.test.js.map +1 -0
- package/dist/commands/admin/manage.d.ts +8 -0
- package/dist/commands/admin/manage.d.ts.map +1 -0
- package/dist/commands/admin/manage.js +76 -0
- package/dist/commands/admin/manage.js.map +1 -0
- package/dist/commands/admin/seed.d.ts +32 -0
- package/dist/commands/admin/seed.d.ts.map +1 -0
- package/dist/commands/admin/seed.js +533 -0
- package/dist/commands/admin/seed.js.map +1 -0
- package/dist/commands/admin/seed.test.d.ts +6 -0
- package/dist/commands/admin/seed.test.d.ts.map +1 -0
- package/dist/commands/admin/seed.test.js +65 -0
- package/dist/commands/admin/seed.test.js.map +1 -0
- package/dist/commands/audit.d.ts +25 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +188 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/authority-domains.d.ts +140 -0
- package/dist/commands/authority-domains.d.ts.map +1 -0
- package/dist/commands/authority-domains.js +268 -0
- package/dist/commands/authority-domains.js.map +1 -0
- package/dist/commands/brand-pack.d.ts +2 -0
- package/dist/commands/brand-pack.d.ts.map +1 -0
- package/dist/commands/brand-pack.js +25 -0
- package/dist/commands/brand-pack.js.map +1 -0
- package/dist/commands/brief.d.ts +28 -0
- package/dist/commands/brief.d.ts.map +1 -0
- package/dist/commands/brief.js +75 -0
- package/dist/commands/brief.js.map +1 -0
- package/dist/commands/capture.d.ts +30 -0
- package/dist/commands/capture.d.ts.map +1 -0
- package/dist/commands/capture.js +339 -0
- package/dist/commands/capture.js.map +1 -0
- package/dist/commands/chain-walk.d.ts +14 -0
- package/dist/commands/chain-walk.d.ts.map +1 -0
- package/dist/commands/chain-walk.js +38 -0
- package/dist/commands/chain-walk.js.map +1 -0
- package/dist/commands/changes.d.ts +11 -0
- package/dist/commands/changes.d.ts.map +1 -0
- package/dist/commands/changes.js +46 -0
- package/dist/commands/changes.js.map +1 -0
- package/dist/commands/codex-prep.d.ts +12 -0
- package/dist/commands/codex-prep.d.ts.map +1 -0
- package/dist/commands/codex-prep.js +122 -0
- package/dist/commands/codex-prep.js.map +1 -0
- package/dist/commands/collections.d.ts +22 -0
- package/dist/commands/collections.d.ts.map +1 -0
- package/dist/commands/collections.js +77 -0
- package/dist/commands/collections.js.map +1 -0
- package/dist/commands/connect-integration.test.d.ts +7 -0
- package/dist/commands/connect-integration.test.d.ts.map +1 -0
- package/dist/commands/connect-integration.test.js +211 -0
- package/dist/commands/connect-integration.test.js.map +1 -0
- package/dist/commands/connect-screens.d.ts +21 -0
- package/dist/commands/connect-screens.d.ts.map +1 -0
- package/dist/commands/connect-screens.js +79 -0
- package/dist/commands/connect-screens.js.map +1 -0
- package/dist/commands/connect.d.ts +23 -0
- package/dist/commands/connect.d.ts.map +1 -0
- package/dist/commands/connect.js +289 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/connect.test.d.ts +6 -0
- package/dist/commands/connect.test.d.ts.map +1 -0
- package/dist/commands/connect.test.js +297 -0
- package/dist/commands/connect.test.js.map +1 -0
- package/dist/commands/constellation.d.ts +11 -0
- package/dist/commands/constellation.d.ts.map +1 -0
- package/dist/commands/constellation.js +33 -0
- package/dist/commands/constellation.js.map +1 -0
- package/dist/commands/context.d.ts +2 -1
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +25 -10
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/cross-cut.d.ts +11 -0
- package/dist/commands/cross-cut.d.ts.map +1 -0
- package/dist/commands/cross-cut.js +23 -0
- package/dist/commands/cross-cut.js.map +1 -0
- package/dist/commands/doctor.d.ts +18 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +232 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/doctor.test.d.ts +8 -0
- package/dist/commands/doctor.test.d.ts.map +1 -0
- package/dist/commands/doctor.test.js +311 -0
- package/dist/commands/doctor.test.js.map +1 -0
- package/dist/commands/fields.d.ts +9 -0
- package/dist/commands/fields.d.ts.map +1 -0
- package/dist/commands/fields.js +30 -0
- package/dist/commands/fields.js.map +1 -0
- package/dist/commands/get.d.ts +8 -1
- package/dist/commands/get.d.ts.map +1 -1
- package/dist/commands/get.js +65 -8
- package/dist/commands/get.js.map +1 -1
- package/dist/commands/handshake.d.ts +30 -0
- package/dist/commands/handshake.d.ts.map +1 -0
- package/dist/commands/handshake.js +929 -0
- package/dist/commands/handshake.js.map +1 -0
- package/dist/commands/ingest.d.ts +14 -0
- package/dist/commands/ingest.d.ts.map +1 -0
- package/dist/commands/ingest.js +189 -0
- package/dist/commands/ingest.js.map +1 -0
- package/dist/commands/init.d.ts +14 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +109 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +9 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +116 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/method.d.ts +99 -0
- package/dist/commands/method.d.ts.map +1 -0
- package/dist/commands/method.js +781 -0
- package/dist/commands/method.js.map +1 -0
- package/dist/commands/migrate-setup.d.ts +18 -0
- package/dist/commands/migrate-setup.d.ts.map +1 -0
- package/dist/commands/migrate-setup.js +198 -0
- package/dist/commands/migrate-setup.js.map +1 -0
- package/dist/commands/orient.d.ts +109 -1
- package/dist/commands/orient.d.ts.map +1 -1
- package/dist/commands/orient.js +94 -7
- package/dist/commands/orient.js.map +1 -1
- package/dist/commands/profile.d.ts +47 -0
- package/dist/commands/profile.d.ts.map +1 -0
- package/dist/commands/profile.js +148 -0
- package/dist/commands/profile.js.map +1 -0
- package/dist/commands/promote.d.ts +12 -0
- package/dist/commands/promote.d.ts.map +1 -0
- package/dist/commands/promote.js +113 -0
- package/dist/commands/promote.js.map +1 -0
- package/dist/commands/proposals.d.ts +9 -0
- package/dist/commands/proposals.d.ts.map +1 -0
- package/dist/commands/proposals.js +24 -0
- package/dist/commands/proposals.js.map +1 -0
- package/dist/commands/reject.d.ts +14 -0
- package/dist/commands/reject.d.ts.map +1 -0
- package/dist/commands/reject.js +43 -0
- package/dist/commands/reject.js.map +1 -0
- package/dist/commands/relate.d.ts +16 -0
- package/dist/commands/relate.d.ts.map +1 -0
- package/dist/commands/relate.js +111 -0
- package/dist/commands/relate.js.map +1 -0
- package/dist/commands/search.d.ts +1 -0
- package/dist/commands/search.d.ts.map +1 -1
- package/dist/commands/search.js +10 -4
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/session.d.ts +20 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +203 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/setup-ingest.d.ts +17 -0
- package/dist/commands/setup-ingest.d.ts.map +1 -0
- package/dist/commands/setup-ingest.js +224 -0
- package/dist/commands/setup-ingest.js.map +1 -0
- package/dist/commands/setup-resolver.d.ts +58 -0
- package/dist/commands/setup-resolver.d.ts.map +1 -0
- package/dist/commands/setup-resolver.js +150 -0
- package/dist/commands/setup-resolver.js.map +1 -0
- package/dist/commands/setup.d.ts +15 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +148 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/update.d.ts +17 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +178 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/usage.d.ts +40 -0
- package/dist/commands/usage.d.ts.map +1 -0
- package/dist/commands/usage.js +232 -0
- package/dist/commands/usage.js.map +1 -0
- package/dist/commands/verify.d.ts +13 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +49 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/welcome.d.ts +21 -0
- package/dist/commands/welcome.d.ts.map +1 -0
- package/dist/commands/welcome.js +50 -0
- package/dist/commands/welcome.js.map +1 -0
- package/dist/commands/workspace.d.ts +92 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +263 -0
- package/dist/commands/workspace.js.map +1 -0
- package/dist/formatters/audit.d.ts +46 -0
- package/dist/formatters/audit.d.ts.map +1 -0
- package/dist/formatters/audit.js +81 -0
- package/dist/formatters/audit.js.map +1 -0
- package/dist/formatters/brief.d.ts +112 -0
- package/dist/formatters/brief.d.ts.map +1 -0
- package/dist/formatters/brief.js +179 -0
- package/dist/formatters/brief.js.map +1 -0
- package/dist/formatters/capture.d.ts +48 -0
- package/dist/formatters/capture.d.ts.map +1 -0
- package/dist/formatters/capture.js +77 -0
- package/dist/formatters/capture.js.map +1 -0
- package/dist/formatters/chain-walk.d.ts +33 -0
- package/dist/formatters/chain-walk.d.ts.map +1 -0
- package/dist/formatters/chain-walk.js +54 -0
- package/dist/formatters/chain-walk.js.map +1 -0
- package/dist/formatters/changes.d.ts +25 -0
- package/dist/formatters/changes.d.ts.map +1 -0
- package/dist/formatters/changes.js +60 -0
- package/dist/formatters/changes.js.map +1 -0
- package/dist/formatters/collections.d.ts +40 -0
- package/dist/formatters/collections.d.ts.map +1 -0
- package/dist/formatters/collections.js +93 -0
- package/dist/formatters/collections.js.map +1 -0
- package/dist/formatters/constellation.d.ts +34 -0
- package/dist/formatters/constellation.d.ts.map +1 -0
- package/dist/formatters/constellation.js +38 -0
- package/dist/formatters/constellation.js.map +1 -0
- package/dist/formatters/cross-cut.d.ts +21 -0
- package/dist/formatters/cross-cut.d.ts.map +1 -0
- package/dist/formatters/cross-cut.js +32 -0
- package/dist/formatters/cross-cut.js.map +1 -0
- package/dist/formatters/entry.d.ts +11 -4
- package/dist/formatters/entry.d.ts.map +1 -1
- package/dist/formatters/entry.js +24 -8
- package/dist/formatters/entry.js.map +1 -1
- package/dist/formatters/fields.d.ts +32 -0
- package/dist/formatters/fields.d.ts.map +1 -0
- package/dist/formatters/fields.js +49 -0
- package/dist/formatters/fields.js.map +1 -0
- package/dist/formatters/handshake.d.ts +46 -0
- package/dist/formatters/handshake.d.ts.map +1 -0
- package/dist/formatters/handshake.js +163 -0
- package/dist/formatters/handshake.js.map +1 -0
- package/dist/formatters/orient.d.ts +129 -1
- package/dist/formatters/orient.d.ts.map +1 -1
- package/dist/formatters/orient.js +156 -17
- package/dist/formatters/orient.js.map +1 -1
- package/dist/formatters/promote.d.ts +30 -0
- package/dist/formatters/promote.d.ts.map +1 -0
- package/dist/formatters/promote.js +39 -0
- package/dist/formatters/promote.js.map +1 -0
- package/dist/formatters/proposals.d.ts +45 -0
- package/dist/formatters/proposals.d.ts.map +1 -0
- package/dist/formatters/proposals.js +62 -0
- package/dist/formatters/proposals.js.map +1 -0
- package/dist/formatters/relate.d.ts +14 -0
- package/dist/formatters/relate.d.ts.map +1 -0
- package/dist/formatters/relate.js +16 -0
- package/dist/formatters/relate.js.map +1 -0
- package/dist/formatters/search.d.ts +0 -4
- package/dist/formatters/search.d.ts.map +1 -1
- package/dist/formatters/search.js +4 -1
- package/dist/formatters/search.js.map +1 -1
- package/dist/formatters/session.d.ts +11 -0
- package/dist/formatters/session.d.ts.map +1 -0
- package/dist/formatters/session.js +53 -0
- package/dist/formatters/session.js.map +1 -0
- package/dist/formatters/update.d.ts +17 -0
- package/dist/formatters/update.d.ts.map +1 -0
- package/dist/formatters/update.js +45 -0
- package/dist/formatters/update.js.map +1 -0
- package/dist/formatters/verify.d.ts +11 -0
- package/dist/formatters/verify.d.ts.map +1 -0
- package/dist/formatters/verify.js +11 -0
- package/dist/formatters/verify.js.map +1 -0
- package/dist/generators/adapters.d.ts +44 -0
- package/dist/generators/adapters.d.ts.map +1 -0
- package/dist/generators/adapters.js +290 -0
- package/dist/generators/adapters.js.map +1 -0
- package/dist/generators/adapters.test.d.ts +2 -0
- package/dist/generators/adapters.test.d.ts.map +1 -0
- package/dist/generators/adapters.test.js +27 -0
- package/dist/generators/adapters.test.js.map +1 -0
- package/dist/generators/archetypes.d.ts +52 -0
- package/dist/generators/archetypes.d.ts.map +1 -0
- package/dist/generators/archetypes.js +153 -0
- package/dist/generators/archetypes.js.map +1 -0
- package/dist/generators/archetypes.test.d.ts +2 -0
- package/dist/generators/archetypes.test.d.ts.map +1 -0
- package/dist/generators/archetypes.test.js +237 -0
- package/dist/generators/archetypes.test.js.map +1 -0
- package/dist/generators/boundary-manifest.d.ts +29 -0
- package/dist/generators/boundary-manifest.d.ts.map +1 -0
- package/dist/generators/boundary-manifest.js +183 -0
- package/dist/generators/boundary-manifest.js.map +1 -0
- package/dist/generators/boundary-manifest.test.d.ts +2 -0
- package/dist/generators/boundary-manifest.test.d.ts.map +1 -0
- package/dist/generators/boundary-manifest.test.js +91 -0
- package/dist/generators/boundary-manifest.test.js.map +1 -0
- package/dist/generators/briefing-md.d.ts +8 -0
- package/dist/generators/briefing-md.d.ts.map +1 -0
- package/dist/generators/briefing-md.js +51 -0
- package/dist/generators/briefing-md.js.map +1 -0
- package/dist/generators/chain-classifier.d.ts +49 -0
- package/dist/generators/chain-classifier.d.ts.map +1 -0
- package/dist/generators/chain-classifier.js +180 -0
- package/dist/generators/chain-classifier.js.map +1 -0
- package/dist/generators/chain-classifier.test.d.ts +2 -0
- package/dist/generators/chain-classifier.test.d.ts.map +1 -0
- package/dist/generators/chain-classifier.test.js +257 -0
- package/dist/generators/chain-classifier.test.js.map +1 -0
- package/dist/generators/chain-rules.d.ts +42 -0
- package/dist/generators/chain-rules.d.ts.map +1 -0
- package/dist/generators/chain-rules.js +144 -0
- package/dist/generators/chain-rules.js.map +1 -0
- package/dist/generators/chain-rules.test.d.ts +2 -0
- package/dist/generators/chain-rules.test.d.ts.map +1 -0
- package/dist/generators/chain-rules.test.js +179 -0
- package/dist/generators/chain-rules.test.js.map +1 -0
- package/dist/generators/context-md.d.ts +8 -0
- package/dist/generators/context-md.d.ts.map +1 -0
- package/dist/generators/context-md.js +134 -0
- package/dist/generators/context-md.js.map +1 -0
- package/dist/generators/handshake-diff.d.ts +67 -0
- package/dist/generators/handshake-diff.d.ts.map +1 -0
- package/dist/generators/handshake-diff.js +183 -0
- package/dist/generators/handshake-diff.js.map +1 -0
- package/dist/generators/handshake-diff.test.d.ts +2 -0
- package/dist/generators/handshake-diff.test.d.ts.map +1 -0
- package/dist/generators/handshake-diff.test.js +264 -0
- package/dist/generators/handshake-diff.test.js.map +1 -0
- package/dist/generators/manifest.d.ts +39 -0
- package/dist/generators/manifest.d.ts.map +1 -0
- package/dist/generators/manifest.js +166 -0
- package/dist/generators/manifest.js.map +1 -0
- package/dist/generators/portable-knowledge.d.ts +165 -0
- package/dist/generators/portable-knowledge.d.ts.map +1 -0
- package/dist/generators/portable-knowledge.js +613 -0
- package/dist/generators/portable-knowledge.js.map +1 -0
- package/dist/generators/portable-knowledge.test.d.ts +2 -0
- package/dist/generators/portable-knowledge.test.d.ts.map +1 -0
- package/dist/generators/portable-knowledge.test.js +927 -0
- package/dist/generators/portable-knowledge.test.js.map +1 -0
- package/dist/generators/surface-profiles.d.ts +49 -0
- package/dist/generators/surface-profiles.d.ts.map +1 -0
- package/dist/generators/surface-profiles.js +98 -0
- package/dist/generators/surface-profiles.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +858 -32
- package/dist/index.js.map +1 -1
- package/dist/lib/activation.d.ts +28 -0
- package/dist/lib/activation.d.ts.map +1 -0
- package/dist/lib/activation.js +57 -0
- package/dist/lib/activation.js.map +1 -0
- package/dist/lib/activation.test.d.ts +6 -0
- package/dist/lib/activation.test.d.ts.map +1 -0
- package/dist/lib/activation.test.js +121 -0
- package/dist/lib/activation.test.js.map +1 -0
- package/dist/lib/canonicalRefs.d.ts +69 -0
- package/dist/lib/canonicalRefs.d.ts.map +1 -0
- package/dist/lib/canonicalRefs.js +83 -0
- package/dist/lib/canonicalRefs.js.map +1 -0
- package/dist/lib/client.d.ts +62 -1
- package/dist/lib/client.d.ts.map +1 -1
- package/dist/lib/client.js +259 -13
- package/dist/lib/client.js.map +1 -1
- package/dist/lib/collectionRegistry.d.ts +38 -0
- package/dist/lib/collectionRegistry.d.ts.map +1 -0
- package/dist/lib/collectionRegistry.js +112 -0
- package/dist/lib/collectionRegistry.js.map +1 -0
- package/dist/lib/config.d.ts +122 -2
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +426 -18
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/connectKeyLabel.d.ts +9 -0
- package/dist/lib/connectKeyLabel.d.ts.map +1 -0
- package/dist/lib/connectKeyLabel.js +12 -0
- package/dist/lib/connectKeyLabel.js.map +1 -0
- package/dist/lib/constants.d.ts +42 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/constants.js +76 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/conversation-engine.d.ts +45 -0
- package/dist/lib/conversation-engine.d.ts.map +1 -0
- package/dist/lib/conversation-engine.js +112 -0
- package/dist/lib/conversation-engine.js.map +1 -0
- package/dist/lib/conversation-phases.d.ts +59 -0
- package/dist/lib/conversation-phases.d.ts.map +1 -0
- package/dist/lib/conversation-phases.js +11 -0
- package/dist/lib/conversation-phases.js.map +1 -0
- package/dist/lib/conversation-signals.d.ts +30 -0
- package/dist/lib/conversation-signals.d.ts.map +1 -0
- package/dist/lib/conversation-signals.js +64 -0
- package/dist/lib/conversation-signals.js.map +1 -0
- package/dist/lib/deployment.d.ts +23 -0
- package/dist/lib/deployment.d.ts.map +1 -0
- package/dist/lib/deployment.js +78 -0
- package/dist/lib/deployment.js.map +1 -0
- package/dist/lib/deployment.test.d.ts +5 -0
- package/dist/lib/deployment.test.d.ts.map +1 -0
- package/dist/lib/deployment.test.js +54 -0
- package/dist/lib/deployment.test.js.map +1 -0
- package/dist/lib/errors.d.ts +60 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +69 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/experiment.d.ts +18 -0
- package/dist/lib/experiment.d.ts.map +1 -0
- package/dist/lib/experiment.js +28 -0
- package/dist/lib/experiment.js.map +1 -0
- package/dist/lib/format.d.ts +10 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +27 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/glossary.d.ts +19 -0
- package/dist/lib/glossary.d.ts.map +1 -0
- package/dist/lib/glossary.js +53 -0
- package/dist/lib/glossary.js.map +1 -0
- package/dist/lib/hook-intents.d.ts +51 -0
- package/dist/lib/hook-intents.d.ts.map +1 -0
- package/dist/lib/hook-intents.js +85 -0
- package/dist/lib/hook-intents.js.map +1 -0
- package/dist/lib/inferSourceDate.d.ts +12 -0
- package/dist/lib/inferSourceDate.d.ts.map +1 -0
- package/dist/lib/inferSourceDate.js +44 -0
- package/dist/lib/inferSourceDate.js.map +1 -0
- package/dist/lib/method-registry.d.ts +32 -0
- package/dist/lib/method-registry.d.ts.map +1 -0
- package/dist/lib/method-registry.js +53 -0
- package/dist/lib/method-registry.js.map +1 -0
- package/dist/lib/onboarding-path-b.d.ts +10 -0
- package/dist/lib/onboarding-path-b.d.ts.map +1 -0
- package/dist/lib/onboarding-path-b.js +214 -0
- package/dist/lib/onboarding-path-b.js.map +1 -0
- package/dist/lib/onboarding-shared.d.ts +81 -0
- package/dist/lib/onboarding-shared.d.ts.map +1 -0
- package/dist/lib/onboarding-shared.js +190 -0
- package/dist/lib/onboarding-shared.js.map +1 -0
- package/dist/lib/onboarding-topics.d.ts +27 -0
- package/dist/lib/onboarding-topics.d.ts.map +1 -0
- package/dist/lib/onboarding-topics.js +57 -0
- package/dist/lib/onboarding-topics.js.map +1 -0
- package/dist/lib/onboarding.d.ts +17 -0
- package/dist/lib/onboarding.d.ts.map +1 -0
- package/dist/lib/onboarding.js +350 -0
- package/dist/lib/onboarding.js.map +1 -0
- package/dist/lib/profiles.d.ts +39 -0
- package/dist/lib/profiles.d.ts.map +1 -0
- package/dist/lib/profiles.js +185 -0
- package/dist/lib/profiles.js.map +1 -0
- package/dist/lib/prompts.d.ts +65 -0
- package/dist/lib/prompts.d.ts.map +1 -0
- package/dist/lib/prompts.js +132 -0
- package/dist/lib/prompts.js.map +1 -0
- package/dist/lib/repo-detect.d.ts +33 -0
- package/dist/lib/repo-detect.d.ts.map +1 -0
- package/dist/lib/repo-detect.js +83 -0
- package/dist/lib/repo-detect.js.map +1 -0
- package/dist/lib/runner.d.ts +33 -0
- package/dist/lib/runner.d.ts.map +1 -0
- package/dist/lib/runner.js +79 -0
- package/dist/lib/runner.js.map +1 -0
- package/dist/lib/session.d.ts +42 -0
- package/dist/lib/session.d.ts.map +1 -0
- package/dist/lib/session.js +109 -0
- package/dist/lib/session.js.map +1 -0
- package/dist/lib/spinner.d.ts +27 -0
- package/dist/lib/spinner.d.ts.map +1 -0
- package/dist/lib/spinner.js +76 -0
- package/dist/lib/spinner.js.map +1 -0
- package/dist/lib/spinner.test.d.ts +2 -0
- package/dist/lib/spinner.test.d.ts.map +1 -0
- package/dist/lib/spinner.test.js +39 -0
- package/dist/lib/spinner.test.js.map +1 -0
- package/dist/lib/state.d.ts +51 -0
- package/dist/lib/state.d.ts.map +1 -0
- package/dist/lib/state.js +90 -0
- package/dist/lib/state.js.map +1 -0
- package/dist/lib/strip.d.ts +12 -0
- package/dist/lib/strip.d.ts.map +1 -0
- package/dist/lib/strip.js +41 -0
- package/dist/lib/strip.js.map +1 -0
- package/dist/lib/style.d.ts +96 -0
- package/dist/lib/style.d.ts.map +1 -0
- package/dist/lib/style.js +169 -0
- package/dist/lib/style.js.map +1 -0
- package/dist/lib/style.test.d.ts +7 -0
- package/dist/lib/style.test.d.ts.map +1 -0
- package/dist/lib/style.test.js +263 -0
- package/dist/lib/style.test.js.map +1 -0
- package/dist/lib/telemetry.d.ts +15 -0
- package/dist/lib/telemetry.d.ts.map +1 -0
- package/dist/lib/telemetry.js +47 -0
- package/dist/lib/telemetry.js.map +1 -0
- package/dist/lib/tokenConstants.d.ts +17 -0
- package/dist/lib/tokenConstants.d.ts.map +1 -0
- package/dist/lib/tokenConstants.js +17 -0
- package/dist/lib/tokenConstants.js.map +1 -0
- package/dist/lib/update-check.d.ts +21 -0
- package/dist/lib/update-check.d.ts.map +1 -0
- package/dist/lib/update-check.js +145 -0
- package/dist/lib/update-check.js.map +1 -0
- package/dist/lib/wizard-surfaces.d.ts +47 -0
- package/dist/lib/wizard-surfaces.d.ts.map +1 -0
- package/dist/lib/wizard-surfaces.js +176 -0
- package/dist/lib/wizard-surfaces.js.map +1 -0
- package/dist/lib/wizard-surfaces.test.d.ts +2 -0
- package/dist/lib/wizard-surfaces.test.d.ts.map +1 -0
- package/dist/lib/wizard-surfaces.test.js +127 -0
- package/dist/lib/wizard-surfaces.test.js.map +1 -0
- package/dist/lib/wizard-trust.d.ts +31 -0
- package/dist/lib/wizard-trust.d.ts.map +1 -0
- package/dist/lib/wizard-trust.js +66 -0
- package/dist/lib/wizard-trust.js.map +1 -0
- package/dist/lib/wizard-trust.test.d.ts +2 -0
- package/dist/lib/wizard-trust.test.d.ts.map +1 -0
- package/dist/lib/wizard-trust.test.js +32 -0
- package/dist/lib/wizard-trust.test.js.map +1 -0
- package/dist/lib/workspace-probe.d.ts +19 -0
- package/dist/lib/workspace-probe.d.ts.map +1 -0
- package/dist/lib/workspace-probe.js +27 -0
- package/dist/lib/workspace-probe.js.map +1 -0
- package/dist/surfaces/registry.d.ts +20 -0
- package/dist/surfaces/registry.d.ts.map +1 -0
- package/dist/surfaces/registry.js +42 -0
- package/dist/surfaces/registry.js.map +1 -0
- package/package.json +15 -5
- package/templates/archetypes/boundary.md +23 -0
- package/templates/archetypes/constraint.md +23 -0
- package/templates/archetypes/convention.md +23 -0
- package/templates/archetypes/policy.md +23 -0
- package/templates/archetypes/quality-gate.md +23 -0
- package/templates/archetypes/workflow.md +23 -0
- package/templates/general/code-integrity.md +11 -0
- package/templates/general/getting-started.md +12 -0
- package/templates/method-registry.json +16 -0
- package/templates/node-ts/code-integrity.md +13 -0
- package/templates/node-ts/testing.md +12 -0
- package/templates/python/code-integrity.md +13 -0
- package/templates/python/testing.md +12 -0
|
@@ -0,0 +1,929 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pb handshake — generate context files for AI developer tools.
|
|
3
|
+
* The fourth delivery surface: context export (GLO-63, DEC-161).
|
|
4
|
+
*/
|
|
5
|
+
import { mkdirSync, writeFileSync, existsSync, readFileSync, readdirSync, copyFileSync } from 'fs';
|
|
6
|
+
import { join, dirname, resolve } from 'path';
|
|
7
|
+
import { homedir } from 'os';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import { createHash } from 'crypto';
|
|
10
|
+
import { getConfigOrGuide } from '../lib/config.js';
|
|
11
|
+
import { select as promptSelect } from '../lib/prompts.js';
|
|
12
|
+
import { composeHooksFromIntents, getHookStatusForSurface } from '../lib/hook-intents.js';
|
|
13
|
+
import { kernelCall, kernelCallWithSession } from '../lib/client.js';
|
|
14
|
+
import { readSession } from '../lib/session.js';
|
|
15
|
+
import { detectRepo, extractWorkspaceProfile } from '../lib/repo-detect.js';
|
|
16
|
+
import { generateContextMd } from '../generators/context-md.js';
|
|
17
|
+
import { generateBriefingMd } from '../generators/briefing-md.js';
|
|
18
|
+
import { MARKER, generateAgentsMd, generateClaudeMd, generateCursorMdc, generateCopilotMd } from '../generators/adapters.js';
|
|
19
|
+
import { readCanonicalSkills, readCanonicalRules, readPersonalLayer, readPersonalSkillsLayer, generateCursorSkill, generateCursorRule, generateCodexSkill, generateCodexSkillIndex, generateClaudeRule, generateClaudeSkillRouter, shouldEmitToTarget, filterByLevel, validateCodexSkills, evaluateConditions, STAGE_TO_MAX_LEVEL, LEVEL_ORDER, } from '../generators/portable-knowledge.js';
|
|
20
|
+
import { generateChainRules } from '../generators/chain-rules.js';
|
|
21
|
+
import { saveHandshakeState, loadPreviousState, diffHandshakeState, formatDiff, buildCurrentState, } from '../generators/handshake-diff.js';
|
|
22
|
+
import { resolveSurfaceProfile } from '../generators/surface-profiles.js';
|
|
23
|
+
import { formatHandshakeReport } from '../formatters/handshake.js';
|
|
24
|
+
import { readManifest, filterByAdoptionState } from '../generators/manifest.js';
|
|
25
|
+
import { generateBoundaryManifest, getBoundaryEnforcementMode } from '../generators/boundary-manifest.js';
|
|
26
|
+
import { loadMethodRegistry } from '../lib/method-registry.js';
|
|
27
|
+
import { CLIError, ErrorCode } from '../lib/errors.js';
|
|
28
|
+
import { trackEvent } from '../lib/telemetry.js';
|
|
29
|
+
const LEVELS = {
|
|
30
|
+
guide: {
|
|
31
|
+
label: 'Guide me',
|
|
32
|
+
description: "Explain what you're doing, ask before anything unfamiliar",
|
|
33
|
+
defaultMode: 'auto',
|
|
34
|
+
allow: ['Bash(pb:*)', 'Bash(git:*)'],
|
|
35
|
+
},
|
|
36
|
+
work: {
|
|
37
|
+
label: 'Just work',
|
|
38
|
+
description: 'Ask only when something could be risky or irreversible',
|
|
39
|
+
defaultMode: 'auto',
|
|
40
|
+
allow: ['Bash(pb:*)', 'Bash(git:*)', 'Bash(npm run:*)'],
|
|
41
|
+
},
|
|
42
|
+
silent: {
|
|
43
|
+
label: 'Silent',
|
|
44
|
+
description: "I'll review the diff; don't ask unless it's destructive",
|
|
45
|
+
defaultMode: 'acceptEdits',
|
|
46
|
+
allow: ['Bash(pb:*)', 'Bash(git:*)', 'Bash(npm run:*)', 'Bash(npx:*)'],
|
|
47
|
+
},
|
|
48
|
+
'full-trust': {
|
|
49
|
+
label: 'Full trust',
|
|
50
|
+
description: 'Never ask (your machine, your call)',
|
|
51
|
+
defaultMode: 'bypassPermissions',
|
|
52
|
+
allow: [],
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
// Explicit ordering — determines menu numbering in promptLevel(). Do not reorder.
|
|
56
|
+
const LEVEL_KEYS = ['guide', 'work', 'silent', 'full-trust'];
|
|
57
|
+
// Hook failure contract (TEN-712): all hook commands MUST end with '2>/dev/null || true'
|
|
58
|
+
// so Claude Code always starts even if pb is unavailable. Never remove this suffix.
|
|
59
|
+
const INIT_PERMISSION = 'Bash(pb:*)';
|
|
60
|
+
function readSettings(filePath) {
|
|
61
|
+
if (!existsSync(filePath))
|
|
62
|
+
return {};
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(readFileSync(filePath, 'utf8'));
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
console.error(`Warning: ${filePath} has invalid JSON — starting fresh.`);
|
|
68
|
+
return {};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Team write: hooks + Bash(pb:*) → .claude/settings.json (safe to commit)
|
|
72
|
+
function writeTeamSettings(cwd, dryRun) {
|
|
73
|
+
const claudeDir = join(cwd, '.claude');
|
|
74
|
+
const settingsPath = join(claudeDir, 'settings.json');
|
|
75
|
+
const settings = readSettings(settingsPath);
|
|
76
|
+
const added = [];
|
|
77
|
+
const permissions = (settings.permissions ?? {});
|
|
78
|
+
const allow = permissions.allow ?? [];
|
|
79
|
+
if (!allow.includes(INIT_PERMISSION)) {
|
|
80
|
+
allow.push(INIT_PERMISSION);
|
|
81
|
+
permissions.allow = allow;
|
|
82
|
+
added.push('Bash(pb:*) permission');
|
|
83
|
+
}
|
|
84
|
+
settings.permissions = permissions;
|
|
85
|
+
const hooks = (settings.hooks ?? {});
|
|
86
|
+
const hookAdditions = composeHooksFromIntents(['session-start', 'session-close', 'pre-compact'], hooks);
|
|
87
|
+
for (const addition of hookAdditions) {
|
|
88
|
+
hooks[addition.event] = [...(hooks[addition.event] ?? []), addition.entry];
|
|
89
|
+
added.push(addition.label);
|
|
90
|
+
}
|
|
91
|
+
settings.hooks = hooks;
|
|
92
|
+
if (!dryRun) {
|
|
93
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
94
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
95
|
+
}
|
|
96
|
+
return added;
|
|
97
|
+
}
|
|
98
|
+
// Personal write: trust level → ~/.claude/settings.json (never committed)
|
|
99
|
+
function writePersonalSettings(levelKey, dryRun) {
|
|
100
|
+
const level = LEVELS[levelKey];
|
|
101
|
+
const globalDir = join(homedir(), '.claude');
|
|
102
|
+
const globalPath = join(globalDir, 'settings.json');
|
|
103
|
+
const settings = readSettings(globalPath);
|
|
104
|
+
const added = [];
|
|
105
|
+
const permissions = (settings.permissions ?? {});
|
|
106
|
+
if (permissions.defaultMode !== level.defaultMode) {
|
|
107
|
+
permissions.defaultMode = level.defaultMode;
|
|
108
|
+
added.push(`defaultMode → ${level.defaultMode}`);
|
|
109
|
+
}
|
|
110
|
+
const allow = permissions.allow ?? [];
|
|
111
|
+
for (const entry of level.allow) {
|
|
112
|
+
if (!allow.includes(entry)) {
|
|
113
|
+
allow.push(entry);
|
|
114
|
+
added.push(`allow: ${entry}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
permissions.allow = allow;
|
|
118
|
+
settings.permissions = permissions;
|
|
119
|
+
if (!dryRun) {
|
|
120
|
+
mkdirSync(globalDir, { recursive: true });
|
|
121
|
+
writeFileSync(globalPath, JSON.stringify(settings, null, 2) + '\n');
|
|
122
|
+
}
|
|
123
|
+
return added;
|
|
124
|
+
}
|
|
125
|
+
async function promptLevel() {
|
|
126
|
+
const result = await promptSelect({
|
|
127
|
+
message: 'How much should Claude explain before acting?',
|
|
128
|
+
options: LEVEL_KEYS.map((key) => ({
|
|
129
|
+
value: key,
|
|
130
|
+
label: LEVELS[key].label,
|
|
131
|
+
hint: LEVELS[key].description,
|
|
132
|
+
})),
|
|
133
|
+
});
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
export async function runHandshakeInit(options = {}) {
|
|
137
|
+
const cwd = process.cwd();
|
|
138
|
+
const dryRun = options.dryRun ?? false;
|
|
139
|
+
const suffix = dryRun ? ' (dry run)' : '';
|
|
140
|
+
if (options.level && !LEVEL_KEYS.includes(options.level)) {
|
|
141
|
+
throw new CLIError(`Unknown level "${options.level}".`, {
|
|
142
|
+
code: ErrorCode.VALIDATION_FAILED,
|
|
143
|
+
category: 'validation',
|
|
144
|
+
guidance: `Valid levels: ${LEVEL_KEYS.join(', ')}`,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
console.log('Setting up Claude Code integration...\n');
|
|
148
|
+
// Step 1: Team config — always, no prompt
|
|
149
|
+
const teamAdded = writeTeamSettings(cwd, dryRun);
|
|
150
|
+
const teamPath = join(cwd, '.claude', 'settings.json');
|
|
151
|
+
if (teamAdded.length === 0) {
|
|
152
|
+
console.log(`✓ ${teamPath} — already up to date${suffix}`);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
console.log(`✓ ${teamPath}${suffix}`);
|
|
156
|
+
for (const item of teamAdded)
|
|
157
|
+
console.log(` + ${item}`);
|
|
158
|
+
}
|
|
159
|
+
// Step 2: Personal config — wizard or --level flag
|
|
160
|
+
// Cast is safe: LEVEL_KEYS.includes() validated above; invalid level already threw CLIError.
|
|
161
|
+
const levelKey = options.level ? options.level : await promptLevel();
|
|
162
|
+
const level = LEVELS[levelKey];
|
|
163
|
+
const personalAdded = writePersonalSettings(levelKey, dryRun);
|
|
164
|
+
const personalPath = join(homedir(), '.claude', 'settings.json');
|
|
165
|
+
if (personalAdded.length === 0) {
|
|
166
|
+
console.log(`✓ ${personalPath} — already at "${level.label}"${suffix}`);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
console.log(`✓ ${personalPath} — set to "${level.label}"${suffix}`);
|
|
170
|
+
for (const item of personalAdded)
|
|
171
|
+
console.log(` + ${item}`);
|
|
172
|
+
}
|
|
173
|
+
console.log('');
|
|
174
|
+
console.log('Team config → commit .claude/settings.json to share hooks with your team');
|
|
175
|
+
console.log('Personal config → ~/.claude/settings.json is private to this machine, never commit');
|
|
176
|
+
console.log('');
|
|
177
|
+
console.log('Hook failure: if pb is unavailable, hooks fail silently — Claude Code always starts.');
|
|
178
|
+
console.log('');
|
|
179
|
+
if (!dryRun)
|
|
180
|
+
console.log('Reload /hooks in Claude Code (or restart) to activate.');
|
|
181
|
+
console.log('Run `pb handshake --init --level <guide|work|silent|full-trust>` to change level.');
|
|
182
|
+
// Step 2b: Report multi-surface hook opt-in status (WP-310 E3b)
|
|
183
|
+
// Reads manifest.hooks.{cursor,copilot} and prints an informational note for
|
|
184
|
+
// each opted-in surface. Silence = no manifest or no hooks flags set.
|
|
185
|
+
// DEC-536: Claude-native default is already wired above; this block only fires
|
|
186
|
+
// when the user has explicitly opted in via manifest.
|
|
187
|
+
const pbDirForManifest = join(cwd, '.productbrain');
|
|
188
|
+
const initManifest = readManifest(pbDirForManifest);
|
|
189
|
+
const multiSurfaceOptIns = [];
|
|
190
|
+
if (initManifest?.hooks?.cursor)
|
|
191
|
+
multiSurfaceOptIns.push('cursor');
|
|
192
|
+
if (initManifest?.hooks?.copilot)
|
|
193
|
+
multiSurfaceOptIns.push('copilot');
|
|
194
|
+
if (multiSurfaceOptIns.length > 0) {
|
|
195
|
+
console.log('');
|
|
196
|
+
for (const surface of multiSurfaceOptIns) {
|
|
197
|
+
const status = getHookStatusForSurface(surface);
|
|
198
|
+
if (status.writable) {
|
|
199
|
+
// Future-proofing path: surface has hook events, would write files.
|
|
200
|
+
console.log(`ℹ ${surface} hooks: opted in — hooks will be written`);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
console.log(`ℹ ${surface} hooks: opted in — no auto-hooks (${surface} has no session events; run \`pb session start\` manually)`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Step 3: Scaffold starter templates if .productbrain/rules/ is empty
|
|
208
|
+
const rulesDir = join(cwd, '.productbrain', 'rules');
|
|
209
|
+
const hasExistingRules = existsSync(rulesDir) && readdirSync(rulesDir).filter((f) => f.endsWith('.md')).length > 0;
|
|
210
|
+
if (!hasExistingRules) {
|
|
211
|
+
// Detect stack from repo to pick template set
|
|
212
|
+
const repo = detectRepo(cwd);
|
|
213
|
+
const stack = repo.detectedStack.map((s) => s.toLowerCase());
|
|
214
|
+
let templateSet;
|
|
215
|
+
if (stack.some((s) => ['typescript', 'sveltekit', 'nextjs', 'react'].includes(s))) {
|
|
216
|
+
templateSet = 'node-ts';
|
|
217
|
+
}
|
|
218
|
+
else if (stack.includes('python')) {
|
|
219
|
+
templateSet = 'python';
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
templateSet = 'general';
|
|
223
|
+
}
|
|
224
|
+
// Resolve templates directory relative to this file
|
|
225
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
226
|
+
const __dirname = dirname(__filename);
|
|
227
|
+
// From dist/commands/handshake.js → ../../templates/
|
|
228
|
+
const templatesRoot = resolve(__dirname, '..', '..', 'templates');
|
|
229
|
+
const templateDir = join(templatesRoot, templateSet);
|
|
230
|
+
if (existsSync(templateDir)) {
|
|
231
|
+
const templateFiles = readdirSync(templateDir).filter((f) => f.endsWith('.md'));
|
|
232
|
+
if (templateFiles.length > 0) {
|
|
233
|
+
console.log('');
|
|
234
|
+
console.log(`Scaffolding starter rules from ${templateSet} template...`);
|
|
235
|
+
if (!dryRun) {
|
|
236
|
+
mkdirSync(rulesDir, { recursive: true });
|
|
237
|
+
for (const file of templateFiles) {
|
|
238
|
+
const src = join(templateDir, file);
|
|
239
|
+
const dest = join(rulesDir, file);
|
|
240
|
+
copyFileSync(src, dest);
|
|
241
|
+
console.log(` + .productbrain/rules/${file}`);
|
|
242
|
+
}
|
|
243
|
+
console.log('');
|
|
244
|
+
console.log('Run `pb handshake` to sync the scaffolded rules to your AI tools.');
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
for (const file of templateFiles) {
|
|
248
|
+
console.log(` + .productbrain/rules/${file} (dry run)`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Normalize volatile handshake-only timestamps before comparing generated files.
|
|
257
|
+
* This keeps the visible timestamps in generated artifacts while avoiding
|
|
258
|
+
* meaningless rewrites when semantic content is unchanged.
|
|
259
|
+
*/
|
|
260
|
+
export function normalizeHandshakeContentForComparison(content) {
|
|
261
|
+
return content
|
|
262
|
+
.replace(/<!-- auto-generated by pb handshake — [0-9]{4}-[0-9]{2}-[0-9]{2}T[^>]+ -->/g, '<!-- auto-generated by pb handshake — <TIMESTAMP> -->')
|
|
263
|
+
.replace(/^_Generated: [0-9]{4}-[0-9]{2}-[0-9]{2}T.*_$/gm, '_Generated: <TIMESTAMP>_');
|
|
264
|
+
}
|
|
265
|
+
function shouldWriteAdapter(filePath, force) {
|
|
266
|
+
if (force)
|
|
267
|
+
return true;
|
|
268
|
+
if (!existsSync(filePath))
|
|
269
|
+
return true;
|
|
270
|
+
const content = readFileSync(filePath, 'utf8');
|
|
271
|
+
return content.includes(MARKER);
|
|
272
|
+
}
|
|
273
|
+
function deduplicateEntries(entries) {
|
|
274
|
+
const seen = new Set();
|
|
275
|
+
const result = [];
|
|
276
|
+
for (const e of entries) {
|
|
277
|
+
const key = e.entryId ?? e.name;
|
|
278
|
+
if (!seen.has(key)) {
|
|
279
|
+
seen.add(key);
|
|
280
|
+
result.push(e);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
export async function runHandshake(options = {}) {
|
|
286
|
+
const config = await getConfigOrGuide(() => runHandshake(options));
|
|
287
|
+
if (!config)
|
|
288
|
+
return;
|
|
289
|
+
const cwd = process.cwd();
|
|
290
|
+
const force = options.force ?? false;
|
|
291
|
+
const dryRun = options.dryRun ?? false;
|
|
292
|
+
// Preview mode: default when neither --apply nor --dry-run is passed.
|
|
293
|
+
// --dry-run is kept as a backward-compat alias for preview (same behavior).
|
|
294
|
+
const preview = !options.apply && !dryRun;
|
|
295
|
+
const applyMode = options.apply === true && !dryRun;
|
|
296
|
+
const level = options.level;
|
|
297
|
+
const quiet = options.quiet ?? false;
|
|
298
|
+
const generate = options.generate ?? false;
|
|
299
|
+
const timestamp = new Date().toISOString();
|
|
300
|
+
// Helper: emit progress line only when not quiet (used when handshake is a sub-step)
|
|
301
|
+
const log = (msg) => { if (!quiet)
|
|
302
|
+
process.stdout.write(msg + '\n'); };
|
|
303
|
+
const logErr = (msg) => { if (!quiet)
|
|
304
|
+
process.stderr.write(msg + '\n'); };
|
|
305
|
+
// Validate --level if provided (for handshake content filtering, not --init trust level)
|
|
306
|
+
const VALID_HANDSHAKE_LEVELS = ['beginner', 'intermediate', 'expert'];
|
|
307
|
+
if (level && !VALID_HANDSHAKE_LEVELS.includes(level)) {
|
|
308
|
+
throw new CLIError(`Unknown level "${level}".`, {
|
|
309
|
+
code: ErrorCode.VALIDATION_FAILED,
|
|
310
|
+
category: 'validation',
|
|
311
|
+
guidance: `Valid levels: ${VALID_HANDSHAKE_LEVELS.join(', ')}`,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
// 1. Detect repo
|
|
315
|
+
const repo = detectRepo(cwd);
|
|
316
|
+
log(`Detecting repo: ${repo.name ?? 'unknown'}${repo.repoSlug ? ` (${repo.repoSlug})` : ''}`);
|
|
317
|
+
if (repo.detectedStack.length > 0) {
|
|
318
|
+
log(`Stack: ${repo.detectedStack.join(', ')}`);
|
|
319
|
+
}
|
|
320
|
+
// 2. Fetch orient view + workspace readiness in parallel (budget max +200ms added latency)
|
|
321
|
+
log('Fetching workspace context...');
|
|
322
|
+
let orientView = null;
|
|
323
|
+
let workspaceProfile = null;
|
|
324
|
+
try {
|
|
325
|
+
const workspaceReadinessPromise = kernelCall('chain.workspaceReadiness', {}).catch(() => null);
|
|
326
|
+
const [orientResult, readinessRaw] = await Promise.all([
|
|
327
|
+
kernelCall('chain.getOrientView', {}).catch((err) => {
|
|
328
|
+
logErr(`Warning: could not fetch workspace context — ${err instanceof Error ? err.message : err}`);
|
|
329
|
+
logErr('Continuing with limited context (Chain search + portable knowledge only).');
|
|
330
|
+
return null;
|
|
331
|
+
}),
|
|
332
|
+
workspaceReadinessPromise,
|
|
333
|
+
]);
|
|
334
|
+
orientView = orientResult;
|
|
335
|
+
workspaceProfile = extractWorkspaceProfile(readinessRaw);
|
|
336
|
+
}
|
|
337
|
+
catch (err) {
|
|
338
|
+
logErr(`Warning: could not fetch workspace context — ${err instanceof Error ? err.message : err}`);
|
|
339
|
+
logErr('Continuing with limited context (Chain search + portable knowledge only).');
|
|
340
|
+
}
|
|
341
|
+
if (workspaceProfile) {
|
|
342
|
+
log(`Workspace profile: stage=${workspaceProfile.stage}, entries=${workspaceProfile.totalEntries}, governance=${workspaceProfile.governanceMode}`);
|
|
343
|
+
}
|
|
344
|
+
// 3. Build search queries from repo context
|
|
345
|
+
const searchQueries = [];
|
|
346
|
+
if (repo.name && repo.name.length >= 2)
|
|
347
|
+
searchQueries.push(repo.name);
|
|
348
|
+
// Add repo slug parts (e.g. "Product-OS" -> "Product-OS")
|
|
349
|
+
if (repo.repoSlug) {
|
|
350
|
+
const repoPart = repo.repoSlug.split('/').pop();
|
|
351
|
+
if (repoPart && repoPart.length >= 2 && repoPart !== repo.name) {
|
|
352
|
+
searchQueries.push(repoPart);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Limit to 5 unique queries
|
|
356
|
+
const uniqueQueries = [...new Set(searchQueries.filter((q) => q.length >= 2))].slice(0, 5);
|
|
357
|
+
// 4. Search the Chain
|
|
358
|
+
let matchedEntries = [];
|
|
359
|
+
if (uniqueQueries.length > 0) {
|
|
360
|
+
log(`Searching Chain for: ${uniqueQueries.join(', ')}...`);
|
|
361
|
+
const searchResults = await Promise.all(uniqueQueries.map((q) => kernelCall('chain.searchEntries', { query: q }).catch(() => [])));
|
|
362
|
+
matchedEntries = deduplicateEntries(searchResults.flat());
|
|
363
|
+
}
|
|
364
|
+
// 5. Read canonical skills & rules — DB-first (WP-345 S0c, TEN-1459), FS fallback.
|
|
365
|
+
// Primary: query setup.listAssetsForUser from DB (workspace SSOT).
|
|
366
|
+
// Fallback: read from .productbrain/ filesystem (legacy — used when DB is empty or unavailable).
|
|
367
|
+
const pbDir = join(cwd, '.productbrain');
|
|
368
|
+
let dbSkills = [];
|
|
369
|
+
let dbRules = [];
|
|
370
|
+
let usedDbSource = false;
|
|
371
|
+
let dbAssetRows = [];
|
|
372
|
+
const dbProjectionHashes = new Map();
|
|
373
|
+
try {
|
|
374
|
+
const dbAssets = await kernelCall('setup.listAssetsForUser', {}).catch(() => null);
|
|
375
|
+
if (dbAssets && dbAssets.length > 0) {
|
|
376
|
+
dbAssetRows = dbAssets;
|
|
377
|
+
// Map DB assets to CanonicalSkill/CanonicalRule shapes
|
|
378
|
+
for (const asset of dbAssets) {
|
|
379
|
+
if (asset.disabledByOwner)
|
|
380
|
+
continue;
|
|
381
|
+
if (asset.assetKind === 'skill') {
|
|
382
|
+
dbSkills.push({
|
|
383
|
+
name: asset.name,
|
|
384
|
+
description: asset.description,
|
|
385
|
+
triggers: asset.triggers ?? [],
|
|
386
|
+
body: asset.body,
|
|
387
|
+
sourcePath: `db:${asset.entryId}`,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
else if (asset.assetKind === 'rule' || asset.assetKind === 'hook') {
|
|
391
|
+
dbRules.push({
|
|
392
|
+
name: asset.name,
|
|
393
|
+
description: asset.description,
|
|
394
|
+
autoApply: false,
|
|
395
|
+
body: asset.body,
|
|
396
|
+
sourcePath: `db:${asset.entryId}`,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
usedDbSource = true;
|
|
401
|
+
log(`Setup assets: ${dbSkills.length} skills, ${dbRules.length} rules/hooks from DB (WP-345 DB-first path)`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
// DB source unavailable — silently fall through to FS path
|
|
406
|
+
}
|
|
407
|
+
const allSkills = usedDbSource ? dbSkills : readCanonicalSkills(pbDir);
|
|
408
|
+
const manualRules = usedDbSource ? dbRules : readCanonicalRules(pbDir);
|
|
409
|
+
if (!usedDbSource) {
|
|
410
|
+
log('Setup assets: reading from filesystem (DB source unavailable or empty)');
|
|
411
|
+
}
|
|
412
|
+
// 5a-pre. E4: Resolve semantic refs in DB assets (WP-345, DEC-A, DEC-C, DEC-K)
|
|
413
|
+
// For each asset with semanticRefs[], resolve them via the Convex resolver and
|
|
414
|
+
// replace {{ref:key}} placeholders in the body. Runs in apply mode only (not preview).
|
|
415
|
+
// NG11: PostHog events fire from CLI side only (never inside Convex mutations).
|
|
416
|
+
if (usedDbSource && applyMode) {
|
|
417
|
+
const projectableDbAssets = dbAssetRows.filter((a) => !a.disabledByOwner && (a.assetKind === 'skill' || a.assetKind === 'rule' || a.assetKind === 'hook'));
|
|
418
|
+
const assetsWithRefs = projectableDbAssets.filter((a) => a.semanticRefs && a.semanticRefs.length > 0);
|
|
419
|
+
if (assetsWithRefs.length > 0) {
|
|
420
|
+
log(`Resolving semantic refs for ${assetsWithRefs.length} asset(s)...`);
|
|
421
|
+
// Collect unique ref keys across all assets
|
|
422
|
+
const allRefKeys = [...new Set(assetsWithRefs.flatMap((a) => a.semanticRefs))];
|
|
423
|
+
// Resolve all refs in a single batch call. Shape: SetupRefResolution[]
|
|
424
|
+
// (DEC-767 / WP-354 Build-Order #6 — kind + status discriminator).
|
|
425
|
+
let resolvedRefs = [];
|
|
426
|
+
try {
|
|
427
|
+
resolvedRefs = await kernelCall('setup.resolveSemanticRefs', { semanticRefs: allRefKeys });
|
|
428
|
+
}
|
|
429
|
+
catch (err) {
|
|
430
|
+
trackEvent('setup.refs.resolve_failed', { error: err instanceof Error ? err.message : String(err) });
|
|
431
|
+
logErr(`Warning: could not resolve semantic refs — ${err instanceof Error ? err.message : String(err)}`);
|
|
432
|
+
}
|
|
433
|
+
// Build resolved map: canonicalKey → display name. Only required refs
|
|
434
|
+
// count as unresolved warnings; seed/unknown refs are not gates.
|
|
435
|
+
const resolvedMap = new Map();
|
|
436
|
+
let unresolvedCount = 0;
|
|
437
|
+
for (const result of resolvedRefs) {
|
|
438
|
+
if (result.status === 'resolved' && result.localEntryId) {
|
|
439
|
+
resolvedMap.set(result.ref, result.localEntryId);
|
|
440
|
+
trackEvent('skill.ref.resolved', { ref: result.ref, kind: result.kind });
|
|
441
|
+
}
|
|
442
|
+
else if (result.status === 'unsupported-future') {
|
|
443
|
+
// seed: refs are explicitly future scope per WP-354 Build-Order #6 — not a warning.
|
|
444
|
+
trackEvent('skill.ref.future', { ref: result.ref, kind: result.kind });
|
|
445
|
+
}
|
|
446
|
+
else if (result.required) {
|
|
447
|
+
unresolvedCount++;
|
|
448
|
+
trackEvent('skill.ref.unresolved', { ref: result.ref, kind: result.kind, status: result.status });
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (unresolvedCount > 0) {
|
|
452
|
+
logErr(`Warning: ${unresolvedCount} required semantic ref(s) could not be resolved.`);
|
|
453
|
+
}
|
|
454
|
+
// Projection must preserve ref tokens as portable machine-readable refs.
|
|
455
|
+
// resolvedMap is only used for validation/telemetry here; generated setup
|
|
456
|
+
// artifacts keep {{ref:domain:...}} / {{ref:entry:...}} intact.
|
|
457
|
+
}
|
|
458
|
+
// Compute projection hash for each projected DB asset (DEC-K).
|
|
459
|
+
// The stored hash is sha256 of the normalized resolved asset body; DB persistence
|
|
460
|
+
// is deferred until the write loop confirms a matching file was emitted.
|
|
461
|
+
// Strip existing hash trailer and timestamp lines, normalize to LF, then hash.
|
|
462
|
+
const HASH_TRAILER_REGEX = /^<!--\s*pb-hash:.*-->\s*$/gm;
|
|
463
|
+
const TIMESTAMP_REGEX = /^<!--\s*pb-generated-at:.*-->\s*$/gm;
|
|
464
|
+
for (const rawAsset of projectableDbAssets) {
|
|
465
|
+
const resolvedAsset = [...dbSkills, ...dbRules].find((a) => a.sourcePath === `db:${rawAsset.entryId}`);
|
|
466
|
+
if (!resolvedAsset)
|
|
467
|
+
continue;
|
|
468
|
+
try {
|
|
469
|
+
// Build the projected body (what will be written to disk)
|
|
470
|
+
const projectedBody = resolvedAsset.body;
|
|
471
|
+
// Normalize: strip existing hash/timestamp trailers, convert to LF
|
|
472
|
+
const normalized = projectedBody
|
|
473
|
+
.replace(HASH_TRAILER_REGEX, '')
|
|
474
|
+
.replace(TIMESTAMP_REGEX, '')
|
|
475
|
+
.replace(/\r\n/g, '\n')
|
|
476
|
+
.replace(/\r/g, '\n')
|
|
477
|
+
.trimEnd();
|
|
478
|
+
// Compute sha256 hash
|
|
479
|
+
const hash = createHash('sha256').update(normalized, 'utf8').digest('hex');
|
|
480
|
+
const hashTrailer = `<!-- pb-hash: sha256:${hash} -->`;
|
|
481
|
+
// Append hash trailer to the projected body
|
|
482
|
+
resolvedAsset.body = `${normalized}\n${hashTrailer}`;
|
|
483
|
+
dbProjectionHashes.set(rawAsset.entryId, {
|
|
484
|
+
hash: `sha256:${hash}`,
|
|
485
|
+
assetKind: rawAsset.assetKind,
|
|
486
|
+
});
|
|
487
|
+
// Drift detection: compare against last known hash
|
|
488
|
+
if (rawAsset.lastProjectedHash && rawAsset.lastProjectedHash !== `sha256:${hash}`) {
|
|
489
|
+
trackEvent('skill.drift.detected', {
|
|
490
|
+
entryId: rawAsset.entryId,
|
|
491
|
+
assetKind: rawAsset.assetKind,
|
|
492
|
+
});
|
|
493
|
+
log(`Drift detected for asset ${rawAsset.entryId} — projecting updated version.`);
|
|
494
|
+
}
|
|
495
|
+
trackEvent('skill.projection.succeeded', {
|
|
496
|
+
entryId: rawAsset.entryId,
|
|
497
|
+
assetKind: rawAsset.assetKind,
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
catch (err) {
|
|
501
|
+
trackEvent('skill.projection.failed', {
|
|
502
|
+
entryId: rawAsset.entryId,
|
|
503
|
+
assetKind: rawAsset.assetKind,
|
|
504
|
+
});
|
|
505
|
+
logErr(`Warning: projection failed for ${rawAsset.entryId} — ${err instanceof Error ? err.message : String(err)}`);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
// 5a. Optionally fetch and merge Chain-derived rules (--generate flag)
|
|
510
|
+
let chainRulesStats = null;
|
|
511
|
+
let chainGaps = [];
|
|
512
|
+
let allRules = manualRules;
|
|
513
|
+
if (generate) {
|
|
514
|
+
log('Generating Chain-derived rules...');
|
|
515
|
+
const chainResult = await generateChainRules(kernelCall, manualRules);
|
|
516
|
+
if (chainResult.sentinel) {
|
|
517
|
+
// MCP unavailable — inject sentinel rule and warn
|
|
518
|
+
allRules = [...manualRules, chainResult.sentinel];
|
|
519
|
+
logErr('Warning: Chain MCP unavailable — generated rules are disabled. Sentinel rule injected.');
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
// Merge generated rules after manual rules (manual takes precedence on dedup)
|
|
523
|
+
allRules = [...manualRules, ...chainResult.rules];
|
|
524
|
+
chainRulesStats = chainResult.stats;
|
|
525
|
+
chainGaps = chainResult.gaps;
|
|
526
|
+
const { generatedRules, suppressedByManual, suppressedByZeroEntries } = chainResult.stats;
|
|
527
|
+
log(`Chain-derived rules: ${generatedRules} generated, ${suppressedByManual} suppressed by manual, ${suppressedByZeroEntries} gaps`);
|
|
528
|
+
if (chainGaps.length > 0) {
|
|
529
|
+
log(`Gaps (no matching governance entries): ${chainGaps.join(', ')}`);
|
|
530
|
+
}
|
|
531
|
+
// Diff: compare current state against previous run
|
|
532
|
+
const previousState = loadPreviousState(pbDir);
|
|
533
|
+
const currentState = buildCurrentState(chainResult.rules, chainResult.classified);
|
|
534
|
+
saveHandshakeState(pbDir, chainResult.rules, chainResult.classified);
|
|
535
|
+
const diff = diffHandshakeState(currentState, previousState);
|
|
536
|
+
const diffText = formatDiff(diff);
|
|
537
|
+
log(diffText);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
// 5b. Read personal layer (WP-310 E2) — machine-local rules/skills from .productbrain/.local/
|
|
541
|
+
// Returns [] when the directory is absent. All returned entries have persist: 'local'.
|
|
542
|
+
const personalRules = readPersonalLayer(pbDir);
|
|
543
|
+
if (personalRules.length > 0) {
|
|
544
|
+
// Name collision detection — warn when a personal rule overrides a team rule
|
|
545
|
+
const teamRuleNames = new Set(allRules.map((r) => r.name));
|
|
546
|
+
for (const pr of personalRules) {
|
|
547
|
+
if (teamRuleNames.has(pr.name)) {
|
|
548
|
+
logErr(`Personal rule "${pr.name}" overrides team rule (local takes precedence).`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
// Merge: team rules first, personal rules appended (personal takes precedence via name collision above)
|
|
552
|
+
allRules = [...allRules, ...personalRules];
|
|
553
|
+
}
|
|
554
|
+
const personalSkills = readPersonalSkillsLayer(pbDir);
|
|
555
|
+
if (personalSkills.length > 0) {
|
|
556
|
+
const teamSkillNames = new Set(allSkills.map((s) => s.name));
|
|
557
|
+
for (const ps of personalSkills) {
|
|
558
|
+
if (teamSkillNames.has(ps.name)) {
|
|
559
|
+
logErr(`Personal skill "${ps.name}" overrides team skill (local takes precedence).`);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
allSkills.push(...personalSkills);
|
|
563
|
+
}
|
|
564
|
+
// 5c. Apply manifest-based adoption filter (WP-310 E1)
|
|
565
|
+
// readManifest returns null when manifest.yaml is absent → filterByAdoptionState is a no-op.
|
|
566
|
+
const manifest = readManifest(pbDir);
|
|
567
|
+
// 5d. Load method registry (WP-310 E4) — only when manifest is present.
|
|
568
|
+
let registrySource;
|
|
569
|
+
let registryStale;
|
|
570
|
+
if (manifest) {
|
|
571
|
+
const registryResult = await loadMethodRegistry(manifest.method_source, kernelCall).catch(() => null);
|
|
572
|
+
if (registryResult) {
|
|
573
|
+
registrySource = registryResult.source;
|
|
574
|
+
registryStale = registryResult.stale;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
const adoptionFilteredSkills = filterByAdoptionState(allSkills, manifest);
|
|
578
|
+
const adoptionFilteredRules = filterByAdoptionState(allRules, manifest);
|
|
579
|
+
// Compute adoption counts for report (only meaningful when manifest is present)
|
|
580
|
+
const adoptedRulesCount = manifest ? adoptionFilteredRules.length : undefined;
|
|
581
|
+
const rejectedRulesCount = manifest ? allRules.length - adoptionFilteredRules.length : undefined;
|
|
582
|
+
// Apply level filtering with stage-gating (after adoption filter, before target filtering in write loop)
|
|
583
|
+
// Stage caps the effective level: blank→beginner, seed→intermediate, grounded+→expert.
|
|
584
|
+
// If stage caps below the requested level, log it so the user knows why items were dropped.
|
|
585
|
+
const profileStage = workspaceProfile?.stage;
|
|
586
|
+
const levelFilteredSkills = filterByLevel(adoptionFilteredSkills, level, profileStage);
|
|
587
|
+
const levelFilteredRules = filterByLevel(adoptionFilteredRules, level, profileStage);
|
|
588
|
+
// Log when stage gating changes the effective level
|
|
589
|
+
if (profileStage) {
|
|
590
|
+
const stageCap = STAGE_TO_MAX_LEVEL[profileStage];
|
|
591
|
+
if (stageCap) {
|
|
592
|
+
const requestedIdx = level ? LEVEL_ORDER.indexOf(level) : LEVEL_ORDER.length - 1;
|
|
593
|
+
const capIdx = LEVEL_ORDER.indexOf(stageCap);
|
|
594
|
+
if (capIdx < requestedIdx) {
|
|
595
|
+
log(`Stage "${profileStage}" caps level from ${level || 'expert'} to ${stageCap}`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
// Apply when-condition filtering (stage-aware, workspace profile + repo context)
|
|
600
|
+
const canonicalSkills = levelFilteredSkills.filter((skill) => {
|
|
601
|
+
const result = evaluateConditions(skill.conditions ?? {}, workspaceProfile, repo);
|
|
602
|
+
if (dryRun && !result.included) {
|
|
603
|
+
log(` EXCLUDED skill ${skill.name}: ${result.reasons.join(', ')}`);
|
|
604
|
+
}
|
|
605
|
+
return result.included;
|
|
606
|
+
});
|
|
607
|
+
const canonicalRules = levelFilteredRules.filter((rule) => {
|
|
608
|
+
const result = evaluateConditions(rule.conditions ?? {}, workspaceProfile, repo);
|
|
609
|
+
if (dryRun && !result.included) {
|
|
610
|
+
log(` EXCLUDED rule ${rule.name}: ${result.reasons.join(', ')}`);
|
|
611
|
+
}
|
|
612
|
+
return result.included;
|
|
613
|
+
});
|
|
614
|
+
if (dryRun && canonicalSkills.length > 0) {
|
|
615
|
+
log(` INCLUDED skills: ${canonicalSkills.map((s) => s.name).join(', ')}`);
|
|
616
|
+
}
|
|
617
|
+
if (dryRun && canonicalRules.length > 0) {
|
|
618
|
+
log(` INCLUDED rules: ${canonicalRules.map((r) => r.name).join(', ')}`);
|
|
619
|
+
}
|
|
620
|
+
if (canonicalSkills.length > 0 || canonicalRules.length > 0) {
|
|
621
|
+
const levelSuffix = level ? ` (level: ${level})` : '';
|
|
622
|
+
const stageSuffix = profileStage ? `, stage: ${profileStage}` : '';
|
|
623
|
+
const stackSuffix = repo.detectedStack.length > 0 ? `, stack: [${repo.detectedStack.join(', ')}]` : '';
|
|
624
|
+
const totalSkills = allSkills.length;
|
|
625
|
+
const totalRules = allRules.length;
|
|
626
|
+
log(`Portable knowledge: ${canonicalSkills.length}/${totalSkills} skills, ${canonicalRules.length}/${totalRules} rules${levelSuffix}${stageSuffix}${stackSuffix}`);
|
|
627
|
+
}
|
|
628
|
+
// 6. Generate file contents
|
|
629
|
+
// Build workspace context for AGENTS.md enrichment (stage, focus, governance, entry count)
|
|
630
|
+
const agentsWorkspaceContext = workspaceProfile
|
|
631
|
+
? {
|
|
632
|
+
stage: workspaceProfile.stage,
|
|
633
|
+
focus: orientView?.strategicContext?.currentBet ?? undefined,
|
|
634
|
+
governanceMode: workspaceProfile.governanceMode,
|
|
635
|
+
totalEntries: workspaceProfile.totalEntries,
|
|
636
|
+
}
|
|
637
|
+
: undefined;
|
|
638
|
+
// Collect codex-targeted skills for AGENTS.md skill directory
|
|
639
|
+
// Exclude persist: 'local' rules — committed adapter files must never include local-only rules.
|
|
640
|
+
const agentsCodexSkills = canonicalSkills
|
|
641
|
+
.filter((s) => shouldEmitToTarget(s, 'codex'))
|
|
642
|
+
.map((s) => ({
|
|
643
|
+
name: s.name,
|
|
644
|
+
description: s.description,
|
|
645
|
+
triggers: s.triggers,
|
|
646
|
+
}));
|
|
647
|
+
// Collect copilot-targeted skills for copilot-instructions.md skill summaries
|
|
648
|
+
const copilotSkills = canonicalSkills
|
|
649
|
+
.filter((s) => shouldEmitToTarget(s, 'copilot'))
|
|
650
|
+
.map((s) => ({
|
|
651
|
+
name: s.name,
|
|
652
|
+
description: s.description,
|
|
653
|
+
triggers: s.triggers,
|
|
654
|
+
}));
|
|
655
|
+
// Collect copilot-targeted rules for copilot-instructions.md rule summaries
|
|
656
|
+
const copilotRules = canonicalRules
|
|
657
|
+
.filter((r) => shouldEmitToTarget(r, 'copilot'))
|
|
658
|
+
.map((r) => ({
|
|
659
|
+
name: r.name,
|
|
660
|
+
description: r.description,
|
|
661
|
+
}));
|
|
662
|
+
const copilotProfile = resolveSurfaceProfile('copilot');
|
|
663
|
+
const copilotOptions = {
|
|
664
|
+
profile: copilotProfile,
|
|
665
|
+
workspaceContext: agentsWorkspaceContext,
|
|
666
|
+
skills: copilotSkills.length > 0 ? copilotSkills : undefined,
|
|
667
|
+
rules: copilotRules.length > 0 ? copilotRules : undefined,
|
|
668
|
+
};
|
|
669
|
+
const contextContent = orientView ? generateContextMd(orientView, repo, timestamp, workspaceProfile?.stage) : null;
|
|
670
|
+
const briefingContent = generateBriefingMd(matchedEntries, repo, uniqueQueries, timestamp);
|
|
671
|
+
const agentsContent = generateAgentsMd(timestamp, {
|
|
672
|
+
workspaceContext: agentsWorkspaceContext,
|
|
673
|
+
skills: agentsCodexSkills.length > 0 ? agentsCodexSkills : undefined,
|
|
674
|
+
});
|
|
675
|
+
const claudeContent = generateClaudeMd(timestamp);
|
|
676
|
+
const cursorContent = generateCursorMdc(timestamp);
|
|
677
|
+
const copilotContent = generateCopilotMd(timestamp, copilotOptions);
|
|
678
|
+
const boundaryEnforcementMode = getBoundaryEnforcementMode(manifest);
|
|
679
|
+
const boundaryManifestContent = boundaryEnforcementMode === 'advisory'
|
|
680
|
+
? null
|
|
681
|
+
: generateBoundaryManifest(pbDir);
|
|
682
|
+
// 7. Write files
|
|
683
|
+
const filesWritten = [];
|
|
684
|
+
const filesSkipped = [];
|
|
685
|
+
const previewPlan = [];
|
|
686
|
+
// Surface filtering: skip adapter writes for targets not in the allowed set
|
|
687
|
+
const allowedTargets = options.surfaces && options.surfaces.length > 0
|
|
688
|
+
? new Set(options.surfaces)
|
|
689
|
+
: null; // null = write all
|
|
690
|
+
const writes = [
|
|
691
|
+
...(contextContent ? [{ path: join(cwd, '.productbrain', 'context.md'), relative: '.productbrain/context.md', content: contextContent, dirs: join(cwd, '.productbrain'), isAdapter: false }] : []),
|
|
692
|
+
{ path: join(cwd, '.productbrain', 'briefing.md'), relative: '.productbrain/briefing.md', content: briefingContent, isAdapter: false },
|
|
693
|
+
{ path: join(cwd, 'AGENTS.md'), relative: 'AGENTS.md', content: agentsContent, isAdapter: true, target: 'codex' },
|
|
694
|
+
{ path: join(cwd, 'CLAUDE.md'), relative: 'CLAUDE.md', content: claudeContent, isAdapter: true, target: 'claude' },
|
|
695
|
+
{ path: join(cwd, '.cursor', 'rules', 'chain.mdc'), relative: '.cursor/rules/chain.mdc', content: cursorContent, dirs: join(cwd, '.cursor', 'rules'), isAdapter: true, target: 'cursor' },
|
|
696
|
+
{ path: join(cwd, '.github', 'copilot-instructions.md'), relative: '.github/copilot-instructions.md', content: copilotContent, dirs: join(cwd, '.github'), isAdapter: true, target: 'copilot' },
|
|
697
|
+
...(boundaryManifestContent ? [{
|
|
698
|
+
path: join(cwd, '.productbrain', 'generated', 'boundaries.json'),
|
|
699
|
+
relative: '.productbrain/generated/boundaries.json',
|
|
700
|
+
content: boundaryManifestContent,
|
|
701
|
+
dirs: join(cwd, '.productbrain', 'generated'),
|
|
702
|
+
isAdapter: false,
|
|
703
|
+
}] : []),
|
|
704
|
+
];
|
|
705
|
+
// Add Cursor skill copies (filtered by target)
|
|
706
|
+
const cursorProfile = resolveSurfaceProfile('cursor');
|
|
707
|
+
for (const skill of canonicalSkills) {
|
|
708
|
+
if (!shouldEmitToTarget(skill, 'cursor'))
|
|
709
|
+
continue;
|
|
710
|
+
const dbAssetEntryId = skill.sourcePath.startsWith('db:') ? skill.sourcePath.slice(3) : undefined;
|
|
711
|
+
const skillDir = join(cwd, '.cursor', 'skills', skill.name);
|
|
712
|
+
writes.push({
|
|
713
|
+
path: join(skillDir, 'SKILL.md'),
|
|
714
|
+
relative: `.cursor/skills/${skill.name}/SKILL.md`,
|
|
715
|
+
content: generateCursorSkill(skill, cursorProfile),
|
|
716
|
+
dirs: skillDir,
|
|
717
|
+
isAdapter: true,
|
|
718
|
+
target: 'cursor',
|
|
719
|
+
dbAssetEntryId,
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
// Add Codex skill copies (projected markdown + index)
|
|
723
|
+
const codexProfile = resolveSurfaceProfile('codex');
|
|
724
|
+
const codexSkills = canonicalSkills.filter((s) => shouldEmitToTarget(s, 'codex'));
|
|
725
|
+
for (const skill of codexSkills) {
|
|
726
|
+
const dbAssetEntryId = skill.sourcePath.startsWith('db:') ? skill.sourcePath.slice(3) : undefined;
|
|
727
|
+
writes.push({
|
|
728
|
+
path: join(cwd, '.codex', 'skills', `${skill.name}.md`),
|
|
729
|
+
relative: `.codex/skills/${skill.name}.md`,
|
|
730
|
+
content: generateCodexSkill(skill, codexProfile),
|
|
731
|
+
dirs: join(cwd, '.codex', 'skills'),
|
|
732
|
+
isAdapter: true,
|
|
733
|
+
target: 'codex',
|
|
734
|
+
dbAssetEntryId,
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
writes.push({
|
|
738
|
+
path: join(cwd, '.codex', 'skills', 'README.md'),
|
|
739
|
+
relative: '.codex/skills/README.md',
|
|
740
|
+
content: generateCodexSkillIndex(codexSkills),
|
|
741
|
+
dirs: join(cwd, '.codex', 'skills'),
|
|
742
|
+
isAdapter: true,
|
|
743
|
+
target: 'codex',
|
|
744
|
+
});
|
|
745
|
+
// Validate Codex-projected skills for dead references
|
|
746
|
+
const codexWarnings = validateCodexSkills(codexSkills);
|
|
747
|
+
// Add Cursor rule copies (filtered by target)
|
|
748
|
+
for (const rule of canonicalRules) {
|
|
749
|
+
if (!shouldEmitToTarget(rule, 'cursor'))
|
|
750
|
+
continue;
|
|
751
|
+
const dbAssetEntryId = rule.sourcePath.startsWith('db:') ? rule.sourcePath.slice(3) : undefined;
|
|
752
|
+
writes.push({
|
|
753
|
+
path: join(cwd, '.cursor', 'rules', `${rule.name}.mdc`),
|
|
754
|
+
relative: `.cursor/rules/${rule.name}.mdc`,
|
|
755
|
+
content: generateCursorRule(rule, cursorProfile),
|
|
756
|
+
dirs: join(cwd, '.cursor', 'rules'),
|
|
757
|
+
isAdapter: true,
|
|
758
|
+
target: 'cursor',
|
|
759
|
+
dbAssetEntryId,
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
// Add Claude Code rule copies (filtered by target)
|
|
763
|
+
const claudeProfile = resolveSurfaceProfile('claude');
|
|
764
|
+
for (const rule of canonicalRules) {
|
|
765
|
+
if (!shouldEmitToTarget(rule, 'claude'))
|
|
766
|
+
continue;
|
|
767
|
+
const dbAssetEntryId = rule.sourcePath.startsWith('db:') ? rule.sourcePath.slice(3) : undefined;
|
|
768
|
+
writes.push({
|
|
769
|
+
path: join(cwd, '.claude', 'rules', `${rule.name}.md`),
|
|
770
|
+
relative: `.claude/rules/${rule.name}.md`,
|
|
771
|
+
content: generateClaudeRule(rule, claudeProfile),
|
|
772
|
+
dirs: join(cwd, '.claude', 'rules'),
|
|
773
|
+
isAdapter: true,
|
|
774
|
+
target: 'claude',
|
|
775
|
+
dbAssetEntryId,
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
// Add Claude Code skill router (filtered by target)
|
|
779
|
+
const claudeSkills = canonicalSkills.filter((s) => shouldEmitToTarget(s, 'claude'));
|
|
780
|
+
const skillRouterContent = generateClaudeSkillRouter(claudeSkills, claudeProfile);
|
|
781
|
+
if (skillRouterContent) {
|
|
782
|
+
writes.push({
|
|
783
|
+
path: join(cwd, '.claude', 'rules', 'skill-router.md'),
|
|
784
|
+
relative: '.claude/rules/skill-router.md',
|
|
785
|
+
content: skillRouterContent,
|
|
786
|
+
dirs: join(cwd, '.claude', 'rules'),
|
|
787
|
+
isAdapter: true,
|
|
788
|
+
target: 'claude',
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
const forkedPaths = [];
|
|
792
|
+
const projectedHashUpdates = new Map();
|
|
793
|
+
const recordProjectedHash = (entryId) => {
|
|
794
|
+
if (!applyMode || !entryId)
|
|
795
|
+
return;
|
|
796
|
+
const projection = dbProjectionHashes.get(entryId);
|
|
797
|
+
if (projection)
|
|
798
|
+
projectedHashUpdates.set(entryId, projection.hash);
|
|
799
|
+
};
|
|
800
|
+
for (const w of writes) {
|
|
801
|
+
// Surface filtering: skip adapter writes for targets not in the allowed set
|
|
802
|
+
if (allowedTargets && w.target && !allowedTargets.has(w.target)) {
|
|
803
|
+
filesSkipped.push({ path: w.relative, reason: `filtered (surface: ${w.target})` });
|
|
804
|
+
if (preview)
|
|
805
|
+
previewPlan.push({ path: w.relative, status: 'filtered' });
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
if (w.isAdapter && !shouldWriteAdapter(w.path, force)) {
|
|
809
|
+
filesSkipped.push({ path: w.relative, reason: 'exists without auto-generated marker (use --force to overwrite)' });
|
|
810
|
+
if (preview) {
|
|
811
|
+
previewPlan.push({ path: w.relative, status: 'forked' });
|
|
812
|
+
}
|
|
813
|
+
else {
|
|
814
|
+
forkedPaths.push(w.relative);
|
|
815
|
+
}
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
if (preview || dryRun) {
|
|
819
|
+
// In preview/dry-run mode: check content to distinguish new/update/unchanged
|
|
820
|
+
if (existsSync(w.path)) {
|
|
821
|
+
const current = readFileSync(w.path, 'utf8');
|
|
822
|
+
const nextNormalized = normalizeHandshakeContentForComparison(w.content);
|
|
823
|
+
const currentNormalized = normalizeHandshakeContentForComparison(current);
|
|
824
|
+
if (nextNormalized === currentNormalized) {
|
|
825
|
+
filesSkipped.push({ path: w.relative, reason: 'unchanged' });
|
|
826
|
+
if (preview)
|
|
827
|
+
previewPlan.push({ path: w.relative, status: 'unchanged' });
|
|
828
|
+
}
|
|
829
|
+
else {
|
|
830
|
+
filesWritten.push(w.relative + (dryRun ? ' (dry run)' : ''));
|
|
831
|
+
if (preview)
|
|
832
|
+
previewPlan.push({ path: w.relative, status: 'would-update' });
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
else {
|
|
836
|
+
filesWritten.push(w.relative + (dryRun ? ' (dry run)' : ''));
|
|
837
|
+
if (preview)
|
|
838
|
+
previewPlan.push({ path: w.relative, status: 'would-write' });
|
|
839
|
+
}
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
if (w.dirs)
|
|
843
|
+
mkdirSync(w.dirs, { recursive: true });
|
|
844
|
+
if (existsSync(w.path)) {
|
|
845
|
+
const current = readFileSync(w.path, 'utf8');
|
|
846
|
+
const nextNormalized = normalizeHandshakeContentForComparison(w.content);
|
|
847
|
+
const currentNormalized = normalizeHandshakeContentForComparison(current);
|
|
848
|
+
if (nextNormalized === currentNormalized) {
|
|
849
|
+
filesSkipped.push({ path: w.relative, reason: 'unchanged' });
|
|
850
|
+
recordProjectedHash(w.dbAssetEntryId);
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
writeFileSync(w.path, w.content);
|
|
855
|
+
filesWritten.push(w.relative);
|
|
856
|
+
recordProjectedHash(w.dbAssetEntryId);
|
|
857
|
+
}
|
|
858
|
+
if (projectedHashUpdates.size > 0) {
|
|
859
|
+
const updates = [...projectedHashUpdates];
|
|
860
|
+
const results = await Promise.allSettled(updates.map(([entryId, hash]) => kernelCall('setup.updateLastProjectedHash', { entryId, hash })));
|
|
861
|
+
results.forEach((result, index) => {
|
|
862
|
+
if (result.status === 'rejected') {
|
|
863
|
+
const [entryId] = updates[index];
|
|
864
|
+
trackEvent('skill.projection.failed', {
|
|
865
|
+
entryId,
|
|
866
|
+
reason: result.reason instanceof Error ? result.reason.message : String(result.reason),
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
// 8. Drift logging — if apply mode encountered forked adapters and a session is active, log a draft TEN
|
|
872
|
+
if (forkedPaths.length > 0) {
|
|
873
|
+
const session = readSession();
|
|
874
|
+
if (session) {
|
|
875
|
+
const names = forkedPaths.join(', ');
|
|
876
|
+
kernelCallWithSession('chain.createEntry', {
|
|
877
|
+
collectionSlug: 'tensions',
|
|
878
|
+
name: `TEN: handshake drift — ${forkedPaths.length} adapter(s) forked, sync blocked`,
|
|
879
|
+
status: 'draft',
|
|
880
|
+
data: {
|
|
881
|
+
description: `pb handshake --apply encountered forked adapters that blocked sync. Files: ${names}. Use --force to overwrite or resolve drift manually.`,
|
|
882
|
+
},
|
|
883
|
+
sessionId: session.sessionId,
|
|
884
|
+
createdBy: `agent:${session.sessionId}`,
|
|
885
|
+
}).catch(() => { });
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
// 8b. Setup receipt — record which assets were materialized (apply mode only)
|
|
889
|
+
// Fail-open: receipt write is advisory, never blocks the handshake.
|
|
890
|
+
if (applyMode) {
|
|
891
|
+
const session = readSession();
|
|
892
|
+
const caller = session ? kernelCallWithSession : kernelCall;
|
|
893
|
+
try {
|
|
894
|
+
const receiptResult = await caller('setup.materializeSetup', {});
|
|
895
|
+
if (receiptResult?.assetCount > 0) {
|
|
896
|
+
log(`Setup receipt: ${receiptResult.assetCount} asset(s) recorded.`);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
catch (err) {
|
|
900
|
+
trackEvent('setup.receipt.write_failed', { error: err instanceof Error ? err.message : String(err) });
|
|
901
|
+
logErr(`Warning: could not write setup receipt — ${err instanceof Error ? err.message : String(err)}`);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
// 9. Report
|
|
905
|
+
const report = {
|
|
906
|
+
filesWritten,
|
|
907
|
+
filesSkipped,
|
|
908
|
+
matchedEntries,
|
|
909
|
+
searchQueries: uniqueQueries,
|
|
910
|
+
repo,
|
|
911
|
+
codexWarnings: codexWarnings.length > 0 ? codexWarnings : undefined,
|
|
912
|
+
chainRulesStats: chainRulesStats ?? undefined,
|
|
913
|
+
chainGaps: chainGaps.length > 0 ? chainGaps : undefined,
|
|
914
|
+
adoptedCount: adoptedRulesCount,
|
|
915
|
+
rejectedCount: rejectedRulesCount,
|
|
916
|
+
personalRuleCount: personalRules.length > 0 ? personalRules.length : undefined,
|
|
917
|
+
personalSkillCount: personalSkills.length > 0 ? personalSkills.length : undefined,
|
|
918
|
+
registrySource,
|
|
919
|
+
registryStale,
|
|
920
|
+
preview: preview ? true : undefined,
|
|
921
|
+
previewPlan: preview && previewPlan.length > 0 ? previewPlan : undefined,
|
|
922
|
+
driftConflicts: forkedPaths.length > 0 ? forkedPaths : undefined,
|
|
923
|
+
};
|
|
924
|
+
if (!quiet) {
|
|
925
|
+
process.stdout.write('\n');
|
|
926
|
+
process.stdout.write(formatHandshakeReport(report) + '\n');
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
//# sourceMappingURL=handshake.js.map
|