just-vibe 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +12 -0
- package/.claude-plugin/marketplace.json +12 -0
- package/CHANGELOG.md +49 -0
- package/LICENSE +21 -0
- package/README.md +282 -0
- package/bin/just-vibe.mjs +3 -0
- package/docs/command-quality.md +74 -0
- package/docs/compatibility.md +29 -0
- package/docs/releases.md +51 -0
- package/evals/README.md +47 -0
- package/evals/behavior/cases/arch-events/flow.json +12 -0
- package/evals/behavior/cases/arch-events/task.md +3 -0
- package/evals/behavior/cases/authz/access.mjs +1 -0
- package/evals/behavior/cases/authz/task.md +3 -0
- package/evals/behavior/cases/checkout/checkout.mjs +1 -0
- package/evals/behavior/cases/checkout/contract.md +1 -0
- package/evals/behavior/cases/checkout/keep.txt +1 -0
- package/evals/behavior/cases/checkout/task.md +3 -0
- package/evals/behavior/cases/data-reconcile/source.json +14 -0
- package/evals/behavior/cases/data-reconcile/target.json +14 -0
- package/evals/behavior/cases/data-reconcile/task.md +3 -0
- package/evals/behavior/cases/db-migrate/context.json +13 -0
- package/evals/behavior/cases/db-migrate/migration.sql +3 -0
- package/evals/behavior/cases/db-migrate/task.md +3 -0
- package/evals/behavior/cases/db-query/query.sql +1 -0
- package/evals/behavior/cases/db-query/rows.json +32 -0
- package/evals/behavior/cases/db-query/task.md +3 -0
- package/evals/behavior/cases/decision-matrix/decision.json +22 -0
- package/evals/behavior/cases/decision-matrix/task.md +3 -0
- package/evals/behavior/cases/github-pr/prs.json +16 -0
- package/evals/behavior/cases/github-pr/request.json +9 -0
- package/evals/behavior/cases/github-pr/task.md +3 -0
- package/evals/behavior/cases/idempotency/contract.md +1 -0
- package/evals/behavior/cases/idempotency/orders.mjs +1 -0
- package/evals/behavior/cases/idempotency/task.md +3 -0
- package/evals/behavior/cases/ml-checkpoint/checkpoint.json +6 -0
- package/evals/behavior/cases/ml-checkpoint/task.md +3 -0
- package/evals/behavior/cases/ml-checkpoint/training.json +16 -0
- package/evals/behavior/cases/ml-evaluate/labels.json +18 -0
- package/evals/behavior/cases/ml-evaluate/predictions.json +14 -0
- package/evals/behavior/cases/ml-evaluate/task.md +3 -0
- package/evals/behavior/cases/ml-leakage/task.json +30 -0
- package/evals/behavior/cases/ml-leakage/task.md +3 -0
- package/evals/behavior/cases/ml-parity/serving.json +16 -0
- package/evals/behavior/cases/ml-parity/task.md +3 -0
- package/evals/behavior/cases/ml-parity/training.json +16 -0
- package/evals/behavior/cases/ml-split/task.json +7 -0
- package/evals/behavior/cases/ml-split/task.md +3 -0
- package/evals/behavior/cases/ops-logs/context.json +4 -0
- package/evals/behavior/cases/ops-logs/events.json +17 -0
- package/evals/behavior/cases/ops-logs/task.md +3 -0
- package/evals/behavior/cases/rag-boundary/documents.json +26 -0
- package/evals/behavior/cases/rag-boundary/query.json +5 -0
- package/evals/behavior/cases/rag-boundary/task.md +3 -0
- package/evals/behavior/cases/react-race/AccountPanel.jsx +1 -0
- package/evals/behavior/cases/react-race/loader.mjs +1 -0
- package/evals/behavior/cases/react-race/task.md +3 -0
- package/evals/behavior/cases/regression-test/checkout.mjs +1 -0
- package/evals/behavior/cases/regression-test/contract.md +1 -0
- package/evals/behavior/cases/regression-test/task.md +3 -0
- package/evals/behavior/cases/ui-accessibility/observations.json +17 -0
- package/evals/behavior/cases/ui-accessibility/task.md +3 -0
- package/evals/behavior/cases/vercel-env/consumers.json +13 -0
- package/evals/behavior/cases/vercel-env/metadata.json +11 -0
- package/evals/behavior/cases/vercel-env/task.md +3 -0
- package/evals/behavior/cases/vite-assets/deployment.json +8 -0
- package/evals/behavior/cases/vite-assets/render.mjs +1 -0
- package/evals/behavior/cases/vite-assets/task.md +3 -0
- package/evals/behavior/cases/vite-assets/vite.config.mjs +1 -0
- package/evals/behavior/cases.json +185 -0
- package/evals/behavior/code-oracles.mjs +58 -0
- package/evals/behavior/harness.mjs +109 -0
- package/evals/behavior/oracles.json +196 -0
- package/evals/benchmark/README.md +57 -0
- package/evals/benchmark/cases.json +9 -0
- package/evals/benchmark/harness.mjs +231 -0
- package/evals/benchmark/oracles/node.mjs +69 -0
- package/evals/benchmark/oracles/python.py +117 -0
- package/evals/benchmark/report.mjs +62 -0
- package/evals/benchmark/repos/async-cache/README.md +12 -0
- package/evals/benchmark/repos/async-cache/TASK.md +1 -0
- package/evals/benchmark/repos/async-cache/package.json +1 -0
- package/evals/benchmark/repos/async-cache/src/cache.mjs +13 -0
- package/evals/benchmark/repos/async-cache/src/view.mjs +9 -0
- package/evals/benchmark/repos/async-cache/test/smoke.test.mjs +9 -0
- package/evals/benchmark/repos/ledger/README.md +11 -0
- package/evals/benchmark/repos/ledger/TASK.md +1 -0
- package/evals/benchmark/repos/ledger/src/service.py +14 -0
- package/evals/benchmark/repos/ledger/src/store.py +12 -0
- package/evals/benchmark/repos/ledger/test/test_smoke.py +9 -0
- package/evals/benchmark/repos/scoped-commit/README.md +5 -0
- package/evals/benchmark/repos/scoped-commit/TASK.md +1 -0
- package/evals/benchmark/repos/scoped-commit/package.json +1 -0
- package/evals/benchmark/repos/scoped-commit/src/invoice.mjs +8 -0
- package/evals/benchmark/repos/scoped-commit/test/invoice.test.mjs +4 -0
- package/evals/benchmark/repos/temporal-ml/README.md +12 -0
- package/evals/benchmark/repos/temporal-ml/TASK.md +1 -0
- package/evals/benchmark/repos/temporal-ml/src/features.py +9 -0
- package/evals/benchmark/repos/temporal-ml/src/pipeline.py +10 -0
- package/evals/benchmark/repos/temporal-ml/src/report.py +2 -0
- package/evals/benchmark/repos/temporal-ml/test/test_smoke.py +7 -0
- package/evals/benchmark/support/commit-tree.mjs +11 -0
- package/evals/benchmark/support/python-test-report.py +48 -0
- package/evals/fixtures/checkout/checkout.mjs +4 -0
- package/evals/fixtures/checkout/checkout.test.mjs +13 -0
- package/evals/fixtures/checkout/package.json +6 -0
- package/evals/fixtures/checkout/unrelated.txt +1 -0
- package/evals/fixtures/ml/observations.csv +5 -0
- package/evals/fixtures/ml/task.md +1 -0
- package/evals/releases/0.2.0.md +45 -0
- package/evals/releases/0.3.0.md +23 -0
- package/evals/releases/0.4.0-results.json +1274 -0
- package/evals/releases/0.4.0.md +55 -0
- package/evals/releases/0.5.0.md +28 -0
- package/evals/releases/0.6.0-after-results.json +1307 -0
- package/evals/releases/0.6.0-before-results.json +4850 -0
- package/evals/releases/0.6.0.md +94 -0
- package/evals/releases/0.7.0.md +32 -0
- package/evals/scenarios.json +7777 -0
- package/package.json +50 -0
- package/plugins/just-vibe/.claude-plugin/plugin.json +11 -0
- package/plugins/just-vibe/.codex-plugin/plugin.json +24 -0
- package/plugins/just-vibe/LICENSE +21 -0
- package/plugins/just-vibe/catalog/commands.json +16757 -0
- package/plugins/just-vibe/catalog/packs.json +115 -0
- package/plugins/just-vibe/catalog/profiles.json +2503 -0
- package/plugins/just-vibe/hooks/hooks.json +11 -0
- package/plugins/just-vibe/references/command-reference.md +328 -0
- package/plugins/just-vibe/references/daily-workflows.md +133 -0
- package/plugins/just-vibe/references/execution.md +60 -0
- package/plugins/just-vibe/references/instruction-memory.md +86 -0
- package/plugins/just-vibe/references/packs/api.md +27 -0
- package/plugins/just-vibe/references/packs/architecture.md +29 -0
- package/plugins/just-vibe/references/packs/backend.md +43 -0
- package/plugins/just-vibe/references/packs/data.md +27 -0
- package/plugins/just-vibe/references/packs/database.md +32 -0
- package/plugins/just-vibe/references/packs/decisions.md +29 -0
- package/plugins/just-vibe/references/packs/general.md +34 -0
- package/plugins/just-vibe/references/packs/git.md +45 -0
- package/plugins/just-vibe/references/packs/github.md +31 -0
- package/plugins/just-vibe/references/packs/installation.md +27 -0
- package/plugins/just-vibe/references/packs/llm.md +33 -0
- package/plugins/just-vibe/references/packs/ml-data.md +43 -0
- package/plugins/just-vibe/references/packs/ml-deployment.md +31 -0
- package/plugins/just-vibe/references/packs/ml-evaluation.md +29 -0
- package/plugins/just-vibe/references/packs/ml-experiments.md +29 -0
- package/plugins/just-vibe/references/packs/operations.md +35 -0
- package/plugins/just-vibe/references/packs/react.md +29 -0
- package/plugins/just-vibe/references/packs/security.md +31 -0
- package/plugins/just-vibe/references/packs/testing.md +35 -0
- package/plugins/just-vibe/references/packs/ui.md +29 -0
- package/plugins/just-vibe/references/packs/vercel.md +29 -0
- package/plugins/just-vibe/references/packs/vite.md +29 -0
- package/plugins/just-vibe/references/profile-reference.md +155 -0
- package/plugins/just-vibe/references/profiles/accessibility-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/agent-systems-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ai-evaluation-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ai-security-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/analytics-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/android-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/api-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/application-security-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/applied-ai-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/backend-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/bioinformatics-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/blockchain-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/build-release-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/business-intelligence-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/capacity-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/causal-inference-scientist.md +31 -0
- package/plugins/just-vibe/references/profiles/cloud-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/cloud-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/cloud-security-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/compiler-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/computer-vision-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/controls-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/creative-technologist.md +31 -0
- package/plugins/just-vibe/references/profiles/cryptography-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/data-analyst.md +31 -0
- package/plugins/just-vibe/references/profiles/data-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/data-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/data-governance-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/data-platform-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/data-quality-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/data-scientist.md +31 -0
- package/plugins/just-vibe/references/profiles/database-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/database-reliability-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/design-systems-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/desktop-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/detection-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/developer-advocate.md +31 -0
- package/plugins/just-vibe/references/profiles/developer-experience-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/devops-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/distributed-systems-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/edge-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/embedded-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/engineering-manager.md +31 -0
- package/plugins/just-vibe/references/profiles/enterprise-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/experimentation-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/finops-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/firmware-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/frontend-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/frontend-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/fullstack-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/game-networking-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/gameplay-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/geospatial-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/graphics-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/hpc-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/identity-access-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/inference-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/infrastructure-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/integration-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/integration-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ios-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/iot-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/kubernetes-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/llm-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/machine-learning-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ml-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/ml-data-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ml-platform-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/mlops-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/mobile-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/network-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/nlp-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/observability-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/performance-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/platform-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/platform-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/principal-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/privacy-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/product-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/product-security-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/protocol-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/qa-automation-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/recommendation-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/reinforcement-learning-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/research-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/research-scientist.md +31 -0
- package/plugins/just-vibe/references/profiles/responsible-ai-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/robotics-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/runtime-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/scientific-software-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/search-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/security-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/security-automation-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/security-incident-responder.md +31 -0
- package/plugins/just-vibe/references/profiles/senior-software-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/simulation-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/site-reliability-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/software-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/solutions-architect.md +31 -0
- package/plugins/just-vibe/references/profiles/speech-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/staff-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/storage-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/streaming-data-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/supply-chain-security-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/systems-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/tech-lead.md +31 -0
- package/plugins/just-vibe/references/profiles/technical-writer.md +31 -0
- package/plugins/just-vibe/references/profiles/test-infrastructure-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ui-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/ux-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/web-performance-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles/xr-engineer.md +31 -0
- package/plugins/just-vibe/references/profiles.md +59 -0
- package/plugins/just-vibe/references/runtime.md +70 -0
- package/plugins/just-vibe/references/scenarios/auth.md +31 -0
- package/plugins/just-vibe/references/scenarios/combobox.md +9 -0
- package/plugins/just-vibe/references/scenarios/date-picker.md +9 -0
- package/plugins/just-vibe/references/scenarios/delivery-evidence.md +21 -0
- package/plugins/just-vibe/references/scenarios/dialog.md +9 -0
- package/plugins/just-vibe/references/scenarios/training.md +21 -0
- package/plugins/just-vibe/references/teach-test.md +37 -0
- package/plugins/just-vibe/references/teaching.md +34 -0
- package/plugins/just-vibe/references/validation.md +11 -0
- package/plugins/just-vibe/scripts/discover-capabilities.mjs +3 -0
- package/plugins/just-vibe/scripts/hooks.mjs +14 -0
- package/plugins/just-vibe/scripts/inspect-project.mjs +3 -0
- package/plugins/just-vibe/scripts/installer.mjs +280 -0
- package/plugins/just-vibe/scripts/lib/automation.mjs +142 -0
- package/plugins/just-vibe/scripts/lib/bundle.mjs +100 -0
- package/plugins/just-vibe/scripts/lib/catalog.mjs +135 -0
- package/plugins/just-vibe/scripts/lib/command.mjs +26 -0
- package/plugins/just-vibe/scripts/lib/continuity.mjs +77 -0
- package/plugins/just-vibe/scripts/lib/discovery.mjs +84 -0
- package/plugins/just-vibe/scripts/lib/entrypoint.mjs +12 -0
- package/plugins/just-vibe/scripts/lib/evidence.mjs +136 -0
- package/plugins/just-vibe/scripts/lib/process.mjs +44 -0
- package/plugins/just-vibe/scripts/lib/profiles.mjs +83 -0
- package/plugins/just-vibe/scripts/lib/project.mjs +60 -0
- package/plugins/just-vibe/scripts/lib/routing.mjs +82 -0
- package/plugins/just-vibe/scripts/lib/run.mjs +248 -0
- package/plugins/just-vibe/scripts/lib/storage.mjs +84 -0
- package/plugins/just-vibe/scripts/lib/teaching.mjs +118 -0
- package/plugins/just-vibe/scripts/toolkit.mjs +225 -0
- package/plugins/just-vibe/skills/a11y/SKILL.md +8 -0
- package/plugins/just-vibe/skills/api-breaking/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-client/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-contract-test/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-design/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-errors/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-openapi/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-pagination/SKILL.md +56 -0
- package/plugins/just-vibe/skills/api-webhooks/SKILL.md +56 -0
- package/plugins/just-vibe/skills/arch-boundaries/SKILL.md +58 -0
- package/plugins/just-vibe/skills/arch-contracts/SKILL.md +56 -0
- package/plugins/just-vibe/skills/arch-event-flow/SKILL.md +56 -0
- package/plugins/just-vibe/skills/arch-feature/SKILL.md +57 -0
- package/plugins/just-vibe/skills/arch-map/SKILL.md +56 -0
- package/plugins/just-vibe/skills/arch-modernize/SKILL.md +56 -0
- package/plugins/just-vibe/skills/arch-scale/SKILL.md +58 -0
- package/plugins/just-vibe/skills/arch-tenancy/SKILL.md +56 -0
- package/plugins/just-vibe/skills/auto/SKILL.md +67 -0
- package/plugins/just-vibe/skills/automate/SKILL.md +56 -0
- package/plugins/just-vibe/skills/backend-auth/SKILL.md +63 -0
- package/plugins/just-vibe/skills/backend-cache/SKILL.md +59 -0
- package/plugins/just-vibe/skills/backend-concurrency/SKILL.md +58 -0
- package/plugins/just-vibe/skills/backend-idempotency/SKILL.md +58 -0
- package/plugins/just-vibe/skills/backend-jobs/SKILL.md +56 -0
- package/plugins/just-vibe/skills/backend-permissions/SKILL.md +56 -0
- package/plugins/just-vibe/skills/backend-resilience/SKILL.md +56 -0
- package/plugins/just-vibe/skills/backend-service/SKILL.md +56 -0
- package/plugins/just-vibe/skills/brainstorm/SKILL.md +56 -0
- package/plugins/just-vibe/skills/build/SKILL.md +56 -0
- package/plugins/just-vibe/skills/challenge/SKILL.md +56 -0
- package/plugins/just-vibe/skills/checkpoint/SKILL.md +59 -0
- package/plugins/just-vibe/skills/ci/SKILL.md +56 -0
- package/plugins/just-vibe/skills/cleanup/SKILL.md +56 -0
- package/plugins/just-vibe/skills/compare/SKILL.md +56 -0
- package/plugins/just-vibe/skills/copy/SKILL.md +56 -0
- package/plugins/just-vibe/skills/coverage/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-backfill/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-contract/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-incremental/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-lineage/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-pipeline/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-profile/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-quality/SKILL.md +56 -0
- package/plugins/just-vibe/skills/data-reconcile/SKILL.md +56 -0
- package/plugins/just-vibe/skills/db-access/SKILL.md +56 -0
- package/plugins/just-vibe/skills/db-explain/SKILL.md +56 -0
- package/plugins/just-vibe/skills/db-index/SKILL.md +56 -0
- package/plugins/just-vibe/skills/db-integrity/SKILL.md +57 -0
- package/plugins/just-vibe/skills/db-locks/SKILL.md +56 -0
- package/plugins/just-vibe/skills/db-migrate/SKILL.md +63 -0
- package/plugins/just-vibe/skills/db-query/SKILL.md +56 -0
- package/plugins/just-vibe/skills/db-schema/SKILL.md +56 -0
- package/plugins/just-vibe/skills/debug/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decide/SKILL.md +57 -0
- package/plugins/just-vibe/skills/decision-adr/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decision-buy-build/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decision-matrix/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decision-premortem/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decision-reversible/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decision-revisit/SKILL.md +56 -0
- package/plugins/just-vibe/skills/decision-spike/SKILL.md +58 -0
- package/plugins/just-vibe/skills/deploy/SKILL.md +56 -0
- package/plugins/just-vibe/skills/deps/SKILL.md +56 -0
- package/plugins/just-vibe/skills/design/SKILL.md +56 -0
- package/plugins/just-vibe/skills/do/SKILL.md +8 -0
- package/plugins/just-vibe/skills/docs/SKILL.md +56 -0
- package/plugins/just-vibe/skills/doctor/SKILL.md +58 -0
- package/plugins/just-vibe/skills/explain/SKILL.md +56 -0
- package/plugins/just-vibe/skills/fix/SKILL.md +57 -0
- package/plugins/just-vibe/skills/git-bisect/SKILL.md +58 -0
- package/plugins/just-vibe/skills/git-commit/SKILL.md +59 -0
- package/plugins/just-vibe/skills/git-conflicts/SKILL.md +58 -0
- package/plugins/just-vibe/skills/git-diff/SKILL.md +58 -0
- package/plugins/just-vibe/skills/git-recover/SKILL.md +58 -0
- package/plugins/just-vibe/skills/git-split/SKILL.md +58 -0
- package/plugins/just-vibe/skills/git-status/SKILL.md +58 -0
- package/plugins/just-vibe/skills/git-worktree/SKILL.md +58 -0
- package/plugins/just-vibe/skills/github-actions/SKILL.md +64 -0
- package/plugins/just-vibe/skills/github-address-review/SKILL.md +58 -0
- package/plugins/just-vibe/skills/github-fix-ci/SKILL.md +63 -0
- package/plugins/just-vibe/skills/github-issue/SKILL.md +58 -0
- package/plugins/just-vibe/skills/github-pr/SKILL.md +63 -0
- package/plugins/just-vibe/skills/github-release/SKILL.md +58 -0
- package/plugins/just-vibe/skills/github-review/SKILL.md +58 -0
- package/plugins/just-vibe/skills/github-triage/SKILL.md +58 -0
- package/plugins/just-vibe/skills/handoff/SKILL.md +62 -0
- package/plugins/just-vibe/skills/help/SKILL.md +63 -0
- package/plugins/just-vibe/skills/integrate/SKILL.md +56 -0
- package/plugins/just-vibe/skills/learn/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-cost/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-evals/SKILL.md +58 -0
- package/plugins/just-vibe/skills/llm-injection/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-prompt/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-rag/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-retrieval/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-structured/SKILL.md +56 -0
- package/plugins/just-vibe/skills/llm-tools/SKILL.md +58 -0
- package/plugins/just-vibe/skills/map/SKILL.md +56 -0
- package/plugins/just-vibe/skills/match/SKILL.md +56 -0
- package/plugins/just-vibe/skills/migrate/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-ablation/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-baseline/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-batch/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-calibrate/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-dataset/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-dataset-version/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-debug-training/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-drift/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-error-analysis/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-evaluate/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-experiments/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-explain/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-features/SKILL.md +59 -0
- package/plugins/just-vibe/skills/ml-frame/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-imbalance/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-inference-perf/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-labels/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-leakage/SKILL.md +64 -0
- package/plugins/just-vibe/skills/ml-monitor/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-package/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-parity/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-report/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-reproduce/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-robustness/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-rollout/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-serving/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-slices/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-split/SKILL.md +58 -0
- package/plugins/just-vibe/skills/ml-threshold/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-train/SKILL.md +62 -0
- package/plugins/just-vibe/skills/ml-training-cost/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ml-tune/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-alerts/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-container/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-incident/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-logs/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-observability/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-postmortem/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-restore/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ops-runbook/SKILL.md +56 -0
- package/plugins/just-vibe/skills/orient/SKILL.md +58 -0
- package/plugins/just-vibe/skills/perf/SKILL.md +56 -0
- package/plugins/just-vibe/skills/plan/SKILL.md +56 -0
- package/plugins/just-vibe/skills/polish/SKILL.md +56 -0
- package/plugins/just-vibe/skills/pr/SKILL.md +58 -0
- package/plugins/just-vibe/skills/profile/SKILL.md +66 -0
- package/plugins/just-vibe/skills/profiles/SKILL.md +58 -0
- package/plugins/just-vibe/skills/react-async/SKILL.md +57 -0
- package/plugins/just-vibe/skills/react-audit/SKILL.md +56 -0
- package/plugins/just-vibe/skills/react-component/SKILL.md +65 -0
- package/plugins/just-vibe/skills/react-effects/SKILL.md +58 -0
- package/plugins/just-vibe/skills/react-forms/SKILL.md +56 -0
- package/plugins/just-vibe/skills/react-hydration/SKILL.md +57 -0
- package/plugins/just-vibe/skills/react-rerenders/SKILL.md +56 -0
- package/plugins/just-vibe/skills/react-state/SKILL.md +56 -0
- package/plugins/just-vibe/skills/refactor/SKILL.md +56 -0
- package/plugins/just-vibe/skills/release/SKILL.md +58 -0
- package/plugins/just-vibe/skills/remember/SKILL.md +70 -0
- package/plugins/just-vibe/skills/repro/SKILL.md +56 -0
- package/plugins/just-vibe/skills/research/SKILL.md +56 -0
- package/plugins/just-vibe/skills/responsive/SKILL.md +8 -0
- package/plugins/just-vibe/skills/resume/SKILL.md +63 -0
- package/plugins/just-vibe/skills/review/SKILL.md +56 -0
- package/plugins/just-vibe/skills/scope/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-authz/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-config/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-dependencies/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-fix/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-inputs/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-secrets/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-threat-model/SKILL.md +56 -0
- package/plugins/just-vibe/skills/security-uploads/SKILL.md +56 -0
- package/plugins/just-vibe/skills/setup/SKILL.md +60 -0
- package/plugins/just-vibe/skills/skill/SKILL.md +56 -0
- package/plugins/just-vibe/skills/spec/SKILL.md +56 -0
- package/plugins/just-vibe/skills/tasks/SKILL.md +56 -0
- package/plugins/just-vibe/skills/teach/SKILL.md +63 -0
- package/plugins/just-vibe/skills/teach-test/SKILL.md +65 -0
- package/plugins/just-vibe/skills/test/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-e2e/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-fixtures/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-flaky/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-integration/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-load/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-property/SKILL.md +56 -0
- package/plugins/just-vibe/skills/test-regression/SKILL.md +57 -0
- package/plugins/just-vibe/skills/test-unit/SKILL.md +56 -0
- package/plugins/just-vibe/skills/tools/SKILL.md +64 -0
- package/plugins/just-vibe/skills/trace/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-accessibility/SKILL.md +57 -0
- package/plugins/just-vibe/skills/ui-audit/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-flow/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-motion/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-responsive/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-states/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-system/SKILL.md +56 -0
- package/plugins/just-vibe/skills/ui-visual-diff/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vercel-audit/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vercel-build-fix/SKILL.md +63 -0
- package/plugins/just-vibe/skills/vercel-env/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vercel-performance/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vercel-preview/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vercel-release-check/SKILL.md +57 -0
- package/plugins/just-vibe/skills/vercel-routing/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vercel-runtime/SKILL.md +61 -0
- package/plugins/just-vibe/skills/verify/SKILL.md +59 -0
- package/plugins/just-vibe/skills/vite-assets/SKILL.md +57 -0
- package/plugins/just-vibe/skills/vite-bundle/SKILL.md +58 -0
- package/plugins/just-vibe/skills/vite-chunks/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vite-config/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vite-env/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vite-hmr/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vite-setup/SKILL.md +56 -0
- package/plugins/just-vibe/skills/vite-upgrade/SKILL.md +56 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { isDirectRun } from './lib/entrypoint.mjs';
|
|
7
|
+
import { managedSource, stageBundle, inspectManaged, validateBundle } from './lib/bundle.mjs';
|
|
8
|
+
import { commandInvocation } from './lib/command.mjs';
|
|
9
|
+
|
|
10
|
+
export const REPOSITORY = 'Zachshotamartin/just-vibe';
|
|
11
|
+
export const MARKETPLACE = 'just-vibe';
|
|
12
|
+
export const PLUGIN = 'just-vibe@just-vibe';
|
|
13
|
+
const VERSION = JSON.parse(readFileSync(new URL('../.codex-plugin/plugin.json', import.meta.url))).version;
|
|
14
|
+
const COMMANDS = new Set(['setup', 'update', 'uninstall', 'doctor', 'help']);
|
|
15
|
+
const SCOPES = new Set(['user', 'project', 'local']);
|
|
16
|
+
|
|
17
|
+
export const HELP = `just-vibe ${VERSION}
|
|
18
|
+
|
|
19
|
+
Usage: just-vibe <command> [options]
|
|
20
|
+
|
|
21
|
+
Commands:
|
|
22
|
+
setup Install through the selected host's native plugin manager
|
|
23
|
+
update Refresh this marketplace and update its installed plugin
|
|
24
|
+
uninstall Remove this plugin; keep its marketplace and persistent data
|
|
25
|
+
doctor Check prerequisites, marketplace source, and installation
|
|
26
|
+
help Show this help
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
--target codex|claude Host to configure (default: codex)
|
|
30
|
+
--scope user|project|local
|
|
31
|
+
Claude installation scope (default: user)
|
|
32
|
+
--local Register this persistent repository checkout
|
|
33
|
+
--github Register the GitHub repository (requires access)
|
|
34
|
+
--dry-run Print steps without running any host commands
|
|
35
|
+
--version Print the package version
|
|
36
|
+
--help Show this help
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
pnpm dlx just-vibe@latest setup
|
|
40
|
+
pnpm dlx just-vibe@latest setup --target claude
|
|
41
|
+
node bin/just-vibe.mjs setup --local --dry-run
|
|
42
|
+
|
|
43
|
+
Requires Node.js 22+ and the selected host CLI with plugin support.
|
|
44
|
+
Default: install bundled files into ~/.just-vibe (override JUST_VIBE_HOME).
|
|
45
|
+
No GitHub access is needed for bundled installs. --github also requires Git.
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
export function parseArgs(args) {
|
|
49
|
+
const options = { command: 'help', target: 'codex', scope: 'user', local: false, github: false, dryRun: false };
|
|
50
|
+
let commandSeen = false;
|
|
51
|
+
let scopeSeen = false;
|
|
52
|
+
const flags = new Set();
|
|
53
|
+
for (let i = 0; i < args.length; i++) {
|
|
54
|
+
const arg = args[i];
|
|
55
|
+
if (arg === '--help' || arg === '-h') return { ...options, command: 'help' };
|
|
56
|
+
if (arg === '--version') return { ...options, command: 'version' };
|
|
57
|
+
if (['--target', '--scope', '--local', '--github', '--dry-run'].includes(arg)) {
|
|
58
|
+
if (flags.has(arg)) throw new Error(`Duplicate option: ${arg}`);
|
|
59
|
+
flags.add(arg);
|
|
60
|
+
if (arg === '--local') options.local = true;
|
|
61
|
+
else if (arg === '--github') options.github = true;
|
|
62
|
+
else if (arg === '--dry-run') options.dryRun = true;
|
|
63
|
+
else {
|
|
64
|
+
const value = args[++i];
|
|
65
|
+
if (!value || value.startsWith('-')) throw new Error(`${arg} requires a value.`);
|
|
66
|
+
options[arg.slice(2)] = value;
|
|
67
|
+
if (arg === '--scope') scopeSeen = true;
|
|
68
|
+
}
|
|
69
|
+
} else if (!arg.startsWith('-') && !commandSeen) {
|
|
70
|
+
if (!COMMANDS.has(arg)) throw new Error(`Unknown command: ${arg}. Run just-vibe help.`);
|
|
71
|
+
options.command = arg;
|
|
72
|
+
commandSeen = true;
|
|
73
|
+
} else throw new Error(`Unexpected argument: ${arg}`);
|
|
74
|
+
}
|
|
75
|
+
if (!['codex', 'claude'].includes(options.target)) throw new Error('--target must be codex or claude.');
|
|
76
|
+
if (!SCOPES.has(options.scope)) throw new Error('--scope must be user, project, or local.');
|
|
77
|
+
if (options.local && options.github) throw new Error('--local and --github cannot be combined.');
|
|
78
|
+
if (scopeSeen && options.target !== 'claude') throw new Error('--scope applies only to Claude Code.');
|
|
79
|
+
return options;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Pass arguments separately: user input is never evaluated as shell code.
|
|
83
|
+
export function execute(binary, args) {
|
|
84
|
+
const [program, parameters] = commandInvocation(binary, args);
|
|
85
|
+
const result = spawnSync(program, parameters, {
|
|
86
|
+
encoding: 'utf8', shell: false, timeout: 120_000, maxBuffer: 8 * 1024 * 1024,
|
|
87
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
88
|
+
});
|
|
89
|
+
if (result.error?.code === 'ENOENT') {
|
|
90
|
+
throw new Error(`${binary} was not found on PATH. Install it, then open a new terminal and retry.`);
|
|
91
|
+
}
|
|
92
|
+
if (result.error) throw new Error(`${binary} could not finish: ${result.error.message}`);
|
|
93
|
+
if (result.status !== 0) {
|
|
94
|
+
throw new Error(`${binary} ${args.join(' ')} failed${result.signal ? ` (${result.signal})` : ''}.\n${(result.stderr || result.stdout || '').trim()}`);
|
|
95
|
+
}
|
|
96
|
+
return result.stdout.trim();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function json(output, label) {
|
|
100
|
+
try { return JSON.parse(output); }
|
|
101
|
+
catch { throw new Error(`${label} did not return valid JSON. Update the host CLI and retry.`); }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function sourceFor(options) {
|
|
105
|
+
if (options.github) return REPOSITORY;
|
|
106
|
+
if (!options.local) return managedSource(options.target);
|
|
107
|
+
const root = fileURLToPath(new URL('../../../', import.meta.url));
|
|
108
|
+
for (const manifest of ['.agents/plugins/marketplace.json', '.claude-plugin/marketplace.json']) {
|
|
109
|
+
try {
|
|
110
|
+
if (JSON.parse(readFileSync(resolve(root, manifest))).name !== MARKETPLACE) throw new Error();
|
|
111
|
+
} catch {
|
|
112
|
+
throw new Error('--local requires a complete just-vibe repository checkout. Use the default bundled installation or --github otherwise.');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return root;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function canonicalSource(value, local) {
|
|
119
|
+
if (typeof value !== 'string') return null;
|
|
120
|
+
if (local) {
|
|
121
|
+
try { return realpathSync(value); } catch { return resolve(value); }
|
|
122
|
+
}
|
|
123
|
+
return value.replace(/^git\+/, '').replace(/^https?:\/\/github\.com\//i, '')
|
|
124
|
+
.replace(/^git@github\.com:/i, '').replace(/\/$/, '').replace(/\.git$/, '').toLowerCase();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function marketplaceMatches(entry, options, source) {
|
|
128
|
+
if (options.target === 'codex') {
|
|
129
|
+
const actual = entry.marketplaceSource;
|
|
130
|
+
if (!actual || actual.sourceType !== (options.github ? 'git' : 'local')) return false;
|
|
131
|
+
return canonicalSource(actual.source, !options.github) === canonicalSource(source, !options.github);
|
|
132
|
+
}
|
|
133
|
+
if (!options.github) {
|
|
134
|
+
return ['directory', 'local'].includes(entry.source)
|
|
135
|
+
&& canonicalSource(entry.path || entry.installLocation, true) === canonicalSource(source, true);
|
|
136
|
+
}
|
|
137
|
+
return ['github', 'git'].includes(entry.source)
|
|
138
|
+
&& canonicalSource(entry.repo || entry.url, false) === canonicalSource(source, false);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function inventory(options, run) {
|
|
142
|
+
const host = options.target;
|
|
143
|
+
const marketplaces = json(run(host, ['plugin', 'marketplace', 'list', '--json']), `${host} marketplace list`);
|
|
144
|
+
const plugins = json(run(host, ['plugin', 'list', '--json']), `${host} plugin list`);
|
|
145
|
+
const marketList = host === 'codex' ? marketplaces.marketplaces : marketplaces;
|
|
146
|
+
const pluginList = host === 'codex' ? plugins.installed : plugins;
|
|
147
|
+
if (!Array.isArray(marketList) || !Array.isArray(pluginList)) {
|
|
148
|
+
throw new Error('Unsupported host inventory format. Update just-vibe or the host CLI; no changes were made.');
|
|
149
|
+
}
|
|
150
|
+
const matches = marketList.filter(entry => entry.name === MARKETPLACE);
|
|
151
|
+
if (matches.length > 1) throw new Error('Multiple just-vibe marketplaces found. Resolve the ambiguity in the host before retrying.');
|
|
152
|
+
const installed = pluginList.filter(entry => (entry.pluginId || entry.id) === PLUGIN);
|
|
153
|
+
if (host === 'claude' && installed.some(entry => entry.scope !== options.scope)) {
|
|
154
|
+
throw new Error('just-vibe is also installed at another Claude scope. Use that scope or resolve it with claude plugin list before proceeding.');
|
|
155
|
+
}
|
|
156
|
+
return { marketplace: matches[0], installed: installed[0] };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function mutationSteps(options, state, source) {
|
|
160
|
+
const host = options.target;
|
|
161
|
+
const scope = host === 'claude' ? ['--scope', options.scope] : [];
|
|
162
|
+
const steps = [];
|
|
163
|
+
if (options.command === 'uninstall') {
|
|
164
|
+
if (state.installed) steps.push(host === 'codex'
|
|
165
|
+
? ['plugin', 'remove', PLUGIN]
|
|
166
|
+
: ['plugin', 'uninstall', PLUGIN, ...scope, '--keep-data']);
|
|
167
|
+
return steps;
|
|
168
|
+
}
|
|
169
|
+
if (options.command === 'doctor') return steps;
|
|
170
|
+
if (!state.marketplace) steps.push(['plugin', 'marketplace', 'add', source]);
|
|
171
|
+
else if (options.command === 'update' && (options.github || host === 'claude')) {
|
|
172
|
+
steps.push(['plugin', 'marketplace', host === 'codex' ? 'upgrade' : 'update', MARKETPLACE]);
|
|
173
|
+
}
|
|
174
|
+
if (!state.installed || options.command === 'update') {
|
|
175
|
+
steps.push(host === 'codex' ? ['plugin', 'add', PLUGIN]
|
|
176
|
+
: ['plugin', state.installed ? 'update' : 'install', PLUGIN, ...scope]);
|
|
177
|
+
} else if (state.installed.enabled === false) {
|
|
178
|
+
steps.push(host === 'claude' ? ['plugin', 'enable', PLUGIN, ...scope] : ['plugin', 'add', PLUGIN]);
|
|
179
|
+
}
|
|
180
|
+
return steps;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function display(host, args) {
|
|
184
|
+
return [host, ...args].map(arg => /^[a-zA-Z0-9_./:@+-]+$/.test(arg) ? arg : JSON.stringify(arg)).join(' ');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function install(options, { run = execute, log = console.log, source = sourceFor(options), prepare = stageBundle } = {}) {
|
|
188
|
+
if (options.dryRun) {
|
|
189
|
+
log('Dry run — no commands executed; installed state has not been inspected.');
|
|
190
|
+
log(`Target: ${options.target}${options.target === 'claude' ? ` (${options.scope} scope)` : ''}`);
|
|
191
|
+
log(`Expected marketplace source: ${source}`);
|
|
192
|
+
log('Preflight: host CLI, native plugin subcommands, marketplace and plugin inventory.');
|
|
193
|
+
if (!options.github && !options.local && ['setup', 'update'].includes(options.command)) log(`Copy bundled plugin files to ${source} after preflight (setup preserves an existing copy; update replaces it).`);
|
|
194
|
+
if (options.command === 'doctor') log('Inspect installation and report health.');
|
|
195
|
+
else if (options.command === 'uninstall') {
|
|
196
|
+
for (const args of mutationSteps(options, { installed: {} }, source)) log(display(options.target, args));
|
|
197
|
+
log('Only runs if installed. Marketplace and persistent plugin data are retained.');
|
|
198
|
+
} else {
|
|
199
|
+
log('If missing, register the marketplace and install the plugin:');
|
|
200
|
+
for (const args of mutationSteps({ ...options, command: 'setup' }, {}, source)) log(display(options.target, args));
|
|
201
|
+
if (options.command === 'update') {
|
|
202
|
+
log('If already installed, refresh only this marketplace and update this plugin:');
|
|
203
|
+
for (const args of mutationSteps(options, { marketplace: {}, installed: {} }, source)) log(display(options.target, args));
|
|
204
|
+
} else log('Already installed: leave it unchanged (or enable it if disabled). Use update for a new version.');
|
|
205
|
+
}
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const host = options.target;
|
|
210
|
+
if (options.github) log(run('git', ['--version']));
|
|
211
|
+
log(run(host, ['--version']));
|
|
212
|
+
// Complete preflight, including command support, before the first mutation.
|
|
213
|
+
const state = inventory(options, run);
|
|
214
|
+
if (state.marketplace && !marketplaceMatches(state.marketplace, options, source)) {
|
|
215
|
+
throw new Error('A marketplace named just-vibe already points to a different or unrecognized source. No changes were made. Use --github for an existing GitHub installation or --local for a checkout. To change channels, uninstall using the old flag and remove its marketplace with the host CLI first.');
|
|
216
|
+
}
|
|
217
|
+
if (state.installed && !state.marketplace) {
|
|
218
|
+
throw new Error('just-vibe is installed without its expected marketplace. Repair its source in the host CLI before continuing.');
|
|
219
|
+
}
|
|
220
|
+
if (options.command === 'doctor') {
|
|
221
|
+
if (!state.marketplace || !state.installed) throw new Error('just-vibe is not fully installed. Run setup for this target.');
|
|
222
|
+
if (state.installed.enabled === false) throw new Error('just-vibe is installed but disabled. Run setup to enable it.');
|
|
223
|
+
if (!options.github && !options.local) {
|
|
224
|
+
if (!inspectManaged(source)) throw new Error('Managed marketplace files are missing. Run setup to restore them.');
|
|
225
|
+
const version = validateBundle(source);
|
|
226
|
+
if (state.installed.version !== version) throw new Error(`Installed plugin version differs from the managed source (${version}). Run update to finish applying it.`);
|
|
227
|
+
}
|
|
228
|
+
log(`Healthy: ${PLUGIN}${state.installed.version ? ` v${state.installed.version}` : ''}.`);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const steps = mutationSteps(options, state, source);
|
|
232
|
+
for (const args of steps) run(host, [...args.slice(0, args[1] === 'marketplace' ? 3 : 2), '--help']);
|
|
233
|
+
let expectedVersion;
|
|
234
|
+
if (!options.github && !options.local && ['setup', 'update'].includes(options.command)) {
|
|
235
|
+
const version = prepare(source, { replace: options.command === 'update' });
|
|
236
|
+
expectedVersion = version;
|
|
237
|
+
log(`Bundled source: v${version} at ${source}.`);
|
|
238
|
+
}
|
|
239
|
+
for (const args of steps) {
|
|
240
|
+
log(`> ${display(host, args)}`);
|
|
241
|
+
try {
|
|
242
|
+
const output = run(host, args);
|
|
243
|
+
if (output) log(output);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
throw new Error(`${error.message}\nStopped at this step. Earlier native steps may have completed; no unrelated settings were changed. Fix the error and rerun the command.`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const final = inventory(options, run);
|
|
249
|
+
if (options.command === 'uninstall') {
|
|
250
|
+
if (final.installed) throw new Error('The host still reports just-vibe installed; inspect its plugin list.');
|
|
251
|
+
log('just-vibe is uninstalled. Marketplace registration and persistent plugin data were retained.');
|
|
252
|
+
} else {
|
|
253
|
+
if (!final.marketplace || !marketplaceMatches(final.marketplace, options, source)
|
|
254
|
+
|| !final.installed || final.installed.enabled === false) {
|
|
255
|
+
throw new Error('The host did not report an enabled just-vibe installation. Run doctor and inspect the host plugin list.');
|
|
256
|
+
}
|
|
257
|
+
if (expectedVersion && final.installed.version !== expectedVersion) throw new Error(`Host still reports v${final.installed.version || 'unknown'} instead of bundled v${expectedVersion}. Run update and inspect its plugin list.`);
|
|
258
|
+
log(`Ready: ${PLUGIN}. Start a new conversation to load the skills.`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function main(args, dependencies = {}) {
|
|
263
|
+
const log = dependencies.log || console.log;
|
|
264
|
+
const error = dependencies.error || console.error;
|
|
265
|
+
try {
|
|
266
|
+
const options = parseArgs(args);
|
|
267
|
+
if (options.command === 'help') { log(HELP); return 0; }
|
|
268
|
+
if (options.command === 'version') { log(VERSION); return 0; }
|
|
269
|
+
if (Number(process.versions.node.split('.')[0]) < 22) throw new Error('Node.js 22 or newer is required.');
|
|
270
|
+
install(options, dependencies);
|
|
271
|
+
return 0;
|
|
272
|
+
} catch (failure) {
|
|
273
|
+
error(`just-vibe: ${failure.message}`);
|
|
274
|
+
return 1;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (isDirectRun(import.meta.url)) {
|
|
279
|
+
process.exitCode = main(process.argv.slice(2));
|
|
280
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, openSync, closeSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join, relative, extname, resolve, isAbsolute } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { atomicJson, digest, within, readJson, projectRoot, fingerprint, privateName } from './storage.mjs';
|
|
5
|
+
import { gitRead } from './project.mjs';
|
|
6
|
+
import { runCommand, redact } from './process.mjs';
|
|
7
|
+
|
|
8
|
+
const configPath = '.just-vibe/automation.json';
|
|
9
|
+
function validate(config) {
|
|
10
|
+
if (!config || config.schemaVersion !== 1 || typeof config.enabled !== 'boolean' || typeof config.saveSummary !== 'boolean'
|
|
11
|
+
|| !Array.isArray(config.checks) || !Array.isArray(config.formatters) || config.checks.length + config.formatters.length > 4
|
|
12
|
+
|| Object.keys(config).some(k => !['schemaVersion', 'revision', 'enabled', 'saveSummary', 'checks', 'formatters'].includes(k))) throw Error('Invalid automation configuration.');
|
|
13
|
+
const names = new Set();
|
|
14
|
+
for (const [kind, list] of [['check', config.checks], ['formatter', config.formatters]]) for (const item of list) {
|
|
15
|
+
if (!item || typeof item.name !== 'string' || !/^[a-z0-9-]{1,64}$/.test(item.name) || names.has(item.name)
|
|
16
|
+
|| !Array.isArray(item.command) || !item.command.length || item.command.length > 40 || item.command.some(v => typeof v !== 'string' || !v || v.includes('\0') || v.length > 2000)
|
|
17
|
+
|| !Number.isInteger(item.timeoutMs) || item.timeoutMs < 100 || item.timeoutMs > 15000
|
|
18
|
+
|| !Array.isArray(item.extensions) || item.extensions.some(v => !/^\.[a-z0-9]+$/.test(v))
|
|
19
|
+
|| Object.keys(item).some(k => !['name', 'command', 'timeoutMs', 'extensions'].includes(k))) throw Error('Automation entries require unique names, argv commands, extensions and a 100–15000 ms timeout.');
|
|
20
|
+
if (kind === 'formatter' && item.command.filter(v => v === '{file}').length !== 1) throw Error('A formatter must include exactly one {file} argument.');
|
|
21
|
+
if (kind === 'check' && item.command.some(v => v.includes('{file}'))) throw Error('Stop checks cannot use a file placeholder.');
|
|
22
|
+
names.add(item.name);
|
|
23
|
+
}
|
|
24
|
+
return config;
|
|
25
|
+
}
|
|
26
|
+
function config(root) {
|
|
27
|
+
const path = within(root, configPath);
|
|
28
|
+
return existsSync(path) ? validate(readJson(path)) : null;
|
|
29
|
+
}
|
|
30
|
+
function trustDirectory(home) {
|
|
31
|
+
const root = home || process.env.JUST_VIBE_HOME || join(homedir(), '.just-vibe');
|
|
32
|
+
mkdirSync(root, { recursive: true });
|
|
33
|
+
return projectRoot(root);
|
|
34
|
+
}
|
|
35
|
+
function trustPath(root) { return `automation-trust/${digest(projectRoot(root))}.json`; }
|
|
36
|
+
function trustRecord(root, home) {
|
|
37
|
+
const base = home || process.env.JUST_VIBE_HOME || join(homedir(), '.just-vibe');
|
|
38
|
+
if (!existsSync(base)) return null;
|
|
39
|
+
const path = within(base, trustPath(root));
|
|
40
|
+
return existsSync(path) ? readJson(path) : null;
|
|
41
|
+
}
|
|
42
|
+
const configHash = c => digest(JSON.stringify(c));
|
|
43
|
+
|
|
44
|
+
export function manageHooks(root, operation, payload = {}, { home } = {}) {
|
|
45
|
+
root = projectRoot(root);
|
|
46
|
+
const current = config(root);
|
|
47
|
+
if (operation === 'configure') {
|
|
48
|
+
validate(payload);
|
|
49
|
+
if (!Number.isInteger(payload.revision) || payload.revision < 0) throw Error('Supply current revision, or 0 for a new configuration.');
|
|
50
|
+
return atomicJson(root, configPath, payload, payload.revision);
|
|
51
|
+
}
|
|
52
|
+
if (operation === 'status') return { configuration: current, trusted: Boolean(current && trustRecord(root, home)?.configHash === configHash(current)), note: 'Host hook support and native trust are separate. Configuration alone never enables command execution.' };
|
|
53
|
+
if (operation === 'trust') {
|
|
54
|
+
if (!current) throw Error('Configure this project before trusting its commands.');
|
|
55
|
+
const base = trustDirectory(home), previous = trustRecord(root, base);
|
|
56
|
+
return atomicJson(base, trustPath(root), { schemaVersion: 1, root, configHash: configHash(current), trustedAt: new Date().toISOString() }, previous?.revision || 0);
|
|
57
|
+
}
|
|
58
|
+
if (operation === 'untrust') {
|
|
59
|
+
const previous = trustRecord(root, home);
|
|
60
|
+
if (previous) unlinkSync(within(trustDirectory(home), trustPath(root)));
|
|
61
|
+
return { trusted: false };
|
|
62
|
+
}
|
|
63
|
+
if (operation === 'disable') {
|
|
64
|
+
if (!current) return { enabled: false };
|
|
65
|
+
return atomicJson(root, configPath, { ...current, enabled: false }, current.revision);
|
|
66
|
+
}
|
|
67
|
+
if (operation === 'recover') {
|
|
68
|
+
const lock = within(root, '.just-vibe/automation/active.lock');
|
|
69
|
+
if (!existsSync(lock)) return { recovered: false };
|
|
70
|
+
const owner = readJson(lock);
|
|
71
|
+
if (!Number.isInteger(owner.pid) || owner.pid < 1) throw Error('Invalid lock owner; inspect the lock manually.');
|
|
72
|
+
try { process.kill(owner.pid, 0); throw Error('Automation owner is still running.'); }
|
|
73
|
+
catch (error) { if (error.code !== 'ESRCH') throw error; }
|
|
74
|
+
unlinkSync(lock);
|
|
75
|
+
return { recovered: true };
|
|
76
|
+
}
|
|
77
|
+
throw Error(`Unknown hooks operation: ${operation}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function touchedFiles(event) {
|
|
81
|
+
const direct = event.tool_input?.file_path || event.tool_input?.path;
|
|
82
|
+
if (typeof direct === 'string') return [direct];
|
|
83
|
+
if (event.tool_name === 'apply_patch') return [...String(event.tool_input?.command || '').matchAll(/^\*\*\* (?:Add|Update) File: (.+)$/gm)].map(m => m[1]);
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function handleHook(event, { home, run = runCommand } = {}) {
|
|
88
|
+
if (!event || !['Stop', 'PostToolUse'].includes(event.hook_event_name) || typeof event.cwd !== 'string') return { skipped: 'unsupported-event' };
|
|
89
|
+
// Use the exact configured project, or its Git root when launched in a subdirectory.
|
|
90
|
+
let root = projectRoot(event.cwd);
|
|
91
|
+
if (!existsSync(within(root, configPath))) root = gitRead(root, ['rev-parse', '--show-toplevel']) || root;
|
|
92
|
+
const selected = config(root);
|
|
93
|
+
if (!selected?.enabled) return { skipped: 'disabled' };
|
|
94
|
+
if (trustRecord(root, home)?.configHash !== configHash(selected)) return { skipped: 'untrusted', message: 'just-vibe automation configuration changed or is untrusted. Review it with hooks status, then use hooks trust if intended.' };
|
|
95
|
+
if (event.stop_hook_active) return { skipped: 'stop-loop' };
|
|
96
|
+
const directory = within(root, '.just-vibe/automation'); mkdirSync(directory, { recursive: true });
|
|
97
|
+
const lock = join(directory, 'active.lock'); let handle;
|
|
98
|
+
try { handle = openSync(lock, 'wx', 0o600); writeFileSync(handle, JSON.stringify({ pid: process.pid, startedAt: new Date().toISOString() })); } catch { return { skipped: 'busy' }; }
|
|
99
|
+
try {
|
|
100
|
+
const snapshot = fingerprint(root), key = event.hook_event_name === 'Stop' ? 'stop' : 'edit';
|
|
101
|
+
const statePath = `.just-vibe/automation/${key}.json`, path = within(root, statePath);
|
|
102
|
+
const previous = existsSync(path) ? readJson(path) : null;
|
|
103
|
+
const candidates = key === 'edit' ? touchedFiles(event).map(file => within(root, resolve(projectRoot(event.cwd), isAbsolute(file) ? relative(resolve(event.cwd), file) : file))) : [];
|
|
104
|
+
const inputHash = digest(JSON.stringify({ snapshot, config: configHash(selected), candidates }));
|
|
105
|
+
const results = [];
|
|
106
|
+
if (snapshot.partial || previous?.inputHash !== inputHash) {
|
|
107
|
+
const deadline = Date.now() + 20000;
|
|
108
|
+
if (key === 'stop') {
|
|
109
|
+
for (const check of selected.checks) {
|
|
110
|
+
const time = Math.min(check.timeoutMs, deadline - Date.now());
|
|
111
|
+
if (time <= 0) { results.push({ name: check.name, result: 'skipped', reason: 'Total hook time budget exhausted' }); continue; }
|
|
112
|
+
const result = await run(check.command, { cwd: root, timeoutMs: time, maxBytes: 64000 });
|
|
113
|
+
results.push({ name: check.name, result: result.status === 0 && !result.error && !result.timedOut && !result.truncated ? 'passed' : 'failed', exitCode: result.status, timedOut: result.timedOut, truncated: result.truncated, output: redact(result.error || result.stderr || result.stdout).slice(-2000) });
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
const staged = gitRead(root, ['diff', '--cached', '--name-only', '-z']);
|
|
117
|
+
for (const file of candidates.slice(0, 10)) {
|
|
118
|
+
if (!existsSync(file) || privateName(file.split(/[\\/]/).pop())) continue;
|
|
119
|
+
const rel = relative(root, file).replaceAll('\\', '/');
|
|
120
|
+
for (const formatter of selected.formatters.filter(f => !f.extensions.length || f.extensions.includes(extname(file)))) {
|
|
121
|
+
if (staged === null || staged.split('\0').includes(rel)) { results.push({ name: formatter.name, file: rel, result: 'skipped', reason: 'Cannot safely format a staged file or a file without Git index evidence' }); continue; }
|
|
122
|
+
const time = Math.min(formatter.timeoutMs, deadline - Date.now());
|
|
123
|
+
if (time <= 0) { results.push({ name: formatter.name, result: 'skipped', reason: 'Total hook time budget exhausted' }); continue; }
|
|
124
|
+
const result = await run(formatter.command.map(arg => arg === '{file}' ? file : arg), { cwd: root, timeoutMs: time, maxBytes: 64000 });
|
|
125
|
+
results.push({ name: formatter.name, file: rel, result: result.status === 0 && !result.error && !result.timedOut && !result.truncated ? 'passed' : 'failed', output: redact(result.error || result.stderr || result.stdout).slice(-2000) });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// A formatter intentionally changes its input. A check must not certify a later tree.
|
|
130
|
+
const finalSnapshot = fingerprint(root);
|
|
131
|
+
const changedDuringChecks = key === 'stop' && JSON.stringify(snapshot) !== JSON.stringify(finalSnapshot);
|
|
132
|
+
if (changedDuringChecks) for (const result of results) if (result.result === 'passed') { result.result = 'stale'; result.reason = 'Project changed while checks ran; verify the current tree.'; }
|
|
133
|
+
atomicJson(root, statePath, { schemaVersion: 1, inputHash: key === 'edit' ? digest(JSON.stringify({ snapshot: finalSnapshot, config: configHash(selected), candidates })) : inputHash, observedAt: new Date().toISOString(), changedDuringChecks, results }, previous?.revision || 0);
|
|
134
|
+
}
|
|
135
|
+
if (key === 'stop' && selected.saveSummary) {
|
|
136
|
+
const file = '.just-vibe/automation/continuation.json', previousSummary = existsSync(within(root, file)) ? readJson(within(root, file)) : null;
|
|
137
|
+
const summary = redact(event.last_assistant_message || '').slice(0, 6000);
|
|
138
|
+
if (previousSummary?.summary !== summary || previousSummary?.snapshot?.content !== snapshot.content || previousSummary?.snapshot?.head !== snapshot.head) atomicJson(root, file, { schemaVersion: 1, kind: 'automatic-continuation', observedAt: new Date().toISOString(), snapshot: fingerprint(root), summary, note: 'Last response and filesystem identity only; verify task state and checks before continuing.' }, previousSummary?.revision || 0);
|
|
139
|
+
}
|
|
140
|
+
return { results, ...(results.some(r => ['failed', 'stale'].includes(r.result)) ? { message: `just-vibe checks need attention: ${results.filter(r => ['failed', 'stale'].includes(r.result)).map(r => `${r.name} (${r.result})`).join(', ')}. See .just-vibe/automation/${key}.json.` } : {}) };
|
|
141
|
+
} finally { closeSync(handle); unlinkSync(lock); }
|
|
142
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join, dirname, resolve } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
export const packageRoot = fileURLToPath(new URL('../../../../', import.meta.url));
|
|
7
|
+
const marker = '.just-vibe-managed.json';
|
|
8
|
+
const assets = ['plugins/just-vibe', '.agents/plugins/marketplace.json', '.claude-plugin/marketplace.json'];
|
|
9
|
+
|
|
10
|
+
export function managedSource(target, env = process.env) {
|
|
11
|
+
return resolve(env.JUST_VIBE_HOME || join(homedir(), '.just-vibe'), 'marketplaces', target);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function validateBundle(root) {
|
|
15
|
+
for (const name of ['.agents/plugins/marketplace.json', '.claude-plugin/marketplace.json']) {
|
|
16
|
+
const manifest = JSON.parse(readFileSync(join(root, name), 'utf8'));
|
|
17
|
+
const plugin = manifest.plugins?.[0];
|
|
18
|
+
if (manifest.name !== 'just-vibe' || manifest.plugins.length !== 1 || plugin.name !== 'just-vibe'
|
|
19
|
+
|| (typeof plugin.source === 'string' ? plugin.source : plugin.source.path) !== './plugins/just-vibe') {
|
|
20
|
+
throw new Error('Invalid bundled marketplace.');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const versions = ['codex', 'claude'].map(host => {
|
|
24
|
+
const manifest = JSON.parse(readFileSync(join(root, `plugins/just-vibe/.${host}-plugin/plugin.json`), 'utf8'));
|
|
25
|
+
if (manifest.name !== 'just-vibe' || !/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(manifest.version)) throw new Error('Invalid bundled plugin.');
|
|
26
|
+
return manifest.version;
|
|
27
|
+
});
|
|
28
|
+
if (versions[0] !== versions[1]) throw new Error('Bundled plugin versions disagree.');
|
|
29
|
+
const catalog = JSON.parse(readFileSync(join(root, 'plugins/just-vibe/catalog/commands.json'), 'utf8'));
|
|
30
|
+
for (const command of catalog.commands) {
|
|
31
|
+
if (!/^[a-z0-9-]+$/.test(command.id)
|
|
32
|
+
|| !existsSync(join(root, `plugins/just-vibe/skills/${command.id}/SKILL.md`))) throw new Error('Incomplete bundled skills.');
|
|
33
|
+
}
|
|
34
|
+
if (!existsSync(join(root, 'plugins/just-vibe/scripts/toolkit.mjs'))) throw new Error('Missing bundled runtime.');
|
|
35
|
+
return versions[0];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function inspectManaged(root) {
|
|
39
|
+
let stat;
|
|
40
|
+
try { stat = lstatSync(root); } catch (error) { if (error.code === 'ENOENT') return null; throw error; }
|
|
41
|
+
if (stat.isSymbolicLink()) throw new Error(`Refusing to replace a symlink at ${root}.`);
|
|
42
|
+
let data;
|
|
43
|
+
try { data = JSON.parse(readFileSync(join(root, marker), 'utf8')); } catch {}
|
|
44
|
+
if (data?.owner !== 'just-vibe' || data.schemaVersion !== 1) {
|
|
45
|
+
throw new Error(`Directory is not managed by just-vibe: ${root}. Choose another JUST_VIBE_HOME; no files were replaced.`);
|
|
46
|
+
}
|
|
47
|
+
return data;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function rejectLinks(path) {
|
|
51
|
+
const stat = lstatSync(path);
|
|
52
|
+
if (stat.isSymbolicLink() || (!stat.isDirectory() && !stat.isFile())) throw new Error(`Unsupported bundled file: ${path}`);
|
|
53
|
+
if (stat.isDirectory()) for (const name of readdirSync(path)) rejectLinks(join(path, name));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Only the explicit package payload is copied. A failed copy leaves the previous source intact.
|
|
57
|
+
// Retain a valid managed copy on host failure so the same operation can be retried.
|
|
58
|
+
export function stageBundle(destination, { root = packageRoot, replace = false } = {}) {
|
|
59
|
+
const existing = inspectManaged(destination);
|
|
60
|
+
if (existing && !replace) { validateBundle(destination); return existing.version; }
|
|
61
|
+
let version;
|
|
62
|
+
try { version = validateBundle(root); }
|
|
63
|
+
catch (error) {
|
|
64
|
+
throw new Error(`A complete npm package is needed to install/update the bundled source. Run pnpm dlx just-vibe@latest ${replace ? 'update' : 'setup'}. ${error.message}`);
|
|
65
|
+
}
|
|
66
|
+
for (const asset of assets) rejectLinks(join(root, asset));
|
|
67
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
68
|
+
const lock = `${destination}.lock`;
|
|
69
|
+
try { mkdirSync(lock); }
|
|
70
|
+
catch { throw new Error(`Another installation may be running (${lock}). Retry after it finishes. If it was interrupted, inspect and remove only that lock directory.`); }
|
|
71
|
+
let staging;
|
|
72
|
+
let backup;
|
|
73
|
+
let committed = false;
|
|
74
|
+
try {
|
|
75
|
+
// Recheck ownership while holding the copy lock.
|
|
76
|
+
inspectManaged(destination);
|
|
77
|
+
staging = mkdtempSync(join(dirname(destination), '.just-vibe-stage-'));
|
|
78
|
+
for (const asset of assets) {
|
|
79
|
+
mkdirSync(dirname(join(staging, asset)), { recursive: true });
|
|
80
|
+
cpSync(join(root, asset), join(staging, asset), { recursive: true, dereference: false });
|
|
81
|
+
}
|
|
82
|
+
if (existsSync(join(root, 'LICENSE'))) cpSync(join(root, 'LICENSE'), join(staging, 'LICENSE'));
|
|
83
|
+
validateBundle(staging);
|
|
84
|
+
writeFileSync(join(staging, marker), `${JSON.stringify({ schemaVersion: 1, owner: 'just-vibe', version })}\n`);
|
|
85
|
+
if (existsSync(destination)) {
|
|
86
|
+
backup = `${staging}-previous`;
|
|
87
|
+
renameSync(destination, backup);
|
|
88
|
+
}
|
|
89
|
+
try { renameSync(staging, destination); }
|
|
90
|
+
catch (error) { if (backup) { renameSync(backup, destination); backup = undefined; } throw error; }
|
|
91
|
+
staging = undefined;
|
|
92
|
+
committed = true;
|
|
93
|
+
return version;
|
|
94
|
+
} finally {
|
|
95
|
+
if (staging) rmSync(staging, { recursive: true, force: true });
|
|
96
|
+
// Never delete the recovery copy if restoring the old destination failed.
|
|
97
|
+
if (backup && committed) rmSync(backup, { recursive: true, force: true });
|
|
98
|
+
rmSync(lock, { recursive: true, force: true });
|
|
99
|
+
}
|
|
100
|
+
}
|