@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.
Files changed (145) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/config-and-local-state.md +1 -0
  3. package/assets/docs/cli-utilities/configuration.md +10 -8
  4. package/assets/docs/cli-utilities/project-log.md +16 -0
  5. package/assets/docs/cli-utilities/tool-packs.md +83 -0
  6. package/assets/docs/cli-utilities/workflow-gates.md +124 -16
  7. package/assets/docs/contributing/skills.md +42 -7
  8. package/assets/docs/provider-sync/commands.md +3 -0
  9. package/assets/docs/provider-sync/instruction-sync.md +107 -0
  10. package/assets/docs/provider-sync/manifest-and-drift.md +105 -0
  11. package/assets/docs/reference/cli-reference.md +16 -6
  12. package/assets/docs/reference/oat-directory-structure.md +32 -31
  13. package/assets/docs/workflows/projects/lifecycle.md +45 -0
  14. package/assets/docs/workflows/projects/picking-up-projects.md +14 -0
  15. package/assets/docs/workflows/skills/explainer-kit.md +10 -4
  16. package/assets/docs/workflows/skills/repo-improve.md +15 -1
  17. package/assets/public-package-versions.json +4 -4
  18. package/assets/skills/create-agnostic-skill/SKILL.md +9 -7
  19. package/assets/skills/create-agnostic-skill/references/skill-template.md +9 -7
  20. package/assets/skills/oat-explainer-kit/SKILL.md +14 -1
  21. package/assets/skills/oat-explainer-kit/references/config-contract.md +12 -0
  22. package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +57 -10
  23. package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +28 -5
  24. package/assets/skills/oat-explainer-kit/scripts/probe-recap-seams.mjs +371 -0
  25. package/assets/skills/oat-explainer-kit/scripts/resolve-intent.mjs +123 -1
  26. package/assets/skills/oat-project-autonomous/SKILL.md +10 -1
  27. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +122 -121
  28. package/assets/skills/oat-project-complete/SKILL.md +134 -4
  29. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +122 -121
  30. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  31. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +38 -6
  32. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +122 -121
  33. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +122 -121
  34. package/assets/skills/oat-project-next/SKILL.md +62 -19
  35. package/assets/skills/oat-project-plan/SKILL.md +31 -5
  36. package/assets/skills/oat-project-pr-final/SKILL.md +281 -6
  37. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +122 -121
  38. package/assets/skills/oat-project-progress/SKILL.md +12 -3
  39. package/assets/skills/oat-project-quick-start/SKILL.md +264 -4
  40. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +122 -121
  41. package/assets/skills/oat-project-summary/SKILL.md +34 -23
  42. package/assets/skills/oat-repo-improve/SKILL.md +40 -5
  43. package/assets/skills/oat-repo-improve/references/plan-template.md +84 -7
  44. package/dist/agents/canonical/resolve.d.ts.map +1 -1
  45. package/dist/agents/canonical/resolve.js +25 -4
  46. package/dist/commands/config/index.d.ts +1 -0
  47. package/dist/commands/config/index.d.ts.map +1 -1
  48. package/dist/commands/config/index.js +401 -18
  49. package/dist/commands/doctor/index.d.ts +7 -0
  50. package/dist/commands/doctor/index.d.ts.map +1 -1
  51. package/dist/commands/doctor/index.js +10 -2
  52. package/dist/commands/gate/index.d.ts +3 -1
  53. package/dist/commands/gate/index.d.ts.map +1 -1
  54. package/dist/commands/gate/index.js +699 -211
  55. package/dist/commands/init/tools/index.d.ts.map +1 -1
  56. package/dist/commands/init/tools/index.js +68 -36
  57. package/dist/commands/init/tools/shared/skill-script-references.d.ts +126 -0
  58. package/dist/commands/init/tools/shared/skill-script-references.d.ts.map +1 -0
  59. package/dist/commands/init/tools/shared/skill-script-references.js +311 -0
  60. package/dist/commands/instructions/instructions.types.d.ts +63 -0
  61. package/dist/commands/instructions/instructions.types.d.ts.map +1 -1
  62. package/dist/commands/instructions/instructions.utils.d.ts +51 -2
  63. package/dist/commands/instructions/instructions.utils.d.ts.map +1 -1
  64. package/dist/commands/instructions/instructions.utils.js +201 -4
  65. package/dist/commands/instructions/sync/sync.d.ts.map +1 -1
  66. package/dist/commands/instructions/sync/sync.js +12 -1
  67. package/dist/commands/instructions/validate/validate.d.ts.map +1 -1
  68. package/dist/commands/instructions/validate/validate.js +12 -1
  69. package/dist/commands/project/log/append.d.ts +195 -0
  70. package/dist/commands/project/log/append.d.ts.map +1 -1
  71. package/dist/commands/project/log/append.js +1034 -11
  72. package/dist/commands/shared/frontmatter.d.ts +51 -0
  73. package/dist/commands/shared/frontmatter.d.ts.map +1 -1
  74. package/dist/commands/shared/frontmatter.js +118 -6
  75. package/dist/commands/status/index.d.ts.map +1 -1
  76. package/dist/commands/status/index.js +5 -3
  77. package/dist/commands/sync/index.d.ts +14 -0
  78. package/dist/commands/sync/index.d.ts.map +1 -1
  79. package/dist/commands/sync/index.js +21 -0
  80. package/dist/commands/tools/info/index.d.ts.map +1 -1
  81. package/dist/commands/tools/info/index.js +14 -1
  82. package/dist/commands/tools/info/info-tool.d.ts +12 -0
  83. package/dist/commands/tools/info/info-tool.d.ts.map +1 -1
  84. package/dist/commands/tools/info/info-tool.js +54 -6
  85. package/dist/commands/tools/info/skill-views.d.ts +86 -0
  86. package/dist/commands/tools/info/skill-views.d.ts.map +1 -0
  87. package/dist/commands/tools/info/skill-views.js +383 -0
  88. package/dist/commands/tools/install/index.d.ts.map +1 -1
  89. package/dist/commands/tools/install/index.js +3 -25
  90. package/dist/commands/tools/list/list-tools.d.ts +2 -0
  91. package/dist/commands/tools/list/list-tools.d.ts.map +1 -1
  92. package/dist/commands/tools/list/list-tools.js +23 -4
  93. package/dist/commands/tools/remove/index.d.ts.map +1 -1
  94. package/dist/commands/tools/remove/index.js +35 -42
  95. package/dist/commands/tools/remove/remove-tools.d.ts +3 -2
  96. package/dist/commands/tools/remove/remove-tools.d.ts.map +1 -1
  97. package/dist/commands/tools/remove/remove-tools.js +14 -4
  98. package/dist/commands/tools/shared/auto-sync.d.ts +12 -1
  99. package/dist/commands/tools/shared/auto-sync.d.ts.map +1 -1
  100. package/dist/commands/tools/shared/auto-sync.js +20 -4
  101. package/dist/commands/tools/shared/format-pack-inventory.d.ts +12 -1
  102. package/dist/commands/tools/shared/format-pack-inventory.d.ts.map +1 -1
  103. package/dist/commands/tools/shared/format-pack-inventory.js +48 -6
  104. package/dist/commands/tools/shared/in-process-sync.d.ts +11 -0
  105. package/dist/commands/tools/shared/in-process-sync.d.ts.map +1 -0
  106. package/dist/commands/tools/shared/in-process-sync.js +79 -0
  107. package/dist/commands/tools/shared/pack-evidence.d.ts +78 -2
  108. package/dist/commands/tools/shared/pack-evidence.d.ts.map +1 -1
  109. package/dist/commands/tools/shared/pack-evidence.js +96 -2
  110. package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts +11 -0
  111. package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts.map +1 -1
  112. package/dist/commands/tools/shared/pack-lifecycle-outcome.js +24 -3
  113. package/dist/commands/tools/shared/pack-provider-evidence.d.ts +73 -0
  114. package/dist/commands/tools/shared/pack-provider-evidence.d.ts.map +1 -0
  115. package/dist/commands/tools/shared/pack-provider-evidence.js +161 -0
  116. package/dist/commands/tools/shared/provider-context.d.ts +88 -0
  117. package/dist/commands/tools/shared/provider-context.d.ts.map +1 -0
  118. package/dist/commands/tools/shared/provider-context.js +77 -0
  119. package/dist/commands/tools/shared/provider-reachability.d.ts +93 -0
  120. package/dist/commands/tools/shared/provider-reachability.d.ts.map +1 -0
  121. package/dist/commands/tools/shared/provider-reachability.js +358 -0
  122. package/dist/commands/tools/shared/sync-evidence.d.ts +57 -0
  123. package/dist/commands/tools/shared/sync-evidence.d.ts.map +1 -0
  124. package/dist/commands/tools/shared/sync-evidence.js +147 -0
  125. package/dist/commands/tools/update/index.d.ts +0 -4
  126. package/dist/commands/tools/update/index.d.ts.map +1 -1
  127. package/dist/commands/tools/update/index.js +22 -40
  128. package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
  129. package/dist/commands/tools/update/update-tools.js +23 -8
  130. package/dist/config/json.d.ts +36 -0
  131. package/dist/config/json.d.ts.map +1 -1
  132. package/dist/config/json.js +135 -3
  133. package/dist/config/oat-config.d.ts +41 -0
  134. package/dist/config/oat-config.d.ts.map +1 -1
  135. package/dist/config/oat-config.js +111 -0
  136. package/dist/drift/index.d.ts +2 -0
  137. package/dist/drift/index.d.ts.map +1 -1
  138. package/dist/drift/index.js +1 -0
  139. package/dist/drift/skill-view-diagnostic.d.ts +197 -0
  140. package/dist/drift/skill-view-diagnostic.d.ts.map +1 -0
  141. package/dist/drift/skill-view-diagnostic.js +343 -0
  142. package/dist/validation/skills.d.ts +1 -0
  143. package/dist/validation/skills.d.ts.map +1 -1
  144. package/dist/validation/skills.js +181 -19
  145. 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":"AAgBA,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,kCAAkC,CAAC;AAS1C,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;AAmBxB,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAAA;CAAE,GAClD,MAAM,EAAE,CAaV;AAgCD,wBAAgB,wBAAwB,CACtC,YAAY,GAAE,uBAA6C,EAC3D,gBAAgB,GAAE,oBAA8C,GAC/D,OAAO,CAkPT;AAwCD;;;;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
+ {"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
- export function buildSyncSubprocessArgs(entrypoint, execArgv, options) {
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
- scopes: [...new Set(relevant.flatMap(({ scopes }) => scopes))],
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
  }