@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,10 +23,10 @@
|
|
|
23
23
|
const fs = require('node:fs');
|
|
24
24
|
const path = require('node:path');
|
|
25
25
|
|
|
26
|
-
const { MARKER_START, MARKER_END } = require('../../helper/marker-merge.
|
|
27
|
-
const { planTree, applyTree, removeTree, verifyTree, copyTreeAssets, copyTreeDestDir, sharedTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, fingerprint, sourceDirFor } = require('../copy-tree.
|
|
28
|
-
const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.
|
|
29
|
-
const { mcpRows, discoverPiMcp, ownedPiMcpIds, planPiMcp, applyPiMcp, verifyPiMcp } = require('./mcp.
|
|
26
|
+
const { MARKER_START, MARKER_END } = require('../../helper/marker-merge.js');
|
|
27
|
+
const { planTree, applyTree, removeTree, verifyTree, copyTreeAssets, copyTreeDestDir, sharedTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, fingerprint, sourceDirFor } = require('../copy-tree.js');
|
|
28
|
+
const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.js');
|
|
29
|
+
const { mcpRows, discoverPiMcp, ownedPiMcpIds, planPiMcp, applyPiMcp, verifyPiMcp } = require('./mcp.js');
|
|
30
30
|
|
|
31
31
|
const HARNESS = 'pi';
|
|
32
32
|
const AGENT_DIR_NOTICE = 'PI_CODING_AGENT_DIR is set: Pi reads its whole agent dir from it, but DoFlow moves only mcp.json there; skills and AGENTS.md stay in ~/.pi/agent.';
|
|
@@ -40,7 +40,7 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
40
40
|
// Parity note: Pi's historical nativePaths() derived even its user-scope root from scopeRoot
|
|
41
41
|
// (the CLI passes $HOME there for -g installs), never from a separate homeDir argument — so the
|
|
42
42
|
// resolver is fed no homeDir and "root" keeps resolving exactly as before.
|
|
43
|
-
function nativePaths({ scope, scopeRoot }
|
|
43
|
+
function nativePaths({ scope, scopeRoot } = {}) {
|
|
44
44
|
const paths = resolveHarnessPaths(declaredPaths, { scope, scopeRoot });
|
|
45
45
|
// Pi's user-scope mcp.json follows $PI_CODING_AGENT_DIR when set. It is not declared in the
|
|
46
46
|
// registry because the path schema has no environment base (src/helper/harness-paths.js); only
|
|
@@ -54,7 +54,7 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
54
54
|
return typeof agentDir === 'string' && agentDir.trim() !== '' ? agentDir.trim() : null;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function discover({ scope, scopeRoot, mcp = [], ledger, context = {}, fsImpl = fs }
|
|
57
|
+
function discover({ scope, scopeRoot, mcp = [], ledger, context = {}, fsImpl = fs } ) {
|
|
58
58
|
const paths = nativePaths({ scope, scopeRoot });
|
|
59
59
|
const instruction = fsImpl.existsSync(paths.instruction) ? fsImpl.readFileSync(paths.instruction, 'utf8') : null;
|
|
60
60
|
const rows = mcpRows(ledger);
|
|
@@ -62,18 +62,18 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
62
62
|
// Pi reads mcp.json only for a selection or a row; the names in it are read here regardless.
|
|
63
63
|
const file = snapshot[paths.mcp] ?? discoverPiMcp({ selected: [true], rows: [], file: paths.mcp, fsImpl })[paths.mcp];
|
|
64
64
|
const members = file?.exists && file.doc?.ok ? file.doc.servers?.members ?? [] : null;
|
|
65
|
-
const mcpConfigured = members && members.map((member
|
|
65
|
+
const mcpConfigured = members && members.map((member ) => member.key).sort();
|
|
66
66
|
return { paths, instruction, mcp: snapshot, mcpOwned: ownedPiMcpIds({ rows, snapshot }), mcpConfigured };
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
function render({ content = '' }
|
|
69
|
+
function render({ content = '' } = {}) {
|
|
70
70
|
return `${MARKER_START}\n${String(content).trimEnd()}\n${MARKER_END}\n`;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/** Replace only the span between DoFlow's markers. An AGENTS.md with no DoFlow section is refused
|
|
74
74
|
* rather than appended to — Pi shares AGENTS.md with every other agent that reads it, so where
|
|
75
75
|
* DoFlow's content belongs is its owner's decision. Add an empty marker pair to opt in. */
|
|
76
|
-
function managedInstruction(existing
|
|
76
|
+
function managedInstruction(existing , rendered ) {
|
|
77
77
|
if (existing === null) return { ok: true, operation: 'create', content: rendered };
|
|
78
78
|
const start = existing.indexOf(MARKER_START);
|
|
79
79
|
const end = existing.indexOf(MARKER_END);
|
|
@@ -83,7 +83,7 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
83
83
|
return { ok: true, operation: content === existing ? 'none' : 'merge', content };
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
function strippedInstruction(existing
|
|
86
|
+
function strippedInstruction(existing ) {
|
|
87
87
|
if (typeof existing !== 'string') return null;
|
|
88
88
|
const start = existing.indexOf(MARKER_START);
|
|
89
89
|
const end = existing.indexOf(MARKER_END);
|
|
@@ -95,7 +95,7 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
95
95
|
|
|
96
96
|
// ---- copy-tree assets (skills) ----
|
|
97
97
|
|
|
98
|
-
function planCopyTreeAssets({ assets, scope, scopeRoot, context, ledger, removing, fsImpl = fs }
|
|
98
|
+
function planCopyTreeAssets({ assets, scope, scopeRoot, context, ledger, removing, fsImpl = fs } ) {
|
|
99
99
|
const paths = nativePaths({ scope, scopeRoot });
|
|
100
100
|
const changes = [];
|
|
101
101
|
const conflicts = [];
|
|
@@ -112,10 +112,10 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
112
112
|
// is never deleted on removal, forced or not.
|
|
113
113
|
force: !removing && context?.force === true, });
|
|
114
114
|
treeResults.push(result);
|
|
115
|
-
conflicts.push(...result.conflicts.map((reason
|
|
115
|
+
conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
|
|
116
116
|
// A hand-edited file at a path DoFlow no longer writes is the user's: it stays, only its
|
|
117
117
|
// ownership row is released.
|
|
118
|
-
notices.push(...result.kept.map((item
|
|
118
|
+
notices.push(...result.kept.map((item ) => `kept hand-edited ${path.relative(paths.root, item.target)}; DoFlow no longer manages it`));
|
|
119
119
|
for (const change of result.changes) {
|
|
120
120
|
changes.push({
|
|
121
121
|
assetId: asset.id, target: change.target, source: change.source, operation: change.operation,
|
|
@@ -130,19 +130,19 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
130
130
|
return { changes, conflicts, notices: [...siblingReplacedNotices(treeResults), ...notices] };
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
function applyCopyTreeAssets(changes
|
|
134
|
-
const treeChanges = changes.filter((change
|
|
135
|
-
.map((change
|
|
133
|
+
function applyCopyTreeAssets(changes , { fsImpl = fs } = {}) {
|
|
134
|
+
const treeChanges = changes.filter((change ) => change.projection?.renderer === 'copy-tree' && change.operation !== 'remove')
|
|
135
|
+
.map((change ) => ({ relPath: change.identity, target: change.target, source: change.source, operation: change.operation, fingerprint: change.fingerprint }));
|
|
136
136
|
return applyTree({ changes: treeChanges, fsImpl }).applied;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
function removeCopyTreeAssets(changes
|
|
140
|
-
const treeChanges = changes.filter((change
|
|
141
|
-
.map((change
|
|
139
|
+
function removeCopyTreeAssets(changes , { fsImpl = fs } = {}) {
|
|
140
|
+
const treeChanges = changes.filter((change ) => change.kind === 'copy-tree-file' && change.operation === 'remove' && !change.retained)
|
|
141
|
+
.map((change ) => ({ relPath: change.identity, target: change.target, operation: 'remove', fingerprint: change.fingerprint, pruneGuard: change.pruneGuard }));
|
|
142
142
|
return removeTree({ changes: treeChanges, fsImpl }).removed;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
function verifyCopyTreeAssets({ assets, scope, scopeRoot, context, fsImpl = fs }
|
|
145
|
+
function verifyCopyTreeAssets({ assets, scope, scopeRoot, context, fsImpl = fs } ) {
|
|
146
146
|
const paths = nativePaths({ scope, scopeRoot });
|
|
147
147
|
const statuses = [];
|
|
148
148
|
const resources = [];
|
|
@@ -151,7 +151,7 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
151
151
|
const destDir = sharedTreeDestDir(paths.root, asset.nativeDir) ?? copyTreeDestDir(paths.configDir, asset);
|
|
152
152
|
const sourceDir = sourceDirFor(asset, context, fsImpl, 'Pi');
|
|
153
153
|
const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, plannedWrites: context?.plannedWrites });
|
|
154
|
-
conflicts.push(...result.conflicts.map((reason
|
|
154
|
+
conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
|
|
155
155
|
for (const resource of result.resources) {
|
|
156
156
|
resources.push({
|
|
157
157
|
assetId: asset.id, target: resource.target, ownershipIdentity: `doflow:pi:copy-tree:${asset.id}:${resource.relPath}`,
|
|
@@ -167,13 +167,13 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
167
167
|
|
|
168
168
|
// ---- shared adapter contract ----
|
|
169
169
|
|
|
170
|
-
function plan({ scope, scopeRoot, assets = [], mcp = [], discovery, context = {}, ledger, fsImpl = fs }
|
|
170
|
+
function plan({ scope, scopeRoot, assets = [], mcp = [], discovery, context = {}, ledger, fsImpl = fs } ) {
|
|
171
171
|
const found = discovery || discover({ scope, scopeRoot, mcp, ledger, context, fsImpl });
|
|
172
172
|
const changes = [];
|
|
173
173
|
const conflicts = [];
|
|
174
174
|
const removing = context.operation === 'remove';
|
|
175
175
|
|
|
176
|
-
const guidance = assets.find((asset
|
|
176
|
+
const guidance = assets.find((asset ) => asset.capability === 'instructions');
|
|
177
177
|
if (guidance) {
|
|
178
178
|
const rendered = render({ content: fsImpl.readFileSync(path.resolve(context.repoRoot, guidance.source), 'utf8') });
|
|
179
179
|
const outcome = removing
|
|
@@ -200,14 +200,14 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
200
200
|
return { changes, conflicts, notices, paths: found.paths };
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
-
function writeChange(change
|
|
203
|
+
function writeChange(change , fsImpl ) {
|
|
204
204
|
fsImpl.mkdirSync(path.dirname(change.target), { recursive: true });
|
|
205
205
|
fsImpl.writeFileSync(change.target, change.content, 'utf8');
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
function isPiMcp(change
|
|
208
|
+
function isPiMcp(change ) { return change.projection?.renderer === 'pi-mcp'; }
|
|
209
209
|
|
|
210
|
-
function apply({ changes = [], fsImpl = fs }
|
|
210
|
+
function apply({ changes = [], fsImpl = fs } ) {
|
|
211
211
|
// MCP first: its base-hash check refuses a stale plan before any other Pi file is written.
|
|
212
212
|
let applied = applyPiMcp(changes.filter(isPiMcp), { fsImpl }).applied;
|
|
213
213
|
for (const change of changes) {
|
|
@@ -222,7 +222,7 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
222
222
|
return { applied };
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
function remove({ changes = [], fsImpl = fs }
|
|
225
|
+
function remove({ changes = [], fsImpl = fs } ) {
|
|
226
226
|
let removed = applyPiMcp(changes.filter(isPiMcp), { fsImpl }).applied;
|
|
227
227
|
for (const change of changes) {
|
|
228
228
|
if (change.operation !== 'remove' || change.projection?.renderer === 'copy-tree' || isPiMcp(change)) continue;
|
|
@@ -234,12 +234,12 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
|
|
|
234
234
|
return { removed };
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
function verify({ scope, scopeRoot, assets = [], mcp = [], discovery, ledger, context = {}, fsImpl = fs }
|
|
237
|
+
function verify({ scope, scopeRoot, assets = [], mcp = [], discovery, ledger, context = {}, fsImpl = fs } ) {
|
|
238
238
|
const found = discover({ scope, scopeRoot, mcp, ledger, context, fsImpl });
|
|
239
239
|
const statuses = [];
|
|
240
240
|
const resources = [];
|
|
241
241
|
const conflicts = [];
|
|
242
|
-
const instructionsAssetId = assets.find((asset
|
|
242
|
+
const instructionsAssetId = assets.find((asset ) => asset.capability === 'instructions')?.id ?? assets[0]?.id;
|
|
243
243
|
|
|
244
244
|
const hasSection = typeof found.instruction === 'string'
|
|
245
245
|
&& found.instruction.includes(MARKER_START) && found.instruction.includes(MARKER_END);
|
|
@@ -274,6 +274,6 @@ const singleton = createPiAdapter();
|
|
|
274
274
|
module.exports = {
|
|
275
275
|
HARNESS, nativePaths: singleton.nativePaths, discover: singleton.discover, render: singleton.render,
|
|
276
276
|
plan: singleton.plan, apply: singleton.apply, remove: singleton.remove, verify: singleton.verify,
|
|
277
|
-
managedInstruction: (singleton
|
|
277
|
+
managedInstruction: (singleton ).managedInstruction, strippedInstruction: (singleton ).strippedInstruction,
|
|
278
278
|
createPiAdapter,
|
|
279
279
|
};
|
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
const SKELETON = '{\n "mcpServers": {}\n}\n';
|
|
11
11
|
const WHITESPACE = ' \t\n\r';
|
|
12
12
|
|
|
13
|
-
function isPlainObject(value
|
|
13
|
+
function isPlainObject(value ) {
|
|
14
14
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
function skipWhitespace(text
|
|
17
|
+
function skipWhitespace(text , i ) {
|
|
18
18
|
while (i < text.length && WHITESPACE.includes(text[i])) i += 1;
|
|
19
19
|
return i;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/** Index just past the string token that opens at `i`. */
|
|
23
|
-
function stringEnd(text
|
|
23
|
+
function stringEnd(text , i ) {
|
|
24
24
|
for (i += 1; text[i] !== '"'; i += 1) if (text[i] === '\\') i += 1;
|
|
25
25
|
return i + 1;
|
|
26
26
|
}
|
|
@@ -28,7 +28,7 @@ function stringEnd(text: string, i: number): number {
|
|
|
28
28
|
// The text has already passed JSON.parse, so the scanner only has to find spans, never validate.
|
|
29
29
|
// Only the top-level object and mcpServers need member spans; every other value is skipped by
|
|
30
30
|
// counting brackets, without recursion, so a deeply nested file cannot exhaust the stack.
|
|
31
|
-
function valueEnd(text
|
|
31
|
+
function valueEnd(text , i ) {
|
|
32
32
|
const ch = text[i];
|
|
33
33
|
if (ch === '"') return stringEnd(text, i);
|
|
34
34
|
if (ch !== '{' && ch !== '[') {
|
|
@@ -47,8 +47,8 @@ function valueEnd(text: string, i: number): number {
|
|
|
47
47
|
|
|
48
48
|
/** Member spans of the object opening at `open`; `nested` names the one member whose object value
|
|
49
49
|
* is scanned for its own members too. */
|
|
50
|
-
function scanObject(text
|
|
51
|
-
const members
|
|
50
|
+
function scanObject(text , open , nested = null) {
|
|
51
|
+
const members = [];
|
|
52
52
|
let i = skipWhitespace(text, open + 1);
|
|
53
53
|
while (text[i] !== '}') {
|
|
54
54
|
const keyStart = i;
|
|
@@ -66,7 +66,7 @@ function scanObject(text: string, open: number, nested: string | null = null): {
|
|
|
66
66
|
|
|
67
67
|
/** JSON.parse's message quotes the text around the error, which may be a token or a password, so
|
|
68
68
|
* only the position is reported. */
|
|
69
|
-
function invalidJsonReason(text
|
|
69
|
+
function invalidJsonReason(text , error ) {
|
|
70
70
|
const match = /at position (\d+)/.exec(error.message);
|
|
71
71
|
if (!match) return 'invalid JSON';
|
|
72
72
|
const before = text.slice(0, Number(match[1]));
|
|
@@ -75,14 +75,14 @@ function invalidJsonReason(text: string, error: any): string {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/** The whitespace that starts the line `at` sits on, or null when something else precedes it there. */
|
|
78
|
-
function lineIndent(text
|
|
78
|
+
function lineIndent(text , at ) {
|
|
79
79
|
const prefix = text.slice(text.lastIndexOf('\n', at - 1) + 1, at);
|
|
80
80
|
return /^[ \t]*$/.test(prefix) ? prefix : null;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/** The line ending most of the lines in `[start, end)` use, or `fallback` when the span has none:
|
|
84
84
|
* one stray CRLF in an LF file does not turn every inserted line into CRLF. */
|
|
85
|
-
function lineEnding(text
|
|
85
|
+
function lineEnding(text , start , end , fallback ) {
|
|
86
86
|
const span = text.slice(start, end);
|
|
87
87
|
const lines = span.split('\n').length - 1;
|
|
88
88
|
if (!lines) return fallback;
|
|
@@ -91,7 +91,7 @@ function lineEnding(text: string, start: number, end: number, fallback: string):
|
|
|
91
91
|
|
|
92
92
|
/** Attach the formatting a container's new members follow: whether it spans lines, its line ending,
|
|
93
93
|
* the indent of its members and the indent of its closing brace. An empty container takes its parent's. */
|
|
94
|
-
function attachLayout(text
|
|
94
|
+
function attachLayout(text , node , parent , unit , eol ) {
|
|
95
95
|
const parentIndent = parent ? parent.indent : '';
|
|
96
96
|
const empty = node.members.length === 0;
|
|
97
97
|
node.multiline = text.slice(node.open, node.close).includes('\n') || (empty && Boolean(parent?.multiline));
|
|
@@ -100,8 +100,8 @@ function attachLayout(text: string, node: any, parent: any, unit: string, eol: s
|
|
|
100
100
|
node.closeIndent = parentIndent;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
function duplicateKey(node
|
|
104
|
-
const seen = new Set
|
|
103
|
+
function duplicateKey(node ) {
|
|
104
|
+
const seen = new Set ();
|
|
105
105
|
for (const member of node.members) {
|
|
106
106
|
if (seen.has(member.key)) return member.key;
|
|
107
107
|
seen.add(member.key);
|
|
@@ -109,9 +109,9 @@ function duplicateKey(node: any): string | null {
|
|
|
109
109
|
return null;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
function readDocument(text
|
|
112
|
+
function readDocument(text ) {
|
|
113
113
|
let parsed;
|
|
114
|
-
try { parsed = JSON.parse(text); } catch (error
|
|
114
|
+
try { parsed = JSON.parse(text); } catch (error ) { return { ok: false, reason: invalidJsonReason(text, error) }; }
|
|
115
115
|
if (!isPlainObject(parsed)) return { ok: false, reason: 'top level is not an object' };
|
|
116
116
|
if (Object.prototype.hasOwnProperty.call(parsed, 'mcpServers') && !isPlainObject(parsed.mcpServers)) {
|
|
117
117
|
return { ok: false, reason: 'mcpServers is not an object' };
|
|
@@ -120,7 +120,7 @@ function readDocument(text: string): any {
|
|
|
120
120
|
try { root = scanObject(text, skipWhitespace(text, 0), 'mcpServers'); } catch { return { ok: false, reason: 'the file could not be scanned' }; }
|
|
121
121
|
const duplicateTop = duplicateKey(root);
|
|
122
122
|
if (duplicateTop !== null) return { ok: false, reason: `duplicate key '${duplicateTop}'` };
|
|
123
|
-
const servers = root.members.find((member
|
|
123
|
+
const servers = root.members.find((member ) => member.key === 'mcpServers')?.node ?? null;
|
|
124
124
|
const duplicateServer = servers ? duplicateKey(servers) : null;
|
|
125
125
|
if (duplicateServer !== null) return { ok: false, reason: `duplicate key '${duplicateServer}'` };
|
|
126
126
|
|
|
@@ -132,21 +132,21 @@ function readDocument(text: string): any {
|
|
|
132
132
|
return { ok: true, root, servers, eol, unit };
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
function renderValue(value
|
|
135
|
+
function renderValue(value , container , layout ) {
|
|
136
136
|
if (!container.multiline) return JSON.stringify(value);
|
|
137
137
|
return JSON.stringify(value, null, layout.unit).split('\n').join(container.eol + container.indent);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
function renderMember(key
|
|
140
|
+
function renderMember(key , value , container , layout ) {
|
|
141
141
|
return `${JSON.stringify(key)}: ${renderValue(value, container, layout)}`;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
function splice(text
|
|
144
|
+
function splice(text , start , end , inserted ) {
|
|
145
145
|
return text.slice(0, start) + inserted + text.slice(end);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
/** `layout` is the readDocument result the container or member came from. */
|
|
149
|
-
function insertMember(text
|
|
149
|
+
function insertMember(text , container , key , value , layout ) {
|
|
150
150
|
const member = renderMember(key, value, container, layout);
|
|
151
151
|
const last = container.members[container.members.length - 1];
|
|
152
152
|
if (last) {
|
|
@@ -159,12 +159,12 @@ function insertMember(text: string, container: any, key: string, value: any, lay
|
|
|
159
159
|
return splice(text, container.open + 1, container.close, inserted);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
function replaceValue(text
|
|
162
|
+
function replaceValue(text , member , value , layout ) {
|
|
163
163
|
const container = layout.servers?.members.includes(member) ? layout.servers : layout.root;
|
|
164
164
|
return splice(text, member.valueStart, member.valueEnd, renderValue(value, container, layout));
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
function removeMember(text
|
|
167
|
+
function removeMember(text , container , member ) {
|
|
168
168
|
const index = container.members.indexOf(member);
|
|
169
169
|
if (index === -1) throw new Error(`member '${member.key}' is not in this container`);
|
|
170
170
|
if (container.members.length === 1) return splice(text, container.open + 1, container.close, '');
|
|
@@ -173,12 +173,12 @@ function removeMember(text: string, container: any, member: any): string {
|
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
/** Set the text between an empty container's braces, which a removal of its only member drops. */
|
|
176
|
-
function replaceInner(text
|
|
176
|
+
function replaceInner(text , container , inner ) {
|
|
177
177
|
if (container.members.length) throw new Error('only an empty container\'s inner text can be replaced');
|
|
178
178
|
return splice(text, container.open + 1, container.close, inner);
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
function renderNewDocument(entries
|
|
181
|
+
function renderNewDocument(entries ) {
|
|
182
182
|
return `${JSON.stringify({ mcpServers: Object.fromEntries(entries) }, null, 2)}\n`;
|
|
183
183
|
}
|
|
184
184
|
|