@khoavu882/doflow 2.0.0-beta.5 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -16
- package/bin/doflow.js +22 -12
- package/core/.antigravity-plugin/plugin.json +1 -2
- package/core/.claude-plugin/plugin.json +1 -1
- package/core/.codex-plugin/plugin.json +1 -1
- package/core/.plugin/plugin.json +1 -1
- package/core/harnesses/claude/hooks/lib.sh +43 -24
- package/core/harnesses/claude/hooks/mcp-tool-guard.sh +4 -1
- package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
- package/core/harnesses/claude/hooks/post-compact.sh +35 -32
- package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
- package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
- package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
- package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +4 -1
- package/core/harnesses/claude/hooks/session-end.sh +23 -10
- package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
- package/core/harnesses/claude/settings/settings.json +26 -9
- package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
- package/core/harnesses/codex/agents/core-implementer.toml +1 -0
- package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
- package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
- package/core/harnesses/codex/agents/research-writer.toml +1 -1
- package/core/harnesses/codex/agents/reviewer.toml +10 -0
- package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
- package/core/harnesses/codex/agents/system-architect.toml +1 -1
- package/core/harnesses/codex/hooks/hooks.json +0 -10
- package/core/harnesses/codex/hooks/lib.sh +44 -25
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +4 -1
- package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +4 -1
- package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
- package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
- package/core/harnesses/copilot/hooks/doflow-lib.sh +94 -0
- package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow.json +51 -0
- package/core/harnesses/kiro/agents/doflow.json +1 -11
- package/core/harnesses/kiro/hooks/doflow.json +0 -10
- package/core/harnesses/kiro/hooks/lib.sh +26 -6
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +8 -3
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +8 -3
- package/core/harnesses/kiro/hooks/session-start.sh +1 -0
- package/core/harnesses/kiro/hooks/stop-check.sh +1 -0
- package/core/harnesses/pi/extensions/doflow-guard.js +11 -2
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +1 -2
- package/core/harnesses/shared/hooks/adapters/antigravity.js +1 -1
- package/core/harnesses/shared/hooks/bridge/bridge.sh +126 -23
- package/core/harnesses/shared/hooks/bridge/envelope.jq +87 -18
- package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +16 -2
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +49 -16
- package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
- package/core/harnesses/shared/hooks/policies/contract.sh +3 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +20 -7
- package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
- package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
- package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
- package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
- package/core/harnesses/shared/hooks/policies/lib.sh +63 -40
- package/core/harnesses/shared/hooks/policies/mcp-policy.conf +1 -1
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +7 -8
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +132 -57
- package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
- package/core/harnesses/shared/hooks/policies/stop-check.sh +27 -15
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +6 -3
- package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +60 -155
- package/core/harnesses/shared/hooks/stream-hook-runner.js +58 -20
- package/core/harnesses/shared/locator/doflow-run +2 -3
- package/core/registry/advice.json +3 -3
- package/core/registry/assets.json +55 -82
- package/core/registry/capabilities.json +12 -0
- package/core/registry/contracts.json +58 -31
- package/core/registry/contribution-points.json +35 -0
- package/core/registry/gates.json +32 -0
- package/core/registry/guidance-caps.json +40 -0
- package/core/registry/harnesses.json +1445 -366
- package/core/registry/intake.json +53 -0
- package/core/registry/lifecycle.json +599 -996
- package/core/registry/mcp.json +5 -1
- package/core/registry/models.json +7 -1
- package/core/registry/package-templates/README.md +19 -0
- package/core/registry/package-templates/agent/files/agent.md +9 -0
- package/core/registry/package-templates/agent/template.json +4 -0
- package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
- package/core/registry/package-templates/capability-provider/template.json +11 -0
- package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
- package/core/registry/package-templates/debate-preset/template.json +4 -0
- package/core/registry/package-templates/intake-category/files/category.json +8 -0
- package/core/registry/package-templates/intake-category/template.json +4 -0
- package/core/registry/package-templates/review-preset/files/preset.json +30 -0
- package/core/registry/package-templates/review-preset/template.json +4 -0
- package/core/registry/package-templates/skill/files/SKILL.md +8 -0
- package/core/registry/package-templates/skill/template.json +4 -0
- package/core/registry/package-templates/worker/files/worker.json +92 -0
- package/core/registry/package-templates/worker/template.json +11 -0
- package/core/registry/packages/doflow/doflow-package.json +15 -0
- package/core/registry/packages/doflow/presets/debate-3.json +26 -0
- package/core/registry/protocols.json +61 -0
- package/core/registry/routes.json +17 -0
- package/core/registry/rules.json +431 -0
- package/core/registry/settings.json +272 -0
- package/core/registry/storage.json +687 -31
- package/core/registry/telemetry.json +236 -0
- package/core/registry/transcripts.json +142 -0
- package/core/registry/verification.json +76 -6
- package/core/registry/workflows.json +55 -0
- package/core/shared/agent-specs/core-implementer.md +3 -0
- package/core/shared/agent-specs/delivery-lead.md +68 -0
- package/core/shared/agent-specs/quality-guardian.md +5 -1
- package/core/shared/agent-specs/research-writer.md +10 -3
- package/core/shared/agent-specs/reviewer.md +53 -0
- package/core/shared/agent-specs/spec-analyst.md +5 -6
- package/core/shared/agent-specs/system-architect.md +5 -1
- package/core/shared/guidance/DOFLOW_CORE.md +1 -5
- package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
- package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
- package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
- package/core/shared/guidance/pointers/codex.md +1 -4
- package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
- package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
- package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
- package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
- package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
- package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
- package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
- package/core/shared/guidance/references/ORCHESTRATION.md +3 -2
- package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +19 -1
- package/core/shared/guidance/rules/RULES.md +45 -0
- package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
- package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
- package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
- package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
- package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
- package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
- package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
- package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
- package/core/shared/scripts/doflow/bash/validate-artifacts.sh +49 -5
- package/core/shared/scripts/doflow/bin/doflow-run +152 -34
- package/core/shared/skills/do/SKILL.md +14 -12
- package/core/shared/skills/do/references/advice_calls.md +26 -0
- package/core/shared/skills/do/references/pm_routing.md +1 -2
- package/core/shared/skills/do/references/program_mode.md +31 -0
- package/core/shared/skills/do-brainstorm/SKILL.md +45 -97
- package/core/shared/skills/do-brainstorm/references/handoff_branches.md +36 -0
- package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
- package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
- package/core/shared/skills/do-code-review/SKILL.md +48 -148
- package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
- package/core/shared/skills/do-code-review/references/tools.md +90 -0
- package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
- package/core/shared/skills/do-design/SKILL.md +23 -20
- package/core/shared/skills/do-diagnose/SKILL.md +18 -6
- package/core/shared/skills/do-document/SKILL.md +12 -1
- package/core/shared/skills/do-document/references/deep_research.md +1 -2
- package/core/shared/skills/do-document/references/embedded-research.md +19 -7
- package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +19 -60
- package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
- package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +26 -1
- package/core/shared/skills/do-execute-plan/references/run_position.md +16 -2
- package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
- package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
- package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
- package/core/shared/skills/do-flow/SKILL.md +10 -11
- package/core/shared/skills/do-flow/references/task_classes.md +5 -5
- package/core/shared/skills/do-git/SKILL.md +4 -0
- package/core/shared/skills/do-implement/SKILL.md +13 -14
- package/core/shared/skills/do-plan/SKILL.md +34 -20
- package/core/shared/skills/do-test/SKILL.md +7 -4
- package/core/shared/templates/doflow/constitution-template.md +9 -3
- package/core/shared/templates/doflow/data-model-template.md +1 -1
- package/core/shared/templates/doflow/design-template.md +113 -16
- package/core/shared/templates/doflow/plan-template.md +11 -10
- package/core/shared/templates/doflow/question-log-template.md +14 -2
- package/core/shared/templates/doflow/requirement-template.md +1 -1
- package/core/shared/workflows/do-flow-chain.md +2 -0
- package/core/worker-evidence/antigravity/checks.json +14 -0
- package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
- package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
- package/core/worker-evidence/codex/checks.json +38 -0
- package/package.json +10 -6
- package/src/adapters/antigravity/index.js +143 -66
- package/src/adapters/claude/index.js +46 -5
- package/src/adapters/codex/agents.js +25 -15
- package/src/adapters/codex/hooks.js +39 -3
- package/src/adapters/codex/index.js +61 -13
- package/src/adapters/codex/native-projection.js +6 -2
- package/src/adapters/copilot/index.js +58 -23
- package/src/adapters/copy-tree.js +291 -46
- package/src/adapters/guidance-manifest.js +71 -0
- package/src/adapters/guidance-pointer.js +19 -0
- package/src/adapters/hook-commands.js +6 -10
- package/src/adapters/index.js +1 -0
- package/src/adapters/kiro/index.js +23 -12
- package/src/adapters/opencode/index.js +8 -8
- package/src/adapters/pi/hooks.js +2 -1
- package/src/adapters/pi/index.js +14 -7
- package/src/adapters/resolve-source.js +26 -2
- package/src/cli/commands/install.js +2 -1
- package/src/cli/commands/reconcile.js +53 -9
- package/src/cli/commands/remove.js +2 -2
- package/src/cli/commands/rollback.js +6 -3
- package/src/cli/commands/self-update.js +18 -9
- package/src/cli/commands/status.js +22 -3
- package/src/cli/commands/ui.js +99 -0
- package/src/cli/commands/update.js +2 -1
- package/src/cli/index.js +337 -32
- package/src/cli/runtime-commands.js +220 -60
- package/src/cli/shared.js +0 -2
- package/src/helper/git.js +1 -1
- package/src/helper/guardrails.js +1 -1
- package/src/helper/marker-merge.js +31 -13
- package/src/helper/package-ids.js +37 -0
- package/src/helper/semver.js +94 -0
- package/src/install/backup.js +105 -15
- package/src/install/context.js +1 -1
- package/src/install/targets.js +31 -16
- package/src/lifecycle/coverage.js +4 -16
- package/src/lifecycle/hook-tier.js +160 -0
- package/src/lifecycle/index.js +412 -78
- package/src/lifecycle/retired-harnesses.js +318 -0
- package/src/lifecycle/view.js +35 -15
- package/src/registry/index.js +551 -26
- package/src/registry/worker-ids.js +45 -0
- package/src/release/build-package.js +0 -3
- package/src/release/docs-blocks.js +472 -0
- package/src/release/preflight.js +425 -0
- package/src/release/rehearse.js +401 -0
- package/src/release/release-workflow.js +733 -0
- package/src/release/sync-plugin-versions.js +43 -7
- package/src/runtime/advice/bench-gate.js +25 -0
- package/src/runtime/advice/cli.js +59 -16
- package/src/runtime/advice/client.js +32 -9
- package/src/runtime/advice/config.js +98 -133
- package/src/runtime/advice/doctor.js +1 -1
- package/src/runtime/advice/elevation.js +97 -0
- package/src/runtime/advice/endpoint-guard.js +9 -3
- package/src/runtime/advice/mode.js +37 -0
- package/src/runtime/advice/record.js +18 -5
- package/src/runtime/advice/registry.js +4 -1
- package/src/runtime/advice/shadow-config.js +229 -0
- package/src/runtime/advice/shadow-dispatcher.js +276 -0
- package/src/runtime/brief-rules.js +81 -0
- package/src/runtime/brief-tools.js +111 -34
- package/src/runtime/candidate.js +20 -11
- package/src/runtime/capabilities/executor.js +450 -0
- package/src/runtime/capabilities/loopback.js +41 -0
- package/src/runtime/capabilities/sanitizer.js +88 -0
- package/src/runtime/capability-router.js +135 -3
- package/src/runtime/checkouts.js +73 -11
- package/src/runtime/claims.js +13 -7
- package/src/runtime/cli.js +196 -19
- package/src/runtime/config-home.js +42 -0
- package/src/runtime/context/arbitrate.js +767 -0
- package/src/runtime/context/delivery-store.js +4 -6
- package/src/runtime/context/emission-rules.js +118 -0
- package/src/runtime/context/identity.js +227 -0
- package/src/runtime/context/pointer.js +67 -12
- package/src/runtime/context/primary-command.js +117 -0
- package/src/runtime/context/primary.js +344 -0
- package/src/runtime/context/summary-store.js +655 -0
- package/src/runtime/context-pack.js +158 -79
- package/src/runtime/decision-register.js +13 -4
- package/src/runtime/dispatch/admission.js +392 -0
- package/src/runtime/dispatch/attempt-end.js +274 -0
- package/src/runtime/dispatch/command.js +628 -0
- package/src/runtime/dispatch/controls.js +904 -0
- package/src/runtime/dispatch/envelope.js +276 -0
- package/src/runtime/dispatch/grants.js +310 -0
- package/src/runtime/dispatch/integrate.js +617 -0
- package/src/runtime/dispatch/loop.js +1835 -0
- package/src/runtime/dispatch/store.js +352 -0
- package/src/runtime/dispatch/verify-run.js +201 -0
- package/src/runtime/evidence-ledger.js +52 -7
- package/src/runtime/failure/capture.js +3 -1
- package/src/runtime/failure/cli.js +25 -9
- package/src/runtime/failure/home.js +57 -30
- package/src/runtime/failure/store.js +7 -2
- package/src/runtime/feature-pointer.js +174 -0
- package/src/runtime/feature-resolve.js +13 -2
- package/src/runtime/fleet/command.js +77 -0
- package/src/runtime/fleet/projects.js +178 -0
- package/src/runtime/fleet/tree.js +383 -0
- package/src/runtime/freshness.js +33 -3
- package/src/runtime/git-memo.js +46 -0
- package/src/runtime/guidance/ablation.js +427 -0
- package/src/runtime/health.js +624 -18
- package/src/runtime/history-compactor.js +13 -3
- package/src/runtime/implementation-gate.js +281 -26
- package/src/runtime/intake/engine.js +677 -0
- package/src/runtime/intake/index.js +136 -0
- package/src/runtime/intake/manifest.js +140 -0
- package/src/runtime/intake/taxonomy.js +206 -0
- package/src/runtime/inventory/coverage.js +1 -1
- package/src/runtime/inventory/index.js +1 -1
- package/src/runtime/inventory/precedence.js +2 -2
- package/src/runtime/inventory/siblings.js +6 -4
- package/src/runtime/knowledge/code-corpus.js +277 -0
- package/src/runtime/knowledge/code-lexical.js +130 -0
- package/src/runtime/knowledge/code-retrieval.js +307 -0
- package/src/runtime/knowledge/code-validation.js +106 -0
- package/src/runtime/knowledge/gp3-gate.js +74 -0
- package/src/runtime/knowledge/graph-adapter.js +288 -0
- package/src/runtime/knowledge/graph-fallback.js +394 -0
- package/src/runtime/knowledge/hybrid.js +136 -8
- package/src/runtime/knowledge/retrieval.js +34 -1
- package/src/runtime/knowledge/rtk-compress.js +77 -0
- package/src/runtime/knowledge/semble-bridge.py +106 -0
- package/src/runtime/knowledge/semble-lifecycle.js +114 -0
- package/src/runtime/knowledge/semble-provider.js +365 -0
- package/src/runtime/lifecycle/cli.js +88 -10
- package/src/runtime/lifecycle/event-store.js +8 -6
- package/src/runtime/lifecycle/followup.js +38 -7
- package/src/runtime/lifecycle/overview.js +26 -2
- package/src/runtime/lifecycle/report-store.js +8 -10
- package/src/runtime/lifecycle/retention.js +95 -31
- package/src/runtime/lifecycle/root.js +9 -1
- package/src/runtime/lifecycle/status.js +34 -17
- package/src/runtime/lifecycle/store-upkeep.js +56 -7
- package/src/runtime/mask.js +134 -7
- package/src/runtime/mcp-consultations.js +40 -6
- package/src/runtime/model-router.js +79 -7
- package/src/runtime/models/catalogue.js +191 -0
- package/src/runtime/models/command.js +153 -0
- package/src/runtime/off-words.js +12 -0
- package/src/runtime/outcome.js +2 -2
- package/src/runtime/packages/command.js +1009 -0
- package/src/runtime/packages/contributions.js +116 -0
- package/src/runtime/packages/digest.js +132 -0
- package/src/runtime/packages/fetch.js +182 -0
- package/src/runtime/packages/grants.js +192 -0
- package/src/runtime/packages/index-file.js +134 -0
- package/src/runtime/packages/instruction.js +90 -0
- package/src/runtime/packages/manifest.js +331 -0
- package/src/runtime/packages/points.js +487 -0
- package/src/runtime/packages/programs.js +216 -0
- package/src/runtime/packages/scaffold.js +277 -0
- package/src/runtime/packages/source.js +90 -0
- package/src/runtime/packages/state.js +164 -0
- package/src/runtime/packages/store.js +449 -0
- package/src/runtime/packages/tar.js +106 -0
- package/src/runtime/packages/view.js +406 -0
- package/src/runtime/plan-check.js +59 -0
- package/src/runtime/plan-content.js +324 -0
- package/src/runtime/policy-init.js +118 -0
- package/src/runtime/profiles/doctor.js +96 -0
- package/src/runtime/profiles/resolve.js +244 -0
- package/src/runtime/profiles/rollback.js +254 -0
- package/src/runtime/profiles/sampler.js +41 -0
- package/src/runtime/readiness-record.js +16 -11
- package/src/runtime/readiness.js +46 -5
- package/src/runtime/research-request.js +50 -25
- package/src/runtime/retrieval-plan.js +10 -30
- package/src/runtime/retrieval-record.js +75 -5
- package/src/runtime/review/command.js +246 -0
- package/src/runtime/review/dominance.js +68 -0
- package/src/runtime/review/findings.js +434 -0
- package/src/runtime/review/identity.js +255 -0
- package/src/runtime/review/protocol-command.js +109 -0
- package/src/runtime/review/protocols.js +405 -0
- package/src/runtime/review/resolve-protocol.js +302 -0
- package/src/runtime/review/start.js +530 -0
- package/src/runtime/roadmap/attendance.js +137 -0
- package/src/runtime/roadmap/facts.js +479 -0
- package/src/runtime/roadmap/gates.js +273 -0
- package/src/runtime/roadmap/loop.js +1719 -0
- package/src/runtime/roadmap/merge.js +390 -0
- package/src/runtime/roadmap/rows.js +1122 -0
- package/src/runtime/roadmap/seed.js +1063 -0
- package/src/runtime/roadmap/worktree-cleanup.js +147 -0
- package/src/runtime/rules-report.js +336 -0
- package/src/runtime/scaffold/artifacts.js +60 -9
- package/src/runtime/scaffold/generate.js +21 -9
- package/src/runtime/settings/autopilot.js +106 -0
- package/src/runtime/settings/cli.js +280 -0
- package/src/runtime/settings/config-write.js +314 -0
- package/src/runtime/settings/files.js +271 -0
- package/src/runtime/settings/legacy.js +154 -0
- package/src/runtime/settings/lexical.js +56 -0
- package/src/runtime/settings/prepared-run.js +269 -0
- package/src/runtime/settings/project-layers.js +118 -0
- package/src/runtime/settings/refs.js +288 -0
- package/src/runtime/settings/registry.js +553 -0
- package/src/runtime/settings/resolve.js +713 -0
- package/src/runtime/settings/shared.js +37 -0
- package/src/runtime/settings/worker-billing.js +107 -0
- package/src/runtime/settings/write.js +269 -0
- package/src/runtime/slot-command.js +78 -0
- package/src/runtime/slots.js +671 -0
- package/src/runtime/source-version.js +1 -0
- package/src/runtime/state-root.js +217 -0
- package/src/runtime/storage/cli.js +12 -5
- package/src/runtime/storage/engine.js +9 -2
- package/src/runtime/storage/registry.js +14 -1
- package/src/runtime/storage/roots.js +66 -16
- package/src/runtime/storage/rules.js +82 -10
- package/src/runtime/storage/upkeep.js +10 -2
- package/src/runtime/storage/work-status.js +245 -26
- package/src/runtime/task-attempts.js +589 -69
- package/src/runtime/task-classifier.js +85 -14
- package/src/runtime/task-graph.js +373 -0
- package/src/runtime/task-run.js +778 -0
- package/src/runtime/task-scope.js +176 -55
- package/src/runtime/task-sources.js +147 -0
- package/src/runtime/task-state.js +373 -64
- package/src/runtime/team/admission.js +301 -0
- package/src/runtime/team/envelope.js +324 -0
- package/src/runtime/team/review-guard.js +156 -0
- package/src/runtime/trace/analyze.js +427 -0
- package/src/runtime/trace/baseline.js +770 -0
- package/src/runtime/trace/ledger.js +63 -140
- package/src/runtime/trace/otlp-export.js +568 -0
- package/src/runtime/trace/otlp-map.js +481 -0
- package/src/runtime/trace/spans.js +745 -0
- package/src/runtime/trace/telemetry.js +225 -0
- package/src/runtime/trace/views.js +1 -0
- package/src/runtime/transcripts/command.js +98 -0
- package/src/runtime/transcripts/cursor.js +326 -0
- package/src/runtime/transcripts/read.js +126 -0
- package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
- package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
- package/src/runtime/transcripts/readers/copilot-events.js +90 -0
- package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
- package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
- package/src/runtime/transcripts/registry.js +161 -0
- package/src/runtime/transcripts/stats.js +161 -0
- package/src/runtime/verb-view.js +267 -0
- package/src/runtime/verification/check-key.js +628 -0
- package/src/runtime/verification/contract-runner.js +546 -11
- package/src/runtime/verification/engine.js +499 -54
- package/src/runtime/verification/impact.js +617 -0
- package/src/runtime/verification/inflight.js +205 -0
- package/src/runtime/verification/registry.js +176 -0
- package/src/runtime/verification/reuse.js +184 -0
- package/src/runtime/verification/telemetry-emit.js +254 -0
- package/src/runtime/verification/test-selection.js +67 -7
- package/src/runtime/worker/active.js +90 -0
- package/src/runtime/worker/cancel.js +222 -0
- package/src/runtime/worker/catalogue.js +122 -0
- package/src/runtime/worker/command.js +677 -0
- package/src/runtime/worker/evidence.js +288 -0
- package/src/runtime/worker/launch.js +256 -0
- package/src/runtime/worker/models.js +137 -0
- package/src/runtime/worker/parsers.js +218 -0
- package/src/runtime/worker/probes/index.js +706 -0
- package/src/runtime/worker/process-tree.js +415 -0
- package/src/runtime/worker/quota.js +43 -0
- package/src/runtime/worker/readiness.js +713 -0
- package/src/runtime/worker/run-store.js +166 -0
- package/src/runtime/worker/supervisor.js +541 -0
- package/src/runtime/worker/worktree-check.js +252 -0
- package/src/runtime/workflow-engine.js +5 -1
- package/src/runtime/workflow-orchestrator.js +610 -67
- package/src/runtime/worktree.js +149 -22
- package/src/state/index.js +1 -1
- package/src/state/lockfile.js +64 -10
- package/src/state/run-lock.js +1 -1
- package/types/node-commonjs.d.ts +106 -7
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/gemini/hooks/hooks.json +0 -77
- package/core/harnesses/gemini/hooks/lib.sh +0 -302
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -10
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -10
- package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
- package/core/harnesses/gemini/hooks/session-end.sh +0 -5
- package/core/harnesses/gemini/hooks/session-start.sh +0 -7
- package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +0 -24
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +0 -91
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +0 -378
- package/core/shared/guidance/FLAGS.md +0 -25
- package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
- package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
- package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
- package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
- package/src/adapters/gemini/hooks.js +0 -251
- package/src/adapters/gemini/index.js +0 -429
package/src/runtime/worktree.js
CHANGED
|
@@ -27,6 +27,7 @@ const fs = require('node:fs');
|
|
|
27
27
|
const path = require('node:path');
|
|
28
28
|
const crypto = require('node:crypto');
|
|
29
29
|
const { execFileSync } = require('node:child_process');
|
|
30
|
+
const { memoGit } = require('./git-memo.js');
|
|
30
31
|
|
|
31
32
|
/** Worktree ids become branch names and directory names, so anything outside this set is a
|
|
32
33
|
* potential argument-injection or path-traversal vector. Rejecting loudly beats sanitising
|
|
@@ -56,30 +57,67 @@ function sha256File(file ) {
|
|
|
56
57
|
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
/** Bounds for git children (082 IC-016). Null fields leave execFileSync's defaults, which is the
|
|
61
|
+
* bench behaviour: no timeout and the parent's environment. */
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
/** What `removeVerified` observed after removing (082 IC-021). `errors` holds exit codes or error
|
|
65
|
+
* codes only, never git's output, so a record can carry it without leaking repository text. */
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
function git(
|
|
73
|
+
args ,
|
|
74
|
+
cwd ,
|
|
75
|
+
{ allowFailure = false, timeoutMs = null, env = null } = {},
|
|
76
|
+
) {
|
|
77
|
+
const opts = { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] };
|
|
78
|
+
if (timeoutMs != null) opts.timeout = timeoutMs;
|
|
79
|
+
if (env != null) opts.env = env;
|
|
60
80
|
try {
|
|
61
|
-
return {
|
|
62
|
-
ok: true,
|
|
63
|
-
stdout: execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }),
|
|
64
|
-
};
|
|
81
|
+
return { ok: true, stdout: execFileSync('git', args, opts) };
|
|
65
82
|
} catch (err ) {
|
|
66
83
|
if (!allowFailure) throw err;
|
|
67
|
-
return {
|
|
84
|
+
return {
|
|
85
|
+
ok: false,
|
|
86
|
+
stdout: '',
|
|
87
|
+
stderr: err.stderr ? String(err.stderr) : String(err.message),
|
|
88
|
+
// Exit status, else the signal of a timed-out child, else the spawn error code.
|
|
89
|
+
code: String(err.status ?? err.signal ?? err.code ?? 'unknown'),
|
|
90
|
+
};
|
|
68
91
|
}
|
|
69
92
|
}
|
|
70
93
|
|
|
71
94
|
class WorktreeManager {
|
|
72
95
|
repoRoot ;
|
|
73
96
|
worktreesDir ;
|
|
97
|
+
branchPrefix ;
|
|
98
|
+
gitBounds ;
|
|
74
99
|
|
|
75
100
|
/**
|
|
76
101
|
* @param {string} repoRoot repository whose worktrees are managed
|
|
77
102
|
* @param {string} [worktreesDir] override the sandbox root; defaults to `<repo>/.doflow/worktrees`,
|
|
78
103
|
* which is gitignored so sandboxes never appear as untracked noise in the parent tree.
|
|
104
|
+
* @param {object} [options] 082 IC-016: `branchPrefix` (default `task/`), and `timeoutMs` and
|
|
105
|
+
* `env` passed to every git child when given. Omitted, every bench path is unchanged.
|
|
79
106
|
*/
|
|
80
|
-
constructor(
|
|
107
|
+
constructor(
|
|
108
|
+
repoRoot ,
|
|
109
|
+
worktreesDir ,
|
|
110
|
+
{ branchPrefix = 'task/', timeoutMs = null, env = null } = {},
|
|
111
|
+
) {
|
|
81
112
|
this.repoRoot = repoRoot;
|
|
82
113
|
this.worktreesDir = worktreesDir || path.join(repoRoot, '.doflow', 'worktrees');
|
|
114
|
+
this.branchPrefix = branchPrefix;
|
|
115
|
+
this.gitBounds = { timeoutMs, env };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** The module's git helper with this manager's bounds applied. */
|
|
119
|
+
git(args , cwd , opts = {}) {
|
|
120
|
+
return git(args, cwd, { ...opts, ...this.gitBounds });
|
|
83
121
|
}
|
|
84
122
|
|
|
85
123
|
static assertSafeId(taskId ) {
|
|
@@ -95,13 +133,13 @@ class WorktreeManager {
|
|
|
95
133
|
|
|
96
134
|
branchFor(taskId ) {
|
|
97
135
|
WorktreeManager.assertSafeId(taskId);
|
|
98
|
-
return
|
|
136
|
+
return `${this.branchPrefix}${taskId}`;
|
|
99
137
|
}
|
|
100
138
|
|
|
101
139
|
/** Whether git itself considers this path a registered worktree — the directory existing on disk
|
|
102
140
|
* is not the same question, which is the bug fixed here. */
|
|
103
141
|
isRegistered(wtPath ) {
|
|
104
|
-
const res = git(['worktree', 'list', '--porcelain'], this.repoRoot, { allowFailure: true });
|
|
142
|
+
const res = this.git(['worktree', 'list', '--porcelain'], this.repoRoot, { allowFailure: true });
|
|
105
143
|
if (!res.ok) return false;
|
|
106
144
|
const resolved = path.resolve(wtPath);
|
|
107
145
|
return res.stdout
|
|
@@ -110,6 +148,52 @@ class WorktreeManager {
|
|
|
110
148
|
.some((l ) => path.resolve(l.slice('worktree '.length).trim()) === resolved);
|
|
111
149
|
}
|
|
112
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Drops git's administrative record for `wtPath` when its directory is gone, and no other. A
|
|
153
|
+
* blanket `git worktree prune` also forgets every unrelated worktree of the user's whose directory
|
|
154
|
+
* is merely missing (an unmounted drive, a directory deleted by hand), which is not this
|
|
155
|
+
* manager's to decide. Git has no per-path prune, so this reads `<common-dir>/worktrees/<name>/gitdir`
|
|
156
|
+
* (the file git itself reads to decide a record is prunable) and removes only the matching record.
|
|
157
|
+
* A locked record, or one whose `.git` file is still present, is left alone.
|
|
158
|
+
*/
|
|
159
|
+
dropStaleRecord(wtPath ) {
|
|
160
|
+
// The repository's common directory does not move while one call runs; a failed read is not kept.
|
|
161
|
+
const routing = this.gitBounds.env ?? process.env;
|
|
162
|
+
const common = memoGit(`commonDir:${this.repoRoot}:${routing.GIT_DIR ?? ''}:${routing.GIT_COMMON_DIR ?? ''}`, () => {
|
|
163
|
+
const res = this.git(['rev-parse', '--git-common-dir'], this.repoRoot, { allowFailure: true });
|
|
164
|
+
return res.ok ? res.stdout.trim() : undefined;
|
|
165
|
+
});
|
|
166
|
+
if (common === undefined) return;
|
|
167
|
+
const admin = path.join(path.resolve(this.repoRoot, common), 'worktrees');
|
|
168
|
+
let names ;
|
|
169
|
+
try {
|
|
170
|
+
names = fs.readdirSync(admin);
|
|
171
|
+
} catch {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// git writes the real path of the parent, so compare with that of the worktree's parent too.
|
|
175
|
+
const target = path.resolve(wtPath);
|
|
176
|
+
let realTarget = target;
|
|
177
|
+
try {
|
|
178
|
+
realTarget = path.join(fs.realpathSync(path.dirname(target)), path.basename(target));
|
|
179
|
+
} catch {
|
|
180
|
+
// The parent is gone as well: only the plain form can match.
|
|
181
|
+
}
|
|
182
|
+
for (const name of names) {
|
|
183
|
+
const record = path.join(admin, name);
|
|
184
|
+
try {
|
|
185
|
+
if (fs.existsSync(path.join(record, 'locked'))) continue;
|
|
186
|
+
// A relative gitdir (worktree.useRelativePaths) is relative to the record directory.
|
|
187
|
+
const dotGit = path.resolve(record, fs.readFileSync(path.join(record, 'gitdir'), 'utf8').trim());
|
|
188
|
+
if (path.dirname(dotGit) !== target && path.dirname(dotGit) !== realTarget) continue;
|
|
189
|
+
if (fs.existsSync(dotGit)) continue;
|
|
190
|
+
fs.rmSync(record, { recursive: true, force: true });
|
|
191
|
+
} catch {
|
|
192
|
+
// An unreadable record is not ours to judge.
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
113
197
|
/**
|
|
114
198
|
* Create an isolated worktree, reusing it only when git agrees it is registered. Returns the
|
|
115
199
|
* absolute path.
|
|
@@ -120,23 +204,23 @@ class WorktreeManager {
|
|
|
120
204
|
|
|
121
205
|
if (this.isRegistered(wtPath)) return wtPath;
|
|
122
206
|
|
|
123
|
-
// A leftover directory that git does not know about blocks `worktree add`. Clear it, and
|
|
124
|
-
//
|
|
207
|
+
// A leftover directory that git does not know about blocks `worktree add`. Clear it, and this
|
|
208
|
+
// path's stale administrative record, before trying; other worktrees' records are not touched.
|
|
125
209
|
if (fs.existsSync(wtPath)) fs.rmSync(wtPath, { recursive: true, force: true });
|
|
126
|
-
|
|
210
|
+
this.dropStaleRecord(wtPath);
|
|
127
211
|
|
|
128
212
|
fs.mkdirSync(this.worktreesDir, { recursive: true });
|
|
129
213
|
|
|
130
|
-
const branchExists = git(['rev-parse', '--verify', '--quiet', branch], this.repoRoot, { allowFailure: true }).ok;
|
|
214
|
+
const branchExists = this.git(['rev-parse', '--verify', '--quiet', branch], this.repoRoot, { allowFailure: true }).ok;
|
|
131
215
|
const args = branchExists
|
|
132
216
|
? ['worktree', 'add', wtPath, branch]
|
|
133
217
|
: ['worktree', 'add', '-b', branch, wtPath, baseRef];
|
|
134
|
-
git(args, this.repoRoot);
|
|
218
|
+
this.git(args, this.repoRoot);
|
|
135
219
|
|
|
136
220
|
// Persist the base as a resolved SHA. A symbolic ref is useless later: inside the worktree
|
|
137
221
|
// `HEAD` moves with every commit the task makes, so diffing against it reports nothing. The
|
|
138
222
|
// file also survives a process boundary, since grading may run separately from execution.
|
|
139
|
-
const baseSha = git(['rev-parse', baseRef], this.repoRoot).stdout.trim();
|
|
223
|
+
const baseSha = this.git(['rev-parse', baseRef], this.repoRoot).stdout.trim();
|
|
140
224
|
fs.writeFileSync(path.join(wtPath, BASE_FILE), `${baseSha}\n`);
|
|
141
225
|
return wtPath;
|
|
142
226
|
}
|
|
@@ -250,7 +334,7 @@ class WorktreeManager {
|
|
|
250
334
|
/** Repo HEAD, or null outside a repository. Recorded for readability; the per-skill hashes are
|
|
251
335
|
* the actual provenance, and they stay correct on a dirty tree where a commit id does not. */
|
|
252
336
|
headCommit() {
|
|
253
|
-
const res = git(['rev-parse', 'HEAD'], this.repoRoot, { allowFailure: true });
|
|
337
|
+
const res = this.git(['rev-parse', 'HEAD'], this.repoRoot, { allowFailure: true });
|
|
254
338
|
return res.ok ? res.stdout.trim() : null;
|
|
255
339
|
}
|
|
256
340
|
|
|
@@ -269,11 +353,54 @@ class WorktreeManager {
|
|
|
269
353
|
const wtPath = this.pathFor(taskId);
|
|
270
354
|
const branch = this.branchFor(taskId);
|
|
271
355
|
|
|
272
|
-
git(['worktree', 'remove', '--force', wtPath], this.repoRoot, { allowFailure: true });
|
|
356
|
+
this.git(['worktree', 'remove', '--force', wtPath], this.repoRoot, { allowFailure: true });
|
|
273
357
|
if (fs.existsSync(wtPath)) fs.rmSync(wtPath, { recursive: true, force: true });
|
|
274
|
-
|
|
358
|
+
this.dropStaleRecord(wtPath);
|
|
275
359
|
// Deleting the branch is what keeps repeated bench runs from accumulating one branch per case.
|
|
276
|
-
git(['branch', '-D', branch], this.repoRoot, { allowFailure: true });
|
|
360
|
+
this.git(['branch', '-D', branch], this.repoRoot, { allowFailure: true });
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* `remove`'s steps, then a reading of what is actually gone (082 IC-021). Never throws for a
|
|
365
|
+
* failed step: the failure is an entry in `errors` and a false `verified`, so a caller can keep
|
|
366
|
+
* the worktree and list it by path instead of assuming it was removed.
|
|
367
|
+
*/
|
|
368
|
+
removeVerified(taskId ) {
|
|
369
|
+
const wtPath = this.pathFor(taskId);
|
|
370
|
+
const branch = this.branchFor(taskId);
|
|
371
|
+
const errors = [];
|
|
372
|
+
let threw = false;
|
|
373
|
+
const step = (args ) => {
|
|
374
|
+
const res = this.git(args, this.repoRoot, { allowFailure: true });
|
|
375
|
+
if (!res.ok) errors.push(res.code );
|
|
376
|
+
return res;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
step(['worktree', 'remove', '--force', wtPath]);
|
|
380
|
+
if (fs.existsSync(wtPath)) {
|
|
381
|
+
try {
|
|
382
|
+
fs.rmSync(wtPath, { recursive: true, force: true });
|
|
383
|
+
} catch (err ) {
|
|
384
|
+
threw = true;
|
|
385
|
+
errors.push(String(err?.code ?? 'unknown'));
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
this.dropStaleRecord(wtPath);
|
|
389
|
+
// `branch -D` fails harmlessly when the branch never existed; only the reading below decides.
|
|
390
|
+
step(['branch', '-D', branch]);
|
|
391
|
+
|
|
392
|
+
const pathGone = !fs.existsSync(wtPath);
|
|
393
|
+
const branchGone = !this.git(['rev-parse', '--verify', '--quiet', `refs/heads/${branch}`], this.repoRoot, { allowFailure: true }).ok;
|
|
394
|
+
const unregistered = !this.isRegistered(wtPath);
|
|
395
|
+
return {
|
|
396
|
+
path: wtPath,
|
|
397
|
+
branch,
|
|
398
|
+
pathGone,
|
|
399
|
+
branchGone,
|
|
400
|
+
unregistered,
|
|
401
|
+
verified: !threw && pathGone && branchGone && unregistered,
|
|
402
|
+
errors,
|
|
403
|
+
};
|
|
277
404
|
}
|
|
278
405
|
|
|
279
406
|
/**
|
|
@@ -292,9 +419,9 @@ class WorktreeManager {
|
|
|
292
419
|
if (!base) {
|
|
293
420
|
return { diff: null, untracked: [], error: `no recorded base for ${taskId}; pass baseRef explicitly` };
|
|
294
421
|
}
|
|
295
|
-
const committed = git(['diff', `${base}...HEAD`], wtPath, { allowFailure: true });
|
|
296
|
-
const working = git(['diff', 'HEAD'], wtPath, { allowFailure: true });
|
|
297
|
-
const untracked = git(['ls-files', '--others', '--exclude-standard'], wtPath, { allowFailure: true });
|
|
422
|
+
const committed = this.git(['diff', `${base}...HEAD`], wtPath, { allowFailure: true });
|
|
423
|
+
const working = this.git(['diff', 'HEAD'], wtPath, { allowFailure: true });
|
|
424
|
+
const untracked = this.git(['ls-files', '--others', '--exclude-standard'], wtPath, { allowFailure: true });
|
|
298
425
|
|
|
299
426
|
if (!committed.ok && !working.ok) {
|
|
300
427
|
return { diff: null, untracked: [], error: committed.stderr || working.stderr };
|
package/src/state/index.js
CHANGED
|
@@ -174,7 +174,7 @@ function migrateLegacyManifest({ root, scope, scopeRoot, claudeDir, now = new Da
|
|
|
174
174
|
for (const resource of resources) if (!existing.has(ownershipKey(resource))) current.resources.push(resource);
|
|
175
175
|
|
|
176
176
|
// The legacy manifest's MCP selection was owned by Claude's config flow. Do not infer an
|
|
177
|
-
// equivalent selection for Codex
|
|
177
|
+
// equivalent selection for Codex merely because it happened to share the old file.
|
|
178
178
|
if (Array.isArray(legacy.mcpServers) && !Object.hasOwn(current.mcpSelections, 'claude')) current.mcpSelections.claude = [...legacy.mcpServers];
|
|
179
179
|
for (const [harness, status] of Object.entries(legacy.tools) ) {
|
|
180
180
|
if (status?.installed && !current.targets[harness]) current.targets[harness] = { installed: true, lastUpdated: status.last_updated ?? null, imported: true };
|
package/src/state/lockfile.js
CHANGED
|
@@ -11,8 +11,13 @@ const fs = require('node:fs');
|
|
|
11
11
|
const os = require('node:os');
|
|
12
12
|
const path = require('node:path');
|
|
13
13
|
const { atomicJsonWrite } = require('./index.js');
|
|
14
|
+
const { isPackageId } = require('../helper/package-ids.js');
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
// Version plan (122 IC-007): a lock holding no third-party package stays version 1, byte for byte what
|
|
17
|
+
// every earlier DoFlow wrote and reads; one holding packages is version 2, which an older DoFlow
|
|
18
|
+
// refuses with its existing `Unsupported doflow.lock version` (the fail-closed direction).
|
|
19
|
+
const LOCK_VERSION = 2;
|
|
20
|
+
const LOCK_VERSION_BASE = 1;
|
|
16
21
|
const LOCK_FILE = 'doflow.lock';
|
|
17
22
|
|
|
18
23
|
function lockRoot({ scope, projectRoot = '.', homeDir = os.homedir() } ) {
|
|
@@ -31,7 +36,7 @@ function stable(value ) {
|
|
|
31
36
|
function defaultLock({ scope, scopeRoot } ) {
|
|
32
37
|
if (scope !== 'project' && scope !== 'global') throw new Error(`Invalid lock scope: '${scope}'`);
|
|
33
38
|
return {
|
|
34
|
-
version:
|
|
39
|
+
version: LOCK_VERSION_BASE,
|
|
35
40
|
scope,
|
|
36
41
|
scopeRoot: path.resolve(scopeRoot),
|
|
37
42
|
generatedAt: null,
|
|
@@ -42,9 +47,46 @@ function defaultLock({ scope, scopeRoot } )
|
|
|
42
47
|
};
|
|
43
48
|
}
|
|
44
49
|
|
|
50
|
+
const DIGEST = /^[0-9a-f]{64}$/;
|
|
51
|
+
const ISO_UTC = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
|
|
52
|
+
const SOURCE_TYPES = ['local', 'git', 'npm', 'bundled'];
|
|
53
|
+
const PACKAGE_FIELDS = ['id', 'version', 'source', 'contentDigest', 'capabilityDigest', 'enabled', 'addedAt'];
|
|
54
|
+
|
|
55
|
+
/** The package rows of a lock: a v1 lock, an absent section and no lock all read as `[]`. */
|
|
56
|
+
function lockPackages(lock ) {
|
|
57
|
+
return Array.isArray(lock?.packages) ? lock.packages : [];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Refuse a malformed `packages` section, naming the field. The row is a closed shape (IC-007). */
|
|
61
|
+
function validatePackages(value ) {
|
|
62
|
+
if (value.scope === 'project') throw new Error('Invalid doflow.lock: packages is not allowed in a project-scope lock');
|
|
63
|
+
if (!Array.isArray(value.packages)) throw new Error('Invalid doflow.lock: packages must be an array');
|
|
64
|
+
const seen = new Set ();
|
|
65
|
+
value.packages.forEach((row , index ) => {
|
|
66
|
+
const at = `packages[${index}]`;
|
|
67
|
+
const bad = (field , why ) => new Error(`Invalid doflow.lock: ${at}.${field} ${why}`);
|
|
68
|
+
if (!row || typeof row !== 'object' || Array.isArray(row)) throw new Error(`Invalid doflow.lock: ${at} must be an object`);
|
|
69
|
+
for (const key of Object.keys(row)) if (!PACKAGE_FIELDS.includes(key)) throw bad(key, 'is not a package field');
|
|
70
|
+
if (!isPackageId(row.id)) throw bad('id', 'must be a package id');
|
|
71
|
+
if (seen.has(row.id)) throw bad('id', `'${row.id}' is a duplicate`);
|
|
72
|
+
seen.add(row.id);
|
|
73
|
+
if (typeof row.version !== 'string' || row.version === '') throw bad('version', 'must be a non-empty string');
|
|
74
|
+
if (!row.source || typeof row.source !== 'object' || Array.isArray(row.source) || !SOURCE_TYPES.includes(row.source.type)) {
|
|
75
|
+
throw bad('source', `must be an object whose type is one of ${SOURCE_TYPES.join(', ')}`);
|
|
76
|
+
}
|
|
77
|
+
for (const field of ['contentDigest', 'capabilityDigest']) {
|
|
78
|
+
if (typeof row[field] !== 'string' || !DIGEST.test(row[field])) throw bad(field, 'must be 64 lowercase hex characters');
|
|
79
|
+
}
|
|
80
|
+
if (typeof row.enabled !== 'boolean') throw bad('enabled', 'must be a boolean');
|
|
81
|
+
if (typeof row.addedAt !== 'string' || !ISO_UTC.test(row.addedAt) || Number.isNaN(Date.parse(row.addedAt))) {
|
|
82
|
+
throw bad('addedAt', 'must be an ISO 8601 UTC time');
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
45
87
|
function validateLock(value ) {
|
|
46
88
|
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Invalid doflow.lock: expected object');
|
|
47
|
-
if (value.version !== LOCK_VERSION) throw new Error(`Unsupported doflow.lock version: '${value.version}'`);
|
|
89
|
+
if (value.version !== LOCK_VERSION_BASE && value.version !== LOCK_VERSION) throw new Error(`Unsupported doflow.lock version: '${value.version}'`);
|
|
48
90
|
if (value.scope !== 'project' && value.scope !== 'global') throw new Error(`Invalid doflow.lock scope: '${value.scope}'`);
|
|
49
91
|
if (typeof value.scopeRoot !== 'string' || !path.isAbsolute(value.scopeRoot)) throw new Error('Invalid doflow.lock: scopeRoot must be absolute');
|
|
50
92
|
for (const field of ['targets', 'assets']) {
|
|
@@ -58,6 +100,7 @@ function validateLock(value ) {
|
|
|
58
100
|
throw new Error(`Invalid doflow.lock: mcpSelections.${harness} must be an array of server ids`);
|
|
59
101
|
}
|
|
60
102
|
}
|
|
103
|
+
if ('packages' in value) validatePackages(value);
|
|
61
104
|
return value;
|
|
62
105
|
}
|
|
63
106
|
|
|
@@ -72,7 +115,14 @@ function writeLock(options , lock , { fsImpl = fs } =
|
|
|
72
115
|
// Validate the caller's document first so a foreign version fails loudly here rather than
|
|
73
116
|
// being silently normalized into something no future reader can distinguish.
|
|
74
117
|
validateLock(lock);
|
|
75
|
-
|
|
118
|
+
// The `packages` key is written only when a package is recorded, and then the file is version 2;
|
|
119
|
+
// otherwise it is the version 1 bytes every earlier DoFlow wrote. `stable` orders arrays by their
|
|
120
|
+
// JSON text, so the rows are put back in `id` order afterwards.
|
|
121
|
+
const packages = lockPackages(lock);
|
|
122
|
+
const { packages: _omit, ...rest } = lock;
|
|
123
|
+
const document = stable(packages.length ? { ...rest, version: LOCK_VERSION, packages } : { ...rest, version: LOCK_VERSION_BASE });
|
|
124
|
+
if (packages.length) document.packages = document.packages.sort((a , b ) => (a.id < b.id ? -1 : 1));
|
|
125
|
+
return atomicJsonWrite(lockPath(options), document, { fsImpl });
|
|
76
126
|
}
|
|
77
127
|
|
|
78
128
|
/** The lock's MCP rows for the harnesses it targets. A row naming a harness outside `targets` is
|
|
@@ -82,8 +132,11 @@ function pinnedSelections(lock ) {
|
|
|
82
132
|
return Object.fromEntries(Object.entries(lock?.mcpSelections ?? {}).filter(([harness]) => targets.has(harness)));
|
|
83
133
|
}
|
|
84
134
|
|
|
85
|
-
/**
|
|
86
|
-
|
|
135
|
+
/** The facts of a package row that make drift: its source and the time it was added are record keeping. */
|
|
136
|
+
const packageFacts = (row ) => ({ version: row.version, contentDigest: row.contentDigest, capabilityDigest: row.capabilityDigest, enabled: row.enabled });
|
|
137
|
+
|
|
138
|
+
/** Compare two lock documents. Arrays are keyed by identity (assets and packages by id, targets by
|
|
139
|
+
* harness, fallback to full JSON), packages on their `packageFacts` only, mcpSelections by harness, where an absent row (`null`) and a recorded
|
|
87
140
|
* empty selection (`[]`) differ. Returns {added, removed, changed} per section plus a clean flag. */
|
|
88
141
|
function diffLocks(previousLock , nextLock ) {
|
|
89
142
|
// Compare in the order writeLock stores, so a document built in memory never differs from the
|
|
@@ -91,13 +144,13 @@ function diffLocks(previousLock , nextLock ) {
|
|
|
91
144
|
const previous = previousLock && stable(previousLock);
|
|
92
145
|
const next = nextLock && stable(nextLock);
|
|
93
146
|
const key = (entry ) => entry?.id ?? entry?.harness ?? JSON.stringify(entry);
|
|
94
|
-
const sectionDiff = (before , after ) => {
|
|
147
|
+
const sectionDiff = (before , after , compared = (entry) => entry) => {
|
|
95
148
|
const beforeKeys = new Map ((before || []).map((entry ) => [key(entry), entry]));
|
|
96
149
|
const afterKeys = new Map ((after || []).map((entry ) => [key(entry), entry]));
|
|
97
150
|
const added = [...afterKeys].filter(([k]) => !beforeKeys.has(k)).map(([, v]) => v);
|
|
98
151
|
const removed = [...beforeKeys].filter(([k]) => !afterKeys.has(k)).map(([, v]) => v);
|
|
99
152
|
const changed = [...afterKeys]
|
|
100
|
-
.filter(([k, v]) => beforeKeys.has(k) && JSON.stringify(beforeKeys.get(k)) !== JSON.stringify(v))
|
|
153
|
+
.filter(([k, v]) => beforeKeys.has(k) && JSON.stringify(compared(beforeKeys.get(k))) !== JSON.stringify(compared(v)))
|
|
101
154
|
.map(([, v]) => v);
|
|
102
155
|
return { added, removed, changed };
|
|
103
156
|
};
|
|
@@ -110,13 +163,14 @@ function diffLocks(previousLock , nextLock ) {
|
|
|
110
163
|
const result = {
|
|
111
164
|
targets: sectionDiff(previous?.targets, next?.targets),
|
|
112
165
|
assets: sectionDiff(previous?.assets, next?.assets),
|
|
166
|
+
packages: sectionDiff(previous?.packages, next?.packages, (row) => stable(packageFacts(row))),
|
|
113
167
|
mcpSelections: { changed: mcpChanged },
|
|
114
168
|
meta: {
|
|
115
169
|
sourceVersion: (previous?.sourceVersion ?? null) !== (next?.sourceVersion ?? null)
|
|
116
170
|
? { before: previous?.sourceVersion ?? null, after: next?.sourceVersion ?? null } : null,
|
|
117
171
|
},
|
|
118
172
|
};
|
|
119
|
-
result.clean = ['targets', 'assets'].every((name) => {
|
|
173
|
+
result.clean = ['targets', 'assets', 'packages'].every((name) => {
|
|
120
174
|
const section = result[name];
|
|
121
175
|
return section.added.length === 0 && section.removed.length === 0 && section.changed.length === 0;
|
|
122
176
|
}) && result.mcpSelections.changed.length === 0 && result.meta.sourceVersion === null;
|
|
@@ -132,5 +186,5 @@ function removeLock(options , { fsImpl = fs } = {})
|
|
|
132
186
|
}
|
|
133
187
|
|
|
134
188
|
module.exports = {
|
|
135
|
-
LOCK_VERSION, LOCK_FILE, lockRoot, lockPath, defaultLock, validateLock, readLock, writeLock, pinnedSelections, diffLocks, removeLock,
|
|
189
|
+
LOCK_VERSION, LOCK_VERSION_BASE, LOCK_FILE, lockRoot, lockPath, defaultLock, validateLock, lockPackages, readLock, writeLock, pinnedSelections, diffLocks, removeLock,
|
|
136
190
|
};
|
package/src/state/run-lock.js
CHANGED
|
@@ -404,5 +404,5 @@ function holdHandle({ fsImpl, root, stateDir, lockPath, token, hostname, firstCr
|
|
|
404
404
|
}
|
|
405
405
|
|
|
406
406
|
module.exports = {
|
|
407
|
-
acquireRunLock, RunLockTimeoutError, RunLockError, RunLockLostError, runLockPath, formatRunLockMessage,
|
|
407
|
+
acquireRunLock, RunLockTimeoutError, RunLockError, RunLockLostError, runLockPath, formatRunLockMessage, defaultIsAlive,
|
|
408
408
|
};
|
package/types/node-commonjs.d.ts
CHANGED
|
@@ -5,26 +5,80 @@
|
|
|
5
5
|
// the surface the migrated modules actually touch. Extend it as more modules migrate, not ahead of
|
|
6
6
|
// need. See agent-docs/BRIEF_GOAL_DOFLOW.md for the migration roadmap.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
type TestCallback = (t?: any, done?: any) => any;
|
|
9
|
+
interface TestFn {
|
|
10
|
+
(name: string, fn?: TestCallback): any;
|
|
11
|
+
(name: string, opts: any, fn?: TestCallback): any;
|
|
12
|
+
(fn: TestCallback): any;
|
|
13
|
+
skip: TestFn;
|
|
14
|
+
todo: TestFn;
|
|
15
|
+
only: TestFn;
|
|
16
|
+
}
|
|
17
|
+
interface NodeTestModule extends TestFn {
|
|
18
|
+
test: TestFn;
|
|
19
|
+
describe: TestFn;
|
|
20
|
+
it: TestFn;
|
|
21
|
+
suite: TestFn;
|
|
22
|
+
before(fn: TestCallback, opts?: any): void;
|
|
23
|
+
after(fn: TestCallback, opts?: any): void;
|
|
24
|
+
beforeEach(fn: TestCallback, opts?: any): void;
|
|
25
|
+
afterEach(fn: TestCallback, opts?: any): void;
|
|
26
|
+
mock: any;
|
|
27
|
+
run: any;
|
|
28
|
+
[key: string]: any;
|
|
29
|
+
}
|
|
30
|
+
interface NodeHttpModule {
|
|
31
|
+
createServer(listener?: (req?: any, res?: any) => any): any;
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
}
|
|
34
|
+
interface NodeNetModule {
|
|
35
|
+
createServer(opts?: any, listener?: (socket?: any) => any): any;
|
|
36
|
+
[key: string]: any;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
declare var require: {
|
|
40
|
+
(id: 'node:test'): NodeTestModule;
|
|
41
|
+
(id: 'node:http' | 'http'): NodeHttpModule;
|
|
42
|
+
(id: 'node:net' | 'net'): NodeNetModule;
|
|
43
|
+
(id: string): any;
|
|
44
|
+
main: any;
|
|
45
|
+
resolve(id: string, options?: any): string;
|
|
46
|
+
cache: Record<string, any>;
|
|
47
|
+
};
|
|
9
48
|
|
|
10
|
-
declare var module: { exports: any };
|
|
49
|
+
declare var module: { exports: any; parent?: any; filename?: string; [key: string]: any };
|
|
11
50
|
|
|
12
51
|
declare var process: {
|
|
13
52
|
features?: { typescript?: false | 'strip' | 'transform' };
|
|
14
53
|
env: Record<string, string | undefined>;
|
|
15
54
|
platform: string;
|
|
16
55
|
execPath: string;
|
|
56
|
+
execArgv: string[];
|
|
17
57
|
version: string;
|
|
18
58
|
pid: number;
|
|
59
|
+
ppid: number;
|
|
19
60
|
argv: string[];
|
|
20
61
|
cwd(): string;
|
|
21
62
|
exit(code?: number): never;
|
|
22
63
|
exitCode?: number;
|
|
23
64
|
kill(pid: number, signal?: string | number): void;
|
|
24
65
|
hrtime: { (time?: [number, number]): [number, number]; bigint(): bigint };
|
|
25
|
-
stdin: { isTTY?: boolean; isRaw?: boolean; setRawMode(mode: boolean): void };
|
|
26
|
-
stdout: { write(chunk:
|
|
27
|
-
stderr: { write(chunk:
|
|
66
|
+
stdin: { isTTY?: boolean; isRaw?: boolean; setRawMode(mode: boolean): void; [key: string]: any };
|
|
67
|
+
stdout: { write(chunk: any, ...rest: any[]): boolean; isTTY?: boolean; columns?: number; [key: string]: any };
|
|
68
|
+
stderr: { write(chunk: any, ...rest: any[]): boolean; [key: string]: any };
|
|
69
|
+
arch: string;
|
|
70
|
+
versions: Record<string, string>;
|
|
71
|
+
memoryUsage(): { rss: number; heapTotal: number; heapUsed: number; external: number; [key: string]: number };
|
|
72
|
+
resourceUsage(): Record<string, number>;
|
|
73
|
+
uptime(): number;
|
|
74
|
+
umask(mask?: number): number;
|
|
75
|
+
getuid?(): number;
|
|
76
|
+
nextTick(callback: (...args: any[]) => void, ...args: any[]): void;
|
|
77
|
+
emitWarning(warning: any, ...rest: any[]): void;
|
|
78
|
+
once(event: string, listener: (...args: any[]) => void): any;
|
|
79
|
+
off(event: string, listener: (...args: any[]) => void): any;
|
|
80
|
+
emit(event: string, ...args: any[]): boolean;
|
|
81
|
+
chdir(directory: string): void;
|
|
28
82
|
on(event: string, listener: (...args: any[]) => void): any;
|
|
29
83
|
removeListener(event: string, listener: (...args: any[]) => void): any;
|
|
30
84
|
};
|
|
@@ -38,25 +92,47 @@ declare var console: {
|
|
|
38
92
|
log(...args: any[]): void;
|
|
39
93
|
error(...args: any[]): void;
|
|
40
94
|
warn(...args: any[]): void;
|
|
95
|
+
info(...args: any[]): void;
|
|
96
|
+
debug(...args: any[]): void;
|
|
97
|
+
table(...args: any[]): void;
|
|
98
|
+
[key: string]: any;
|
|
41
99
|
};
|
|
42
100
|
|
|
43
101
|
interface Buffer extends Uint8Array {
|
|
44
102
|
toString(encoding?: string, start?: number, end?: number): string;
|
|
45
103
|
equals(other: Uint8Array): boolean;
|
|
46
104
|
subarray(begin?: number, end?: number): Buffer;
|
|
105
|
+
readUInt16LE(offset?: number): number;
|
|
106
|
+
readUInt32LE(offset?: number): number;
|
|
107
|
+
readInt32LE(offset?: number): number;
|
|
108
|
+
readBigInt64LE(offset?: number): bigint;
|
|
109
|
+
readUInt8(offset?: number): number;
|
|
110
|
+
readUInt16BE(offset?: number): number;
|
|
111
|
+
readUInt32BE(offset?: number): number;
|
|
112
|
+
writeUInt8(value: number, offset?: number): number;
|
|
113
|
+
writeUInt16LE(value: number, offset?: number): number;
|
|
114
|
+
writeUInt32LE(value: number, offset?: number): number;
|
|
115
|
+
writeUInt32BE(value: number, offset?: number): number;
|
|
116
|
+
write(string: string, offset?: number, length?: number, encoding?: string): number;
|
|
117
|
+
slice(begin?: number, end?: number): Buffer;
|
|
118
|
+
toJSON(): { type: 'Buffer'; data: number[] };
|
|
47
119
|
}
|
|
48
120
|
declare var Buffer: {
|
|
49
|
-
alloc(size: number): Buffer;
|
|
50
|
-
from(data: any,
|
|
121
|
+
alloc(size: number, fill?: any, encoding?: string): Buffer;
|
|
122
|
+
from(data: any, encodingOrOffset?: any, length?: number): Buffer;
|
|
51
123
|
byteLength(string: string, encoding?: string): number;
|
|
52
124
|
isBuffer(value: any): boolean;
|
|
53
125
|
allocUnsafe(size: number): Buffer;
|
|
54
126
|
concat(list: Uint8Array[], totalLength?: number): Buffer;
|
|
127
|
+
compare(a: Uint8Array, b: Uint8Array): number;
|
|
128
|
+
prototype: Buffer;
|
|
55
129
|
};
|
|
56
130
|
|
|
57
131
|
// Timers and WHATWG URL, as the migrated .ts modules use them (opaque handle; parsed URL fields only).
|
|
58
132
|
declare function setTimeout(callback: (...args: any[]) => void, ms?: number, ...args: any[]): any;
|
|
59
133
|
declare function clearTimeout(handle: any): void;
|
|
134
|
+
declare function setInterval(callback: (...args: any[]) => void, ms?: number, ...args: any[]): any;
|
|
135
|
+
declare function clearInterval(handle: any): void;
|
|
60
136
|
declare var URL: {
|
|
61
137
|
new (input: string, base?: string): { protocol: string; username: string; password: string; hostname: string; [key: string]: any };
|
|
62
138
|
};
|
|
@@ -66,3 +142,26 @@ declare var performance: { now(): number };
|
|
|
66
142
|
|
|
67
143
|
// structuredClone (workflow-orchestrator previewHandoff deep-copies a run program).
|
|
68
144
|
declare function structuredClone<T>(value: T): T;
|
|
145
|
+
|
|
146
|
+
// Ambient globals used by test/ and bench/ (all present in Node >=24; declared loosely, no @types/node).
|
|
147
|
+
declare function setImmediate(callback: (...args: any[]) => void, ...args: any[]): any;
|
|
148
|
+
declare function clearImmediate(handle: any): void;
|
|
149
|
+
declare function queueMicrotask(callback: () => void): void;
|
|
150
|
+
declare function fetch(input: any, init?: any): Promise<any>;
|
|
151
|
+
declare var AbortController: { new (): { signal: any; abort(reason?: any): void }; prototype: any };
|
|
152
|
+
declare var AbortSignal: { new (): any; abort(reason?: any): any; timeout(ms: number): any; any(signals: any[]): any; prototype: any };
|
|
153
|
+
declare var TextEncoder: { new (): { encode(input?: string): Uint8Array; encoding: string } };
|
|
154
|
+
declare var TextDecoder: { new (label?: string, options?: any): { decode(input?: any, options?: any): string; encoding: string } };
|
|
155
|
+
declare var URLSearchParams: { new (init?: any): any; prototype: any };
|
|
156
|
+
declare var EventTarget: { new (): any; prototype: any };
|
|
157
|
+
declare var Event: { new (type: string, init?: any): any; prototype: any };
|
|
158
|
+
|
|
159
|
+
interface ObjectConstructor {
|
|
160
|
+
entries(o: any): [string, any][];
|
|
161
|
+
values(o: any): any[];
|
|
162
|
+
fromEntries(entries: any): any;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
interface ErrorConstructor {
|
|
166
|
+
stackTraceLimit?: number;
|
|
167
|
+
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Claude front door: pre-bash-guard through the shared lifecycle bridge.
|
|
3
|
-
set -uo pipefail
|
|
4
|
-
export DOFLOW_AGENT="${DOFLOW_AGENT:-claude}"
|
|
5
|
-
if ! . "$(dirname "$0")/../../.doflow/shared/hooks/bridge/bridge.sh" 2>/dev/null; then
|
|
6
|
-
printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"[doflow] shared hooks lack the bridge; run: doflow update -t claude"}}'
|
|
7
|
-
exit 0
|
|
8
|
-
fi
|
|
9
|
-
doflow_bridge claude PreToolUse pre-bash-guard
|
|
10
|
-
exit $?
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Codex front door: pre-bash-guard through the shared lifecycle bridge.
|
|
3
|
-
set -uo pipefail
|
|
4
|
-
export DOFLOW_AGENT="${DOFLOW_AGENT:-codex}"
|
|
5
|
-
if ! . "$(dirname "$0")/../../.doflow/shared/hooks/bridge/bridge.sh" 2>/dev/null; then
|
|
6
|
-
printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"[doflow] shared hooks lack the bridge; run: doflow update -t codex"}}'
|
|
7
|
-
exit 0
|
|
8
|
-
fi
|
|
9
|
-
doflow_bridge codex PreToolUse pre-bash-guard
|
|
10
|
-
exit $?
|