@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,383 @@
1
+ import { lstat, readFile } from 'node:fs/promises';
2
+ import { isAbsolute, join } from 'node:path';
3
+ import { getFrontmatterBlock, parseSkillFrontmatter, resolveSkillVersion, } from '../../shared/frontmatter.js';
4
+ import { resolveScopeProviderContextOutcome } from '../../tools/shared/provider-context.js';
5
+ import { diagnoseSkillViews } from '../../../drift/index.js';
6
+ import { OAT_MARKER_PREFIX } from '../../../engine/markers.js';
7
+ function canonicalSkillPath(skillName) {
8
+ return join('.agents', 'skills', skillName);
9
+ }
10
+ /**
11
+ * Existence probe that treats only a genuinely absent path as absent.
12
+ *
13
+ * A permission or I/O failure is not evidence of a projection gap: reporting
14
+ * one as `missing-additive` would tell the user to run a sync that cannot fix
15
+ * anything. Anything we failed to read is reported as present, which downgrades
16
+ * the classification to `untracked` and suppresses the repair suggestion.
17
+ */
18
+ export async function probeProviderPath(path, stat = lstat) {
19
+ try {
20
+ await stat(path);
21
+ return true;
22
+ }
23
+ catch (error) {
24
+ const code = typeof error === 'object' && error !== null && 'code' in error
25
+ ? error.code
26
+ : undefined;
27
+ return !(code === 'ENOENT' || code === 'ENOTDIR');
28
+ }
29
+ }
30
+ /**
31
+ * Reads the version of a projected skill copy, with the state of that reading.
32
+ *
33
+ * The only thing this does differently from the canonical reader is strip the
34
+ * OAT-managed provenance banner the engine prepends to a copied `SKILL.md`
35
+ * (`engine/markers.ts`), which puts the frontmatter past byte zero where
36
+ * `getFrontmatterBlock`'s start-anchored match will not look. Everything after
37
+ * that is `parseSkillFrontmatter` + `resolveSkillVersion` — the same functions
38
+ * `getSkillVersion` uses — so a view and its canonical source can never
39
+ * resolve by different precedence rules. A private parse here previously read
40
+ * the deprecated top-level `version` while the canonical side had already
41
+ * moved to `metadata.version`, which reported a byte-identical copy as stale.
42
+ *
43
+ * The state follows the resolver, never the parse in isolation: once the
44
+ * resolver returns a version, that version is what canonical resolution would
45
+ * report too, so an ignored sibling declaration that happens to be unusable
46
+ * must not downgrade it. `unusable` and `malformed` are reserved for readings
47
+ * that produced no version at all. A read failure yields no version rather
48
+ * than a guess, so a transient failure can never launder a conflicting
49
+ * declaration into a clean one.
50
+ */
51
+ export async function readProjectedSkillVersion(skillDir) {
52
+ let content;
53
+ try {
54
+ content = await readFile(join(skillDir, 'SKILL.md'), 'utf8');
55
+ }
56
+ catch {
57
+ return { version: null, state: 'absent' };
58
+ }
59
+ const stripped = content.startsWith(OAT_MARKER_PREFIX)
60
+ ? content.slice(content.indexOf('\n') + 1)
61
+ : content;
62
+ const block = getFrontmatterBlock(stripped);
63
+ if (!block)
64
+ return { version: null, state: 'absent' };
65
+ const parsed = parseSkillFrontmatter(block);
66
+ if (parsed.malformed)
67
+ return { version: null, state: 'malformed' };
68
+ const resolved = resolveSkillVersion(parsed);
69
+ if (!resolved) {
70
+ return {
71
+ version: null,
72
+ state: parsed.unusableVersionDeclaration ? 'unusable' : 'absent',
73
+ };
74
+ }
75
+ if (resolved.conflict) {
76
+ return {
77
+ version: resolved.version,
78
+ state: 'conflict',
79
+ conflict: resolved.conflict,
80
+ };
81
+ }
82
+ return { version: resolved.version, state: 'resolved' };
83
+ }
84
+ /**
85
+ * Diagnoses one canonical skill against every provider view, for each concrete
86
+ * scope where the canonical skill exists.
87
+ *
88
+ * The scope loop is deliberately wider than `oat tools info`'s name
89
+ * resolution, which returns the first scope that has the skill: a project-scope
90
+ * copy resolving first does not make a user-scope projection gap disappear, and
91
+ * the repair for each scope is its own concrete `oat sync --scope`.
92
+ */
93
+ export async function collectSkillViewDiagnoses(input) {
94
+ const canonicalRelative = canonicalSkillPath(input.skillName);
95
+ const diagnoses = [];
96
+ for (const scope of input.scopes) {
97
+ const scopeRoot = input.roots[scope];
98
+ if (!scopeRoot)
99
+ continue;
100
+ try {
101
+ const diagnosis = await diagnoseScope({
102
+ ...input,
103
+ scope,
104
+ scopeRoot,
105
+ canonicalRelative,
106
+ });
107
+ if (diagnosis)
108
+ diagnoses.push(diagnosis);
109
+ }
110
+ catch (error) {
111
+ // Every input this diagnostic reads can fail independently of the tool
112
+ // the user asked about: the sync config and the manifest both throw when
113
+ // present but invalid or unreadable, and the probes can fail on a
114
+ // permission error. None of that may remove the tool detail or change
115
+ // the exit code, so the section reports itself unavailable instead.
116
+ diagnoses.push({
117
+ skill: input.skillName,
118
+ scope,
119
+ result: 'unavailable',
120
+ reason: redactScopeRoot(error instanceof Error ? error.message : String(error), scope, scopeRoot),
121
+ views: [],
122
+ });
123
+ }
124
+ }
125
+ return diagnoses;
126
+ }
127
+ function escapeRegExp(value) {
128
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
129
+ }
130
+ /**
131
+ * An absolute path inside a quoted span, taken whole.
132
+ *
133
+ * A path may contain spaces, commas, and even a stray quote of the other kind,
134
+ * and every reachable producer of one of these messages quotes it
135
+ * (`lstat '/a/Private Client/x'`). Matching to the *matching* delimiter, rather
136
+ * than to the first character that looks like a separator, is what stops the
137
+ * bare pass below from redacting a prefix and forwarding the rest. The span must
138
+ * start with `/`, so an already-redacted `'<project>/…'` or `'~/…'` is left
139
+ * intact, and the match is lazy so two quoted spans in one message do not merge.
140
+ */
141
+ const QUOTED_ABSOLUTE_PATH = /(['"`])(\/[^\n]*?)\1/g;
142
+ /**
143
+ * An absolute path that is not quoted.
144
+ *
145
+ * The leading `/` may follow anything that is not a path character, which covers
146
+ * `=`, `:`, `[`, `<`, and a bare token boundary alike; requiring a specific
147
+ * delimiter is what let `path=/Users/…` through. Excluding `[A-Za-z0-9_~.>-]`
148
+ * before the slash is what keeps a relative `.oat/sync/…` and an
149
+ * already-redacted `<project>/…` or `~/…` intact.
150
+ *
151
+ * The path itself runs to whitespace or a quote and to nothing else. A comma, a
152
+ * bracket, and a brace are all legal in a filename, so treating one as a
153
+ * terminator would end the match inside a real path and forward its tail —
154
+ * `/outside/public,Private` would redact to `<path>,Private`. Swallowing a
155
+ * trailing bracket is the safe direction. A URL is redacted from its `//`
156
+ * onwards, which is over-redaction rather than a leak, and no producer of these
157
+ * messages emits one.
158
+ */
159
+ const BARE_ABSOLUTE_PATH = /(^|[^A-Za-z0-9_~.>-])(\/[^\s'"`]*)/g;
160
+ /**
161
+ * Makes a failure reason safe to print and to paste into a bug report.
162
+ *
163
+ * The scope root becomes its conventional placeholder, matching how the pack
164
+ * surfaces redact paths, but only where the root genuinely contains what
165
+ * follows — a `/` or the end of the string. Replacing it anywhere turned a
166
+ * sibling that merely shares the prefix (`/project-private/secret`, or
167
+ * `/project private/secret`) into `<project>-private/secret`, which both lies
168
+ * about the location and forwards the rest of a path that was never inside the
169
+ * scope root. A non-contained sibling falls through to the passes below and is
170
+ * replaced wholesale instead.
171
+ *
172
+ * Any absolute path still standing after that is by definition outside the
173
+ * scope root — a manifest `providerPath` that escapes it, for instance, which
174
+ * the detector resolves and names verbatim in an `ENOTDIR`/`ENOENT` message —
175
+ * so it is replaced wholesale rather than forwarded. Redaction is deliberately
176
+ * greedy here: over-redacting a reason costs a user nothing, and under-redacting
177
+ * one puts a username or a private directory name into a bug report.
178
+ */
179
+ function redactScopeRoot(text, scope, scopeRoot) {
180
+ const placeholder = scope === 'project' ? '<project>' : '~';
181
+ return text
182
+ .replace(new RegExp(`${escapeRegExp(scopeRoot)}(?=/|$)`, 'g'), placeholder)
183
+ .replace(QUOTED_ABSOLUTE_PATH, '$1<path>$1')
184
+ .replace(BARE_ABSOLUTE_PATH, '$1<path>');
185
+ }
186
+ async function diagnoseScope(input) {
187
+ const { dependencies: deps, scope, scopeRoot, canonicalRelative } = input;
188
+ const canonicalDir = join(scopeRoot, canonicalRelative);
189
+ if (!(await deps.pathExists(canonicalDir)))
190
+ return null;
191
+ // A sync config that is present but unreadable is exactly the failure this
192
+ // diagnostic exists to explain, so it degrades to `unavailable` with a
193
+ // reason like every other unreadable input. Dropping the scope instead would
194
+ // print nothing at all, which reads as "no providers configured" — the
195
+ // opposite of the truth — to the one user most likely to be running this.
196
+ // An absent config is not that case: `loadSyncConfig` answers `ENOENT` with
197
+ // the defaults, so it resolves normally and the scope is diagnosed.
198
+ const contextOutcome = await resolveScopeProviderContextOutcome({
199
+ scope,
200
+ scopeRoot,
201
+ ...(input.providerContext ? { dependencies: input.providerContext } : {}),
202
+ });
203
+ if (contextOutcome.status === 'failed') {
204
+ throw contextOutcome.error instanceof Error
205
+ ? contextOutcome.error
206
+ : new Error(String(contextOutcome.error));
207
+ }
208
+ const providerScopeContext = contextOutcome.context;
209
+ const projections = deps.resolveExpectedProjections({
210
+ skillName: input.skillName,
211
+ scope,
212
+ registrations: providerScopeContext.registrations,
213
+ });
214
+ const manifest = await deps.loadManifest(join(scopeRoot, '.oat', 'sync', 'manifest.json'));
215
+ // Collection entries ride in this compatibility view; the per-entry
216
+ // manifest reader narrows them, so read the V2 shape back for `strategy`.
217
+ const entries = manifest.entries;
218
+ const observations = [];
219
+ for (const projection of projections) {
220
+ const manifestEntry = entries.find((entry) => entry.canonicalPath === canonicalRelative &&
221
+ entry.provider === projection.provider) ?? null;
222
+ // The probes below use the real path; the row may not. An escaping path is
223
+ // replaced with the same placeholder a reason gets, so the divergence is
224
+ // still reported honestly without naming a location outside the scope.
225
+ const renderableEntry = manifestEntry && escapesScope(manifestEntry.providerPath)
226
+ ? { ...manifestEntry, providerPath: '<path>' }
227
+ : manifestEntry;
228
+ // One provider's unreadable path is not evidence about the other four.
229
+ // `detectDrift` throws before `readProjectedVersion`'s own catch can
230
+ // classify, and letting that reach the scope-level catch took the whole
231
+ // section down with it. Only the failing row degrades now.
232
+ try {
233
+ observations.push(await observeProjection({
234
+ deps,
235
+ scopeRoot,
236
+ projection,
237
+ manifestEntry,
238
+ renderableEntry,
239
+ }));
240
+ }
241
+ catch (error) {
242
+ observations.push({
243
+ projection,
244
+ manifestEntry: renderableEntry,
245
+ drift: null,
246
+ viewPresent: false,
247
+ unavailableReason: redactScopeRoot(error instanceof Error ? error.message : String(error), scope, scopeRoot),
248
+ });
249
+ }
250
+ }
251
+ return diagnoseSkillViews({
252
+ skillName: input.skillName,
253
+ scope,
254
+ canonicalPresent: true,
255
+ canonicalVersion: scope === input.resolvedScope
256
+ ? input.resolvedVersion
257
+ : await deps.getSkillVersion(canonicalDir),
258
+ providerScopeContext,
259
+ observations,
260
+ });
261
+ }
262
+ /**
263
+ * Whether a manifest `providerPath` names something outside its own scope.
264
+ *
265
+ * Manifest paths are scope-relative by convention, so anything absolute or
266
+ * reaching back through `..` describes a location the scope root placeholder
267
+ * cannot stand in for. Such a path is never rendered verbatim: a row that named
268
+ * it would disclose a location outside the tree the user asked about, which is
269
+ * exactly what the reason redaction exists to prevent.
270
+ */
271
+ function escapesScope(providerPath) {
272
+ return isAbsolute(providerPath) || providerPath.split(/[\\/]/).includes('..');
273
+ }
274
+ /** Reads one provider's reality for one expected projection. */
275
+ async function observeProjection(input) {
276
+ const { deps, scopeRoot, projection, manifestEntry } = input;
277
+ const viewPresent = await deps.pathExists(join(scopeRoot, projection.providerPath));
278
+ // `detectDrift` requires a manifest entry, so a never-synced projection
279
+ // is classified from the expected path alone and never probes drift.
280
+ const drift = manifestEntry
281
+ ? await deps.detectDrift(manifestEntry, scopeRoot)
282
+ : null;
283
+ // `detectDrift` resolves the manifest entry's own `providerPath`, and the
284
+ // manifest is keyed by `(canonicalPath, provider)` with no path check, so
285
+ // the two can name different files. Everything about a tracked row is then
286
+ // read from the tracked path, because that is the path the drift verdict
287
+ // already describes; mixing the two would report one file's version beside
288
+ // another file's state. The extra probe only runs when they actually
289
+ // diverge, so the ordinary row is unchanged.
290
+ const trackedPath = manifestEntry?.providerPath;
291
+ const pathDiverges = trackedPath !== undefined && trackedPath !== projection.providerPath;
292
+ const trackedPresent = pathDiverges
293
+ ? await deps.pathExists(join(scopeRoot, trackedPath))
294
+ : viewPresent;
295
+ // A tracked path that escapes the scope is not read for a version. Its
296
+ // `SKILL.md` is a file outside the tree the user asked about, so reporting
297
+ // its version would both disclose content from outside the scope and compare
298
+ // the canonical skill against something that is not a view of it.
299
+ const comparable = manifestEntry?.strategy === 'copy' &&
300
+ !projection.nativeRead &&
301
+ trackedPresent &&
302
+ !(trackedPath !== undefined && escapesScope(trackedPath));
303
+ return {
304
+ projection,
305
+ manifestEntry: input.renderableEntry,
306
+ drift,
307
+ viewPresent,
308
+ ...(comparable
309
+ ? {
310
+ projectedVersion: await deps.readProjectedVersion(join(scopeRoot, trackedPath ?? projection.providerPath)),
311
+ }
312
+ : {}),
313
+ };
314
+ }
315
+ function qualifier(view) {
316
+ if (view.nativeRead)
317
+ return ' (native read)';
318
+ if (view.strategy === 'symlink')
319
+ return ' (symlink)';
320
+ if (view.strategy === 'copy')
321
+ return ' (copy)';
322
+ if (view.strategy === 'collection')
323
+ return ' (collection alias)';
324
+ return '';
325
+ }
326
+ const ACTIONABLE = new Set(['missing-additive', 'removed', 'modified']);
327
+ /**
328
+ * Classes for which nothing is projected at all.
329
+ *
330
+ * These rows carry no projection qualifier and no path: `copilot: inactive
331
+ * (native read) .agents/skills/x` named a file the provider was never going to
332
+ * read and described a strategy that was never going to run.
333
+ */
334
+ const NO_PROJECTION = new Set(['inactive', 'unsupported', 'excluded']);
335
+ /** Renders the human-readable provider-view block. Pure. */
336
+ export function formatSkillViewLines(diagnoses) {
337
+ const lines = [];
338
+ for (const diagnosis of diagnoses) {
339
+ if (diagnosis.result === 'unavailable') {
340
+ lines.push(` Provider views (${diagnosis.scope}): unavailable`);
341
+ if (diagnosis.reason)
342
+ lines.push(` ${diagnosis.reason}`);
343
+ continue;
344
+ }
345
+ if (diagnosis.result !== 'diagnosed' || diagnosis.views.length === 0) {
346
+ continue;
347
+ }
348
+ lines.push(` Provider views (${diagnosis.scope}):`);
349
+ const width = Math.max(...diagnosis.views.map(({ provider }) => provider.length));
350
+ for (const view of diagnosis.views) {
351
+ const label = `${view.provider}:`.padEnd(width + 1);
352
+ const projected = !NO_PROJECTION.has(view.viewClass);
353
+ const path = projected && view.providerPath ? ` ${view.providerPath}` : '';
354
+ lines.push(` ${label} ${view.viewClass}${projected ? qualifier(view) : ''}${path}`);
355
+ // A withheld or ambiguous version comparison is exactly the case a user
356
+ // cannot infer from the class word alone, so it is shown even when the
357
+ // class itself is not actionable.
358
+ if (ACTIONABLE.has(view.viewClass) ||
359
+ view.viewClass === 'untracked' ||
360
+ // `unverified` is the class a per-view read failure degrades to, and
361
+ // its detail carries the redacted reason. Withholding it would hide
362
+ // exactly what the scope-level `unavailable` used to print.
363
+ view.viewClass === 'unverified' ||
364
+ // A row naming a path the adapter does not expect is unreadable
365
+ // without the sentence that says why, whatever its class.
366
+ view.expectedProviderPath !== undefined ||
367
+ (view.versionEvidence !== undefined &&
368
+ view.versionEvidence !== 'resolved' &&
369
+ view.versionEvidence !== 'absent')) {
370
+ lines.push(` ${view.detail}`);
371
+ }
372
+ if (view.versionComparable) {
373
+ lines.push(` versions: canonical ${view.canonicalVersion ?? '-'}, view ${view.viewVersion ?? '-'}`);
374
+ }
375
+ }
376
+ // One suggestion per affected scope, never one per provider and never
377
+ // `--scope all`: the narrowest safe repair is the scope that has the gap.
378
+ const suggestion = diagnosis.views.find(({ suggestion: value }) => value !== null)?.suggestion;
379
+ if (suggestion)
380
+ lines.push(` Repair: ${suggestion}`);
381
+ }
382
+ return lines;
383
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/install/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,qBAAqB,EAE3B,MAAM,sBAAsB,CAAC;AAO9B,OAAO,EACL,KAAK,oBAAoB,EAE1B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBzC,wBAAgB,yBAAyB,CACvC,gBAAgB,GAAE,oBAA8C,EAChE,aAAa,GAAE,OAAO,CAAC,qBAAqB,CAAM,EAClD,iBAAiB,CAAC,EAAE,MAAM,OAAO,GAChC,OAAO,CAyDT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/install/index.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,qBAAqB,EAE3B,MAAM,sBAAsB,CAAC;AAO9B,OAAO,EACL,KAAK,oBAAoB,EAE1B,MAAM,kCAAkC,CAAC;AAG1C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,wBAAgB,yBAAyB,CACvC,gBAAgB,GAAE,oBAAgD,EAClE,aAAa,GAAE,OAAO,CAAC,qBAAqB,CAAM,EAClD,iBAAiB,CAAC,EAAE,MAAM,OAAO,GAChC,OAAO,CAyDT"}
@@ -1,41 +1,19 @@
1
- import { execFile } from 'node:child_process';
2
1
  import { buildCommandContext } from '../../../app/command-context.js';
3
2
  import { consumeInitToolsRunMetadata, createInitToolsCommand, wasInstallSyncHandled, } from '../../init/tools/index.js';
4
3
  import { withProjectGuidanceOptions } from '../../init/tools/project-guidance.js';
5
4
  import { withScopeOption } from '../../shared/scope-option.js';
6
5
  import { readGlobalOptions, resolveConcreteScopes, } from '../../shared/shared.utils.js';
7
6
  import { autoSync, } from '../../tools/shared/auto-sync.js';
7
+ import { inProcessSyncDependencies } from '../../tools/shared/in-process-sync.js';
8
8
  import { getInstalledCanonicalPaths as getInstallSyncCanonicalPaths } from '../../tools/shared/install-sync-context.js';
9
- const defaultSyncDependencies = {
10
- runSync: async ({ scope, cwd, installedCanonicalPaths }) => {
11
- const syncArgs = [
12
- ...process.execArgv,
13
- process.argv[1],
14
- 'sync',
15
- '--scope',
16
- scope,
17
- ];
18
- for (const canonicalPath of installedCanonicalPaths ?? []) {
19
- syncArgs.push('--install-canonical', canonicalPath);
20
- }
21
- await new Promise((resolve, reject) => {
22
- execFile(process.execPath, syncArgs, { cwd }, (error) => {
23
- if (error)
24
- reject(error);
25
- else
26
- resolve();
27
- });
28
- });
29
- },
30
- };
31
- export function createToolsInstallCommand(syncDependencies = defaultSyncDependencies, initOverrides = {}, createBaseCommand) {
9
+ export function createToolsInstallCommand(syncDependencies = inProcessSyncDependencies, initOverrides = {}, createBaseCommand) {
32
10
  let syncEnabled = true;
33
11
  const buildContext = initOverrides.buildCommandContext ?? buildCommandContext;
34
12
  const effectiveOverrides = {
35
13
  ...initOverrides,
36
14
  syncAfterInstall: async (scopes, context, installedCanonicalPaths) => syncEnabled
37
15
  ? autoSync(scopes, context.cwd, context.home, context.logger, syncDependencies, { installedCanonicalPaths })
38
- : { synced: false, scopes: [], error: null },
16
+ : { synced: false, scopes: [], error: null, evidence: [] },
39
17
  };
40
18
  const cmd = createBaseCommand === undefined
41
19
  ? createInitToolsCommand(effectiveOverrides)
@@ -1,6 +1,7 @@
1
1
  import type { CommandContext } from '../../../app/command-context.js';
2
2
  import { type PackEvidenceBlockV1 } from '../../tools/shared/format-pack-inventory.js';
3
3
  import { type InventoryPackInput, type PackInventory } from '../../tools/shared/pack-inventory.js';
4
+ import { type ProviderContextDependencies } from '../../tools/shared/provider-context.js';
4
5
  import type { ScanToolsOptions } from '../../tools/shared/scan-tools.js';
5
6
  import type { ToolInfo } from '../../tools/shared/types.js';
6
7
  export interface ListToolsDependencies {
@@ -8,6 +9,7 @@ export interface ListToolsDependencies {
8
9
  resolveScopeRoot: (scope: 'project' | 'user', cwd: string, home: string) => Promise<string>;
9
10
  resolveAssetsRoot: () => Promise<string>;
10
11
  inventoryPack?: (input: InventoryPackInput) => Promise<PackInventory>;
12
+ providerContext?: ProviderContextDependencies;
11
13
  }
12
14
  export interface ListToolsResult {
13
15
  tools: ToolInfo[];
@@ -1 +1 @@
1
- {"version":3,"file":"list-tools.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/list/list-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAML,KAAK,mBAAmB,EACzB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EACnB,MAAM,uCAAuC,CAAC;AAE/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAE7D,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9D,gBAAgB,EAAE,CAChB,KAAK,EAAE,SAAS,GAAG,MAAM,EACzB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;CACvE;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,YAAY,EAAE,mBAAmB,CAAC;CACnC;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,qBAAqB,GAClC,OAAO,CAAC,eAAe,CAAC,CAkI1B"}
1
+ {"version":3,"file":"list-tools.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/list/list-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAML,KAAK,mBAAmB,EACzB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EACnB,MAAM,uCAAuC,CAAC;AAG/C,OAAO,EAEL,KAAK,2BAA2B,EACjC,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAE7D,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9D,gBAAgB,EAAE,CAChB,KAAK,EAAE,SAAS,GAAG,MAAM,EACzB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACtE,eAAe,CAAC,EAAE,2BAA2B,CAAC;CAC/C;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,YAAY,EAAE,mBAAmB,CAAC;CACnC;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,qBAAqB,GAClC,OAAO,CAAC,eAAe,CAAC,CA2J1B"}
@@ -2,6 +2,8 @@ import { resolveConcreteScopes } from '../../shared/shared.utils.js';
2
2
  import { formatPackEvidenceDetails, formatPackInventoryDetails, packEvidenceBlock, projectRenderablePackEvidence, unavailablePackEvidence, } from '../../tools/shared/format-pack-inventory.js';
3
3
  import { inventoryPack, } from '../../tools/shared/pack-inventory.js';
4
4
  import { PACK_MANIFEST } from '../../tools/shared/pack-manifest.js';
5
+ import { applyUserAgentCoverage } from '../../tools/shared/pack-provider-evidence.js';
6
+ import { resolvePackProviderSurface, } from '../../tools/shared/provider-context.js';
5
7
  export async function runListTools(context, dependencies) {
6
8
  const { logger } = context;
7
9
  const scopes = resolveConcreteScopes(context.scope);
@@ -23,15 +25,32 @@ export async function runListTools(context, dependencies) {
23
25
  userRoot: roots.user,
24
26
  };
25
27
  const inspectPack = dependencies.inventoryPack ?? inventoryPack;
28
+ // `list` reports the same packs as `status` and `doctor`, so it resolves the
29
+ // same config-aware provider surface. Without it, `list` reported user
30
+ // agents as unmaterialized that an active Codex or Cursor adapter supplies.
31
+ const providerSurface = await resolvePackProviderSurface({
32
+ scopeRoots: roots,
33
+ ...(dependencies.providerContext
34
+ ? { dependencies: dependencies.providerContext }
35
+ : {}),
36
+ });
26
37
  const inspected = await Promise.all(PACK_MANIFEST.map(({ name }) => inspectPack({
27
38
  pack: name,
28
39
  assetsRoot,
29
40
  projectRoot: roots.project,
30
41
  userRoot: roots.user,
31
- }).then((canonical) => ({
32
- canonical,
33
- evidence: projectRenderablePackEvidence(canonical, packRoots),
34
- }), (error) => ({
42
+ ...(roots.user
43
+ ? {
44
+ userManagedRoleMaterialization: providerSurface.userAgentCoverage !== 'none',
45
+ }
46
+ : {}),
47
+ }).then((raw) => {
48
+ const canonical = applyUserAgentCoverage(raw, providerSurface.userAgentCoverage);
49
+ return {
50
+ canonical,
51
+ evidence: projectRenderablePackEvidence(canonical, packRoots, providerSurface.contexts),
52
+ };
53
+ }, (error) => ({
35
54
  canonical: null,
36
55
  evidence: unavailablePackEvidence({
37
56
  pack: name,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/remove/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,kCAAkC,CAAC;AAW1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAEL,KAAK,uBAAuB,EAM7B,MAAM,gBAAgB,CAAC;AAqExB,wBAAgB,wBAAwB,CACtC,YAAY,GAAE,uBAA6C,EAC3D,gBAAgB,GAAE,oBAA8C,GAC/D,OAAO,CAkOT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/remove/index.ts"],"names":[],"mappings":"AASA,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,kCAAkC,CAAC;AAkB1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAEL,KAAK,uBAAuB,EAM7B,MAAM,gBAAgB,CAAC;AAkDxB,wBAAgB,wBAAwB,CACtC,YAAY,GAAE,uBAA6C,EAC3D,gBAAgB,GAAE,oBAA8C,GAC/D,OAAO,CAmPT"}
@@ -1,11 +1,15 @@
1
- import { execFile } from 'node:child_process';
2
1
  import { lstat, rm } from 'node:fs/promises';
3
2
  import { relative } from 'node:path';
4
3
  import { buildCommandContext } from '../../../app/command-context.js';
5
4
  import { withScopeOption } from '../../shared/scope-option.js';
6
5
  import { readGlobalOptions, resolveConcreteScopes, } from '../../shared/shared.utils.js';
7
6
  import { autoSync, } from '../../tools/shared/auto-sync.js';
7
+ import { inProcessSyncDependencies } from '../../tools/shared/in-process-sync.js';
8
8
  import { inventoryScopedPack } from '../../tools/shared/pack-inventory.js';
9
+ import { providerSyncOutcomeFromAutoSync, } from '../../tools/shared/pack-lifecycle-outcome.js';
10
+ import { withLifecycleProviderEvidence } from '../../tools/shared/pack-provider-evidence.js';
11
+ import { resolveProviderScopeContexts } from '../../tools/shared/provider-context.js';
12
+ import { applySyncEvidence } from '../../tools/shared/provider-reachability.js';
9
13
  import { scanTools } from '../../tools/shared/scan-tools.js';
10
14
  import { hasScopedPackOwnershipEvidence, writeScopedPackIntent, } from '../../tools/shared/scoped-pack-intent.js';
11
15
  import { resolveAssetsRoot } from '../../../fs/assets.js';
@@ -45,28 +49,7 @@ const defaultDependencies = {
45
49
  inventoryScopedPack,
46
50
  writeScopedPackIntent,
47
51
  };
48
- const defaultSyncDependencies = {
49
- runSync: async ({ scope, cwd, removedCanonicalPaths }) => {
50
- const args = [
51
- ...process.execArgv,
52
- process.argv[1],
53
- 'sync',
54
- '--scope',
55
- scope,
56
- ];
57
- for (const canonicalPath of removedCanonicalPaths ?? []) {
58
- args.push('--remove-canonical', canonicalPath);
59
- }
60
- await new Promise((resolve, reject) => {
61
- execFile(process.execPath, args, { cwd }, (error) => {
62
- if (error)
63
- reject(error);
64
- else
65
- resolve();
66
- });
67
- });
68
- },
69
- };
52
+ const defaultSyncDependencies = inProcessSyncDependencies;
70
53
  const VALID_PACKS = [
71
54
  'core',
72
55
  'ideas',
@@ -164,7 +147,12 @@ export function createToolsRemoveCommand(dependencies = defaultDependencies, syn
164
147
  }));
165
148
  }
166
149
  }
167
- result.lifecycle = removalLifecycleOutcomes(selectedPacks(target), scopes, result.packOutcomes, false, finalInventories);
150
+ result.lifecycle = removalLifecycleOutcomes(selectedPacks(target), scopes, result.packOutcomes, false, finalInventories, await resolveProviderScopeContexts({
151
+ scopeRoots: Object.fromEntries(await Promise.all(scopes.map(async (scope) => [
152
+ scope,
153
+ await dependencies.resolveScopeRoot(scope, context.cwd, context.home),
154
+ ]))),
155
+ }));
168
156
  }
169
157
  }
170
158
  catch (error) {
@@ -249,27 +237,32 @@ function finalizeRemovalLifecycle(lifecycle, syncResults) {
249
237
  return lifecycle;
250
238
  return lifecycle.map((outcome) => {
251
239
  const relevant = syncResults.filter(({ scopes }) => scopes.some((scope) => outcome.selection.targetScopes.includes(scope)));
252
- if (relevant.length === 0 || relevant.every(({ synced }) => synced)) {
253
- return relevant.length === 0
254
- ? outcome
255
- : {
256
- ...outcome,
257
- sync: {
258
- scopes: [...new Set(relevant.flatMap(({ scopes }) => scopes))],
259
- status: 'complete',
260
- providers: [],
261
- },
262
- };
240
+ const merged = {
241
+ synced: relevant.every(({ synced }) => synced),
242
+ scopes: [...new Set(relevant.flatMap(({ scopes }) => scopes))],
243
+ error: relevant.find((result) => result.error)?.error ?? null,
244
+ evidence: relevant.flatMap(({ evidence }) => evidence),
245
+ };
246
+ const providers = applySyncEvidence(outcome.sync.providers, {
247
+ syncRan: merged.evidence.some(({ ran }) => ran),
248
+ syncOperationResults: merged.evidence.flatMap(({ operationResults }) => operationResults),
249
+ extensionResults: merged.evidence.flatMap(({ extensionResults }) => extensionResults),
250
+ });
251
+ if (relevant.length === 0)
252
+ return outcome;
253
+ // The status comes from the shared severity-matrix rule rather than from
254
+ // `synced` alone: a sync that returned successfully can still carry an
255
+ // observed provider materialization failure, which is a `partial`.
256
+ const sync = providerSyncOutcomeFromAutoSync(merged, providers);
257
+ const finalEvidence = withLifecycleProviderEvidence(outcome.finalEvidence, providers);
258
+ if (sync.status === 'complete') {
259
+ return { ...outcome, sync, finalEvidence };
263
260
  }
264
- const error = relevant.find((result) => result.error)?.error;
261
+ const error = merged.error ?? undefined;
265
262
  return {
266
263
  ...outcome,
267
- sync: {
268
- scopes: [...new Set(relevant.flatMap(({ scopes }) => scopes))],
269
- status: 'failed',
270
- providers: [],
271
- ...(error ? { error } : {}),
272
- },
264
+ sync,
265
+ finalEvidence,
273
266
  status: 'partial',
274
267
  recovery: [
275
268
  ...outcome.recovery,
@@ -1,8 +1,9 @@
1
1
  import { type InventoryScopedPackInput, type ScopedPackInventory } from '../../tools/shared/pack-inventory.js';
2
2
  import { type PackLifecycleRequest, type PackLifecycleResult } from '../../tools/shared/pack-lifecycle.js';
3
- import type { PackLifecycleOutcome } from '../../tools/shared/pack-lifecycle-outcome.js';
3
+ import { type PackLifecycleOutcome } from '../../tools/shared/pack-lifecycle-outcome.js';
4
4
  import type { ScanToolsOptions } from '../../tools/shared/scan-tools.js';
5
5
  import type { PackName, ToolInfo } from '../../tools/shared/types.js';
6
+ import type { ProviderScopeContext } from '../../../providers/shared/registry.js';
6
7
  import type { ConcreteScope } from '../../../shared/types.js';
7
8
  export type RemoveTarget = {
8
9
  kind: 'name';
@@ -72,7 +73,7 @@ export declare function selectedPacks(target: Exclude<RemoveTarget, {
72
73
  kind: 'name';
73
74
  }>): PackName[];
74
75
  export declare function removeTools(target: RemoveTarget, scopes: ConcreteScope[], cwd: string, home: string, dryRun: boolean, deps: RemoveToolsDependencies): Promise<RemoveResult>;
75
- export declare function removalLifecycleOutcomes(packs: readonly PackName[], scopes: readonly ConcreteScope[], outcomes: readonly PackRemovalOutcome[], dryRun: boolean, finalInventories: readonly ScopedPackInventory[]): PackLifecycleOutcome[];
76
+ export declare function removalLifecycleOutcomes(packs: readonly PackName[], scopes: readonly ConcreteScope[], outcomes: readonly PackRemovalOutcome[], dryRun: boolean, finalInventories: readonly ScopedPackInventory[], providerContexts?: readonly ProviderScopeContext[]): PackLifecycleOutcome[];
76
77
  export declare function failedRemovalLifecycleOutcomes(target: Exclude<RemoveTarget, {
77
78
  kind: 'name';
78
79
  }>, scopes: readonly ConcreteScope[], error: unknown): PackLifecycleOutcome[];