@open-agent-toolkit/cli 0.2.62 → 0.2.64
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/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/config-and-local-state.md +1 -0
- package/assets/docs/cli-utilities/configuration.md +10 -8
- package/assets/docs/cli-utilities/project-log.md +16 -0
- package/assets/docs/cli-utilities/tool-packs.md +83 -0
- package/assets/docs/cli-utilities/workflow-gates.md +124 -16
- package/assets/docs/contributing/skills.md +42 -7
- package/assets/docs/provider-sync/commands.md +3 -0
- package/assets/docs/provider-sync/instruction-sync.md +107 -0
- package/assets/docs/provider-sync/manifest-and-drift.md +105 -0
- package/assets/docs/reference/cli-reference.md +16 -6
- package/assets/docs/reference/oat-directory-structure.md +32 -31
- package/assets/docs/workflows/projects/lifecycle.md +45 -0
- package/assets/docs/workflows/projects/picking-up-projects.md +14 -0
- package/assets/docs/workflows/skills/explainer-kit.md +10 -4
- package/assets/docs/workflows/skills/repo-improve.md +15 -1
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/create-agnostic-skill/SKILL.md +9 -7
- package/assets/skills/create-agnostic-skill/references/skill-template.md +9 -7
- package/assets/skills/oat-explainer-kit/SKILL.md +14 -1
- package/assets/skills/oat-explainer-kit/references/config-contract.md +12 -0
- package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +57 -10
- package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +28 -5
- package/assets/skills/oat-explainer-kit/scripts/probe-recap-seams.mjs +371 -0
- package/assets/skills/oat-explainer-kit/scripts/resolve-intent.mjs +123 -1
- package/assets/skills/oat-project-autonomous/SKILL.md +10 -1
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +122 -121
- package/assets/skills/oat-project-complete/SKILL.md +134 -4
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +122 -121
- package/assets/skills/oat-project-implement/SKILL.md +1 -1
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +38 -6
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +122 -121
- package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +122 -121
- package/assets/skills/oat-project-next/SKILL.md +62 -19
- package/assets/skills/oat-project-plan/SKILL.md +31 -5
- package/assets/skills/oat-project-pr-final/SKILL.md +281 -6
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +122 -121
- package/assets/skills/oat-project-progress/SKILL.md +12 -3
- package/assets/skills/oat-project-quick-start/SKILL.md +264 -4
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +122 -121
- package/assets/skills/oat-project-summary/SKILL.md +34 -23
- package/assets/skills/oat-repo-improve/SKILL.md +40 -5
- package/assets/skills/oat-repo-improve/references/plan-template.md +84 -7
- package/dist/agents/canonical/resolve.d.ts.map +1 -1
- package/dist/agents/canonical/resolve.js +25 -4
- package/dist/commands/config/index.d.ts +1 -0
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +401 -18
- package/dist/commands/doctor/index.d.ts +7 -0
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +10 -2
- package/dist/commands/gate/index.d.ts +3 -1
- package/dist/commands/gate/index.d.ts.map +1 -1
- package/dist/commands/gate/index.js +699 -211
- package/dist/commands/init/tools/index.d.ts.map +1 -1
- package/dist/commands/init/tools/index.js +68 -36
- package/dist/commands/init/tools/shared/skill-script-references.d.ts +126 -0
- package/dist/commands/init/tools/shared/skill-script-references.d.ts.map +1 -0
- package/dist/commands/init/tools/shared/skill-script-references.js +311 -0
- package/dist/commands/instructions/instructions.types.d.ts +63 -0
- package/dist/commands/instructions/instructions.types.d.ts.map +1 -1
- package/dist/commands/instructions/instructions.utils.d.ts +51 -2
- package/dist/commands/instructions/instructions.utils.d.ts.map +1 -1
- package/dist/commands/instructions/instructions.utils.js +201 -4
- package/dist/commands/instructions/sync/sync.d.ts.map +1 -1
- package/dist/commands/instructions/sync/sync.js +12 -1
- package/dist/commands/instructions/validate/validate.d.ts.map +1 -1
- package/dist/commands/instructions/validate/validate.js +12 -1
- package/dist/commands/project/log/append.d.ts +195 -0
- package/dist/commands/project/log/append.d.ts.map +1 -1
- package/dist/commands/project/log/append.js +1034 -11
- package/dist/commands/shared/frontmatter.d.ts +51 -0
- package/dist/commands/shared/frontmatter.d.ts.map +1 -1
- package/dist/commands/shared/frontmatter.js +118 -6
- package/dist/commands/status/index.d.ts.map +1 -1
- package/dist/commands/status/index.js +5 -3
- package/dist/commands/sync/index.d.ts +14 -0
- package/dist/commands/sync/index.d.ts.map +1 -1
- package/dist/commands/sync/index.js +21 -0
- package/dist/commands/tools/info/index.d.ts.map +1 -1
- package/dist/commands/tools/info/index.js +14 -1
- package/dist/commands/tools/info/info-tool.d.ts +12 -0
- package/dist/commands/tools/info/info-tool.d.ts.map +1 -1
- package/dist/commands/tools/info/info-tool.js +54 -6
- package/dist/commands/tools/info/skill-views.d.ts +86 -0
- package/dist/commands/tools/info/skill-views.d.ts.map +1 -0
- package/dist/commands/tools/info/skill-views.js +383 -0
- package/dist/commands/tools/install/index.d.ts.map +1 -1
- package/dist/commands/tools/install/index.js +3 -25
- package/dist/commands/tools/list/list-tools.d.ts +2 -0
- package/dist/commands/tools/list/list-tools.d.ts.map +1 -1
- package/dist/commands/tools/list/list-tools.js +23 -4
- package/dist/commands/tools/remove/index.d.ts.map +1 -1
- package/dist/commands/tools/remove/index.js +35 -42
- package/dist/commands/tools/remove/remove-tools.d.ts +3 -2
- package/dist/commands/tools/remove/remove-tools.d.ts.map +1 -1
- package/dist/commands/tools/remove/remove-tools.js +14 -4
- package/dist/commands/tools/shared/auto-sync.d.ts +12 -1
- package/dist/commands/tools/shared/auto-sync.d.ts.map +1 -1
- package/dist/commands/tools/shared/auto-sync.js +20 -4
- package/dist/commands/tools/shared/format-pack-inventory.d.ts +12 -1
- package/dist/commands/tools/shared/format-pack-inventory.d.ts.map +1 -1
- package/dist/commands/tools/shared/format-pack-inventory.js +48 -6
- package/dist/commands/tools/shared/in-process-sync.d.ts +11 -0
- package/dist/commands/tools/shared/in-process-sync.d.ts.map +1 -0
- package/dist/commands/tools/shared/in-process-sync.js +79 -0
- package/dist/commands/tools/shared/pack-evidence.d.ts +78 -2
- package/dist/commands/tools/shared/pack-evidence.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-evidence.js +96 -2
- package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts +11 -0
- package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-lifecycle-outcome.js +24 -3
- package/dist/commands/tools/shared/pack-provider-evidence.d.ts +73 -0
- package/dist/commands/tools/shared/pack-provider-evidence.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-provider-evidence.js +161 -0
- package/dist/commands/tools/shared/provider-context.d.ts +88 -0
- package/dist/commands/tools/shared/provider-context.d.ts.map +1 -0
- package/dist/commands/tools/shared/provider-context.js +77 -0
- package/dist/commands/tools/shared/provider-reachability.d.ts +93 -0
- package/dist/commands/tools/shared/provider-reachability.d.ts.map +1 -0
- package/dist/commands/tools/shared/provider-reachability.js +358 -0
- package/dist/commands/tools/shared/sync-evidence.d.ts +57 -0
- package/dist/commands/tools/shared/sync-evidence.d.ts.map +1 -0
- package/dist/commands/tools/shared/sync-evidence.js +147 -0
- package/dist/commands/tools/update/index.d.ts +0 -4
- package/dist/commands/tools/update/index.d.ts.map +1 -1
- package/dist/commands/tools/update/index.js +22 -40
- package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
- package/dist/commands/tools/update/update-tools.js +23 -8
- package/dist/config/json.d.ts +36 -0
- package/dist/config/json.d.ts.map +1 -1
- package/dist/config/json.js +135 -3
- package/dist/config/oat-config.d.ts +41 -0
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +111 -0
- package/dist/drift/index.d.ts +2 -0
- package/dist/drift/index.d.ts.map +1 -1
- package/dist/drift/index.js +1 -0
- package/dist/drift/skill-view-diagnostic.d.ts +197 -0
- package/dist/drift/skill-view-diagnostic.d.ts.map +1 -0
- package/dist/drift/skill-view-diagnostic.js +343 -0
- package/dist/validation/skills.d.ts +1 -0
- package/dist/validation/skills.d.ts.map +1 -1
- package/dist/validation/skills.js +181 -19
- package/package.json +2 -2
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
const CONTENT_KIND_ORDER = [
|
|
2
|
+
'skill',
|
|
3
|
+
'agent',
|
|
4
|
+
'rule',
|
|
5
|
+
'directory',
|
|
6
|
+
];
|
|
7
|
+
function projectionModeFor(capability) {
|
|
8
|
+
return (capability.projectionModes.find((mode) => mode !== 'unsupported') ?? null);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Maps a registered catalog-refresh policy onto visibility evidence.
|
|
12
|
+
*
|
|
13
|
+
* The switch is exhaustive with a `never` default rather than a fall-through:
|
|
14
|
+
* a policy state added later must fail to compile instead of silently
|
|
15
|
+
* becoming "needs a new session", which would be advice this repository never
|
|
16
|
+
* sourced.
|
|
17
|
+
*/
|
|
18
|
+
function visibilityFor(policy, provider) {
|
|
19
|
+
switch (policy.state) {
|
|
20
|
+
case 'unknown':
|
|
21
|
+
return { state: 'unknown', reason: policy.reason };
|
|
22
|
+
case 'live':
|
|
23
|
+
return {
|
|
24
|
+
state: 'live',
|
|
25
|
+
reason: `${provider} reads the projected content without a refresh`,
|
|
26
|
+
};
|
|
27
|
+
case 'manual-refresh':
|
|
28
|
+
return {
|
|
29
|
+
state: 'manual-refresh',
|
|
30
|
+
reason: `${provider} needs a catalog refresh before the change is visible`,
|
|
31
|
+
};
|
|
32
|
+
case 'restart-required':
|
|
33
|
+
return {
|
|
34
|
+
state: 'restart-required',
|
|
35
|
+
reason: `${provider} needs a new session before the change is visible`,
|
|
36
|
+
};
|
|
37
|
+
default: {
|
|
38
|
+
const unhandled = policy;
|
|
39
|
+
throw new Error(`Unhandled provider catalog refresh state: ${JSON.stringify(unhandled)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function syncRecovery(scope) {
|
|
44
|
+
return {
|
|
45
|
+
code: 'run-sync',
|
|
46
|
+
command: `oat sync --scope ${scope}`,
|
|
47
|
+
message: `Run sync for ${scope} scope to materialize provider views.`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function recoveryFor(input) {
|
|
51
|
+
if (input.activationState === 'inactive') {
|
|
52
|
+
return [
|
|
53
|
+
{
|
|
54
|
+
code: 'enable-provider',
|
|
55
|
+
message: `Enable ${input.provider} in the ${input.scope} sync config to project content to it.`,
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
if (input.support !== 'supported')
|
|
60
|
+
return [];
|
|
61
|
+
if (input.materialization === 'failed') {
|
|
62
|
+
return [
|
|
63
|
+
{
|
|
64
|
+
code: 'retry-sync',
|
|
65
|
+
command: `oat sync --scope ${input.scope}`,
|
|
66
|
+
message: `Re-run sync for ${input.provider} and inspect the reported failure.`,
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
if (input.materialization === 'missing')
|
|
71
|
+
return [syncRecovery(input.scope)];
|
|
72
|
+
if (!input.projected)
|
|
73
|
+
return [];
|
|
74
|
+
if (input.visibility === 'manual-refresh') {
|
|
75
|
+
return [
|
|
76
|
+
{
|
|
77
|
+
code: 'refresh-provider-catalog',
|
|
78
|
+
message: `Refresh the ${input.provider} catalog to see the updated content.`,
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
if (input.visibility === 'restart-required') {
|
|
83
|
+
return [
|
|
84
|
+
{
|
|
85
|
+
code: 'restart-provider-session',
|
|
86
|
+
message: `Start a new ${input.provider} session to see the updated content.`,
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
if (input.visibility === 'unknown') {
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
code: 'verify-provider-visibility',
|
|
94
|
+
message: `Verify in ${input.provider} that the content is listed; no sourced refresh contract is registered.`,
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Projects one reachability row per active-or-registered provider and per
|
|
102
|
+
* content kind the pack places at this scope.
|
|
103
|
+
*/
|
|
104
|
+
export function projectProviderReachability(input) {
|
|
105
|
+
const { providerScopeContext: context } = input;
|
|
106
|
+
const scope = context.scope;
|
|
107
|
+
const active = new Set(context.activeProviders);
|
|
108
|
+
const kinds = CONTENT_KIND_ORDER.filter((kind) => (input.assets[kind]?.length ?? 0) > 0);
|
|
109
|
+
const evidence = [];
|
|
110
|
+
for (const registration of context.registrations) {
|
|
111
|
+
const provider = registration.adapter.name;
|
|
112
|
+
const activation = context.activation.find((candidate) => candidate.provider === provider);
|
|
113
|
+
const activationState = active.has(provider) ? 'active' : 'inactive';
|
|
114
|
+
const capabilities = input.capabilities ?? registration.capabilities;
|
|
115
|
+
for (const contentKind of kinds) {
|
|
116
|
+
const assets = input.assets[contentKind] ?? [];
|
|
117
|
+
const capability = capabilities.find((candidate) => candidate.scope === scope && candidate.contentKind === contentKind);
|
|
118
|
+
const support = capability?.support ?? 'unknown';
|
|
119
|
+
const projectionModes = capability?.projectionModes ?? [];
|
|
120
|
+
const capabilityReason = capability === undefined
|
|
121
|
+
? `${provider} registers no ${scope} ${contentKind} capability`
|
|
122
|
+
: (capability.unsupportedReason ??
|
|
123
|
+
`${provider} projects ${scope} ${contentKind} content via ${projectionModes.join(', ') || 'no mode'}`);
|
|
124
|
+
let materialization;
|
|
125
|
+
if (activationState === 'inactive' || support !== 'supported') {
|
|
126
|
+
materialization = {
|
|
127
|
+
state: 'not-applicable',
|
|
128
|
+
detail: activationState === 'inactive'
|
|
129
|
+
? `${provider} is inactive for ${scope} scope`
|
|
130
|
+
: capabilityReason,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
else if (input.mode === 'lifecycle' && input.syncRan === false) {
|
|
134
|
+
materialization = {
|
|
135
|
+
state: 'not-applicable',
|
|
136
|
+
detail: 'No sync ran for this operation',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
else if (input.mode === 'lifecycle') {
|
|
140
|
+
materialization = observedMaterialization({
|
|
141
|
+
provider,
|
|
142
|
+
scope,
|
|
143
|
+
contentKind,
|
|
144
|
+
assets,
|
|
145
|
+
syncOperationResults: input.syncOperationResults ?? [],
|
|
146
|
+
extensionResults: input.extensionResults ?? [],
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const unmaterialized = input.unmaterializedAssets?.[contentKind] ?? [];
|
|
151
|
+
materialization =
|
|
152
|
+
unmaterialized.length > 0
|
|
153
|
+
? {
|
|
154
|
+
state: 'missing',
|
|
155
|
+
detail: `${provider} has no ${scope} ${contentKind} materialization for ${unmaterialized.length} managed asset(s)`,
|
|
156
|
+
}
|
|
157
|
+
: {
|
|
158
|
+
// A read-only surface observed no sync. The pack inventory can
|
|
159
|
+
// report a positive absence, but its silence is not evidence
|
|
160
|
+
// that the provider view exists: claiming `materialized` here
|
|
161
|
+
// would assert reachability nothing established, which is
|
|
162
|
+
// exactly what DR-260831 forbids. Silence stays a non-claim.
|
|
163
|
+
state: 'not-applicable',
|
|
164
|
+
detail: `No sync was observed for ${provider} ${scope} ${contentKind} content`,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const policy = input.refreshPolicy?.({ provider, scope, contentKind }) ??
|
|
168
|
+
capability?.catalogRefresh ?? {
|
|
169
|
+
state: 'unknown',
|
|
170
|
+
reason: `No ${provider} ${scope} ${contentKind} refresh contract is registered`,
|
|
171
|
+
};
|
|
172
|
+
const reachable = activationState === 'active' && support === 'supported';
|
|
173
|
+
// Projection and materialization already collapse to `not-applicable`
|
|
174
|
+
// for a provider that is inactive or cannot carry this content kind.
|
|
175
|
+
// Visibility follows them: refresh or restart advice about a provider
|
|
176
|
+
// that is not even active reads as guidance the user should act on.
|
|
177
|
+
const visibility = reachable
|
|
178
|
+
? visibilityFor(policy, provider)
|
|
179
|
+
: {
|
|
180
|
+
state: 'not-applicable',
|
|
181
|
+
reason: activationState === 'inactive'
|
|
182
|
+
? `${provider} is inactive for ${scope} scope, so it has no catalog visibility`
|
|
183
|
+
: `${provider} does not project ${scope} ${contentKind} content, so it has no catalog visibility`,
|
|
184
|
+
};
|
|
185
|
+
const projected = materialization.state === 'materialized';
|
|
186
|
+
const projectionState = activationState === 'inactive' || support !== 'supported'
|
|
187
|
+
? 'not-applicable'
|
|
188
|
+
: projected
|
|
189
|
+
? 'projected'
|
|
190
|
+
: materialization.state === 'missing'
|
|
191
|
+
? 'absent'
|
|
192
|
+
: 'not-applicable';
|
|
193
|
+
evidence.push({
|
|
194
|
+
provider,
|
|
195
|
+
scope,
|
|
196
|
+
contentKind,
|
|
197
|
+
assets: materialization.state === 'missing'
|
|
198
|
+
? (input.unmaterializedAssets?.[contentKind] ?? assets)
|
|
199
|
+
: assets,
|
|
200
|
+
activation: {
|
|
201
|
+
state: activationState,
|
|
202
|
+
source: activation?.source ?? 'undetected-unset',
|
|
203
|
+
reason: activation?.reason ??
|
|
204
|
+
`${provider} has no recorded activation evidence`,
|
|
205
|
+
},
|
|
206
|
+
capability: {
|
|
207
|
+
support,
|
|
208
|
+
projectionModes,
|
|
209
|
+
reason: capabilityReason,
|
|
210
|
+
},
|
|
211
|
+
projection: {
|
|
212
|
+
state: projectionState,
|
|
213
|
+
mode: projectionState === 'not-applicable' || capability === undefined
|
|
214
|
+
? null
|
|
215
|
+
: projectionModeFor(capability),
|
|
216
|
+
},
|
|
217
|
+
materialization,
|
|
218
|
+
visibility,
|
|
219
|
+
recovery: recoveryFor({
|
|
220
|
+
provider,
|
|
221
|
+
scope,
|
|
222
|
+
activationState,
|
|
223
|
+
support,
|
|
224
|
+
materialization: materialization.state,
|
|
225
|
+
visibility: visibility.state,
|
|
226
|
+
projected,
|
|
227
|
+
}),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return evidence;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Materialization state observed from a sync run's own operation results.
|
|
235
|
+
*
|
|
236
|
+
* A sync that ran and planned nothing for this provider and content kind is
|
|
237
|
+
* not evidence of a missing projection, so it stays a non-claim rather than
|
|
238
|
+
* becoming a warning that would flip a healthy pack evidence block to
|
|
239
|
+
* `partial`.
|
|
240
|
+
*/
|
|
241
|
+
/**
|
|
242
|
+
* Canonical identity of an asset path.
|
|
243
|
+
*
|
|
244
|
+
* Sync operation results identify an asset by its canonical entry name, while
|
|
245
|
+
* a pack inventory row carries the asset's filesystem path. Comparing the
|
|
246
|
+
* final path segment is what keeps one pack's operation from being attributed
|
|
247
|
+
* to a different pack that merely shares a content kind.
|
|
248
|
+
*/
|
|
249
|
+
function canonicalAssetName(path) {
|
|
250
|
+
return (path.split('/').pop() ?? path).replace(/\.[^.]+$/, '');
|
|
251
|
+
}
|
|
252
|
+
function operationBelongsToAssets(identity, assetNames) {
|
|
253
|
+
// An operation with no usable identity cannot be attributed to a specific
|
|
254
|
+
// pack, so it is deliberately not counted for any of them.
|
|
255
|
+
if (identity === undefined || identity === '')
|
|
256
|
+
return false;
|
|
257
|
+
return assetNames.has(canonicalAssetName(identity));
|
|
258
|
+
}
|
|
259
|
+
function observedMaterialization(input) {
|
|
260
|
+
const { provider, scope, contentKind } = input;
|
|
261
|
+
const assetNames = new Set(input.assets.map(canonicalAssetName));
|
|
262
|
+
const operations = [
|
|
263
|
+
...input.syncOperationResults.filter((result) => result.provider === provider &&
|
|
264
|
+
result.scope === scope &&
|
|
265
|
+
result.contentKind === contentKind &&
|
|
266
|
+
operationBelongsToAssets(result.asset, assetNames)),
|
|
267
|
+
...input.extensionResults.filter((result) => result.provider === provider &&
|
|
268
|
+
result.scope === scope &&
|
|
269
|
+
(result.contentKind === undefined ||
|
|
270
|
+
result.contentKind === contentKind) &&
|
|
271
|
+
// A `config` target is the provider's shared aggregate file, which is
|
|
272
|
+
// not any one pack's asset but which every managed role for this
|
|
273
|
+
// provider depends on. It is therefore attributed provider-wide
|
|
274
|
+
// rather than dropped; a `role` target must still match this row's
|
|
275
|
+
// own assets so one pack's failure cannot be reported against another.
|
|
276
|
+
(result.target === 'config' ||
|
|
277
|
+
operationBelongsToAssets(result.path, assetNames))),
|
|
278
|
+
];
|
|
279
|
+
const failed = operations.filter((operation) => operation.status === 'failed');
|
|
280
|
+
if (failed.length > 0) {
|
|
281
|
+
return {
|
|
282
|
+
state: 'failed',
|
|
283
|
+
detail: `${provider} ${scope} ${contentKind} materialization failed: ${failed
|
|
284
|
+
.map((operation) => operation.failure)
|
|
285
|
+
.filter((failure) => Boolean(failure))
|
|
286
|
+
.join('; ') || 'no failure detail reported'}`,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
// Status is classified explicitly rather than as "anything but failed":
|
|
290
|
+
// `missing`, `unsupported`, `planned`, and `unknown` are not evidence that
|
|
291
|
+
// the provider view now exists.
|
|
292
|
+
const succeeded = operations.filter((operation) => operation.status === 'changed' || operation.status === 'current');
|
|
293
|
+
if (succeeded.length > 0) {
|
|
294
|
+
return {
|
|
295
|
+
state: 'materialized',
|
|
296
|
+
detail: `${succeeded.length} ${provider} ${scope} ${contentKind} operation(s) succeeded`,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
const missing = operations.filter((operation) => operation.status === 'missing');
|
|
300
|
+
if (missing.length > 0) {
|
|
301
|
+
return {
|
|
302
|
+
state: 'missing',
|
|
303
|
+
detail: `${provider} has no ${scope} ${contentKind} projection for ${missing.length} asset(s)`,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
state: 'not-applicable',
|
|
308
|
+
detail: `No ${provider} ${scope} ${contentKind} operation was required`,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Re-projects existing reachability rows with a sync run's observed results.
|
|
313
|
+
*
|
|
314
|
+
* Activation and capability were already derived from the registry before the
|
|
315
|
+
* sync ran and do not change; only projection, materialization, visibility,
|
|
316
|
+
* and recovery are refined. Inactive or unsupported rows are returned
|
|
317
|
+
* untouched.
|
|
318
|
+
*/
|
|
319
|
+
export function applySyncEvidence(providers, input) {
|
|
320
|
+
if (!input.syncRan)
|
|
321
|
+
return [...providers];
|
|
322
|
+
return providers.map((evidence) => {
|
|
323
|
+
if (evidence.activation.state === 'inactive' ||
|
|
324
|
+
evidence.capability.support !== 'supported') {
|
|
325
|
+
return evidence;
|
|
326
|
+
}
|
|
327
|
+
const materialization = observedMaterialization({
|
|
328
|
+
provider: evidence.provider,
|
|
329
|
+
scope: evidence.scope,
|
|
330
|
+
contentKind: evidence.contentKind,
|
|
331
|
+
assets: evidence.assets,
|
|
332
|
+
syncOperationResults: input.syncOperationResults ?? [],
|
|
333
|
+
extensionResults: input.extensionResults ?? [],
|
|
334
|
+
});
|
|
335
|
+
const projected = materialization.state === 'materialized';
|
|
336
|
+
return {
|
|
337
|
+
...evidence,
|
|
338
|
+
projection: {
|
|
339
|
+
state: projected
|
|
340
|
+
? 'projected'
|
|
341
|
+
: materialization.state === 'missing'
|
|
342
|
+
? 'absent'
|
|
343
|
+
: 'not-applicable',
|
|
344
|
+
mode: evidence.projection.mode,
|
|
345
|
+
},
|
|
346
|
+
materialization,
|
|
347
|
+
recovery: recoveryFor({
|
|
348
|
+
provider: evidence.provider,
|
|
349
|
+
scope: evidence.scope,
|
|
350
|
+
activationState: evidence.activation.state,
|
|
351
|
+
support: evidence.capability.support,
|
|
352
|
+
materialization: materialization.state,
|
|
353
|
+
visibility: evidence.visibility.state,
|
|
354
|
+
projected,
|
|
355
|
+
}),
|
|
356
|
+
};
|
|
357
|
+
});
|
|
358
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ManagedContentKind, ProviderCatalogRefreshPolicy } from '../../../providers/shared/registry.js';
|
|
2
|
+
import type { ConcreteScope } from '../../../shared/types.js';
|
|
3
|
+
import type { ProviderExtensionOperationEvidence, ProviderSyncOperationEvidence } from './provider-reachability.js';
|
|
4
|
+
/**
|
|
5
|
+
* Per-provider refresh advice as the sync run reports it.
|
|
6
|
+
*
|
|
7
|
+
* Structurally mirrors the `providerRefreshAdvice` entries in the sync JSON
|
|
8
|
+
* payload. The tools lifecycle consumes that payload; it never reshapes it.
|
|
9
|
+
*/
|
|
10
|
+
export interface SyncProviderRefreshAdvice {
|
|
11
|
+
scope: ConcreteScope;
|
|
12
|
+
provider: string;
|
|
13
|
+
contentKind: ManagedContentKind;
|
|
14
|
+
visibility?: {
|
|
15
|
+
state?: string;
|
|
16
|
+
reason?: string;
|
|
17
|
+
policy?: ProviderCatalogRefreshPolicy;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Normalized evidence from one sync run at one scope. */
|
|
21
|
+
export interface SyncRunEvidence {
|
|
22
|
+
scope: ConcreteScope;
|
|
23
|
+
ran: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The run's aggregate failure count from `summary.failed`.
|
|
26
|
+
*
|
|
27
|
+
* Carried separately because a sync can fail without throwing and without
|
|
28
|
+
* emitting a per-operation `failed` result: a rejected collection counts as
|
|
29
|
+
* a failure but is not a planned operation.
|
|
30
|
+
*/
|
|
31
|
+
failedOperations: number;
|
|
32
|
+
operationResults: readonly ProviderSyncOperationEvidence[];
|
|
33
|
+
extensionResults: readonly ProviderExtensionOperationEvidence[];
|
|
34
|
+
refreshAdvice: readonly SyncProviderRefreshAdvice[];
|
|
35
|
+
}
|
|
36
|
+
export declare function emptySyncRunEvidence(scope: ConcreteScope): SyncRunEvidence;
|
|
37
|
+
/**
|
|
38
|
+
* Normalizes a captured sync JSON payload into reachability inputs.
|
|
39
|
+
*
|
|
40
|
+
* Defensive rather than trusting: the payload shape is owned by the sync
|
|
41
|
+
* command, so an absent or renamed field degrades to "no evidence" instead of
|
|
42
|
+
* throwing inside a lifecycle operation that already succeeded.
|
|
43
|
+
*/
|
|
44
|
+
export declare function normalizeSyncEvidence(scope: ConcreteScope, payload: unknown): SyncRunEvidence;
|
|
45
|
+
/**
|
|
46
|
+
* Builds a refresh-policy lookup from observed sync advice.
|
|
47
|
+
*
|
|
48
|
+
* Returns `undefined` for a provider the run reported nothing about, so the
|
|
49
|
+
* mapper falls back to the registered registry policy rather than inventing
|
|
50
|
+
* one.
|
|
51
|
+
*/
|
|
52
|
+
export declare function refreshPolicyFromAdvice(advice: readonly SyncProviderRefreshAdvice[]): (input: {
|
|
53
|
+
provider: string;
|
|
54
|
+
scope: ConcreteScope;
|
|
55
|
+
contentKind: ManagedContentKind;
|
|
56
|
+
}) => ProviderCatalogRefreshPolicy | undefined;
|
|
57
|
+
//# sourceMappingURL=sync-evidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-evidence.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/shared/sync-evidence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,4BAA4B,EAC7B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EACV,kCAAkC,EAClC,6BAA6B,EAC9B,MAAM,yBAAyB,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,kBAAkB,CAAC;IAChC,UAAU,CAAC,EAAE;QACX,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,4BAA4B,CAAC;KACvC,CAAC;CACH;AAED,0DAA0D;AAC1D,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,CAAC;IACrB,GAAG,EAAE,OAAO,CAAC;IACb;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,SAAS,6BAA6B,EAAE,CAAC;IAC3D,gBAAgB,EAAE,SAAS,kCAAkC,EAAE,CAAC;IAChE,aAAa,EAAE,SAAS,yBAAyB,EAAE,CAAC;CACrD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,eAAe,CAS1E;AAgBD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,OAAO,GACf,eAAe,CA0HjB;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,yBAAyB,EAAE,GAC3C,CAAC,KAAK,EAAE;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,EAAE,kBAAkB,CAAC;CACjC,KAAK,4BAA4B,GAAG,SAAS,CAQ7C"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
export function emptySyncRunEvidence(scope) {
|
|
2
|
+
return {
|
|
3
|
+
scope,
|
|
4
|
+
ran: false,
|
|
5
|
+
failedOperations: 0,
|
|
6
|
+
operationResults: [],
|
|
7
|
+
extensionResults: [],
|
|
8
|
+
refreshAdvice: [],
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function asRecord(value) {
|
|
12
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
13
|
+
? value
|
|
14
|
+
: null;
|
|
15
|
+
}
|
|
16
|
+
function asArray(value) {
|
|
17
|
+
return Array.isArray(value) ? value : [];
|
|
18
|
+
}
|
|
19
|
+
function asString(value) {
|
|
20
|
+
return typeof value === 'string' ? value : undefined;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Normalizes a captured sync JSON payload into reachability inputs.
|
|
24
|
+
*
|
|
25
|
+
* Defensive rather than trusting: the payload shape is owned by the sync
|
|
26
|
+
* command, so an absent or renamed field degrades to "no evidence" instead of
|
|
27
|
+
* throwing inside a lifecycle operation that already succeeded.
|
|
28
|
+
*/
|
|
29
|
+
export function normalizeSyncEvidence(scope, payload) {
|
|
30
|
+
const record = asRecord(payload);
|
|
31
|
+
if (record === null)
|
|
32
|
+
return { ...emptySyncRunEvidence(scope), ran: true };
|
|
33
|
+
const summary = asRecord(record.summary);
|
|
34
|
+
const failedOperations = typeof summary?.failed === 'number' ? summary.failed : 0;
|
|
35
|
+
const operationResults = asArray(record.operationResults).flatMap((entry) => {
|
|
36
|
+
const operation = asRecord(entry);
|
|
37
|
+
const provider = asString(operation?.provider);
|
|
38
|
+
const status = asString(operation?.status);
|
|
39
|
+
// `contentKind` is required on the same footing as `provider` and
|
|
40
|
+
// `status`: defaulting it would let an operation of unknown kind be
|
|
41
|
+
// attributed to a skill row it never touched. An absent field is
|
|
42
|
+
// dropped rather than guessed.
|
|
43
|
+
const contentKind = asString(operation?.contentKind);
|
|
44
|
+
if (!operation ||
|
|
45
|
+
provider === undefined ||
|
|
46
|
+
status === undefined ||
|
|
47
|
+
contentKind === undefined) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
provider,
|
|
53
|
+
scope: asString(operation.scope) ?? scope,
|
|
54
|
+
contentKind: contentKind,
|
|
55
|
+
asset: asString(operation.asset) ?? '',
|
|
56
|
+
status: status,
|
|
57
|
+
...(asString(operation.failure) !== undefined
|
|
58
|
+
? { failure: asString(operation.failure) }
|
|
59
|
+
: {}),
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
});
|
|
63
|
+
const extensionResults = asArray(record.materializationExtensions).flatMap((entry) => {
|
|
64
|
+
const extension = asRecord(entry);
|
|
65
|
+
const provider = asString(extension?.provider);
|
|
66
|
+
if (!extension || provider === undefined)
|
|
67
|
+
return [];
|
|
68
|
+
return asArray(extension.operationResults ?? extension.operations).flatMap((candidate) => {
|
|
69
|
+
const operation = asRecord(candidate);
|
|
70
|
+
const status = asString(operation?.status);
|
|
71
|
+
if (!operation || status === undefined)
|
|
72
|
+
return [];
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
provider,
|
|
76
|
+
scope: asString(extension.scope) ?? scope,
|
|
77
|
+
// Extension operations carry no canonical content kind; the
|
|
78
|
+
// registered extensions materialize agent roles.
|
|
79
|
+
contentKind: 'agent',
|
|
80
|
+
...(asString(operation.target) !== undefined
|
|
81
|
+
? { target: asString(operation.target) }
|
|
82
|
+
: {}),
|
|
83
|
+
...(asString(operation.path) !== undefined
|
|
84
|
+
? { path: asString(operation.path) }
|
|
85
|
+
: {}),
|
|
86
|
+
status: status,
|
|
87
|
+
...(asString(operation.failure) !== undefined
|
|
88
|
+
? { failure: asString(operation.failure) }
|
|
89
|
+
: {}),
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
const refreshAdvice = asArray(record.providerRefreshAdvice).flatMap((entry) => {
|
|
95
|
+
const advice = asRecord(entry);
|
|
96
|
+
const provider = asString(advice?.provider);
|
|
97
|
+
const contentKind = asString(advice?.contentKind);
|
|
98
|
+
if (!advice || provider === undefined || contentKind === undefined) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
const visibility = asRecord(advice.visibility);
|
|
102
|
+
return [
|
|
103
|
+
{
|
|
104
|
+
provider,
|
|
105
|
+
scope: asString(advice.scope) ?? scope,
|
|
106
|
+
contentKind: contentKind,
|
|
107
|
+
...(visibility
|
|
108
|
+
? {
|
|
109
|
+
visibility: {
|
|
110
|
+
...(asString(visibility.state) !== undefined
|
|
111
|
+
? { state: asString(visibility.state) }
|
|
112
|
+
: {}),
|
|
113
|
+
...(asString(visibility.reason) !== undefined
|
|
114
|
+
? { reason: asString(visibility.reason) }
|
|
115
|
+
: {}),
|
|
116
|
+
...(asRecord(visibility.policy)
|
|
117
|
+
? {
|
|
118
|
+
policy: visibility.policy,
|
|
119
|
+
}
|
|
120
|
+
: {}),
|
|
121
|
+
},
|
|
122
|
+
}
|
|
123
|
+
: {}),
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
scope,
|
|
129
|
+
ran: true,
|
|
130
|
+
failedOperations,
|
|
131
|
+
operationResults,
|
|
132
|
+
extensionResults,
|
|
133
|
+
refreshAdvice,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Builds a refresh-policy lookup from observed sync advice.
|
|
138
|
+
*
|
|
139
|
+
* Returns `undefined` for a provider the run reported nothing about, so the
|
|
140
|
+
* mapper falls back to the registered registry policy rather than inventing
|
|
141
|
+
* one.
|
|
142
|
+
*/
|
|
143
|
+
export function refreshPolicyFromAdvice(advice) {
|
|
144
|
+
return ({ provider, scope, contentKind }) => advice.find((candidate) => candidate.provider === provider &&
|
|
145
|
+
candidate.scope === scope &&
|
|
146
|
+
candidate.contentKind === contentKind)?.visibility?.policy;
|
|
147
|
+
}
|
|
@@ -2,10 +2,6 @@ import { type AutoSyncDependencies } from '../../tools/shared/auto-sync.js';
|
|
|
2
2
|
import type { ToolInfo } from '../../tools/shared/types.js';
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import { type UpdateTarget, type UpdateResult, type UpdateToolsDependencies } from './update-tools.js';
|
|
5
|
-
export declare function buildSyncSubprocessArgs(entrypoint: string, execArgv: string[], options: {
|
|
6
|
-
cwd: string;
|
|
7
|
-
scope: 'project' | 'user';
|
|
8
|
-
}): string[];
|
|
9
5
|
export declare function createToolsUpdateCommand(dependencies?: UpdateToolsDependencies, syncDependencies?: AutoSyncDependencies): Command;
|
|
10
6
|
/**
|
|
11
7
|
* @internal Exported for focused unit coverage. Current/newer workflow tools
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/update/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/update/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,kCAAkC,CAAC;AAa1C,OAAO,KAAK,EAAY,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAKvE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAG7B,MAAM,gBAAgB,CAAC;AAgCxB,wBAAgB,wBAAwB,CACtC,YAAY,GAAE,uBAA6C,EAC3D,gBAAgB,GAAE,oBAA8C,GAC/D,OAAO,CAkPT;AAuDD;;;;GAIG;AACH,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAS7E;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,YAAY,GACnB,OAAO,CAOT;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,OAAO,GACd,MAAM,CAMR"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { execFile } from 'node:child_process';
|
|
2
1
|
import { chmod } from 'node:fs/promises';
|
|
3
2
|
import { join } from 'node:path';
|
|
4
3
|
import { buildCommandContext } from '../../../app/command-context.js';
|
|
@@ -8,10 +7,13 @@ import { copyDirWithStatus, copyFileWithStatus, } from '../../init/tools/shared/
|
|
|
8
7
|
import { withScopeOption } from '../../shared/scope-option.js';
|
|
9
8
|
import { readGlobalOptions, resolveConcreteScopes, } from '../../shared/shared.utils.js';
|
|
10
9
|
import { autoSync, } from '../../tools/shared/auto-sync.js';
|
|
10
|
+
import { inProcessSyncDependencies } from '../../tools/shared/in-process-sync.js';
|
|
11
11
|
import { inventoryScopedPack } from '../../tools/shared/pack-inventory.js';
|
|
12
12
|
import { reconcilePackLifecycles } from '../../tools/shared/pack-lifecycle.js';
|
|
13
|
-
import { evaluatePackLifecycleOutcome, } from '../../tools/shared/pack-lifecycle-outcome.js';
|
|
13
|
+
import { evaluatePackLifecycleOutcome, providerSyncOutcomeFromAutoSync, } from '../../tools/shared/pack-lifecycle-outcome.js';
|
|
14
|
+
import { withLifecycleProviderEvidence } from '../../tools/shared/pack-provider-evidence.js';
|
|
14
15
|
import { reconcileProjectToolsConfig } from '../../tools/shared/project-tools-config.js';
|
|
16
|
+
import { applySyncEvidence } from '../../tools/shared/provider-reachability.js';
|
|
15
17
|
import { scanTools } from '../../tools/shared/scan-tools.js';
|
|
16
18
|
import { readOatConfig, writeOatConfig } from '../../../config/oat-config.js';
|
|
17
19
|
import { resolveAssetsRoot } from '../../../fs/assets.js';
|
|
@@ -36,35 +38,7 @@ const defaultDependencies = {
|
|
|
36
38
|
inventoryScopedPack,
|
|
37
39
|
reconcilePacks: reconcilePackLifecycles,
|
|
38
40
|
};
|
|
39
|
-
|
|
40
|
-
// `--scope` is a per-command option on `sync` (not a global flag), so it must
|
|
41
|
-
// come AFTER the `sync` subcommand token. `--cwd` remains a global flag and
|
|
42
|
-
// can precede the subcommand.
|
|
43
|
-
return [
|
|
44
|
-
...execArgv,
|
|
45
|
-
entrypoint,
|
|
46
|
-
'--cwd',
|
|
47
|
-
options.cwd,
|
|
48
|
-
'sync',
|
|
49
|
-
'--scope',
|
|
50
|
-
options.scope,
|
|
51
|
-
];
|
|
52
|
-
}
|
|
53
|
-
const defaultSyncDependencies = {
|
|
54
|
-
runSync: async ({ scope, cwd }) => {
|
|
55
|
-
await new Promise((resolve, reject) => {
|
|
56
|
-
execFile(process.execPath, buildSyncSubprocessArgs(process.argv[1], process.execArgv, {
|
|
57
|
-
cwd,
|
|
58
|
-
scope,
|
|
59
|
-
}), { cwd: process.cwd() }, (error) => {
|
|
60
|
-
if (error)
|
|
61
|
-
reject(error);
|
|
62
|
-
else
|
|
63
|
-
resolve();
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
},
|
|
67
|
-
};
|
|
41
|
+
const defaultSyncDependencies = inProcessSyncDependencies;
|
|
68
42
|
const VALID_PACKS = [
|
|
69
43
|
'core',
|
|
70
44
|
'ideas',
|
|
@@ -270,18 +244,26 @@ function finalizeUpdateLifecycle(lifecycle, syncResults) {
|
|
|
270
244
|
const relevant = syncResults.filter(({ scopes }) => scopes.some((scope) => outcome.selection.targetScopes.includes(scope)));
|
|
271
245
|
if (relevant.length === 0)
|
|
272
246
|
return outcome;
|
|
247
|
+
const merged = {
|
|
248
|
+
synced: relevant.every(({ synced }) => synced),
|
|
249
|
+
scopes: [...new Set(relevant.flatMap(({ scopes }) => scopes))],
|
|
250
|
+
error: relevant.find(({ error }) => error)?.error ?? null,
|
|
251
|
+
evidence: relevant.flatMap(({ evidence }) => evidence),
|
|
252
|
+
};
|
|
253
|
+
// The pre-sync outcome already carries registry-derived activation and
|
|
254
|
+
// capability evidence; the sync run only refines projection and
|
|
255
|
+
// materialization, so the existing rows are re-projected rather than
|
|
256
|
+
// dropped.
|
|
257
|
+
const providers = applySyncEvidence(outcome.sync.providers, {
|
|
258
|
+
syncRan: merged.evidence.some(({ ran }) => ran),
|
|
259
|
+
syncOperationResults: merged.evidence.flatMap(({ operationResults }) => operationResults),
|
|
260
|
+
extensionResults: merged.evidence.flatMap(({ extensionResults }) => extensionResults),
|
|
261
|
+
});
|
|
273
262
|
return evaluatePackLifecycleOutcome({
|
|
274
263
|
selection: outcome.selection,
|
|
275
264
|
lifecycle: outcome.canonical.results,
|
|
276
|
-
sync:
|
|
277
|
-
|
|
278
|
-
status: relevant.every(({ synced }) => synced) ? 'complete' : 'failed',
|
|
279
|
-
providers: [],
|
|
280
|
-
...(relevant.find(({ error }) => error)?.error
|
|
281
|
-
? { error: relevant.find(({ error }) => error).error }
|
|
282
|
-
: {}),
|
|
283
|
-
},
|
|
284
|
-
finalEvidence: outcome.finalEvidence,
|
|
265
|
+
sync: providerSyncOutcomeFromAutoSync(merged, providers),
|
|
266
|
+
finalEvidence: withLifecycleProviderEvidence(outcome.finalEvidence, providers),
|
|
285
267
|
});
|
|
286
268
|
});
|
|
287
269
|
}
|