@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,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":"
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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":"
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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 =
|
|
261
|
+
const error = merged.error ?? undefined;
|
|
265
262
|
return {
|
|
266
263
|
...outcome,
|
|
267
|
-
sync
|
|
268
|
-
|
|
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
|
|
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[];
|