@khoavu882/doflow 2.0.0-beta.3 → 2.0.0-beta.4
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/.claude-plugin/plugin.json +1 -1
- package/core/.codex-plugin/plugin.json +1 -1
- package/core/.plugin/plugin.json +1 -1
- package/core/registry/storage.json +87 -83
- package/core/registry/verification.json +1 -1
- package/core/shared/scripts/doflow/bash/render-puml.sh +2 -2
- package/core/shared/skills/do-execute-plan/references/scaffold.md +2 -2
- package/package.json +4 -2
- package/src/adapters/antigravity/{index.ts → index.js} +60 -60
- package/src/adapters/claude/{index.ts → index.js} +48 -48
- 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} +23 -23
- package/src/adapters/codex/{index.ts → index.js} +98 -98
- 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} +70 -70
- package/src/adapters/gemini/{hooks.ts → hooks.js} +24 -24
- package/src/adapters/gemini/{index.ts → index.js} +41 -41
- 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} +34 -34
- 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/{index.ts → index.js} +30 -30
- 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} +24 -24
- 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} +19 -19
- package/src/cli/{index.ts → index.js} +26 -26
- package/src/cli/{runtime-commands.ts → runtime-commands.js} +48 -48
- 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/{index.ts → index.js} +140 -140
- 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} +60 -60
- 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/{capability-router.ts → capability-router.js} +39 -39
- package/src/runtime/{checkouts.ts → checkouts.js} +21 -21
- package/src/runtime/{claims.ts → claims.js} +30 -30
- package/src/runtime/{cli-result.ts → cli-result.js} +4 -4
- package/src/runtime/{cli.ts → cli.js} +37 -37
- package/src/runtime/{command-detect.ts → command-detect.js} +47 -47
- package/src/runtime/{context-pack.ts → context-pack.js} +20 -20
- package/src/runtime/{decision-register.ts → decision-register.js} +67 -67
- package/src/runtime/{evidence-ledger.ts → evidence-ledger.js} +25 -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} +15 -15
- 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} +35 -35
- 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} +40 -40
- package/src/runtime/scaffold/{artifacts.ts → artifacts.js} +19 -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/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} +61 -61
- package/src/runtime/{task-classifier.ts → task-classifier.js} +34 -34
- package/src/runtime/{task-scope.ts → task-scope.js} +13 -13
- package/src/runtime/{task-state.ts → task-state.js} +15 -15
- 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
|
@@ -23,52 +23,52 @@ 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
|
-
// `doctor` comes from the health module rather than src/runtime/cli.
|
|
26
|
+
} = require('../runtime/cli.js');
|
|
27
|
+
// `doctor` comes from the health module rather than src/runtime/cli.js: the health-probe report
|
|
28
28
|
// (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.
|
|
29
|
+
const { handleDoctorCommand } = require('../runtime/health.js');
|
|
30
|
+
const { handleTraceCommand, handleStatsCommand, handleDiscoverCommand, resolveRunsLocation } = require('../runtime/trace/ledger.js');
|
|
31
|
+
const { handleIndicatorsCommand } = require('../runtime/trace/indicators.js');
|
|
32
32
|
// The rest of the verb surface design §4.2 declares. `classify`, `workflow`, `route`, `claim`,
|
|
33
33
|
// `context-pack`, `verify`, `recover` and `scaffold` each have exactly one implementation, in the
|
|
34
34
|
// 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.
|
|
35
|
+
const { ReadinessEngine } = require('../runtime/readiness.js');
|
|
36
|
+
const { handleClaimCommand } = require('../runtime/claims.js');
|
|
37
|
+
const { handleClassifyCommand } = require('../runtime/task-classifier.js');
|
|
38
|
+
const { handleWorkflowCommand } = require('../runtime/workflow-engine.js');
|
|
39
|
+
const { handleOrchestrateCommand } = require('../runtime/workflow-orchestrator.js');
|
|
40
|
+
const { handleResearchRequestCommand } = require('../runtime/research-request.js');
|
|
41
|
+
const { handleTaskAttemptCommand } = require('../runtime/task-attempts.js');
|
|
42
|
+
const { handleRetrieveCommand } = require('../runtime/knowledge/retrieval.js');
|
|
43
|
+
const { handleModelRoleCommand } = require('../runtime/model-router.js');
|
|
44
|
+
const { handleRouteCommand } = require('../runtime/capability-router.js');
|
|
45
|
+
const { handleContextPackCommand } = require('../runtime/context-pack.js');
|
|
46
|
+
const { handleRetrievalPlanCommand } = require('../runtime/retrieval-plan.js');
|
|
47
|
+
const { handleOutcomeCommand } = require('../runtime/outcome.js');
|
|
48
|
+
const { handleVerifyCommand } = require('../runtime/verification/engine.js');
|
|
49
|
+
const { handleLeakScanCommand } = require('../runtime/leak-scan.js');
|
|
50
|
+
const { handleRecoverCommand } = require('../runtime/recovery.js');
|
|
51
|
+
const { handleScaffoldCommand } = require('../runtime/scaffold/generate.js');
|
|
52
|
+
const { handleDecisionCommand } = require('../runtime/decision-register.js');
|
|
53
|
+
const { handleFollowupCommand, handleLifecycleCommand, handleGoalCommand } = require('../runtime/lifecycle/cli.js');
|
|
54
|
+
const { projectRoot: lifecycleProjectRoot } = require('../runtime/lifecycle/root.js');
|
|
55
|
+
const { upkeepAfterVerb } = require('../runtime/storage/upkeep.js');
|
|
56
|
+
const { handleStorageCommand } = require('../runtime/storage/cli.js');
|
|
57
57
|
// 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.
|
|
58
|
+
function handleFailureCommand(options ) { return require('../runtime/failure/cli.js').handleFailureCommand(options); }
|
|
59
|
+
const { handleInventoryCommand } = require('../runtime/inventory/index.js');
|
|
60
|
+
const { finishRuntime, usageError } = require('../runtime/cli-result.js');
|
|
61
|
+
const { setDefaultSlug, invalidSlugRefusal, slugNamesNoFeature } = require('../runtime/task-scope.js');
|
|
62
|
+
const { listCheckouts } = require('../runtime/checkouts.js');
|
|
63
|
+
const { featureSlugFor } = require('../runtime/implementation-gate.js');
|
|
64
|
+
const { handleBriefToolsCommand } = require('../runtime/brief-tools.js');
|
|
65
|
+
const { handleAdviseCommand } = require('../runtime/advice/cli.js');
|
|
66
|
+
const { REPO_ROOT, buildAdapterRegistry } = require('./shared.js');
|
|
67
67
|
|
|
68
68
|
/** Where `readiness`/`evidence` read and write per-task state. Mirrors scopeOf()'s rules so these
|
|
69
69
|
* commands are scope-aware like the rest of the CLI, rather than defaulting to the DoFlow install
|
|
70
70
|
* directory — which for an npm install is inside node_modules/. */
|
|
71
|
-
function evidenceRoot(o
|
|
71
|
+
function evidenceRoot(o ) {
|
|
72
72
|
return o.global ? os.homedir() : path.resolve(o.positional[0] || '.');
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -81,7 +81,7 @@ function evidenceRoot(o: any): string {
|
|
|
81
81
|
* The valid set comes from the readiness registry rather than a list written here, so it cannot
|
|
82
82
|
* drift from the templates that actually exist.
|
|
83
83
|
*/
|
|
84
|
-
function requireTaskClass(o
|
|
84
|
+
function requireTaskClass(o ) {
|
|
85
85
|
if (typeof o.taskClass === 'string' && o.taskClass.trim() !== '') return o.taskClass;
|
|
86
86
|
let valid = '';
|
|
87
87
|
try {
|
|
@@ -92,7 +92,7 @@ function requireTaskClass(o: any): string {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/** The task id the caller named, or exit 2. Same reasoning as requireTaskClass. */
|
|
95
|
-
function requireTaskId(o
|
|
95
|
+
function requireTaskId(o ) {
|
|
96
96
|
if (typeof o.taskId === 'string' && o.taskId.trim() !== '') return o.taskId;
|
|
97
97
|
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
98
|
process.exit(2);
|
|
@@ -104,8 +104,8 @@ function requireTaskId(o: any): string {
|
|
|
104
104
|
* Absent flags stay absent. Filling a gap here with 'unknown'/'general' would move the omission out
|
|
105
105
|
* of the caller's error message and into the stored record, where it reads as a measurement.
|
|
106
106
|
*/
|
|
107
|
-
function evidenceItemFromFlags(o
|
|
108
|
-
const item
|
|
107
|
+
function evidenceItemFromFlags(o ) {
|
|
108
|
+
const item = {};
|
|
109
109
|
if (o.kind !== undefined) item.kind = o.kind;
|
|
110
110
|
if (o.provenance !== undefined) item.provenance = o.provenance;
|
|
111
111
|
if (o.locator !== undefined) item.locator = o.locator;
|
|
@@ -118,7 +118,7 @@ function evidenceItemFromFlags(o: any): any {
|
|
|
118
118
|
if (o.observedCommand !== undefined) item.observation.command = o.observedCommand;
|
|
119
119
|
if (o.observedExit !== undefined) item.observation.exitCode = o.observedExit;
|
|
120
120
|
}
|
|
121
|
-
const source
|
|
121
|
+
const source = {};
|
|
122
122
|
if (o.provider !== undefined) source.provider = o.provider;
|
|
123
123
|
if (o.capability !== undefined) source.capability = o.capability;
|
|
124
124
|
if (Object.keys(source).length > 0) item.source = source;
|
|
@@ -139,12 +139,12 @@ const FEATURE_STORE_VERBS = new Set(['evidence', 'claim', 'readiness']);
|
|
|
139
139
|
* the readiness record written there is the one verify, orchestrate and the edit hook read from
|
|
140
140
|
* the worktree.
|
|
141
141
|
*/
|
|
142
|
-
function featureStoreRoot(o
|
|
142
|
+
function featureStoreRoot(o ) {
|
|
143
143
|
if (!FEATURE_STORE_VERBS.has(o.cmd) || o.global) return null;
|
|
144
144
|
const checkouts = listCheckouts({ cwd: evidenceRoot(o) });
|
|
145
145
|
if (!checkouts.isLinked) return null;
|
|
146
146
|
const slug = featureSlugFor({ projectRoot: checkouts.current, slug: o.slug });
|
|
147
|
-
const folder = (root
|
|
147
|
+
const folder = (root ) => path.join(root, 'agent-docs', 'doflow', slug || '');
|
|
148
148
|
if (!slug || fs.existsSync(folder(checkouts.current)) || !fs.existsSync(folder(checkouts.main))) return null;
|
|
149
149
|
return { root: checkouts.main, slug };
|
|
150
150
|
}
|
|
@@ -155,7 +155,7 @@ function featureStoreRoot(o: any): { root: string; slug: string } | null {
|
|
|
155
155
|
* validate parsed CLI arguments, which is the CLI's job, not a library's); everything else belongs
|
|
156
156
|
* to the engine module each case names.
|
|
157
157
|
*/
|
|
158
|
-
function dispatchRuntimeCommandInner(o
|
|
158
|
+
function dispatchRuntimeCommandInner(o , moment ) {
|
|
159
159
|
// A slug becomes a directory name and a state key, so one that could name a path is refused
|
|
160
160
|
// here, once, for every verb that reads `--slug`, before any of them reads or writes state
|
|
161
161
|
// (FR-013, IC-009). A well-formed slug that names no feature is the verbs' own business.
|
|
@@ -253,7 +253,7 @@ function dispatchRuntimeCommandInner(o: any, moment: any): any {
|
|
|
253
253
|
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
254
|
// The store is shared by every linked worktree of the clone (lifecycle's main-worktree root); the
|
|
255
255
|
// 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
|
|
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 ) => featureSlugFor({ projectRoot: cwd }) });
|
|
257
257
|
case 'retrieve': return handleRetrieveCommand({ query: o.query, top: o.top, json: o.json });
|
|
258
258
|
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
259
|
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) });
|
|
@@ -271,8 +271,8 @@ function dispatchRuntimeCommandInner(o: any, moment: any): any {
|
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
/** Await the verb without changing its return value, output or exit code; usage exits never return. */
|
|
274
|
-
async function dispatchRuntimeCommand(o
|
|
275
|
-
const moment
|
|
274
|
+
async function dispatchRuntimeCommand(o ) {
|
|
275
|
+
const moment = {};
|
|
276
276
|
const result = await dispatchRuntimeCommandInner(o, moment);
|
|
277
277
|
if (o.cmd !== 'storage') upkeepAfterVerb({ projectRoot: moment.projectRoot });
|
|
278
278
|
return result;
|
|
@@ -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];
|
|
@@ -28,33 +28,33 @@
|
|
|
28
28
|
//
|
|
29
29
|
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): authored as `.ts`, run directly by Node
|
|
30
30
|
// native type-stripping — no build step, no runtime dependency. Body stays CommonJS so a `.js`
|
|
31
|
-
// caller's `require('../helper/harness-paths.
|
|
31
|
+
// caller's `require('../helper/harness-paths.js')` keeps working while the tree is still `.js`. The
|
|
32
32
|
// `./repo-root` require stays bare while repo-root is still `.js`.
|
|
33
33
|
|
|
34
34
|
const fs = require('node:fs');
|
|
35
35
|
const path = require('node:path');
|
|
36
|
-
const { REPO_ROOT } = require('./repo-root.
|
|
36
|
+
const { REPO_ROOT } = require('./repo-root.js');
|
|
37
37
|
|
|
38
38
|
const PATH_BASES = new Set(['root', 'home']);
|
|
39
39
|
const SURFACE_NAME = /^[a-z][a-zA-Z0-9]*$/;
|
|
40
40
|
const SEGMENT = /^(?!\.\.$)[^\\/]+$/;
|
|
41
41
|
const SCOPES = ['project', 'user'];
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
53
|
|
|
54
|
-
function issue(errors
|
|
54
|
+
function issue(errors , location , message ) { errors.push(`${location}: ${message}`); }
|
|
55
55
|
|
|
56
56
|
/** Validate one {base, segments} rule. Returns errors via the pushed array. */
|
|
57
|
-
function validateRule(rule
|
|
57
|
+
function validateRule(rule , location , errors ) {
|
|
58
58
|
if (!rule || typeof rule !== 'object' || Array.isArray(rule)) {
|
|
59
59
|
issue(errors, location, 'must be an object'); return;
|
|
60
60
|
}
|
|
@@ -63,7 +63,7 @@ function validateRule(rule: any, location: string, errors: string[]): void {
|
|
|
63
63
|
}
|
|
64
64
|
if (!PATH_BASES.has(rule.base)) issue(errors, location, `base must be one of ${[...PATH_BASES].join(', ')}`);
|
|
65
65
|
const segments = rule.segments;
|
|
66
|
-
if (!Array.isArray(segments) || segments.some((segment
|
|
66
|
+
if (!Array.isArray(segments) || segments.some((segment ) => typeof segment !== 'string' || !SEGMENT.test(segment))) {
|
|
67
67
|
issue(errors, location, "segments must be an array of plain single path segments (no '/', no '\\', no '..')");
|
|
68
68
|
}
|
|
69
69
|
}
|
|
@@ -71,12 +71,12 @@ function validateRule(rule: any, location: string, errors: string[]): void {
|
|
|
71
71
|
/** Validate one harness's whole paths section. Unknown keys and malformed rules are rejected
|
|
72
72
|
* loudly: a typo'd declaration must fail the registry load, not silently fall back to whatever
|
|
73
73
|
* the adapter used to hardcode. Returns an array of error messages (empty when valid). */
|
|
74
|
-
function validatePathsSection(section
|
|
75
|
-
const errors
|
|
74
|
+
function validatePathsSection(section , location ) {
|
|
75
|
+
const errors = [];
|
|
76
76
|
if (!section || typeof section !== 'object' || Array.isArray(section)) {
|
|
77
77
|
return [`${location}: must be an object`];
|
|
78
78
|
}
|
|
79
|
-
for (const [name, value] of Object.entries
|
|
79
|
+
for (const [name, value] of Object.entries (section)) {
|
|
80
80
|
const at = `${location}.${name}`;
|
|
81
81
|
if (!SURFACE_NAME.test(name)) { issue(errors, at, `surface name '${name}' must be camelCase`); continue; }
|
|
82
82
|
if (!value || typeof value !== 'object' || Array.isArray(value)) { issue(errors, at, 'must be an object'); continue; }
|
|
@@ -98,7 +98,7 @@ function validatePathsSection(section: any, location: string): string[] {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/** Resolve one rule to an absolute path. */
|
|
101
|
-
function resolveRule(rule
|
|
101
|
+
function resolveRule(rule , { root, home } ) {
|
|
102
102
|
const base = rule.base === 'home' ? home : root;
|
|
103
103
|
return path.join(base, ...(rule.segments || []));
|
|
104
104
|
}
|
|
@@ -109,12 +109,12 @@ function resolveRule(rule: PathRule, { root, home }: { root: string; home: strin
|
|
|
109
109
|
* Returns { <surface>: absolutePath | null } — null when the scope has no rule for a surface —
|
|
110
110
|
* plus `root`, the scope's install root itself (an input, not a declaration).
|
|
111
111
|
*/
|
|
112
|
-
function resolveHarnessPaths(section
|
|
112
|
+
function resolveHarnessPaths(section , { scope, scopeRoot, homeDir } = {}) {
|
|
113
113
|
const userScope = scope === 'user' || scope === 'global';
|
|
114
114
|
const home = path.resolve(homeDir || scopeRoot);
|
|
115
115
|
const root = userScope ? home : path.resolve(scopeRoot);
|
|
116
|
-
const resolved
|
|
117
|
-
for (const [name, value] of Object.entries
|
|
116
|
+
const resolved = { root };
|
|
117
|
+
for (const [name, value] of Object.entries (section || {})) {
|
|
118
118
|
const rule = (value.base !== undefined || value.segments !== undefined)
|
|
119
119
|
? value
|
|
120
120
|
: (value[userScope ? 'user' : 'project'] ?? null);
|
|
@@ -123,16 +123,16 @@ function resolveHarnessPaths(section: any, { scope, scopeRoot, homeDir }: Resolv
|
|
|
123
123
|
return resolved;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
let declaredCache
|
|
126
|
+
let declaredCache ;
|
|
127
127
|
/** Every harness's declared paths section, keyed by harness id, read straight from
|
|
128
128
|
* core/registry/harnesses.json (memoized per process — the file ships inside the package).
|
|
129
129
|
* Adapters default their factories to this, so a factory called with no arguments and the CLI
|
|
130
130
|
* wiring in src/cli/shared.js#buildAdapterRegistry consume byte-identical declarations. */
|
|
131
|
-
function declaredHarnessPaths(repoRoot
|
|
131
|
+
function declaredHarnessPaths(repoRoot = REPO_ROOT) {
|
|
132
132
|
if (declaredCache && declaredCache.repoRoot === path.resolve(repoRoot)) return declaredCache.paths;
|
|
133
133
|
const file = path.join(path.resolve(repoRoot), 'core', 'registry', 'harnesses.json');
|
|
134
134
|
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
135
|
-
const paths = Object.fromEntries((parsed.harnesses || []).map((harness
|
|
135
|
+
const paths = Object.fromEntries((parsed.harnesses || []).map((harness ) => [harness.id, harness.paths ?? {}]));
|
|
136
136
|
declaredCache = { repoRoot: path.resolve(repoRoot), paths };
|
|
137
137
|
return paths;
|
|
138
138
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
//
|
|
6
6
|
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): authored as `.ts`, run directly by Node
|
|
7
7
|
// native type-stripping — no build step, no runtime dependency. Body stays CommonJS so a `.js`
|
|
8
|
-
// caller's `require('../helper/marker-merge.
|
|
8
|
+
// caller's `require('../helper/marker-merge.js')` keeps working while the tree is still `.js`.
|
|
9
9
|
const fs = require('node:fs');
|
|
10
10
|
const path = require('node:path');
|
|
11
11
|
|
|
@@ -17,14 +17,14 @@ const MARKER_END = '<!-- doflow:end -->';
|
|
|
17
17
|
* Merge `srcAbs`'s content into `dstAbs` inside a doflow-owned marker span,
|
|
18
18
|
* preserving any of `dstAbs`'s own content outside that span byte-for-byte.
|
|
19
19
|
*/
|
|
20
|
-
function mergeMarkedSection(srcAbs
|
|
20
|
+
function mergeMarkedSection(srcAbs , dstAbs , { dryRun = false } = {}) {
|
|
21
21
|
const srcContent = fs.readFileSync(srcAbs, 'utf8').replace(/\s+$/, '');
|
|
22
22
|
const section = `${MARKER_START}\n${srcContent}\n${MARKER_END}\n`;
|
|
23
23
|
|
|
24
24
|
// Read once, reused both for parsing and for the final no-op comparison below.
|
|
25
25
|
const existing = fs.existsSync(dstAbs) ? fs.readFileSync(dstAbs, 'utf8') : null;
|
|
26
26
|
|
|
27
|
-
let newContent
|
|
27
|
+
let newContent ;
|
|
28
28
|
if (existing === null) {
|
|
29
29
|
newContent = section;
|
|
30
30
|
} else {
|
|
@@ -90,7 +90,7 @@ function mergeMarkedSection(srcAbs: string, dstAbs: string, { dryRun = false }:
|
|
|
90
90
|
* @returns whether a managed span was found and stripped (false if the file doesn't
|
|
91
91
|
* exist or has no doflow markers — both are no-ops, not errors)
|
|
92
92
|
*/
|
|
93
|
-
function removeMarkedSection(file
|
|
93
|
+
function removeMarkedSection(file ) {
|
|
94
94
|
if (!fs.existsSync(file)) return false;
|
|
95
95
|
const existing = fs.readFileSync(file, 'utf8');
|
|
96
96
|
const start = existing.indexOf(MARKER_START);
|