@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
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
const path = require('node:path');
|
|
16
16
|
const nodeFs = require('node:fs');
|
|
17
|
-
const { parseYamlFile } = require('../capability-router.
|
|
18
|
-
const { REPO_ROOT } = require('../../helper/repo-root.
|
|
17
|
+
const { parseYamlFile } = require('../capability-router.js');
|
|
18
|
+
const { REPO_ROOT } = require('../../helper/repo-root.js');
|
|
19
19
|
|
|
20
20
|
const REGISTRY_FILENAME = 'verification.json';
|
|
21
21
|
|
|
@@ -48,7 +48,7 @@ const RECOVERY_OUTCOMES = Object.freeze([
|
|
|
48
48
|
'NO_REMEDIATION',
|
|
49
49
|
]);
|
|
50
50
|
|
|
51
|
-
function assert(condition
|
|
51
|
+
function assert(condition , message ) {
|
|
52
52
|
if (!condition) throw new Error(`verification.json is invalid: ${message}`);
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -67,7 +67,7 @@ function assert(condition: any, message: string): void {
|
|
|
67
67
|
* @param {Object} [options.fsImpl]
|
|
68
68
|
* @returns {Object} the validated registry
|
|
69
69
|
*/
|
|
70
|
-
function loadVerificationRegistry(options
|
|
70
|
+
function loadVerificationRegistry(options = {}) {
|
|
71
71
|
let data = options.registry;
|
|
72
72
|
if (!data) {
|
|
73
73
|
const repoRoot = options.repoRoot || REPO_ROOT;
|
|
@@ -84,7 +84,7 @@ function loadVerificationRegistry(options: any = {}): any {
|
|
|
84
84
|
// FR-009 states this as a MUST, so it is enforced at load rather than trusted at call sites.
|
|
85
85
|
assert(policy.modelReviewIsPrimaryProof === false, 'policy.modelReviewIsPrimaryProof must be false');
|
|
86
86
|
|
|
87
|
-
const seen = new Set
|
|
87
|
+
const seen = new Set ();
|
|
88
88
|
let previousOrder = -Infinity;
|
|
89
89
|
let lastNonModelOrder = -Infinity;
|
|
90
90
|
let firstModelOrder = Infinity;
|
|
@@ -107,7 +107,7 @@ function loadVerificationRegistry(options: any = {}): any {
|
|
|
107
107
|
// of the registry, so it is checkable here instead of being re-asserted in prose.
|
|
108
108
|
assert(firstModelOrder > lastNonModelOrder, 'every model-based tier must be ordered after every deterministic tier');
|
|
109
109
|
|
|
110
|
-
for (const [level, def] of Object.entries(data.riskLevels)
|
|
110
|
+
for (const [level, def] of Object.entries(data.riskLevels) ) {
|
|
111
111
|
assert(def && Array.isArray(def.requiredTiers), `risk level '${level}' needs requiredTiers`);
|
|
112
112
|
for (const id of def.requiredTiers.concat(def.advisoryTiers || [])) {
|
|
113
113
|
assert(seen.has(id), `risk level '${level}' references unknown tier '${id}'`);
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
const fs = require('node:fs');
|
|
6
6
|
const path = require('node:path');
|
|
7
7
|
const { spawnSync } = require('node:child_process');
|
|
8
|
-
const { resolveActiveFeature } = require('../feature-resolve.
|
|
9
|
-
const { resolveBashHelper } = require('../../helper/bash-helper.
|
|
10
|
-
const { listCheckouts } = require('../checkouts.
|
|
8
|
+
const { resolveActiveFeature } = require('../feature-resolve.js');
|
|
9
|
+
const { resolveBashHelper } = require('../../helper/bash-helper.js');
|
|
10
|
+
const { listCheckouts } = require('../checkouts.js');
|
|
11
11
|
|
|
12
12
|
// The change-scope bound of a planned feature, and the baseline a change is measured from
|
|
13
13
|
// (feature 045, IC-003 / IC-004).
|
|
@@ -36,13 +36,13 @@ const NONE_FIELD = /^none(?=$|[\s(;,`])/i;
|
|
|
36
36
|
* @param {string} planText
|
|
37
37
|
* @returns {Array<string>}
|
|
38
38
|
*/
|
|
39
|
-
function taskFilesFromPlan(planText
|
|
39
|
+
function taskFilesFromPlan(planText ) {
|
|
40
40
|
const lines = String(planText || '').split('\n');
|
|
41
|
-
const paths
|
|
41
|
+
const paths = [];
|
|
42
42
|
for (let i = 0; i < lines.length; i += 1) {
|
|
43
43
|
if (!TASK_LINE.test(lines[i])) continue;
|
|
44
44
|
let at = -1;
|
|
45
|
-
for (const m of lines[i].matchAll(FILES_FIELD)) at = (m.index
|
|
45
|
+
for (const m of lines[i].matchAll(FILES_FIELD)) at = (m.index ) + m[0].length;
|
|
46
46
|
if (at === -1) continue;
|
|
47
47
|
let rest = lines[i].slice(at);
|
|
48
48
|
// `files: none (...)` states that a task changes no file; its words are not paths.
|
|
@@ -65,9 +65,9 @@ function taskFilesFromPlan(planText: any): string[] {
|
|
|
65
65
|
return [...new Set(paths)];
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
const folderOf = (slug
|
|
68
|
+
const folderOf = (slug ) => (slug ? `agent-docs/doflow/${slug}/` : null);
|
|
69
69
|
|
|
70
|
-
function isFile(file
|
|
70
|
+
function isFile(file ) {
|
|
71
71
|
try {
|
|
72
72
|
return fs.statSync(file).isFile();
|
|
73
73
|
} catch {
|
|
@@ -75,7 +75,7 @@ function isFile(file: string): boolean {
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
function isDirectory(dir
|
|
78
|
+
function isDirectory(dir ) {
|
|
79
79
|
try {
|
|
80
80
|
return fs.statSync(dir).isDirectory();
|
|
81
81
|
} catch {
|
|
@@ -84,7 +84,7 @@ function isDirectory(dir: string): boolean {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/** Why no other checkout was consulted, in the words a reason text lists. */
|
|
87
|
-
function noMainCheckout(checkouts
|
|
87
|
+
function noMainCheckout(checkouts ) {
|
|
88
88
|
if (!checkouts.ok) {
|
|
89
89
|
if (checkouts.reason === 'not-a-git-repository') return 'not a git repository';
|
|
90
90
|
if (checkouts.reason === 'git-unavailable') return 'git is not available';
|
|
@@ -110,20 +110,20 @@ function noMainCheckout(checkouts: any): string {
|
|
|
110
110
|
* @returns {{plan: string|null, planRel: string|null, origin: string|null, root: string|null, slug: string|null,
|
|
111
111
|
* folderRel: string|null, hasRegister: boolean, searched: Array<{place: string, result: string}>, reason: string|null}}
|
|
112
112
|
*/
|
|
113
|
-
function resolvePlanSource({ projectRoot, slug = null, planPath = null, exec = spawnSync }
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const searched
|
|
120
|
-
const result = (fields
|
|
113
|
+
function resolvePlanSource({ projectRoot, slug = null, planPath = null, exec = spawnSync }
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
) {
|
|
119
|
+
const searched = [];
|
|
120
|
+
const result = (fields ) => ({
|
|
121
121
|
plan: null, planRel: null, origin: null, root: null, slug: null, folderRel: null, hasRegister: false, searched, reason: null, ...fields,
|
|
122
122
|
});
|
|
123
|
-
const resolve = (s
|
|
123
|
+
const resolve = (s ) => {
|
|
124
124
|
try {
|
|
125
125
|
return resolveActiveFeature({ projectRoot, slug: s });
|
|
126
|
-
} catch (error
|
|
126
|
+
} catch (error ) {
|
|
127
127
|
return { error: 'resolver-failed', message: `could not run the feature resolver: ${error.message}` };
|
|
128
128
|
}
|
|
129
129
|
};
|
|
@@ -151,7 +151,7 @@ function resolvePlanSource({ projectRoot, slug = null, planPath = null, exec = s
|
|
|
151
151
|
}
|
|
152
152
|
const s = found.paths.feature_slug;
|
|
153
153
|
const folderRel = folderOf(s);
|
|
154
|
-
const withFolder = (fields
|
|
154
|
+
const withFolder = (fields ) => {
|
|
155
155
|
const plan = path.resolve(fields.root, fields.planRel);
|
|
156
156
|
let reason = null;
|
|
157
157
|
if (!isFile(plan)) reason = 'no-plan';
|
|
@@ -182,7 +182,7 @@ function resolvePlanSource({ projectRoot, slug = null, planPath = null, exec = s
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
/** Why a declared-scope token is refused, or null when it is a repository-relative path. */
|
|
185
|
-
function refusedToken(token
|
|
185
|
+
function refusedToken(token ) {
|
|
186
186
|
if (token === '') return 'it is empty';
|
|
187
187
|
if (/[\s\\\0]/.test(token)) return 'it contains whitespace';
|
|
188
188
|
if (token.startsWith('/') || token.startsWith('~') || /^[A-Za-z]:/.test(token)) return 'it is absolute';
|
|
@@ -200,8 +200,8 @@ function refusedToken(token: string): string | null {
|
|
|
200
200
|
* @param {string} text
|
|
201
201
|
* @returns {{paths: Array<string>, reason: null}|{paths: null, reason: string}}
|
|
202
202
|
*/
|
|
203
|
-
function parseDeclaredScope(text
|
|
204
|
-
const paths
|
|
203
|
+
function parseDeclaredScope(text ) {
|
|
204
|
+
const paths = [];
|
|
205
205
|
for (const raw of String(text).trim().split(',')) {
|
|
206
206
|
const written = raw.trim();
|
|
207
207
|
const token = written.replace(/^(\.\/)+/, '');
|
|
@@ -225,20 +225,20 @@ function parseDeclaredScope(text: any): any {
|
|
|
225
225
|
* @param {Function} [options.exec]
|
|
226
226
|
* @returns {{bound: Object|null, reason: string|null, searched: Array<Object>, planSource: Object}}
|
|
227
227
|
*/
|
|
228
|
-
function resolveScopeBound({ projectRoot, slug = null, planPath = null, declared = null, baselineRef = null, exec = spawnSync }
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
228
|
+
function resolveScopeBound({ projectRoot, slug = null, planPath = null, declared = null, baselineRef = null, exec = spawnSync }
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
) {
|
|
236
236
|
const planSource = resolvePlanSource({ projectRoot, slug, planPath, exec });
|
|
237
237
|
let reason = planSource.reason;
|
|
238
|
-
const allowed
|
|
239
|
-
const sources
|
|
238
|
+
const allowed = [];
|
|
239
|
+
const sources = [];
|
|
240
240
|
if (reason === null) {
|
|
241
|
-
let text
|
|
241
|
+
let text = null;
|
|
242
242
|
try {
|
|
243
243
|
text = fs.readFileSync(planSource.plan, 'utf8');
|
|
244
244
|
} catch {
|
|
@@ -257,7 +257,7 @@ function resolveScopeBound({ projectRoot, slug = null, planPath = null, declared
|
|
|
257
257
|
sources.push({ kind: 'declared', paths: [...declared.paths], origin: declared.origin, ...(declared.record ? { record: declared.record } : {}) });
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
-
let baseline
|
|
260
|
+
let baseline = null;
|
|
261
261
|
if (baselineRef !== null && baselineRef !== undefined) {
|
|
262
262
|
const res = resolveExplicitBaseline({ cwd: projectRoot, ref: baselineRef, exec });
|
|
263
263
|
if (!res.ok) {
|
|
@@ -294,12 +294,12 @@ function resolveScopeBound({ projectRoot, slug = null, planPath = null, declared
|
|
|
294
294
|
* @param {string|null} [options.slug]
|
|
295
295
|
* @returns {{allowedPaths: Array<string>, source: string}|null}
|
|
296
296
|
*/
|
|
297
|
-
function buildScopeBound({ projectRoot, slug = null }
|
|
297
|
+
function buildScopeBound({ projectRoot, slug = null } ) {
|
|
298
298
|
const { bound } = resolveScopeBound({ projectRoot, slug });
|
|
299
299
|
return bound ? { allowedPaths: bound.allowedPaths, source: bound.source } : null;
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
function originText(origin
|
|
302
|
+
function originText(origin , root ) {
|
|
303
303
|
if (origin === 'main-checkout') return `main checkout ${root}`;
|
|
304
304
|
if (origin === 'plan-path') return '--plan-path';
|
|
305
305
|
return 'current checkout';
|
|
@@ -314,8 +314,8 @@ function originText(origin: string | null, root: string | null): string {
|
|
|
314
314
|
* @param {Object} options.planSource the `resolvePlanSource` result
|
|
315
315
|
* @returns {string}
|
|
316
316
|
*/
|
|
317
|
-
function scopeReasonText({ reason, taskId, planSource }
|
|
318
|
-
const places = planSource.searched.map((s
|
|
317
|
+
function scopeReasonText({ reason, taskId, planSource } ) {
|
|
318
|
+
const places = planSource.searched.map((s ) => `${s.place}: ${s.result}`).join('; ');
|
|
319
319
|
const where = `${planSource.folderRel} (${originText(planSource.origin, planSource.root)})`;
|
|
320
320
|
switch (reason) {
|
|
321
321
|
case 'no-plan':
|
|
@@ -336,8 +336,8 @@ function scopeReasonText({ reason, taskId, planSource }: { reason: string; taskI
|
|
|
336
336
|
* @param {Array<Object>} sources a bound's `sources`
|
|
337
337
|
* @returns {string}
|
|
338
338
|
*/
|
|
339
|
-
function boundSourcesText(sources
|
|
340
|
-
return sources.map((s
|
|
339
|
+
function boundSourcesText(sources ) {
|
|
340
|
+
return sources.map((s ) => (s.kind === 'plan'
|
|
341
341
|
? `plan ${s.path} (${originText(s.origin, s.root)})`
|
|
342
342
|
: `declared ${s.paths.join(',')} (${s.origin === 'readiness-record' ? `readiness record ${s.record}` : '--scope'})`)).join(' + ');
|
|
343
343
|
}
|
|
@@ -351,17 +351,17 @@ function boundSourcesText(sources: any[]): string {
|
|
|
351
351
|
* @param {Function} [options.exec] spawnSync-compatible
|
|
352
352
|
* @returns {{ref: string, mergeBase: string}|{reason: string}}
|
|
353
353
|
*/
|
|
354
|
-
function resolveIntegrationBase({ cwd, exec = spawnSync }
|
|
355
|
-
const run = (cmd
|
|
354
|
+
function resolveIntegrationBase({ cwd, exec = spawnSync } ) {
|
|
355
|
+
const run = (cmd , args ) => {
|
|
356
356
|
try {
|
|
357
357
|
return exec(cmd, args, { cwd, encoding: 'utf8', timeout: 30000 });
|
|
358
|
-
} catch (error
|
|
358
|
+
} catch (error ) {
|
|
359
359
|
return { error };
|
|
360
360
|
}
|
|
361
361
|
};
|
|
362
362
|
const gitState = resolveBashHelper('do-git-state.sh');
|
|
363
363
|
const state = gitState ? run('bash', [gitState, '--json']) : null;
|
|
364
|
-
let ref
|
|
364
|
+
let ref = null;
|
|
365
365
|
if (state && !state.error && state.status === 0) {
|
|
366
366
|
try { ref = JSON.parse(state.stdout).integration_ref || null; } catch { /* reported below */ }
|
|
367
367
|
}
|
|
@@ -386,13 +386,13 @@ function resolveIntegrationBase({ cwd, exec = spawnSync }: { cwd: string; exec?:
|
|
|
386
386
|
* @param {Function} [options.exec] spawnSync-compatible
|
|
387
387
|
* @returns {{ok: true, ref: string, sha: string}|{ok: false, ref: string, reason: string}}
|
|
388
388
|
*/
|
|
389
|
-
function resolveExplicitBaseline({ cwd, ref, exec = spawnSync }
|
|
390
|
-
const fail = (reason
|
|
389
|
+
function resolveExplicitBaseline({ cwd, ref, exec = spawnSync } ) {
|
|
390
|
+
const fail = (reason ) => ({ ok: false, ref, reason });
|
|
391
391
|
if (typeof ref !== 'string' || ref.trim() === '' || ref.startsWith('-')) {
|
|
392
392
|
return fail(`baseline ref '${ref}' is not a usable commit or ref`);
|
|
393
393
|
}
|
|
394
|
-
const run = (args
|
|
395
|
-
try { return exec('git', args, { cwd, encoding: 'utf8', timeout: 30000 }); } catch (error
|
|
394
|
+
const run = (args ) => {
|
|
395
|
+
try { return exec('git', args, { cwd, encoding: 'utf8', timeout: 30000 }); } catch (error ) { return { error }; }
|
|
396
396
|
};
|
|
397
397
|
const parsed = run(['rev-parse', '--verify', '--quiet', '--end-of-options', `${ref}^{commit}`]);
|
|
398
398
|
const sha = parsed && !parsed.error && parsed.status === 0 ? String(parsed.stdout || '').trim() : '';
|
|
@@ -40,18 +40,18 @@ const WIN32_UNQUOTABLE = /[%!]/;
|
|
|
40
40
|
* @param {string} [platform]
|
|
41
41
|
* @returns {string}
|
|
42
42
|
*/
|
|
43
|
-
function quotePath(file
|
|
43
|
+
function quotePath(file , platform = process.platform) {
|
|
44
44
|
if (SAFE_WORD.test(file)) return file;
|
|
45
45
|
if (platform === 'win32') return `"${file.replace(/"/g, '""')}"`;
|
|
46
46
|
return `'${file.replace(/'/g, `'\\''`)}'`;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/** Normalises a repository-relative path: forward slashes, no leading `./`, no trailing `/`. */
|
|
50
|
-
function normalize(file
|
|
50
|
+
function normalize(file ) {
|
|
51
51
|
return String(file).replace(/\\/g, '/').replace(/^(?:\.\/)+/, '').replace(/\/+$/, '');
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
function statOf(fsImpl
|
|
54
|
+
function statOf(fsImpl , file ) {
|
|
55
55
|
try {
|
|
56
56
|
return fsImpl.statSync(file);
|
|
57
57
|
} catch {
|
|
@@ -60,14 +60,14 @@ function statOf(fsImpl: any, file: string): any {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/** Repository-relative paths of every file under `rel` (a file counts as itself). */
|
|
63
|
-
function walkFiles(fsImpl
|
|
63
|
+
function walkFiles(fsImpl , root , rel ) {
|
|
64
64
|
const abs = path.join(root, rel);
|
|
65
65
|
const stat = statOf(fsImpl, abs);
|
|
66
66
|
if (!stat) return [];
|
|
67
67
|
if (stat.isFile()) return [normalize(rel)];
|
|
68
68
|
if (!stat.isDirectory()) return [];
|
|
69
|
-
const out
|
|
70
|
-
let entries
|
|
69
|
+
const out = [];
|
|
70
|
+
let entries ;
|
|
71
71
|
try {
|
|
72
72
|
entries = fsImpl.readdirSync(abs, { withFileTypes: true });
|
|
73
73
|
} catch {
|
|
@@ -88,19 +88,19 @@ function walkFiles(fsImpl: any, root: string, rel: string): string[] {
|
|
|
88
88
|
* @param {{ source?: string, testDirs?: Array<string> }} found the detected `testTargeted` entry
|
|
89
89
|
* @returns {null|{ basis: string, isTest: Function, roots: Array<string>, guardDir: string|null }}
|
|
90
90
|
*/
|
|
91
|
-
function conventionFor(found
|
|
91
|
+
function conventionFor(found ) {
|
|
92
92
|
if (found && found.source === 'package.json' && Array.isArray(found.testDirs) && found.testDirs.length > 0) {
|
|
93
93
|
const roots = found.testDirs.map(normalize).filter(Boolean);
|
|
94
|
-
const underRoot = (file
|
|
94
|
+
const underRoot = (file ) => roots.some((r ) => file === r || file.startsWith(`${r}/`));
|
|
95
95
|
return {
|
|
96
96
|
basis: 'changed-test-files',
|
|
97
97
|
roots,
|
|
98
|
-
isTest: (file
|
|
99
|
-
guardDir: roots.some((r
|
|
98
|
+
isTest: (file ) => NODE_TEST_FILE.test(file) && underRoot(file),
|
|
99
|
+
guardDir: roots.some((r ) => GUARD_DIR === r || GUARD_DIR.startsWith(`${r}/`)) ? GUARD_DIR : null,
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
if (found && found.source === 'pyproject.toml') {
|
|
103
|
-
return { basis: 'changed-test-files', roots: ['.'], isTest: (file
|
|
103
|
+
return { basis: 'changed-test-files', roots: ['.'], isTest: (file ) => PYTEST_FILE.test(file), guardDir: null };
|
|
104
104
|
}
|
|
105
105
|
return null;
|
|
106
106
|
}
|
|
@@ -116,22 +116,22 @@ function conventionFor(found: any): any {
|
|
|
116
116
|
* @returns {null|{ pattern: string|null, selection: { basis: string, selectedCount: number|null,
|
|
117
117
|
* totalCount: number|null, fallback: string|null } }} null when no convention applies to the toolchain
|
|
118
118
|
*/
|
|
119
|
-
function selectChangedTests({ found, observation, root, fsImpl = nodeFs, platform = process.platform }
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
119
|
+
function selectChangedTests({ found, observation, root, fsImpl = nodeFs, platform = process.platform }
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
) {
|
|
126
126
|
const convention = conventionFor(found);
|
|
127
127
|
if (!convention) return null;
|
|
128
128
|
|
|
129
|
-
const total = new Set
|
|
129
|
+
const total = new Set ();
|
|
130
130
|
for (const r of convention.roots) {
|
|
131
131
|
for (const file of walkFiles(fsImpl, root, r)) if (convention.isTest(file)) total.add(file);
|
|
132
132
|
}
|
|
133
133
|
const totalCount = total.size;
|
|
134
|
-
const fallback = (reason
|
|
134
|
+
const fallback = (reason , selectedCount = 0) => ({
|
|
135
135
|
pattern: null,
|
|
136
136
|
selection: { basis: convention.basis, selectedCount, totalCount, fallback: reason },
|
|
137
137
|
});
|
|
@@ -141,22 +141,22 @@ function selectChangedTests({ found, observation, root, fsImpl = nodeFs, platfor
|
|
|
141
141
|
const guardDir = convention.guardDir && statOf(fsImpl, path.join(root, convention.guardDir))?.isDirectory()
|
|
142
142
|
? convention.guardDir
|
|
143
143
|
: null;
|
|
144
|
-
const underGuard = (file
|
|
144
|
+
const underGuard = (file ) => guardDir !== null && file.startsWith(`${guardDir}/`);
|
|
145
145
|
|
|
146
|
-
const changed = [...new Set
|
|
147
|
-
.filter((file
|
|
148
|
-
.filter((file
|
|
146
|
+
const changed = [...new Set (observation.files.map(normalize))]
|
|
147
|
+
.filter((file ) => convention.isTest(file))
|
|
148
|
+
.filter((file ) => statOf(fsImpl, path.join(root, file))?.isFile())
|
|
149
149
|
.sort();
|
|
150
150
|
if (changed.length === 0) return fallback('no-changed-tests');
|
|
151
151
|
|
|
152
152
|
// A changed guard file is already covered by running the guard directory itself.
|
|
153
|
-
const files = changed.filter((file
|
|
153
|
+
const files = changed.filter((file ) => !underGuard(file));
|
|
154
154
|
const guardFiles = guardDir ? [...total].filter(underGuard).length : 0;
|
|
155
155
|
// `cmd.exe` expands `%` and `!` even inside quotes, so such a path cannot be passed safely there.
|
|
156
|
-
if (platform === 'win32' && files.some((file
|
|
156
|
+
if (platform === 'win32' && files.some((file ) => WIN32_UNQUOTABLE.test(file))) return fallback('unsafe-path');
|
|
157
157
|
// A relative path starting with `-` would be read as a runner option, so it gets a directory prefix.
|
|
158
|
-
const asArgument = (file
|
|
159
|
-
const words = [...files.map((file
|
|
158
|
+
const asArgument = (file ) => (file.startsWith('-') ? `./${file}` : file);
|
|
159
|
+
const words = [...files.map((file ) => quotePath(asArgument(file), platform))];
|
|
160
160
|
if (guardDir) words.push(quotePath(`${guardDir}/`, platform));
|
|
161
161
|
return {
|
|
162
162
|
pattern: words.join(' '),
|