@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
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
// settings/MCP/extensions as first-class native-surface results for the lifecycle UI.
|
|
8
8
|
const fs = require('node:fs');
|
|
9
9
|
const path = require('node:path');
|
|
10
|
-
const { planTree, applyTree, removeTree, verifyTree, copyTreeAssets, copyTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, fingerprint, readJson, sourceDirFor, resolveTransform } = require('../copy-tree.
|
|
11
|
-
const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.
|
|
12
|
-
const { planGeminiHooks, deployGeminiHooks, planRemoveGeminiHooks, deployRemoveGeminiHooks } = require('./hooks.
|
|
10
|
+
const { planTree, applyTree, removeTree, verifyTree, copyTreeAssets, copyTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, fingerprint, readJson, sourceDirFor, resolveTransform } = require('../copy-tree.js');
|
|
11
|
+
const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.js');
|
|
12
|
+
const { planGeminiHooks, deployGeminiHooks, planRemoveGeminiHooks, deployRemoveGeminiHooks } = require('./hooks.js');
|
|
13
13
|
|
|
14
14
|
const MARKER_START = '<!-- doflow:start -->';
|
|
15
15
|
const MARKER_END = '<!-- doflow:end -->';
|
|
@@ -22,12 +22,12 @@ const MARKER_END = '<!-- doflow:end -->';
|
|
|
22
22
|
const DEFAULTS_FILE = path.resolve(__dirname, '../../../core/harnesses/gemini/settings/adapter-defaults.json');
|
|
23
23
|
const HARNESS = 'gemini';
|
|
24
24
|
|
|
25
|
-
function defaults({ fsImpl = fs }
|
|
25
|
+
function defaults({ fsImpl = fs } = {}) { return JSON.parse(fsImpl.readFileSync(DEFAULTS_FILE, 'utf8')); }
|
|
26
26
|
|
|
27
27
|
/** Pure string helper (no path facts): replace only the span between DoFlow's markers in an
|
|
28
28
|
* existing GEMINI.md body. An existing file with no DoFlow section is refused rather than appended
|
|
29
29
|
* to — GEMINI.md is shared with the user and other tools. */
|
|
30
|
-
function managedInstruction(existing
|
|
30
|
+
function managedInstruction(existing , rendered ) {
|
|
31
31
|
if (existing === null) return { ok: true, operation: 'create', content: rendered };
|
|
32
32
|
const start = existing.indexOf(MARKER_START);
|
|
33
33
|
const end = existing.indexOf(MARKER_END);
|
|
@@ -38,7 +38,7 @@ function managedInstruction(existing: any, rendered: any) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/** Server names in one settings read, or null when the file is absent or unreadable. */
|
|
41
|
-
function settingsServerNames(read
|
|
41
|
+
function settingsServerNames(read ) {
|
|
42
42
|
if (!read.exists || read.error || !read.value || typeof read.value !== 'object') return null;
|
|
43
43
|
const servers = read.value.mcpServers;
|
|
44
44
|
return Object.keys(servers && typeof servers === 'object' ? servers : {});
|
|
@@ -50,7 +50,7 @@ function settingsServerNames(read: any) {
|
|
|
50
50
|
* neither can be read. Gemini also merges system settings and extension servers, which DoFlow does
|
|
51
51
|
* not read, so the view is never complete (`mcpComplete: false`) and absence is never proof.
|
|
52
52
|
*/
|
|
53
|
-
function configuredGeminiMcp({ scope, settings, homeDir, fsImpl }
|
|
53
|
+
function configuredGeminiMcp({ scope, settings, homeDir, fsImpl } ) {
|
|
54
54
|
const own = settingsServerNames(settings);
|
|
55
55
|
const user = scope === 'project' && homeDir ? settingsServerNames(readJson(path.join(homeDir, '.gemini', 'settings.json'), { fsImpl })) : null;
|
|
56
56
|
const mcpConfigured = own || user ? [...new Set([...(own || []), ...(user || [])])].sort() : null;
|
|
@@ -66,13 +66,13 @@ function configuredGeminiMcp({ scope, settings, homeDir, fsImpl }: any) {
|
|
|
66
66
|
* historical shape.
|
|
67
67
|
*/
|
|
68
68
|
function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } = {}) {
|
|
69
|
-
function nativePaths({ scope, scopeRoot, homeDir, fsImpl = fs }
|
|
69
|
+
function nativePaths({ scope, scopeRoot, homeDir, fsImpl = fs } ) {
|
|
70
70
|
const resolved = resolveHarnessPaths(declaredPaths, { scope, scopeRoot, homeDir });
|
|
71
71
|
// Non-path native fact carried alongside the resolved surfaces (see DEFAULTS_FILE residue note).
|
|
72
72
|
return { ...resolved, extensionMode: defaults({ fsImpl }).extensionInstall };
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
function discover({ scope, scopeRoot, context = {}, fsImpl = fs }
|
|
75
|
+
function discover({ scope, scopeRoot, context = {}, fsImpl = fs } ) {
|
|
76
76
|
const paths = nativePaths({ scope, scopeRoot, homeDir: context.homeDir, fsImpl });
|
|
77
77
|
const instruction = fsImpl.existsSync(paths.instruction) ? fsImpl.readFileSync(paths.instruction, 'utf8') : null;
|
|
78
78
|
// Gemini takes no servers from DoFlow's MCP catalog.
|
|
@@ -81,7 +81,7 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
81
81
|
return { paths, instruction, settings, mcpOwned: [], mcpConfigured, mcpComplete };
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function render({ content = '' }
|
|
84
|
+
function render({ content = '' } = {}) {
|
|
85
85
|
const body = String(content).trimEnd();
|
|
86
86
|
return `${MARKER_START}\n${body}\n${MARKER_END}\n`;
|
|
87
87
|
}
|
|
@@ -99,7 +99,7 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
99
99
|
* (instruction vs. the guidance tree), not on any single declared path — expressible only by
|
|
100
100
|
* joining and diffing, which is computation, not declaration.
|
|
101
101
|
*/
|
|
102
|
-
function geminiDestDir(paths
|
|
102
|
+
function geminiDestDir(paths , asset ) {
|
|
103
103
|
if (asset.nativeDir && asset.nativeDir.startsWith('../')) {
|
|
104
104
|
const dest = path.join(paths.root, asset.nativeDir.slice(3));
|
|
105
105
|
const rel = path.relative(paths.root, dest);
|
|
@@ -111,8 +111,8 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
111
111
|
return copyTreeDestDir(paths.configDir, asset);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
function rewriteGuidanceImport(content
|
|
115
|
-
const guidance = assets.find((item
|
|
114
|
+
function rewriteGuidanceImport(content , assets , paths ) {
|
|
115
|
+
const guidance = assets.find((item ) => item.id === 'guidance.context-layer');
|
|
116
116
|
if (!guidance?.nativeDir || !paths?.instruction || !paths?.configDir) return content;
|
|
117
117
|
const guidanceDir = geminiDestDir(paths, guidance);
|
|
118
118
|
const rel = path.relative(path.dirname(paths.instruction), guidanceDir).split(path.sep).join('/');
|
|
@@ -123,22 +123,22 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
123
123
|
return content.replace(/@\S*?(DOFLOW_CORE\.md)/g, `@${prefix}/$1`);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
function instructionContent({ assets, context, paths, fsImpl = fs }
|
|
126
|
+
function instructionContent({ assets, context, paths, fsImpl = fs } ) {
|
|
127
127
|
if (context.instructionContent !== undefined) return String(context.instructionContent);
|
|
128
|
-
const asset = assets.find((item
|
|
128
|
+
const asset = assets.find((item ) => item.id === 'guidance.core');
|
|
129
129
|
if (!asset || !context.repoRoot) return null;
|
|
130
130
|
const source = path.resolve(context.repoRoot, asset.source);
|
|
131
131
|
if (!fsImpl.existsSync(source)) return null;
|
|
132
132
|
return rewriteGuidanceImport(fsImpl.readFileSync(source, 'utf8'), assets, paths);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
function policyStatuses(policies
|
|
136
|
-
return policies.map((policy
|
|
135
|
+
function policyStatuses(policies = []) {
|
|
136
|
+
return policies.map((policy ) => ({ id: policy.id, status: 'unavailable', fallback: 'guidance', reason: 'Gemini policy automation is not rendered by this adapter' }));
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
// ---- copy-tree assets (rules, skills, agents, modes, references) ----
|
|
140
140
|
|
|
141
|
-
function planCopyTreeAssets({ assets, scope, scopeRoot, context, ledger, removing, fsImpl = fs }
|
|
141
|
+
function planCopyTreeAssets({ assets, scope, scopeRoot, context, ledger, removing, fsImpl = fs } ) {
|
|
142
142
|
const paths = nativePaths({ scope, scopeRoot, homeDir: context.homeDir, fsImpl });
|
|
143
143
|
const changes = [];
|
|
144
144
|
const conflicts = [];
|
|
@@ -156,10 +156,10 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
156
156
|
// Found by the guard written for the six adapters that forwarded nothing.
|
|
157
157
|
force: !removing && context?.force === true });
|
|
158
158
|
treeResults.push(result);
|
|
159
|
-
conflicts.push(...result.conflicts.map((reason
|
|
159
|
+
conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
|
|
160
160
|
// A hand-edited file at a path DoFlow no longer writes (the agents folder layout) is the
|
|
161
161
|
// user's: it stays, only its ownership row is released.
|
|
162
|
-
notices.push(...result.kept.map((item
|
|
162
|
+
notices.push(...result.kept.map((item ) => `kept hand-edited ${path.relative(paths.root, item.target)}; DoFlow no longer manages it`));
|
|
163
163
|
for (const change of result.changes) {
|
|
164
164
|
changes.push({
|
|
165
165
|
assetId: asset.id, target: change.target, source: change.source, operation: change.operation,
|
|
@@ -175,7 +175,7 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
175
175
|
return { changes, conflicts, notices: [...siblingReplacedNotices(treeResults), ...notices] };
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
function applyCopyTreeAssets(changes
|
|
178
|
+
function applyCopyTreeAssets(changes , { fsImpl = fs } = {}) {
|
|
179
179
|
let applied = 0;
|
|
180
180
|
const grouped = new Map();
|
|
181
181
|
for (const change of changes) {
|
|
@@ -190,13 +190,13 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
190
190
|
return applied;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
function removeCopyTreeAssets(changes
|
|
194
|
-
const treeChanges = changes.filter((change
|
|
195
|
-
.map((change
|
|
193
|
+
function removeCopyTreeAssets(changes , { fsImpl = fs } = {}) {
|
|
194
|
+
const treeChanges = changes.filter((change ) => change.projection?.renderer === 'copy-tree' && change.operation === 'remove' && !change.retained)
|
|
195
|
+
.map((change ) => ({ relPath: change.identity, target: change.target, operation: 'remove', fingerprint: change.fingerprint, pruneGuard: change.pruneGuard }));
|
|
196
196
|
return removeTree({ changes: treeChanges, fsImpl }).removed;
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
function verifyCopyTreeAssets({ assets, scope, scopeRoot, context, fsImpl = fs }
|
|
199
|
+
function verifyCopyTreeAssets({ assets, scope, scopeRoot, context, fsImpl = fs } ) {
|
|
200
200
|
const paths = nativePaths({ scope, scopeRoot, homeDir: context.homeDir, fsImpl });
|
|
201
201
|
const statuses = [];
|
|
202
202
|
const resources = [];
|
|
@@ -205,7 +205,7 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
205
205
|
const destDir = geminiDestDir(paths, asset);
|
|
206
206
|
const sourceDir = sourceDirFor(asset, context, fsImpl, 'Gemini');
|
|
207
207
|
const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, transform: asset.transform, plannedWrites: context?.plannedWrites });
|
|
208
|
-
conflicts.push(...result.conflicts.map((reason
|
|
208
|
+
conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
|
|
209
209
|
for (const resource of result.resources) {
|
|
210
210
|
resources.push({
|
|
211
211
|
assetId: asset.id, target: resource.target, ownershipIdentity: `doflow:gemini:copy-tree:${asset.id}:${resource.relPath}`,
|
|
@@ -225,15 +225,15 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
225
225
|
// integration) — the lifecycle layer's normalizeChange() requires every change's assetId to
|
|
226
226
|
// match a real registered asset, so this piggybacks on guidance.core's id the same way Codex's
|
|
227
227
|
// assetIdFor(options, 'hooks') does, rather than inventing an unregistered 'hooks' id.
|
|
228
|
-
function hooksAssetId(assets
|
|
229
|
-
return assets.find((asset
|
|
228
|
+
function hooksAssetId(assets ) {
|
|
229
|
+
return assets.find((asset ) => asset.id === 'guidance.core')?.id ?? assets[0]?.id;
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
/** Plan the hooks pseudo-component's single change (or none), mirroring the {changes, conflicts}
|
|
233
233
|
* shape planCopyTreeAssets already returns so plan() can fold it in the same way. The hook
|
|
234
234
|
* scripts directory beside settings.json is declared (paths.hooksDirectory); the merge/remove
|
|
235
235
|
* planning itself lives in ./hooks.ts. */
|
|
236
|
-
function planHooksChange({ removing, found, context, assets, fsImpl }
|
|
236
|
+
function planHooksChange({ removing, found, context, assets, fsImpl } ) {
|
|
237
237
|
if (found.settings.error) return { changes: [], conflicts: [] };
|
|
238
238
|
const hooksPlan = removing
|
|
239
239
|
? planRemoveGeminiHooks({ settingsFile: found.paths.settings, fsImpl })
|
|
@@ -248,7 +248,7 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
248
248
|
companionTargets: removing ? [] : (hooksPlan.companionTargets ?? []) }], conflicts: [] };
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
function plan({ scope, scopeRoot, assets = [], mcp = [], discovery, context = {}, ledger, fsImpl = fs }
|
|
251
|
+
function plan({ scope, scopeRoot, assets = [], mcp = [], discovery, context = {}, ledger, fsImpl = fs } ) {
|
|
252
252
|
const found = discovery || discover({ scope, scopeRoot, context, fsImpl });
|
|
253
253
|
const changes = []; const conflicts = [];
|
|
254
254
|
const removing = context.operation === 'remove';
|
|
@@ -256,7 +256,7 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
256
256
|
// Removal only ever targets a section this adapter itself owns — a foreign or absent
|
|
257
257
|
// GEMINI.md yields no change, never a forced deletion of user content.
|
|
258
258
|
const owns = found.instruction !== null && found.instruction.includes(MARKER_START) && found.instruction.includes(MARKER_END);
|
|
259
|
-
if (assets.some((asset
|
|
259
|
+
if (assets.some((asset ) => asset.id === 'guidance.core') && owns) {
|
|
260
260
|
changes.push({ assetId: 'guidance.core', target: found.paths.instruction, operation: 'remove', ownershipIdentity: 'gemini:instructions', projection: { renderer: 'gemini-instructions' } });
|
|
261
261
|
}
|
|
262
262
|
} else {
|
|
@@ -277,14 +277,14 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
277
277
|
surfaces: {
|
|
278
278
|
instructions: { status: 'supported', target: found.paths.instruction },
|
|
279
279
|
settings: { status: found.settings.error ? 'invalid' : 'supported', target: found.paths.settings },
|
|
280
|
-
mcp: { status: found.settings.error ? 'blocked' : 'supported', target: found.paths.settings, selected: mcp.map((item
|
|
280
|
+
mcp: { status: found.settings.error ? 'blocked' : 'supported', target: found.paths.settings, selected: mcp.map((item ) => item.id) },
|
|
281
281
|
extensions: { status: 'different', mode: found.paths.extensionMode, action: 'use Gemini CLI extension management' },
|
|
282
282
|
policies: policyStatuses(context.policies),
|
|
283
283
|
},
|
|
284
284
|
};
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
-
function atomicWrite(file
|
|
287
|
+
function atomicWrite(file , content , { fsImpl = fs } = {}) {
|
|
288
288
|
fsImpl.mkdirSync(path.dirname(file), { recursive: true });
|
|
289
289
|
const temp = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${Date.now()}.tmp`);
|
|
290
290
|
try {
|
|
@@ -294,9 +294,9 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
294
294
|
if (fsImpl.existsSync(temp)) fsImpl.rmSync(temp, { force: true });
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
|
-
function apply({ changes = [], fsImpl = fs }
|
|
297
|
+
function apply({ changes = [], fsImpl = fs } ) {
|
|
298
298
|
for (const change of changes) if (change.content !== undefined) atomicWrite(change.target, change.content, { fsImpl });
|
|
299
|
-
const hooksChange = changes.find((change
|
|
299
|
+
const hooksChange = changes.find((change ) => change.nativeComponent === 'hooks');
|
|
300
300
|
if (hooksChange?.nativePlan) deployGeminiHooks(hooksChange.nativePlan, { fsImpl });
|
|
301
301
|
applyCopyTreeAssets(changes, { fsImpl });
|
|
302
302
|
// An update that relocates an asset (the agents folder layout -> flat files) carries the old
|
|
@@ -307,7 +307,7 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
307
307
|
/** Strip only the DoFlow-managed span, exactly like the Claude adapter's removeManagedSection —
|
|
308
308
|
* a file with user content before/after the managed section must keep that content. Only delete
|
|
309
309
|
* the file outright once nothing but the managed section (and its separator) remains. */
|
|
310
|
-
function removeManagedSection(file
|
|
310
|
+
function removeManagedSection(file , { fsImpl = fs } = {}) {
|
|
311
311
|
if (!fsImpl.existsSync(file)) return false;
|
|
312
312
|
const existing = fsImpl.readFileSync(file, 'utf8');
|
|
313
313
|
const start = existing.indexOf(MARKER_START);
|
|
@@ -324,17 +324,17 @@ function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] }
|
|
|
324
324
|
else fsImpl.writeFileSync(file, next);
|
|
325
325
|
return true;
|
|
326
326
|
}
|
|
327
|
-
function remove({ changes = [], fsImpl = fs }
|
|
327
|
+
function remove({ changes = [], fsImpl = fs } ) {
|
|
328
328
|
for (const change of changes) if (change.target && change.projection?.renderer === 'gemini-instructions') removeManagedSection(change.target, { fsImpl });
|
|
329
|
-
const hooksChange = changes.find((change
|
|
329
|
+
const hooksChange = changes.find((change ) => change.nativeComponent === 'hooks');
|
|
330
330
|
if (hooksChange?.nativePlan) deployRemoveGeminiHooks(hooksChange.nativePlan, { fsImpl });
|
|
331
331
|
removeCopyTreeAssets(changes, { fsImpl });
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
function verify({ scope, scopeRoot, assets = [], context = {}, fsImpl = fs }
|
|
334
|
+
function verify({ scope, scopeRoot, assets = [], context = {}, fsImpl = fs } ) {
|
|
335
335
|
const found = discover({ scope, scopeRoot, context, fsImpl });
|
|
336
336
|
const resources = [];
|
|
337
|
-
if (assets.some((asset
|
|
337
|
+
if (assets.some((asset ) => asset.id === 'guidance.core') && found.instruction?.includes(MARKER_START) && found.instruction.includes(MARKER_END)) {
|
|
338
338
|
resources.push({ assetId: 'guidance.core', target: found.paths.instruction, ownershipIdentity: 'gemini:instructions', fingerprint: fingerprint(found.instruction), sourceVersion: context.sourceVersion ?? 'unknown', projection: { renderer: 'gemini-instructions' } });
|
|
339
339
|
}
|
|
340
340
|
const copyTree = verifyCopyTreeAssets({ assets, scope, scopeRoot, context, fsImpl });
|
|
@@ -370,7 +370,7 @@ const singleton = createGeminiAdapter();
|
|
|
370
370
|
* @param {Object} target a lifecycle status target ({adapterInput, discovery})
|
|
371
371
|
* @returns {*} the plan's trust, or null
|
|
372
372
|
*/
|
|
373
|
-
function hookTrustFor(target
|
|
373
|
+
function hookTrustFor(target ) {
|
|
374
374
|
const context = target.adapterInput?.context || {};
|
|
375
375
|
const settingsFile = target.discovery?.paths?.settings;
|
|
376
376
|
if (!context.geminiHooksSourceFile || !settingsFile || target.discovery?.settings?.error) return null;
|
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
const fs = require('node:fs');
|
|
12
12
|
const path = require('node:path');
|
|
13
13
|
|
|
14
|
-
function commandScriptNames(command
|
|
14
|
+
function commandScriptNames(command ) {
|
|
15
15
|
// DoFlow's wrappers are shell scripts selected from a project or CODEX_HOME hooks directory.
|
|
16
16
|
// Extracting the basename works for both literal and command-substitution based paths.
|
|
17
17
|
return [...new Set((command.match(/[A-Za-z0-9][A-Za-z0-9_-]*\.sh\b/g) || []))];
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function hookHandlers(config
|
|
21
|
-
return Object.entries(config.hooks || {}).flatMap(([event, groups]
|
|
22
|
-
(Array.isArray(groups) ? groups : []).flatMap((group
|
|
23
|
-
(Array.isArray(group?.hooks) ? group.hooks : []).map((handler
|
|
20
|
+
function hookHandlers(config ) {
|
|
21
|
+
return Object.entries(config.hooks || {}).flatMap(([event, groups] ) =>
|
|
22
|
+
(Array.isArray(groups) ? groups : []).flatMap((group ) =>
|
|
23
|
+
(Array.isArray(group?.hooks) ? group.hooks : []).map((handler ) => ({ event, handler }))));
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// On `trusted`: both harnesses gate hook execution behind their own trust prompt — Codex through
|
|
@@ -36,8 +36,8 @@ function hookHandlers(config: any): { event: string, handler: any }[] {
|
|
|
36
36
|
// exec-bit half of the check is POSIX-only; existence is checked everywhere.
|
|
37
37
|
const EXEC_BITS_EXIST = process.platform !== 'win32';
|
|
38
38
|
|
|
39
|
-
function verifyHookCommands(config
|
|
40
|
-
const checks
|
|
39
|
+
function verifyHookCommands(config , { scriptsDir, trusted = false, fsImpl = fs } = {}) {
|
|
40
|
+
const checks = [];
|
|
41
41
|
for (const { event, handler } of hookHandlers(config)) {
|
|
42
42
|
if (!handler || handler.type !== 'command' || typeof handler.command !== 'string' || !handler.command.trim()) continue;
|
|
43
43
|
const names = commandScriptNames(handler.command);
|
|
@@ -54,7 +54,7 @@ function verifyHookCommands(config: any, { scriptsDir, trusted = false, fsImpl =
|
|
|
54
54
|
requiresTrust: true, trusted });
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
return { ok: checks.every((check
|
|
57
|
+
return { ok: checks.every((check ) => check.ok), checks, trust: { required: checks.length > 0, trusted: Boolean(trusted), status: trusted ? 'trusted' : 'review-required' } };
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
module.exports = { commandScriptNames, hookHandlers, verifyHookCommands };
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
// Harness adapters own native formats and paths. This module intentionally only
|
|
6
6
|
// validates and resolves that boundary; it never reads or writes harness files.
|
|
7
|
-
const { resolveRegistrySource } = require('./resolve-source.
|
|
7
|
+
const { resolveRegistrySource } = require('./resolve-source.js');
|
|
8
8
|
|
|
9
9
|
const ADAPTER_METHODS = Object.freeze(['discover', 'render', 'plan', 'apply', 'remove', 'verify']);
|
|
10
10
|
|
|
@@ -12,12 +12,12 @@ const ADAPTER_METHODS = Object.freeze(['discover', 'render', 'plan', 'apply', 'r
|
|
|
12
12
|
* A3: the Codex shape used to be inlined here, putting per-harness knowledge on the shared side of
|
|
13
13
|
* the boundary); this module only dispatches by adapter name. An adapter gaining a
|
|
14
14
|
* nativeProjection declaration registers its normalizer here. */
|
|
15
|
-
const NATIVE_PROJECTION_NORMALIZERS
|
|
16
|
-
codex: () => require('./codex/native-projection.
|
|
15
|
+
const NATIVE_PROJECTION_NORMALIZERS = Object.freeze({
|
|
16
|
+
codex: () => require('./codex/native-projection.js').normalizeNativeProjection,
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
/** Dispatch a harness's nativeProjection declaration to its adapter's own normalizer. */
|
|
20
|
-
function nativeProjectionFor(registry
|
|
20
|
+
function nativeProjectionFor(registry , harness , mcp , context ) {
|
|
21
21
|
if (!harness.nativeProjection) return {};
|
|
22
22
|
const load = NATIVE_PROJECTION_NORMALIZERS[harness.adapter];
|
|
23
23
|
if (!load) {
|
|
@@ -32,9 +32,9 @@ function nativeProjectionFor(registry: any, harness: any, mcp: any, context: any
|
|
|
32
32
|
* adapter receives. Native serializers may add adapter-specific fields, but
|
|
33
33
|
* cannot infer applicability from a missing projection.
|
|
34
34
|
*/
|
|
35
|
-
function projectAdapterInput({ registry, harness, scope, scopeRoot, assets = [], mcp = [], mcpAdoptable = [], policies = [], context = {} }
|
|
35
|
+
function projectAdapterInput({ registry, harness, scope, scopeRoot, assets = [], mcp = [], mcpAdoptable = [], policies = [], context = {} } ) {
|
|
36
36
|
if (!registry || !harness) throw new Error('registry and harness are required for adapter projection');
|
|
37
|
-
const projectAsset = (asset
|
|
37
|
+
const projectAsset = (asset ) => {
|
|
38
38
|
const projection = asset?.projection?.[harness.id];
|
|
39
39
|
if (!projection || typeof projection.renderer !== 'string' || typeof projection.capability !== 'string') {
|
|
40
40
|
throw new Error(`Asset '${asset?.id ?? '?'}' lacks a projection for '${harness.id}'`);
|
|
@@ -62,16 +62,16 @@ function projectAdapterInput({ registry, harness, scope, scopeRoot, assets = [],
|
|
|
62
62
|
return Object.freeze({
|
|
63
63
|
harness, scope, scopeRoot, context,
|
|
64
64
|
assets: Object.freeze(assets.map(projectAsset)),
|
|
65
|
-
mcp: Object.freeze(mcp.map((server
|
|
65
|
+
mcp: Object.freeze(mcp.map((server ) => Object.freeze({ ...server }))),
|
|
66
66
|
// Servers whose existing entries the adapter may take as its own while it holds no MCP row.
|
|
67
|
-
mcpAdoptable: Object.freeze(mcpAdoptable.map((server
|
|
68
|
-
policies: Object.freeze(policies.map((policy
|
|
67
|
+
mcpAdoptable: Object.freeze(mcpAdoptable.map((server ) => Object.freeze({ ...server }))),
|
|
68
|
+
policies: Object.freeze(policies.map((policy ) => Object.freeze({ ...policy }))),
|
|
69
69
|
nativeTargets: Object.freeze({ ...harness.nativeTargets }),
|
|
70
70
|
projection: Object.freeze({ ...projection }),
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
function assertAdapter(adapter
|
|
74
|
+
function assertAdapter(adapter , name = 'adapter') {
|
|
75
75
|
if (!adapter || typeof adapter !== 'object') throw new Error(`${name} must be an object`);
|
|
76
76
|
for (const method of ADAPTER_METHODS) {
|
|
77
77
|
if (typeof adapter[method] !== 'function') throw new Error(`${name} must implement ${method}()`);
|
|
@@ -88,8 +88,8 @@ function createAdapterRegistry(adapters = {}) {
|
|
|
88
88
|
}
|
|
89
89
|
return Object.freeze({
|
|
90
90
|
names: () => [...resolved.keys()],
|
|
91
|
-
has: (name
|
|
92
|
-
get(name
|
|
91
|
+
has: (name ) => resolved.has(name),
|
|
92
|
+
get(name ) {
|
|
93
93
|
const adapter = resolved.get(name);
|
|
94
94
|
if (!adapter) throw new Error(`No registered adapter '${name}'`);
|
|
95
95
|
return adapter;
|
|
@@ -97,7 +97,7 @@ function createAdapterRegistry(adapters = {}) {
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
function resolveAdapter(adapterRegistry
|
|
100
|
+
function resolveAdapter(adapterRegistry , harness ) {
|
|
101
101
|
if (!adapterRegistry || typeof adapterRegistry.get !== 'function') throw new Error('adapterRegistry must expose get(name)');
|
|
102
102
|
if (!harness || typeof harness.adapter !== 'string') throw new Error('Harness must declare an adapter');
|
|
103
103
|
return adapterRegistry.get(harness.adapter);
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
// https://kiro.dev/docs/hooks/
|
|
18
18
|
const fs = require('node:fs');
|
|
19
19
|
const path = require('node:path');
|
|
20
|
-
const { planTree, applyTree, removeTree, verifyTree, resolveTransform, copyTreeAssets, copyTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, sourceDirFor } = require('../copy-tree.
|
|
21
|
-
const { readMcpFiles, planMcpEntries, verifyMcpEntries, ownedMcpIds, writeMcpEntries } = require('../mcp-entries.
|
|
22
|
-
const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.
|
|
20
|
+
const { planTree, applyTree, removeTree, verifyTree, resolveTransform, copyTreeAssets, copyTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, sourceDirFor } = require('../copy-tree.js');
|
|
21
|
+
const { readMcpFiles, planMcpEntries, verifyMcpEntries, ownedMcpIds, writeMcpEntries } = require('../mcp-entries.js');
|
|
22
|
+
const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.js');
|
|
23
23
|
|
|
24
24
|
const HARNESS = 'kiro';
|
|
25
25
|
const MCP_CONTAINER = 'mcpServers';
|
|
@@ -41,13 +41,13 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
41
41
|
// Parity note: Kiro's historical nativePaths() derived even its user-scope root from scopeRoot
|
|
42
42
|
// (the CLI passes $HOME there for -g installs), never from a separate homeDir argument — so the
|
|
43
43
|
// resolver is fed no homeDir and "root" keeps resolving exactly as before.
|
|
44
|
-
function nativePaths({ scope, scopeRoot }
|
|
44
|
+
function nativePaths({ scope, scopeRoot } = {}) {
|
|
45
45
|
return resolveHarnessPaths(declaredPaths, { scope, scopeRoot });
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/** Discovery keeps the MCP files as plan read them (`mcpSnapshot`), which verify compares against,
|
|
49
49
|
* and reports the servers DoFlow owns in Kiro's mcp.json now (`mcpOwned`). */
|
|
50
|
-
function discover({ scope, scopeRoot, mcp = [], mcpAdoptable = [], ledger, fsImpl = fs }
|
|
50
|
+
function discover({ scope, scopeRoot, mcp = [], mcpAdoptable = [], ledger, fsImpl = fs } ) {
|
|
51
51
|
const paths = nativePaths({ scope, scopeRoot });
|
|
52
52
|
const entries = mcpEntriesInput({ paths, mcp, mcpAdoptable, ledger });
|
|
53
53
|
const mcpSnapshot = readMcpFiles({ file: entries.file, ownRows: entries.ownRows, container: MCP_CONTAINER, fsImpl });
|
|
@@ -65,7 +65,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
65
65
|
* `kiro-agents`, so nothing reaches this function. It is kept as a pure passthrough only to
|
|
66
66
|
* satisfy the six-function adapter contract every harness must implement identically.
|
|
67
67
|
*/
|
|
68
|
-
function render({ content = '' }
|
|
68
|
+
function render({ content = '' } = {}) {
|
|
69
69
|
return String(content);
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -84,11 +84,11 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
84
84
|
* `kiro-agent-json` layout and `kiro-agents` transform: kiro-cli discovers agents as
|
|
85
85
|
* `.kiro/agents/<name>.json`, so each shared `<name>.md` spec is rendered, not mirrored.
|
|
86
86
|
*/
|
|
87
|
-
function kiroTreeAssets(assets
|
|
88
|
-
return [...copyTreeAssets(assets), ...(assets || []).filter((asset
|
|
87
|
+
function kiroTreeAssets(assets ) {
|
|
88
|
+
return [...copyTreeAssets(assets), ...(assets || []).filter((asset ) => asset.renderer === 'kiro-agents')];
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
function planCopyTreeAssets({ assets, scope, scopeRoot, context, ledger, removing, fsImpl = fs }
|
|
91
|
+
function planCopyTreeAssets({ assets, scope, scopeRoot, context, ledger, removing, fsImpl = fs } ) {
|
|
92
92
|
const paths = nativePaths({ scope, scopeRoot });
|
|
93
93
|
const changes = [];
|
|
94
94
|
const conflicts = [];
|
|
@@ -105,10 +105,10 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
105
105
|
// is never deleted on removal, forced or not.
|
|
106
106
|
force: !removing && context?.force === true, });
|
|
107
107
|
treeResults.push(result);
|
|
108
|
-
conflicts.push(...result.conflicts.map((reason
|
|
108
|
+
conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
|
|
109
109
|
// A hand-edited file at a path DoFlow no longer writes is the user's: it stays, only its
|
|
110
110
|
// ownership row is released.
|
|
111
|
-
notices.push(...result.kept.map((item
|
|
111
|
+
notices.push(...result.kept.map((item ) => `kept hand-edited ${path.relative(paths.root, item.target)}; DoFlow no longer manages it`));
|
|
112
112
|
for (const change of result.changes) {
|
|
113
113
|
changes.push({
|
|
114
114
|
assetId: asset.id, target: change.target, source: change.source, operation: change.operation,
|
|
@@ -124,7 +124,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
124
124
|
return { changes, conflicts, notices: [...siblingReplacedNotices(treeResults), ...notices] };
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
function applyCopyTreeAssets(changes
|
|
127
|
+
function applyCopyTreeAssets(changes , { fsImpl = fs } = {}) {
|
|
128
128
|
let applied = 0;
|
|
129
129
|
const grouped = new Map();
|
|
130
130
|
for (const change of changes) {
|
|
@@ -139,13 +139,13 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
139
139
|
return applied;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
function removeCopyTreeAssets(changes
|
|
143
|
-
const treeChanges = changes.filter((change
|
|
144
|
-
.map((change
|
|
142
|
+
function removeCopyTreeAssets(changes , { fsImpl = fs } = {}) {
|
|
143
|
+
const treeChanges = changes.filter((change ) => change.kind === 'copy-tree-file' && change.operation === 'remove' && !change.retained)
|
|
144
|
+
.map((change ) => ({ relPath: change.identity, target: change.target, operation: 'remove', fingerprint: change.fingerprint, pruneGuard: change.pruneGuard }));
|
|
145
145
|
return removeTree({ changes: treeChanges, fsImpl }).removed;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
function verifyCopyTreeAssets({ assets, scope, scopeRoot, context, fsImpl = fs }
|
|
148
|
+
function verifyCopyTreeAssets({ assets, scope, scopeRoot, context, fsImpl = fs } ) {
|
|
149
149
|
const paths = nativePaths({ scope, scopeRoot });
|
|
150
150
|
const statuses = [];
|
|
151
151
|
const resources = [];
|
|
@@ -154,7 +154,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
154
154
|
const destDir = copyTreeDestDir(paths.configDir, asset);
|
|
155
155
|
const sourceDir = sourceDirFor(asset, context, fsImpl, 'Kiro');
|
|
156
156
|
const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, transform: asset.transform, plannedWrites: context?.plannedWrites });
|
|
157
|
-
conflicts.push(...result.conflicts.map((reason
|
|
157
|
+
conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
|
|
158
158
|
for (const resource of result.resources) {
|
|
159
159
|
resources.push({
|
|
160
160
|
assetId: asset.id, target: resource.target, ownershipIdentity: `doflow:${HARNESS}:copy-tree:${asset.id}:${resource.relPath}`,
|
|
@@ -177,15 +177,15 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
177
177
|
* registry (core/registry/mcp.json) only ever declares stdio servers today, so only the fields it
|
|
178
178
|
* actually carries are emitted — this never invents a field the registry doesn't supply.
|
|
179
179
|
*/
|
|
180
|
-
function buildServerEntry(server
|
|
180
|
+
function buildServerEntry(server ) {
|
|
181
181
|
if (server.url) return { url: server.url, ...(server.headers ? { headers: server.headers } : {}) };
|
|
182
182
|
return { command: server.command, ...(server.args?.length ? { args: server.args } : {}) };
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
function ownershipIdentity(id
|
|
185
|
+
function ownershipIdentity(id ) { return `doflow:${HARNESS}:mcp-server:${id}`; }
|
|
186
186
|
|
|
187
|
-
function mcpOwnRows(ledger
|
|
188
|
-
return (ledger?.resources || []).filter((row
|
|
187
|
+
function mcpOwnRows(ledger ) {
|
|
188
|
+
return (ledger?.resources || []).filter((row ) => row.harness === HARNESS).flatMap((row ) => {
|
|
189
189
|
if (row.kind === 'mcp-server') {
|
|
190
190
|
return [{ identity: row.identity, target: row.target, fingerprint: row.fingerprint ?? null, ownershipIdentity: row.ownershipIdentity, legacy: false }];
|
|
191
191
|
}
|
|
@@ -196,13 +196,13 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
/** Everything the shared entry-ownership rules (../mcp-entries.ts) need from this adapter. */
|
|
199
|
-
function mcpEntriesInput({ paths, mcp = [], mcpAdoptable = [], ledger, assets = [] }
|
|
200
|
-
const rendered = (servers
|
|
199
|
+
function mcpEntriesInput({ paths, mcp = [], mcpAdoptable = [], ledger, assets = [] } ) {
|
|
200
|
+
const rendered = (servers ) => servers.map((server ) => ({ id: server.id, entry: buildServerEntry(server) }));
|
|
201
201
|
return {
|
|
202
202
|
file: paths.mcp, selected: rendered(mcp), adoptable: rendered(mcpAdoptable),
|
|
203
203
|
ownRows: mcpOwnRows(ledger),
|
|
204
|
-
foreignRows: (ledger?.resources || []).filter((row
|
|
205
|
-
.map((row
|
|
204
|
+
foreignRows: (ledger?.resources || []).filter((row ) => row.kind === 'mcp-server' && row.harness !== HARNESS)
|
|
205
|
+
.map((row ) => ({ harness: row.harness, identity: row.identity, target: row.target })),
|
|
206
206
|
identityFor: ownershipIdentity, assetId: pseudoAssetId(assets), renderer: MCP_RENDERER, label: 'Kiro MCP',
|
|
207
207
|
};
|
|
208
208
|
}
|
|
@@ -210,9 +210,9 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
210
210
|
/** Kiro has no hooks or mcp asset of its own in core/registry/assets.json (mcp servers come from
|
|
211
211
|
* core/registry/mcp.json, not an asset), so an MCP entry change piggybacks on an asset id
|
|
212
212
|
* this harness actually receives — the same "pseudo-component" technique
|
|
213
|
-
* src/adapters/gemini/index.
|
|
214
|
-
function pseudoAssetId(assets
|
|
215
|
-
return assets.find((asset
|
|
213
|
+
* src/adapters/gemini/index.js#hooksAssetId already uses for its own settings-only component. */
|
|
214
|
+
function pseudoAssetId(assets ) {
|
|
215
|
+
return assets.find((asset ) => asset.id === 'guidance.context-layer')?.id ?? assets[0]?.id;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
// ---- shared adapter contract ----
|
|
@@ -226,11 +226,11 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
226
226
|
* `assets` array; per-file installed status for the hooks tree itself is reported through
|
|
227
227
|
* `copyTree.statuses`/`copyTree.resources`, exactly like it is for skills and agents.
|
|
228
228
|
*/
|
|
229
|
-
function hooksSurface(paths
|
|
229
|
+
function hooksSurface(paths ) {
|
|
230
230
|
return { status: 'supported', target: paths.hooks };
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
function plan({ scope, scopeRoot, assets = [], mcp = [], mcpAdoptable = [], discovery, context = {}, ledger, fsImpl = fs }
|
|
233
|
+
function plan({ scope, scopeRoot, assets = [], mcp = [], mcpAdoptable = [], discovery, context = {}, ledger, fsImpl = fs } ) {
|
|
234
234
|
const found = discovery || discover({ scope, scopeRoot, mcp, mcpAdoptable, ledger, fsImpl });
|
|
235
235
|
const changes = [];
|
|
236
236
|
const conflicts = [];
|
|
@@ -253,12 +253,12 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
253
253
|
skills: { status: 'supported', target: found.paths.skills },
|
|
254
254
|
agents: { status: 'supported', target: found.paths.agents },
|
|
255
255
|
hooks: hooksSurface(found.paths),
|
|
256
|
-
mcp: { status: found.mcpSnapshot[found.paths.mcp].ok ? 'supported' : 'blocked', target: found.paths.mcp, selected: mcp.map((item
|
|
256
|
+
mcp: { status: found.mcpSnapshot[found.paths.mcp].ok ? 'supported' : 'blocked', target: found.paths.mcp, selected: mcp.map((item ) => item.id) },
|
|
257
257
|
},
|
|
258
258
|
};
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
function apply({ changes = [], fsImpl = fs }
|
|
261
|
+
function apply({ changes = [], fsImpl = fs } ) {
|
|
262
262
|
writeMcpEntries(changes, { container: MCP_CONTAINER, fsImpl });
|
|
263
263
|
applyCopyTreeAssets(changes, { fsImpl });
|
|
264
264
|
// An update that no longer ships a copy-tree file carries its rows as removals inside the
|
|
@@ -266,12 +266,12 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
|
|
|
266
266
|
removeCopyTreeAssets(changes, { fsImpl });
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
function remove({ changes = [], fsImpl = fs }
|
|
269
|
+
function remove({ changes = [], fsImpl = fs } ) {
|
|
270
270
|
writeMcpEntries(changes, { container: MCP_CONTAINER, fsImpl });
|
|
271
271
|
removeCopyTreeAssets(changes, { fsImpl });
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
function verify({ scope, scopeRoot, assets = [], mcp = [], mcpAdoptable = [], discovery, ledger, operation, context = {}, fsImpl = fs }
|
|
274
|
+
function verify({ scope, scopeRoot, assets = [], mcp = [], mcpAdoptable = [], discovery, ledger, operation, context = {}, fsImpl = fs } ) {
|
|
275
275
|
const paths = nativePaths({ scope, scopeRoot });
|
|
276
276
|
const resources = [];
|
|
277
277
|
const conflicts = [];
|