@aexol/opencode-wizard 0.3.10 → 0.3.13
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/README.md +4 -1
- package/dist/graphql-operations.d.ts +5 -4
- package/dist/graphql-operations.js +27 -2
- package/dist/graphql-operations.js.map +1 -1
- package/dist/plugin-tools.d.ts +3 -0
- package/dist/plugin-tools.js +12 -9
- package/dist/plugin-tools.js.map +1 -1
- package/dist/published-skills-system-note.js +1 -1
- package/dist/published-skills-system-note.js.map +1 -1
- package/dist/published-skills-transform.d.ts +22 -4
- package/dist/published-skills-transform.js +21 -5
- package/dist/published-skills-transform.js.map +1 -1
- package/dist/server/client.d.ts +11 -4
- package/dist/server/client.js +89 -6
- package/dist/server/client.js.map +1 -1
- package/dist/server/preferences.js +2 -1
- package/dist/server/preferences.js.map +1 -1
- package/dist/server/runtime.d.ts +1 -1
- package/dist/server/runtime.js +178 -27
- package/dist/server/runtime.js.map +1 -1
- package/dist/server/status.js +6 -2
- package/dist/server/status.js.map +1 -1
- package/dist/server/types.d.ts +17 -4
- package/dist/server/types.js.map +1 -1
- package/dist/smoke-published-skills.js +6 -3
- package/dist/smoke-published-skills.js.map +1 -1
- package/dist/tui/components/skill-picker-dialog.d.ts +7 -0
- package/dist/tui/components/skill-picker-dialog.js +94 -0
- package/dist/tui/components/skill-picker-dialog.js.map +1 -0
- package/dist/tui/components/status-content.d.ts +4 -0
- package/dist/tui/components/status-content.js +145 -20
- package/dist/tui/components/status-content.js.map +1 -1
- package/dist/tui/plugin.js +36 -69
- package/dist/tui/plugin.js.map +1 -1
- package/dist/tui/skill-helpers.d.ts +15 -2
- package/dist/tui/skill-helpers.js +82 -20
- package/dist/tui/skill-helpers.js.map +1 -1
- package/dist/tui/slots.js +13 -3
- package/dist/tui/slots.js.map +1 -1
- package/package.json +2 -2
package/dist/server/runtime.js
CHANGED
|
@@ -6,11 +6,11 @@ import { resolveStoredAuthState, toAuthState, writeAuthState } from './auth-stor
|
|
|
6
6
|
import { resolveConfig } from './config.js';
|
|
7
7
|
export { resolveConfig } from './config.js';
|
|
8
8
|
import { createPluginSession, openBrowser, startLoginFlow } from './auth-flow.js';
|
|
9
|
-
import { fetchPublishedSkillDetail, fetchPublishedSkillsCatalog, fetchPublishedSkillsGraphQl, fetchWizardArtifactDetail, fetchWizardArtifactsCatalog, maybePersistWorkspaceSlugFromCatalog } from './client.js';
|
|
9
|
+
import { fetchPublishedSkillDetail, fetchPublishedSkillsCatalog, fetchPublishedSkillsGraphQl, fetchWizardArtifactDetail, fetchWizardArtifactsCatalog, hydrateStoredAuthStateRole, maybePersistWorkspaceSlugFromCatalog } from './client.js';
|
|
10
10
|
import { normalizeAbsolutePath } from './path-utils.js';
|
|
11
11
|
import { emitPluginActionEvent, emitPresenceEvent } from './presence.js';
|
|
12
12
|
import { normalizeDirectoryArg, normalizeRepositoryPath, resolveWorkspace, toWorkspaceResolutionMetadata, toWorkspaceResolutionOutput } from './workspace.js';
|
|
13
|
-
import { parseRequestedSkillArgs, selectPublishedSkills, toPublishedSkillDetail, toPublishedSkillSummary, toWizardArtifactCatalog, toWizardArtifactDetail, toWizardArtifactSummary } from '../published-skills-transform.js';
|
|
13
|
+
import { parseRequestedSkillArgs, selectPublishedSkills, toInstallableSkillSummary, toPublishedSkillDetail, toPublishedSkillSummary, toWizardArtifactCatalog, toWizardArtifactDetail, toWizardArtifactSummary } from '../published-skills-transform.js';
|
|
14
14
|
import { CACHE_TTL_MS, LOGIN_TIMEOUT_MS, OIDC_CALLBACK_URL, PLUGIN_ID, PRESENCE_SHUTDOWN_SIGNALS, PRESENCE_SIGNAL_EXIT_CODES } from './constants.js';
|
|
15
15
|
export { PLUGIN_ID, NATIVE_SKILLS_URL_COMPATIBILITY } from './constants.js';
|
|
16
16
|
import { buildSystemNote, filterIgnoredPublishedSkills, resolvePluginStatusSnapshot, toAiFacingPluginStatusSnapshot, toFetchFailureOutput, toPluginStatusMetadata, toPublishedSkillCatalog } from './status.js';
|
|
@@ -20,7 +20,7 @@ import { getCatalogCacheKey, resolvePublishedSkillPreferenceCacheContext, setPub
|
|
|
20
20
|
export { buildSystemNote, resolvePluginStatusSnapshot, toPluginAuthStateSummary, toPublishedSkillCatalog } from './status.js';
|
|
21
21
|
const importOpencodePluginModule = new Function('specifier', 'return import(specifier)');
|
|
22
22
|
export { resolvePluginStatusSnapshotWithAuthBootstrap } from './auth-bootstrap.js';
|
|
23
|
-
export { setPublishedSkillIgnored, setPublishedSkillInstalled } from './preferences.js';
|
|
23
|
+
export { setPublishedSkillIgnored, setPublishedSkillInstalled, toPublishedSkillPreferenceScope } from './preferences.js';
|
|
24
24
|
const getDetailCacheKey = (catalogCacheKey, skillVersionId, revision) => {
|
|
25
25
|
return JSON.stringify([catalogCacheKey, skillVersionId, revision]);
|
|
26
26
|
};
|
|
@@ -118,6 +118,105 @@ const selectWizardArtifacts = (items, identifiers) => {
|
|
|
118
118
|
missingIdentifiers
|
|
119
119
|
};
|
|
120
120
|
};
|
|
121
|
+
const MAX_IDENTIFIER_SUGGESTIONS = 3;
|
|
122
|
+
const RECOMMENDATION_METADATA_NOTE = 'Recommendation fields are backend metadata for routing and discoverability only; fetch/detail tools are still required before using artifact bodies as guidance.';
|
|
123
|
+
const normalizeRecommendationContext = args => {
|
|
124
|
+
if (typeof args.recommendationContext !== 'string') return null;
|
|
125
|
+
const recommendationContext = args.recommendationContext.trim();
|
|
126
|
+
if (!recommendationContext) return null;
|
|
127
|
+
return recommendationContext;
|
|
128
|
+
};
|
|
129
|
+
const normalizeSuggestionIdentifier = value => value.trim().toLowerCase().normalize('NFKD').replace(/[^\p{Letter}\p{Number}]+/gu, '');
|
|
130
|
+
const getEditDistance = (left, right) => {
|
|
131
|
+
if (left === right) return 0;
|
|
132
|
+
if (!left) return right.length;
|
|
133
|
+
if (!right) return left.length;
|
|
134
|
+
const previous = Array.from({
|
|
135
|
+
length: right.length + 1
|
|
136
|
+
}, (_, index) => index);
|
|
137
|
+
for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
|
|
138
|
+
const current = [leftIndex];
|
|
139
|
+
for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) {
|
|
140
|
+
const substitutionCost = left[leftIndex - 1] === right[rightIndex - 1] ? 0 : 1;
|
|
141
|
+
current[rightIndex] = Math.min(current[rightIndex - 1] + 1, previous[rightIndex] + 1, previous[rightIndex - 1] + substitutionCost);
|
|
142
|
+
}
|
|
143
|
+
previous.splice(0, previous.length, ...current);
|
|
144
|
+
}
|
|
145
|
+
return previous[right.length];
|
|
146
|
+
};
|
|
147
|
+
const getSuggestionScore = (requested, candidate) => {
|
|
148
|
+
const normalizedRequested = normalizeSuggestionIdentifier(requested);
|
|
149
|
+
const normalizedCandidate = normalizeSuggestionIdentifier(candidate);
|
|
150
|
+
if (!normalizedRequested || !normalizedCandidate) return null;
|
|
151
|
+
if (normalizedRequested === normalizedCandidate) return 0;
|
|
152
|
+
if (normalizedCandidate.includes(normalizedRequested) || normalizedRequested.includes(normalizedCandidate)) return 1;
|
|
153
|
+
const distance = getEditDistance(normalizedRequested, normalizedCandidate);
|
|
154
|
+
const threshold = Math.max(2, Math.ceil(Math.max(normalizedRequested.length, normalizedCandidate.length) * 0.35));
|
|
155
|
+
if (distance > threshold) return null;
|
|
156
|
+
return distance + 2;
|
|
157
|
+
};
|
|
158
|
+
const dedupeSuggestionCandidates = candidates => {
|
|
159
|
+
const seen = new Set();
|
|
160
|
+
return candidates.filter(candidate => {
|
|
161
|
+
const key = normalizeSuggestionIdentifier(candidate.identifier);
|
|
162
|
+
if (seen.has(key)) return false;
|
|
163
|
+
seen.add(key);
|
|
164
|
+
return true;
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
const getIdentifierSuggestions = (requestedIdentifier, candidates) => dedupeSuggestionCandidates(candidates).map(candidate => ({
|
|
168
|
+
candidate,
|
|
169
|
+
score: getSuggestionScore(requestedIdentifier, candidate.identifier)
|
|
170
|
+
})).filter(result => result.score !== null).sort((left, right) => {
|
|
171
|
+
if (left.score !== right.score) return left.score - right.score;
|
|
172
|
+
if (left.candidate.source !== right.candidate.source) return left.candidate.source === 'active' ? -1 : 1;
|
|
173
|
+
return left.candidate.identifier.localeCompare(right.candidate.identifier);
|
|
174
|
+
}).slice(0, MAX_IDENTIFIER_SUGGESTIONS).map(({
|
|
175
|
+
candidate
|
|
176
|
+
}) => ({
|
|
177
|
+
identifier: candidate.canonicalIdentifier,
|
|
178
|
+
matchedIdentifier: candidate.identifier,
|
|
179
|
+
label: candidate.label,
|
|
180
|
+
source: candidate.source
|
|
181
|
+
}));
|
|
182
|
+
const getSkillSuggestionCandidates = payload => [...payload.skills.flatMap(item => {
|
|
183
|
+
const summary = toPublishedSkillSummary(item);
|
|
184
|
+
return summary.identifiers.map(identifier => ({
|
|
185
|
+
identifier,
|
|
186
|
+
canonicalIdentifier: summary.skillSlug,
|
|
187
|
+
label: summary.artifactName || summary.skillName || summary.skillSlug,
|
|
188
|
+
source: 'active'
|
|
189
|
+
}));
|
|
190
|
+
}), ...payload.catalogSkills.flatMap(item => {
|
|
191
|
+
const summary = toInstallableSkillSummary(item);
|
|
192
|
+
return summary.identifiers.map(identifier => ({
|
|
193
|
+
identifier,
|
|
194
|
+
canonicalIdentifier: summary.skillSlug,
|
|
195
|
+
label: summary.artifactName || summary.skillName || summary.skillSlug,
|
|
196
|
+
source: 'catalog'
|
|
197
|
+
}));
|
|
198
|
+
})];
|
|
199
|
+
const getWizardArtifactSuggestionCandidates = (items, catalogItems) => [...items.flatMap(item => {
|
|
200
|
+
const summary = toWizardArtifactSummary(item);
|
|
201
|
+
return summary.identifiers.map(identifier => ({
|
|
202
|
+
identifier,
|
|
203
|
+
canonicalIdentifier: summary.artifactSlug,
|
|
204
|
+
label: summary.artifactName || summary.artifactSlug,
|
|
205
|
+
source: 'active'
|
|
206
|
+
}));
|
|
207
|
+
}), ...toWizardArtifactCatalogCursorItems(catalogItems).flatMap(item => {
|
|
208
|
+
const summary = toWizardArtifactSummary(item);
|
|
209
|
+
return summary.identifiers.map(identifier => ({
|
|
210
|
+
identifier,
|
|
211
|
+
canonicalIdentifier: summary.artifactSlug,
|
|
212
|
+
label: summary.artifactName || summary.artifactSlug,
|
|
213
|
+
source: 'catalog'
|
|
214
|
+
}));
|
|
215
|
+
})];
|
|
216
|
+
const getMissingIdentifierSuggestions = (identifiers, candidates) => identifiers.map(identifier => ({
|
|
217
|
+
identifier,
|
|
218
|
+
suggestions: getIdentifierSuggestions(identifier, candidates)
|
|
219
|
+
}));
|
|
121
220
|
export const OpencodeWizardSkillsPlugin = async input => {
|
|
122
221
|
const {
|
|
123
222
|
tool
|
|
@@ -130,7 +229,12 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
130
229
|
const wizardArtifactDetailCache = new Map();
|
|
131
230
|
const detailInflight = new Map();
|
|
132
231
|
const wizardArtifactDetailInflight = new Map();
|
|
133
|
-
const initialAuthState = await
|
|
232
|
+
const initialAuthState = await hydrateStoredAuthStateRole({
|
|
233
|
+
worktree: input.worktree,
|
|
234
|
+
config,
|
|
235
|
+
signal: AbortSignal.timeout(5_000),
|
|
236
|
+
onAuthStateChanged: clearPublishedSkillState
|
|
237
|
+
});
|
|
134
238
|
const registeredTools = resolveAvailableTools(initialAuthState?.role ?? null);
|
|
135
239
|
const loginBootstrap = {
|
|
136
240
|
promise: null,
|
|
@@ -221,14 +325,14 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
221
325
|
continue;
|
|
222
326
|
}
|
|
223
327
|
}
|
|
224
|
-
|
|
328
|
+
function clearPublishedSkillState() {
|
|
225
329
|
cache.clear();
|
|
226
330
|
catalogInflight.clear();
|
|
227
331
|
detailCache.clear();
|
|
228
332
|
wizardArtifactDetailCache.clear();
|
|
229
333
|
detailInflight.clear();
|
|
230
334
|
wizardArtifactDetailInflight.clear();
|
|
231
|
-
}
|
|
335
|
+
}
|
|
232
336
|
const persistAuthState = async session => {
|
|
233
337
|
const authState = toAuthState(session);
|
|
234
338
|
await writeAuthState(config.authStatePath, authState);
|
|
@@ -334,7 +438,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
334
438
|
const loadPublishedSkillCatalog = async ({
|
|
335
439
|
directory,
|
|
336
440
|
useCache,
|
|
337
|
-
signal
|
|
441
|
+
signal,
|
|
442
|
+
recommendationContext
|
|
338
443
|
}) => {
|
|
339
444
|
const workspaceResolution = await resolveWorkspace({
|
|
340
445
|
config,
|
|
@@ -342,7 +447,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
342
447
|
});
|
|
343
448
|
const directoryPath = workspaceResolution.directoryPath;
|
|
344
449
|
const preferenceContext = await resolvePublishedSkillPreferenceCacheContext(config);
|
|
345
|
-
const cacheKey = getCatalogCacheKey(workspaceResolution, preferenceContext);
|
|
450
|
+
const cacheKey = JSON.stringify([getCatalogCacheKey(workspaceResolution, preferenceContext), recommendationContext ?? '']);
|
|
346
451
|
const cached = cache.get(cacheKey);
|
|
347
452
|
if (useCache && cached && cached.expiresAt > Date.now()) {
|
|
348
453
|
return {
|
|
@@ -359,7 +464,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
359
464
|
return inflight;
|
|
360
465
|
}
|
|
361
466
|
const requestPromise = (async () => {
|
|
362
|
-
const fetchResult = await fetchPublishedSkillsCatalog(input.worktree, config, workspaceResolution, signal, clearPublishedSkillState);
|
|
467
|
+
const fetchResult = await fetchPublishedSkillsCatalog(input.worktree, config, workspaceResolution, signal, recommendationContext, clearPublishedSkillState);
|
|
363
468
|
await maybePersistWorkspaceSlugFromCatalog({
|
|
364
469
|
config,
|
|
365
470
|
resolution: workspaceResolution,
|
|
@@ -416,6 +521,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
416
521
|
config,
|
|
417
522
|
resolution: workspaceResolution,
|
|
418
523
|
skillVersionId: item.skillVersion.id,
|
|
524
|
+
artifactVersionId: item.publishedArtifact.id,
|
|
419
525
|
signal,
|
|
420
526
|
onAuthStateChanged: clearPublishedSkillState,
|
|
421
527
|
purpose
|
|
@@ -524,6 +630,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
524
630
|
}) => {
|
|
525
631
|
const requestedDirectory = normalizeDirectoryArg(context.directory, args.directory);
|
|
526
632
|
const requestedSkills = parseRequestedSkillArgs(args);
|
|
633
|
+
const recommendationContext = normalizeRecommendationContext(args);
|
|
527
634
|
const fetchActionDirectoryPath = normalizeRepositoryPath(workspacePath, requestedDirectory);
|
|
528
635
|
lastInteractiveDirectoryPath = fetchActionDirectoryPath;
|
|
529
636
|
const emitFetchOutcome = async event => {
|
|
@@ -535,7 +642,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
535
642
|
let publishedSkillsResult = await loadPublishedSkillCatalog({
|
|
536
643
|
directory: requestedDirectory,
|
|
537
644
|
useCache: !args.refresh,
|
|
538
|
-
signal: context.abort
|
|
645
|
+
signal: context.abort,
|
|
646
|
+
recommendationContext
|
|
539
647
|
});
|
|
540
648
|
if (publishedSkillsResult.fetchResult.ok) {
|
|
541
649
|
await scheduleInteractivePresenceStart();
|
|
@@ -548,7 +656,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
548
656
|
publishedSkillsResult = await loadPublishedSkillCatalog({
|
|
549
657
|
directory: requestedDirectory,
|
|
550
658
|
useCache: false,
|
|
551
|
-
signal: context.abort
|
|
659
|
+
signal: context.abort,
|
|
660
|
+
recommendationContext
|
|
552
661
|
});
|
|
553
662
|
if (publishedSkillsResult.fetchResult.ok) {
|
|
554
663
|
await scheduleInteractivePresenceStart();
|
|
@@ -578,6 +687,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
578
687
|
}
|
|
579
688
|
const selection = selectPublishedSkills(filteredPublishedSkillsResult.fetchResult.payload, requestedSkills);
|
|
580
689
|
const isSingleRequest = requestedSkills.length === 1;
|
|
690
|
+
const skillSuggestionCandidates = getSkillSuggestionCandidates(filteredPublishedSkillsResult.fetchResult.payload);
|
|
581
691
|
if (requestedSkills.length === 0) {
|
|
582
692
|
const catalog = toPublishedSkillCatalog(filteredPublishedSkillsResult.fetchResult.payload, registeredTools);
|
|
583
693
|
context.metadata({
|
|
@@ -605,9 +715,12 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
605
715
|
},
|
|
606
716
|
fetchedAt: filteredPublishedSkillsResult.fetchResult.fetchedAt,
|
|
607
717
|
source: filteredPublishedSkillsResult.fetchResult.source,
|
|
718
|
+
recommendationContext,
|
|
719
|
+
recommendationNote: RECOMMENDATION_METADATA_NOTE,
|
|
608
720
|
cacheCursor: getCatalogCursor(filteredPublishedSkillsResult.fetchResult.payload.skills, filteredPublishedSkillsResult.fetchResult.payload.catalogSkills),
|
|
609
721
|
cacheTtlMs: CACHE_TTL_MS,
|
|
610
|
-
message: args.refresh ? 'Catalog discovery refreshed from the backend. Provide `skill` for one identifier or prefer `skills` for comma/newline-separated multiple identifiers to fetch markdown bodies/details.' : 'Catalog discovery only. Cached results include deterministic revision cursors; pass `refresh: true` to force a backend refresh immediately. Provide `skill` for one identifier or prefer `skills` for comma/newline-separated multiple identifiers to fetch markdown bodies/details.'
|
|
722
|
+
message: args.refresh ? 'Catalog discovery refreshed from the backend. Provide `skill` for one identifier or prefer `skills` for comma/newline-separated multiple identifiers to fetch markdown bodies/details.' : 'Catalog discovery only. Cached results include deterministic revision cursors; pass `refresh: true` to force a backend refresh immediately. Provide `skill` for one identifier or prefer `skills` for comma/newline-separated multiple identifiers to fetch markdown bodies/details.',
|
|
723
|
+
nextStep: 'Choose an active skill identifier from `skills` and call `opencode_wizard_published_skills_fetch` with `skill`; catalog-only skills must be installed before their bodies are fetchable.'
|
|
611
724
|
}, null, 2),
|
|
612
725
|
metadata: {
|
|
613
726
|
status: 'ready',
|
|
@@ -622,7 +735,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
622
735
|
}
|
|
623
736
|
};
|
|
624
737
|
}
|
|
625
|
-
if (selection.selectedItems.length === 0
|
|
738
|
+
if (selection.selectedItems.length === 0) {
|
|
626
739
|
await emitFetchOutcome('FETCH_FAILED');
|
|
627
740
|
return {
|
|
628
741
|
output: JSON.stringify({
|
|
@@ -631,12 +744,19 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
631
744
|
status: 'not_found',
|
|
632
745
|
requestedDirectoryPath: filteredPublishedSkillsResult.directoryPath,
|
|
633
746
|
workspaceResolution: toWorkspaceResolutionOutput(filteredPublishedSkillsResult.workspaceResolution),
|
|
634
|
-
requestedSkill: requestedSkills[0],
|
|
747
|
+
requestedSkill: isSingleRequest ? requestedSkills[0] : undefined,
|
|
748
|
+
requestedSkills,
|
|
749
|
+
missingSkills: selection.missingIdentifiers,
|
|
750
|
+
recommendationContext,
|
|
751
|
+
recommendationNote: RECOMMENDATION_METADATA_NOTE,
|
|
752
|
+
suggestions: isSingleRequest ? getIdentifierSuggestions(requestedSkills[0] ?? '', skillSuggestionCandidates) : undefined,
|
|
753
|
+
missingSkillSuggestions: getMissingIdentifierSuggestions(selection.missingIdentifiers, skillSuggestionCandidates),
|
|
635
754
|
availableSkills: filteredPublishedSkillsResult.fetchResult.payload.skills.map(toPublishedSkillSummary),
|
|
636
755
|
ignoredPublishedSkills: {
|
|
637
756
|
scopeKey: filteredPublishedSkillsResult.ignoreState.scopeKey,
|
|
638
757
|
count: filteredPublishedSkillsResult.ignoreState.ignoredSkillSlugs.length
|
|
639
|
-
}
|
|
758
|
+
},
|
|
759
|
+
nextStep: 'Retry with one suggested identifier, fetch the catalog with no args/refresh to inspect available skills, or install a catalog-only suggestion before fetching its body.'
|
|
640
760
|
}, null, 2),
|
|
641
761
|
metadata: {
|
|
642
762
|
status: 'not_found',
|
|
@@ -698,6 +818,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
698
818
|
workspace: filteredPublishedSkillsResult.fetchResult.payload.workspace,
|
|
699
819
|
fetchedAt: filteredPublishedSkillsResult.fetchResult.fetchedAt,
|
|
700
820
|
source: filteredPublishedSkillsResult.fetchResult.source,
|
|
821
|
+
recommendationContext,
|
|
822
|
+
recommendationNote: RECOMMENDATION_METADATA_NOTE,
|
|
701
823
|
skill: detail
|
|
702
824
|
}, null, 2),
|
|
703
825
|
metadata: {
|
|
@@ -728,6 +850,10 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
728
850
|
source: filteredPublishedSkillsResult.fetchResult.source,
|
|
729
851
|
requestedSkills,
|
|
730
852
|
missingSkills: selection.missingIdentifiers,
|
|
853
|
+
recommendationContext,
|
|
854
|
+
recommendationNote: RECOMMENDATION_METADATA_NOTE,
|
|
855
|
+
missingSkillSuggestions: getMissingIdentifierSuggestions(selection.missingIdentifiers, skillSuggestionCandidates),
|
|
856
|
+
nextStep: selection.missingIdentifiers.length > 0 ? 'Fetched matched skills. Retry missing identifiers with suggested active names, or inspect the no-arg catalog for catalog-only skills that need installation first.' : 'Use each fetched `skill.markdownDocument` as reference content for matched backend-published skills.',
|
|
731
857
|
skills: skillDetails
|
|
732
858
|
}, null, 2),
|
|
733
859
|
metadata: {
|
|
@@ -752,11 +878,12 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
752
878
|
});
|
|
753
879
|
}
|
|
754
880
|
if (artifactKind === 'DESIGN_DOC') {
|
|
881
|
+
const recommendationContext = normalizeRecommendationContext(args);
|
|
755
882
|
const workspaceResolution = await resolveWorkspace({
|
|
756
883
|
config,
|
|
757
884
|
directory: requestedDirectory
|
|
758
885
|
});
|
|
759
|
-
const fetchResult = await fetchWizardArtifactsCatalog(input.worktree, config, workspaceResolution, artifactKind, context.abort, clearPublishedSkillState);
|
|
886
|
+
const fetchResult = await fetchWizardArtifactsCatalog(input.worktree, config, workspaceResolution, artifactKind, context.abort, recommendationContext, clearPublishedSkillState);
|
|
760
887
|
if (!fetchResult.ok) {
|
|
761
888
|
return {
|
|
762
889
|
output: JSON.stringify({
|
|
@@ -791,9 +918,12 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
791
918
|
workspaceResolution: toWorkspaceResolutionOutput(workspaceResolution),
|
|
792
919
|
fetchedAt: fetchResult.fetchedAt,
|
|
793
920
|
source: fetchResult.source,
|
|
921
|
+
recommendationContext,
|
|
922
|
+
recommendationNote: RECOMMENDATION_METADATA_NOTE,
|
|
794
923
|
cacheCursor,
|
|
795
924
|
cacheTtlMs: CACHE_TTL_MS,
|
|
796
|
-
message: 'Generic artifact catalog discovery only. Full bodies/files require opencode_wizard_artifact_fetch with artifactKind and artifact identifiers.'
|
|
925
|
+
message: 'Generic artifact catalog discovery only. Full bodies/files require opencode_wizard_artifact_fetch with artifactKind and artifact identifiers.',
|
|
926
|
+
nextStep: 'Choose an active artifact identifier from `artifacts` and call `opencode_wizard_artifact_fetch`; catalog-only artifacts must be installed before their bodies are fetchable.'
|
|
797
927
|
}, null, 2),
|
|
798
928
|
metadata: {
|
|
799
929
|
status: 'ready',
|
|
@@ -809,7 +939,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
809
939
|
const result = await executePublishedSkillsFetchTool({
|
|
810
940
|
args: {
|
|
811
941
|
directory: args.directory,
|
|
812
|
-
refresh: args.refresh
|
|
942
|
+
refresh: args.refresh,
|
|
943
|
+
recommendationContext: args.recommendationContext
|
|
813
944
|
},
|
|
814
945
|
context
|
|
815
946
|
});
|
|
@@ -833,11 +964,12 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
833
964
|
skill: args.artifact,
|
|
834
965
|
skills: args.artifacts
|
|
835
966
|
});
|
|
967
|
+
const recommendationContext = normalizeRecommendationContext(args);
|
|
836
968
|
const workspaceResolution = await resolveWorkspace({
|
|
837
969
|
config,
|
|
838
970
|
directory: requestedDirectory
|
|
839
971
|
});
|
|
840
|
-
const fetchResult = await fetchWizardArtifactsCatalog(input.worktree, config, workspaceResolution, artifactKind, context.abort, clearPublishedSkillState);
|
|
972
|
+
const fetchResult = await fetchWizardArtifactsCatalog(input.worktree, config, workspaceResolution, artifactKind, context.abort, recommendationContext, clearPublishedSkillState);
|
|
841
973
|
if (!fetchResult.ok) {
|
|
842
974
|
return {
|
|
843
975
|
output: JSON.stringify({
|
|
@@ -860,6 +992,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
860
992
|
};
|
|
861
993
|
}
|
|
862
994
|
const selection = selectWizardArtifacts(fetchResult.payload.artifacts, requestedArtifacts);
|
|
995
|
+
const artifactSuggestionCandidates = getWizardArtifactSuggestionCandidates(fetchResult.payload.artifacts, fetchResult.payload.catalogArtifacts);
|
|
863
996
|
if (requestedArtifacts.length === 0) {
|
|
864
997
|
const catalog = toWizardArtifactCatalog(fetchResult.payload, {
|
|
865
998
|
pluginId: PLUGIN_ID,
|
|
@@ -874,9 +1007,12 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
874
1007
|
workspaceResolution: toWorkspaceResolutionOutput(workspaceResolution),
|
|
875
1008
|
fetchedAt: fetchResult.fetchedAt,
|
|
876
1009
|
source: fetchResult.source,
|
|
1010
|
+
recommendationContext,
|
|
1011
|
+
recommendationNote: RECOMMENDATION_METADATA_NOTE,
|
|
877
1012
|
cacheCursor,
|
|
878
1013
|
cacheTtlMs: CACHE_TTL_MS,
|
|
879
|
-
message: 'Provide artifact or artifacts to fetch artifact body/files.'
|
|
1014
|
+
message: 'Provide artifact or artifacts to fetch artifact body/files.',
|
|
1015
|
+
nextStep: 'Choose an active artifact identifier from `artifacts` and call `opencode_wizard_artifact_fetch`; install catalog-only artifacts before fetching their bodies.'
|
|
880
1016
|
}, null, 2),
|
|
881
1017
|
metadata: {
|
|
882
1018
|
status: 'ready',
|
|
@@ -887,17 +1023,24 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
887
1023
|
}
|
|
888
1024
|
};
|
|
889
1025
|
}
|
|
890
|
-
if (selection.selectedItems.length === 0
|
|
1026
|
+
if (selection.selectedItems.length === 0) {
|
|
891
1027
|
return {
|
|
892
1028
|
output: JSON.stringify({
|
|
893
1029
|
pluginId: PLUGIN_ID,
|
|
894
1030
|
runtimeMode: 'tool_fetch_only',
|
|
895
1031
|
status: 'not_found',
|
|
896
1032
|
artifactKind,
|
|
897
|
-
requestedArtifact: requestedArtifacts[0],
|
|
1033
|
+
requestedArtifact: requestedArtifacts.length === 1 ? requestedArtifacts[0] : undefined,
|
|
1034
|
+
requestedArtifacts,
|
|
1035
|
+
missingArtifacts: selection.missingIdentifiers,
|
|
1036
|
+
recommendationContext,
|
|
1037
|
+
recommendationNote: RECOMMENDATION_METADATA_NOTE,
|
|
1038
|
+
suggestions: requestedArtifacts.length === 1 ? getIdentifierSuggestions(requestedArtifacts[0] ?? '', artifactSuggestionCandidates) : undefined,
|
|
1039
|
+
missingArtifactSuggestions: getMissingIdentifierSuggestions(selection.missingIdentifiers, artifactSuggestionCandidates),
|
|
898
1040
|
availableArtifacts: fetchResult.payload.artifacts.map(toWizardArtifactSummary),
|
|
899
1041
|
requestedDirectoryPath: directoryPath,
|
|
900
|
-
workspaceResolution: toWorkspaceResolutionOutput(workspaceResolution)
|
|
1042
|
+
workspaceResolution: toWorkspaceResolutionOutput(workspaceResolution),
|
|
1043
|
+
nextStep: 'Retry with one suggested identifier, fetch the artifact catalog to inspect available artifacts, or install a catalog-only suggestion before fetching its body.'
|
|
901
1044
|
}, null, 2),
|
|
902
1045
|
metadata: {
|
|
903
1046
|
status: 'not_found',
|
|
@@ -957,6 +1100,10 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
957
1100
|
cacheTtlMs: CACHE_TTL_MS,
|
|
958
1101
|
requestedArtifacts,
|
|
959
1102
|
missingArtifacts: selection.missingIdentifiers,
|
|
1103
|
+
recommendationContext,
|
|
1104
|
+
recommendationNote: RECOMMENDATION_METADATA_NOTE,
|
|
1105
|
+
missingArtifactSuggestions: getMissingIdentifierSuggestions(selection.missingIdentifiers, artifactSuggestionCandidates),
|
|
1106
|
+
nextStep: selection.missingIdentifiers.length > 0 ? 'Fetched matched artifacts. Retry missing identifiers with suggested active names, or inspect the catalog for catalog-only artifacts that need installation first.' : 'Use the fetched artifact markdown as reference content for matched backend-published artifacts.',
|
|
960
1107
|
artifacts: details
|
|
961
1108
|
}, null, 2),
|
|
962
1109
|
metadata: {
|
|
@@ -973,7 +1120,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
973
1120
|
skill: args.artifact,
|
|
974
1121
|
skills: args.artifacts,
|
|
975
1122
|
directory: args.directory,
|
|
976
|
-
refresh: args.refresh
|
|
1123
|
+
refresh: args.refresh,
|
|
1124
|
+
recommendationContext: args.recommendationContext
|
|
977
1125
|
},
|
|
978
1126
|
context
|
|
979
1127
|
});
|
|
@@ -1043,7 +1191,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
1043
1191
|
const catalogResult = await loadPublishedSkillCatalog({
|
|
1044
1192
|
directory: requestedDirectory,
|
|
1045
1193
|
useCache: true,
|
|
1046
|
-
signal: context.abort
|
|
1194
|
+
signal: context.abort,
|
|
1195
|
+
recommendationContext: null
|
|
1047
1196
|
});
|
|
1048
1197
|
if (!catalogResult.fetchResult.ok) {
|
|
1049
1198
|
await emitPreferenceOutcome('PREFERENCE_FAILED');
|
|
@@ -1559,7 +1708,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
1559
1708
|
let publishedSkillsResult = await loadPublishedSkillCatalog({
|
|
1560
1709
|
directory: input.directory,
|
|
1561
1710
|
useCache: true,
|
|
1562
|
-
signal: AbortSignal.timeout(5_000)
|
|
1711
|
+
signal: AbortSignal.timeout(5_000),
|
|
1712
|
+
recommendationContext: null
|
|
1563
1713
|
});
|
|
1564
1714
|
if (!publishedSkillsResult.fetchResult.ok && publishedSkillsResult.fetchResult.status === 'missing_auth') {
|
|
1565
1715
|
try {
|
|
@@ -1569,7 +1719,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
1569
1719
|
publishedSkillsResult = await loadPublishedSkillCatalog({
|
|
1570
1720
|
directory: input.directory,
|
|
1571
1721
|
useCache: false,
|
|
1572
|
-
signal: AbortSignal.timeout(5_000)
|
|
1722
|
+
signal: AbortSignal.timeout(5_000),
|
|
1723
|
+
recommendationContext: null
|
|
1573
1724
|
});
|
|
1574
1725
|
} catch {
|
|
1575
1726
|
const loginMessage = loginBootstrap.snapshot.message ? ` Last login status: ${loginBootstrap.snapshot.message}` : '';
|