@khoavu882/doflow 2.0.0-beta.3 → 2.0.0-beta.5
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/bin/doflow.js +2 -2
- package/core/.antigravity-plugin/plugin.json +0 -1
- 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/mcp-tool-guard.sh +7 -17
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
- package/core/harnesses/claude/hooks/session-start.sh +5 -3
- 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/codex/hooks/mcp-tool-guard.sh +7 -15
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
- 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 +17 -6
- package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +7 -18
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +7 -18
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +7 -19
- package/core/harnesses/gemini/hooks/session-start.sh +5 -3
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +20 -5
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +20 -5
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +20 -5
- package/core/harnesses/kiro/hooks/session-start.sh +11 -5
- package/core/harnesses/kiro/hooks/stop-check.sh +21 -30
- package/core/harnesses/pi/extensions/doflow-guard.js +159 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -11
- package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
- package/core/harnesses/shared/hooks/bridge/bridge.sh +184 -0
- package/core/harnesses/shared/hooks/bridge/envelope.jq +60 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +62 -0
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +4 -2
- package/core/harnesses/shared/hooks/policies/contract.sh +75 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +39 -4
- package/core/harnesses/shared/hooks/policies/lib.sh +4 -2
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +46 -34
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +75 -25
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +67 -73
- package/core/harnesses/shared/hooks/policies/session-context.sh +37 -7
- package/core/harnesses/shared/hooks/policies/stop-check.sh +42 -15
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +35 -5
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +40 -7
- package/core/harnesses/shared/hooks/stream-hook-runner.js +69 -181
- package/core/registry/assets.json +19 -0
- package/core/registry/contracts.json +2 -2
- package/core/registry/harnesses.json +196 -10
- package/core/registry/lifecycle.json +2626 -7
- package/core/registry/storage.json +110 -83
- package/core/registry/verification.json +1 -1
- package/core/shared/scripts/doflow/bash/render-puml.sh +2 -2
- package/core/shared/scripts/doflow/bin/doflow-run +10 -6
- package/core/shared/skills/do-design/SKILL.md +6 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +11 -10
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +2 -0
- package/core/shared/skills/do-execute-plan/references/scaffold.md +2 -2
- package/core/shared/skills/do-implement/SKILL.md +3 -2
- package/core/shared/skills/do-plan/SKILL.md +7 -8
- package/package.json +4 -2
- package/src/adapters/antigravity/{index.ts → index.js} +141 -105
- package/src/adapters/claude/{index.ts → index.js} +63 -49
- package/src/adapters/claude/{mcp.ts → mcp.js} +16 -16
- package/src/adapters/codex/{agents.ts → agents.js} +24 -24
- package/src/adapters/codex/{config.ts → config.js} +49 -49
- package/src/adapters/codex/{hooks.ts → hooks.js} +62 -27
- package/src/adapters/codex/{index.ts → index.js} +148 -105
- package/src/adapters/codex/{mcp.ts → mcp.js} +38 -38
- package/src/adapters/codex/{native-projection.ts → native-projection.js} +5 -5
- package/src/adapters/copilot/{index.ts → index.js} +42 -42
- package/src/adapters/{copy-tree.ts → copy-tree.js} +74 -74
- package/src/adapters/gemini/{hooks.ts → hooks.js} +78 -37
- package/src/adapters/gemini/{index.ts → index.js} +96 -57
- package/src/adapters/{hook-commands.ts → hook-commands.js} +8 -8
- package/src/adapters/{index.ts → index.js} +13 -13
- package/src/adapters/kiro/{index.ts → index.js} +49 -35
- package/src/adapters/{mcp-entries.ts → mcp-entries.js} +55 -55
- package/src/adapters/opencode/{index.ts → index.js} +52 -52
- package/src/adapters/pi/hooks.js +119 -0
- package/src/adapters/pi/{index.ts → index.js} +64 -39
- package/src/adapters/pi/{json-members.ts → json-members.js} +23 -23
- package/src/adapters/pi/{mcp.ts → mcp.js} +70 -70
- package/src/adapters/{resolve-source.ts → resolve-source.js} +1 -1
- package/src/cli/commands/{install.ts → install.js} +25 -25
- package/src/cli/commands/{list-backups.ts → list-backups.js} +4 -4
- package/src/cli/commands/{reconcile.ts → reconcile.js} +19 -19
- package/src/cli/commands/{remove.ts → remove.js} +10 -10
- package/src/cli/commands/{rollback.ts → rollback.js} +17 -17
- package/src/cli/commands/{self-update.ts → self-update.js} +3 -3
- package/src/cli/commands/{status.ts → status.js} +24 -24
- package/src/cli/commands/{tools.ts → tools.js} +16 -16
- package/src/cli/commands/{update.ts → update.js} +20 -20
- package/src/cli/{index.ts → index.js} +43 -31
- package/src/cli/{runtime-commands.ts → runtime-commands.js} +97 -58
- package/src/cli/{shared.ts → shared.js} +40 -40
- package/src/helper/{bash-helper.ts → bash-helper.js} +7 -7
- package/src/helper/{git.ts → git.js} +2 -2
- package/src/helper/{guardrails.ts → guardrails.js} +10 -10
- package/src/helper/{harness-paths.ts → harness-paths.js} +24 -24
- package/src/helper/{marker-merge.ts → marker-merge.js} +4 -4
- package/src/helper/{prompt.ts → prompt.js} +5 -5
- package/src/helper/{repo-root.ts → repo-root.js} +2 -2
- package/src/helper/{settings-merge.ts → settings-merge.js} +27 -27
- package/src/helper/{settings-scope.ts → settings-scope.js} +3 -3
- package/src/helper/{toml.ts → toml.js} +48 -48
- package/src/install/{backup-set.ts → backup-set.js} +16 -16
- package/src/install/{backup.ts → backup.js} +65 -65
- package/src/install/{context.ts → context.js} +15 -15
- package/src/install/{manifest.ts → manifest.js} +27 -27
- package/src/install/{mcp.ts → mcp.js} +30 -30
- package/src/install/{paths.ts → paths.js} +15 -15
- package/src/install/{targets.ts → targets.js} +10 -10
- package/src/install/{tool-lifecycle.ts → tool-lifecycle.js} +20 -20
- package/src/lifecycle/{bash-availability.ts → bash-availability.js} +1 -1
- package/src/lifecycle/coverage.js +57 -0
- package/src/lifecycle/hook-step.js +61 -0
- package/src/lifecycle/{index.ts → index.js} +160 -146
- package/src/lifecycle/{mcp-index.ts → mcp-index.js} +1 -1
- package/src/lifecycle/{policies.ts → policies.js} +11 -11
- package/src/lifecycle/{view.ts → view.js} +28 -28
- package/src/registry/{index.ts → index.js} +195 -66
- package/src/release/build-package.js +91 -0
- package/src/release/changelog-section.js +70 -0
- package/src/release/{sync-plugin-versions.ts → sync-plugin-versions.js} +4 -4
- package/src/runtime/advice/{cli.ts → cli.js} +31 -31
- package/src/runtime/advice/{client.ts → client.js} +44 -44
- package/src/runtime/advice/{config.ts → config.js} +16 -16
- package/src/runtime/advice/{doctor.ts → doctor.js} +32 -32
- package/src/runtime/advice/{endpoint-guard.ts → endpoint-guard.js} +20 -20
- package/src/runtime/advice/{kinds.ts → kinds.js} +16 -16
- package/src/runtime/advice/providers/{ollama-systemone.ts → ollama-systemone.js} +21 -21
- package/src/runtime/advice/{record.ts → record.js} +19 -19
- package/src/runtime/advice/{registry.ts → registry.js} +16 -16
- package/src/runtime/advice/{strength.ts → strength.js} +7 -7
- package/src/runtime/{brief-tools.ts → brief-tools.js} +50 -50
- package/src/runtime/{c4-project.ts → c4-project.js} +27 -27
- package/src/runtime/candidate.js +505 -0
- package/src/runtime/{capability-router.ts → capability-router.js} +39 -39
- package/src/runtime/{checkouts.ts → checkouts.js} +21 -21
- package/src/runtime/{claims.ts → claims.js} +44 -35
- package/src/runtime/{cli-result.ts → cli-result.js} +15 -8
- package/src/runtime/{cli.ts → cli.js} +59 -292
- package/src/runtime/{command-detect.ts → command-detect.js} +47 -47
- package/src/runtime/context/budget.js +203 -0
- package/src/runtime/context/compose.js +1367 -0
- package/src/runtime/context/delivery-store.js +102 -0
- package/src/runtime/context/mandatory.js +594 -0
- package/src/runtime/context/pointer.js +142 -0
- package/src/runtime/context-pack.js +1277 -0
- package/src/runtime/{decision-register.ts → decision-register.js} +67 -67
- package/src/runtime/evidence-fields.js +39 -0
- package/src/runtime/{evidence-ledger.ts → evidence-ledger.js} +32 -25
- package/src/runtime/failure/{capture.ts → capture.js} +29 -29
- package/src/runtime/failure/{classifier.ts → classifier.js} +4 -4
- package/src/runtime/failure/{cli.ts → cli.js} +30 -30
- package/src/runtime/failure/{home.ts → home.js} +6 -6
- package/src/runtime/failure/{store.ts → store.js} +32 -32
- package/src/runtime/{feature-resolve.ts → feature-resolve.js} +6 -6
- package/src/runtime/{freshness.ts → freshness.js} +59 -22
- package/src/runtime/{health.ts → health.js} +72 -72
- package/src/runtime/{history-compactor.ts → history-compactor.js} +25 -25
- package/src/runtime/{implementation-gate.ts → implementation-gate.js} +30 -30
- package/src/runtime/inventory/{coverage.ts → coverage.js} +10 -10
- package/src/runtime/inventory/{identity.ts → identity.js} +2 -2
- package/src/runtime/inventory/{index.ts → index.js} +36 -36
- package/src/runtime/inventory/{precedence.ts → precedence.js} +2 -2
- package/src/runtime/inventory/{read-scopes.ts → read-scopes.js} +9 -9
- package/src/runtime/inventory/{siblings.ts → siblings.js} +15 -15
- package/src/runtime/knowledge/{chunker.ts → chunker.js} +12 -12
- package/src/runtime/knowledge/{hybrid.ts → hybrid.js} +17 -17
- package/src/runtime/knowledge/{index-store.ts → index-store.js} +23 -23
- package/src/runtime/knowledge/{retrieval.ts → retrieval.js} +27 -27
- package/src/runtime/{leak-scan.ts → leak-scan.js} +12 -12
- package/src/runtime/lifecycle/{cli.ts → cli.js} +32 -32
- package/src/runtime/lifecycle/{event-store.ts → event-store.js} +48 -48
- package/src/runtime/lifecycle/{fold.ts → fold.js} +41 -41
- package/src/runtime/lifecycle/{followup.ts → followup.js} +54 -54
- package/src/runtime/lifecycle/{goal.ts → goal.js} +50 -50
- package/src/runtime/lifecycle/{intent-writer.ts → intent-writer.js} +5 -5
- package/src/runtime/lifecycle/{overview.ts → overview.js} +47 -47
- package/src/runtime/lifecycle/{release.ts → release.js} +29 -29
- package/src/runtime/lifecycle/{report-store.ts → report-store.js} +25 -25
- package/src/runtime/lifecycle/{retention.ts → retention.js} +37 -37
- package/src/runtime/lifecycle/{root.ts → root.js} +9 -9
- package/src/runtime/lifecycle/{status.ts → status.js} +64 -64
- package/src/runtime/lifecycle/{store-upkeep.ts → store-upkeep.js} +46 -46
- package/src/runtime/{locator-resolve.ts → locator-resolve.js} +7 -7
- package/src/runtime/{mask.ts → mask.js} +22 -22
- package/src/runtime/{mcp-consultations.ts → mcp-consultations.js} +54 -54
- package/src/runtime/{mcp-reach.ts → mcp-reach.js} +24 -24
- package/src/runtime/{model-router.ts → model-router.js} +20 -20
- package/src/runtime/{outcome.ts → outcome.js} +27 -27
- package/src/runtime/{reach.ts → reach.js} +21 -21
- package/src/runtime/{readiness-record.ts → readiness-record.js} +12 -12
- package/src/runtime/{readiness.ts → readiness.js} +113 -43
- package/src/runtime/{recovery.ts → recovery.js} +15 -15
- package/src/runtime/{research-request.ts → research-request.js} +34 -34
- package/src/runtime/{retrieval-plan.ts → retrieval-plan.js} +453 -104
- package/src/runtime/retrieval-record.js +223 -0
- package/src/runtime/scaffold/{artifacts.ts → artifacts.js} +22 -19
- package/src/runtime/scaffold/{fingerprint.ts → fingerprint.js} +3 -3
- package/src/runtime/scaffold/{generate.ts → generate.js} +109 -109
- package/src/runtime/scaffold/{languages.ts → languages.js} +34 -34
- package/src/runtime/source-version.js +289 -0
- package/src/runtime/storage/{cli.ts → cli.js} +9 -9
- package/src/runtime/storage/{engine.ts → engine.js} +37 -37
- package/src/runtime/storage/{registry.ts → registry.js} +19 -19
- package/src/runtime/storage/{roots.ts → roots.js} +8 -8
- package/src/runtime/storage/{rules.ts → rules.js} +26 -26
- package/src/runtime/storage/{upkeep.ts → upkeep.js} +35 -35
- package/src/runtime/storage/{work-status.ts → work-status.js} +39 -39
- package/src/runtime/{task-attempts.ts → task-attempts.js} +62 -62
- package/src/runtime/{task-classifier.ts → task-classifier.js} +34 -34
- package/src/runtime/{task-scope.ts → task-scope.js} +40 -15
- package/src/runtime/{task-state.ts → task-state.js} +86 -21
- package/src/runtime/trace/{indicators.ts → indicators.js} +22 -22
- package/src/runtime/trace/{ledger.ts → ledger.js} +42 -42
- package/src/runtime/trace/{render.ts → render.js} +7 -7
- package/src/runtime/trace/{views.ts → views.js} +40 -40
- package/src/runtime/verification/{contract-runner.ts → contract-runner.js} +23 -23
- package/src/runtime/verification/{engine.ts → engine.js} +143 -143
- package/src/runtime/verification/{registry.ts → registry.js} +6 -6
- package/src/runtime/verification/{scope-bound.ts → scope-bound.js} +50 -50
- package/src/runtime/verification/{test-selection.ts → test-selection.js} +28 -28
- package/src/runtime/{workflow-engine.ts → workflow-engine.js} +50 -50
- package/src/runtime/{workflow-orchestrator.ts → workflow-orchestrator.js} +91 -91
- package/src/runtime/{worktree.ts → worktree.js} +31 -31
- package/src/state/{index.ts → index.js} +26 -26
- package/src/state/{lockfile.ts → lockfile.js} +20 -20
- package/src/state/{run-lock.ts → run-lock.js} +44 -44
- package/core/.antigravity-plugin/hooks.json +0 -39
- 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/src/runtime/context-pack.ts +0 -492
|
@@ -23,52 +23,54 @@ const os = require('node:os');
|
|
|
23
23
|
const path = require('node:path');
|
|
24
24
|
const {
|
|
25
25
|
handleCapabilitiesCommand, handleReadinessCommand, handleEvidenceCommand,
|
|
26
|
-
} = require('../runtime/cli.
|
|
27
|
-
|
|
26
|
+
} = require('../runtime/cli.js');
|
|
27
|
+
const { loadRegistry } = require('../registry/index.js');
|
|
28
|
+
const { enforcementFor, UnknownEnforcementTarget } = require('../lifecycle/coverage.js');
|
|
29
|
+
// `doctor` comes from the health module rather than src/runtime/cli.js: the health-probe report
|
|
28
30
|
// (FR-013) supersedes the presence-check version, and one verb must have one implementation.
|
|
29
|
-
const { handleDoctorCommand } = require('../runtime/health.
|
|
30
|
-
const { handleTraceCommand, handleStatsCommand, handleDiscoverCommand, resolveRunsLocation } = require('../runtime/trace/ledger.
|
|
31
|
-
const { handleIndicatorsCommand } = require('../runtime/trace/indicators.
|
|
31
|
+
const { handleDoctorCommand } = require('../runtime/health.js');
|
|
32
|
+
const { handleTraceCommand, handleStatsCommand, handleDiscoverCommand, resolveRunsLocation } = require('../runtime/trace/ledger.js');
|
|
33
|
+
const { handleIndicatorsCommand } = require('../runtime/trace/indicators.js');
|
|
32
34
|
// The rest of the verb surface design §4.2 declares. `classify`, `workflow`, `route`, `claim`,
|
|
33
35
|
// `context-pack`, `verify`, `recover` and `scaffold` each have exactly one implementation, in the
|
|
34
36
|
// engine module named on the right; this file only dispatches to them.
|
|
35
|
-
const { ReadinessEngine } = require('../runtime/readiness.
|
|
36
|
-
const { handleClaimCommand } = require('../runtime/claims.
|
|
37
|
-
const { handleClassifyCommand } = require('../runtime/task-classifier.
|
|
38
|
-
const { handleWorkflowCommand } = require('../runtime/workflow-engine.
|
|
39
|
-
const { handleOrchestrateCommand } = require('../runtime/workflow-orchestrator.
|
|
40
|
-
const { handleResearchRequestCommand } = require('../runtime/research-request.
|
|
41
|
-
const { handleTaskAttemptCommand } = require('../runtime/task-attempts.
|
|
42
|
-
const { handleRetrieveCommand } = require('../runtime/knowledge/retrieval.
|
|
43
|
-
const { handleModelRoleCommand } = require('../runtime/model-router.
|
|
44
|
-
const { handleRouteCommand } = require('../runtime/capability-router.
|
|
45
|
-
const { handleContextPackCommand } = require('../runtime/context-pack.
|
|
46
|
-
const { handleRetrievalPlanCommand } = require('../runtime/retrieval-plan.
|
|
47
|
-
const { handleOutcomeCommand } = require('../runtime/outcome.
|
|
48
|
-
const { handleVerifyCommand } = require('../runtime/verification/engine.
|
|
49
|
-
const { handleLeakScanCommand } = require('../runtime/leak-scan.
|
|
50
|
-
const { handleRecoverCommand } = require('../runtime/recovery.
|
|
51
|
-
const { handleScaffoldCommand } = require('../runtime/scaffold/generate.
|
|
52
|
-
const { handleDecisionCommand } = require('../runtime/decision-register.
|
|
53
|
-
const { handleFollowupCommand, handleLifecycleCommand, handleGoalCommand } = require('../runtime/lifecycle/cli.
|
|
54
|
-
const { projectRoot: lifecycleProjectRoot } = require('../runtime/lifecycle/root.
|
|
55
|
-
const { upkeepAfterVerb } = require('../runtime/storage/upkeep.
|
|
56
|
-
const { handleStorageCommand } = require('../runtime/storage/cli.
|
|
37
|
+
const { ReadinessEngine } = require('../runtime/readiness.js');
|
|
38
|
+
const { handleClaimCommand } = require('../runtime/claims.js');
|
|
39
|
+
const { handleClassifyCommand } = require('../runtime/task-classifier.js');
|
|
40
|
+
const { handleWorkflowCommand } = require('../runtime/workflow-engine.js');
|
|
41
|
+
const { handleOrchestrateCommand } = require('../runtime/workflow-orchestrator.js');
|
|
42
|
+
const { handleResearchRequestCommand } = require('../runtime/research-request.js');
|
|
43
|
+
const { handleTaskAttemptCommand } = require('../runtime/task-attempts.js');
|
|
44
|
+
const { handleRetrieveCommand } = require('../runtime/knowledge/retrieval.js');
|
|
45
|
+
const { handleModelRoleCommand } = require('../runtime/model-router.js');
|
|
46
|
+
const { handleRouteCommand } = require('../runtime/capability-router.js');
|
|
47
|
+
const { handleContextPackCommand } = require('../runtime/context-pack.js');
|
|
48
|
+
const { handleRetrievalPlanCommand } = require('../runtime/retrieval-plan.js');
|
|
49
|
+
const { handleOutcomeCommand } = require('../runtime/outcome.js');
|
|
50
|
+
const { handleVerifyCommand } = require('../runtime/verification/engine.js');
|
|
51
|
+
const { handleLeakScanCommand } = require('../runtime/leak-scan.js');
|
|
52
|
+
const { handleRecoverCommand } = require('../runtime/recovery.js');
|
|
53
|
+
const { handleScaffoldCommand } = require('../runtime/scaffold/generate.js');
|
|
54
|
+
const { handleDecisionCommand } = require('../runtime/decision-register.js');
|
|
55
|
+
const { handleFollowupCommand, handleLifecycleCommand, handleGoalCommand } = require('../runtime/lifecycle/cli.js');
|
|
56
|
+
const { projectRoot: lifecycleProjectRoot } = require('../runtime/lifecycle/root.js');
|
|
57
|
+
const { upkeepAfterVerb } = require('../runtime/storage/upkeep.js');
|
|
58
|
+
const { handleStorageCommand } = require('../runtime/storage/cli.js');
|
|
57
59
|
// Required on first use, so a CLI whose failure modules cannot be loaded still runs every other verb.
|
|
58
|
-
function handleFailureCommand(options
|
|
59
|
-
const { handleInventoryCommand } = require('../runtime/inventory/index.
|
|
60
|
-
const { finishRuntime, usageError } = require('../runtime/cli-result.
|
|
61
|
-
const { setDefaultSlug, invalidSlugRefusal, slugNamesNoFeature } = require('../runtime/task-scope.
|
|
62
|
-
const { listCheckouts } = require('../runtime/checkouts.
|
|
63
|
-
const { featureSlugFor } = require('../runtime/implementation-gate.
|
|
64
|
-
const { handleBriefToolsCommand } = require('../runtime/brief-tools.
|
|
65
|
-
const { handleAdviseCommand } = require('../runtime/advice/cli.
|
|
66
|
-
const { REPO_ROOT, buildAdapterRegistry } = require('./shared.
|
|
60
|
+
function handleFailureCommand(options ) { return require('../runtime/failure/cli.js').handleFailureCommand(options); }
|
|
61
|
+
const { handleInventoryCommand } = require('../runtime/inventory/index.js');
|
|
62
|
+
const { finishRuntime, usageError } = require('../runtime/cli-result.js');
|
|
63
|
+
const { setDefaultSlug, invalidSlugRefusal, slugNamesNoFeature, resolveFeature } = require('../runtime/task-scope.js');
|
|
64
|
+
const { listCheckouts } = require('../runtime/checkouts.js');
|
|
65
|
+
const { featureSlugFor } = require('../runtime/implementation-gate.js');
|
|
66
|
+
const { handleBriefToolsCommand } = require('../runtime/brief-tools.js');
|
|
67
|
+
const { handleAdviseCommand } = require('../runtime/advice/cli.js');
|
|
68
|
+
const { REPO_ROOT, buildAdapterRegistry } = require('./shared.js');
|
|
67
69
|
|
|
68
70
|
/** Where `readiness`/`evidence` read and write per-task state. Mirrors scopeOf()'s rules so these
|
|
69
71
|
* commands are scope-aware like the rest of the CLI, rather than defaulting to the DoFlow install
|
|
70
72
|
* directory — which for an npm install is inside node_modules/. */
|
|
71
|
-
function evidenceRoot(o
|
|
73
|
+
function evidenceRoot(o ) {
|
|
72
74
|
return o.global ? os.homedir() : path.resolve(o.positional[0] || '.');
|
|
73
75
|
}
|
|
74
76
|
|
|
@@ -81,7 +83,7 @@ function evidenceRoot(o: any): string {
|
|
|
81
83
|
* The valid set comes from the readiness registry rather than a list written here, so it cannot
|
|
82
84
|
* drift from the templates that actually exist.
|
|
83
85
|
*/
|
|
84
|
-
function requireTaskClass(o
|
|
86
|
+
function requireTaskClass(o ) {
|
|
85
87
|
if (typeof o.taskClass === 'string' && o.taskClass.trim() !== '') return o.taskClass;
|
|
86
88
|
let valid = '';
|
|
87
89
|
try {
|
|
@@ -92,7 +94,7 @@ function requireTaskClass(o: any): string {
|
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
/** The task id the caller named, or exit 2. Same reasoning as requireTaskClass. */
|
|
95
|
-
function requireTaskId(o
|
|
97
|
+
function requireTaskId(o ) {
|
|
96
98
|
if (typeof o.taskId === 'string' && o.taskId.trim() !== '') return o.taskId;
|
|
97
99
|
usageError(o.cmd, '--task-id is required — evidence and claims belong to one task, so guessing an id would report on a different one.', o.json);
|
|
98
100
|
process.exit(2);
|
|
@@ -104,13 +106,16 @@ function requireTaskId(o: any): string {
|
|
|
104
106
|
* Absent flags stay absent. Filling a gap here with 'unknown'/'general' would move the omission out
|
|
105
107
|
* of the caller's error message and into the stored record, where it reads as a measurement.
|
|
106
108
|
*/
|
|
107
|
-
function evidenceItemFromFlags(o
|
|
108
|
-
const item
|
|
109
|
+
function evidenceItemFromFlags(o ) {
|
|
110
|
+
const item = {};
|
|
109
111
|
if (o.kind !== undefined) item.kind = o.kind;
|
|
110
112
|
if (o.provenance !== undefined) item.provenance = o.provenance;
|
|
111
113
|
if (o.locator !== undefined) item.locator = o.locator;
|
|
112
114
|
if (o.content !== undefined) item.content = o.content;
|
|
113
115
|
if (o.establishes !== undefined) item.establishes = o.establishes;
|
|
116
|
+
if (o.need !== undefined) item.need = o.need;
|
|
117
|
+
if (o.result !== undefined) item.result = o.result;
|
|
118
|
+
if (o.absence !== undefined) item.absence = o.absence;
|
|
114
119
|
// Both halves of an observation travel together; a lone half must reach the write boundary as
|
|
115
120
|
// the partial object it is, so the refusal names the missing field instead of dropping the pair.
|
|
116
121
|
if (o.observedCommand !== undefined || o.observedExit !== undefined) {
|
|
@@ -118,7 +123,7 @@ function evidenceItemFromFlags(o: any): any {
|
|
|
118
123
|
if (o.observedCommand !== undefined) item.observation.command = o.observedCommand;
|
|
119
124
|
if (o.observedExit !== undefined) item.observation.exitCode = o.observedExit;
|
|
120
125
|
}
|
|
121
|
-
const source
|
|
126
|
+
const source = {};
|
|
122
127
|
if (o.provider !== undefined) source.provider = o.provider;
|
|
123
128
|
if (o.capability !== undefined) source.capability = o.capability;
|
|
124
129
|
if (Object.keys(source).length > 0) item.source = source;
|
|
@@ -128,9 +133,9 @@ function evidenceItemFromFlags(o: any): any {
|
|
|
128
133
|
/** Verbs that read or write per-task records, and so accept `--slug` (IC-002). */
|
|
129
134
|
const TASK_STORE_VERBS = new Set(['evidence', 'claim', 'readiness', 'context-pack', 'research-request', 'outcome', 'retrieval-plan']);
|
|
130
135
|
|
|
131
|
-
/** Verbs
|
|
132
|
-
*
|
|
133
|
-
const FEATURE_STORE_VERBS = new Set(['evidence', 'claim', 'readiness']);
|
|
136
|
+
/** Verbs whose task records live beside the feature folder when run from a linked worktree that
|
|
137
|
+
* does not contain it. */
|
|
138
|
+
const FEATURE_STORE_VERBS = new Set(['evidence', 'claim', 'readiness', 'retrieval-plan']);
|
|
134
139
|
|
|
135
140
|
/**
|
|
136
141
|
* The main checkout, when `o` runs one of FEATURE_STORE_VERBS in a linked worktree that lacks the
|
|
@@ -139,12 +144,17 @@ const FEATURE_STORE_VERBS = new Set(['evidence', 'claim', 'readiness']);
|
|
|
139
144
|
* the readiness record written there is the one verify, orchestrate and the edit hook read from
|
|
140
145
|
* the worktree.
|
|
141
146
|
*/
|
|
142
|
-
function featureStoreRoot(o
|
|
147
|
+
function featureStoreRoot(o , checkouts ) {
|
|
143
148
|
if (!FEATURE_STORE_VERBS.has(o.cmd) || o.global) return null;
|
|
144
|
-
const checkouts = listCheckouts({ cwd: evidenceRoot(o) });
|
|
145
149
|
if (!checkouts.isLinked) return null;
|
|
146
|
-
|
|
147
|
-
|
|
150
|
+
let slug = o.slug || null;
|
|
151
|
+
if (!slug) {
|
|
152
|
+
try {
|
|
153
|
+
const feature = resolveFeature({ projectRoot: checkouts.current });
|
|
154
|
+
slug = !feature.error && feature.paths ? feature.paths.feature_slug : null;
|
|
155
|
+
} catch { /* a failed branch lookup cannot route state to a guessed feature */ }
|
|
156
|
+
}
|
|
157
|
+
const folder = (root ) => path.join(root, 'agent-docs', 'doflow', slug || '');
|
|
148
158
|
if (!slug || fs.existsSync(folder(checkouts.current)) || !fs.existsSync(folder(checkouts.main))) return null;
|
|
149
159
|
return { root: checkouts.main, slug };
|
|
150
160
|
}
|
|
@@ -155,7 +165,7 @@ function featureStoreRoot(o: any): { root: string; slug: string } | null {
|
|
|
155
165
|
* validate parsed CLI arguments, which is the CLI's job, not a library's); everything else belongs
|
|
156
166
|
* to the engine module each case names.
|
|
157
167
|
*/
|
|
158
|
-
function dispatchRuntimeCommandInner(o
|
|
168
|
+
function dispatchRuntimeCommandInner(o , moment ) {
|
|
159
169
|
// A slug becomes a directory name and a state key, so one that could name a path is refused
|
|
160
170
|
// here, once, for every verb that reads `--slug`, before any of them reads or writes state
|
|
161
171
|
// (FR-013, IC-009). A well-formed slug that names no feature is the verbs' own business.
|
|
@@ -169,8 +179,11 @@ function dispatchRuntimeCommandInner(o: any, moment: any): any {
|
|
|
169
179
|
// here so the verbs whose handlers build their own ledger (readiness, evidence) route the same
|
|
170
180
|
// way as the ones that take `slug` directly. The orchestration journal is keyed by slug already.
|
|
171
181
|
// Reset on every dispatch, so one invocation's slug can never carry into the next in-process call.
|
|
172
|
-
const
|
|
182
|
+
const checkouts = FEATURE_STORE_VERBS.has(o.cmd) && !o.global
|
|
183
|
+
? listCheckouts({ cwd: evidenceRoot(o) }) : null;
|
|
184
|
+
const featureStore = featureStoreRoot(o, checkouts);
|
|
173
185
|
const storeRoot = featureStore ? featureStore.root : evidenceRoot(o);
|
|
186
|
+
const attemptRoot = checkouts?.ok && !checkouts.sandbox ? (checkouts.main || checkouts.current || null) : null;
|
|
174
187
|
// Use the same root resolver as the handler: task redirection, the nearest run ledger,
|
|
175
188
|
// and lifecycle's shared main-worktree store are deliberately different scopes.
|
|
176
189
|
try {
|
|
@@ -197,6 +210,7 @@ function dispatchRuntimeCommandInner(o: any, moment: any): any {
|
|
|
197
210
|
// REPO_ROOT locates the capability registry; projectRoot is the tree whose index freshness
|
|
198
211
|
// and build/test commands are being reported on, which follows the usual scope rules.
|
|
199
212
|
case 'capabilities': return handleCapabilitiesCommand({ json: o.json, check: o.check, repoRoot: REPO_ROOT });
|
|
213
|
+
case 'enforcement': return handleEnforcementCommand(o);
|
|
200
214
|
case 'doctor': return handleDoctorCommand({ json: o.json, repoRoot: REPO_ROOT, projectRoot: evidenceRoot(o), adapters: buildAdapterRegistry() });
|
|
201
215
|
// REPO_ROOT locates the registry (templates ship with the package); stateRoot locates the
|
|
202
216
|
// caller's evidence, which follows the same scope rules as every other command: -g means
|
|
@@ -211,8 +225,8 @@ function dispatchRuntimeCommandInner(o: any, moment: any): any {
|
|
|
211
225
|
// than from evidence, so the gate had one reachable answer for every task. Forwarding is
|
|
212
226
|
// all this does: the handler names them back under `callerAsserted` so a stated input is
|
|
213
227
|
// never mistaken for a measured one.
|
|
214
|
-
case 'readiness': return handleReadinessCommand({ taskClass: requireTaskClass(o), taskId: requireTaskId(o), verificationPlan: o.verificationPlan, scopeClear: o.scope, invariants: o.invariants, userDecisionPending: o.userDecisionPending, mode: o.mode, slug: storeSlug, json: o.json, repoRoot: REPO_ROOT, stateRoot: storeRoot });
|
|
215
|
-
case 'evidence': return handleEvidenceCommand({ taskId: requireTaskId(o), action: o.action, item: evidenceItemFromFlags(o), batchPath: o.batchPath, evidenceId: o.evidenceId, replacedBy: o.replacedBy, json: o.json, repoRoot: REPO_ROOT, stateRoot: storeRoot });
|
|
228
|
+
case 'readiness': return handleReadinessCommand({ taskClass: requireTaskClass(o), taskId: requireTaskId(o), verificationPlan: o.verificationPlan, scopeClear: o.scope, invariants: o.invariants, userDecisionPending: o.userDecisionPending, mode: o.mode, slug: storeSlug, json: o.json, repoRoot: REPO_ROOT, stateRoot: storeRoot, sourceRoot: evidenceRoot(o) });
|
|
229
|
+
case 'evidence': return handleEvidenceCommand({ taskId: requireTaskId(o), action: o.action, item: evidenceItemFromFlags(o), batchPath: o.batchPath, evidenceId: o.evidenceId, replacedBy: o.replacedBy, json: o.json, repoRoot: REPO_ROOT, stateRoot: storeRoot, slug: storeSlug, sourceRoot: evidenceRoot(o), attemptRoot });
|
|
216
230
|
// Run-ledger views. They resolve their own ledger the way the dispatcher does (nearest
|
|
217
231
|
// `.doflow` walking up, or the global one) rather than assuming cwd is the project root, so
|
|
218
232
|
// a view invoked from a subdirectory reads the runs that were actually recorded.
|
|
@@ -253,15 +267,15 @@ function dispatchRuntimeCommandInner(o: any, moment: any): any {
|
|
|
253
267
|
case 'research-request': return handleResearchRequestCommand({ slug: o.slug, action: o.action === 'status' ? 'list' : o.action, taskId: requireTaskId(o), stageId: o.stageId, question: o.question, reason: o.reason, blocking: o.blocking === undefined ? undefined : o.blocking === 'true' ? true : o.blocking === 'false' ? false : o.blocking, requestId: o.requestId, outcome: o.researchOutcome, claimId: o.claimId, evidenceIds: o.evidenceIds, gap: o.gap, json: o.json, projectRoot: evidenceRoot(o) });
|
|
254
268
|
// The store is shared by every linked worktree of the clone (lifecycle's main-worktree root); the
|
|
255
269
|
// feature is `--slug`, else the branch's, resolved the way decision and scaffold resolve it.
|
|
256
|
-
case 'task-attempt': return handleTaskAttemptCommand({ action: o.actionGiven ? o.action : undefined, slug: o.slug, task: o.task, owner: o.owner, mode: o.mode, token: o.token, result: o.result, evidenceId: o.evidenceId, reason: o.reason, json: o.json, cwd: evidenceRoot(o), resolveSlug: (cwd
|
|
270
|
+
case 'task-attempt': return handleTaskAttemptCommand({ action: o.actionGiven ? o.action : undefined, slug: o.slug, task: o.task, owner: o.owner, mode: o.mode, token: o.token, result: o.result, evidenceId: o.evidenceId, reason: o.reason, json: o.json, cwd: evidenceRoot(o), resolveSlug: (cwd ) => featureSlugFor({ projectRoot: cwd }) });
|
|
257
271
|
case 'retrieve': return handleRetrieveCommand({ query: o.query, top: o.top, json: o.json });
|
|
258
272
|
case 'brief-tools': return handleBriefToolsCommand({ owner: o.owner, tier: o.tier, harness: o.harness, slug: o.slug, headingLevel: o.headingLevel, tasks: o.tasks, json: o.json, repoRoot: REPO_ROOT, projectRoot: storeRoot, buildAdapters: buildAdapterRegistry });
|
|
259
273
|
case 'advise': return handleAdviseCommand({ action: o.action, actionGiven: o.actionGiven, kind: o.kind, server: o.server, state: o.state, stateFile: o.stateFile, staticAnswer: o.staticAnswer, questionsFile: o.questionsFile, taskId: o.taskId, pickId: o.pickId, actual: o.actual, model: o.model, endpoint: o.endpoint, off: o.off, project: o.project, force: o.force, global: o.global, json: o.json, projectRoot: evidenceRoot(o) });
|
|
260
274
|
case 'model-role': return handleModelRoleCommand({ role: o.role, exclude: o.exclude, json: o.json, repoRoot: REPO_ROOT });
|
|
261
275
|
case 'route': return handleRouteCommand({ intent: o.intent, query: o.query, check: o.check, json: o.json, projectRoot: evidenceRoot(o) });
|
|
262
276
|
case 'claim': return handleClaimCommand({ slug: storeSlug, taskId: requireTaskId(o), action: o.action, statement: o.statement, claimId: o.claimId, evidenceId: o.evidenceId, replacedBy: o.replacedBy, relation: o.relation, role: o.role, json: o.json, stateRoot: storeRoot });
|
|
263
|
-
case 'context-pack': return handleContextPackCommand({ taskId: requireTaskId(o), taskClass: o.taskClass, objective: o.objective, json: o.json, stateRoot: evidenceRoot(o), slug: o.slug });
|
|
264
|
-
case 'retrieval-plan': return handleRetrievalPlanCommand({ slug:
|
|
277
|
+
case 'context-pack': return handleContextPackCommand({ taskId: requireTaskId(o), taskClass: o.taskClass, objective: o.objective, json: o.json, stateRoot: evidenceRoot(o), slug: o.slug, mandatoryFile: o.mandatoryFile, harness: o.harness, maxBytes: o.maxBytes, bare: o.bare, noRecord: o.noRecord, reason: o.reason, since: o.since, expand: o.expand });
|
|
278
|
+
case 'retrieval-plan': return handleRetrievalPlanCommand({ slug: storeSlug, taskId: requireTaskId(o), action: o.action, need: o.need, stage: o.stage, provider: o.provider, attemptStatus: o.attemptStatus, reason: o.reason, note: o.note, json: o.json, repoRoot: REPO_ROOT, stateRoot: storeRoot, sourceRoot: evidenceRoot(o) });
|
|
265
279
|
case 'outcome': return handleOutcomeCommand({ slug: o.slug, taskId: requireTaskId(o), action: o.action, state: o.state, taskClass: o.taskClass, stage: o.stage, readiness: o.readiness, verification: o.verification, json: o.json, repoRoot: REPO_ROOT, stateRoot: evidenceRoot(o) });
|
|
266
280
|
case 'verify': return handleVerifyCommand({ slug: o.slug, taskId: requireTaskId(o), action: o.action, risk: o.risk, planPath: o.planPath, scope: o.scope, verificationInput: o.verificationInput, verificationPhase: o.verificationPhase, baselineRef: o.baselineRef, json: o.json, projectRoot: evidenceRoot(o) });
|
|
267
281
|
case 'leak-scan': return handleLeakScanCommand({ paths: o.paths, exclude: o.exclude, json: o.json, repoRoot: evidenceRoot(o) });
|
|
@@ -271,11 +285,36 @@ function dispatchRuntimeCommandInner(o: any, moment: any): any {
|
|
|
271
285
|
}
|
|
272
286
|
|
|
273
287
|
/** Await the verb without changing its return value, output or exit code; usage exits never return. */
|
|
274
|
-
async function dispatchRuntimeCommand(o
|
|
275
|
-
const moment
|
|
288
|
+
async function dispatchRuntimeCommand(o ) {
|
|
289
|
+
const moment = {};
|
|
276
290
|
const result = await dispatchRuntimeCommandInner(o, moment);
|
|
277
291
|
if (o.cmd !== 'storage') upkeepAfterVerb({ projectRoot: moment.projectRoot });
|
|
278
292
|
return result;
|
|
279
293
|
}
|
|
280
294
|
|
|
295
|
+
function handleEnforcementCommand(o ) {
|
|
296
|
+
const fail = (error , detail ) => {
|
|
297
|
+
if (o.json) console.log(JSON.stringify({ error, detail }));
|
|
298
|
+
else console.error(`doflow enforcement: ${detail}`);
|
|
299
|
+
process.exitCode = 2;
|
|
300
|
+
return 2;
|
|
301
|
+
};
|
|
302
|
+
if (typeof o.harness !== 'string' || !o.harness || typeof o.policy !== 'string' || !o.policy || (o.profile !== undefined && typeof o.profile !== 'string')) return fail('usage', 'requires --harness <id> --policy <id> [--profile <id>]');
|
|
303
|
+
try {
|
|
304
|
+
const registry = loadRegistry({ repoRoot: REPO_ROOT });
|
|
305
|
+
const answer = enforcementFor(registry.coverage, { harness: o.harness, policy: o.policy, ...(o.profile ? { profile: o.profile } : {}) });
|
|
306
|
+
if (o.json) console.log(JSON.stringify(answer));
|
|
307
|
+
else {
|
|
308
|
+
console.log(`${answer.harness} ${answer.policy}: ${answer.enforced ? 'enforced' : 'not enforced'} (${answer.class}, ${answer.verification})`);
|
|
309
|
+
for (const item of answer.doesNotCover) console.log(`does not cover: ${item}`);
|
|
310
|
+
for (const item of answer.prerequisites) console.log(`prerequisite: ${item}`);
|
|
311
|
+
}
|
|
312
|
+
process.exitCode = answer.enforced ? 0 : 1;
|
|
313
|
+
return process.exitCode;
|
|
314
|
+
} catch (error ) {
|
|
315
|
+
if (error instanceof UnknownEnforcementTarget) return fail(error.code, error.message);
|
|
316
|
+
return fail('usage', error.message);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
281
320
|
module.exports = { dispatchRuntimeCommand };
|
|
@@ -9,23 +9,23 @@
|
|
|
9
9
|
const fs = require('node:fs');
|
|
10
10
|
const os = require('node:os');
|
|
11
11
|
const path = require('node:path');
|
|
12
|
-
const { REPO_ROOT } = require('../helper/repo-root.
|
|
13
|
-
const { doflowPaths } = require('../install/paths.
|
|
14
|
-
const { readInstallManifest } = require('../install/manifest.
|
|
15
|
-
const { DEFAULT_BACKUP_RETENTION, formatBytes } = require('../install/backup.
|
|
16
|
-
const { stateRoot, readLedger } = require('../state/index.
|
|
17
|
-
const { readLock } = require('../state/lockfile.
|
|
18
|
-
const { acquireRunLock, RunLockTimeoutError, RunLockError, RunLockLostError } = require('../state/run-lock.
|
|
19
|
-
const { createAdapterRegistry } = require('../adapters/index.
|
|
20
|
-
const { declaredHarnessPaths } = require('../helper/harness-paths.
|
|
21
|
-
const claudeAdapter = require('../adapters/claude/index.
|
|
22
|
-
const codexAdapter = require('../adapters/codex/index.
|
|
23
|
-
const { createGeminiAdapter } = require('../adapters/gemini/index.
|
|
24
|
-
const { createOpenCodeAdapter } = require('../adapters/opencode/index.
|
|
25
|
-
const { createPiAdapter } = require('../adapters/pi/index.
|
|
26
|
-
const { createCopilotAdapter } = require('../adapters/copilot/index.
|
|
27
|
-
const { createKiroAdapter } = require('../adapters/kiro/index.
|
|
28
|
-
const { createAntigravityAdapter } = require('../adapters/antigravity/index.
|
|
12
|
+
const { REPO_ROOT } = require('../helper/repo-root.js');
|
|
13
|
+
const { doflowPaths } = require('../install/paths.js');
|
|
14
|
+
const { readInstallManifest } = require('../install/manifest.js');
|
|
15
|
+
const { DEFAULT_BACKUP_RETENTION, formatBytes } = require('../install/backup.js');
|
|
16
|
+
const { stateRoot, readLedger } = require('../state/index.js');
|
|
17
|
+
const { readLock } = require('../state/lockfile.js');
|
|
18
|
+
const { acquireRunLock, RunLockTimeoutError, RunLockError, RunLockLostError } = require('../state/run-lock.js');
|
|
19
|
+
const { createAdapterRegistry } = require('../adapters/index.js');
|
|
20
|
+
const { declaredHarnessPaths } = require('../helper/harness-paths.js');
|
|
21
|
+
const claudeAdapter = require('../adapters/claude/index.js');
|
|
22
|
+
const codexAdapter = require('../adapters/codex/index.js');
|
|
23
|
+
const { createGeminiAdapter } = require('../adapters/gemini/index.js');
|
|
24
|
+
const { createOpenCodeAdapter } = require('../adapters/opencode/index.js');
|
|
25
|
+
const { createPiAdapter } = require('../adapters/pi/index.js');
|
|
26
|
+
const { createCopilotAdapter } = require('../adapters/copilot/index.js');
|
|
27
|
+
const { createKiroAdapter } = require('../adapters/kiro/index.js');
|
|
28
|
+
const { createAntigravityAdapter } = require('../adapters/antigravity/index.js');
|
|
29
29
|
|
|
30
30
|
// The directory of the `doflow` entrypoint. Derived from the shared package root rather than
|
|
31
31
|
// __dirname so its value survives this code physically moving between directories: manifests,
|
|
@@ -36,7 +36,7 @@ const SCRIPT_DIR = path.join(REPO_ROOT, 'bin');
|
|
|
36
36
|
// core/registry/ but no package.json — see the `runtime.*` assets in core/registry/assets.json.
|
|
37
37
|
// A hard require here would make every Node-backed verb fail in an install, which is the exact
|
|
38
38
|
// defect that projection exists to fix. Only version reporting depends on this.
|
|
39
|
-
function loadPkg()
|
|
39
|
+
function loadPkg() {
|
|
40
40
|
try { return require('../../package.json'); } catch { return { version: '0.0.0-installed', name: '@khoavu882/doflow' }; }
|
|
41
41
|
}
|
|
42
42
|
const pkg = loadPkg();
|
|
@@ -44,13 +44,13 @@ const pkg = loadPkg();
|
|
|
44
44
|
/**
|
|
45
45
|
* The one adapter registry construction, used by install/update/remove/reconcile. There used to
|
|
46
46
|
* be three inline copies of the same construction (one per mutating command); a fourth variant
|
|
47
|
-
* once grew in src/lifecycle/view.
|
|
47
|
+
* once grew in src/lifecycle/view.js and silently fell behind (fixed in 96006da) — which is why
|
|
48
48
|
* the registry guard pins every declared harness into each call site by parsing them.
|
|
49
49
|
* Build one here rather than copying the object literal again. Stage 3: each adapter factory
|
|
50
50
|
* receives its harness's declared native paths from core/registry/harnesses.json, so the CLI
|
|
51
51
|
* consumes exactly what the loader validates.
|
|
52
52
|
*/
|
|
53
|
-
function buildAdapterRegistry()
|
|
53
|
+
function buildAdapterRegistry() {
|
|
54
54
|
const declared = declaredHarnessPaths();
|
|
55
55
|
return createAdapterRegistry({
|
|
56
56
|
claude: claudeAdapter.createClaudeAdapter({ declaredPaths: declared.claude }),
|
|
@@ -65,11 +65,11 @@ function buildAdapterRegistry(): any {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/** Resolve {global, projectRoot} scope options for src/install/targets.js#toolDirs from parsed args. */
|
|
68
|
-
function scopeOf(o
|
|
68
|
+
function scopeOf(o ) {
|
|
69
69
|
return { global: o.global, projectRoot: o.positional[0] || '.' };
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
function installPaths(scope
|
|
72
|
+
function installPaths(scope ) {
|
|
73
73
|
const scopeRoot = scope.global ? os.homedir() : path.resolve(scope.projectRoot);
|
|
74
74
|
return doflowPaths({ scopeRoot });
|
|
75
75
|
}
|
|
@@ -77,7 +77,7 @@ function installPaths(scope: any): any {
|
|
|
77
77
|
/** Whether two paths name the same directory. Device and inode follow symlinks (macOS reaches
|
|
78
78
|
* /var through /private/var) and ignore letter case on a case-insensitive volume; a file system
|
|
79
79
|
* that reports inode 0 falls back to real paths, and paths that do not exist to resolved strings. */
|
|
80
|
-
function sameDirectory(a
|
|
80
|
+
function sameDirectory(a , b , { fsImpl = fs } = {}) {
|
|
81
81
|
const ra = path.resolve(a);
|
|
82
82
|
const rb = path.resolve(b);
|
|
83
83
|
try {
|
|
@@ -95,7 +95,7 @@ function sameDirectory(a: string, b: string, { fsImpl = fs }: any = {}): boolean
|
|
|
95
95
|
/** A project install, update, remove or reconcile rooted at the home directory would share the
|
|
96
96
|
* global install's files and records, and its remove could delete them. Refuses that run with one
|
|
97
97
|
* line and exit 1 before anything is read, locked or written; `-g` and every other root pass. */
|
|
98
|
-
function refuseHomeRootedProject(o
|
|
98
|
+
function refuseHomeRootedProject(o , command ) {
|
|
99
99
|
if (o.global) return;
|
|
100
100
|
const projectRoot = o.positional[0] || '.';
|
|
101
101
|
if (!sameDirectory(projectRoot, os.homedir())) return;
|
|
@@ -106,40 +106,40 @@ function refuseHomeRootedProject(o: any, command: string): void {
|
|
|
106
106
|
|
|
107
107
|
/** Takes the scope's run lock for a mutating command, or returns null under --dry-run. A run that
|
|
108
108
|
* cannot take it prints the one reason line and exits 1 before it has read or changed anything. */
|
|
109
|
-
function holdRunLock(o
|
|
109
|
+
function holdRunLock(o , scope , command ) {
|
|
110
110
|
if (o.dryRun) return null;
|
|
111
111
|
try {
|
|
112
112
|
return acquireRunLock({ scopeRoot: installPaths(scope).scopeRoot, scope: scope.global ? 'global' : 'project', command });
|
|
113
113
|
} catch (err) {
|
|
114
114
|
if (!(err instanceof RunLockTimeoutError || err instanceof RunLockError)) throw err;
|
|
115
|
-
console.error((err
|
|
115
|
+
console.error((err ).message);
|
|
116
116
|
process.exit(1);
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/** After the confirm prompt: renews the hold, and exits 1 before the first write if another run
|
|
121
121
|
* took the lock over while the prompt waited. */
|
|
122
|
-
function checkpointRunLock(hold
|
|
122
|
+
function checkpointRunLock(hold ) {
|
|
123
123
|
if (!hold) return;
|
|
124
124
|
try {
|
|
125
125
|
hold.checkpoint();
|
|
126
126
|
} catch (err) {
|
|
127
127
|
if (!(err instanceof RunLockLostError)) throw err;
|
|
128
|
-
console.error((err
|
|
128
|
+
console.error((err ).message);
|
|
129
129
|
process.exit(1);
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
/** Surface a reconciled-away MCP server rather than dropping it silently: the user picked it once,
|
|
134
134
|
* so its disappearance from their config should be explained, not discovered. */
|
|
135
|
-
function reportRetiredMcp(retired
|
|
135
|
+
function reportRetiredMcp(retired ) {
|
|
136
136
|
console.error(`[WARN] Dropping MCP server(s) no longer in the registry: ${retired.join(', ')}`);
|
|
137
137
|
console.error(' They were removed from DoFlow; your saved selection is being reconciled.');
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
/** The scope's doflow.lock, ledger and 1.18.0 manifest MCP list, as the MCP selection functions in
|
|
141
141
|
* src/install/mcp.js read them. */
|
|
142
|
-
function scopeSelectionState(scope
|
|
142
|
+
function scopeSelectionState(scope ) {
|
|
143
143
|
const scopeRoot = scope.global ? os.homedir() : path.resolve(scope.projectRoot);
|
|
144
144
|
return {
|
|
145
145
|
lock: readLock(scope.global ? { scope: 'global', homeDir: scopeRoot } : { scope: 'project', projectRoot: scopeRoot }),
|
|
@@ -148,13 +148,13 @@ function scopeSelectionState(scope: any): any {
|
|
|
148
148
|
};
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
const MCP_SOURCE_WORDS = Object.freeze({ flag: '--mcp', prompt: 'prompt', recorded: 'recorded', kept: 'kept', manifest: 'remembered', default: 'default' }
|
|
151
|
+
const MCP_SOURCE_WORDS = Object.freeze({ flag: '--mcp', prompt: 'prompt', recorded: 'recorded', kept: 'kept', manifest: 'remembered', default: 'default' } );
|
|
152
152
|
|
|
153
153
|
/** The one line a run names its MCP selections in, from what the plan resolved for each harness:
|
|
154
154
|
* harnesses with the same servers and source share a group, groups follow target order. Prints
|
|
155
155
|
* nothing when no target takes MCP servers, except that an explicit --mcp is said to do nothing. */
|
|
156
|
-
function printMcpSelection(view
|
|
157
|
-
const groups
|
|
156
|
+
function printMcpSelection(view , sources , { requested, prefix = '[INFO]' } = {}) {
|
|
157
|
+
const groups = [];
|
|
158
158
|
for (const target of view.plan.targets) {
|
|
159
159
|
if (target.skipped || !Array.isArray(target.mcpSelected) || !(target.harness in sources)) continue;
|
|
160
160
|
const ids = target.mcpSelected.join(', ') || 'none';
|
|
@@ -172,26 +172,26 @@ function printMcpSelection(view: any, sources: any, { requested, prefix = '[INFO
|
|
|
172
172
|
|
|
173
173
|
/** Says which MCP entries a run with no native change recorded as DoFlow's (lifecycle
|
|
174
174
|
* recordMcpOwnership), or, under --dry-run, would record. */
|
|
175
|
-
function printRecordedMcpOwnership(recorded
|
|
176
|
-
for (const [harness, ids] of Object.entries
|
|
175
|
+
function printRecordedMcpOwnership(recorded , { dryRun = false } = {}) {
|
|
176
|
+
for (const [harness, ids] of Object.entries (recorded)) {
|
|
177
177
|
const what = `${ids.length} MCP ${ids.length === 1 ? 'entry' : 'entries'} (${ids.join(', ')})`;
|
|
178
178
|
console.log(dryRun ? `[DRY] ${harness}: would record DoFlow's ownership of ${what}` : `[INFO] ${harness}: recorded DoFlow's ownership of ${what}`);
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
/** What each planned harness that takes MCP servers will hold, as doflow.lock records it. */
|
|
183
|
-
function plannedMcpSelections(view
|
|
184
|
-
return Object.fromEntries(view.plan.targets.filter((target
|
|
185
|
-
.map((target
|
|
183
|
+
function plannedMcpSelections(view ) {
|
|
184
|
+
return Object.fromEntries(view.plan.targets.filter((target ) => !target.skipped && Array.isArray(target.mcpSelected))
|
|
185
|
+
.map((target ) => [target.harness, target.mcpSelected]));
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
/** Total size of some rows, noting when any of them could not be measured. */
|
|
189
|
-
function rowsSize(rows
|
|
189
|
+
function rowsSize(rows ) {
|
|
190
190
|
const total = formatBytes(rows.reduce((sum, r) => sum + (r.bytes ?? 0), 0));
|
|
191
191
|
return rows.some((r) => r.bytes === null) ? `${total} (some sizes unknown)` : total;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
function printBackupTable(rows
|
|
194
|
+
function printBackupTable(rows , rootLabel ) {
|
|
195
195
|
if (rows.length === 0) { console.log(`[INFO] No backups found in ${rootLabel}`); return; }
|
|
196
196
|
const header = `${'BACKUP ID'.padEnd(42)} ${'OPERATION'.padEnd(14)} ${'TYPE'.padEnd(9)} ${'ORIGIN'.padEnd(9)} ${'SIZE'.padEnd(11)} TIMESTAMP`;
|
|
197
197
|
console.log(`\n${header}`);
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): authored as `.ts`, run directly by Node
|
|
4
4
|
// native type-stripping — no build step, no runtime dependency. Body stays CommonJS so a `.js`
|
|
5
|
-
// caller's `require('../helper/bash-helper.
|
|
5
|
+
// caller's `require('../helper/bash-helper.js')` keeps working while the rest of the tree is still
|
|
6
6
|
// `.js`. The `./repo-root` require stays bare while repo-root is still `.js`; it gains `.ts` in the
|
|
7
7
|
// same slice that converts repo-root.
|
|
8
8
|
const fs = require('node:fs');
|
|
9
9
|
const path = require('node:path');
|
|
10
|
-
const { REPO_ROOT } = require('./repo-root.
|
|
10
|
+
const { REPO_ROOT } = require('./repo-root.js');
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Where a bash helper (`do-paths.sh`, `do-git-state.sh`) is: inside the package (a checkout or a
|
|
@@ -20,15 +20,15 @@ const { REPO_ROOT } = require('./repo-root.ts');
|
|
|
20
20
|
* @param repoRoot the runtime root; only a test moves it
|
|
21
21
|
*/
|
|
22
22
|
function resolveBashHelper(
|
|
23
|
-
name
|
|
24
|
-
repoRoot
|
|
25
|
-
existsImpl
|
|
26
|
-
)
|
|
23
|
+
name ,
|
|
24
|
+
repoRoot = REPO_ROOT,
|
|
25
|
+
existsImpl = fs.existsSync,
|
|
26
|
+
) {
|
|
27
27
|
const candidates = [
|
|
28
28
|
path.join(repoRoot, 'core', 'shared', 'scripts', 'doflow', 'bash', name),
|
|
29
29
|
path.resolve(repoRoot, '..', 'scripts', 'doflow', 'bash', name),
|
|
30
30
|
];
|
|
31
|
-
return candidates.find((candidate
|
|
31
|
+
return candidates.find((candidate ) => existsImpl(candidate)) || null;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
module.exports = { resolveBashHelper };
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
// First module of the TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md). Authored as `.ts` and
|
|
7
7
|
// run directly by Node's native type-stripping (process.features.typescript === 'strip', Node
|
|
8
8
|
// ^22.18.0 || >=24.3.0), so no build step and no runtime dependency are added. The body stays CommonJS
|
|
9
|
-
// (`module.exports`) so a `require('../helper/git.
|
|
9
|
+
// (`module.exports`) so a `require('../helper/git.js')` from a `.js` caller keeps working while the
|
|
10
10
|
// rest of the tree is still `.js`; the explicit `.ts` extension on those requires is what the
|
|
11
11
|
// CommonJS resolver needs to find this file.
|
|
12
12
|
const { execFileSync } = require('node:child_process');
|
|
13
13
|
|
|
14
14
|
/** Short HEAD commit of the repo at `repoRoot`, or 'unknown' if not a git repo / git unavailable. */
|
|
15
|
-
function sourceCommit(repoRoot
|
|
15
|
+
function sourceCommit(repoRoot ) {
|
|
16
16
|
try {
|
|
17
17
|
return execFileSync('git', ['-C', repoRoot, 'rev-parse', '--short', 'HEAD'], { stdio: ['ignore', 'pipe', 'ignore'] })
|
|
18
18
|
.toString().trim() || 'unknown';
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
//
|
|
12
12
|
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): authored as `.ts`, run directly by Node
|
|
13
13
|
// native type-stripping (process.features.typescript === 'strip', Node ^22.18.0 || >=24.3.0) — no build step and
|
|
14
|
-
// no runtime dependency. The body stays CommonJS so a `.js` caller's `require('../helper/guardrails.
|
|
14
|
+
// no runtime dependency. The body stays CommonJS so a `.js` caller's `require('../helper/guardrails.js')`
|
|
15
15
|
// keeps working while the rest of the tree is still `.js`; the explicit `.ts` extension is what the
|
|
16
16
|
// CommonJS resolver needs to find this file.
|
|
17
17
|
|
|
@@ -20,26 +20,26 @@ const path = require('node:path');
|
|
|
20
20
|
|
|
21
21
|
const DENY_RULE = /^Bash\((.*):\*\)$/;
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
31
|
|
|
32
32
|
/** Read the canonical blocklist as bare command globs, e.g. `git push --force*`. Throws when the
|
|
33
33
|
* authored file is missing or malformed — a silent empty blocklist would look like success while
|
|
34
34
|
* projecting no protection at all. */
|
|
35
|
-
function destructiveCommandGlobs({ repoRoot, fsImpl = fs }
|
|
35
|
+
function destructiveCommandGlobs({ repoRoot, fsImpl = fs } = {} ) {
|
|
36
36
|
const file = path.join(repoRoot, 'core', 'harnesses', 'claude', 'settings', 'settings.json');
|
|
37
37
|
const parsed = JSON.parse(fsImpl.readFileSync(file, 'utf8'));
|
|
38
38
|
const deny = parsed?.permissions?.deny;
|
|
39
39
|
if (!Array.isArray(deny) || deny.length === 0) {
|
|
40
40
|
throw new Error(`guardrails: no permissions.deny list in ${file}`);
|
|
41
41
|
}
|
|
42
|
-
return deny.map((rule
|
|
42
|
+
return deny.map((rule ) => {
|
|
43
43
|
const m = DENY_RULE.exec(rule);
|
|
44
44
|
if (!m) throw new Error(`guardrails: unrecognized deny rule '${rule}' in ${file}`);
|
|
45
45
|
return m[1];
|