@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
|
@@ -40,16 +40,16 @@
|
|
|
40
40
|
|
|
41
41
|
const fs = require('node:fs');
|
|
42
42
|
const path = require('node:path');
|
|
43
|
-
const { CapabilityRouter } = require('./capability-router.
|
|
44
|
-
const { EvidenceLedger, assertSafeTaskId } = require('./evidence-ledger.
|
|
45
|
-
const { taskStoreDir } = require('./task-scope.
|
|
46
|
-
const { probeFreshness } = require('./health.
|
|
43
|
+
const { CapabilityRouter } = require('./capability-router.js');
|
|
44
|
+
const { EvidenceLedger, assertSafeTaskId } = require('./evidence-ledger.js');
|
|
45
|
+
const { taskStoreDir } = require('./task-scope.js');
|
|
46
|
+
const { probeFreshness } = require('./health.js');
|
|
47
47
|
// One definition of the score refusal, applied at a second boundary. See the note on
|
|
48
48
|
// EVIDENCE_SCORE_FIELDS in cli.js: relevance is a property of a search, not of a fact, and a plan
|
|
49
49
|
// that recorded one would hand the next worker a confidence dressed as a measurement.
|
|
50
|
-
const { assertNoScoreFields } = require('./cli.
|
|
51
|
-
const { finishRuntime, usageError } = require('./cli-result.
|
|
52
|
-
const { REPO_ROOT } = require('../helper/repo-root.
|
|
50
|
+
const { assertNoScoreFields } = require('./cli.js');
|
|
51
|
+
const { finishRuntime, usageError } = require('./cli-result.js');
|
|
52
|
+
const { REPO_ROOT } = require('../helper/repo-root.js');
|
|
53
53
|
|
|
54
54
|
// ── the closed result vocabulary (design §4) ─────────────────────────────────────────────────
|
|
55
55
|
|
|
@@ -84,7 +84,7 @@ const FRESHNESS_STATES = Object.freeze(['FRESH', 'STALE', 'UNKNOWN', NOT_APPLICA
|
|
|
84
84
|
* this module exists to prevent — so staleness travels as a marker beside `EMPTY`, and the
|
|
85
85
|
* provider's `refresh` command travels with it so the remedy is one step away.
|
|
86
86
|
*/
|
|
87
|
-
const EMPTY_ANSWER_BY_FRESHNESS
|
|
87
|
+
const EMPTY_ANSWER_BY_FRESHNESS = Object.freeze({
|
|
88
88
|
UNKNOWN: UNVERIFIED,
|
|
89
89
|
STALE: EMPTY,
|
|
90
90
|
FRESH: EMPTY,
|
|
@@ -96,7 +96,7 @@ const EMPTY_ANSWER_BY_FRESHNESS: Record<string, any> = Object.freeze({
|
|
|
96
96
|
* @param {string} freshnessState one of FRESHNESS_STATES
|
|
97
97
|
* @returns {string} one of RESULTS
|
|
98
98
|
*/
|
|
99
|
-
function resultForEmptyAnswer(freshnessState
|
|
99
|
+
function resultForEmptyAnswer(freshnessState ) {
|
|
100
100
|
const mapped = EMPTY_ANSWER_BY_FRESHNESS[freshnessState];
|
|
101
101
|
if (!mapped) {
|
|
102
102
|
// Fail closed rather than pick a default: an unrecognised freshness token means the record was
|
|
@@ -119,7 +119,7 @@ function resultForEmptyAnswer(freshnessState: any) {
|
|
|
119
119
|
* @param {string|null} [slug] the feature the task belongs to, when not the branch's (task-scope.js)
|
|
120
120
|
* @returns {string}
|
|
121
121
|
*/
|
|
122
|
-
function planPath(projectRoot
|
|
122
|
+
function planPath(projectRoot , taskId , slug = null) {
|
|
123
123
|
const dir = taskStoreDir({ projectRoot, store: 'retrieval', taskId: assertSafeTaskId(taskId), slug });
|
|
124
124
|
return path.join(dir, `${taskId}.json`);
|
|
125
125
|
}
|
|
@@ -129,12 +129,12 @@ function planPath(projectRoot: any, taskId: any, slug: any = null) {
|
|
|
129
129
|
* @param {string} taskId
|
|
130
130
|
* @returns {Object|null} the record, or null when no plan was declared
|
|
131
131
|
*/
|
|
132
|
-
function readPlan(projectRoot
|
|
132
|
+
function readPlan(projectRoot , taskId , slug = null) {
|
|
133
133
|
const file = planPath(projectRoot, taskId, slug);
|
|
134
134
|
if (!fs.existsSync(file)) return null;
|
|
135
135
|
try {
|
|
136
136
|
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
137
|
-
} catch (error
|
|
137
|
+
} catch (error ) {
|
|
138
138
|
throw new Error(`the retrieval plan at '${file}' is not readable: ${error.message}`);
|
|
139
139
|
}
|
|
140
140
|
}
|
|
@@ -145,7 +145,7 @@ function readPlan(projectRoot: any, taskId: any, slug: any = null) {
|
|
|
145
145
|
* @param {Object} record
|
|
146
146
|
* @returns {string} the file written
|
|
147
147
|
*/
|
|
148
|
-
function writePlan(projectRoot
|
|
148
|
+
function writePlan(projectRoot , taskId , record , slug = null) {
|
|
149
149
|
const file = planPath(projectRoot, taskId, slug);
|
|
150
150
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
151
151
|
fs.writeFileSync(file, `${JSON.stringify(record, null, 2)}\n`, 'utf8');
|
|
@@ -161,16 +161,16 @@ function writePlan(projectRoot: any, taskId: any, record: any, slug: any = null)
|
|
|
161
161
|
* @param {Object} capabilities the router's `capabilities` map
|
|
162
162
|
* @returns {(providerId: string) => string|null} the declared mode, or null for an unknown id
|
|
163
163
|
*/
|
|
164
|
-
function sourceModeLookup(capabilities
|
|
164
|
+
function sourceModeLookup(capabilities ) {
|
|
165
165
|
const modes = new Map();
|
|
166
|
-
for (const capability of Object.values
|
|
166
|
+
for (const capability of Object.values (capabilities || {})) {
|
|
167
167
|
for (const provider of (capability && capability.providers) || []) {
|
|
168
168
|
if (!provider || !provider.id) continue;
|
|
169
169
|
if (modes.get(provider.id) === 'index') continue;
|
|
170
170
|
modes.set(provider.id, provider.sourceMode || null);
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
|
-
return (providerId
|
|
173
|
+
return (providerId ) => (modes.has(providerId) ? modes.get(providerId) : null);
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/**
|
|
@@ -186,7 +186,7 @@ function sourceModeLookup(capabilities: any) {
|
|
|
186
186
|
* absent, every provider without an artifact layout is treated as unknown (fail closed)
|
|
187
187
|
* @returns {{state: string, artifact: string|null, refresh: string|null, label: string|null, reason: string|null}}
|
|
188
188
|
*/
|
|
189
|
-
function freshnessEntry(providerId
|
|
189
|
+
function freshnessEntry(providerId , projectRoot , sourceModeOf = () => null) {
|
|
190
190
|
const probe = probeFreshness(providerId, { root: projectRoot });
|
|
191
191
|
if (!probe) {
|
|
192
192
|
// `probeFreshness` returns null for a provider it has no artifact layout for. What that means
|
|
@@ -230,9 +230,9 @@ function freshnessEntry(providerId: any, projectRoot: any, sourceModeOf: any = (
|
|
|
230
230
|
* @param {Object} need
|
|
231
231
|
* @returns {string} one of FRESHNESS_STATES
|
|
232
232
|
*/
|
|
233
|
-
function freshnessOf(record
|
|
233
|
+
function freshnessOf(record , need ) {
|
|
234
234
|
if (!need.provider) return NOT_APPLICABLE;
|
|
235
|
-
const entry
|
|
235
|
+
const entry = record.providers && record.providers[need.provider];
|
|
236
236
|
return entry && entry.state ? entry.state : NOT_APPLICABLE;
|
|
237
237
|
}
|
|
238
238
|
|
|
@@ -248,14 +248,14 @@ function freshnessOf(record: any, need: any) {
|
|
|
248
248
|
* @param {Array<Object>} items
|
|
249
249
|
* @returns {Array<string>} evidence ids
|
|
250
250
|
*/
|
|
251
|
-
function evidenceIdsFor(need
|
|
251
|
+
function evidenceIdsFor(need , items ) {
|
|
252
252
|
return items
|
|
253
|
-
.filter((item
|
|
253
|
+
.filter((item ) => {
|
|
254
254
|
const source = item.source || {};
|
|
255
255
|
if (need.provider && source.provider === need.provider) return true;
|
|
256
256
|
return Boolean(need.capability) && source.capability === need.capability;
|
|
257
257
|
})
|
|
258
|
-
.map((item
|
|
258
|
+
.map((item ) => item.id);
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
// ── actions ───────────────────────────────────────────────────────────────────────────────────
|
|
@@ -274,13 +274,13 @@ function evidenceIdsFor(need: any, items: any) {
|
|
|
274
274
|
*
|
|
275
275
|
* @returns {number} exit code
|
|
276
276
|
*/
|
|
277
|
-
function declarePlan({ router, projectRoot, taskId, stage, intents, json, slug }
|
|
277
|
+
function declarePlan({ router, projectRoot, taskId, stage, intents, json, slug } ) {
|
|
278
278
|
const needs = [];
|
|
279
279
|
for (const intent of intents) {
|
|
280
280
|
let resolution;
|
|
281
281
|
try {
|
|
282
282
|
resolution = router.resolveIntent(intent);
|
|
283
|
-
} catch (error
|
|
283
|
+
} catch (error ) {
|
|
284
284
|
// A misspelled intent is a caller error, not an unresolvable need. Recording it as
|
|
285
285
|
// declared-unresolvable would file a typo as a machine limitation and hide it until report.
|
|
286
286
|
return usageError('retrieval-plan',
|
|
@@ -299,8 +299,8 @@ function declarePlan({ router, projectRoot, taskId, stage, intents, json, slug }
|
|
|
299
299
|
|
|
300
300
|
// R8, in one loop: the probe set is the set of DISTINCT providers, so cost is bounded by
|
|
301
301
|
// provider count and not by need count. Six needs across two providers pay two scans.
|
|
302
|
-
const providers
|
|
303
|
-
const sourceModeOf
|
|
302
|
+
const providers = {};
|
|
303
|
+
const sourceModeOf = sourceModeLookup(router.capabilities);
|
|
304
304
|
for (const need of needs) {
|
|
305
305
|
if (!need.provider || providers[need.provider]) continue;
|
|
306
306
|
providers[need.provider] = freshnessEntry(need.provider, projectRoot, sourceModeOf);
|
|
@@ -367,11 +367,11 @@ function declarePlan({ router, projectRoot, taskId, stage, intents, json, slug }
|
|
|
367
367
|
*
|
|
368
368
|
* @returns {number} exit code
|
|
369
369
|
*/
|
|
370
|
-
function reportPlan({ projectRoot, taskId, reached, json, slug }
|
|
370
|
+
function reportPlan({ projectRoot, taskId, reached, json, slug } ) {
|
|
371
371
|
let record;
|
|
372
372
|
try {
|
|
373
373
|
record = readPlan(projectRoot, taskId, slug);
|
|
374
|
-
} catch (error
|
|
374
|
+
} catch (error ) {
|
|
375
375
|
return usageError('retrieval-plan', error.message, json, error);
|
|
376
376
|
}
|
|
377
377
|
if (!record) {
|
|
@@ -381,7 +381,7 @@ function reportPlan({ projectRoot, taskId, reached, json, slug }: any) {
|
|
|
381
381
|
+ 'plan would state that nothing was missed, which is not something an absent plan can establish', json);
|
|
382
382
|
}
|
|
383
383
|
|
|
384
|
-
const declared = new Set(record.needs.map((need
|
|
384
|
+
const declared = new Set(record.needs.map((need ) => need.intent));
|
|
385
385
|
const undeclared = [...reached].filter((intent) => !declared.has(intent)).sort();
|
|
386
386
|
if (undeclared.length > 0) {
|
|
387
387
|
return usageError('retrieval-plan',
|
|
@@ -392,7 +392,7 @@ function reportPlan({ projectRoot, taskId, reached, json, slug }: any) {
|
|
|
392
392
|
const ledger = new EvidenceLedger({ repoRoot: projectRoot, slug });
|
|
393
393
|
try {
|
|
394
394
|
ledger.load(taskId);
|
|
395
|
-
} catch (error
|
|
395
|
+
} catch (error ) {
|
|
396
396
|
return usageError('retrieval-plan', error.message, json, error);
|
|
397
397
|
}
|
|
398
398
|
const items = ledger.queryEvidence({ taskId });
|
|
@@ -409,8 +409,8 @@ function reportPlan({ projectRoot, taskId, reached, json, slug }: any) {
|
|
|
409
409
|
|
|
410
410
|
// Derived views, computed at emit and never stored on a need — a need holds a provider id and
|
|
411
411
|
// nothing about that provider's index (plan D7).
|
|
412
|
-
const byResult = (result
|
|
413
|
-
const stale = record.needs.filter((need
|
|
412
|
+
const byResult = (result ) => record.needs.filter((need ) => need.result === result).map((need ) => need.intent);
|
|
413
|
+
const stale = record.needs.filter((need ) => freshnessOf(record, need) === 'STALE').map((need ) => need.intent);
|
|
414
414
|
const unreached = byResult(UNREACHED);
|
|
415
415
|
const unverified = byResult(UNVERIFIED);
|
|
416
416
|
const incomplete = unreached.length > 0 || unverified.length > 0;
|
|
@@ -465,8 +465,8 @@ function reportPlan({ projectRoot, taskId, reached, json, slug }: any) {
|
|
|
465
465
|
}
|
|
466
466
|
|
|
467
467
|
/** The provider index block both actions print: state, what was located, and the remedy. */
|
|
468
|
-
function printProviderIndexes(record
|
|
469
|
-
const entries = Object.entries
|
|
468
|
+
function printProviderIndexes(record ) {
|
|
469
|
+
const entries = Object.entries (record.providers || {});
|
|
470
470
|
if (entries.length === 0) return;
|
|
471
471
|
console.log('─'.repeat(78));
|
|
472
472
|
console.log('Provider indexes (probed once each, at declare time):');
|
|
@@ -481,9 +481,9 @@ function printProviderIndexes(record: any) {
|
|
|
481
481
|
// ── the verb ──────────────────────────────────────────────────────────────────────────────────
|
|
482
482
|
|
|
483
483
|
/** `--need a,b --need c` and `--need a --need b` are the same list. */
|
|
484
|
-
function parseIntents(raw
|
|
484
|
+
function parseIntents(raw ) {
|
|
485
485
|
const list = Array.isArray(raw) ? raw : (raw === undefined || raw === null ? [] : [raw]);
|
|
486
|
-
const out
|
|
486
|
+
const out = [];
|
|
487
487
|
for (const entry of list) {
|
|
488
488
|
for (const token of String(entry).split(',')) {
|
|
489
489
|
const trimmed = token.trim();
|
|
@@ -507,7 +507,7 @@ function parseIntents(raw: any) {
|
|
|
507
507
|
* @param {string} [options.stateRoot] the project the task belongs to; its state and its index
|
|
508
508
|
* @returns {number} exit code
|
|
509
509
|
*/
|
|
510
|
-
function handleRetrievalPlanCommand(options
|
|
510
|
+
function handleRetrievalPlanCommand(options = {}) {
|
|
511
511
|
const { taskId, action = 'report', need, stage, json = false, repoRoot, stateRoot, slug } = options;
|
|
512
512
|
|
|
513
513
|
// NFR-001, at the boundary where a caller's input first becomes a record. `bin/doflow.js` already
|
|
@@ -515,7 +515,7 @@ function handleRetrievalPlanCommand(options: any = {}) {
|
|
|
515
515
|
// option object, so a second input surface cannot arrive later without inheriting the refusal.
|
|
516
516
|
try {
|
|
517
517
|
assertNoScoreFields(options, '');
|
|
518
|
-
} catch (error
|
|
518
|
+
} catch (error ) {
|
|
519
519
|
return usageError('retrieval-plan', error.message, json, error);
|
|
520
520
|
}
|
|
521
521
|
|
|
@@ -534,7 +534,7 @@ function handleRetrievalPlanCommand(options: any = {}) {
|
|
|
534
534
|
|
|
535
535
|
try {
|
|
536
536
|
assertSafeTaskId(taskId);
|
|
537
|
-
} catch (error
|
|
537
|
+
} catch (error ) {
|
|
538
538
|
return usageError('retrieval-plan', error.message, json, error);
|
|
539
539
|
}
|
|
540
540
|
|
|
@@ -552,7 +552,7 @@ function handleRetrievalPlanCommand(options: any = {}) {
|
|
|
552
552
|
let router;
|
|
553
553
|
try {
|
|
554
554
|
router = new CapabilityRouter({ repoRoot: packageRoot });
|
|
555
|
-
} catch (error
|
|
555
|
+
} catch (error ) {
|
|
556
556
|
return usageError('retrieval-plan', error.message, json, error);
|
|
557
557
|
}
|
|
558
558
|
return declarePlan({ router, projectRoot, taskId, stage, intents, json, slug });
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
/** Returns the body of the first `## …<title>…` section, or null when there is none. */
|
|
16
|
-
function section(text
|
|
16
|
+
function section(text , titlePattern ) {
|
|
17
17
|
const lines = text.split('\n');
|
|
18
18
|
let start = -1;
|
|
19
19
|
for (let i = 0; i < lines.length; i += 1) {
|
|
@@ -28,7 +28,7 @@ function section(text: string, titlePattern: RegExp): string | null {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/** Splits a markdown table row into trimmed cells, or null when the line is not a row. */
|
|
31
|
-
function row(line
|
|
31
|
+
function row(line ) {
|
|
32
32
|
if (!/^\s*\|/.test(line)) return null;
|
|
33
33
|
const cells = line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map((c) => c.trim());
|
|
34
34
|
if (cells.every((c) => /^:?-{3,}:?$/.test(c))) return null; // the header separator
|
|
@@ -36,8 +36,8 @@ function row(line: string): string[] | null {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/** Markdown wraps long list items; a criterion or task split over three lines is still one item. */
|
|
39
|
-
function joinWrapped(body
|
|
40
|
-
const out
|
|
39
|
+
function joinWrapped(body ) {
|
|
40
|
+
const out = [];
|
|
41
41
|
for (const line of body.split('\n')) {
|
|
42
42
|
if (/^\s+\S/.test(line) && out.length && out[out.length - 1].trim() !== '') {
|
|
43
43
|
out[out.length - 1] += ` ${line.trim()}`;
|
|
@@ -52,9 +52,9 @@ function joinWrapped(body: string): string[] {
|
|
|
52
52
|
* `requirement.md` → requirement summaries (for traceability headers) and acceptance criteria
|
|
53
53
|
* (for test stubs).
|
|
54
54
|
*/
|
|
55
|
-
function parseRequirement(text
|
|
56
|
-
const gaps
|
|
57
|
-
const requirements = new Map
|
|
55
|
+
function parseRequirement(text ) {
|
|
56
|
+
const gaps = [];
|
|
57
|
+
const requirements = new Map ();
|
|
58
58
|
|
|
59
59
|
const frBody = section(text, /Functional Requirements/i) ?? '';
|
|
60
60
|
const nfrBody = section(text, /Non-Functional Requirements/i) ?? '';
|
|
@@ -72,7 +72,7 @@ function parseRequirement(text: string): any {
|
|
|
72
72
|
gaps.push({ artifact: 'requirement.md', what: 'requirement index', why: 'no `| FR-nnn | … |` table row found under a Functional Requirements heading' });
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
const criteria
|
|
75
|
+
const criteria = [];
|
|
76
76
|
const acceptanceBody = section(text, /Acceptance Criteria/i);
|
|
77
77
|
if (acceptanceBody === null) {
|
|
78
78
|
gaps.push({ artifact: 'requirement.md', what: 'acceptance criteria', why: 'no "Acceptance Criteria" section — no test stubs can be derived' });
|
|
@@ -96,9 +96,9 @@ function parseRequirement(text: string): any {
|
|
|
96
96
|
* `design.md` → the component index (traceability) and whatever §4 declares as an interface
|
|
97
97
|
* (carried through verbatim rather than paraphrased).
|
|
98
98
|
*/
|
|
99
|
-
function parseDesign(text
|
|
100
|
-
const gaps
|
|
101
|
-
const components = new Map
|
|
99
|
+
function parseDesign(text ) {
|
|
100
|
+
const gaps = [];
|
|
101
|
+
const components = new Map ();
|
|
102
102
|
|
|
103
103
|
const body = section(text, /Components\s*(&|and)\s*Boundaries/i) ?? '';
|
|
104
104
|
for (const line of body.split('\n')) {
|
|
@@ -121,9 +121,9 @@ function parseDesign(text: string): any {
|
|
|
121
121
|
gaps.push({ artifact: 'design.md', what: 'interface contracts', why: 'no "API / Interface Contracts" section — every emitted file falls back to a placeholder signature' });
|
|
122
122
|
} else {
|
|
123
123
|
const lines = contractBody.split('\n');
|
|
124
|
-
let heading
|
|
125
|
-
let fence
|
|
126
|
-
let buffer
|
|
124
|
+
let heading = null;
|
|
125
|
+
let fence = null;
|
|
126
|
+
let buffer = [];
|
|
127
127
|
for (const line of lines) {
|
|
128
128
|
const headingMatch = line.match(/^###\s+(.*)$/);
|
|
129
129
|
if (headingMatch && fence === null) { heading = headingMatch[1].trim(); continue; }
|
|
@@ -155,9 +155,9 @@ function parseDesign(text: string): any {
|
|
|
155
155
|
const TASK_KEYS = ['owner', 'files', 'depends-on', 'external-contract'];
|
|
156
156
|
|
|
157
157
|
/** `plan.md` → the task list, which is where the intended file layout actually comes from. */
|
|
158
|
-
function parsePlan(text
|
|
159
|
-
const gaps
|
|
160
|
-
const tasks
|
|
158
|
+
function parsePlan(text ) {
|
|
159
|
+
const gaps = [];
|
|
160
|
+
const tasks = [];
|
|
161
161
|
|
|
162
162
|
// Anchored on the whole heading: `### Task Summary` is orientation, not the task list, and a
|
|
163
163
|
// loose match would read the summary table's rows as tasks.
|
|
@@ -182,14 +182,14 @@ function parsePlan(text: string): any {
|
|
|
182
182
|
const stories = [...new Set(head.match(/US\d+/g) || [])].sort();
|
|
183
183
|
const description = head.replace(/\[P\]/g, '').replace(/\[US\d+\]/g, '').replace(/\s{2,}/g, ' ').trim();
|
|
184
184
|
|
|
185
|
-
const fields
|
|
185
|
+
const fields = {};
|
|
186
186
|
for (const chunk of meta.split(';')) {
|
|
187
187
|
const fieldMatch = chunk.match(/^\s*([a-z-]+):\s*(.*)$/);
|
|
188
188
|
if (!fieldMatch || !TASK_KEYS.includes(fieldMatch[1])) continue;
|
|
189
189
|
fields[fieldMatch[1]] = fieldMatch[2].trim();
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
const list = (value
|
|
192
|
+
const list = (value ) => (value ? value.split(',').map((v) => v.replace(/`/g, '').trim()).filter(Boolean) : []);
|
|
193
193
|
|
|
194
194
|
tasks.push({
|
|
195
195
|
id,
|
|
@@ -17,7 +17,7 @@ const crypto = require('node:crypto');
|
|
|
17
17
|
const FINGERPRINT_MARKER = 'doflow-scaffold-fingerprint';
|
|
18
18
|
const FINGERPRINT_RE = new RegExp(`${FINGERPRINT_MARKER} sha256:([0-9a-f]{64})`);
|
|
19
19
|
|
|
20
|
-
function sha256(text
|
|
20
|
+
function sha256(text ) {
|
|
21
21
|
return crypto.createHash('sha256').update(text, 'utf8').digest('hex');
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -28,7 +28,7 @@ function sha256(text: string): string {
|
|
|
28
28
|
* reading as human-edited; and it lives in the file rather than in a side ledger, so a scaffold
|
|
29
29
|
* copied, moved or committed on its own still carries the evidence needed to protect it.
|
|
30
30
|
*/
|
|
31
|
-
function compose(emitter
|
|
31
|
+
function compose(emitter , headerLines , body ) {
|
|
32
32
|
const fingerprint = sha256(body);
|
|
33
33
|
const header = [
|
|
34
34
|
...headerLines.map((l) => emitter.line(l)),
|
|
@@ -38,7 +38,7 @@ function compose(emitter: any, headerLines: string[], body: string): string {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/** The hash a file claims for its own body, and the body it actually has. */
|
|
41
|
-
function inspectExisting(text
|
|
41
|
+
function inspectExisting(text ) {
|
|
42
42
|
const match = text.match(FINGERPRINT_RE);
|
|
43
43
|
if (!match) return { claimed: null, actual: null, body: null };
|
|
44
44
|
const cut = text.indexOf('\n', text.indexOf(match[0]));
|