bosun 0.42.5 → 0.43.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/.env.example +36 -51
- package/README.md +19 -3
- package/agent/agent-custom-tools.mjs +138 -26
- package/agent/agent-endpoint.mjs +1 -2
- package/agent/agent-event-bus.mjs +33 -2
- package/agent/agent-hooks.mjs +1 -1
- package/agent/agent-launcher.mjs +6210 -0
- package/agent/agent-pool.mjs +7 -4018
- package/agent/agent-prompt-catalog.mjs +5 -6
- package/agent/agent-prompts.mjs +62 -6
- package/agent/agent-sdk.mjs +130 -0
- package/agent/agent-supervisor.mjs +30 -6
- package/agent/auth/_shared.mjs +129 -0
- package/agent/auth/anthropic-api-key.mjs +13 -0
- package/agent/auth/azure-openai.mjs +17 -0
- package/agent/auth/cerebras.mjs +14 -0
- package/agent/auth/chatgpt-codex-subscription.mjs +15 -0
- package/agent/auth/claude-subscription.mjs +15 -0
- package/agent/auth/copilot-oauth.mjs +13 -0
- package/agent/auth/deepinfra.mjs +14 -0
- package/agent/auth/fireworks.mjs +14 -0
- package/agent/auth/gemini-api-key.mjs +14 -0
- package/agent/auth/groq.mjs +14 -0
- package/agent/auth/index.mjs +85 -0
- package/agent/auth/nebius.mjs +14 -0
- package/agent/auth/ollama.mjs +14 -0
- package/agent/auth/openai-api-key.mjs +13 -0
- package/agent/auth/openai-compatible.mjs +15 -0
- package/agent/auth/openrouter.mjs +14 -0
- package/agent/auth/perplexity.mjs +14 -0
- package/agent/auth/sambanova.mjs +14 -0
- package/agent/auth/together.mjs +14 -0
- package/agent/auth/xai.mjs +14 -0
- package/agent/autofix-prompts.mjs +2 -2
- package/agent/autofix.mjs +2 -2
- package/agent/bosun-skills.mjs +215 -86
- package/agent/fleet-coordinator.mjs +161 -32
- package/agent/harness/agent-loop.mjs +26 -0
- package/agent/harness/event-contract.mjs +125 -0
- package/agent/harness/followup-queue.mjs +33 -0
- package/agent/harness/message-normalizer.mjs +43 -0
- package/agent/harness/module-boundaries.md +73 -0
- package/agent/harness/run-contract.mjs +122 -0
- package/agent/harness/runtime-config.mjs +132 -0
- package/agent/harness/session-state.mjs +80 -0
- package/agent/harness/steering-queue.mjs +35 -0
- package/agent/harness/tool-runner.mjs +95 -0
- package/agent/harness/turn-runner.mjs +135 -0
- package/agent/harness-agent-service.mjs +852 -0
- package/agent/harness-executor-config.mjs +384 -0
- package/agent/hook-library.mjs +141 -2
- package/agent/hook-profiles.mjs +15 -2
- package/agent/internal-harness-control-plane.mjs +672 -0
- package/agent/internal-harness-profile.mjs +519 -0
- package/agent/internal-harness-runtime.mjs +1219 -0
- package/agent/lineage-graph.mjs +141 -0
- package/agent/primary-agent.mjs +602 -706
- package/agent/provider-auth-manager.mjs +830 -0
- package/agent/provider-auth-state.mjs +440 -0
- package/agent/provider-capabilities.mjs +116 -0
- package/agent/provider-kernel.mjs +596 -0
- package/agent/provider-message-transform.mjs +583 -0
- package/agent/provider-model-catalog.mjs +163 -0
- package/agent/provider-registry.mjs +657 -0
- package/agent/provider-runtime-discovery.mjs +147 -0
- package/agent/provider-session.mjs +767 -0
- package/agent/providers/_shared.mjs +397 -0
- package/agent/providers/anthropic-messages.mjs +64 -0
- package/agent/providers/azure-openai-responses.mjs +69 -0
- package/agent/providers/cerebras.mjs +66 -0
- package/agent/providers/claude-subscription-shim.mjs +68 -0
- package/agent/providers/copilot-oauth.mjs +66 -0
- package/agent/providers/deepinfra.mjs +66 -0
- package/agent/providers/fireworks.mjs +66 -0
- package/agent/providers/gemini-generate-content.mjs +66 -0
- package/agent/providers/groq.mjs +66 -0
- package/agent/providers/index.mjs +208 -0
- package/agent/providers/nebius.mjs +66 -0
- package/agent/providers/ollama.mjs +66 -0
- package/agent/providers/openai-codex-subscription.mjs +75 -0
- package/agent/providers/openai-compatible.mjs +65 -0
- package/agent/providers/openai-responses.mjs +67 -0
- package/agent/providers/openrouter.mjs +66 -0
- package/agent/providers/perplexity.mjs +66 -0
- package/agent/providers/provider-contract.mjs +138 -0
- package/agent/providers/provider-errors.mjs +63 -0
- package/agent/providers/provider-model-pricing.mjs +246 -0
- package/agent/providers/provider-stream-normalizer.mjs +7 -0
- package/agent/providers/provider-usage-normalizer.mjs +48 -0
- package/agent/providers/sambanova.mjs +66 -0
- package/agent/providers/together.mjs +66 -0
- package/agent/providers/xai.mjs +66 -0
- package/agent/query-engine.mjs +260 -0
- package/agent/retry-queue.mjs +1 -0
- package/agent/review-agent.mjs +1 -1
- package/agent/session-contract.mjs +127 -0
- package/agent/session-manager.mjs +1859 -0
- package/agent/session-replay.mjs +617 -0
- package/agent/session-snapshot-store.mjs +379 -0
- package/agent/skills/agent-coordination.md +6 -0
- package/agent/skills/background-task-execution.md +6 -0
- package/agent/skills/bosun-agent-api.md +6 -0
- package/agent/skills/code-quality-anti-patterns.md +7 -0
- package/agent/skills/commit-conventions.md +6 -0
- package/agent/skills/custom-tool-creation.md +6 -0
- package/agent/skills/error-recovery.md +6 -0
- package/agent/skills/pr-workflow.md +6 -0
- package/agent/skills/tdd-pattern.md +6 -0
- package/agent/subagent-contract.mjs +104 -0
- package/agent/subagent-control.mjs +633 -0
- package/agent/subagent-pool.mjs +260 -0
- package/agent/thread-contract.mjs +88 -0
- package/agent/thread-registry.mjs +552 -0
- package/agent/tool-approval-manager.mjs +259 -0
- package/agent/tool-builtin-catalog.mjs +855 -0
- package/agent/tool-contract.mjs +101 -0
- package/agent/tool-event-contract.mjs +99 -0
- package/agent/tool-execution-ledger.mjs +32 -0
- package/agent/tool-network-policy.mjs +86 -0
- package/agent/tool-orchestrator.mjs +382 -0
- package/agent/tool-output-truncation.mjs +70 -0
- package/agent/tool-registry.mjs +200 -0
- package/agent/tool-retry-policy.mjs +57 -0
- package/agent/tool-runtime-context.mjs +220 -0
- package/bench/harness-load-bench.mjs +281 -0
- package/bench/harness-parity-bench.mjs +214 -0
- package/bench/swebench/bosun-swebench.mjs +21 -6
- package/bosun-tui.mjs +59 -13
- package/bosun.config.example.json +55 -2
- package/bosun.schema.json +598 -5
- package/cli.mjs +656 -160
- package/config/config-doctor.mjs +80 -26
- package/config/config-editor.mjs +417 -0
- package/config/config.mjs +489 -144
- package/config/repo-config.mjs +125 -49
- package/config/repo-root.mjs +33 -1
- package/desktop/main.mjs +554 -115
- package/desktop/package.json +1 -1
- package/git/diff-stats.mjs +7 -5
- package/git/git-editor-fix.mjs +2 -42
- package/github/github-app-auth.mjs +6 -0
- package/github/github-oauth-portal.mjs +20 -0
- package/infra/anomaly-detector.mjs +122 -22
- package/infra/approval-projection-store.mjs +75 -0
- package/infra/config-reload-bus.mjs +33 -0
- package/infra/container-runner.mjs +37 -4
- package/infra/error-detector.mjs +110 -35
- package/infra/event-schema.mjs +353 -0
- package/infra/guardrails.mjs +383 -0
- package/infra/heartbeat-monitor.mjs +432 -0
- package/infra/library-manager.mjs +367 -19
- package/infra/live-event-projector.mjs +197 -0
- package/infra/maintenance.mjs +202 -51
- package/infra/monitor.mjs +1749 -2027
- package/infra/preflight.mjs +107 -6
- package/infra/presence.mjs +33 -9
- package/infra/projection-contract.mjs +27 -0
- package/infra/provider-usage-ledger.mjs +73 -0
- package/infra/replay-reader.mjs +140 -0
- package/infra/runtime-accumulator.mjs +303 -8
- package/infra/runtime-metrics.mjs +156 -0
- package/infra/session-projection-store.mjs +169 -0
- package/infra/session-telemetry-runtime.mjs +580 -0
- package/infra/session-telemetry.mjs +338 -0
- package/infra/session-tracker.mjs +1613 -228
- package/infra/startup-service.mjs +0 -2
- package/infra/storage-janitor.mjs +1046 -0
- package/infra/subagent-projection-store.mjs +89 -0
- package/infra/test-runtime.mjs +53 -20
- package/infra/trace-export.mjs +103 -0
- package/infra/tui-bridge.mjs +607 -5
- package/infra/update-check.mjs +7 -8
- package/infra/windows-hidden-child-processes.mjs +99 -0
- package/infra/worktree-recovery-state.mjs +20 -7
- package/kanban/kanban-adapter.mjs +702 -310
- package/kanban/repo-mirror-projection-store.mjs +871 -0
- package/lib/agent-configuration-guide.mjs +280 -0
- package/lib/hot-path-runtime.mjs +1061 -0
- package/lib/integrations-registry.mjs +294 -0
- package/lib/log-tail.mjs +101 -0
- package/lib/logger.mjs +21 -25
- package/lib/mojibake-repair.mjs +40 -0
- package/lib/repo-map.mjs +137 -24
- package/lib/request-json-api.mjs +59 -0
- package/lib/safe-box.mjs +56 -0
- package/lib/session-insights.mjs +3 -1
- package/lib/skill-markdown-safety.mjs +394 -0
- package/lib/state-ledger-sqlite.mjs +4462 -0
- package/lib/vault-keychain.mjs +259 -0
- package/lib/vault.mjs +374 -0
- package/lib/workflow-flowchart-utils.mjs +326 -0
- package/monitor-tail-sanitizer.mjs +1 -2
- package/native/bosun-telemetry/Cargo.toml +9 -0
- package/native/bosun-telemetry/src/export.rs +151 -0
- package/native/bosun-telemetry/src/main.rs +76 -0
- package/native/bosun-telemetry/src/metrics.rs +114 -0
- package/native/bosun-telemetry/src/session_telemetry.rs +178 -0
- package/native/bosun-unified-exec/Cargo.lock +107 -0
- package/native/bosun-unified-exec/Cargo.toml +8 -0
- package/native/bosun-unified-exec/src/async_watcher.rs +145 -0
- package/native/bosun-unified-exec/src/head_tail_buffer.rs +241 -0
- package/native/bosun-unified-exec/src/main.rs +86 -0
- package/native/bosun-unified-exec/src/process_manager.rs +308 -0
- package/native/bosun-unified-exec/src/tool_orchestrator.rs +187 -0
- package/package.json +230 -59
- package/postinstall.mjs +182 -13
- package/server/bosun-mcp-server.mjs +348 -10
- package/server/routes/harness-agent-bridge.mjs +128 -0
- package/server/routes/harness-approvals.mjs +290 -0
- package/server/routes/harness-events.mjs +469 -0
- package/server/routes/harness-providers.mjs +385 -0
- package/server/routes/harness-sessions.mjs +2230 -0
- package/server/routes/harness-subagents.mjs +138 -0
- package/server/routes/harness-surface-payload.mjs +74 -0
- package/server/setup-web-server.mjs +468 -39
- package/server/ui-server.mjs +13041 -4418
- package/setup.mjs +206 -298
- package/shared-workspaces.json +1 -1
- package/shell/anthropic-native-adapter.mjs +1218 -0
- package/shell/auth-resolver.mjs +247 -0
- package/shell/claude-shell.mjs +85 -2
- package/shell/codex-config-file.mjs +9 -0
- package/shell/codex-config.mjs +192 -249
- package/shell/codex-model-profiles.mjs +76 -12
- package/shell/codex-sdk-import.mjs +7 -0
- package/shell/codex-shell.mjs +708 -170
- package/shell/context-compaction.mjs +898 -0
- package/shell/copilot-shell.mjs +359 -109
- package/shell/gemini-native-adapter.mjs +411 -0
- package/shell/gemini-shell.mjs +121 -13
- package/shell/mcp-client.mjs +401 -0
- package/shell/mcp-registry.mjs +72 -0
- package/shell/message-pruner.mjs +248 -0
- package/shell/openai-native-adapter.mjs +1975 -0
- package/shell/opencode-providers.mjs +16 -531
- package/shell/opencode-shell.mjs +180 -9
- package/shell/provider-transform.mjs +386 -0
- package/shell/pwsh-runtime.mjs +9 -2
- package/shell/retry-fetch.mjs +244 -0
- package/shell/session-resume.mjs +97 -0
- package/shell/session-store.mjs +215 -0
- package/shell/shell-adapter-registry.mjs +346 -0
- package/shell/shell-session-compat.mjs +442 -0
- package/shell/smooth-stream.mjs +233 -0
- package/shell/stop-condition.mjs +238 -0
- package/shell/tool-call-repair.mjs +345 -0
- package/shell/tool-executor.mjs +571 -0
- package/task/pipeline.mjs +3 -1
- package/task/task-assessment.mjs +312 -6
- package/task/task-claims.mjs +312 -48
- package/task/task-cli.mjs +90 -14
- package/task/task-complexity.mjs +6 -6
- package/task/task-context.mjs +37 -0
- package/task/task-debt-ledger.mjs +110 -0
- package/task/task-executor.mjs +1104 -119
- package/task/task-replanner.mjs +553 -0
- package/task/task-simulate-cli.mjs +1481 -0
- package/task/task-store.mjs +996 -68
- package/telegram/executor-health-region-cache.mjs +75 -0
- package/telegram/get-telegram-chat-id.mjs +0 -0
- package/telegram/harness-api-client.mjs +124 -0
- package/telegram/sticky-menu-state.mjs +384 -0
- package/telegram/telegram-bot.mjs +548 -865
- package/telegram/telegram-sentinel.mjs +25 -14
- package/telegram/telegram-surface-runtime.mjs +53 -0
- package/tools/generate-demo-defaults.mjs +23 -4
- package/tools/harness-hotpath-bench.mjs +246 -0
- package/tools/import-check.mjs +279 -234
- package/tools/install-git-hooks.mjs +96 -20
- package/tools/native-rust.mjs +124 -0
- package/tools/packed-cli-smoke.mjs +147 -0
- package/tools/prepublish-check.mjs +53 -1
- package/tools/run-workflow-guaranteed-suite.mjs +56 -0
- package/tools/site-serve.mjs +112 -0
- package/tools/sync-demo-ui.mjs +188 -0
- package/tools/syntax-check.mjs +32 -28
- package/tools/test-kanban-enhancement.mjs +7 -7
- package/tools/test-shared-state-integration.mjs +5 -19
- package/tools/vite-windows-realpath-shim.mjs +274 -0
- package/tools/vitest-esbuild-shim.mjs +45 -0
- package/tools/vitest-full-suite.mjs +310 -0
- package/tools/vitest-runner.mjs +505 -11
- package/tools/workflow-orphan-worktree-recovery.mjs +24 -7
- package/tui/CommandPalette.js +87 -0
- package/tui/app.mjs +463 -37
- package/tui/components/status-header.mjs +43 -1
- package/tui/lib/command-palette.mjs +191 -0
- package/tui/lib/connection-target.mjs +577 -0
- package/tui/lib/header-config.mjs +0 -2
- package/tui/lib/navigation.mjs +8 -3
- package/tui/lib/ws-bridge.mjs +141 -51
- package/tui/screens/agents-screen-helpers.mjs +87 -3
- package/tui/screens/agents.mjs +1074 -202
- package/tui/screens/connection-setup.mjs +363 -0
- package/tui/screens/harness-approvals.mjs +7 -0
- package/tui/screens/harness-sessions.mjs +109 -0
- package/tui/screens/harness-subagents.mjs +18 -0
- package/tui/screens/harness-telemetry.mjs +67 -0
- package/tui/screens/logs.mjs +325 -0
- package/tui/screens/settings-screen-helpers.mjs +75 -0
- package/tui/screens/settings.mjs +397 -0
- package/tui/screens/status.mjs +130 -5
- package/tui/screens/telemetry-screen-helpers.mjs +158 -0
- package/tui/screens/telemetry.mjs +246 -0
- package/tui/screens/workflows.mjs +984 -0
- package/ui/app.js +746 -189
- package/ui/app.monolith.js +2 -3
- package/ui/assets/toastui-editor-all.min.js +24 -0
- package/ui/components/agent-selector.js +706 -49
- package/ui/components/charts.js +16 -12
- package/ui/components/chat-view.js +536 -35
- package/ui/components/commit-graph.js +648 -0
- package/ui/components/context-menu.js +89 -0
- package/ui/components/diff-viewer.js +169 -53
- package/ui/components/forms.js +13 -2
- package/ui/components/kanban-board.js +541 -92
- package/ui/components/session-list.js +303 -66
- package/ui/components/shared.js +9 -1
- package/ui/components/task-markdown.js +272 -0
- package/ui/components/workspace-executor-settings.js +142 -0
- package/ui/components/workspace-switcher.js +35 -79
- package/ui/demo-defaults.js +17278 -7297
- package/ui/demo.html +7058 -5338
- package/ui/index.html +189 -112
- package/ui/modules/agent-events.js +309 -36
- package/ui/modules/api.js +236 -13
- package/ui/modules/chat-turn-groups.js +101 -0
- package/ui/modules/harness-client.js +56 -0
- package/ui/modules/icon-utils.js +9 -1
- package/ui/modules/icons.js +26 -2
- package/ui/modules/repo-area-contention.js +97 -0
- package/ui/modules/router.js +2 -0
- package/ui/modules/session-api.js +158 -14
- package/ui/modules/session-insights-worker.js +28 -0
- package/ui/modules/session-insights.js +173 -4
- package/ui/modules/session-surface.js +221 -0
- package/ui/modules/settings-schema.js +148 -27
- package/ui/modules/state.js +122 -13
- package/ui/modules/streaming.js +196 -60
- package/ui/modules/structured-values.js +47 -0
- package/ui/modules/task-hierarchy.js +374 -0
- package/ui/modules/worktree-recovery.js +10 -1
- package/ui/setup.html +3327 -2538
- package/ui/styles/components.css +983 -99
- package/ui/styles/kanban.css +229 -0
- package/ui/styles/layout.css +578 -106
- package/ui/styles/toastui-editor-dark.css +1 -0
- package/ui/styles/toastui-editor-viewer.css +6 -0
- package/ui/styles/toastui-editor.css +6 -0
- package/ui/styles/variables.css +14 -2
- package/ui/styles/workspace-switcher.css +22 -0
- package/ui/styles.css +20 -5
- package/ui/tabs/agents.js +1222 -86
- package/ui/tabs/chat.js +588 -146
- package/ui/tabs/context-compression-lab.js +962 -0
- package/ui/tabs/control.js +347 -61
- package/ui/tabs/dashboard.js +372 -105
- package/ui/tabs/guardrails.js +1140 -0
- package/ui/tabs/infra.js +91 -12
- package/ui/tabs/integrations.js +388 -0
- package/ui/tabs/library.js +161 -21
- package/ui/tabs/logs.js +410 -52
- package/ui/tabs/manual-flows.js +268 -52
- package/ui/tabs/settings.js +2117 -105
- package/ui/tabs/tasks.js +2851 -303
- package/ui/tabs/telemetry.js +246 -8
- package/ui/tabs/workflow-canvas-utils.mjs +172 -15
- package/ui/tabs/workflows.js +2632 -348
- package/ui/tui/App.js +127 -119
- package/ui/tui/HelpScreen.js +201 -0
- package/ui/tui/SettingsScreen.js +388 -0
- package/ui/tui/TasksScreen.js +30 -7
- package/ui/tui/TelemetryScreen.js +155 -0
- package/ui/tui/WorkflowsScreen.js +350 -0
- package/ui/tui/config-events.js +13 -0
- package/ui/tui/constants.js +1 -1
- package/ui/tui/logs-screen-helpers.js +292 -0
- package/ui/tui/tasks-screen-helpers.js +52 -0
- package/ui/tui/telemetry-helpers.js +158 -0
- package/ui/tui/useWorkflows.js +126 -6
- package/ui/tui/workflows-screen-helpers.js +220 -0
- package/ui/vendor/preact-jsx-runtime.js +5 -0
- package/utils.mjs +2 -2
- package/voice/vision-session-state.mjs +257 -0
- package/voice/voice-action-dispatcher.mjs +57 -12
- package/voice/voice-agents-sdk.mjs +1 -1
- package/voice/voice-auth-manager.mjs +102 -123
- package/voice/voice-tool-definitions.mjs +7 -7
- package/voice/voice-tools.mjs +837 -101
- package/workflow/action-approval.mjs +415 -0
- package/workflow/approval-queue.mjs +1254 -0
- package/workflow/credential-store.mjs +553 -0
- package/workflow/cron-scheduler.mjs +512 -0
- package/workflow/declarative-workflows.mjs +21 -2
- package/workflow/delegation-runtime.mjs +557 -0
- package/workflow/execution-ledger.mjs +1317 -33
- package/workflow/harness-approval-node.mjs +237 -0
- package/workflow/harness-output-contract.mjs +86 -0
- package/workflow/harness-session-node.mjs +160 -0
- package/workflow/harness-subagent-node.mjs +483 -0
- package/workflow/harness-tool-node.mjs +176 -0
- package/workflow/heavy-runner-pool.mjs +546 -0
- package/workflow/manual-flows.mjs +969 -28
- package/workflow/mcp-discovery-proxy.mjs +363 -143
- package/workflow/mcp-registry.mjs +507 -65
- package/workflow/meeting-workflow-service.mjs +24 -12
- package/workflow/pipeline-workflows.mjs +44 -2
- package/workflow/pipeline.mjs +72 -28
- package/workflow/project-detection.mjs +31 -6
- package/workflow/research-evidence-sidecar.mjs +1246 -0
- package/workflow/run-evaluator.mjs +2155 -0
- package/workflow/workflow-cli.mjs +229 -2
- package/workflow/workflow-contract.mjs +130 -2
- package/workflow/workflow-engine.mjs +5636 -331
- package/workflow/workflow-migration.mjs +0 -1
- package/workflow/workflow-nodes/actions.mjs +15526 -0
- package/workflow/workflow-nodes/agent.mjs +1863 -0
- package/workflow/workflow-nodes/conditions.mjs +307 -0
- package/workflow/workflow-nodes/definitions.mjs +210 -29
- package/workflow/workflow-nodes/flow.mjs +749 -0
- package/workflow/workflow-nodes/loop.mjs +449 -0
- package/workflow/workflow-nodes/meetings.mjs +456 -0
- package/workflow/workflow-nodes/notifications.mjs +169 -0
- package/workflow/workflow-nodes/transforms.mjs +83 -30
- package/workflow/workflow-nodes/triggers.mjs +1405 -0
- package/workflow/workflow-nodes/validation.mjs +722 -0
- package/workflow/workflow-nodes.mjs +43 -14965
- package/workflow/workflow-serializer.mjs +294 -0
- package/workflow/workflow-templates.mjs +304 -9
- package/workflow-templates/_helpers.mjs +1 -3
- package/workflow-templates/agents.mjs +235 -27
- package/workflow-templates/bosun-native.mjs +3 -1
- package/workflow-templates/code-quality.mjs +1 -1
- package/workflow-templates/continuation-loop.mjs +21 -5
- package/workflow-templates/coverage.mjs +6 -2
- package/workflow-templates/github.mjs +2565 -177
- package/workflow-templates/reliability.mjs +463 -35
- package/workflow-templates/research-evidence.mjs +389 -0
- package/workflow-templates/security.mjs +75 -62
- package/workflow-templates/sub-workflows.mjs +11 -3
- package/workflow-templates/task-batch.mjs +102 -20
- package/workflow-templates/task-lifecycle.mjs +333 -298
- package/workspace/command-diagnostics.mjs +111 -0
- package/workspace/context-cache.mjs +1308 -124
- package/workspace/context-indexer.mjs +915 -9
- package/workspace/context-injector.mjs +144 -0
- package/workspace/execution-journal.mjs +255 -0
- package/workspace/scope-locks.mjs +481 -0
- package/workspace/shared-knowledge.mjs +496 -91
- package/workspace/shared-state-manager.mjs +344 -1
- package/workspace/shared-workspace-cli.mjs +0 -0
- package/workspace/shared-workspace-registry.mjs +2 -2
- package/workspace/skillbook-store.mjs +681 -0
- package/workspace/workspace-manager.mjs +14 -8
- package/workspace/workspace-monitor.mjs +4 -4
- package/workspace/worktree-manager.mjs +146 -55
- package/workspace/worktree-setup.mjs +822 -0
- package/agent/rotate-agent-logs.sh +0 -134
- package/git/sdk-conflict-resolver.mjs +0 -971
- package/infra/sync-engine.mjs +0 -1160
- package/kanban/ve-kanban.mjs +0 -664
- package/kanban/ve-kanban.ps1 +0 -1365
- package/kanban/ve-kanban.sh +0 -18
- package/kanban/ve-orchestrator.mjs +0 -340
- package/kanban/ve-orchestrator.ps1 +0 -6762
- package/kanban/ve-orchestrator.sh +0 -18
- package/kanban/vibe-kanban-wrapper.mjs +0 -41
- package/kanban/vk-error-resolver.mjs +0 -474
- package/kanban/vk-log-stream.mjs +0 -932
- package/task/task-archiver.mjs +0 -813
- package/tools/publish.mjs +0 -239
- package/ui/components/chat-view.js.bak +0 -1
- package/ui/tabs/infra.js.bak +0 -1
package/tools/import-check.mjs
CHANGED
|
@@ -1,234 +1,279 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* import-check.mjs — ESM named-export validation gate.
|
|
3
|
-
*
|
|
4
|
-
* Uses vm.SourceTextModule.link() to validate that every named import
|
|
5
|
-
* from a local module actually exists as an export in the target module.
|
|
6
|
-
* This catches the class of errors where a named import is added but the
|
|
7
|
-
* corresponding export doesn't exist (e.g., partial merges, abandoned WIP,
|
|
8
|
-
* renames that missed a call-site).
|
|
9
|
-
*
|
|
10
|
-
* External dependencies (node: builtins, npm packages) are dynamically
|
|
11
|
-
* imported and mirrored as SyntheticModules so that local module linking
|
|
12
|
-
* succeeds without requiring the full dependency graph.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import vm from "node:vm";
|
|
16
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
17
|
-
import { resolve, dirname, relative, extname } from "node:path";
|
|
18
|
-
import { execSync } from "node:child_process";
|
|
19
|
-
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
20
|
-
|
|
21
|
-
const JS_EXTENSIONS = new Set([".mjs", ".js", ".cjs"]);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* import-check.mjs — ESM named-export validation gate.
|
|
3
|
+
*
|
|
4
|
+
* Uses vm.SourceTextModule.link() to validate that every named import
|
|
5
|
+
* from a local module actually exists as an export in the target module.
|
|
6
|
+
* This catches the class of errors where a named import is added but the
|
|
7
|
+
* corresponding export doesn't exist (e.g., partial merges, abandoned WIP,
|
|
8
|
+
* renames that missed a call-site).
|
|
9
|
+
*
|
|
10
|
+
* External dependencies (node: builtins, npm packages) are dynamically
|
|
11
|
+
* imported and mirrored as SyntheticModules so that local module linking
|
|
12
|
+
* succeeds without requiring the full dependency graph.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import vm from "node:vm";
|
|
16
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
17
|
+
import { resolve, dirname, relative, extname } from "node:path";
|
|
18
|
+
import { execSync } from "node:child_process";
|
|
19
|
+
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
20
|
+
|
|
21
|
+
const JS_EXTENSIONS = new Set([".mjs", ".js", ".cjs"]);
|
|
22
|
+
|
|
23
|
+
function toErrorMessage(error) {
|
|
24
|
+
return error instanceof Error ? error.message : String(error);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function buildSyntaxError(rootDir, absPath, error) {
|
|
28
|
+
const moduleFile = relative(rootDir, absPath);
|
|
29
|
+
const syntaxError = new Error(`Syntax error in ${moduleFile}: ${toErrorMessage(error)}`);
|
|
30
|
+
syntaxError.code = "module_syntax_error";
|
|
31
|
+
syntaxError.moduleFile = moduleFile;
|
|
32
|
+
return syntaxError;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Discover all .mjs source modules via git, excluding test/bench/site/desktop.
|
|
37
|
+
*/
|
|
38
|
+
export function discoverSourceModules(rootDir) {
|
|
39
|
+
try {
|
|
40
|
+
const output = execSync("git ls-files --cached", {
|
|
41
|
+
encoding: "utf8",
|
|
42
|
+
cwd: rootDir,
|
|
43
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
44
|
+
});
|
|
45
|
+
return output
|
|
46
|
+
.split("\n")
|
|
47
|
+
.map((f) => f.trim())
|
|
48
|
+
.filter(
|
|
49
|
+
(f) =>
|
|
50
|
+
f.endsWith(".mjs") &&
|
|
51
|
+
!f.startsWith("tests/") &&
|
|
52
|
+
!f.startsWith("bench/") &&
|
|
53
|
+
!f.startsWith("site/") &&
|
|
54
|
+
!f.startsWith("desktop/") &&
|
|
55
|
+
!f.startsWith("tools/"),
|
|
56
|
+
)
|
|
57
|
+
.sort((a, b) => a.localeCompare(b));
|
|
58
|
+
} catch {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Validate all ESM named imports by linking modules with vm.SourceTextModule.
|
|
65
|
+
*
|
|
66
|
+
* @param {object} [opts]
|
|
67
|
+
* @param {string} [opts.rootDir] — project root (default: cwd)
|
|
68
|
+
* @param {string[]} [opts.files] — explicit list of relative .mjs paths to check
|
|
69
|
+
* @returns {{ errors: Array<{file: string, error: string}>, moduleCount: number }}
|
|
70
|
+
*/
|
|
71
|
+
export async function validateImports({ rootDir, files } = {}) {
|
|
72
|
+
rootDir = rootDir ?? process.cwd();
|
|
73
|
+
|
|
74
|
+
if (typeof vm.SourceTextModule !== "function") {
|
|
75
|
+
throw new Error(
|
|
76
|
+
"vm.SourceTextModule is unavailable. Run with --experimental-vm-modules.",
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const context = vm.createContext({});
|
|
81
|
+
const moduleCache = new Map(); // absolute path → SourceTextModule | SyntheticModule
|
|
82
|
+
const externalCache = new Map(); // specifier → SyntheticModule
|
|
83
|
+
const unresolvedExternals = new Set(); // specifiers that couldn't be dynamically imported
|
|
84
|
+
const errors = [];
|
|
85
|
+
const parseErrors = new Map(); // absolute path → Error
|
|
86
|
+
|
|
87
|
+
const moduleFiles = files ?? discoverSourceModules(rootDir);
|
|
88
|
+
const seenErrors = new Set();
|
|
89
|
+
|
|
90
|
+
function recordError(file, error) {
|
|
91
|
+
const message = toErrorMessage(error);
|
|
92
|
+
const key = `${file}\n${message}`;
|
|
93
|
+
if (seenErrors.has(key)) return;
|
|
94
|
+
seenErrors.add(key);
|
|
95
|
+
errors.push({ file, error: message });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Phase 1: Parse all source modules into SourceTextModules.
|
|
99
|
+
for (const file of moduleFiles) {
|
|
100
|
+
const absPath = resolve(rootDir, file);
|
|
101
|
+
if (!existsSync(absPath)) continue;
|
|
102
|
+
try {
|
|
103
|
+
const source = readFileSync(absPath, "utf8");
|
|
104
|
+
const mod = new vm.SourceTextModule(source, {
|
|
105
|
+
identifier: absPath,
|
|
106
|
+
context,
|
|
107
|
+
});
|
|
108
|
+
moduleCache.set(absPath, mod);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
parseErrors.set(absPath, buildSyntaxError(rootDir, absPath, error));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Create a SyntheticModule stub for an external dependency.
|
|
116
|
+
* Dynamically imports the real module to mirror its export names.
|
|
117
|
+
*/
|
|
118
|
+
async function stubExternal(specifier) {
|
|
119
|
+
if (externalCache.has(specifier)) return externalCache.get(specifier);
|
|
120
|
+
|
|
121
|
+
let exportNames = ["default"];
|
|
122
|
+
try {
|
|
123
|
+
const real = await import(specifier);
|
|
124
|
+
exportNames = Object.keys(real);
|
|
125
|
+
if (!exportNames.includes("default")) exportNames.push("default");
|
|
126
|
+
} catch {
|
|
127
|
+
// Cannot import (optional dep, missing from node_modules, etc.).
|
|
128
|
+
// Track as unresolved so we can suppress false-positive named-export
|
|
129
|
+
// errors that occur when running from a worktree without node_modules.
|
|
130
|
+
unresolvedExternals.add(specifier);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const synth = new vm.SyntheticModule(
|
|
134
|
+
exportNames,
|
|
135
|
+
function () {
|
|
136
|
+
for (const name of exportNames) this.setExport(name, undefined);
|
|
137
|
+
},
|
|
138
|
+
{ identifier: `external:${specifier}`, context },
|
|
139
|
+
);
|
|
140
|
+
// SyntheticModules have no dependencies, so linker is never called.
|
|
141
|
+
await synth.link(() => {});
|
|
142
|
+
externalCache.set(specifier, synth);
|
|
143
|
+
return synth;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Create a SyntheticModule stub for a non-JS file (e.g., .json, .node).
|
|
148
|
+
*/
|
|
149
|
+
async function stubNonJs(absPath) {
|
|
150
|
+
if (moduleCache.has(absPath)) return moduleCache.get(absPath);
|
|
151
|
+
|
|
152
|
+
const synth = new vm.SyntheticModule(
|
|
153
|
+
["default"],
|
|
154
|
+
function () {
|
|
155
|
+
this.setExport("default", undefined);
|
|
156
|
+
},
|
|
157
|
+
{ identifier: absPath, context },
|
|
158
|
+
);
|
|
159
|
+
await synth.link(() => {});
|
|
160
|
+
moduleCache.set(absPath, synth);
|
|
161
|
+
return synth;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Linker callback for vm.SourceTextModule.link().
|
|
166
|
+
* Resolves specifiers to cached modules or creates stubs.
|
|
167
|
+
*/
|
|
168
|
+
async function linker(specifier, referencingModule) {
|
|
169
|
+
// ── External dependency (node: builtin or npm package) ──
|
|
170
|
+
if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
|
|
171
|
+
return stubExternal(specifier);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Local import — resolve relative to referencing module ──
|
|
175
|
+
const refDir = dirname(referencingModule.identifier);
|
|
176
|
+
const resolved = resolve(refDir, specifier);
|
|
177
|
+
|
|
178
|
+
// Already parsed / stubbed
|
|
179
|
+
if (moduleCache.has(resolved)) return moduleCache.get(resolved);
|
|
180
|
+
if (parseErrors.has(resolved)) throw parseErrors.get(resolved);
|
|
181
|
+
|
|
182
|
+
// Non-JS file (.json, .node, .wasm, etc.)
|
|
183
|
+
if (!JS_EXTENSIONS.has(extname(resolved))) {
|
|
184
|
+
return stubNonJs(resolved);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// JS file outside our initial scan (e.g., vendor/, tools/ dependency)
|
|
188
|
+
if (existsSync(resolved)) {
|
|
189
|
+
try {
|
|
190
|
+
const source = readFileSync(resolved, "utf8");
|
|
191
|
+
const mod = new vm.SourceTextModule(source, {
|
|
192
|
+
identifier: resolved,
|
|
193
|
+
context,
|
|
194
|
+
});
|
|
195
|
+
moduleCache.set(resolved, mod);
|
|
196
|
+
return mod;
|
|
197
|
+
} catch (error) {
|
|
198
|
+
const syntaxError = buildSyntaxError(rootDir, resolved, error);
|
|
199
|
+
parseErrors.set(resolved, syntaxError);
|
|
200
|
+
throw syntaxError;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// File doesn't exist — report clearly.
|
|
205
|
+
throw new Error(
|
|
206
|
+
`Cannot find module '${specifier}' imported from ${relative(rootDir, referencingModule.identifier)}`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Phase 2: Link all modules — this validates named export bindings.
|
|
211
|
+
for (const [absPath, syntaxError] of parseErrors) {
|
|
212
|
+
recordError(relative(rootDir, absPath), syntaxError);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
for (const [absPath, mod] of moduleCache) {
|
|
216
|
+
// Already linked (as a transitive dependency of a previously linked module).
|
|
217
|
+
if (mod.status !== "unlinked") continue;
|
|
218
|
+
try {
|
|
219
|
+
await mod.link(linker);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
const rel = typeof err?.moduleFile === "string" && err.moduleFile
|
|
222
|
+
? err.moduleFile
|
|
223
|
+
: relative(rootDir, absPath);
|
|
224
|
+
recordError(rel, err);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Suppress errors caused by unresolvable external packages (e.g. missing
|
|
229
|
+
// node_modules when running from a git worktree). The pattern emitted by
|
|
230
|
+
// vm.SourceTextModule is: "The requested module '<spec>' does not provide
|
|
231
|
+
// an export named '<name>'".
|
|
232
|
+
const filteredErrors = errors.filter(({ error }) => {
|
|
233
|
+
for (const spec of unresolvedExternals) {
|
|
234
|
+
if (error.includes(`'${spec}'`)) return false;
|
|
235
|
+
}
|
|
236
|
+
return true;
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
return { errors: filteredErrors, moduleCount: moduleCache.size };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ── CLI entrypoint ──────────────────────────────────────────────────────────
|
|
243
|
+
|
|
244
|
+
async function main() {
|
|
245
|
+
const args = process.argv.slice(2);
|
|
246
|
+
|
|
247
|
+
let rootDir = process.cwd();
|
|
248
|
+
let files = undefined;
|
|
249
|
+
|
|
250
|
+
for (let i = 0; i < args.length; i++) {
|
|
251
|
+
if (args[i] === "--root" && args[i + 1]) {
|
|
252
|
+
rootDir = resolve(args[++i]);
|
|
253
|
+
} else if (args[i] === "--files" && args[i + 1]) {
|
|
254
|
+
files = args[++i].split(",").filter(Boolean);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const { errors, moduleCount } = await validateImports({ rootDir, files });
|
|
259
|
+
|
|
260
|
+
if (errors.length > 0) {
|
|
261
|
+
console.error("Import validation failed:\n");
|
|
262
|
+
for (const { file, error } of errors) {
|
|
263
|
+
console.error(` \u2717 ${file}`);
|
|
264
|
+
console.error(` ${error}\n`);
|
|
265
|
+
}
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
console.log(
|
|
270
|
+
`Imports OK: ${moduleCount} modules linked, 0 broken imports`,
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
275
|
+
main().catch((err) => {
|
|
276
|
+
console.error(err.stack || err.message);
|
|
277
|
+
process.exit(1);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
@@ -1,38 +1,114 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { sanitizeGitEnv } from "../git/git-safety.mjs";
|
|
6
8
|
import { isEnvFlagEnabled, shouldAutoInstallGitHooks } from "../task/task-context.mjs";
|
|
7
9
|
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
+
function runGit(args, cwd) {
|
|
11
|
+
return spawnSync("git", args, {
|
|
12
|
+
cwd,
|
|
13
|
+
encoding: "utf8",
|
|
14
|
+
timeout: 15_000,
|
|
15
|
+
windowsHide: true,
|
|
16
|
+
env: sanitizeGitEnv(),
|
|
17
|
+
});
|
|
10
18
|
}
|
|
11
19
|
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
function normalizeHooksPath(value) {
|
|
21
|
+
return String(value || "")
|
|
22
|
+
.trim()
|
|
23
|
+
.replace(/\\/g, "/")
|
|
24
|
+
.replace(/\/+$/, "");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isExpectedHooksPath(value) {
|
|
28
|
+
const normalized = normalizeHooksPath(value);
|
|
29
|
+
return normalized === ".githooks" || normalized.endsWith("/.githooks");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getGitConfigValue(cwd, key) {
|
|
33
|
+
const result = runGit(["config", "--get", key], cwd);
|
|
34
|
+
if (result.status !== 0) return "";
|
|
35
|
+
return String(result.stdout || "").trim();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getRepoRoot(cwd) {
|
|
39
|
+
const result = runGit(["rev-parse", "--show-toplevel"], cwd);
|
|
40
|
+
if (result.status !== 0) return "";
|
|
41
|
+
return String(result.stdout || "").trim();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function installGitHooks(options = {}) {
|
|
45
|
+
const cwd = resolve(options.cwd || process.cwd());
|
|
46
|
+
const env = options.env || process.env;
|
|
47
|
+
const silent = options.silent === true;
|
|
48
|
+
const force = options.force === true;
|
|
19
49
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return;
|
|
50
|
+
if (!force && isEnvFlagEnabled(env.BOSUN_SKIP_GIT_HOOKS, false)) {
|
|
51
|
+
return { ok: true, skipped: true, reason: "env-skip" };
|
|
52
|
+
}
|
|
53
|
+
if (!force && !shouldAutoInstallGitHooks({ env })) {
|
|
54
|
+
return { ok: true, skipped: true, reason: "auto-install-disabled" };
|
|
25
55
|
}
|
|
26
56
|
|
|
27
|
-
|
|
57
|
+
const root = getRepoRoot(cwd);
|
|
58
|
+
if (!root) return { ok: true, skipped: true, reason: "not-a-git-repo" };
|
|
28
59
|
|
|
29
60
|
const hooksDir = resolve(root, ".githooks");
|
|
30
61
|
if (!existsSync(hooksDir)) {
|
|
31
|
-
return;
|
|
62
|
+
return { ok: true, skipped: true, reason: "missing-hooks-dir", root, hooksDir };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const previousHooksPath = getGitConfigValue(root, "core.hooksPath");
|
|
66
|
+
if (isExpectedHooksPath(previousHooksPath)) {
|
|
67
|
+
if (!silent) {
|
|
68
|
+
console.log(`[hooks] installed (core.hooksPath=${previousHooksPath || ".githooks"})`);
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
ok: true,
|
|
72
|
+
changed: false,
|
|
73
|
+
repaired: false,
|
|
74
|
+
root,
|
|
75
|
+
hooksDir,
|
|
76
|
+
hooksPath: previousHooksPath || ".githooks",
|
|
77
|
+
previousHooksPath,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const result = runGit(["config", "core.hooksPath", ".githooks"], root);
|
|
82
|
+
const installedHooksPath = result.status === 0
|
|
83
|
+
? (getGitConfigValue(root, "core.hooksPath") || ".githooks")
|
|
84
|
+
: previousHooksPath;
|
|
85
|
+
const ok = result.status === 0 && isExpectedHooksPath(installedHooksPath);
|
|
86
|
+
|
|
87
|
+
if (ok && !silent) {
|
|
88
|
+
const action = previousHooksPath ? "repaired" : "installed";
|
|
89
|
+
console.log(`[hooks] ${action} (core.hooksPath=${installedHooksPath})`);
|
|
32
90
|
}
|
|
33
91
|
|
|
34
|
-
|
|
35
|
-
|
|
92
|
+
return {
|
|
93
|
+
ok,
|
|
94
|
+
changed: result.status === 0,
|
|
95
|
+
repaired: Boolean(previousHooksPath),
|
|
96
|
+
root,
|
|
97
|
+
hooksDir,
|
|
98
|
+
hooksPath: installedHooksPath,
|
|
99
|
+
previousHooksPath,
|
|
100
|
+
error: ok ? "" : String(result.stderr || result.stdout || "").trim(),
|
|
101
|
+
};
|
|
36
102
|
}
|
|
37
103
|
|
|
38
|
-
main()
|
|
104
|
+
function main() {
|
|
105
|
+
const result = installGitHooks();
|
|
106
|
+
if (!result.ok && !result.skipped) {
|
|
107
|
+
console.error(result.error || "[hooks] failed to install .githooks");
|
|
108
|
+
process.exitCode = 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
113
|
+
main();
|
|
114
|
+
}
|