@aexol/opencode-wizard 0.3.11 → 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 +4 -4
- package/dist/graphql-operations.js +16 -2
- package/dist/graphql-operations.js.map +1 -1
- package/dist/plugin-tools.d.ts +3 -0
- package/dist/plugin-tools.js +8 -5
- package/dist/plugin-tools.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 +4 -3
- package/dist/server/client.js +30 -4
- 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 +169 -23
- package/dist/server/runtime.js.map +1 -1
- package/dist/server/types.d.ts +8 -4
- package/dist/server/types.js.map +1 -1
- package/dist/smoke-published-skills.js +3 -1
- 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.js +6 -1
- 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 +14 -2
- package/dist/tui/skill-helpers.js +73 -19
- package/dist/tui/skill-helpers.js.map +1 -1
- package/package.json +2 -2
package/dist/server/runtime.js
CHANGED
|
@@ -10,7 +10,7 @@ import { fetchPublishedSkillDetail, fetchPublishedSkillsCatalog, fetchPublishedS
|
|
|
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
|
|
@@ -339,7 +438,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
339
438
|
const loadPublishedSkillCatalog = async ({
|
|
340
439
|
directory,
|
|
341
440
|
useCache,
|
|
342
|
-
signal
|
|
441
|
+
signal,
|
|
442
|
+
recommendationContext
|
|
343
443
|
}) => {
|
|
344
444
|
const workspaceResolution = await resolveWorkspace({
|
|
345
445
|
config,
|
|
@@ -347,7 +447,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
347
447
|
});
|
|
348
448
|
const directoryPath = workspaceResolution.directoryPath;
|
|
349
449
|
const preferenceContext = await resolvePublishedSkillPreferenceCacheContext(config);
|
|
350
|
-
const cacheKey = getCatalogCacheKey(workspaceResolution, preferenceContext);
|
|
450
|
+
const cacheKey = JSON.stringify([getCatalogCacheKey(workspaceResolution, preferenceContext), recommendationContext ?? '']);
|
|
351
451
|
const cached = cache.get(cacheKey);
|
|
352
452
|
if (useCache && cached && cached.expiresAt > Date.now()) {
|
|
353
453
|
return {
|
|
@@ -364,7 +464,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
364
464
|
return inflight;
|
|
365
465
|
}
|
|
366
466
|
const requestPromise = (async () => {
|
|
367
|
-
const fetchResult = await fetchPublishedSkillsCatalog(input.worktree, config, workspaceResolution, signal, clearPublishedSkillState);
|
|
467
|
+
const fetchResult = await fetchPublishedSkillsCatalog(input.worktree, config, workspaceResolution, signal, recommendationContext, clearPublishedSkillState);
|
|
368
468
|
await maybePersistWorkspaceSlugFromCatalog({
|
|
369
469
|
config,
|
|
370
470
|
resolution: workspaceResolution,
|
|
@@ -421,6 +521,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
421
521
|
config,
|
|
422
522
|
resolution: workspaceResolution,
|
|
423
523
|
skillVersionId: item.skillVersion.id,
|
|
524
|
+
artifactVersionId: item.publishedArtifact.id,
|
|
424
525
|
signal,
|
|
425
526
|
onAuthStateChanged: clearPublishedSkillState,
|
|
426
527
|
purpose
|
|
@@ -529,6 +630,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
529
630
|
}) => {
|
|
530
631
|
const requestedDirectory = normalizeDirectoryArg(context.directory, args.directory);
|
|
531
632
|
const requestedSkills = parseRequestedSkillArgs(args);
|
|
633
|
+
const recommendationContext = normalizeRecommendationContext(args);
|
|
532
634
|
const fetchActionDirectoryPath = normalizeRepositoryPath(workspacePath, requestedDirectory);
|
|
533
635
|
lastInteractiveDirectoryPath = fetchActionDirectoryPath;
|
|
534
636
|
const emitFetchOutcome = async event => {
|
|
@@ -540,7 +642,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
540
642
|
let publishedSkillsResult = await loadPublishedSkillCatalog({
|
|
541
643
|
directory: requestedDirectory,
|
|
542
644
|
useCache: !args.refresh,
|
|
543
|
-
signal: context.abort
|
|
645
|
+
signal: context.abort,
|
|
646
|
+
recommendationContext
|
|
544
647
|
});
|
|
545
648
|
if (publishedSkillsResult.fetchResult.ok) {
|
|
546
649
|
await scheduleInteractivePresenceStart();
|
|
@@ -553,7 +656,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
553
656
|
publishedSkillsResult = await loadPublishedSkillCatalog({
|
|
554
657
|
directory: requestedDirectory,
|
|
555
658
|
useCache: false,
|
|
556
|
-
signal: context.abort
|
|
659
|
+
signal: context.abort,
|
|
660
|
+
recommendationContext
|
|
557
661
|
});
|
|
558
662
|
if (publishedSkillsResult.fetchResult.ok) {
|
|
559
663
|
await scheduleInteractivePresenceStart();
|
|
@@ -583,6 +687,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
583
687
|
}
|
|
584
688
|
const selection = selectPublishedSkills(filteredPublishedSkillsResult.fetchResult.payload, requestedSkills);
|
|
585
689
|
const isSingleRequest = requestedSkills.length === 1;
|
|
690
|
+
const skillSuggestionCandidates = getSkillSuggestionCandidates(filteredPublishedSkillsResult.fetchResult.payload);
|
|
586
691
|
if (requestedSkills.length === 0) {
|
|
587
692
|
const catalog = toPublishedSkillCatalog(filteredPublishedSkillsResult.fetchResult.payload, registeredTools);
|
|
588
693
|
context.metadata({
|
|
@@ -610,9 +715,12 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
610
715
|
},
|
|
611
716
|
fetchedAt: filteredPublishedSkillsResult.fetchResult.fetchedAt,
|
|
612
717
|
source: filteredPublishedSkillsResult.fetchResult.source,
|
|
718
|
+
recommendationContext,
|
|
719
|
+
recommendationNote: RECOMMENDATION_METADATA_NOTE,
|
|
613
720
|
cacheCursor: getCatalogCursor(filteredPublishedSkillsResult.fetchResult.payload.skills, filteredPublishedSkillsResult.fetchResult.payload.catalogSkills),
|
|
614
721
|
cacheTtlMs: CACHE_TTL_MS,
|
|
615
|
-
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.'
|
|
616
724
|
}, null, 2),
|
|
617
725
|
metadata: {
|
|
618
726
|
status: 'ready',
|
|
@@ -627,7 +735,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
627
735
|
}
|
|
628
736
|
};
|
|
629
737
|
}
|
|
630
|
-
if (selection.selectedItems.length === 0
|
|
738
|
+
if (selection.selectedItems.length === 0) {
|
|
631
739
|
await emitFetchOutcome('FETCH_FAILED');
|
|
632
740
|
return {
|
|
633
741
|
output: JSON.stringify({
|
|
@@ -636,12 +744,19 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
636
744
|
status: 'not_found',
|
|
637
745
|
requestedDirectoryPath: filteredPublishedSkillsResult.directoryPath,
|
|
638
746
|
workspaceResolution: toWorkspaceResolutionOutput(filteredPublishedSkillsResult.workspaceResolution),
|
|
639
|
-
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),
|
|
640
754
|
availableSkills: filteredPublishedSkillsResult.fetchResult.payload.skills.map(toPublishedSkillSummary),
|
|
641
755
|
ignoredPublishedSkills: {
|
|
642
756
|
scopeKey: filteredPublishedSkillsResult.ignoreState.scopeKey,
|
|
643
757
|
count: filteredPublishedSkillsResult.ignoreState.ignoredSkillSlugs.length
|
|
644
|
-
}
|
|
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.'
|
|
645
760
|
}, null, 2),
|
|
646
761
|
metadata: {
|
|
647
762
|
status: 'not_found',
|
|
@@ -703,6 +818,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
703
818
|
workspace: filteredPublishedSkillsResult.fetchResult.payload.workspace,
|
|
704
819
|
fetchedAt: filteredPublishedSkillsResult.fetchResult.fetchedAt,
|
|
705
820
|
source: filteredPublishedSkillsResult.fetchResult.source,
|
|
821
|
+
recommendationContext,
|
|
822
|
+
recommendationNote: RECOMMENDATION_METADATA_NOTE,
|
|
706
823
|
skill: detail
|
|
707
824
|
}, null, 2),
|
|
708
825
|
metadata: {
|
|
@@ -733,6 +850,10 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
733
850
|
source: filteredPublishedSkillsResult.fetchResult.source,
|
|
734
851
|
requestedSkills,
|
|
735
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.',
|
|
736
857
|
skills: skillDetails
|
|
737
858
|
}, null, 2),
|
|
738
859
|
metadata: {
|
|
@@ -757,11 +878,12 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
757
878
|
});
|
|
758
879
|
}
|
|
759
880
|
if (artifactKind === 'DESIGN_DOC') {
|
|
881
|
+
const recommendationContext = normalizeRecommendationContext(args);
|
|
760
882
|
const workspaceResolution = await resolveWorkspace({
|
|
761
883
|
config,
|
|
762
884
|
directory: requestedDirectory
|
|
763
885
|
});
|
|
764
|
-
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);
|
|
765
887
|
if (!fetchResult.ok) {
|
|
766
888
|
return {
|
|
767
889
|
output: JSON.stringify({
|
|
@@ -796,9 +918,12 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
796
918
|
workspaceResolution: toWorkspaceResolutionOutput(workspaceResolution),
|
|
797
919
|
fetchedAt: fetchResult.fetchedAt,
|
|
798
920
|
source: fetchResult.source,
|
|
921
|
+
recommendationContext,
|
|
922
|
+
recommendationNote: RECOMMENDATION_METADATA_NOTE,
|
|
799
923
|
cacheCursor,
|
|
800
924
|
cacheTtlMs: CACHE_TTL_MS,
|
|
801
|
-
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.'
|
|
802
927
|
}, null, 2),
|
|
803
928
|
metadata: {
|
|
804
929
|
status: 'ready',
|
|
@@ -814,7 +939,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
814
939
|
const result = await executePublishedSkillsFetchTool({
|
|
815
940
|
args: {
|
|
816
941
|
directory: args.directory,
|
|
817
|
-
refresh: args.refresh
|
|
942
|
+
refresh: args.refresh,
|
|
943
|
+
recommendationContext: args.recommendationContext
|
|
818
944
|
},
|
|
819
945
|
context
|
|
820
946
|
});
|
|
@@ -838,11 +964,12 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
838
964
|
skill: args.artifact,
|
|
839
965
|
skills: args.artifacts
|
|
840
966
|
});
|
|
967
|
+
const recommendationContext = normalizeRecommendationContext(args);
|
|
841
968
|
const workspaceResolution = await resolveWorkspace({
|
|
842
969
|
config,
|
|
843
970
|
directory: requestedDirectory
|
|
844
971
|
});
|
|
845
|
-
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);
|
|
846
973
|
if (!fetchResult.ok) {
|
|
847
974
|
return {
|
|
848
975
|
output: JSON.stringify({
|
|
@@ -865,6 +992,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
865
992
|
};
|
|
866
993
|
}
|
|
867
994
|
const selection = selectWizardArtifacts(fetchResult.payload.artifacts, requestedArtifacts);
|
|
995
|
+
const artifactSuggestionCandidates = getWizardArtifactSuggestionCandidates(fetchResult.payload.artifacts, fetchResult.payload.catalogArtifacts);
|
|
868
996
|
if (requestedArtifacts.length === 0) {
|
|
869
997
|
const catalog = toWizardArtifactCatalog(fetchResult.payload, {
|
|
870
998
|
pluginId: PLUGIN_ID,
|
|
@@ -879,9 +1007,12 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
879
1007
|
workspaceResolution: toWorkspaceResolutionOutput(workspaceResolution),
|
|
880
1008
|
fetchedAt: fetchResult.fetchedAt,
|
|
881
1009
|
source: fetchResult.source,
|
|
1010
|
+
recommendationContext,
|
|
1011
|
+
recommendationNote: RECOMMENDATION_METADATA_NOTE,
|
|
882
1012
|
cacheCursor,
|
|
883
1013
|
cacheTtlMs: CACHE_TTL_MS,
|
|
884
|
-
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.'
|
|
885
1016
|
}, null, 2),
|
|
886
1017
|
metadata: {
|
|
887
1018
|
status: 'ready',
|
|
@@ -892,17 +1023,24 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
892
1023
|
}
|
|
893
1024
|
};
|
|
894
1025
|
}
|
|
895
|
-
if (selection.selectedItems.length === 0
|
|
1026
|
+
if (selection.selectedItems.length === 0) {
|
|
896
1027
|
return {
|
|
897
1028
|
output: JSON.stringify({
|
|
898
1029
|
pluginId: PLUGIN_ID,
|
|
899
1030
|
runtimeMode: 'tool_fetch_only',
|
|
900
1031
|
status: 'not_found',
|
|
901
1032
|
artifactKind,
|
|
902
|
-
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),
|
|
903
1040
|
availableArtifacts: fetchResult.payload.artifacts.map(toWizardArtifactSummary),
|
|
904
1041
|
requestedDirectoryPath: directoryPath,
|
|
905
|
-
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.'
|
|
906
1044
|
}, null, 2),
|
|
907
1045
|
metadata: {
|
|
908
1046
|
status: 'not_found',
|
|
@@ -962,6 +1100,10 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
962
1100
|
cacheTtlMs: CACHE_TTL_MS,
|
|
963
1101
|
requestedArtifacts,
|
|
964
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.',
|
|
965
1107
|
artifacts: details
|
|
966
1108
|
}, null, 2),
|
|
967
1109
|
metadata: {
|
|
@@ -978,7 +1120,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
978
1120
|
skill: args.artifact,
|
|
979
1121
|
skills: args.artifacts,
|
|
980
1122
|
directory: args.directory,
|
|
981
|
-
refresh: args.refresh
|
|
1123
|
+
refresh: args.refresh,
|
|
1124
|
+
recommendationContext: args.recommendationContext
|
|
982
1125
|
},
|
|
983
1126
|
context
|
|
984
1127
|
});
|
|
@@ -1048,7 +1191,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
1048
1191
|
const catalogResult = await loadPublishedSkillCatalog({
|
|
1049
1192
|
directory: requestedDirectory,
|
|
1050
1193
|
useCache: true,
|
|
1051
|
-
signal: context.abort
|
|
1194
|
+
signal: context.abort,
|
|
1195
|
+
recommendationContext: null
|
|
1052
1196
|
});
|
|
1053
1197
|
if (!catalogResult.fetchResult.ok) {
|
|
1054
1198
|
await emitPreferenceOutcome('PREFERENCE_FAILED');
|
|
@@ -1564,7 +1708,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
1564
1708
|
let publishedSkillsResult = await loadPublishedSkillCatalog({
|
|
1565
1709
|
directory: input.directory,
|
|
1566
1710
|
useCache: true,
|
|
1567
|
-
signal: AbortSignal.timeout(5_000)
|
|
1711
|
+
signal: AbortSignal.timeout(5_000),
|
|
1712
|
+
recommendationContext: null
|
|
1568
1713
|
});
|
|
1569
1714
|
if (!publishedSkillsResult.fetchResult.ok && publishedSkillsResult.fetchResult.status === 'missing_auth') {
|
|
1570
1715
|
try {
|
|
@@ -1574,7 +1719,8 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
1574
1719
|
publishedSkillsResult = await loadPublishedSkillCatalog({
|
|
1575
1720
|
directory: input.directory,
|
|
1576
1721
|
useCache: false,
|
|
1577
|
-
signal: AbortSignal.timeout(5_000)
|
|
1722
|
+
signal: AbortSignal.timeout(5_000),
|
|
1723
|
+
recommendationContext: null
|
|
1578
1724
|
});
|
|
1579
1725
|
} catch {
|
|
1580
1726
|
const loginMessage = loginBootstrap.snapshot.message ? ` Last login status: ${loginBootstrap.snapshot.message}` : '';
|