@khoavu882/doflow 2.0.0-beta.5 → 2.0.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/README.md +12 -11
- package/bin/doflow.js +21 -11
- 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/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/session-end.sh +23 -10
- package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
- package/core/harnesses/claude/settings/settings.json +18 -0
- 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/lib.sh +44 -25
- 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/session-end.impl.sh +23 -10
- package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
- package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
- package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-pre-bash-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 +57 -0
- package/core/harnesses/kiro/agents/doflow.json +1 -1
- package/core/harnesses/kiro/hooks/lib.sh +26 -6
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +1 -0
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +1 -0
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +1 -0
- 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 +8 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +0 -1
- package/core/harnesses/shared/hooks/bridge/bridge.sh +102 -9
- 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 +10 -2
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +48 -15
- 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 +2 -11
- 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-tool-guard.sh +3 -2
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +213 -12
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +114 -38
- package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
- package/core/harnesses/shared/hooks/policies/stop-check.sh +20 -9
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +5 -2
- 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 +47 -8
- 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 +32 -0
- package/core/registry/gates.json +32 -0
- package/core/registry/guidance-caps.json +40 -0
- package/core/registry/harnesses.json +1442 -363
- package/core/registry/lifecycle.json +485 -304
- 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/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 +276 -0
- package/core/registry/storage.json +679 -31
- package/core/registry/telemetry.json +236 -0
- package/core/registry/transcripts.json +142 -0
- package/core/registry/verification.json +76 -1
- 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 +1 -2
- 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 +48 -4
- package/core/shared/scripts/doflow/bin/doflow-run +149 -34
- package/core/shared/skills/do/SKILL.md +12 -10
- 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 +20 -74
- package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -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 +41 -144
- 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 +18 -17
- package/core/shared/skills/do-diagnose/SKILL.md +14 -4
- package/core/shared/skills/do-document/SKILL.md +11 -1
- package/core/shared/skills/do-document/references/deep_research.md +1 -2
- package/core/shared/skills/do-document/references/embedded-research.md +18 -6
- package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +15 -58
- 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 +13 -1
- 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 +8 -9
- package/core/shared/skills/do-flow/references/task_classes.md +2 -2
- package/core/shared/skills/do-git/SKILL.md +4 -0
- package/core/shared/skills/do-implement/SKILL.md +4 -7
- package/core/shared/skills/do-plan/SKILL.md +26 -15
- 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/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 +24 -4
- package/src/adapters/codex/agents.js +25 -15
- package/src/adapters/codex/index.js +48 -13
- package/src/adapters/codex/native-projection.js +6 -2
- package/src/adapters/copilot/index.js +58 -23
- package/src/adapters/copy-tree.js +290 -44
- 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 +21 -2
- package/src/cli/commands/ui.js +99 -0
- package/src/cli/commands/update.js +2 -1
- package/src/cli/index.js +322 -29
- package/src/cli/runtime-commands.js +209 -57
- 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 +20 -3
- 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 +492 -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/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 +154 -78
- 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 +34 -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 +565 -17
- package/src/runtime/history-compactor.js +13 -3
- package/src/runtime/implementation-gate.js +277 -22
- 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 +16 -6
- package/src/runtime/lifecycle/event-store.js +8 -6
- package/src/runtime/lifecycle/followup.js +6 -1
- package/src/runtime/lifecycle/overview.js +24 -1
- 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 +477 -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/facts.js +479 -0
- package/src/runtime/roadmap/gates.js +270 -0
- package/src/runtime/roadmap/loop.js +1636 -0
- package/src/runtime/roadmap/merge.js +381 -0
- package/src/runtime/roadmap/rows.js +1113 -0
- package/src/runtime/roadmap/seed.js +951 -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 +47 -11
- 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 +374 -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/verification/check-key.js +628 -0
- package/src/runtime/verification/contract-runner.js +546 -11
- package/src/runtime/verification/engine.js +499 -9
- 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 +288 -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 +578 -63
- 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/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/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
|
@@ -12,9 +12,11 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const fs = require('node:fs');
|
|
15
|
+
const path = require('node:path');
|
|
15
16
|
const { finishRuntime, usageError } = require('../cli-result.js');
|
|
16
17
|
const { maskLine, printSafe } = require('../mask.js');
|
|
17
|
-
const { failureHome,
|
|
18
|
+
const { failureHome, captureSwitchDetailed } = require('./home.js');
|
|
19
|
+
const { writeLayer, backupLayerFile } = require('../settings/write.js');
|
|
18
20
|
const { rotateIfDue } = require('./capture.js');
|
|
19
21
|
const store = require('./store.js');
|
|
20
22
|
const { projectRoot } = require('../lifecycle/root.js');
|
|
@@ -119,19 +121,33 @@ function settleFailure({ fp, as, reason, cwd, env = process.env, now = new Date(
|
|
|
119
121
|
|
|
120
122
|
// ── capture ────────────────────────────────────────────────────────────────────────────────────
|
|
121
123
|
|
|
122
|
-
function captureState({ set, env = process.env
|
|
124
|
+
function captureState({ set, env = process.env } = {}) {
|
|
123
125
|
if (set !== undefined && set !== 'on' && set !== 'off') throw new FailureUsageError(`--set must be on or off (got '${set}')`);
|
|
124
126
|
const home = failureHome(env);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
let settingsFile = null;
|
|
128
|
+
if (home !== null && set !== undefined) {
|
|
129
|
+
const file = path.join(path.dirname(home), 'settings.json');
|
|
130
|
+
settingsFile = file;
|
|
131
|
+
const written = writeLayer({ file, set: { 'failure.capture': set }, scrubKeys: set === 'on' ? ['failure.capture'] : [] });
|
|
132
|
+
if (!written.ok && set === 'off' && written.code === 'unparseable') {
|
|
133
|
+
const backup = backupLayerFile(file);
|
|
134
|
+
if (!backup.ok) return refusal('capture', 'write-failed', backup.detail);
|
|
135
|
+
console.error(printSafe(`note: malformed settings preserved at ${backup.file}`));
|
|
136
|
+
const recovered = writeLayer({ file, set: { 'failure.capture': set }, force: true });
|
|
137
|
+
if (!recovered.ok) return refusal('capture', 'write-failed', recovered.detail);
|
|
138
|
+
} else if (!written.ok) {
|
|
139
|
+
return refusal('capture', 'write-failed', written.detail);
|
|
140
|
+
}
|
|
130
141
|
}
|
|
131
|
-
const state =
|
|
142
|
+
const state = captureSwitchDetailed(home, env);
|
|
143
|
+
if (settingsFile && state.decidedBy === 'core/registry/settings.json') state.decidedBy = settingsFile;
|
|
144
|
+
for (const note of state.deprecatedNotes) console.error(printSafe(`note: ${note}`));
|
|
132
145
|
// With no failure home nothing is captured, whatever the switch says.
|
|
133
146
|
const result = { ok: true, action: 'capture', effective: home === null ? 'off' : state.effective, sentinel: state.sentinel, env: state.env };
|
|
134
147
|
if (home === null) result.home = null;
|
|
148
|
+
else if (set !== undefined) {
|
|
149
|
+
result.decidedBy = state.decidedBy;
|
|
150
|
+
}
|
|
135
151
|
return result;
|
|
136
152
|
}
|
|
137
153
|
|
|
@@ -147,7 +163,7 @@ function lines(result ) {
|
|
|
147
163
|
return [...out, ...result.next.map((n ) => `next: ${n}`)];
|
|
148
164
|
}
|
|
149
165
|
if (result.action === 'settle') return [`${result.fp} settled as ${result.as}${result.followup ? `, follow-up ${result.followup.id}` : ''}`];
|
|
150
|
-
return [`capture ${result.effective}${result.sentinel ? ' (switched off on this machine)' : ''}${result.env ? ` (DOFLOW_FAILURE_CAPTURE=${result.env})` : ''}`];
|
|
166
|
+
return [`capture ${result.effective}${result.sentinel ? ' (switched off on this machine)' : ''}${result.env ? ` (DOFLOW_FAILURE_CAPTURE=${result.env})` : ''}${result.decidedBy ? ` (decided by ${result.decidedBy})` : ''}`];
|
|
151
167
|
}
|
|
152
168
|
|
|
153
169
|
/**
|
|
@@ -4,32 +4,50 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Where DoFlow's own failures are kept on this machine, and whether capture is on (IC-011, IC-014).
|
|
7
|
-
* One folder per machine, shared by every project (DEC-007)
|
|
8
|
-
* home the
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* One folder per machine, shared by every project (DEC-007), `failures` beside `settings.json` in the
|
|
8
|
+
* config home (`../config-home.js`, shared with the bash writers so a failure captured by either lands
|
|
9
|
+
* in the one list). No config home means no failure home, and capture is skipped.
|
|
10
|
+
*
|
|
11
|
+
* The switch (IC-005), in order: no home skips; the environment can only turn capture off; the sentinel
|
|
12
|
+
* `<home>/off` file turns it off; a `failure.capture` value in `<config home>/settings.json` other than
|
|
13
|
+
* "on" turns it off (the lexical rule, `../settings/lexical.js`). Otherwise it is on.
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
|
-
const fs = require('node:fs');
|
|
15
16
|
const path = require('node:path');
|
|
16
|
-
|
|
17
|
-
const
|
|
17
|
+
const { configHome } = require('../config-home.js');
|
|
18
|
+
const { isOffWord } = require('../off-words.js');
|
|
18
19
|
|
|
19
20
|
/** @param {Object} [env] defaults to process.env
|
|
20
21
|
* @returns {string|null} the failure home, an absolute path, or null when none can be resolved */
|
|
21
22
|
function failureHome(env = process.env) {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
return path.isAbsolute(xdg) ? path.join(xdg, 'doflow', 'failures') : null;
|
|
25
|
-
}
|
|
26
|
-
const home = env.HOME;
|
|
27
|
-
if (typeof home === 'string' && path.isAbsolute(home)) return path.join(home, '.config', 'doflow', 'failures');
|
|
28
|
-
return null;
|
|
23
|
+
const { dir } = configHome(env);
|
|
24
|
+
return dir === null ? null : path.join(dir, 'failures');
|
|
29
25
|
}
|
|
30
26
|
|
|
31
27
|
const sentinelPath = (home ) => path.join(home, 'off');
|
|
32
28
|
const eventsPath = (home ) => path.join(home, 'events.jsonl');
|
|
29
|
+
/** `settings.json` sits in the config home, one level above the failure home. */
|
|
30
|
+
const settingsPath = (home ) => path.join(path.dirname(home), 'settings.json');
|
|
31
|
+
|
|
32
|
+
/** Resolves the machine-wide setting and returns the file and sentinel parts of the old result shape. */
|
|
33
|
+
function resolvedCapture(home , env ) {
|
|
34
|
+
const { loadSettings } = require('../settings/resolve.js');
|
|
35
|
+
const resolverEnv = { ...env, XDG_CONFIG_HOME: path.dirname(path.dirname(home)) };
|
|
36
|
+
const view = loadSettings({ global: true, env: resolverEnv });
|
|
37
|
+
const result = view.get('failure.capture', 'global');
|
|
38
|
+
const sentinel = result.candidates.some((candidate ) =>
|
|
39
|
+
candidate.legacy && candidate.source === sentinelPath(home) && candidate.value === 'off');
|
|
40
|
+
const file = result.candidates.find((candidate ) =>
|
|
41
|
+
!candidate.legacy && candidate.layer === 'user' && candidate.source === settingsPath(home));
|
|
42
|
+
const settings = file?.value === 'on' || file?.value === 'off' ? file.value : null;
|
|
43
|
+
return {
|
|
44
|
+
effective: result.value === 'off' ? 'off' : 'on', sentinel, settings,
|
|
45
|
+
decidedBy: result.origin.source,
|
|
46
|
+
deprecatedNotes: view.notes.filter((note ) => note.code === 'deprecated'
|
|
47
|
+
&& note.key === 'failure.capture' && result.origin.legacy && note.source === result.origin.source)
|
|
48
|
+
.map((note ) => note.text),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
33
51
|
|
|
34
52
|
/** The value of `DOFLOW_FAILURE_CAPTURE` when it is set to anything, else null. */
|
|
35
53
|
function envSetting(env = process.env) {
|
|
@@ -38,27 +56,36 @@ function envSetting(env = process.env) {
|
|
|
38
56
|
}
|
|
39
57
|
|
|
40
58
|
/**
|
|
41
|
-
* The capture switch (IC-014). The
|
|
42
|
-
*
|
|
59
|
+
* The capture switch (IC-014, IC-005). The resolver applies the global sentinel, lexical settings
|
|
60
|
+
* file value and environment layers; any off value wins. The resolver reads without creating a folder.
|
|
43
61
|
* @param {string|null} home
|
|
44
62
|
* @param {Object} [env]
|
|
45
|
-
* @returns {{effective: 'on'|'off', sentinel: boolean, env: string|null}}
|
|
63
|
+
* @returns {{effective: 'on'|'off', sentinel: boolean, env: string|null, settings: 'on'|'off'|null}}
|
|
64
|
+
* `settings` is the lexical read of settings.json: null when the file holds no `failure.capture`
|
|
46
65
|
*/
|
|
47
|
-
function
|
|
48
|
-
let sentinel = false;
|
|
49
|
-
if (home) {
|
|
50
|
-
try { sentinel = fs.statSync(sentinelPath(home)).isFile(); } catch { sentinel = false; }
|
|
51
|
-
}
|
|
66
|
+
function captureSwitchDetailed(home , env = process.env) {
|
|
52
67
|
const setting = envSetting(env);
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
if (!home) {
|
|
69
|
+
const envOff = isOffWord(setting);
|
|
70
|
+
return { effective: envOff ? 'off' : 'on', sentinel: false, env: setting, settings: null, decidedBy: null, deprecatedNotes: [] };
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const resolved = resolvedCapture(home, env);
|
|
74
|
+
return { ...resolved, env: setting };
|
|
75
|
+
} catch {
|
|
76
|
+
return { effective: 'off', sentinel: false, env: setting, settings: null, decidedBy: null, deprecatedNotes: [] };
|
|
77
|
+
}
|
|
55
78
|
}
|
|
56
79
|
|
|
57
|
-
/** The
|
|
80
|
+
/** The public switch shape, with the resolver's deciding source added by IC-005 P3. */
|
|
81
|
+
function captureSwitch(home , env = process.env) {
|
|
82
|
+
const state = captureSwitchDetailed(home, env);
|
|
83
|
+
return { effective: state.effective, sentinel: state.sentinel, env: state.env, settings: state.settings, decidedBy: state.decidedBy };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** The hot-path check a writer makes; the shared resolver owns sentinel and settings-file reads. */
|
|
58
87
|
function captureIsOff(home , env = process.env) {
|
|
59
|
-
|
|
60
|
-
if (setting !== null && OFF_VALUES.has(setting.trim().toLowerCase())) return true;
|
|
61
|
-
try { return fs.statSync(sentinelPath(home)).isFile(); } catch { return false; }
|
|
88
|
+
try { return resolvedCapture(home, env).effective === 'off'; } catch { return true; }
|
|
62
89
|
}
|
|
63
90
|
|
|
64
|
-
module.exports = { failureHome, captureSwitch, captureIsOff, sentinelPath, eventsPath, envSetting };
|
|
91
|
+
module.exports = { failureHome, captureSwitch, captureSwitchDetailed, captureIsOff, sentinelPath, eventsPath, envSetting };
|
|
@@ -20,6 +20,7 @@ const path = require('node:path');
|
|
|
20
20
|
const crypto = require('node:crypto');
|
|
21
21
|
const { failureHome, captureSwitch, eventsPath } = require('./home.js');
|
|
22
22
|
const { printSafe } = require('../mask.js');
|
|
23
|
+
const { appendRegular } = require('./capture.js');
|
|
23
24
|
|
|
24
25
|
const ROTATED = /^events-\d{8}T\d{6}Z-\d+\.jsonl$/;
|
|
25
26
|
const SETTLEMENTS = 'settlements.jsonl';
|
|
@@ -203,14 +204,18 @@ function loadEntries({ env = process.env } = {}) {
|
|
|
203
204
|
}
|
|
204
205
|
|
|
205
206
|
/**
|
|
206
|
-
* Appends one settlement line (IC-013). Only `failure --action settle` calls this.
|
|
207
|
+
* Appends one settlement line (IC-013). Only `failure --action settle` calls this. The line goes to a
|
|
208
|
+
* regular file only (`appendRegular`, shared with the capture writer): a FIFO at `settlements.jsonl`
|
|
209
|
+
* would block the open for ever, and a symlink or device is not a place to write.
|
|
207
210
|
* @param {string} home
|
|
208
211
|
* @param {{fp: string, as: string, reason?: string|null, followup?: string|null, now?: Date}} settlement
|
|
212
|
+
* @throws when the file cannot be appended to; it is left as it was
|
|
209
213
|
*/
|
|
210
214
|
function appendSettlement(home , { fp, as, reason = null, followup = null, now = new Date() } ) {
|
|
211
215
|
fs.mkdirSync(home, { recursive: true, mode: 0o700 });
|
|
212
216
|
const line = JSON.stringify({ v: 1, at: now.toISOString(), fp, as, reason, followup });
|
|
213
|
-
|
|
217
|
+
const written = appendRegular(settlementsPath(home), `${line}\n`);
|
|
218
|
+
if (!written) throw new Error(`${settlementsPath(home)} is not a regular file that can be appended to (a FIFO, a link, a device or a file without write access), so the settlement was not recorded. Nothing was written.`);
|
|
214
219
|
}
|
|
215
220
|
|
|
216
221
|
const DOFLOW_PACKAGE = '@khoavu882/doflow';
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The feature pointer (row 135): `<storeRoot>/.doflow/state/feature/<id>.json` names the feature one
|
|
7
|
+
* checkout of the repository is working on, so a fresh session reads it instead of inferring the
|
|
8
|
+
* feature from the branch alone. Only the runtime writes it, when a feature is started or switched
|
|
9
|
+
* (`lifecycle --action init`, an `orchestrate` start or catch-up that begins a `feature` run) and
|
|
10
|
+
* clears it when the feature finishes. The branch stays the other witness: `describe` reports a
|
|
11
|
+
* disagreement instead of trusting either side.
|
|
12
|
+
*
|
|
13
|
+
* One file per checkout, named by the first 16 hex of the sha256 of the checkout's realpath git
|
|
14
|
+
* toplevel, so two worktrees on two features never share a file: there is no read-modify-write
|
|
15
|
+
* across checkouts to lose an update in, and each write is a temp file renamed into place.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('node:fs');
|
|
19
|
+
const path = require('node:path');
|
|
20
|
+
const crypto = require('node:crypto');
|
|
21
|
+
const { execFileSync } = require('node:child_process');
|
|
22
|
+
const { resolveActiveFeature } = require('./feature-resolve.js');
|
|
23
|
+
|
|
24
|
+
const SCHEMA = 1;
|
|
25
|
+
const POINTER_DIR = path.join('.doflow', 'state', 'feature');
|
|
26
|
+
const POINTER_NAME = /^[0-9a-f]{16}\.json$/;
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
const checkoutId = (checkout ) => crypto.createHash('sha256').update(checkout).digest('hex').slice(0, 16);
|
|
31
|
+
/** The pointer file of the checkout whose realpath git toplevel is `checkout`. */
|
|
32
|
+
const pointerFile = (storeRoot , checkout ) => path.join(storeRoot, POINTER_DIR, `${checkoutId(checkout)}.json`);
|
|
33
|
+
|
|
34
|
+
function git(cwd , args ) {
|
|
35
|
+
try { return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return null; }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The checkout `cwd` is in (its realpath git toplevel) and its current branch, or null outside a repository. */
|
|
39
|
+
function checkoutOf(cwd ) {
|
|
40
|
+
const top = git(cwd, ['rev-parse', '--show-toplevel']);
|
|
41
|
+
if (!top) return null;
|
|
42
|
+
let key = top;
|
|
43
|
+
try { key = fs.realpathSync(top); } catch { /* keep git's spelling */ }
|
|
44
|
+
return { key, branch: git(cwd, ['rev-parse', '--abbrev-ref', 'HEAD']) || null };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** One pointer file: `{status:'none'}`, `{status:'ok', data}` or `{status:'unreadable', reason}`. */
|
|
48
|
+
function readEntry(file ) {
|
|
49
|
+
let text ;
|
|
50
|
+
try { text = fs.readFileSync(file, 'utf8'); } catch (error ) {
|
|
51
|
+
return error.code === 'ENOENT' ? { status: 'none' } : { status: 'unreadable', reason: error.message };
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const data = JSON.parse(text);
|
|
55
|
+
if (!data || typeof data !== 'object' || data.schema !== SCHEMA || typeof data.slug !== 'string') {
|
|
56
|
+
return { status: 'unreadable', reason: `${path.basename(file)} is not a schema ${SCHEMA} pointer file` };
|
|
57
|
+
}
|
|
58
|
+
return { status: 'ok', data };
|
|
59
|
+
} catch (error ) {
|
|
60
|
+
return { status: 'unreadable', reason: `${path.basename(file)} is not valid JSON: ${error.message}` };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function writeEntry(file , entry ) {
|
|
65
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
66
|
+
const temp = `${file}.${process.pid}.tmp`;
|
|
67
|
+
try {
|
|
68
|
+
fs.writeFileSync(temp, `${JSON.stringify(entry, null, 2)}\n`);
|
|
69
|
+
fs.renameSync(temp, file);
|
|
70
|
+
} finally {
|
|
71
|
+
try { fs.unlinkSync(temp); } catch { /* renamed away, or never written */ }
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function removeEntry(file ) {
|
|
76
|
+
try { fs.unlinkSync(file); } catch (error ) { if (error.code !== 'ENOENT') throw error; }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Runs a pointer write; a failure is one stderr note and never reaches the verb's exit status. */
|
|
80
|
+
function bestEffort(what , fn ) {
|
|
81
|
+
try { fn(); } catch (error ) { console.error(`doflow: note: feature pointer not ${what}: ${error.message}`); }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The artifact folder of `slug`, repo-relative: the resolver's answer, else the default folder when it exists. */
|
|
85
|
+
function artifactDirOf(checkout , storeRoot , slug ) {
|
|
86
|
+
const found = resolveActiveFeature({ projectRoot: checkout, slug });
|
|
87
|
+
if (!found.error && found.paths && typeof found.paths.feature_dir === 'string') return found.paths.feature_dir;
|
|
88
|
+
const rel = path.join('agent-docs', 'doflow', slug);
|
|
89
|
+
for (const root of [checkout, storeRoot]) {
|
|
90
|
+
try { if (fs.statSync(path.join(root, rel)).isDirectory()) return rel; } catch { /* absent */ }
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Sets the calling checkout's entry to `slug` (a later start of another slug overwrites it, and so does an unreadable file). */
|
|
96
|
+
function recordFeature({ storeRoot, cwd, slug, taskId = null } ) {
|
|
97
|
+
if (!slug) return;
|
|
98
|
+
bestEffort('written', () => {
|
|
99
|
+
const here = checkoutOf(cwd);
|
|
100
|
+
if (!here) return;
|
|
101
|
+
writeEntry(pointerFile(storeRoot, here.key), {
|
|
102
|
+
schema: SCHEMA, checkout: here.key, slug, artifactDir: artifactDirOf(here.key, storeRoot, slug), branch: here.branch, taskId, updatedAt: new Date().toISOString(),
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Removes the calling checkout's entry when it names `slug`; an unreadable file is left alone. */
|
|
108
|
+
function clearCheckout({ storeRoot, cwd, slug } ) {
|
|
109
|
+
if (!slug) return;
|
|
110
|
+
bestEffort('cleared', () => {
|
|
111
|
+
const here = checkoutOf(cwd);
|
|
112
|
+
if (!here) return;
|
|
113
|
+
const file = pointerFile(storeRoot, here.key);
|
|
114
|
+
const read = readEntry(file);
|
|
115
|
+
if (read.status === 'ok' && read.data .slug === slug) removeEntry(file);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Removes every entry naming `slug`, whichever checkout holds it (the feature is finished). */
|
|
120
|
+
function clearSlug({ storeRoot, slug } ) {
|
|
121
|
+
if (!slug) return;
|
|
122
|
+
bestEffort('cleared', () => {
|
|
123
|
+
const dir = path.join(storeRoot, POINTER_DIR);
|
|
124
|
+
let names ;
|
|
125
|
+
try { names = fs.readdirSync(dir); } catch (error ) { if (error.code === 'ENOENT') return; throw error; }
|
|
126
|
+
for (const name of names.filter((n) => POINTER_NAME.test(n))) {
|
|
127
|
+
const read = readEntry(path.join(dir, name));
|
|
128
|
+
if (read.status === 'ok' && read.data .slug === slug) removeEntry(path.join(dir, name));
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* What the calling checkout's pointer says against what its branch says.
|
|
135
|
+
* `state`: none (no entry), agree, mismatch (branch or branch-derived slug differs) or unreadable.
|
|
136
|
+
* A mismatch names neither side as the answer: `slug` is null and both are reported.
|
|
137
|
+
*/
|
|
138
|
+
function describeActive({ storeRoot, cwd } ) {
|
|
139
|
+
const here = checkoutOf(cwd);
|
|
140
|
+
const found = resolveActiveFeature({ projectRoot: here ? here.key : cwd });
|
|
141
|
+
const derived = !found.error && found.paths && typeof found.paths.feature_slug === 'string' ? found.paths.feature_slug : null;
|
|
142
|
+
const current = { branch: here ? here.branch : null, slug: derived };
|
|
143
|
+
const base = { slug: null, artifactDir: null, branch: null, pointer: null, current };
|
|
144
|
+
if (!here) return { ...base, state: 'none' };
|
|
145
|
+
const file = pointerFile(storeRoot, here.key);
|
|
146
|
+
const read = readEntry(file);
|
|
147
|
+
if (read.status === 'unreadable') return { ...base, state: 'unreadable', reason: read.reason, file: path.relative(storeRoot, file) };
|
|
148
|
+
if (read.status === 'none') return { ...base, state: 'none' };
|
|
149
|
+
const entry = read.data ;
|
|
150
|
+
const mismatch = entry.branch !== current.branch || (derived !== null && derived !== entry.slug);
|
|
151
|
+
if (mismatch) return { ...base, state: 'mismatch', pointer: entry };
|
|
152
|
+
return { state: 'agree', slug: entry.slug, artifactDir: entry.artifactDir ?? null, branch: entry.branch ?? null, pointer: entry, current };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The calling checkout's pointer entry when its recorded branch is the checkout's branch, else null
|
|
157
|
+
* (no repository, no entry, unreadable, or a branch switch since). Unlike `describeActive` it does
|
|
158
|
+
* not run the feature resolver: a caller on a hot path (the context-pack verb, issue #9) needs the
|
|
159
|
+
* runtime's own record, not a second derivation from the branch.
|
|
160
|
+
*/
|
|
161
|
+
function activeEntry({ storeRoot, cwd, checkout } ) {
|
|
162
|
+
// `checkout` is the caller's already-resolved checkout (its feature resolution): no git child is spawned for it.
|
|
163
|
+
let here ;
|
|
164
|
+
if (checkout?.repoRoot) {
|
|
165
|
+
let key = checkout.repoRoot;
|
|
166
|
+
try { key = fs.realpathSync(key); } catch { /* keep the resolver's spelling */ }
|
|
167
|
+
here = { key, branch: checkout.branch || null };
|
|
168
|
+
} else here = checkoutOf(cwd);
|
|
169
|
+
if (!here) return null;
|
|
170
|
+
const read = readEntry(pointerFile(storeRoot, here.key));
|
|
171
|
+
return read.status === 'ok' && read.data .branch === here.branch ? read.data : null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = { recordFeature, clearCheckout, clearSlug, describeActive, activeEntry, pointerFile, POINTER_DIR };
|
|
@@ -27,7 +27,7 @@ const PATHS_HELPER = resolveBashHelper('do-paths.sh');
|
|
|
27
27
|
* @param {Object} options
|
|
28
28
|
* @param {string} options.projectRoot working directory the resolution is relative to
|
|
29
29
|
* @param {string|null} [options.slug] explicit feature override, passed straight through
|
|
30
|
-
* @returns {{repoRoot:string, featureDir:string, paths:Object}|{error:string, message:string}}
|
|
30
|
+
* @returns {{repoRoot:string, featureRoot:string, featureDir:string, featureFolder:string, paths:Object}|{error:string, message:string}}
|
|
31
31
|
* `paths` is the resolver's own parsed JSON (repo-root-relative artifact paths and has_* flags),
|
|
32
32
|
* so a caller needs no second resolver call.
|
|
33
33
|
*/
|
|
@@ -73,7 +73,18 @@ function resolveActiveFeature({ projectRoot, slug = null }
|
|
|
73
73
|
if (!paths.repo_root || !paths.feature_dir) {
|
|
74
74
|
return { error: 'no-active-feature', message: 'the resolver named no active feature directory to scaffold' };
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
// `featureDir` is the folder in the checkout asked about (what every caller used before 115 and
|
|
77
|
+
// still tests for "this checkout has the folder"). `featureRoot` is the checkout that holds the
|
|
78
|
+
// folder (the main checkout when a linked worktree has none of its own) and `featureFolder` the
|
|
79
|
+
// folder there: the `decision` verb reads and writes its register through those.
|
|
80
|
+
const featureRoot = paths.feature_root || paths.repo_root;
|
|
81
|
+
return {
|
|
82
|
+
repoRoot: paths.repo_root,
|
|
83
|
+
featureRoot,
|
|
84
|
+
featureDir: path.resolve(paths.repo_root, paths.feature_dir),
|
|
85
|
+
featureFolder: path.resolve(featureRoot, paths.feature_dir),
|
|
86
|
+
paths,
|
|
87
|
+
};
|
|
77
88
|
}
|
|
78
89
|
|
|
79
90
|
module.exports = { resolveActiveFeature, PATHS_HELPER };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `doflow fleet` (093 V8): the cross-project, read-only Fleet verb. `--action projects` prints
|
|
5
|
+
* `listProjects` (IC-019); `--action tree`, run with `cwd` = one project, prints that project's agent
|
|
6
|
+
* hierarchy (IC-020, `buildTree`). It writes nothing: no state, no config home, no upkeep.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { finishRuntime, captureCaught } = require('../cli-result.js');
|
|
10
|
+
|
|
11
|
+
const ALLOWED_FLAGS = new Set(['--action', '--json']);
|
|
12
|
+
|
|
13
|
+
function dash(value ) {
|
|
14
|
+
return value === null || value === undefined ? '-' : String(value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The text form: one line per project, no path (`root` is for the Console server only). */
|
|
18
|
+
function projectLines(body ) {
|
|
19
|
+
const { excluded } = body;
|
|
20
|
+
return [
|
|
21
|
+
`store: ${body.store}`,
|
|
22
|
+
...body.projects.map((p ) => `project ${p.projectKey} ${p.label}${p.current ? ' current' : ''} checkouts ${p.checkouts} sources ${p.sources.join(',')} last-record ${dash(p.lastRecordAt)}`),
|
|
23
|
+
`excluded: missing ${excluded.missing} temp ${excluded.temp} not-doflow ${excluded.notDoflow} unreadable ${excluded.unreadable}`,
|
|
24
|
+
...(body.truncated ? ['truncated: true'] : []),
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The text form of `tree`: one line per node and per edge; keys, labels and bases only. */
|
|
29
|
+
function treeLines(body ) {
|
|
30
|
+
return [
|
|
31
|
+
`project ${body.projectKey}`,
|
|
32
|
+
...body.nodes.map((n ) => `node ${n.agentKey} ${n.kind} harness ${dash(n.harness)} model ${dash(n.model)} label ${dash(n.label)} depth ${n.depth} liveness ${n.liveness} transcript ${n.transcript.linked ? 'linked' : n.transcript.reason}`),
|
|
33
|
+
...body.edges.map((e ) => `edge ${e.from} -> ${e.to} ${e.basis}`),
|
|
34
|
+
`limits: nodes ${body.limits.nodes} not-checked ${body.limits.notChecked} unreadable ${body.unreadable}`,
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function handleFleetCommand({ action, json = false, flags = [], env = process.env, cwd = process.cwd(), exec, tempRoots, alive, out = (line ) => console.log(line) } = {}) {
|
|
39
|
+
const usage = (message ) => {
|
|
40
|
+
out(json ? JSON.stringify({ ok: false, error: 'usage', message }) : `doflow fleet: ${message}`);
|
|
41
|
+
return finishRuntime(2);
|
|
42
|
+
};
|
|
43
|
+
if (action !== 'projects' && action !== 'tree') return usage('action must be projects or tree');
|
|
44
|
+
const extra = (flags ).map((flag) => flag.split('=')[0]).find((flag) => !ALLOWED_FLAGS.has(flag));
|
|
45
|
+
if (extra !== undefined) return usage(`unsupported flag ${extra}; only --action and --json apply`);
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
if (action === 'tree') {
|
|
49
|
+
const { buildTree } = require('./tree.js');
|
|
50
|
+
const built = buildTree({ env, cwd, ...(exec ? { exec } : {}), ...(alive ? { alive } : {}) });
|
|
51
|
+
if (!built.ok) {
|
|
52
|
+
if (json) out(JSON.stringify({ v: 1, error: built.error }));
|
|
53
|
+
else console.error('[ERROR] fleet: cannot list checkouts');
|
|
54
|
+
return finishRuntime(1);
|
|
55
|
+
}
|
|
56
|
+
if (json) out(JSON.stringify(built.body));
|
|
57
|
+
else for (const line of treeLines(built.body)) out(line);
|
|
58
|
+
return finishRuntime(0);
|
|
59
|
+
}
|
|
60
|
+
const { listProjects } = require('./projects.js');
|
|
61
|
+
const listed = listProjects({ env, cwd, ...(exec ? { exec } : {}), ...(tempRoots ? { tempRoots } : {}) });
|
|
62
|
+
if (!listed.ok) {
|
|
63
|
+
if (json) out(JSON.stringify({ v: 1, error: listed.error }));
|
|
64
|
+
else console.error('[ERROR] fleet: cannot list projects');
|
|
65
|
+
return finishRuntime(1);
|
|
66
|
+
}
|
|
67
|
+
if (json) out(JSON.stringify(listed.body));
|
|
68
|
+
else for (const line of projectLines(listed.body)) out(line);
|
|
69
|
+
return finishRuntime(0);
|
|
70
|
+
} catch (error ) {
|
|
71
|
+
captureCaught(error, 'fleet', 1);
|
|
72
|
+
console.error(`[ERROR] fleet: ${error?.message || String(error)}`);
|
|
73
|
+
return finishRuntime(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = { handleFleetCommand };
|