release-skill 0.9.16 → 0.9.18
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/plugin.json +1 -1
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/.cursor-plugin/plugin.json +10 -0
- package/.kimi-plugin/plugin.json +1 -1
- package/.qoder-plugin/plugin.json +10 -0
- package/CHANGELOG.md +64 -0
- package/INSTALL.md +104 -9
- package/INSTALL.zh-CN.md +78 -7
- package/README.md +87 -32
- package/README.zh-CN.md +71 -30
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/consumer-contract-vectors.json +7 -7
- package/adapters/claude/bin/error-codes.json +1 -1
- package/adapters/claude/bin/foundation-resource-binding.json +1 -1
- package/adapters/claude/bin/prebuild-manifest.json +8 -4
- package/adapters/claude/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/claude/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/claude/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/claude/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/claude/bin/registry.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +4950 -1819
- package/adapters/claude/bin/rules.json +1 -1
- package/adapters/claude/data/hosts/cursor/host-descriptor.json +34 -0
- package/adapters/claude/data/hosts/registry.json +1 -0
- package/adapters/claude/schemas/release-plan.schema.json +255 -25
- package/adapters/claude/schemas/release-project.schema.json +228 -26
- package/adapters/claude/skills/release-finish/SKILL.md +29 -15
- package/adapters/claude/skills/release-help/SKILL.md +40 -39
- package/adapters/claude/skills/release-verify/SKILL.md +2 -2
- package/adapters/claude/src/schemas/host-descriptor.schema.json +9 -2
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/consumer-contract-vectors.json +7 -7
- package/adapters/codex/bin/error-codes.json +1 -1
- package/adapters/codex/bin/foundation-resource-binding.json +1 -1
- package/adapters/codex/bin/prebuild-manifest.json +8 -4
- package/adapters/codex/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/codex/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/codex/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/codex/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/codex/bin/registry.json +1 -1
- package/adapters/codex/bin/release-skill.bundle.mjs +4950 -1819
- package/adapters/codex/bin/rules.json +1 -1
- package/adapters/codex/data/hosts/cursor/host-descriptor.json +34 -0
- package/adapters/codex/data/hosts/registry.json +1 -0
- package/adapters/codex/schemas/release-plan.schema.json +255 -25
- package/adapters/codex/schemas/release-project.schema.json +228 -26
- package/adapters/codex/skills/release-finish/SKILL.md +29 -15
- package/adapters/codex/skills/release-help/SKILL.md +40 -39
- package/adapters/codex/skills/release-verify/SKILL.md +2 -2
- package/adapters/codex/src/schemas/host-descriptor.schema.json +9 -2
- package/adapters/cursor/.cursor-plugin/plugin.json +10 -0
- package/adapters/cursor/bin/consumer-contract-vectors.json +110 -0
- package/adapters/cursor/bin/error-codes.json +161 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-contracts/LICENSE +202 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-contracts/NOTICE +10 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-engineering-kit/LICENSE +202 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-engineering-kit/NOTICE +10 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-engineering-kit/THIRD_PARTY_NOTICES +142 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-harness-node/LICENSE +202 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-harness-node/NOTICE +10 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-harness-node/native-NOTICE +2 -0
- package/adapters/cursor/bin/foundation-resource-binding.json +1 -0
- package/adapters/cursor/bin/kernel-protocol.json +60 -0
- package/adapters/cursor/bin/license-texts/Apache-2.0.txt +201 -0
- package/adapters/cursor/bin/license-texts/MIT.txt +21 -0
- package/adapters/cursor/bin/prebuild-manifest.json +83 -0
- package/adapters/cursor/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/cursor/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/cursor/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/cursor/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/cursor/bin/registry.json +347 -0
- package/adapters/cursor/bin/release-skill-local-finish.mjs +4 -0
- package/adapters/cursor/bin/release-skill.bundle.mjs +151806 -0
- package/adapters/cursor/bin/release-skill.mjs +54 -0
- package/adapters/cursor/bin/rules.json +104 -0
- package/adapters/cursor/bin/schemas/consumer-contract-vector.schema.json +149 -0
- package/adapters/cursor/data/hosts/claude/host-descriptor.json +45 -0
- package/adapters/cursor/data/hosts/codebuddy/host-descriptor.json +26 -0
- package/adapters/cursor/data/hosts/codex/host-descriptor.json +45 -0
- package/adapters/cursor/data/hosts/cursor/host-descriptor.json +34 -0
- package/adapters/cursor/data/hosts/deepseek-harness/host-descriptor.json +23 -0
- package/adapters/cursor/data/hosts/kimi-code/host-descriptor.json +36 -0
- package/adapters/cursor/data/hosts/qoder/host-descriptor.json +26 -0
- package/adapters/cursor/data/hosts/registry.json +14 -0
- package/adapters/cursor/data/hosts/workbuddy/host-descriptor.json +34 -0
- package/adapters/cursor/data/licensing/registry.json +211 -0
- package/adapters/cursor/data/licensing/schema.json +207 -0
- package/adapters/cursor/native/safe-write/binding.gyp +41 -0
- package/adapters/cursor/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
- package/adapters/cursor/native/safe-write/prebuilds.json +24 -0
- package/adapters/cursor/native/safe-write/src/safe_write.cc +2032 -0
- package/adapters/cursor/schemas/.render-manifest.json +41 -0
- package/adapters/cursor/schemas/approval-record.schema.json +115 -0
- package/adapters/cursor/schemas/artifact-lock.schema.json +111 -0
- package/adapters/cursor/schemas/artifact-plan.schema.json +52 -0
- package/adapters/cursor/schemas/artifact-policy.schema.json +76 -0
- package/adapters/cursor/schemas/evidence-event-v2.schema.json +164 -0
- package/adapters/cursor/schemas/evidence-event.schema.json +89 -0
- package/adapters/cursor/schemas/postpublish-approval-record.schema.json +47 -0
- package/adapters/cursor/schemas/release-plan.schema.json +2226 -0
- package/adapters/cursor/schemas/release-project.schema.json +1815 -0
- package/adapters/cursor/schemas/release-run.schema.json +777 -0
- package/adapters/cursor/skills/release-assess/SKILL.md +61 -0
- package/adapters/cursor/skills/release-config/SKILL.md +151 -0
- package/adapters/cursor/skills/release-docs/SKILL.md +120 -0
- package/adapters/cursor/skills/release-finish/SKILL.md +155 -0
- package/adapters/cursor/skills/release-help/SKILL.md +155 -0
- package/adapters/cursor/skills/release-marketplace/SKILL.md +157 -0
- package/adapters/cursor/skills/release-prepare/SKILL.md +136 -0
- package/adapters/cursor/skills/release-publish/SKILL.md +92 -0
- package/adapters/cursor/skills/release-reconcile/SKILL.md +81 -0
- package/adapters/cursor/skills/release-setup/SKILL.md +132 -0
- package/adapters/cursor/skills/release-verify/SKILL.md +110 -0
- package/adapters/cursor/src/schemas/adapter-build-manifest.schema.json +41 -0
- package/adapters/cursor/src/schemas/adapter-peer-verification-request.schema.json +54 -0
- package/adapters/cursor/src/schemas/adapter-peer-verification-result.schema.json +54 -0
- package/adapters/cursor/src/schemas/adapter-source.schema.json +47 -0
- package/adapters/cursor/src/schemas/audit-baseline-pin.schema.json +163 -0
- package/adapters/cursor/src/schemas/declared-read-surface-result.schema.json +61 -0
- package/adapters/cursor/src/schemas/engineering-baseline.schema.json +77 -0
- package/adapters/cursor/src/schemas/executable-identity-observation.schema.json +162 -0
- package/adapters/cursor/src/schemas/filesystem-root-binding.schema.json +14 -0
- package/adapters/cursor/src/schemas/filesystem-tree-observation.schema.json +71 -0
- package/adapters/cursor/src/schemas/fixed-set-publication-manifest.schema.json +108 -0
- package/adapters/cursor/src/schemas/fixed-set-publication-receipt.schema.json +148 -0
- package/adapters/cursor/src/schemas/host-capability-fact.schema.json +31 -0
- package/adapters/cursor/src/schemas/host-descriptor.schema.json +144 -0
- package/adapters/cursor/src/schemas/host-operation-plan.schema.json +57 -0
- package/adapters/cursor/src/schemas/host-operation-receipt.schema.json +81 -0
- package/adapters/cursor/src/schemas/host-probe-result.schema.json +30 -0
- package/adapters/cursor/src/schemas/host-registry.schema.json +18 -0
- package/adapters/cursor/src/schemas/host-verification-request.schema.json +67 -0
- package/adapters/cursor/src/schemas/host-verification-result.schema.json +149 -0
- package/adapters/cursor/src/schemas/managed-file-lock.schema.json +77 -0
- package/adapters/cursor/src/schemas/migration-manifest.schema.json +412 -0
- package/adapters/cursor/src/schemas/observation-scope.schema.json +91 -0
- package/adapters/cursor/src/schemas/operation-request.schema.json +58 -0
- package/adapters/cursor/src/schemas/operation-result.schema.json +130 -0
- package/adapters/cursor/src/schemas/platform-difference-registry.schema.json +110 -0
- package/adapters/cursor/src/schemas/plugin-verification-request.schema.json +499 -0
- package/adapters/cursor/src/schemas/plugin-verification-result.schema.json +1217 -0
- package/adapters/cursor/src/schemas/profile-adoption-declaration.schema.json +111 -0
- package/adapters/cursor/src/schemas/profile-descriptor.schema.json +99 -0
- package/adapters/cursor/src/schemas/project-manifest.schema.json +67 -0
- package/adapters/cursor/src/schemas/project-profile.schema.json +25 -0
- package/adapters/cursor/src/schemas/public-boundary-declaration.schema.json +73 -0
- package/adapters/cursor/src/schemas/report-binding.schema.json +42 -0
- package/adapters/cursor/src/schemas/report-model.schema.json +308 -0
- package/adapters/cursor/src/schemas/skill-family-directory-verification-request.schema.json +100 -0
- package/adapters/cursor/src/schemas/skill-family-directory-verification-result.schema.json +193 -0
- package/adapters/cursor/src/schemas/source-authority-receipt.schema.json +31 -0
- package/adapters/cursor/src/schemas/state-event-envelope.schema.json +38 -0
- package/adapters/cursor/src/schemas/state-snapshot-metadata.schema.json +25 -0
- package/adapters/cursor/src/schemas/structured-scan-policy.schema.json +97 -0
- package/adapters/cursor/src/schemas/surface-scan-policy.schema.json +42 -0
- package/adapters/cursor/src/schemas/timeout-policy.schema.json +24 -0
- package/adapters/cursor/src/schemas/token-estimate-record.schema.json +75 -0
- package/adapters/cursor/src/schemas/token-estimate-result.schema.json +43 -0
- package/adapters/cursor/src/schemas/watchdog-termination-envelope.schema.json +156 -0
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/consumer-contract-vectors.json +7 -7
- package/adapters/kimi/bin/error-codes.json +1 -1
- package/adapters/kimi/bin/foundation-resource-binding.json +1 -1
- package/adapters/kimi/bin/prebuild-manifest.json +8 -4
- package/adapters/kimi/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/kimi/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/kimi/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/kimi/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/kimi/bin/registry.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +4950 -1819
- package/adapters/kimi/bin/rules.json +1 -1
- package/adapters/kimi/data/hosts/cursor/host-descriptor.json +34 -0
- package/adapters/kimi/data/hosts/registry.json +1 -0
- package/adapters/kimi/schemas/release-plan.schema.json +255 -25
- package/adapters/kimi/schemas/release-project.schema.json +228 -26
- package/adapters/kimi/skills/release-finish/SKILL.md +29 -15
- package/adapters/kimi/skills/release-help/SKILL.md +40 -39
- package/adapters/kimi/skills/release-verify/SKILL.md +2 -2
- package/adapters/kimi/src/schemas/host-descriptor.schema.json +9 -2
- package/adapters/qoder/.qoder-plugin/plugin.json +10 -0
- package/adapters/qoder/bin/consumer-contract-vectors.json +110 -0
- package/adapters/qoder/bin/error-codes.json +161 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-contracts/LICENSE +202 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-contracts/NOTICE +10 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-engineering-kit/LICENSE +202 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-engineering-kit/NOTICE +10 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-engineering-kit/THIRD_PARTY_NOTICES +142 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-harness-node/LICENSE +202 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-harness-node/NOTICE +10 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-harness-node/native-NOTICE +2 -0
- package/adapters/qoder/bin/foundation-resource-binding.json +1 -0
- package/adapters/qoder/bin/kernel-protocol.json +60 -0
- package/adapters/qoder/bin/license-texts/Apache-2.0.txt +201 -0
- package/adapters/qoder/bin/license-texts/MIT.txt +21 -0
- package/adapters/qoder/bin/prebuild-manifest.json +83 -0
- package/adapters/qoder/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/qoder/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/qoder/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/qoder/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/qoder/bin/registry.json +347 -0
- package/adapters/qoder/bin/release-skill-local-finish.mjs +4 -0
- package/adapters/qoder/bin/release-skill.bundle.mjs +151806 -0
- package/adapters/qoder/bin/release-skill.mjs +54 -0
- package/adapters/qoder/bin/rules.json +104 -0
- package/adapters/qoder/bin/schemas/consumer-contract-vector.schema.json +149 -0
- package/adapters/qoder/data/hosts/claude/host-descriptor.json +45 -0
- package/adapters/qoder/data/hosts/codebuddy/host-descriptor.json +26 -0
- package/adapters/qoder/data/hosts/codex/host-descriptor.json +45 -0
- package/adapters/qoder/data/hosts/cursor/host-descriptor.json +34 -0
- package/adapters/qoder/data/hosts/deepseek-harness/host-descriptor.json +23 -0
- package/adapters/qoder/data/hosts/kimi-code/host-descriptor.json +36 -0
- package/adapters/qoder/data/hosts/qoder/host-descriptor.json +26 -0
- package/adapters/qoder/data/hosts/registry.json +14 -0
- package/adapters/qoder/data/hosts/workbuddy/host-descriptor.json +34 -0
- package/adapters/qoder/data/licensing/registry.json +211 -0
- package/adapters/qoder/data/licensing/schema.json +207 -0
- package/adapters/qoder/native/safe-write/binding.gyp +41 -0
- package/adapters/qoder/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
- package/adapters/qoder/native/safe-write/prebuilds.json +24 -0
- package/adapters/qoder/native/safe-write/src/safe_write.cc +2032 -0
- package/adapters/qoder/schemas/.render-manifest.json +41 -0
- package/adapters/qoder/schemas/approval-record.schema.json +115 -0
- package/adapters/qoder/schemas/artifact-lock.schema.json +111 -0
- package/adapters/qoder/schemas/artifact-plan.schema.json +52 -0
- package/adapters/qoder/schemas/artifact-policy.schema.json +76 -0
- package/adapters/qoder/schemas/evidence-event-v2.schema.json +164 -0
- package/adapters/qoder/schemas/evidence-event.schema.json +89 -0
- package/adapters/qoder/schemas/postpublish-approval-record.schema.json +47 -0
- package/adapters/qoder/schemas/release-plan.schema.json +2226 -0
- package/adapters/qoder/schemas/release-project.schema.json +1815 -0
- package/adapters/qoder/schemas/release-run.schema.json +777 -0
- package/adapters/qoder/skills/release-assess/SKILL.md +60 -0
- package/adapters/qoder/skills/release-config/SKILL.md +150 -0
- package/adapters/qoder/skills/release-docs/SKILL.md +119 -0
- package/adapters/qoder/skills/release-finish/SKILL.md +154 -0
- package/adapters/qoder/skills/release-help/SKILL.md +154 -0
- package/adapters/qoder/skills/release-marketplace/SKILL.md +156 -0
- package/adapters/qoder/skills/release-prepare/SKILL.md +135 -0
- package/adapters/qoder/skills/release-publish/SKILL.md +91 -0
- package/adapters/qoder/skills/release-reconcile/SKILL.md +80 -0
- package/adapters/qoder/skills/release-setup/SKILL.md +131 -0
- package/adapters/qoder/skills/release-verify/SKILL.md +109 -0
- package/adapters/qoder/src/schemas/adapter-build-manifest.schema.json +41 -0
- package/adapters/qoder/src/schemas/adapter-peer-verification-request.schema.json +54 -0
- package/adapters/qoder/src/schemas/adapter-peer-verification-result.schema.json +54 -0
- package/adapters/qoder/src/schemas/adapter-source.schema.json +47 -0
- package/adapters/qoder/src/schemas/audit-baseline-pin.schema.json +163 -0
- package/adapters/qoder/src/schemas/declared-read-surface-result.schema.json +61 -0
- package/adapters/qoder/src/schemas/engineering-baseline.schema.json +77 -0
- package/adapters/qoder/src/schemas/executable-identity-observation.schema.json +162 -0
- package/adapters/qoder/src/schemas/filesystem-root-binding.schema.json +14 -0
- package/adapters/qoder/src/schemas/filesystem-tree-observation.schema.json +71 -0
- package/adapters/qoder/src/schemas/fixed-set-publication-manifest.schema.json +108 -0
- package/adapters/qoder/src/schemas/fixed-set-publication-receipt.schema.json +148 -0
- package/adapters/qoder/src/schemas/host-capability-fact.schema.json +31 -0
- package/adapters/qoder/src/schemas/host-descriptor.schema.json +144 -0
- package/adapters/qoder/src/schemas/host-operation-plan.schema.json +57 -0
- package/adapters/qoder/src/schemas/host-operation-receipt.schema.json +81 -0
- package/adapters/qoder/src/schemas/host-probe-result.schema.json +30 -0
- package/adapters/qoder/src/schemas/host-registry.schema.json +18 -0
- package/adapters/qoder/src/schemas/host-verification-request.schema.json +67 -0
- package/adapters/qoder/src/schemas/host-verification-result.schema.json +149 -0
- package/adapters/qoder/src/schemas/managed-file-lock.schema.json +77 -0
- package/adapters/qoder/src/schemas/migration-manifest.schema.json +412 -0
- package/adapters/qoder/src/schemas/observation-scope.schema.json +91 -0
- package/adapters/qoder/src/schemas/operation-request.schema.json +58 -0
- package/adapters/qoder/src/schemas/operation-result.schema.json +130 -0
- package/adapters/qoder/src/schemas/platform-difference-registry.schema.json +110 -0
- package/adapters/qoder/src/schemas/plugin-verification-request.schema.json +499 -0
- package/adapters/qoder/src/schemas/plugin-verification-result.schema.json +1217 -0
- package/adapters/qoder/src/schemas/profile-adoption-declaration.schema.json +111 -0
- package/adapters/qoder/src/schemas/profile-descriptor.schema.json +99 -0
- package/adapters/qoder/src/schemas/project-manifest.schema.json +67 -0
- package/adapters/qoder/src/schemas/project-profile.schema.json +25 -0
- package/adapters/qoder/src/schemas/public-boundary-declaration.schema.json +73 -0
- package/adapters/qoder/src/schemas/report-binding.schema.json +42 -0
- package/adapters/qoder/src/schemas/report-model.schema.json +308 -0
- package/adapters/qoder/src/schemas/skill-family-directory-verification-request.schema.json +100 -0
- package/adapters/qoder/src/schemas/skill-family-directory-verification-result.schema.json +193 -0
- package/adapters/qoder/src/schemas/source-authority-receipt.schema.json +31 -0
- package/adapters/qoder/src/schemas/state-event-envelope.schema.json +38 -0
- package/adapters/qoder/src/schemas/state-snapshot-metadata.schema.json +25 -0
- package/adapters/qoder/src/schemas/structured-scan-policy.schema.json +97 -0
- package/adapters/qoder/src/schemas/surface-scan-policy.schema.json +42 -0
- package/adapters/qoder/src/schemas/timeout-policy.schema.json +24 -0
- package/adapters/qoder/src/schemas/token-estimate-record.schema.json +75 -0
- package/adapters/qoder/src/schemas/token-estimate-result.schema.json +43 -0
- package/adapters/qoder/src/schemas/watchdog-termination-envelope.schema.json +156 -0
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/consumer-contract-vectors.json +7 -7
- package/adapters/workbuddy/bin/error-codes.json +1 -1
- package/adapters/workbuddy/bin/foundation-resource-binding.json +1 -1
- package/adapters/workbuddy/bin/prebuild-manifest.json +8 -4
- package/adapters/workbuddy/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/workbuddy/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/workbuddy/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/workbuddy/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/workbuddy/bin/registry.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +4950 -1819
- package/adapters/workbuddy/bin/rules.json +1 -1
- package/adapters/workbuddy/data/hosts/cursor/host-descriptor.json +34 -0
- package/adapters/workbuddy/data/hosts/registry.json +1 -0
- package/adapters/workbuddy/schemas/release-plan.schema.json +255 -25
- package/adapters/workbuddy/schemas/release-project.schema.json +228 -26
- package/adapters/workbuddy/skills/release-finish/SKILL.md +29 -15
- package/adapters/workbuddy/skills/release-help/SKILL.md +40 -39
- package/adapters/workbuddy/skills/release-verify/SKILL.md +2 -2
- package/adapters/workbuddy/src/schemas/host-descriptor.schema.json +9 -2
- package/bin/consumer-contract-vectors.json +7 -7
- package/bin/error-codes.json +1 -1
- package/bin/foundation-resource-binding.json +1 -1
- package/bin/prebuild-manifest.json +8 -4
- package/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/bin/registry.json +1 -1
- package/bin/release-skill-cli.mjs +148 -6
- package/bin/release-skill.bundle.mjs +4950 -1819
- package/bin/rules.json +1 -1
- package/data/hosts/cursor/host-descriptor.json +34 -0
- package/data/hosts/registry.json +1 -0
- package/package.json +9 -5
- package/platform-manifest.json +140 -8
- package/references/02-project-config.md +9 -2
- package/schemas/release-plan.schema.json +255 -25
- package/schemas/release-project.schema.json +228 -26
- package/scripts/sync-public-files.mjs +3 -22
- package/skills/release-finish/SKILL.md +29 -15
- package/skills/release-help/SKILL.md +40 -39
- package/skills/release-verify/SKILL.md +2 -2
- package/skills-src/release-finish/SKILL.md +29 -15
- package/skills-src/release-help/SKILL.md +40 -39
- package/skills-src/release-verify/SKILL.md +2 -2
- package/src/adapters/contract.mjs +1 -0
- package/src/adapters/plugin-marketplace.mjs +201 -9
- package/src/commands/post-release-local.mjs +618 -53
- package/src/commands/prepare.mjs +74 -9
- package/src/commands/publish.mjs +4 -1
- package/src/commands/ship.mjs +2 -0
- package/src/commands/verify-records.mjs +467 -0
- package/src/commands/verify.mjs +73 -0
- package/src/core/checkpoints.mjs +5 -2
- package/src/core/config.mjs +1 -1
- package/src/core/foundation-host-verification.mjs +168 -0
- package/src/core/plan.mjs +19 -1
- package/src/core/postpublish.mjs +9 -1
- package/src/core/run-retention.mjs +1 -1
- package/src/core/run.mjs +1 -1
- package/src/core/surface-host-bindings.mjs +1 -0
- package/src/core/verification-gates.mjs +1 -1
- package/src/platforms/registry.mjs +225 -16
- package/src/producers/build-adapters.mjs +43 -25
- package/src/producers/foundation-resource-projection.mjs +3 -3
- package/src/schemas/host-descriptor.schema.json +9 -2
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import { access, readFile } from 'node:fs/promises';
|
|
1
|
+
import { access, readFile, mkdir, mkdtemp, cp, rename, rm, lstat, realpath, chmod } from 'node:fs/promises';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
-
import { join, relative } from 'node:path';
|
|
3
|
+
import { join, relative, isAbsolute, normalize } from 'node:path';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
readFileContained,
|
|
7
|
+
readFileStrict,
|
|
7
8
|
resolveContained,
|
|
8
9
|
superviseProcess,
|
|
9
10
|
withTemporaryWorkspace,
|
|
11
|
+
createTemporaryWorkspace,
|
|
12
|
+
createFilesystemRootBinding,
|
|
13
|
+
observeFilesystemTree,
|
|
14
|
+
createFixedSetPublicationManifest,
|
|
15
|
+
publishFixedSet,
|
|
16
|
+
replaceFixedSetAtomic,
|
|
10
17
|
} from 'skill-family-harness-node';
|
|
11
18
|
|
|
12
19
|
import { getPlatform } from '../platforms/registry.mjs';
|
|
@@ -14,6 +21,7 @@ import { resolveCodeBuddyMarketplace } from '../platforms/codebuddy.mjs';
|
|
|
14
21
|
import { verifyInstalledMarketplacePayload } from '../adapters/plugin-marketplace.mjs';
|
|
15
22
|
import { normalizePostPublishView, postPublishActionId } from '../core/postpublish.mjs';
|
|
16
23
|
import { loadRun, validateRunLineage } from '../core/run.mjs';
|
|
24
|
+
import { verifyFrozenSnapshot } from '../snapshot/frozen.mjs';
|
|
17
25
|
|
|
18
26
|
const HOSTS_BY_ACTION = Object.freeze({
|
|
19
27
|
'claude-marketplace-install': ['claude'],
|
|
@@ -40,6 +48,9 @@ const SAFE_ENV_KEYS = Object.freeze([
|
|
|
40
48
|
]);
|
|
41
49
|
const CODEBUDDY_PLUGIN_LIST_ARGS = Object.freeze(['plugin', 'list', '--json']);
|
|
42
50
|
const CODEBUDDY_MARKETPLACE_LIST_ARGS = Object.freeze(['plugin', 'marketplace', 'list']);
|
|
51
|
+
const QODER_PLUGIN_LIST_ARGS = Object.freeze(['plugins', 'list', '--json']);
|
|
52
|
+
const QODER_MARKETPLACE_LIST_ARGS = Object.freeze(['plugins', 'marketplace', 'list', '--json']);
|
|
53
|
+
const QODER_PAYLOAD_CONTRACT = 'external-marketplace-v1';
|
|
43
54
|
|
|
44
55
|
function attachFoundationFailure(error, { envelope, stdout }) {
|
|
45
56
|
Object.defineProperties(error, {
|
|
@@ -172,17 +183,49 @@ function hubTargets(plan) {
|
|
|
172
183
|
codebuddy: 'Handle manually; CodeBuddy cannot pin a Hub ref in this flow.',
|
|
173
184
|
workbuddy: 'Handle manually; WorkBuddy cannot pin a Hub ref in this flow (it follows the CodeBuddy manual boundary).',
|
|
174
185
|
};
|
|
175
|
-
return local.hosts.map((host) =>
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
+
return local.hosts.map((host) => {
|
|
187
|
+
if (host === 'cursor') {
|
|
188
|
+
return {
|
|
189
|
+
targetKind: 'cursor-local', executionMode: 'executable',
|
|
190
|
+
unitId: declaration.unitId, host, plugin: local.plugin,
|
|
191
|
+
version: unit?.targetVersion,
|
|
192
|
+
snapshotPath: unit?.frozenSnapshot?.path,
|
|
193
|
+
manifestDigest: unit?.frozenSnapshot?.manifestDigest,
|
|
194
|
+
cursor: local.cursor, timeoutMs: 300_000,
|
|
195
|
+
message: `Install or replace the complete frozen ${local.plugin} Cursor Local plugin; quit Cursor first and restart it afterwards.`,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (host === 'qoder') {
|
|
199
|
+
return {
|
|
200
|
+
targetKind: 'hub-backed',
|
|
201
|
+
executionMode: 'executable',
|
|
202
|
+
unitId: declaration.unitId,
|
|
203
|
+
host,
|
|
204
|
+
plugin: local.plugin,
|
|
205
|
+
marketplace: hub.name,
|
|
206
|
+
hub,
|
|
207
|
+
version: unit?.targetVersion,
|
|
208
|
+
pluginRepo: unit?.publicRepo,
|
|
209
|
+
pluginCommit: unit?.frozenSnapshot?.commit,
|
|
210
|
+
snapshotPath: unit?.frozenSnapshot?.path,
|
|
211
|
+
manifestDigest: unit?.frozenSnapshot?.manifestDigest,
|
|
212
|
+
timeoutMs: 300_000,
|
|
213
|
+
message: `Update ${local.plugin} from Qoder Hub ${hub.name}; a new session or /plugins reload is required before treating the updated plugin as loaded.`,
|
|
214
|
+
...(tag ? { frozenTag: tag } : {}),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
targetKind: 'hub-backed',
|
|
219
|
+
executionMode: 'manual',
|
|
220
|
+
unitId: declaration.unitId,
|
|
221
|
+
host,
|
|
222
|
+
plugin: local.plugin,
|
|
223
|
+
hub,
|
|
224
|
+
message: `${manualInstruction[host]} Install or upgrade ${local.plugin} from Hub ${hub.name}; release-skill does not execute or probe this action.`,
|
|
225
|
+
...(unit?.publicRepo ? { publicRepo: unit.publicRepo } : {}),
|
|
226
|
+
...(tag ? { frozenTag: tag } : {}),
|
|
227
|
+
};
|
|
228
|
+
});
|
|
186
229
|
});
|
|
187
230
|
}
|
|
188
231
|
|
|
@@ -240,6 +283,23 @@ function assertExecutableTarget(target) {
|
|
|
240
283
|
}
|
|
241
284
|
}
|
|
242
285
|
|
|
286
|
+
function assertQoderExecutableTarget(target) {
|
|
287
|
+
for (const field of [
|
|
288
|
+
'unitId', 'plugin', 'marketplace', 'version', 'pluginRepo',
|
|
289
|
+
'pluginCommit', 'snapshotPath', 'manifestDigest',
|
|
290
|
+
]) {
|
|
291
|
+
if (typeof target[field] !== 'string' || target[field].length === 0) {
|
|
292
|
+
throw new Error(`Qoder local host update target is missing ${field}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (!/^[a-f0-9]{40}$/u.test(target.pluginCommit)) {
|
|
296
|
+
throw new Error(`Qoder local host update requires the frozen public commit for unit ${target.unitId}`);
|
|
297
|
+
}
|
|
298
|
+
if (!/^[a-f0-9]{64}$/u.test(target.manifestDigest)) {
|
|
299
|
+
throw new Error(`Qoder local host update requires the frozen snapshot digest for unit ${target.unitId}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
243
303
|
function buildShipNextStep({ root, statePath, unitIds }) {
|
|
244
304
|
const argv = ['release-skill', 'ship'];
|
|
245
305
|
if (typeof root === 'string' && root.length > 0) argv.push('--root', root);
|
|
@@ -268,8 +328,12 @@ export function derivePostReleaseChecklist(plan, {
|
|
|
268
328
|
const uncovered = units.filter((unit) => (
|
|
269
329
|
!BRANCH_ACTION_INCLUDED.has(unit.productionConfig?.branchStrategy)
|
|
270
330
|
));
|
|
271
|
-
const
|
|
272
|
-
const
|
|
331
|
+
const declaredHubTargets = hubTargets(plan);
|
|
332
|
+
const executableTargets = [
|
|
333
|
+
...pluginTargets(plan),
|
|
334
|
+
...declaredHubTargets.filter((target) => target.executionMode === 'executable'),
|
|
335
|
+
];
|
|
336
|
+
const manualTargets = declaredHubTargets.filter((target) => target.executionMode === 'manual');
|
|
273
337
|
const targets = mergePostReleaseTargets(executableTargets, manualTargets);
|
|
274
338
|
const hasPendingPostVerify = postVerifyHooks(plan).length > 0 && !postVerifyComplete && targets.length > 0;
|
|
275
339
|
const hasStatePath = typeof statePath === 'string' && statePath.length > 0;
|
|
@@ -717,6 +781,215 @@ function normalizeGitSource(source) {
|
|
|
717
781
|
.replace(/\/$/u, '');
|
|
718
782
|
}
|
|
719
783
|
|
|
784
|
+
function normalizeHubGitSource(source, githubHost) {
|
|
785
|
+
return String(source ?? '')
|
|
786
|
+
.replace(/^git\+/, '')
|
|
787
|
+
.replace(new RegExp(`^https://${String(githubHost).replaceAll('.', '\\.').replaceAll('-', '\\-')}/`), '')
|
|
788
|
+
.replace(new RegExp(`^git@${String(githubHost).replaceAll('.', '\\.').replaceAll('-', '\\-')}:`), '')
|
|
789
|
+
.replace(/\.git$/u, '')
|
|
790
|
+
.replace(/\/$/u, '');
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
async function observeQoderMarketplace(target, command, env, run) {
|
|
794
|
+
const listed = await run(command, [...QODER_MARKETPLACE_LIST_ARGS], {
|
|
795
|
+
env,
|
|
796
|
+
timeout: target.timeoutMs,
|
|
797
|
+
});
|
|
798
|
+
const parsed = parseJson(listed.stdout, 'qoder marketplace list');
|
|
799
|
+
if (!Array.isArray(parsed)) throw new Error('qoder marketplace list did not return an array');
|
|
800
|
+
const matches = parsed.filter((entry) => entry?.name === target.marketplace);
|
|
801
|
+
if (matches.length === 0) return { installed: false };
|
|
802
|
+
if (matches.length !== 1) {
|
|
803
|
+
throw new Error(`qoder marketplace list returned conflicting entries for ${target.marketplace}`);
|
|
804
|
+
}
|
|
805
|
+
const [found] = matches;
|
|
806
|
+
if (
|
|
807
|
+
found.source?.source !== 'git'
|
|
808
|
+
|| normalizeHubGitSource(found.source.url, target.hub.githubHost) !== target.hub.repo
|
|
809
|
+
) {
|
|
810
|
+
throw new Error(`qoder marketplace ${target.marketplace} does not point to ${target.hub.repo}`);
|
|
811
|
+
}
|
|
812
|
+
if (typeof found.installLocation !== 'string' || found.installLocation.length === 0) {
|
|
813
|
+
throw new Error(`qoder marketplace ${target.marketplace} has no observable checkout root`);
|
|
814
|
+
}
|
|
815
|
+
const [remote, branch] = await Promise.all([
|
|
816
|
+
run('git', ['-C', found.installLocation, 'remote', 'get-url', 'origin'], {
|
|
817
|
+
env,
|
|
818
|
+
timeout: 30_000,
|
|
819
|
+
}),
|
|
820
|
+
run('git', ['-C', found.installLocation, 'symbolic-ref', '-q', 'HEAD'], {
|
|
821
|
+
env,
|
|
822
|
+
timeout: 30_000,
|
|
823
|
+
}),
|
|
824
|
+
]);
|
|
825
|
+
if (
|
|
826
|
+
normalizeHubGitSource(remote.stdout.trim(), target.hub.githubHost) !== target.hub.repo
|
|
827
|
+
|| branch.stdout.trim() !== target.hub.ref
|
|
828
|
+
) {
|
|
829
|
+
throw new Error(`qoder marketplace ${target.marketplace} checkout does not match the frozen Hub source`);
|
|
830
|
+
}
|
|
831
|
+
return { installed: true, root: found.installLocation, found };
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
async function observeQoderPlugin(target, command, env, run) {
|
|
835
|
+
const listed = await run(command, [...QODER_PLUGIN_LIST_ARGS], {
|
|
836
|
+
env,
|
|
837
|
+
timeout: target.timeoutMs,
|
|
838
|
+
});
|
|
839
|
+
const parsed = parseJson(listed.stdout, 'qoder plugin list');
|
|
840
|
+
if (!Array.isArray(parsed)) throw new Error('qoder plugin list did not return an array');
|
|
841
|
+
const selector = `${target.plugin}@${target.marketplace}`;
|
|
842
|
+
const matches = parsed.filter((entry) => entry?.id === selector);
|
|
843
|
+
if (matches.length === 0) return { installed: false };
|
|
844
|
+
if (matches.length !== 1) {
|
|
845
|
+
throw new Error(`qoder plugin list returned conflicting entries for ${selector}`);
|
|
846
|
+
}
|
|
847
|
+
const [found] = matches;
|
|
848
|
+
if (
|
|
849
|
+
found.name !== target.plugin
|
|
850
|
+
|| found.source !== selector
|
|
851
|
+
|| found.scope !== 'user'
|
|
852
|
+
|| typeof found.installPath !== 'string'
|
|
853
|
+
|| found.installPath.length === 0
|
|
854
|
+
) {
|
|
855
|
+
throw new Error(`qoder plugin ${selector} does not match its frozen user-scope identity`);
|
|
856
|
+
}
|
|
857
|
+
return {
|
|
858
|
+
installed: true,
|
|
859
|
+
exact: found.version === target.version,
|
|
860
|
+
installPath: found.installPath,
|
|
861
|
+
found,
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
async function readQoderManifest(root, label) {
|
|
866
|
+
const manifest = parseJson(
|
|
867
|
+
await readFileContained(root, '.qoder-plugin/plugin.json', { encoding: 'utf8' }),
|
|
868
|
+
label,
|
|
869
|
+
);
|
|
870
|
+
if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) {
|
|
871
|
+
throw new Error(`${label} did not return an object`);
|
|
872
|
+
}
|
|
873
|
+
return manifest;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
function assertQoderPluginManifest(target, manifest, label) {
|
|
877
|
+
if (
|
|
878
|
+
manifest.name !== target.plugin
|
|
879
|
+
|| manifest.version !== target.version
|
|
880
|
+
|| manifest.skills !== './adapters/qoder/skills/'
|
|
881
|
+
) {
|
|
882
|
+
throw new Error(`${label} does not match the frozen Qoder plugin identity and projection path`);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
async function assertFrozenQoderManifest(target, root) {
|
|
887
|
+
const { snapshotDir: snapshotRoot } = await verifyFrozenSnapshot({
|
|
888
|
+
root,
|
|
889
|
+
snapshotPath: target.snapshotPath,
|
|
890
|
+
expectedDigest: target.manifestDigest,
|
|
891
|
+
});
|
|
892
|
+
const manifest = await readQoderManifest(snapshotRoot, 'frozen Qoder plugin manifest');
|
|
893
|
+
assertQoderPluginManifest(target, manifest, 'frozen Qoder plugin manifest');
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
async function assertQoderHubEntry(target, marketplaceRoot) {
|
|
897
|
+
const marketplace = parseJson(
|
|
898
|
+
await readFileContained(marketplaceRoot, 'marketplace.json', { encoding: 'utf8' }),
|
|
899
|
+
'Qoder Hub marketplace manifest',
|
|
900
|
+
);
|
|
901
|
+
if (marketplace?.name !== target.marketplace || !Array.isArray(marketplace.plugins)) {
|
|
902
|
+
throw new Error('Qoder Hub marketplace manifest does not match the frozen marketplace');
|
|
903
|
+
}
|
|
904
|
+
const matches = marketplace.plugins.filter((entry) => entry?.name === target.plugin);
|
|
905
|
+
if (matches.length !== 1) {
|
|
906
|
+
throw new Error(`Qoder Hub marketplace manifest must contain exactly one ${target.plugin} entry`);
|
|
907
|
+
}
|
|
908
|
+
const source = matches[0]?.source;
|
|
909
|
+
if (
|
|
910
|
+
source?.source !== 'url'
|
|
911
|
+
|| normalizeHubGitSource(source.url, target.hub.githubHost) !== target.pluginRepo
|
|
912
|
+
|| source.sha !== target.pluginCommit
|
|
913
|
+
) {
|
|
914
|
+
throw new Error('Qoder Hub entry does not match the frozen public source identity');
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
async function verifyQoderInstalledPayload({
|
|
919
|
+
target,
|
|
920
|
+
root,
|
|
921
|
+
installPath,
|
|
922
|
+
verifyInstalledPayload,
|
|
923
|
+
}) {
|
|
924
|
+
const manifest = await readQoderManifest(installPath, 'installed Qoder plugin manifest');
|
|
925
|
+
assertQoderPluginManifest(target, manifest, 'installed Qoder plugin manifest');
|
|
926
|
+
await verifyInstalledPayload({
|
|
927
|
+
snapshotPath: target.snapshotPath,
|
|
928
|
+
manifestDigest: target.manifestDigest,
|
|
929
|
+
payloadContract: QODER_PAYLOAD_CONTRACT,
|
|
930
|
+
marketplaceLocation: 'external',
|
|
931
|
+
}, { root }, installPath, 'qoder');
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
async function runQoderUpdate(target, detected, run, {
|
|
935
|
+
root,
|
|
936
|
+
verifyInstalledPayload,
|
|
937
|
+
}) {
|
|
938
|
+
const env = hostEnvironment('qoder');
|
|
939
|
+
await assertFrozenQoderManifest(target, root);
|
|
940
|
+
const marketplace = await observeQoderMarketplace(target, detected.command, env, run);
|
|
941
|
+
if (!marketplace.installed) {
|
|
942
|
+
return {
|
|
943
|
+
status: 'MANUAL_REQUIRED',
|
|
944
|
+
reason: `qoder marketplace ${target.marketplace} is not installed; no marketplace was added`,
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
const before = await observeQoderPlugin(target, detected.command, env, run);
|
|
948
|
+
if (!before.installed) {
|
|
949
|
+
return {
|
|
950
|
+
status: 'MANUAL_REQUIRED',
|
|
951
|
+
reason: 'qoder target plugin is not installed; no initial installation was performed',
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
if (before.exact) {
|
|
955
|
+
await assertQoderHubEntry(target, marketplace.root);
|
|
956
|
+
await verifyQoderInstalledPayload({
|
|
957
|
+
target,
|
|
958
|
+
root,
|
|
959
|
+
installPath: before.installPath,
|
|
960
|
+
verifyInstalledPayload,
|
|
961
|
+
});
|
|
962
|
+
return { status: 'ALREADY_CURRENT', version: target.version };
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
await run(detected.command, ['plugins', 'marketplace', 'update', target.marketplace], {
|
|
966
|
+
env,
|
|
967
|
+
timeout: target.timeoutMs,
|
|
968
|
+
});
|
|
969
|
+
const refreshed = await observeQoderMarketplace(target, detected.command, env, run);
|
|
970
|
+
if (!refreshed.installed) {
|
|
971
|
+
throw new Error(`qoder marketplace ${target.marketplace} disappeared after refresh`);
|
|
972
|
+
}
|
|
973
|
+
await assertQoderHubEntry(target, refreshed.root);
|
|
974
|
+
await run(detected.command, [
|
|
975
|
+
'plugins', 'update', `${target.plugin}@${target.marketplace}`, '--scope', 'user',
|
|
976
|
+
], { env, timeout: target.timeoutMs });
|
|
977
|
+
const after = await observeQoderPlugin(target, detected.command, env, run);
|
|
978
|
+
if (!after.exact) throw new Error('qoder did not update to the frozen plugin version');
|
|
979
|
+
await verifyQoderInstalledPayload({
|
|
980
|
+
target,
|
|
981
|
+
root,
|
|
982
|
+
installPath: after.installPath,
|
|
983
|
+
verifyInstalledPayload,
|
|
984
|
+
});
|
|
985
|
+
return {
|
|
986
|
+
status: 'UPDATED',
|
|
987
|
+
version: target.version,
|
|
988
|
+
restartRequired: true,
|
|
989
|
+
reloadInstruction: 'Start a new Qoder session or run /plugins reload before checking the loaded version.',
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
|
|
720
993
|
function parseFrozenRemoteRef(target, stdout) {
|
|
721
994
|
const directRefs = new Map();
|
|
722
995
|
const peeledRefs = new Map();
|
|
@@ -1046,14 +1319,14 @@ expect {
|
|
|
1046
1319
|
eof { failEof remove-confirmation 126 }
|
|
1047
1320
|
}
|
|
1048
1321
|
expect {
|
|
1049
|
-
-nocase -re {Trust this folder\\?} {
|
|
1322
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1050
1323
|
-re $promptPattern {}
|
|
1051
1324
|
timeout { failTimeout remove-prompt 128 130 }
|
|
1052
1325
|
eof { failEof remove-prompt 129 }
|
|
1053
1326
|
}
|
|
1054
1327
|
}
|
|
1055
1328
|
-re $promptPattern {}
|
|
1056
|
-
-nocase -re {Trust this folder\\?} {
|
|
1329
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1057
1330
|
timeout { failTimeout remove-dialog 122 124 }
|
|
1058
1331
|
eof { failEof remove-dialog 123 }
|
|
1059
1332
|
}
|
|
@@ -1128,9 +1401,133 @@ proc failEof {state code} {
|
|
|
1128
1401
|
exit $code
|
|
1129
1402
|
}
|
|
1130
1403
|
|
|
1131
|
-
proc
|
|
1132
|
-
puts stderr "KIMI_TUI_STATE:directory-trust:
|
|
1133
|
-
exit
|
|
1404
|
+
proc unexpectedDirectoryTrust {} {
|
|
1405
|
+
puts stderr "KIMI_TUI_STATE:directory-trust:unexpected"
|
|
1406
|
+
exit 147
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
proc readDirectoryTrustDialog {prefix state timeoutCode unknownCode eofCode} {
|
|
1410
|
+
global expect_out
|
|
1411
|
+
set dialogBuffer $prefix
|
|
1412
|
+
set cleanedPrefix [cleanScreen $dialogBuffer]
|
|
1413
|
+
if {![regexp -nocase {Trust this folder\\?} $cleanedPrefix]} {
|
|
1414
|
+
expect {
|
|
1415
|
+
-nocase -re {Trust this folder\\?} {
|
|
1416
|
+
append dialogBuffer $expect_out(buffer)
|
|
1417
|
+
}
|
|
1418
|
+
timeout { failTimeout $state $timeoutCode $unknownCode }
|
|
1419
|
+
eof { failEof $state $eofCode }
|
|
1420
|
+
}
|
|
1421
|
+
set cleanedPrefix [cleanScreen $dialogBuffer]
|
|
1422
|
+
}
|
|
1423
|
+
set framed [regexp -nocase {(^|\\n)[ \\t]*─{8,}[ \\t]*\\n[ \\t]*Trust this folder\\?} $cleanedPrefix]
|
|
1424
|
+
if {$framed} {
|
|
1425
|
+
expect {
|
|
1426
|
+
-re {(^|\\r|\\n)[ \\t]*─{8,}[ \\t]*\\r*\\n} {
|
|
1427
|
+
append dialogBuffer $expect_out(buffer)
|
|
1428
|
+
}
|
|
1429
|
+
timeout { failTimeout $state $timeoutCode $unknownCode }
|
|
1430
|
+
eof { failEof $state $eofCode }
|
|
1431
|
+
}
|
|
1432
|
+
} else {
|
|
1433
|
+
expect {
|
|
1434
|
+
-nocase -re {↑↓[^\\r\\n]*navigate[^\\r\\n]*(?:Esc[^\\r\\n]*)?\\r*\\n} {
|
|
1435
|
+
append dialogBuffer $expect_out(buffer)
|
|
1436
|
+
}
|
|
1437
|
+
timeout { failTimeout $state $timeoutCode $unknownCode }
|
|
1438
|
+
eof { failEof $state $eofCode }
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
return [cleanScreen $dialogBuffer]
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
proc directoryTrustAction {dialog state unknownCode} {
|
|
1445
|
+
set inDialog 0
|
|
1446
|
+
set labels {}
|
|
1447
|
+
set selectedCount 0
|
|
1448
|
+
set selectedIndex -1
|
|
1449
|
+
set selectedLabel ""
|
|
1450
|
+
set trustCount 0
|
|
1451
|
+
set trustIndex -1
|
|
1452
|
+
foreach rawLine [split $dialog "\\n"] {
|
|
1453
|
+
set line [string trim $rawLine]
|
|
1454
|
+
if {[regexp -nocase {^Trust this folder\\?$} $line]} {
|
|
1455
|
+
set inDialog 1
|
|
1456
|
+
continue
|
|
1457
|
+
}
|
|
1458
|
+
if {!$inDialog} { continue }
|
|
1459
|
+
if {$line eq ""} { continue }
|
|
1460
|
+
set selected [regexp {^❯[ \\t]*} $line]
|
|
1461
|
+
if {$selected} {
|
|
1462
|
+
regsub {^❯[ \\t]*} $line {} label
|
|
1463
|
+
set label [string trim $label]
|
|
1464
|
+
} else {
|
|
1465
|
+
set label $line
|
|
1466
|
+
}
|
|
1467
|
+
set knownAction [expr {
|
|
1468
|
+
[string equal -nocase $label "Trust this folder"]
|
|
1469
|
+
|| [string equal -nocase $label "No, exit"]
|
|
1470
|
+
|| [string equal -nocase $label "Don't trust"]
|
|
1471
|
+
}]
|
|
1472
|
+
if {!$knownAction && $selected} {
|
|
1473
|
+
puts stderr "KIMI_TUI_STATE:$state:selection-unknown"
|
|
1474
|
+
exit $unknownCode
|
|
1475
|
+
}
|
|
1476
|
+
if {!$knownAction} { continue }
|
|
1477
|
+
set index [llength $labels]
|
|
1478
|
+
lappend labels $label
|
|
1479
|
+
if {[string equal -nocase $label "Trust this folder"]} {
|
|
1480
|
+
incr trustCount
|
|
1481
|
+
set trustIndex $index
|
|
1482
|
+
}
|
|
1483
|
+
if {$selected} {
|
|
1484
|
+
incr selectedCount
|
|
1485
|
+
set selectedIndex $index
|
|
1486
|
+
set selectedLabel $label
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
if {[llength $labels] != 2 || $trustCount != 1 || $selectedCount != 1} {
|
|
1490
|
+
puts stderr "KIMI_TUI_STATE:$state:selection-unknown"
|
|
1491
|
+
exit $unknownCode
|
|
1492
|
+
}
|
|
1493
|
+
if {[string equal -nocase $selectedLabel "Trust this folder"]} {
|
|
1494
|
+
return selected-trust
|
|
1495
|
+
}
|
|
1496
|
+
if {![string equal -nocase $selectedLabel "No, exit"]
|
|
1497
|
+
&& ![string equal -nocase $selectedLabel "Don't trust"]} {
|
|
1498
|
+
puts stderr "KIMI_TUI_STATE:$state:selection-unknown"
|
|
1499
|
+
exit $unknownCode
|
|
1500
|
+
}
|
|
1501
|
+
if {$selectedIndex + 1 == $trustIndex} { return move-down }
|
|
1502
|
+
if {$selectedIndex - 1 == $trustIndex} { return move-up }
|
|
1503
|
+
puts stderr "KIMI_TUI_STATE:$state:selection-unknown"
|
|
1504
|
+
exit $unknownCode
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
proc confirmInitialDirectoryTrust {} {
|
|
1508
|
+
global expect_out promptPattern
|
|
1509
|
+
set dialog [readDirectoryTrustDialog $expect_out(buffer) directory-trust-selected-row 140 143 141]
|
|
1510
|
+
set action [directoryTrustAction $dialog directory-trust 143]
|
|
1511
|
+
if {$action eq "move-down"} {
|
|
1512
|
+
send -- "\\033\\[B"
|
|
1513
|
+
} elseif {$action eq "move-up"} {
|
|
1514
|
+
send -- "\\033\\[A"
|
|
1515
|
+
}
|
|
1516
|
+
if {$action ne "selected-trust"} {
|
|
1517
|
+
set confirmedDialog [readDirectoryTrustDialog "" directory-trust-confirm-selection 144 146 145]
|
|
1518
|
+
set confirmedAction [directoryTrustAction $confirmedDialog directory-trust-confirm-selection 146]
|
|
1519
|
+
if {$confirmedAction ne "selected-trust"} {
|
|
1520
|
+
puts stderr "KIMI_TUI_STATE:directory-trust-confirm-selection:unknown"
|
|
1521
|
+
exit 146
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
send -- "\\033\\[13u"
|
|
1525
|
+
expect {
|
|
1526
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1527
|
+
-re $promptPattern {}
|
|
1528
|
+
timeout { failTimeout directory-trust-prompt 151 153 }
|
|
1529
|
+
eof { failEof directory-trust-prompt 152 }
|
|
1530
|
+
}
|
|
1134
1531
|
}
|
|
1135
1532
|
|
|
1136
1533
|
proc submitCommand {command} {
|
|
@@ -1146,7 +1543,7 @@ if {[catch {exec stty columns 240 rows 60 < $spawn_out(slave,name)} resizeError]
|
|
|
1146
1543
|
exit 131
|
|
1147
1544
|
}
|
|
1148
1545
|
expect {
|
|
1149
|
-
-nocase -re {Trust this folder\\?} {
|
|
1546
|
+
-nocase -re {Trust this folder\\?} { confirmInitialDirectoryTrust }
|
|
1150
1547
|
-re $promptPattern {}
|
|
1151
1548
|
timeout { failTimeout initial-prompt 101 103 }
|
|
1152
1549
|
eof { failEof initial-prompt 102 }
|
|
@@ -1154,7 +1551,7 @@ expect {
|
|
|
1154
1551
|
${removeCommand}submitCommand "/plugins install $installUrl"
|
|
1155
1552
|
set dialogBuffer ""
|
|
1156
1553
|
expect {
|
|
1157
|
-
-nocase -re {Trust this folder\\?} {
|
|
1554
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1158
1555
|
-nocase -re {(?:Install third-party plugin|Trust and install from)[ \\t]} {
|
|
1159
1556
|
append dialogBuffer $expect_out(buffer)
|
|
1160
1557
|
}
|
|
@@ -1162,7 +1559,7 @@ expect {
|
|
|
1162
1559
|
eof { failEof plugin-trust-anchor 105 }
|
|
1163
1560
|
}
|
|
1164
1561
|
expect {
|
|
1165
|
-
-nocase -re {Trust this folder\\?} {
|
|
1562
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1166
1563
|
-nocase -re {❯[^\\r\\n]*(?:Exit|Cancel|Trust and install)} {
|
|
1167
1564
|
append dialogBuffer $expect_out(buffer)
|
|
1168
1565
|
}
|
|
@@ -1198,7 +1595,7 @@ if {[regexp -nocase {(^|\\n)[^\\n]*❯[^\\n]*trust and install} $dialog]} {
|
|
|
1198
1595
|
} elseif {[regexp -nocase {(^|\\n)[^\\n]*❯[^\\n]*(cancel|exit)} $dialog]} {
|
|
1199
1596
|
send -- "\\033\\[B"
|
|
1200
1597
|
expect {
|
|
1201
|
-
-nocase -re {Trust this folder\\?} {
|
|
1598
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1202
1599
|
-nocase -re {❯[^\\r\\n]*Trust and install} {}
|
|
1203
1600
|
-re {❯[^\\r\\n]*\\r*\\n} {
|
|
1204
1601
|
puts stderr "KIMI_TUI_STATE:plugin-trust-confirm-selection:unknown"
|
|
@@ -1214,7 +1611,7 @@ if {[regexp -nocase {(^|\\n)[^\\n]*❯[^\\n]*trust and install} $dialog]} {
|
|
|
1214
1611
|
}
|
|
1215
1612
|
|
|
1216
1613
|
expect {
|
|
1217
|
-
-nocase -re {Trust this folder\\?} {
|
|
1614
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1218
1615
|
-nocase -re {Install finished[^\\r\\n]*see details below\\.} {}
|
|
1219
1616
|
-nocase -re {Installing plugin from[^\\r\\n]*(?:\\r|\\n)} {
|
|
1220
1617
|
exp_continue -continue_timer
|
|
@@ -1234,14 +1631,14 @@ expect {
|
|
|
1234
1631
|
eof { failEof install-result 137 }
|
|
1235
1632
|
}
|
|
1236
1633
|
expect {
|
|
1237
|
-
-nocase -re {Trust this folder\\?} {
|
|
1634
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1238
1635
|
-re $promptPattern {}
|
|
1239
1636
|
timeout { failTimeout post-install-prompt 114 116 }
|
|
1240
1637
|
eof { failEof post-install-prompt 115 }
|
|
1241
1638
|
}
|
|
1242
1639
|
submitCommand "/reload"
|
|
1243
1640
|
expect {
|
|
1244
|
-
-nocase -re {Trust this folder\\?} {
|
|
1641
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1245
1642
|
-re $promptPattern {}
|
|
1246
1643
|
timeout { failTimeout reload-prompt 117 119 }
|
|
1247
1644
|
eof { failEof reload-prompt 118 }
|
|
@@ -1343,29 +1740,20 @@ async function runKimiUpdate(target, detected, run, kimiHome, {
|
|
|
1343
1740
|
}
|
|
1344
1741
|
const installUrl = `https://github.com/${target.pluginRepo}/releases/tag/${target.pluginTag}`;
|
|
1345
1742
|
const removePlugin = before.source === 'legacy' ? target.plugin : '';
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
} catch (error) {
|
|
1361
|
-
if (error?.exitStatus === 80) {
|
|
1362
|
-
return {
|
|
1363
|
-
status: 'MANUAL_REQUIRED',
|
|
1364
|
-
reason: 'Kimi requires folder trust; release-finish did not confirm the folder or continue installation',
|
|
1365
|
-
};
|
|
1366
|
-
}
|
|
1367
|
-
throw error;
|
|
1368
|
-
}
|
|
1743
|
+
await run(detected.expectCommand, ['-c', kimiExpectProgram({
|
|
1744
|
+
...(removePlugin ? { removePlugin } : {}),
|
|
1745
|
+
})], {
|
|
1746
|
+
cwd: root,
|
|
1747
|
+
timeout: Math.max(300_000, target.timeoutMs),
|
|
1748
|
+
env: {
|
|
1749
|
+
...env,
|
|
1750
|
+
RELEASE_SKILL_KIMI_COMMAND: detected.command,
|
|
1751
|
+
RELEASE_SKILL_KIMI_INSTALL_URL: installUrl,
|
|
1752
|
+
RELEASE_SKILL_KIMI_REMOVE_PLUGIN: removePlugin,
|
|
1753
|
+
RELEASE_SKILL_KIMI_EXPECTED_REPO: `https://github.com/${target.pluginRepo}`,
|
|
1754
|
+
RELEASE_SKILL_KIMI_EXPECTED_TAG: target.pluginTag,
|
|
1755
|
+
},
|
|
1756
|
+
});
|
|
1369
1757
|
}, { prefix: 'release-skill-kimi-update-' });
|
|
1370
1758
|
if (tuiOutcome) return tuiOutcome;
|
|
1371
1759
|
const after = await observeKimiTarget(target, kimiHome, run);
|
|
@@ -1380,6 +1768,160 @@ async function runKimiUpdate(target, detected, run, kimiHome, {
|
|
|
1380
1768
|
return { status: 'UPDATED', version: target.version, restartRequired: true };
|
|
1381
1769
|
}
|
|
1382
1770
|
|
|
1771
|
+
async function observeCursorClosure(root) {
|
|
1772
|
+
return observeFilesystemTree({ root, rootBinding: await createFilesystemRootBinding(root) });
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
async function cursorMainProcessRunning(run) {
|
|
1776
|
+
const result = await run('/bin/ps', ['-axo', 'comm='], { timeout: 30_000 });
|
|
1777
|
+
if (typeof result?.stdout !== 'string' || result.stdout.trim().length === 0) throw new Error('Cursor process observation is unavailable');
|
|
1778
|
+
return result.stdout.split('\n').some((line) => /(?:^|\/)Cursor$/u.test(line.trim()));
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
async function runCursorLocalUpdate(target, {
|
|
1782
|
+
root, run, cursorPluginsRoot, cursorPlatform = process.platform,
|
|
1783
|
+
cursorIsRunning = () => cursorMainProcessRunning(run),
|
|
1784
|
+
cursorPublication = { publishFixedSet, replaceFixedSetAtomic },
|
|
1785
|
+
cursorMoveBackup = rename,
|
|
1786
|
+
}) {
|
|
1787
|
+
const frozen = await verifyFrozenSnapshot({ root, snapshotPath: target.snapshotPath, expectedDigest: target.manifestDigest });
|
|
1788
|
+
if (!target.cursor?.sourcePath) throw new Error('Cursor local update requires a frozen cursor.sourcePath');
|
|
1789
|
+
if (!/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/u.test(target.plugin)) throw new Error('Cursor plugin identity must use a valid Cursor plugin name');
|
|
1790
|
+
const source = await resolveContained(frozen.snapshotDir, target.cursor.sourcePath);
|
|
1791
|
+
const identity = JSON.parse(await readFileContained(source, '.cursor-plugin/plugin.json', { encoding: 'utf8' }));
|
|
1792
|
+
if (typeof identity.name !== 'string' || !/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/u.test(identity.name)
|
|
1793
|
+
|| identity.name !== target.plugin || identity.version !== target.version) throw new Error('Cursor frozen plugin manifest identity conflicts with the release unit');
|
|
1794
|
+
if (typeof cursorPluginsRoot !== 'string' || !isAbsolute(cursorPluginsRoot) || normalize(cursorPluginsRoot) !== cursorPluginsRoot) {
|
|
1795
|
+
throw new Error('Cursor local update requires --cursor-plugins-root <normalized-absolute-directory>');
|
|
1796
|
+
}
|
|
1797
|
+
if (cursorPlatform !== 'darwin') return { status: 'MANUAL_REQUIRED', reason: 'Cursor Local automatic installation currently supports macOS only' };
|
|
1798
|
+
try {
|
|
1799
|
+
if (await cursorIsRunning() !== false) return { status: 'MANUAL_REQUIRED', reason: 'Quit the Cursor main process before updating Local plugins' };
|
|
1800
|
+
} catch (cause) {
|
|
1801
|
+
return { status: 'MANUAL_REQUIRED', reason: `Cannot confirm Cursor has exited: ${cause.message}` };
|
|
1802
|
+
}
|
|
1803
|
+
// Bind the caller-selected root before creating any staging directory.
|
|
1804
|
+
await createFilesystemRootBinding(cursorPluginsRoot);
|
|
1805
|
+
const pluginsRoot = await realpath(cursorPluginsRoot);
|
|
1806
|
+
const sourceObservation = await observeCursorClosure(source);
|
|
1807
|
+
const workspace = await createTemporaryWorkspace({ prefix: 'release-skill-cursor-local-' });
|
|
1808
|
+
let sibling;
|
|
1809
|
+
let preserve = false;
|
|
1810
|
+
try {
|
|
1811
|
+
const candidate = join(workspace.root, 'plugin');
|
|
1812
|
+
await cp(source, candidate, { recursive: true, errorOnExist: true, force: false });
|
|
1813
|
+
if ((await observeCursorClosure(candidate)).membersDigest !== sourceObservation.membersDigest) throw new Error('Cursor candidate differs from the frozen source closure');
|
|
1814
|
+
await chmod(candidate, 0o700);
|
|
1815
|
+
if (target.cursor.dependencyInstall !== undefined) {
|
|
1816
|
+
if (target.cursor.dependencyInstall !== 'npm-ci-ignore-scripts') throw new Error('Unsupported Cursor dependency installation policy');
|
|
1817
|
+
await run('npm', ['ci', '--ignore-scripts', '--prefix', candidate], {
|
|
1818
|
+
cwd: candidate, timeout: target.timeoutMs, env: hostEnvironment('cursor'),
|
|
1819
|
+
});
|
|
1820
|
+
// npm may add dependencies, but must not mutate the frozen plugin inputs.
|
|
1821
|
+
for (const member of sourceObservation.members.filter((entry) => entry.type === 'file')) {
|
|
1822
|
+
await readFileStrict(candidate, member.path, { expectedSha256: member.sha256 });
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
const prepared = await observeCursorClosure(candidate);
|
|
1826
|
+
await verifyFrozenSnapshot({ root, snapshotPath: target.snapshotPath, expectedDigest: target.manifestDigest });
|
|
1827
|
+
try {
|
|
1828
|
+
if (await cursorIsRunning() !== false) return { status: 'MANUAL_REQUIRED', reason: 'Cursor started while preparing the plugin; quit Cursor and rerun the update' };
|
|
1829
|
+
} catch (cause) {
|
|
1830
|
+
return { status: 'MANUAL_REQUIRED', reason: `Cannot confirm Cursor has exited before publication: ${cause.message}` };
|
|
1831
|
+
}
|
|
1832
|
+
const local = await resolveContained(pluginsRoot, 'local');
|
|
1833
|
+
await mkdir(local, { recursive: true });
|
|
1834
|
+
await createFilesystemRootBinding(local);
|
|
1835
|
+
const targetPath = await resolveContained(local, target.plugin);
|
|
1836
|
+
let installed = false;
|
|
1837
|
+
let previousClosure;
|
|
1838
|
+
try {
|
|
1839
|
+
await lstat(targetPath);
|
|
1840
|
+
installed = true;
|
|
1841
|
+
} catch (cause) { if (cause.code !== 'ENOENT') throw cause; }
|
|
1842
|
+
if (installed) {
|
|
1843
|
+
const current = JSON.parse(await readFileContained(targetPath, '.cursor-plugin/plugin.json', { encoding: 'utf8' }));
|
|
1844
|
+
if (current.name !== target.plugin || typeof current.version !== 'string') throw new Error('Cursor installed plugin manifest identity conflicts with the frozen plugin');
|
|
1845
|
+
const observed = await observeCursorClosure(targetPath);
|
|
1846
|
+
previousClosure = observed.membersDigest;
|
|
1847
|
+
if (current.version === target.version && observed.membersDigest === prepared.membersDigest) return { status: 'ALREADY_CURRENT', version: target.version, installPath: targetPath };
|
|
1848
|
+
}
|
|
1849
|
+
// Cursor is closed, so this same-parent candidate cannot be discovered by
|
|
1850
|
+
// a live scanner. Only this uniquely-created directory may be cleaned.
|
|
1851
|
+
sibling = await mkdtemp(join(local, `.${target.plugin}-stage-`));
|
|
1852
|
+
await cp(candidate, sibling, { recursive: true, errorOnExist: false, force: false });
|
|
1853
|
+
if ((await observeCursorClosure(sibling)).membersDigest !== prepared.membersDigest) throw new Error('Cursor publication candidate closure drift');
|
|
1854
|
+
const publication = { sourceRoot: sibling, targetParent: local, targetSegment: target.plugin };
|
|
1855
|
+
let backupPath;
|
|
1856
|
+
if (installed) {
|
|
1857
|
+
try {
|
|
1858
|
+
await cursorPublication.replaceFixedSetAtomic(publication);
|
|
1859
|
+
} catch (cause) {
|
|
1860
|
+
preserve = cause.details?.phase === 'post-commit' || cause.details?.publicationState === 'indeterminate' || cause.details?.commitState === 'indeterminate';
|
|
1861
|
+
throw cause;
|
|
1862
|
+
}
|
|
1863
|
+
// Foundation has proved the exchanged mapping. The old complete plugin
|
|
1864
|
+
// is now owned by the user and must leave the Local scanner namespace.
|
|
1865
|
+
preserve = true;
|
|
1866
|
+
try {
|
|
1867
|
+
const backups = await resolveContained(pluginsRoot, `backups/${target.plugin}`);
|
|
1868
|
+
await mkdir(backups, { recursive: true });
|
|
1869
|
+
await createFilesystemRootBinding(backups);
|
|
1870
|
+
const backupDirectory = await mkdtemp(join(backups, 'release-'));
|
|
1871
|
+
backupPath = join(backupDirectory, 'plugin');
|
|
1872
|
+
await cursorMoveBackup(sibling, backupPath);
|
|
1873
|
+
sibling = undefined;
|
|
1874
|
+
preserve = false;
|
|
1875
|
+
} catch (cause) {
|
|
1876
|
+
// One explicit reverse exchange is justified only by the verified
|
|
1877
|
+
// success above and an old directory still at the displaced path.
|
|
1878
|
+
if ((await observeCursorClosure(sibling)).membersDigest !== previousClosure
|
|
1879
|
+
|| (await observeCursorClosure(targetPath)).membersDigest !== prepared.membersDigest) throw cause;
|
|
1880
|
+
await cursorPublication.replaceFixedSetAtomic(publication);
|
|
1881
|
+
preserve = false;
|
|
1882
|
+
throw new Error(`Cursor backup move failed; original plugin restored: ${cause.message}`);
|
|
1883
|
+
}
|
|
1884
|
+
} else {
|
|
1885
|
+
const manifest = await createFixedSetPublicationManifest(publication);
|
|
1886
|
+
const receipt = await cursorPublication.publishFixedSet({ ...publication, manifest });
|
|
1887
|
+
if (receipt.status !== 'succeeded') {
|
|
1888
|
+
preserve = receipt.status === 'indeterminate' || receipt.commitState !== 'not-committed';
|
|
1889
|
+
const error = new Error(`Cursor publication failed: ${receipt.error?.message ?? receipt.status}`);
|
|
1890
|
+
error.details = receipt;
|
|
1891
|
+
throw error;
|
|
1892
|
+
}
|
|
1893
|
+
sibling = undefined;
|
|
1894
|
+
}
|
|
1895
|
+
if ((await observeCursorClosure(targetPath)).membersDigest !== prepared.membersDigest) throw new Error('Cursor installed plugin closure drift after publication');
|
|
1896
|
+
return {
|
|
1897
|
+
status: 'UPDATED', version: target.version, installPath: targetPath,
|
|
1898
|
+
...(backupPath ? { backupPath } : {}), restartRequired: true,
|
|
1899
|
+
reloadInstruction: 'Restart Cursor or run Reload Window, then verify the Local source, version and Skill invocation.',
|
|
1900
|
+
};
|
|
1901
|
+
} catch (error) {
|
|
1902
|
+
if (preserve) error.details = { ...error.details, candidatePath: sibling, workspacePath: workspace.root, manualAction: 'Keep both directories intact; inspect the Cursor Local publication mapping before retrying.' };
|
|
1903
|
+
throw error;
|
|
1904
|
+
} finally {
|
|
1905
|
+
if (!preserve) {
|
|
1906
|
+
// Sealed snapshots carry read-only directories. Restore owner access
|
|
1907
|
+
// only in this invocation's disposable copies before Foundation cleanup.
|
|
1908
|
+
for (const cleanupRoot of [sibling, workspace.root].filter(Boolean)) {
|
|
1909
|
+
const closure = await observeFilesystemTree({
|
|
1910
|
+
root: cleanupRoot,
|
|
1911
|
+
rootBinding: await createFilesystemRootBinding(cleanupRoot),
|
|
1912
|
+
symlinkPolicy: { mode: 'record' },
|
|
1913
|
+
});
|
|
1914
|
+
await chmod(cleanupRoot, 0o700);
|
|
1915
|
+
for (const member of closure.members.filter((entry) => entry.type === 'directory')) {
|
|
1916
|
+
await chmod(await resolveContained(cleanupRoot, member.path), member.statMode | 0o700);
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
if (sibling) await rm(sibling, { recursive: true, force: false });
|
|
1920
|
+
await workspace.dispose();
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1383
1925
|
function aggregateStatus(results) {
|
|
1384
1926
|
const statuses = new Set(results.map((entry) => entry.status));
|
|
1385
1927
|
const completed = results.some((entry) => ['UPDATED', 'ALREADY_CURRENT'].includes(entry.status));
|
|
@@ -1419,6 +1961,11 @@ async function updateLocalHostPluginsInternal({
|
|
|
1419
1961
|
detect = (host) => defaultDetect(host, defaultRun),
|
|
1420
1962
|
run = defaultRun,
|
|
1421
1963
|
kimiHome,
|
|
1964
|
+
cursorPluginsRoot,
|
|
1965
|
+
cursorPlatform,
|
|
1966
|
+
cursorIsRunning,
|
|
1967
|
+
cursorPublication,
|
|
1968
|
+
cursorMoveBackup,
|
|
1422
1969
|
verifyInstalledPayload = verifyInstalledMarketplacePayload,
|
|
1423
1970
|
} = {}) {
|
|
1424
1971
|
const effectiveKimiHome = kimiHome ?? process.env.KIMI_CODE_HOME ?? join(homedir(), '.kimi-code');
|
|
@@ -1429,12 +1976,25 @@ async function updateLocalHostPluginsInternal({
|
|
|
1429
1976
|
const selected = new Set(Array.isArray(selectedHosts) ? selectedHosts : []);
|
|
1430
1977
|
const unknown = [...selected].filter((host) => !checklist.localHostUpdate.hosts.includes(host));
|
|
1431
1978
|
if (unknown.length > 0) throw new Error(`selected hosts are not declared by the plan: ${unknown.join(', ')}`);
|
|
1432
|
-
const targets =
|
|
1433
|
-
|
|
1979
|
+
const targets = checklist.localHostUpdate.targets.filter((item) => (
|
|
1980
|
+
item.executionMode !== 'manual' && selected.has(item.host)
|
|
1981
|
+
));
|
|
1982
|
+
for (const target of targets) {
|
|
1983
|
+
if (target.host === 'cursor') continue;
|
|
1984
|
+
if (target.host === 'qoder') assertQoderExecutableTarget(target);
|
|
1985
|
+
else assertExecutableTarget(target);
|
|
1986
|
+
}
|
|
1434
1987
|
|
|
1435
1988
|
const results = [];
|
|
1436
1989
|
for (const target of targets) {
|
|
1437
1990
|
try {
|
|
1991
|
+
if (target.host === 'cursor') {
|
|
1992
|
+
const outcome = await runCursorLocalUpdate(target, {
|
|
1993
|
+
root, run, cursorPluginsRoot, cursorPlatform, cursorIsRunning, cursorPublication, cursorMoveBackup,
|
|
1994
|
+
});
|
|
1995
|
+
results.push({ host: target.host, unitId: target.unitId, ...outcome });
|
|
1996
|
+
continue;
|
|
1997
|
+
}
|
|
1438
1998
|
const detected = await detect(target.host);
|
|
1439
1999
|
if (!detected?.available) {
|
|
1440
2000
|
results.push({
|
|
@@ -1451,7 +2011,12 @@ async function updateLocalHostPluginsInternal({
|
|
|
1451
2011
|
root,
|
|
1452
2012
|
verifyInstalledPayload,
|
|
1453
2013
|
})
|
|
1454
|
-
:
|
|
2014
|
+
: target.host === 'qoder'
|
|
2015
|
+
? await runQoderUpdate(target, detected, run, {
|
|
2016
|
+
root,
|
|
2017
|
+
verifyInstalledPayload,
|
|
2018
|
+
})
|
|
2019
|
+
: await runStructuredUpdate(target, detected, run, {
|
|
1455
2020
|
plan,
|
|
1456
2021
|
root,
|
|
1457
2022
|
verifyInstalledPayload,
|