@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
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
// Kiro's native root is symmetric across scopes — `.kiro/` (project) / `~/.kiro/` (global) is the
|
|
8
8
|
// one directory that holds everything Kiro reads: steering, agents, hooks, and settings/mcp.json
|
|
9
9
|
// (see src/targets.js's toolDirs() for the same convention this adapter's own nativePaths()
|
|
10
|
-
// mirrors). Unlike every other harness in this codebase, Kiro receives DoFlow's
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// marker-wrapped instruction file.
|
|
10
|
+
// mirrors). Unlike every other harness in this codebase, Kiro receives DoFlow's guidance as
|
|
11
|
+
// steering files rather than a single marker-wrapped pointer file plus a separately-copied tree
|
|
12
|
+
// (design.md §4's "research decision D3"): there is no single-file instruction surface here at all,
|
|
13
|
+
// so — unlike src/adapters/opencode — this adapter never renders a
|
|
14
|
+
// marker-wrapped instruction file. Steering holds the core set only (DOFLOW_CORE.md and the files it
|
|
15
|
+
// imports, selected by the `guidance-manifest` select); the full guidance tree remains under
|
|
16
|
+
// `.doflow/guidance` through kiro.guidance-tree, where `retrieve` and the runtime read it.
|
|
15
17
|
//
|
|
16
18
|
// Evidence: https://kiro.dev/docs/steering/, https://kiro.dev/docs/mcp/configuration/,
|
|
17
19
|
// https://kiro.dev/docs/hooks/
|
|
@@ -37,6 +39,15 @@ const LEGACY_MCP_REGISTRATION = `${HARNESS}:mcp:registration`;
|
|
|
37
39
|
* module-level exports delegate to a default-configured instance so direct callers keep their
|
|
38
40
|
* historical shape.
|
|
39
41
|
*/
|
|
42
|
+
/** Hook tiers (maint-hooks): with Kiro outside `hooks.harnesses` (`context.hooksOff`) its agents carry no
|
|
43
|
+
* `hooks`, so they never name a hook script DoFlow did not install. */
|
|
44
|
+
const CONTENT_ONLY_TRANSFORMS = Object.freeze({ 'kiro-agents': 'kiro-agents-content' });
|
|
45
|
+
function transformFor(asset , context ) {
|
|
46
|
+
if (context?.hooksOff !== true) return asset.transform || null;
|
|
47
|
+
if (asset.id === 'kiro.doflow-agent') return 'kiro-agent-content';
|
|
48
|
+
return CONTENT_ONLY_TRANSFORMS[asset.transform] ?? (asset.transform || null);
|
|
49
|
+
}
|
|
50
|
+
|
|
40
51
|
function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } = {}) {
|
|
41
52
|
// Parity note: Kiro's historical nativePaths() derived even its user-scope root from scopeRoot
|
|
42
53
|
// (the CLI passes $HOME there for -g installs), never from a separate homeDir argument — so the
|
|
@@ -59,8 +70,8 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
59
70
|
|
|
60
71
|
/**
|
|
61
72
|
* Kiro has no single-file, marker-managed instruction surface for this adapter to render into —
|
|
62
|
-
*
|
|
63
|
-
* (guidance.context-layer) and
|
|
73
|
+
* guidance is projected file-for-file by the copy-tree engine below, the core set as steering
|
|
74
|
+
* (guidance.context-layer) and the whole tree into the shared `.doflow/guidance` (kiro.guidance-tree). Every
|
|
64
75
|
* asset routed to 'kiro' in core/registry/assets.json uses `renderer: copy-tree` or
|
|
65
76
|
* `kiro-agents`, so nothing reaches this function. It is kept as a pure passthrough only to
|
|
66
77
|
* satisfy the six-function adapter contract every harness must implement identically.
|
|
@@ -98,7 +109,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
98
109
|
const destDir = copyTreeDestDir(paths.configDir, asset);
|
|
99
110
|
const sourceDir = sourceDirFor(asset, context, fsImpl, 'Kiro');
|
|
100
111
|
const previousResources = ledgerFileResources(ledger?.resources, HARNESS, asset.id);
|
|
101
|
-
const result = planTree({ sourceDir, destDir, previousResources, siblingFingerprints: ledgerSiblingFingerprints(ledger?.resources, HARNESS), operation: removing ? 'remove' : 'apply', fsImpl, layout: asset.layout, transform: asset.
|
|
112
|
+
const result = planTree({ sourceDir, destDir, previousResources, siblingFingerprints: ledgerSiblingFingerprints(ledger?.resources, HARNESS), operation: removing ? 'remove' : 'apply', fsImpl, layout: asset.layout, transform: transformFor(asset, context), select: asset.select, keepModified: !removing,
|
|
102
113
|
// Forwarded so the CLI's --force reaches planTree's conflict check; omitting it let
|
|
103
114
|
// planTree's own `force = false` default stand in silently. Gated on `!removing` for the
|
|
104
115
|
// reason codex/index.ts states in full: force heals drift on apply, but a hand-edited file
|
|
@@ -115,7 +126,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
115
126
|
ownershipIdentity: `doflow:${HARNESS}:copy-tree:${asset.id}:${change.relPath}`,
|
|
116
127
|
kind: 'copy-tree-file', identity: change.relPath,
|
|
117
128
|
afterFingerprint: change.fingerprint, fingerprint: change.fingerprint, sourceVersion: 'registry-v1',
|
|
118
|
-
transformName: asset
|
|
129
|
+
transformName: transformFor(asset, context),
|
|
119
130
|
...(change.kept ? { retained: true, retainedFor: [] } : {}),
|
|
120
131
|
projection: { renderer: asset.renderer },
|
|
121
132
|
});
|
|
@@ -153,7 +164,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
153
164
|
for (const asset of kiroTreeAssets(assets)) {
|
|
154
165
|
const destDir = copyTreeDestDir(paths.configDir, asset);
|
|
155
166
|
const sourceDir = sourceDirFor(asset, context, fsImpl, 'Kiro');
|
|
156
|
-
const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, transform: asset.
|
|
167
|
+
const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, transform: transformFor(asset, context), select: asset.select, plannedWrites: context?.plannedWrites });
|
|
157
168
|
conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
|
|
158
169
|
for (const resource of result.resources) {
|
|
159
170
|
resources.push({
|
|
@@ -209,8 +220,8 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
209
220
|
|
|
210
221
|
/** Kiro has no hooks or mcp asset of its own in core/registry/assets.json (mcp servers come from
|
|
211
222
|
* core/registry/mcp.json, not an asset), so an MCP entry change piggybacks on an asset id
|
|
212
|
-
* this harness actually receives — the same "pseudo-component" technique
|
|
213
|
-
*
|
|
223
|
+
* this harness actually receives — the same "pseudo-component" technique the opencode and
|
|
224
|
+
* copilot adapters use for their settings-only changes. */
|
|
214
225
|
function pseudoAssetId(assets ) {
|
|
215
226
|
return assets.find((asset ) => asset.id === 'guidance.context-layer')?.id ?? assets[0]?.id;
|
|
216
227
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
// OpenCode adapter.
|
|
6
6
|
//
|
|
7
|
-
// OpenCode gets its own copy of DoFlow's skills tree, the same as Claude
|
|
7
|
+
// OpenCode gets its own copy of DoFlow's skills tree, the same as Claude and Codex: it
|
|
8
8
|
// natively discovers skills under `.opencode/skills/` (project) and `~/.config/opencode/skills/`
|
|
9
9
|
// (global) without any settings entry, so the shared copy-tree engine in ../copy-tree.ts
|
|
10
10
|
// materialises `skills.doflow` there directly. What remains registration, rather than
|
|
@@ -20,11 +20,11 @@ const { planTree, applyTree, removeTree, verifyTree, copyTreeAssets, copyTreeDes
|
|
|
20
20
|
const { readMcpFiles, planMcpEntries, verifyMcpEntries, ownedMcpIds, writeMcpEntries } = require('../mcp-entries.js');
|
|
21
21
|
|
|
22
22
|
// Only the marker constants: marker-merge.js reads and writes files itself, which cannot be used
|
|
23
|
-
// from plan(), whose contract is to compute changes without touching disk.
|
|
24
|
-
// solves this the same way, with a pure managedInstruction() over strings.
|
|
23
|
+
// from plan(), whose contract is to compute changes without touching disk.
|
|
25
24
|
const { MARKER_START, MARKER_END } = require('../../helper/marker-merge.js');
|
|
26
25
|
const { destructiveCommandGlobs } = require('../../helper/guardrails.js');
|
|
27
26
|
const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.js');
|
|
27
|
+
const { renderGuidancePointer } = require('../guidance-pointer.js');
|
|
28
28
|
|
|
29
29
|
const HARNESS = 'opencode';
|
|
30
30
|
/**
|
|
@@ -107,9 +107,8 @@ function createOpenCodeAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS]
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
/** Replace only the span between DoFlow's markers, leaving everything else byte-identical.
|
|
110
|
-
* An AGENTS.md that exists without a DoFlow section is refused rather than appended to
|
|
111
|
-
*
|
|
112
|
-
* cross-tool file, so where DoFlow's content belongs inside it is a decision for its owner, not a
|
|
110
|
+
* An AGENTS.md that exists without a DoFlow section is refused rather than appended to:
|
|
111
|
+
* AGENTS.md is a shared, cross-tool file, so where DoFlow's content belongs inside it is a decision for its owner, not a
|
|
113
112
|
* guess for the installer. Add an empty marker pair to opt in. */
|
|
114
113
|
/** Opt-in (--permissions) guardrail projection: DoFlow's destructive-command blocklist lands as
|
|
115
114
|
* deny rules under permission.bash. Only keys this feature owns (each carries a matching
|
|
@@ -259,7 +258,7 @@ function createOpenCodeAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS]
|
|
|
259
258
|
/** OpenCode's settings-merge change has no asset of its own in core/registry/assets.json (it's a
|
|
260
259
|
* registration side-effect of the instructions/mcp assets, not a projected asset), so it piggybacks
|
|
261
260
|
* on a real asset id already routed to this harness — the same "pseudo-component" technique
|
|
262
|
-
* src/adapters/
|
|
261
|
+
* src/adapters/kiro/index.js#pseudoAssetId uses. The
|
|
263
262
|
* lifecycle layer validates every change's assetId against the harness's actual asset list, so an
|
|
264
263
|
* invented id like the literal string 'opencode.config' is rejected as unknown. */
|
|
265
264
|
function pseudoAssetId(assets ) {
|
|
@@ -308,7 +307,8 @@ function createOpenCodeAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS]
|
|
|
308
307
|
|
|
309
308
|
const guidance = assets.find((asset ) => asset.capability === 'instructions');
|
|
310
309
|
if (guidance) {
|
|
311
|
-
const
|
|
310
|
+
const pointer = renderGuidancePointer(fsImpl.readFileSync(path.resolve(context.repoRoot, guidance.source), 'utf8'), { scope, scopeRoot });
|
|
311
|
+
const rendered = render({ content: pointer });
|
|
312
312
|
const outcome = removing
|
|
313
313
|
? { ok: true, operation: 'remove', content: strippedInstruction(found.instruction) }
|
|
314
314
|
: managedInstruction(found.instruction, rendered);
|
package/src/adapters/pi/hooks.js
CHANGED
|
@@ -93,7 +93,8 @@ function verifyPiHooks({ scope, scopeRoot, assets = [], ledger, context = {}, ad
|
|
|
93
93
|
const plannedWrite = (context?.plannedWrites || []).includes(target);
|
|
94
94
|
const bridgeMatches = !!content && content.includes(JSON.stringify(path.relative(path.dirname(target), bridgePath).split(path.sep).join('/')));
|
|
95
95
|
const commandOnPath = (command ) => String(pathEnv || '').split(path.delimiter)
|
|
96
|
-
.some((dir) =>
|
|
96
|
+
.some((dir) => [command, ...(process.platform === 'win32' ? ['.exe', '.cmd', '.bat'].map((ext) => command + ext) : [])]
|
|
97
|
+
.some((name) => fsImpl.existsSync(path.join(dir, name))));
|
|
97
98
|
const status = present && !owned && !plannedWrite && !adopt ? 'conflict'
|
|
98
99
|
: (present && (fingerprintMatches || plannedWrite) && bridgeMatches && commandOnPath('bash') && commandOnPath('jq') ? 'managed' : 'absent');
|
|
99
100
|
const resources = status === 'managed' ? [{ assetId: ASSET_ID, target, ownershipIdentity: OWNERSHIP,
|
package/src/adapters/pi/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
4
|
|
|
5
5
|
// Pi adapter — a marker-merged instruction file plus copy-tree materialisation, the same shape
|
|
6
|
-
// Claude/Codex
|
|
6
|
+
// Claude/Codex already use for their own skills trees.
|
|
7
7
|
//
|
|
8
8
|
// Skills materialise into Pi's own directories (`~/.pi/agent/skills` global, `.pi/skills` project)
|
|
9
9
|
// via the shared copy-tree engine, rather than pointing Pi's settings.json at DoFlow's already
|
|
@@ -22,10 +22,11 @@ const fs = require('node:fs');
|
|
|
22
22
|
const path = require('node:path');
|
|
23
23
|
|
|
24
24
|
const { MARKER_START, MARKER_END } = require('../../helper/marker-merge.js');
|
|
25
|
-
const { planTree, applyTree, removeTree, verifyTree, copyTreeAssets, copyTreeDestDir, sharedTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, fingerprint, sourceDirFor } = require('../copy-tree.js');
|
|
25
|
+
const { planTree, applyTree, removeTree, verifyTree, copyTreeAssets, copyTreeDestDir, sharedTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, fingerprint, sourceDirFor, resolveTransform } = require('../copy-tree.js');
|
|
26
26
|
const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.js');
|
|
27
27
|
const { mcpRows, discoverPiMcp, ownedPiMcpIds, planPiMcp, applyPiMcp, verifyPiMcp } = require('./mcp.js');
|
|
28
28
|
const { planPiHooks, verifyPiHooks, removePiHooks } = require('./hooks.js');
|
|
29
|
+
const { renderGuidancePointer } = require('../guidance-pointer.js');
|
|
29
30
|
|
|
30
31
|
const HARNESS = 'pi';
|
|
31
32
|
const AGENT_DIR_MCP_NOTICE = 'PI_CODING_AGENT_DIR is set: Pi reads its whole agent dir from it, but DoFlow moves only mcp.json there; skills and AGENTS.md stay in ~/.pi/agent.';
|
|
@@ -105,7 +106,7 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
105
106
|
const destDir = sharedTreeDestDir(paths.root, asset.nativeDir) ?? copyTreeDestDir(paths.configDir, asset);
|
|
106
107
|
const sourceDir = sourceDirFor(asset, context, fsImpl, 'Pi');
|
|
107
108
|
const previousResources = ledgerFileResources(ledger?.resources, HARNESS, asset.id);
|
|
108
|
-
const result = planTree({ sourceDir, destDir, previousResources, siblingFingerprints: ledgerSiblingFingerprints(ledger?.resources, HARNESS), operation: removing ? 'remove' : 'apply', fsImpl, layout: asset.layout, keepModified: !removing,
|
|
109
|
+
const result = planTree({ sourceDir, destDir, previousResources, siblingFingerprints: ledgerSiblingFingerprints(ledger?.resources, HARNESS), operation: removing ? 'remove' : 'apply', fsImpl, layout: asset.layout, transform: asset.transform, keepModified: !removing,
|
|
109
110
|
// Forwarded so the CLI's --force reaches planTree's conflict check; omitting it let
|
|
110
111
|
// planTree's own `force = false` default stand in silently. Gated on `!removing` for the
|
|
111
112
|
// reason codex/index.ts states in full: force heals drift on apply, but a hand-edited file
|
|
@@ -123,6 +124,7 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
123
124
|
kind: 'copy-tree-file', identity: change.relPath,
|
|
124
125
|
afterFingerprint: change.fingerprint, fingerprint: change.fingerprint, sourceVersion: 'registry-v1',
|
|
125
126
|
...(change.kept ? { retained: true, retainedFor: [] } : {}),
|
|
127
|
+
transformName: asset.transform || null,
|
|
126
128
|
projection: { renderer: 'copy-tree' },
|
|
127
129
|
});
|
|
128
130
|
}
|
|
@@ -132,8 +134,12 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
132
134
|
|
|
133
135
|
function applyCopyTreeAssets(changes , { fsImpl = fs } = {}) {
|
|
134
136
|
const treeChanges = changes.filter((change ) => change.projection?.renderer === 'copy-tree' && change.operation !== 'remove')
|
|
135
|
-
.map((change ) => ({ relPath: change.identity, target: change.target, source: change.source, operation: change.operation, fingerprint: change.fingerprint }));
|
|
136
|
-
|
|
137
|
+
.map((change ) => ({ relPath: change.identity, target: change.target, source: change.source, operation: change.operation, fingerprint: change.fingerprint, transformName: change.transformName || null }));
|
|
138
|
+
const grouped = new Map ();
|
|
139
|
+
for (const change of treeChanges) grouped.set(change.transformName, [...(grouped.get(change.transformName) || []), change]);
|
|
140
|
+
let applied = 0;
|
|
141
|
+
for (const [transformName, group] of grouped) applied += applyTree({ changes: group, fsImpl, transform: resolveTransform(transformName) }).applied;
|
|
142
|
+
return applied;
|
|
137
143
|
}
|
|
138
144
|
|
|
139
145
|
function removeCopyTreeAssets(changes , { fsImpl = fs } = {}) {
|
|
@@ -150,7 +156,7 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
150
156
|
for (const asset of copyTreeAssets(assets)) {
|
|
151
157
|
const destDir = sharedTreeDestDir(paths.root, asset.nativeDir) ?? copyTreeDestDir(paths.configDir, asset);
|
|
152
158
|
const sourceDir = sourceDirFor(asset, context, fsImpl, 'Pi');
|
|
153
|
-
const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, plannedWrites: context?.plannedWrites });
|
|
159
|
+
const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, transform: asset.transform, plannedWrites: context?.plannedWrites });
|
|
154
160
|
conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
|
|
155
161
|
for (const resource of result.resources) {
|
|
156
162
|
resources.push({
|
|
@@ -175,7 +181,8 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
175
181
|
|
|
176
182
|
const guidance = assets.find((asset ) => asset.capability === 'instructions');
|
|
177
183
|
if (guidance) {
|
|
178
|
-
const
|
|
184
|
+
const pointer = renderGuidancePointer(fsImpl.readFileSync(path.resolve(context.repoRoot, guidance.source), 'utf8'), { scope, scopeRoot });
|
|
185
|
+
const rendered = render({ content: pointer });
|
|
179
186
|
const outcome = removing
|
|
180
187
|
? { ok: true, operation: 'remove', content: strippedInstruction(found.instruction) }
|
|
181
188
|
: managedInstruction(found.instruction, rendered);
|
|
@@ -2,12 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
4
|
|
|
5
|
+
const fs = require('node:fs');
|
|
5
6
|
const path = require('node:path');
|
|
6
7
|
|
|
7
8
|
/** Resolve a registry-declared source path against the repo root, refusing escapes. Shared by the
|
|
8
9
|
* common projection module and by per-adapter native-projection normalizers, which is why it lives
|
|
9
|
-
* in its own module rather than in index.js — a normalizer importing index.js would be a cycle.
|
|
10
|
-
|
|
10
|
+
* in its own module rather than in index.js — a normalizer importing index.js would be a cycle.
|
|
11
|
+
* `sourceRoot` checks containment against that root using real paths, as required by IC-017.
|
|
12
|
+
* When omitted, the existing lexical repository-root check is preserved. */
|
|
13
|
+
function resolveRegistrySource(registry , source , label , sourceRoot ) {
|
|
14
|
+
if (sourceRoot !== undefined) {
|
|
15
|
+
if (typeof sourceRoot !== 'string' || !sourceRoot) {
|
|
16
|
+
throw new Error(`Registry ${label} sourceRoot must be a non-empty string`);
|
|
17
|
+
}
|
|
18
|
+
if (typeof source !== 'string' || !source) throw new Error(`Registry ${label} source is required`);
|
|
19
|
+
let realRoot ;
|
|
20
|
+
let realSource ;
|
|
21
|
+
try {
|
|
22
|
+
realRoot = fs.realpathSync(path.resolve(sourceRoot));
|
|
23
|
+
realSource = fs.realpathSync(path.resolve(sourceRoot, source));
|
|
24
|
+
} catch (error ) {
|
|
25
|
+
if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR') {
|
|
26
|
+
throw new Error(`Registry ${label} source is missing under source root: ${source}`);
|
|
27
|
+
}
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
if (!realSource.startsWith(`${realRoot}${path.sep}`)) {
|
|
31
|
+
throw new Error(`Registry ${label} source escapes source root: ${source}`);
|
|
32
|
+
}
|
|
33
|
+
return realSource;
|
|
34
|
+
}
|
|
11
35
|
if (typeof registry.repoRoot !== 'string' || !registry.repoRoot) throw new Error(`Registry repoRoot is required for ${label}`);
|
|
12
36
|
if (typeof source !== 'string' || !source) throw new Error(`Registry ${label} source is required`);
|
|
13
37
|
const root = path.resolve(registry.repoRoot);
|
|
@@ -20,7 +20,7 @@ const { loadRegistry } = require('../../registry/index.js');
|
|
|
20
20
|
const { storageAfterInstall } = require('../../runtime/storage/upkeep.js');
|
|
21
21
|
const { applyLifecycle, applyMcpIndex, recordMcpOwnership } = require('../../lifecycle/index.js');
|
|
22
22
|
const {
|
|
23
|
-
codexScope, registryLifecycleView, printRegistryLifecycle, printPlanNotices, assertSafeRegistryPlan,
|
|
23
|
+
codexScope, registryLifecycleView, printRegistryLifecycle, printRetiredPlans, printPlanNotices, assertSafeRegistryPlan,
|
|
24
24
|
lockDocument, recordLock,
|
|
25
25
|
} = require('../../lifecycle/view.js');
|
|
26
26
|
const {
|
|
@@ -120,6 +120,7 @@ function cmdInstall(o ) {
|
|
|
120
120
|
const owned = result.ledger.resources.filter((resource ) => resource.harness === target.harness).length;
|
|
121
121
|
console.log(`[INFO] ${target.harness}: lifecycle verified (${owned} owned resource(s))`);
|
|
122
122
|
}
|
|
123
|
+
printRetiredPlans(lifecycleView);
|
|
123
124
|
ledgerAfter = result.ledger;
|
|
124
125
|
} else {
|
|
125
126
|
// MCP_INDEX.md is generated, not a tracked resource, so it never appears in plan.changes and
|
|
@@ -2,13 +2,19 @@
|
|
|
2
2
|
// `doflow reconcile` — converge observed state onto the doflow.lock pin: report drift, then heal
|
|
3
3
|
// it on confirmation. Never re-prompts for MCP; selections ride exactly as pinned.
|
|
4
4
|
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
5
|
+
const path = require('node:path');
|
|
5
6
|
const { toolDirs } = require('../../install/targets.js');
|
|
6
7
|
const { resolveContext, printContext } = require('../../install/context.js');
|
|
7
8
|
const { writeManifest } = require('../../install/manifest.js');
|
|
9
|
+
const { DEFAULT_BACKUP_RETENTION, createFileBackup } = require('../../install/backup.js');
|
|
10
|
+
const { backupSetFromPlan, backupSetFromPaths } = require('../../install/backup-set.js');
|
|
11
|
+
const { ledgerPath } = require('../../state/index.js');
|
|
12
|
+
const { backupStep, reportBackup, applyBackupRetention } = require('./install.js');
|
|
8
13
|
const { confirm } = require('../../helper/prompt.js');
|
|
9
14
|
const { sourceCommit } = require('../../helper/git.js');
|
|
10
|
-
const { loadRegistry } = require('../../registry/index.js');
|
|
15
|
+
const { loadRegistry, retiredFor } = require('../../registry/index.js');
|
|
11
16
|
const { applyLifecycle, recordMcpOwnership } = require('../../lifecycle/index.js');
|
|
17
|
+
const { formatRetiredPlans, retiredReleaseKeys } = require('../../lifecycle/retired-harnesses.js');
|
|
12
18
|
const {
|
|
13
19
|
registryLifecycleView, assertSafeRegistryPlan,
|
|
14
20
|
} = require('../../lifecycle/view.js');
|
|
@@ -33,6 +39,10 @@ function reconcileReport(view ) {
|
|
|
33
39
|
}
|
|
34
40
|
perHarness.set(target.harness, entry);
|
|
35
41
|
}
|
|
42
|
+
// A retired harness's rows (row 127 IC-003) are drift too: converging means releasing them.
|
|
43
|
+
for (const change of (view.plan.retiredHarnesses ?? []).flatMap((item ) => item.changes)) {
|
|
44
|
+
drifts.push({ harness: change.harness, operation: change.operation, assetId: change.assetId, target: change.target, retired: true });
|
|
45
|
+
}
|
|
36
46
|
return {
|
|
37
47
|
drifts,
|
|
38
48
|
conflicts: view.plan.conflicts,
|
|
@@ -42,17 +52,29 @@ function reconcileReport(view ) {
|
|
|
42
52
|
};
|
|
43
53
|
}
|
|
44
54
|
|
|
45
|
-
function printReconcileReport(report , lock ) {
|
|
55
|
+
function printReconcileReport(report , lock , retiredLines = []) {
|
|
46
56
|
console.log(`[INFO] Reconciling against doflow.lock (sourceVersion ${lock.sourceVersion ?? 'unknown'})`);
|
|
47
57
|
for (const [harness, counts] of Object.entries (report.perHarness)) {
|
|
48
58
|
const total = counts.create + counts.update + counts.remove;
|
|
49
59
|
console.log(`[INFO] ${harness}: ${total === 0 ? 'clean' : `${total} drift(s) (${counts.create} create, ${counts.update} update, ${counts.remove} remove)`}`);
|
|
50
60
|
}
|
|
61
|
+
for (const line of retiredLines) console.log(line);
|
|
51
62
|
for (const conflict of report.conflicts) console.log(`[CONFLICT] ${conflict.harness}: ${conflict.reason}`);
|
|
52
63
|
for (const prerequisite of report.prerequisites) console.log(`[PENDING-TRUST] ${prerequisite.harness}: ${prerequisite.prerequisite}`);
|
|
53
64
|
if (report.clean) console.log('[OK] Observed state matches doflow.lock.');
|
|
54
65
|
}
|
|
55
66
|
|
|
67
|
+
/** The backup a retirement sweep takes (row 127 FR-4): the set install and update take, files and
|
|
68
|
+
* ledger. A sweep that only gives up rows (release, drop, keep) touches no file, so that set is empty;
|
|
69
|
+
* the ledger alone is then what a rollback needs to bring the rows and the target entry back. */
|
|
70
|
+
function retirementBackupSet(view , paths , scope ) {
|
|
71
|
+
const ledger = ledgerPath(view.stateRoot);
|
|
72
|
+
const args = { scope: scope.global ? 'global' : 'project', scopeRoot: paths.scopeRoot, records: [ledger],
|
|
73
|
+
exclude: [paths.backupRoot, view.stateRoot, paths.manifestPath, path.join(paths.doflowRoot, 'doflow.lock')] };
|
|
74
|
+
const set = backupSetFromPlan({ plan: view.plan, ...args });
|
|
75
|
+
return set.count ? set : backupSetFromPaths({ items: [{ path: ledger, harnesses: [] }], ...args });
|
|
76
|
+
}
|
|
77
|
+
|
|
56
78
|
function cmdReconcile(o ) {
|
|
57
79
|
refuseHomeRootedProject(o, 'reconcile');
|
|
58
80
|
const scope = scopeOf(o);
|
|
@@ -61,23 +83,33 @@ function cmdReconcile(o ) {
|
|
|
61
83
|
const dirs = toolDirs(scope);
|
|
62
84
|
const lifecyclePaths = installPaths(scope);
|
|
63
85
|
const { lock, ledger, manifestServers } = scopeSelectionState(scope);
|
|
86
|
+
// Read only when there is a pin or a row to classify, as before this check existed.
|
|
87
|
+
const registry = lock?.targets?.length || ledger?.resources?.length ? loadRegistry({ repoRoot: REPO_ROOT }) : null;
|
|
88
|
+
const retired = (harness ) => (registry ? retiredFor(registry, harness) : null);
|
|
89
|
+
// A retired pin (row 127 IC-004) names a harness with no adapter: dropped, said, and the rest
|
|
90
|
+
// converge. Its ledger rows are released by this run's plan, like install's and update's.
|
|
91
|
+
for (const entry of lock?.targets ?? []) {
|
|
92
|
+
const record = retired(entry.harness);
|
|
93
|
+
if (record) console.log(`[WARN] ${entry.harness}: retired (use ${record.replacement}); dropped from the pinned targets`);
|
|
94
|
+
}
|
|
95
|
+
const livePins = (lock?.targets ?? []).filter((entry ) => !retired(entry.harness));
|
|
64
96
|
// A harness the ledger holds but the lock does not pin is installed and outside reconcile's reach.
|
|
65
97
|
// Said, not converged: the lock records choices, and reconcile has no choice to record for it.
|
|
66
|
-
const pinned = new Set(
|
|
67
|
-
const unpinned = [...new Set((ledger?.resources ?? []).map((resource ) => resource.harness))]
|
|
98
|
+
const pinned = new Set(livePins.map((entry ) => entry.harness));
|
|
99
|
+
const unpinned = [...new Set((ledger?.resources ?? []).map((resource ) => resource.harness))]
|
|
100
|
+
.filter((harness ) => !pinned.has(harness) && !retired(harness)).sort();
|
|
68
101
|
for (const harness of unpinned) {
|
|
69
102
|
const count = ledger.resources.filter((resource ) => resource.harness === harness).length;
|
|
70
103
|
console.log(`[WARN] ${harness}: installed (ledger holds ${count} resource(s)) but absent from doflow.lock; reconcile does not converge it.`);
|
|
71
104
|
}
|
|
72
|
-
if (!lock || !
|
|
105
|
+
if (!lock || !livePins.length) {
|
|
73
106
|
// Reconcile converges onto what install pinned; with no pin there is no desired state to
|
|
74
107
|
// converge to, and guessing one from the registry would silently adopt targets the user
|
|
75
108
|
// never chose.
|
|
76
109
|
console.log('[INFO] No doflow.lock in this scope — nothing pinned to reconcile against. Run `doflow install` first.');
|
|
77
110
|
return;
|
|
78
111
|
}
|
|
79
|
-
const targets =
|
|
80
|
-
const registry = loadRegistry({ repoRoot: REPO_ROOT });
|
|
112
|
+
const targets = livePins.map((entry ) => entry.harness);
|
|
81
113
|
printContext(resolveContext({ repoRoot: REPO_ROOT, targets, dirs, sourceCommit: sourceCommit(SCRIPT_DIR), ...scope }));
|
|
82
114
|
// Each harness converges onto its own pinned selection — reconcile never re-prompts and never
|
|
83
115
|
// lends one harness's servers to another.
|
|
@@ -87,7 +119,7 @@ function cmdReconcile(o ) {
|
|
|
87
119
|
if (!lifecycleView.plan.safe) { assertSafeRegistryPlan(lifecycleView); return; }
|
|
88
120
|
|
|
89
121
|
const report = reconcileReport(lifecycleView);
|
|
90
|
-
printReconcileReport(report, lock);
|
|
122
|
+
printReconcileReport(report, lock, formatRetiredPlans(lifecycleView.plan.retiredHarnesses ?? []));
|
|
91
123
|
if (o.json) console.log(JSON.stringify({ scope: lock.scope, sourceVersion: lock.sourceVersion, targets, unpinned, ...report }, null, 2));
|
|
92
124
|
if (o.dryRun) {
|
|
93
125
|
console.log('[DRY] Reconcile plan complete — no changes written');
|
|
@@ -107,10 +139,22 @@ function cmdReconcile(o ) {
|
|
|
107
139
|
process.exit(1);
|
|
108
140
|
}
|
|
109
141
|
checkpointRunLock(hold);
|
|
142
|
+
// A run that changes a retired harness's rows backs the retirement up first, as install and
|
|
143
|
+
// update do, so `doflow rollback` restores it (row 127 FR-4). Any other reconcile takes none.
|
|
144
|
+
let bid = '';
|
|
145
|
+
const retiring = retiredReleaseKeys(lifecycleView.plan.retiredHarnesses ?? []).size > 0;
|
|
146
|
+
if (retiring && o.noBackup) {
|
|
147
|
+
console.error('[WARN] Skipping backup (--no-backup)');
|
|
148
|
+
} else if (retiring) {
|
|
149
|
+
const commit = sourceCommit(SCRIPT_DIR);
|
|
150
|
+
bid = reportBackup(backupStep(() => createFileBackup({ operation: 'reconcile', set: retirementBackupSet(lifecycleView, lifecyclePaths, scope),
|
|
151
|
+
backupRoot: lifecyclePaths.backupRoot, repoRoot: SCRIPT_DIR, sourceCommit: commit, version: pkg.version, date: new Date() })));
|
|
152
|
+
}
|
|
110
153
|
applyLifecycle({ plan: lifecycleView.plan, registry: lifecycleView.registry,
|
|
111
154
|
adapters: buildAdapterRegistry(),
|
|
112
155
|
stateRoot: lifecycleView.stateRoot, ledger: lifecycleView.ledger });
|
|
113
|
-
writeManifest({ scopeRoot: lifecyclePaths.scopeRoot, scriptVersion: pkg.version, operation: 'update', repoRoot: SCRIPT_DIR, sourceCommit: sourceCommit(SCRIPT_DIR), backupId:
|
|
156
|
+
writeManifest({ scopeRoot: lifecyclePaths.scopeRoot, scriptVersion: pkg.version, operation: 'update', repoRoot: SCRIPT_DIR, sourceCommit: sourceCommit(SCRIPT_DIR), backupId: bid, tools: targets, date: new Date() });
|
|
157
|
+
if (bid) applyBackupRetention({ backupRoot: lifecyclePaths.backupRoot, prune: o.prune, keep: o.prune ?? DEFAULT_BACKUP_RETENTION, bid });
|
|
114
158
|
console.log('[OK] Reconciliation complete — state converged onto doflow.lock.');
|
|
115
159
|
} finally {
|
|
116
160
|
hold?.release();
|
|
@@ -59,8 +59,8 @@ function cmdRemove(o ) {
|
|
|
59
59
|
scope: codexScope(scope), scopeRoot: scope.global ? os.homedir() : path.resolve(scope.projectRoot),
|
|
60
60
|
targets: lifecycleTargets, mcpAdoptable, retainedMcpIds, stateRoot: view.stateRoot, ledger: view.ledger,
|
|
61
61
|
context: view.plan.targets[0].adapterInput.context });
|
|
62
|
-
// Shared destinations (one .doflow/scripts tree for claude/codex
|
|
63
|
-
//
|
|
62
|
+
// Shared destinations (one .doflow/scripts tree for claude/codex, one .agents for
|
|
63
|
+
// antigravity/copilot) mean a removal can legitimately leave files standing. Saying only "removed"
|
|
64
64
|
// would be half the truth, so what was kept and who still claims it is printed, not implied.
|
|
65
65
|
for (const line of retentionSummary(result.retained)) console.log(`[INFO] ${line}`);
|
|
66
66
|
console.log(`[OK] Removed ${result.ledger.resources.length === 0 ? 'all' : 'eligible'} native resource(s) for ${lifecycleTargets.join(', ')}; ${result.ledger.resources.length} owned record(s) remain.`);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// restores per tool, as it always did.
|
|
7
7
|
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
8
8
|
const path = require('node:path');
|
|
9
|
-
const { resolveTargets, toolDirs } = require('../../install/targets.js');
|
|
9
|
+
const { VALID, resolveTargets, toolDirs } = require('../../install/targets.js');
|
|
10
10
|
const { resolveContext, printContext } = require('../../install/context.js');
|
|
11
11
|
const {
|
|
12
12
|
BackupError, createFileBackup, planRestore, executeRestore, listBackups, formatBytes,
|
|
@@ -59,9 +59,12 @@ function cmdRollback(o ) {
|
|
|
59
59
|
fail(error);
|
|
60
60
|
}
|
|
61
61
|
// A per-file backup restores the harnesses it holds (only those in --target when given), so the
|
|
62
|
-
// banner and the install manifest name those, not the default target.
|
|
62
|
+
// banner and the install manifest name those, not the default target. A backup taken by a run
|
|
63
|
+
// that swept a retired harness (127 IC-003) holds that harness's files and rows too: they are
|
|
64
|
+
// restored like any other, but a retired id is no target, so the banner and manifest skip it.
|
|
63
65
|
const restoredTools = plan.format === 2
|
|
64
|
-
? [...new Set(plan.restore.flatMap((item ) => item.harnesses))]
|
|
66
|
+
? [...new Set(plan.restore.flatMap((item ) => item.harnesses))]
|
|
67
|
+
.filter((h ) => VALID.includes(h) && (!explicitTargets || targets.includes(h))).sort()
|
|
65
68
|
: targets;
|
|
66
69
|
printContext(resolveContext({ repoRoot: REPO_ROOT, targets: restoredTools, dirs, sourceCommit: commit, global: o.global, projectRoot: '.' }));
|
|
67
70
|
|
|
@@ -2,32 +2,41 @@
|
|
|
2
2
|
// `doflow self-update` — git fetch/pull the checkout, then re-run the install path so the
|
|
3
3
|
// installed trees match the freshly pulled source.
|
|
4
4
|
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const path = require('node:path');
|
|
5
7
|
const { execFileSync } = require('node:child_process');
|
|
6
8
|
const { REPO_ROOT } = require('../shared.js');
|
|
7
9
|
const cmdInstall = require('./install.js');
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
/** The second argument is for tests: the checkout to update and the install it re-runs. */
|
|
12
|
+
function cmdSelfUpdate(o , { repoRoot = REPO_ROOT, install = cmdInstall } = {}) {
|
|
10
13
|
console.log('[INFO] Self-update: checking for upstream changes...');
|
|
11
14
|
let pulled = false;
|
|
12
15
|
let fetchOk = false;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
// An npm install is not a git checkout: `git -C` there would act on whatever repository holds
|
|
17
|
+
// node_modules (or fail), never on DoFlow's source.
|
|
18
|
+
const isCheckout = fs.existsSync(path.join(repoRoot, '.git'));
|
|
19
|
+
if (!isCheckout) console.log('[INFO] This install is not a git checkout; to upgrade DoFlow run: npm install -g @khoavu882/doflow');
|
|
20
|
+
if (isCheckout) {
|
|
21
|
+
try {
|
|
22
|
+
execFileSync('git', ['-C', repoRoot, 'fetch', '--depth=1', 'origin'], { stdio: ['ignore', 'ignore', 'pipe'], timeout: 10000 });
|
|
23
|
+
fetchOk = true;
|
|
24
|
+
} catch {
|
|
25
|
+
console.error('[WARN] git fetch failed (offline or no remote configured) — installing from current HEAD');
|
|
26
|
+
}
|
|
18
27
|
}
|
|
19
28
|
if (fetchOk) {
|
|
20
29
|
try {
|
|
21
|
-
execFileSync('git', ['-C',
|
|
30
|
+
execFileSync('git', ['-C', repoRoot, 'pull', '--ff-only'], { stdio: ['ignore', 'ignore', 'pipe'], timeout: 10000 });
|
|
22
31
|
pulled = true;
|
|
23
|
-
const commit = execFileSync('git', ['-C',
|
|
32
|
+
const commit = execFileSync('git', ['-C', repoRoot, 'rev-parse', '--short', 'HEAD']).toString().trim();
|
|
24
33
|
console.log(`[INFO] Updated to commit: ${commit}`);
|
|
25
34
|
} catch {
|
|
26
35
|
console.error('[WARN] Fast-forward not possible — using current local state');
|
|
27
36
|
}
|
|
28
37
|
}
|
|
29
38
|
console.log(`[INFO] Running install (after ${pulled ? 'a git pull' : 'checking for updates'})...`);
|
|
30
|
-
|
|
39
|
+
install(o);
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
module.exports = cmdSelfUpdate;
|
|
@@ -42,6 +42,10 @@ function cmdStatus(o ) {
|
|
|
42
42
|
ledgerPresent: Boolean(readLedger(registryView.stateRoot)),
|
|
43
43
|
plan: { changes: registryView.plan.changes.length, conflicts: registryView.plan.conflicts, prerequisites: registryView.plan.prerequisites },
|
|
44
44
|
};
|
|
45
|
+
// Row 127: a retired harness the ledger still holds rows of, which the next install or update
|
|
46
|
+
// releases. Absent when there is none, so the report is unchanged for every other ledger.
|
|
47
|
+
const retired = (registryView.plan.retiredHarnesses ?? []).map((item ) => ({ harness: item.harness, replacement: item.replacement, rows: item.changes.length }));
|
|
48
|
+
if (retired.length) ctx.registry.retired = retired;
|
|
45
49
|
// Per-harness status must derive from that harness's own plan target, not the flattened
|
|
46
50
|
// registryView.plan.changes/conflicts across every target — otherwise, e.g., Codex having
|
|
47
51
|
// pending changes when it isn't installed yet would make Claude's line falsely report
|
|
@@ -50,7 +54,7 @@ function cmdStatus(o ) {
|
|
|
50
54
|
// Hook-wiring status needs the same general per-harness verification every install/update run
|
|
51
55
|
// already flows through (src/lifecycle's verifyLifecycle) rather than a Codex-only hardcode:
|
|
52
56
|
// it is the one place that can tell 'installed and active' apart from 'installed but pending a
|
|
53
|
-
// prerequisite' (Codex's unreviewed trust
|
|
57
|
+
// prerequisite' (Codex's unreviewed trust) from 'absent'.
|
|
54
58
|
const verification = verifyLifecycle({ plan: registryView.plan, adapters: registryView.adapters, context: { registry } });
|
|
55
59
|
const hooksFor = (harness ) => verification.verifications.find((item ) => item.harness === harness)?.hookWiring ?? null;
|
|
56
60
|
const harnessStatus = (harness ) => {
|
|
@@ -80,8 +84,19 @@ function cmdStatus(o ) {
|
|
|
80
84
|
ctx.registry = { status: 'invalid', error: error.message };
|
|
81
85
|
}
|
|
82
86
|
|
|
87
|
+
// The primary is read on use only (row 120 A.4): a fault in resolving it never breaks status.
|
|
88
|
+
let primary = null;
|
|
89
|
+
let primaryText ;
|
|
90
|
+
try {
|
|
91
|
+
const { primarySummary, resolveForReport } = require('../../runtime/context/primary-command.js');
|
|
92
|
+
primary = resolveForReport({ env: process.env, projectRoot: path.resolve(scope.projectRoot) }).primary;
|
|
93
|
+
primaryText = primarySummary(primary);
|
|
94
|
+
} catch (error ) {
|
|
95
|
+
primaryText = `unavailable (${error?.message || String(error)})`;
|
|
96
|
+
}
|
|
97
|
+
|
|
83
98
|
if (o.json) {
|
|
84
|
-
console.log(JSON.stringify({ context: ctx, manifest, codex: ctx.codex ?? null, mcpSelections }, null, 2));
|
|
99
|
+
console.log(JSON.stringify({ context: ctx, manifest, codex: ctx.codex ?? null, mcpSelections, primary }, null, 2));
|
|
85
100
|
return;
|
|
86
101
|
}
|
|
87
102
|
|
|
@@ -92,6 +107,7 @@ function cmdStatus(o ) {
|
|
|
92
107
|
};
|
|
93
108
|
|
|
94
109
|
printContext(ctx);
|
|
110
|
+
console.log(`primary: ${primaryText}`);
|
|
95
111
|
if (!manifest) {
|
|
96
112
|
console.log("[WARN] No install manifest found — run 'doflow install' to get started");
|
|
97
113
|
return;
|
|
@@ -126,6 +142,9 @@ function cmdStatus(o ) {
|
|
|
126
142
|
if (ctx.registry) {
|
|
127
143
|
console.log(` Registry lifecycle: ${ctx.registry.status === 'invalid' ? ctx.registry.error : `${ctx.registry.plan.changes} pending, ${ctx.registry.plan.conflicts.length} conflict(s)`}`);
|
|
128
144
|
if (ctx.registry.status !== 'invalid') console.log(` Neutral state: ${ctx.registry.stateRoot}${ctx.registry.ledgerPresent ? ' (ledger present)' : ' (not yet created)'}`);
|
|
145
|
+
for (const item of ctx.registry.retired ?? []) {
|
|
146
|
+
console.log(` Retired harness: ${item.harness} (use ${item.replacement}): ${item.rows} ledger row(s); the next 'doflow install' or 'doflow update' in this scope releases them`);
|
|
147
|
+
}
|
|
129
148
|
}
|
|
130
149
|
console.log('\n TOOL STATUS LAST UPDATED');
|
|
131
150
|
for (const tool of LIFECYCLE_HARNESSES) {
|