@khoavu882/doflow 2.0.0-beta.4 → 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 -3
- package/core/.claude-plugin/plugin.json +1 -1
- package/core/.codex-plugin/plugin.json +1 -1
- package/core/.plugin/plugin.json +1 -1
- package/core/harnesses/claude/hooks/lib.sh +43 -24
- package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
- 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-bash-guard.sh +7 -17
- package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
- package/core/harnesses/claude/hooks/session-end.sh +23 -10
- package/core/harnesses/claude/hooks/session-start.sh +5 -3
- package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
- package/core/harnesses/claude/hooks/stop-check.sh +5 -3
- package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
- package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
- 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/mcp-tool-guard.sh +7 -15
- package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
- package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
- package/core/harnesses/codex/hooks/session-start.sh +5 -3
- package/core/harnesses/codex/hooks/stop-check.sh +6 -5
- package/core/harnesses/codex/hooks/subagent-audit.sh +18 -6
- package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
- 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 +21 -5
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +21 -5
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +21 -5
- package/core/harnesses/kiro/hooks/session-start.sh +12 -5
- package/core/harnesses/kiro/hooks/stop-check.sh +22 -30
- package/core/harnesses/pi/extensions/doflow-guard.js +167 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -12
- package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
- package/core/harnesses/shared/hooks/bridge/bridge.sh +277 -0
- package/core/harnesses/shared/hooks/bridge/envelope.jq +129 -0
- package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +70 -0
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +51 -16
- package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
- package/core/harnesses/shared/hooks/policies/contract.sh +78 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +35 -9
- 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 +65 -40
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +48 -35
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +284 -33
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +180 -110
- package/core/harnesses/shared/hooks/policies/session-context.sh +66 -7
- package/core/harnesses/shared/hooks/policies/stop-check.sh +61 -23
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +39 -6
- package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +97 -159
- package/core/harnesses/shared/hooks/stream-hook-runner.js +114 -187
- package/core/harnesses/shared/locator/doflow-run +2 -3
- package/core/registry/advice.json +3 -3
- package/core/registry/assets.json +72 -80
- package/core/registry/capabilities.json +12 -0
- package/core/registry/contracts.json +60 -33
- 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 +1542 -277
- package/core/registry/lifecycle.json +2837 -37
- 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 +701 -30
- 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 +154 -35
- 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 +24 -23
- 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 +17 -59
- 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 +28 -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 +7 -9
- package/core/shared/skills/do-plan/SKILL.md +29 -19
- 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 +226 -113
- package/src/adapters/claude/index.js +39 -5
- package/src/adapters/codex/agents.js +25 -15
- package/src/adapters/codex/hooks.js +40 -5
- package/src/adapters/codex/index.js +100 -22
- package/src/adapters/codex/native-projection.js +6 -2
- package/src/adapters/copilot/index.js +58 -23
- package/src/adapters/copy-tree.js +294 -48
- 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 +38 -13
- package/src/adapters/opencode/index.js +8 -8
- package/src/adapters/pi/hooks.js +120 -0
- package/src/adapters/pi/index.js +48 -16
- package/src/adapters/resolve-source.js +26 -2
- package/src/cli/commands/install.js +3 -2
- 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 +3 -2
- package/src/cli/index.js +338 -33
- package/src/cli/runtime-commands.js +243 -52
- 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 +74 -0
- package/src/lifecycle/hook-step.js +61 -0
- package/src/lifecycle/hook-tier.js +160 -0
- package/src/lifecycle/index.js +434 -86
- package/src/lifecycle/retired-harnesses.js +318 -0
- package/src/lifecycle/view.js +35 -15
- package/src/registry/index.js +686 -32
- 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 +514 -0
- 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 +27 -12
- package/src/runtime/cli-result.js +11 -4
- package/src/runtime/cli.js +221 -277
- package/src/runtime/config-home.js +42 -0
- package/src/runtime/context/arbitrate.js +767 -0
- package/src/runtime/context/budget.js +203 -0
- package/src/runtime/context/compose.js +1367 -0
- package/src/runtime/context/delivery-store.js +100 -0
- package/src/runtime/context/emission-rules.js +118 -0
- package/src/runtime/context/identity.js +227 -0
- package/src/runtime/context/mandatory.js +594 -0
- package/src/runtime/context/pointer.js +142 -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 +1106 -245
- 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-fields.js +39 -0
- package/src/runtime/evidence-ledger.js +41 -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 +77 -10
- 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 +3 -3
- 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 +127 -16
- package/src/runtime/research-request.js +50 -25
- package/src/runtime/retrieval-plan.js +402 -73
- package/src/runtime/retrieval-record.js +293 -0
- 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 +63 -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 +290 -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 +202 -56
- package/src/runtime/task-sources.js +147 -0
- package/src/runtime/task-state.js +440 -65
- 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/.antigravity-plugin/hooks.json +0 -39
- 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 -21
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -21
- package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -22
- 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 -5
- package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
- package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
- package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
- package/core/harnesses/shared/hooks/hooks.json +0 -47
- 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 -210
- package/src/adapters/gemini/index.js +0 -390
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
4
|
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// adapter). verifyHookCommands and the two private helpers it calls, commandScriptNames and
|
|
5
|
+
// Hook-command verification, kept apart from codex/hooks.ts because it does not depend on the
|
|
6
|
+
// harness (unlike SUPPORTED_EVENTS, validateHooksConfig, and classifyClaudeGuardrails, which
|
|
7
|
+
// encode genuine per-harness differences and stay in each adapter). verifyHookCommands and the two private helpers it calls, commandScriptNames and
|
|
9
8
|
// hookHandlers, check that a hook config's command handlers resolve to a real, executable script
|
|
10
9
|
// in the harness's own hooks directory — that check does not vary by harness, so it lives once.
|
|
11
10
|
const fs = require('node:fs');
|
|
@@ -23,12 +22,9 @@ function hookHandlers(config ) {
|
|
|
23
22
|
(Array.isArray(group?.hooks) ? group.hooks : []).map((handler ) => ({ event, handler }))));
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
// On `trusted`:
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
// a running CLI session. It reports trust as a prerequisite whose shape it observed, not one it
|
|
30
|
-
// bypassed or assumed satisfied. That reading is identical for both harnesses, which is part of
|
|
31
|
-
// why this function is shared.
|
|
25
|
+
// On `trusted`: Codex gates hook execution behind its own trust prompt, through its config.
|
|
26
|
+
// DoFlow does not verify that gate live; doing so would need a running CLI session. It reports
|
|
27
|
+
// trust as a prerequisite whose shape it observed, not one it bypassed or assumed satisfied.
|
|
32
28
|
// Windows carries no executable permission bits — statSync().mode always reports 0666/0444 and
|
|
33
29
|
// chmodSync can only toggle the read-only flag — so the POSIX "not executable" failure mode
|
|
34
30
|
// cannot exist there. Requiring the bit would refuse every hooks install on win32 (the scripts
|
package/src/adapters/index.js
CHANGED
|
@@ -52,6 +52,7 @@ function projectAdapterInput({ registry, harness, scope, scopeRoot, assets = [],
|
|
|
52
52
|
// source paths, which is all of them except Antigravity's directory-per-agent requirement.
|
|
53
53
|
layout: projection.layout ?? null,
|
|
54
54
|
transform: projection.transform ?? null,
|
|
55
|
+
select: projection.select ?? null,
|
|
55
56
|
prerequisites: [...(capability.prerequisites || [])],
|
|
56
57
|
});
|
|
57
58
|
};
|
|
@@ -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
|
}
|
|
@@ -258,6 +269,19 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
258
269
|
};
|
|
259
270
|
}
|
|
260
271
|
|
|
272
|
+
/** The hook front doors (IC-037): the scripts and the agent that wires them. */
|
|
273
|
+
function hookDoorAssets(assets ) { return (assets || []).filter((asset ) => asset.id === 'kiro.hooks-scripts' || asset.id === 'kiro.doflow-agent'); }
|
|
274
|
+
|
|
275
|
+
function planHookDoors({ scope, scopeRoot, assets = [], context = {}, ledger, fsImpl = fs } ) {
|
|
276
|
+
const doors = planCopyTreeAssets({ assets: hookDoorAssets(assets), scope, scopeRoot, context, ledger, removing: false, fsImpl });
|
|
277
|
+
return { changes: doors.changes, conflicts: doors.conflicts, prerequisites: [], notices: doors.notices };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function verifyHookDoors({ scope, scopeRoot, assets = [], context = {}, fsImpl = fs } ) {
|
|
281
|
+
const doors = verifyCopyTreeAssets({ assets: hookDoorAssets(assets), scope, scopeRoot, context, fsImpl });
|
|
282
|
+
return { ok: doors.conflicts.length === 0, statuses: doors.statuses, resources: doors.resources, conflicts: doors.conflicts };
|
|
283
|
+
}
|
|
284
|
+
|
|
261
285
|
function apply({ changes = [], fsImpl = fs } ) {
|
|
262
286
|
writeMcpEntries(changes, { container: MCP_CONTAINER, fsImpl });
|
|
263
287
|
applyCopyTreeAssets(changes, { fsImpl });
|
|
@@ -297,7 +321,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
297
321
|
};
|
|
298
322
|
}
|
|
299
323
|
|
|
300
|
-
return { nativePaths, discover, render, plan, apply, remove, verify, buildServerEntry };
|
|
324
|
+
return { nativePaths, discover, render, plan, apply, remove, verify, planHookDoors, verifyHookDoors, buildServerEntry };
|
|
301
325
|
}
|
|
302
326
|
|
|
303
327
|
const singleton = createKiroAdapter();
|
|
@@ -305,6 +329,7 @@ const singleton = createKiroAdapter();
|
|
|
305
329
|
module.exports = {
|
|
306
330
|
nativePaths: singleton.nativePaths, discover: singleton.discover, render: singleton.render,
|
|
307
331
|
plan: singleton.plan, apply: singleton.apply, remove: singleton.remove, verify: singleton.verify,
|
|
332
|
+
planHookDoors: singleton.planHookDoors, verifyHookDoors: singleton.verifyHookDoors,
|
|
308
333
|
buildServerEntry: singleton.buildServerEntry,
|
|
309
334
|
createKiroAdapter,
|
|
310
335
|
};
|
|
@@ -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);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
|
+
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const { fingerprint } = require('../copy-tree.js');
|
|
8
|
+
|
|
9
|
+
const ASSET_ID = 'pi.guard-extension';
|
|
10
|
+
const EXTENSION_NAME = 'doflow-guard.js';
|
|
11
|
+
const OWNERSHIP = 'pi:guard-extension';
|
|
12
|
+
const TEMPLATE = 'core/harnesses/pi/extensions/doflow-guard.js';
|
|
13
|
+
const BRIDGE_SUFFIX = ['.doflow', 'shared', 'hooks', 'bridge', 'bridge.sh'];
|
|
14
|
+
|
|
15
|
+
function userAgentDir(scopeRoot , env ) {
|
|
16
|
+
const override = env?.PI_CODING_AGENT_DIR;
|
|
17
|
+
return typeof override === 'string' && override.trim() ? path.resolve(override.trim()) : path.join(scopeRoot, '.pi', 'agent');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function targetPath(scopeRoot , env , scope = 'user') {
|
|
21
|
+
return scope === 'project'
|
|
22
|
+
? path.join(scopeRoot, '.pi', 'extensions', EXTENSION_NAME)
|
|
23
|
+
: path.join(userAgentDir(scopeRoot, env), 'extensions', EXTENSION_NAME);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function renderExtension({ repoRoot, scopeRoot, target, fsImpl = fs } ) {
|
|
27
|
+
const templatePath = path.join(repoRoot, TEMPLATE);
|
|
28
|
+
const template = fsImpl.readFileSync(templatePath, 'utf8');
|
|
29
|
+
const bridgePath = path.join(scopeRoot, ...BRIDGE_SUFFIX);
|
|
30
|
+
const relative = path.relative(path.dirname(target), bridgePath).split(path.sep).join('/');
|
|
31
|
+
const rendered = template.replace("const BRIDGE = new URL('../../shared/hooks/bridge/bridge.sh', import.meta.url);",
|
|
32
|
+
`const BRIDGE = new URL(${JSON.stringify(relative)}, import.meta.url);`);
|
|
33
|
+
if (rendered === template) throw new Error('Pi extension bridge path template was not found');
|
|
34
|
+
return rendered;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function planPiHooks({ scope, scopeRoot, assets = [], ledger, context = {}, adopt = false, env = process.env, fsImpl = fs } ) {
|
|
38
|
+
if (scope !== 'user' && scope !== 'global' && scope !== 'project') return { changes: [], conflicts: [], notices: [] };
|
|
39
|
+
const asset = assets.find((row ) => row.id === ASSET_ID);
|
|
40
|
+
if (!asset) return { changes: [], conflicts: [], notices: [] };
|
|
41
|
+
const computedTarget = targetPath(scopeRoot, env, scope);
|
|
42
|
+
const removing = context.operation === 'remove';
|
|
43
|
+
const ownedRows = (ledger?.resources || []).filter((row ) => row.harness === 'pi' && row.assetId === ASSET_ID
|
|
44
|
+
&& row.ownershipIdentity === OWNERSHIP);
|
|
45
|
+
const exactOwned = ownedRows.find((row ) => row.target === computedTarget);
|
|
46
|
+
const owned = exactOwned || (removing && ownedRows.length === 1 ? ownedRows[0] : undefined);
|
|
47
|
+
const target = removing && owned ? owned.target : computedTarget;
|
|
48
|
+
const exists = fsImpl.existsSync(target);
|
|
49
|
+
const notices = [];
|
|
50
|
+
if (removing) {
|
|
51
|
+
if (!owned) return { changes: [], conflicts: [], notices };
|
|
52
|
+
if (!exists) return { changes: [{ assetId: ASSET_ID, target, operation: 'remove', release: true,
|
|
53
|
+
ownershipIdentity: OWNERSHIP, harness: 'pi', projection: { renderer: 'pi-guard-extension' } }], conflicts: [], notices };
|
|
54
|
+
const current = fsImpl.readFileSync(target, 'utf8');
|
|
55
|
+
if (fingerprint(current) !== owned.fingerprint) {
|
|
56
|
+
notices.push(`kept hand-edited ${target}; DoFlow no longer manages it`);
|
|
57
|
+
return { changes: [{ assetId: ASSET_ID, target, operation: 'remove', release: true,
|
|
58
|
+
ownershipIdentity: OWNERSHIP, harness: 'pi', projection: { renderer: 'pi-guard-extension' } }], conflicts: [], notices };
|
|
59
|
+
}
|
|
60
|
+
return { changes: [{ assetId: ASSET_ID, target, operation: 'remove', content: null,
|
|
61
|
+
ownershipIdentity: OWNERSHIP, harness: 'pi', projection: { renderer: 'pi-guard-extension' } }], conflicts: [], notices };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const content = renderExtension({ repoRoot: context.repoRoot, scopeRoot, target, fsImpl });
|
|
65
|
+
if (!exists) return { changes: [{ assetId: ASSET_ID, target, operation: 'create', content,
|
|
66
|
+
ownershipIdentity: OWNERSHIP, harness: 'pi', fingerprint: fingerprint(content),
|
|
67
|
+
projection: { renderer: 'pi-guard-extension' } }], conflicts: [], notices };
|
|
68
|
+
if (!owned && !adopt) {
|
|
69
|
+
return { changes: [], conflicts: [`Pi extension already exists and is not owned by DoFlow: ${target}`], notices };
|
|
70
|
+
}
|
|
71
|
+
const current = fsImpl.readFileSync(target, 'utf8');
|
|
72
|
+
if (owned && fingerprint(current) !== owned.fingerprint) {
|
|
73
|
+
return { changes: [], conflicts: [`Pi extension was modified outside DoFlow: ${target}`], notices };
|
|
74
|
+
}
|
|
75
|
+
if (current === content) return { changes: [], conflicts: [], notices };
|
|
76
|
+
return { changes: [{ assetId: ASSET_ID, target, operation: 'update', content,
|
|
77
|
+
ownershipIdentity: OWNERSHIP, harness: 'pi', fingerprint: fingerprint(content),
|
|
78
|
+
projection: { renderer: 'pi-guard-extension' } }], conflicts: [], notices };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function verifyPiHooks({ scope, scopeRoot, assets = [], ledger, context = {}, adopt = false, env = process.env, fsImpl = fs, pathEnv = process.env.PATH } ) {
|
|
82
|
+
if (scope !== 'user' && scope !== 'global' && scope !== 'project') return { statuses: [], resources: [], conflicts: [] };
|
|
83
|
+
const asset = assets.find((row ) => row.id === ASSET_ID);
|
|
84
|
+
if (!asset) return { statuses: [], resources: [], conflicts: [] };
|
|
85
|
+
const target = targetPath(scopeRoot, env, scope);
|
|
86
|
+
const owned = (ledger?.resources || []).find((row ) => row.harness === 'pi' && row.assetId === ASSET_ID
|
|
87
|
+
&& row.ownershipIdentity === OWNERSHIP && row.target === target);
|
|
88
|
+
const bridgePath = path.join(scopeRoot, ...BRIDGE_SUFFIX);
|
|
89
|
+
const present = fsImpl.existsSync(target);
|
|
90
|
+
const content = present ? fsImpl.readFileSync(target, 'utf8') : null;
|
|
91
|
+
const actualFingerprint = content === null ? null : fingerprint(content);
|
|
92
|
+
const fingerprintMatches = !!owned && actualFingerprint === owned.fingerprint;
|
|
93
|
+
const plannedWrite = (context?.plannedWrites || []).includes(target);
|
|
94
|
+
const bridgeMatches = !!content && content.includes(JSON.stringify(path.relative(path.dirname(target), bridgePath).split(path.sep).join('/')));
|
|
95
|
+
const commandOnPath = (command ) => String(pathEnv || '').split(path.delimiter)
|
|
96
|
+
.some((dir) => [command, ...(process.platform === 'win32' ? ['.exe', '.cmd', '.bat'].map((ext) => command + ext) : [])]
|
|
97
|
+
.some((name) => fsImpl.existsSync(path.join(dir, name))));
|
|
98
|
+
const status = present && !owned && !plannedWrite && !adopt ? 'conflict'
|
|
99
|
+
: (present && (fingerprintMatches || plannedWrite) && bridgeMatches && commandOnPath('bash') && commandOnPath('jq') ? 'managed' : 'absent');
|
|
100
|
+
const resources = status === 'managed' ? [{ assetId: ASSET_ID, target, ownershipIdentity: OWNERSHIP,
|
|
101
|
+
fingerprint: actualFingerprint, sourceVersion: context.sourceVersion ?? 'unknown',
|
|
102
|
+
projection: { renderer: 'pi-guard-extension' } }] : [];
|
|
103
|
+
const statuses = [{ harness: 'pi', assetId: ASSET_ID, capability: 'hooks', status, target,
|
|
104
|
+
ownershipIdentity: OWNERSHIP, fingerprint: actualFingerprint, fingerprintMatches,
|
|
105
|
+
bridgePath, bridgeExists: fsImpl.existsSync(bridgePath), bash: commandOnPath('bash'), jq: commandOnPath('jq'),
|
|
106
|
+
prerequisites: scope === 'project' ? ['project-trust'] : [], piCodingAgentDir: typeof env?.PI_CODING_AGENT_DIR === 'string' && env.PI_CODING_AGENT_DIR.trim() ? env.PI_CODING_AGENT_DIR.trim() : null }];
|
|
107
|
+
const conflicts = status === 'conflict' ? [`Pi extension is not owned by DoFlow: ${target}`] : [];
|
|
108
|
+
return { statuses, resources, conflicts };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function removePiHooks(changes , { fsImpl = fs } = {}) {
|
|
112
|
+
let removed = 0;
|
|
113
|
+
for (const change of changes) {
|
|
114
|
+
if (change.projection?.renderer !== 'pi-guard-extension' || change.operation !== 'remove' || change.release) continue;
|
|
115
|
+
if (fsImpl.existsSync(change.target)) { fsImpl.unlinkSync(change.target); removed += 1; }
|
|
116
|
+
}
|
|
117
|
+
return removed;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = { ASSET_ID, EXTENSION_NAME, OWNERSHIP, userAgentDir, targetPath, renderExtension, planPiHooks, verifyPiHooks, removePiHooks };
|
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
|
|
@@ -13,9 +13,7 @@
|
|
|
13
13
|
//
|
|
14
14
|
// MCP servers are merged per server into Pi's mcp.json (.pi/mcp.json project; ~/.pi/agent/mcp.json
|
|
15
15
|
// or $PI_CODING_AGENT_DIR/mcp.json user) by ./mcp.ts; Pi reads it through its built-in MCP from
|
|
16
|
-
// 0.99.0.
|
|
17
|
-
// executing with full system permissions, which is a different trust model from the shell scripts
|
|
18
|
-
// DoFlow ships.
|
|
16
|
+
// 0.99.0. The user- and project-scope lifecycle extension is ledger-owned and resolves its shared bridge at install time.
|
|
19
17
|
//
|
|
20
18
|
// Evidence: https://pi.dev/docs/latest/settings, https://pi.dev/docs/latest/skills,
|
|
21
19
|
// https://pi.dev/docs/latest/quickstart, https://pi.dev/docs/latest/extensions,
|
|
@@ -24,12 +22,15 @@ const fs = require('node:fs');
|
|
|
24
22
|
const path = require('node:path');
|
|
25
23
|
|
|
26
24
|
const { MARKER_START, MARKER_END } = require('../../helper/marker-merge.js');
|
|
27
|
-
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');
|
|
28
26
|
const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.js');
|
|
29
27
|
const { mcpRows, discoverPiMcp, ownedPiMcpIds, planPiMcp, applyPiMcp, verifyPiMcp } = require('./mcp.js');
|
|
28
|
+
const { planPiHooks, verifyPiHooks, removePiHooks } = require('./hooks.js');
|
|
29
|
+
const { renderGuidancePointer } = require('../guidance-pointer.js');
|
|
30
30
|
|
|
31
31
|
const HARNESS = 'pi';
|
|
32
|
-
const
|
|
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.';
|
|
33
|
+
const AGENT_DIR_NOTICE = 'PI_CODING_AGENT_DIR is set: Pi reads its whole agent dir from it, and DoFlow moves mcp.json and the guard extension there; skills and AGENTS.md stay in ~/.pi/agent.';
|
|
33
34
|
|
|
34
35
|
// Native path facts live in core/registry/harnesses.json under this harness's "paths" section and
|
|
35
36
|
// resolve through the shared harness-paths resolver; nothing below hardcodes them.
|
|
@@ -43,8 +44,8 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
43
44
|
function nativePaths({ scope, scopeRoot } = {}) {
|
|
44
45
|
const paths = resolveHarnessPaths(declaredPaths, { scope, scopeRoot });
|
|
45
46
|
// Pi's user-scope mcp.json follows $PI_CODING_AGENT_DIR when set. It is not declared in the
|
|
46
|
-
// registry because the path schema has no environment base (src/helper/harness-paths.js);
|
|
47
|
-
//
|
|
47
|
+
// registry because the path schema has no environment base (src/helper/harness-paths.js); the
|
|
48
|
+
// guard extension follows it too, while skills and AGENTS.md keep their declared locations.
|
|
48
49
|
if ((scope === 'user' || scope === 'global') && agentDirOverride()) paths.mcp = path.join(path.resolve(agentDirOverride()), 'mcp.json');
|
|
49
50
|
return paths;
|
|
50
51
|
}
|
|
@@ -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);
|
|
@@ -191,15 +198,32 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
191
198
|
changes.push(...copyTree.changes);
|
|
192
199
|
conflicts.push(...copyTree.conflicts);
|
|
193
200
|
|
|
201
|
+
const piHooks = planPiHooks({ scope, scopeRoot, assets, ledger, context, adopt: context?.adopt === true, env, fsImpl });
|
|
202
|
+
changes.push(...piHooks.changes);
|
|
203
|
+
conflicts.push(...piHooks.conflicts);
|
|
204
|
+
|
|
194
205
|
const piMcp = planPiMcp({ selected: mcp, rows: mcpRows(ledger), file: found.paths.mcp, scope, removing, snapshot: found.mcp, assets });
|
|
195
206
|
changes.push(...piMcp.changes);
|
|
196
207
|
conflicts.push(...piMcp.conflicts);
|
|
197
208
|
|
|
198
|
-
const notices = [...copyTree.notices, ...piMcp.notices];
|
|
199
|
-
if (!removing && scope !== 'project' && agentDirOverride())
|
|
209
|
+
const notices = [...copyTree.notices, ...piMcp.notices, ...piHooks.notices];
|
|
210
|
+
if (!removing && scope !== 'project' && agentDirOverride()) {
|
|
211
|
+
notices.push(assets.some((asset ) => asset.id === 'pi.guard-extension') ? AGENT_DIR_NOTICE : AGENT_DIR_MCP_NOTICE);
|
|
212
|
+
}
|
|
200
213
|
return { changes, conflicts, notices, paths: found.paths };
|
|
201
214
|
}
|
|
202
215
|
|
|
216
|
+
/** The hook front door (IC-037): the guard extension. */
|
|
217
|
+
function planHookDoors({ scope, scopeRoot, assets = [], context = {}, ledger, fsImpl = fs } ) {
|
|
218
|
+
const door = planPiHooks({ scope, scopeRoot, assets, ledger, context, adopt: context?.adopt === true, env, fsImpl });
|
|
219
|
+
return { changes: door.changes, conflicts: door.conflicts, notices: door.notices };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function verifyHookDoors({ scope, scopeRoot, assets = [], ledger, context = {}, fsImpl = fs } ) {
|
|
223
|
+
const door = verifyPiHooks({ scope, scopeRoot, assets, ledger, context, adopt: context?.adopt === true, env, fsImpl });
|
|
224
|
+
return { ok: door.conflicts.length === 0 && !door.statuses.some((s ) => s.status === 'invalid' || s.status === 'conflict'), ...door };
|
|
225
|
+
}
|
|
226
|
+
|
|
203
227
|
function writeChange(change , fsImpl ) {
|
|
204
228
|
fsImpl.mkdirSync(path.dirname(change.target), { recursive: true });
|
|
205
229
|
fsImpl.writeFileSync(change.target, change.content, 'utf8');
|
|
@@ -226,10 +250,12 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
226
250
|
let removed = applyPiMcp(changes.filter(isPiMcp), { fsImpl }).applied;
|
|
227
251
|
for (const change of changes) {
|
|
228
252
|
if (change.operation !== 'remove' || change.projection?.renderer === 'copy-tree' || isPiMcp(change)) continue;
|
|
253
|
+
if (change.projection?.renderer === 'pi-guard-extension') continue;
|
|
229
254
|
if (change.content === null) continue;
|
|
230
255
|
writeChange(change, fsImpl);
|
|
231
256
|
removed += 1;
|
|
232
257
|
}
|
|
258
|
+
removed += removePiHooks(changes, { fsImpl });
|
|
233
259
|
removed += removeCopyTreeAssets(changes, { fsImpl });
|
|
234
260
|
return { removed };
|
|
235
261
|
}
|
|
@@ -263,10 +289,15 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
263
289
|
resources.push(...piMcp.resources);
|
|
264
290
|
conflicts.push(...piMcp.conflicts);
|
|
265
291
|
|
|
292
|
+
const piHooks = verifyPiHooks({ scope, scopeRoot, assets, ledger, context, adopt: context?.adopt === true, env, fsImpl });
|
|
293
|
+
statuses.push(...piHooks.statuses);
|
|
294
|
+
resources.push(...piHooks.resources);
|
|
295
|
+
conflicts.push(...piHooks.conflicts);
|
|
296
|
+
|
|
266
297
|
return { ok: conflicts.length === 0 && !statuses.some((s) => s.status === 'invalid' || s.status === 'conflict'), resources, statuses, conflicts };
|
|
267
298
|
}
|
|
268
299
|
|
|
269
|
-
return { nativePaths, discover, render, plan, apply, remove, verify };
|
|
300
|
+
return { nativePaths, discover, render, plan, apply, remove, verify, planHookDoors, verifyHookDoors };
|
|
270
301
|
}
|
|
271
302
|
|
|
272
303
|
const singleton = createPiAdapter();
|
|
@@ -274,6 +305,7 @@ const singleton = createPiAdapter();
|
|
|
274
305
|
module.exports = {
|
|
275
306
|
HARNESS, nativePaths: singleton.nativePaths, discover: singleton.discover, render: singleton.render,
|
|
276
307
|
plan: singleton.plan, apply: singleton.apply, remove: singleton.remove, verify: singleton.verify,
|
|
308
|
+
planHookDoors: singleton.planHookDoors, verifyHookDoors: singleton.verifyHookDoors,
|
|
277
309
|
managedInstruction: (singleton ).managedInstruction, strippedInstruction: (singleton ).strippedInstruction,
|
|
278
310
|
createPiAdapter,
|
|
279
311
|
};
|
|
@@ -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 {
|
|
@@ -55,7 +55,7 @@ function cmdInstall(o ) {
|
|
|
55
55
|
// One lifecycle view across every requested target — computed unconditionally (not only under
|
|
56
56
|
// --dry-run) so its safety gate and its plan are the exact same object the real apply below uses.
|
|
57
57
|
const lifecycleView = registryLifecycleView({ registry, repoRoot: REPO_ROOT, scope, dirs, targets,
|
|
58
|
-
mcpSelections: selection.selections, mcpAdoptable: selection.adoptable, retainedMcpIds: selection.retainedMcpIds,
|
|
58
|
+
mcpSelections: selection.selections, mcpAdoptable: selection.adoptable, retainedMcpIds: selection.retainedMcpIds, operation: 'install',
|
|
59
59
|
force: o.force, adopt: o.adopt === true, permissions: o.permissions === true, statusline: o.statusline === true });
|
|
60
60
|
if (!lifecycleView.plan.safe) { assertSafeRegistryPlan(lifecycleView); return; }
|
|
61
61
|
|
|
@@ -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
|