@aexol/opencode-wizard 0.3.4 → 0.3.5
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 +9 -7
- package/dist/graphql-operations.d.ts +5 -2
- package/dist/graphql-operations.js +161 -156
- package/dist/graphql-operations.js.map +1 -1
- package/dist/plugin-tools.d.ts +26 -0
- package/dist/plugin-tools.js +43 -7
- package/dist/plugin-tools.js.map +1 -1
- package/dist/published-skills-system-note.js +3 -7
- package/dist/published-skills-system-note.js.map +1 -1
- package/dist/published-skills-terminology.d.ts +21 -0
- package/dist/published-skills-terminology.js +38 -0
- package/dist/published-skills-terminology.js.map +1 -0
- package/dist/published-skills-transform.d.ts +99 -2
- package/dist/published-skills-transform.js +91 -19
- package/dist/published-skills-transform.js.map +1 -1
- package/dist/server/auth-bootstrap.d.ts +7 -0
- package/dist/server/auth-bootstrap.js +89 -0
- package/dist/server/auth-bootstrap.js.map +1 -0
- package/dist/server/client.d.ts +30 -1
- package/dist/server/client.js +81 -1
- package/dist/server/client.js.map +1 -1
- package/dist/server/preferences.d.ts +22 -0
- package/dist/server/preferences.js +121 -0
- package/dist/server/preferences.js.map +1 -0
- package/dist/server/runtime.d.ts +3 -22
- package/dist/server/runtime.js +447 -242
- package/dist/server/runtime.js.map +1 -1
- package/dist/server/types.d.ts +75 -0
- package/dist/server/types.js.map +1 -1
- package/dist/smoke-published-skills.js +4 -4
- package/dist/smoke-published-skills.js.map +1 -1
- package/dist/tui/components/skill-catalog-row.js +45 -44
- package/dist/tui/components/skill-catalog-row.js.map +1 -1
- package/dist/tui/components/wizard-skills-dialog-content.js +73 -63
- package/dist/tui/components/wizard-skills-dialog-content.js.map +1 -1
- package/dist/tui/skill-helpers.js +7 -6
- package/dist/tui/skill-helpers.js.map +1 -1
- package/package.json +1 -1
package/dist/server/runtime.js
CHANGED
|
@@ -1,226 +1,97 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { CREATE_OR_UPDATE_SKILL_FROM_MARKDOWN_MUTATION,
|
|
4
|
-
import { createPublishedSkillToolDefinitions } from '../plugin-tools.js';
|
|
5
|
-
import {
|
|
3
|
+
import { CREATE_OR_UPDATE_SKILL_FROM_MARKDOWN_MUTATION, SET_WIZARD_ARTIFACT_PREFERENCE_MUTATION } from '../graphql-operations.js';
|
|
4
|
+
import { createPublishedSkillToolDefinitions, resolveAvailableTools } from '../plugin-tools.js';
|
|
5
|
+
import { resolveStoredAuthState, toAuthState, writeAuthState } from './auth-store.js';
|
|
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, maybePersistWorkspaceSlugFromCatalog } from './client.js';
|
|
9
|
+
import { fetchPublishedSkillDetail, fetchPublishedSkillsCatalog, fetchPublishedSkillsGraphQl, fetchWizardArtifactDetail, fetchWizardArtifactsCatalog, maybePersistWorkspaceSlugFromCatalog } from './client.js';
|
|
10
10
|
import { normalizeAbsolutePath } from './path-utils.js';
|
|
11
11
|
import { emitPluginActionEvent, emitPresenceEvent } from './presence.js';
|
|
12
|
-
import { normalizeDirectoryArg, normalizeRepositoryPath, resolveWorkspace,
|
|
13
|
-
import { parseRequestedSkillArgs, selectPublishedSkills, toPublishedSkillDetail, toPublishedSkillSummary } from '../published-skills-transform.js';
|
|
14
|
-
import { CACHE_TTL_MS, LOGIN_TIMEOUT_MS, OIDC_CALLBACK_URL, PLUGIN_ID,
|
|
12
|
+
import { normalizeDirectoryArg, normalizeRepositoryPath, resolveWorkspace, toWorkspaceResolutionMetadata, toWorkspaceResolutionOutput } from './workspace.js';
|
|
13
|
+
import { parseRequestedSkillArgs, selectPublishedSkills, toPublishedSkillDetail, toPublishedSkillSummary, toWizardArtifactCatalog, toWizardArtifactDetail, toWizardArtifactSummary } from '../published-skills-transform.js';
|
|
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
|
-
import { buildSystemNote, filterIgnoredPublishedSkills, resolvePluginStatusSnapshot, toAiFacingPluginStatusSnapshot, toFetchFailureOutput, toPluginStatusMetadata, toPublishedSkillCatalog
|
|
16
|
+
import { buildSystemNote, filterIgnoredPublishedSkills, resolvePluginStatusSnapshot, toAiFacingPluginStatusSnapshot, toFetchFailureOutput, toPluginStatusMetadata, toPublishedSkillCatalog } from './status.js';
|
|
17
|
+
import { createIdleLoginBootstrapSnapshot } from './auth-bootstrap.js';
|
|
18
|
+
import { getCatalogCacheKey, resolvePublishedSkillPreferenceCacheContext, setPublishedSkillIgnored, setPublishedSkillInstalled, toPublishedSkillPreferenceAction, toPublishedSkillPreferenceScope } from './preferences.js';
|
|
17
19
|
export { buildSystemNote, resolvePluginStatusSnapshot, toPluginAuthStateSummary, toPublishedSkillCatalog } from './status.js';
|
|
18
|
-
const createIdleLoginBootstrapSnapshot = () => ({
|
|
19
|
-
status: 'idle',
|
|
20
|
-
trigger: null,
|
|
21
|
-
startedAt: null,
|
|
22
|
-
expiresAt: null,
|
|
23
|
-
browserUrl: null,
|
|
24
|
-
browserOpenError: null,
|
|
25
|
-
email: null,
|
|
26
|
-
message: null
|
|
27
|
-
});
|
|
28
|
-
const STATUS_PATH_LOGIN_RETRY_COOLDOWN_MS = 60_000;
|
|
29
|
-
const statusPathLoginBootstrap = {
|
|
30
|
-
promise: null,
|
|
31
|
-
status: 'idle',
|
|
32
|
-
message: null,
|
|
33
|
-
failedAt: null
|
|
34
|
-
};
|
|
35
20
|
const importOpencodePluginModule = new Function('specifier', 'return import(specifier)');
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return normalized;
|
|
41
|
-
};
|
|
42
|
-
const toPublishedSkillPreferenceAction = value => {
|
|
43
|
-
const normalized = value.trim().toLowerCase();
|
|
44
|
-
if (normalized === 'install' || normalized === 'uninstall' || normalized === 'ignore' || normalized === 'unignore') {
|
|
45
|
-
return normalized;
|
|
46
|
-
}
|
|
47
|
-
throw new Error('Published skill preference action must be one of: install, uninstall, ignore, unignore.');
|
|
48
|
-
};
|
|
49
|
-
const toPublishedSkillPreferenceScope = (value, defaultScope) => {
|
|
50
|
-
if (!value) return defaultScope;
|
|
51
|
-
const normalized = value.trim().toLowerCase();
|
|
52
|
-
if (normalized === 'global' || normalized === 'project') return normalized;
|
|
53
|
-
throw new Error('Published skill preferenceScope must be either global or project.');
|
|
21
|
+
export { resolvePluginStatusSnapshotWithAuthBootstrap } from './auth-bootstrap.js';
|
|
22
|
+
export { setPublishedSkillIgnored, setPublishedSkillInstalled } from './preferences.js';
|
|
23
|
+
const getDetailCacheKey = (catalogCacheKey, skillVersionId) => {
|
|
24
|
+
return JSON.stringify([catalogCacheKey, skillVersionId]);
|
|
54
25
|
};
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
userKey: toStoredUserKey(authState),
|
|
59
|
-
preferenceVersion: publishedSkillPreferenceCacheVersion
|
|
60
|
-
};
|
|
26
|
+
const getDetailInflightKey = (catalogCacheKey, skillVersionId, purpose) => {
|
|
27
|
+
return JSON.stringify([catalogCacheKey, skillVersionId, purpose]);
|
|
61
28
|
};
|
|
62
|
-
const
|
|
63
|
-
|
|
29
|
+
const SUPPORTED_WIZARD_ARTIFACT_KINDS = ['SKILL', 'DESIGN_DOC'];
|
|
30
|
+
const DESIGN_DOC_UNSUPPORTED_MESSAGE = 'DESIGN_DOC is supported by generic wizard artifact persistence; use catalog/detail tools for assigned or installed documents.';
|
|
31
|
+
const toWizardArtifactKind = value => {
|
|
32
|
+
if (!value) return 'SKILL';
|
|
33
|
+
const normalized = value.trim().toUpperCase().replace(/[-\s]+/gu, '_');
|
|
34
|
+
if (normalized === 'SKILL') return 'SKILL';
|
|
35
|
+
if (normalized === 'DESIGN_DOC') return 'DESIGN_DOC';
|
|
36
|
+
return null;
|
|
64
37
|
};
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
38
|
+
const buildUnsupportedWizardArtifactOutput = ({
|
|
39
|
+
artifactKind,
|
|
40
|
+
directoryPath
|
|
41
|
+
}) => ({
|
|
42
|
+
output: JSON.stringify({
|
|
43
|
+
pluginId: PLUGIN_ID,
|
|
44
|
+
runtimeMode: 'tool_fetch_only',
|
|
45
|
+
status: artifactKind === 'DESIGN_DOC' ? 'unsupported' : 'bad_artifact_kind',
|
|
46
|
+
artifactKind,
|
|
47
|
+
supportedArtifactKinds: SUPPORTED_WIZARD_ARTIFACT_KINDS,
|
|
48
|
+
requestedDirectoryPath: directoryPath,
|
|
49
|
+
message: artifactKind === 'DESIGN_DOC' ? DESIGN_DOC_UNSUPPORTED_MESSAGE : 'Unsupported wizard artifact kind. Use SKILL or DESIGN_DOC.'
|
|
50
|
+
}, null, 2),
|
|
51
|
+
metadata: {
|
|
52
|
+
status: artifactKind === 'DESIGN_DOC' ? 'unsupported' : 'bad_artifact_kind',
|
|
53
|
+
artifactKind,
|
|
54
|
+
directoryPath
|
|
69
55
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
56
|
+
});
|
|
57
|
+
const withWizardArtifactEnvelope = (result, artifactKind) => {
|
|
58
|
+
if (!result || typeof result !== 'object' || !('output' in result)) return result;
|
|
59
|
+
const outputResult = result;
|
|
60
|
+
if (typeof outputResult.output !== 'string') return result;
|
|
61
|
+
try {
|
|
62
|
+
return {
|
|
63
|
+
...outputResult,
|
|
64
|
+
output: JSON.stringify({
|
|
65
|
+
...JSON.parse(outputResult.output),
|
|
66
|
+
artifactKind,
|
|
67
|
+
supportedArtifactKinds: SUPPORTED_WIZARD_ARTIFACT_KINDS,
|
|
68
|
+
compatibilityAliases: ['opencode_wizard_published_skills_fetch', 'opencode_wizard_published_skill_preference_set']
|
|
69
|
+
}, null, 2),
|
|
70
|
+
metadata: {
|
|
71
|
+
...(outputResult.metadata ?? {}),
|
|
72
|
+
artifactKind
|
|
87
73
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
redirectUri: OIDC_CALLBACK_URL,
|
|
92
|
-
config,
|
|
93
|
-
signal: loginSignal
|
|
94
|
-
});
|
|
95
|
-
const authState = toAuthState(pluginSession);
|
|
96
|
-
await writeAuthState(config.authStatePath, authState);
|
|
97
|
-
statusPathLoginBootstrap.status = 'authenticated';
|
|
98
|
-
statusPathLoginBootstrap.message = `Browser login completed successfully for ${authState.email}.`;
|
|
99
|
-
return authState;
|
|
100
|
-
} finally {
|
|
101
|
-
await loginStart.closeCallbackServer().catch(() => undefined);
|
|
102
|
-
}
|
|
103
|
-
})().catch(error => {
|
|
104
|
-
statusPathLoginBootstrap.status = 'failed';
|
|
105
|
-
statusPathLoginBootstrap.failedAt = Date.now();
|
|
106
|
-
statusPathLoginBootstrap.message = error instanceof Error ? error.message : 'Browser login failed.';
|
|
107
|
-
throw error;
|
|
108
|
-
}).finally(() => {
|
|
109
|
-
statusPathLoginBootstrap.promise = null;
|
|
110
|
-
});
|
|
111
|
-
statusPathLoginBootstrap.promise.catch(() => undefined);
|
|
112
|
-
};
|
|
113
|
-
export const resolvePluginStatusSnapshotWithAuthBootstrap = async ({
|
|
114
|
-
worktree,
|
|
115
|
-
directory,
|
|
116
|
-
signal
|
|
117
|
-
}) => {
|
|
118
|
-
const snapshot = await resolvePluginStatusSnapshot({
|
|
119
|
-
worktree,
|
|
120
|
-
directory,
|
|
121
|
-
signal
|
|
122
|
-
});
|
|
123
|
-
if (snapshot.status !== 'missing_auth') return snapshot;
|
|
124
|
-
const config = await resolveConfig(worktree);
|
|
125
|
-
startStatusPathLoginBootstrap(worktree, config);
|
|
126
|
-
if (statusPathLoginBootstrap.message) {
|
|
127
|
-
return withStatusMessage(snapshot, statusPathLoginBootstrap.message);
|
|
74
|
+
};
|
|
75
|
+
} catch {
|
|
76
|
+
return result;
|
|
128
77
|
}
|
|
129
|
-
return withStatusMessage(snapshot, 'Browser login is pending from the TUI/status path.');
|
|
130
78
|
};
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
return
|
|
79
|
+
const matchesWizardArtifactIdentifier = (item, identifier) => {
|
|
80
|
+
const normalizedIdentifier = identifier.trim().toLowerCase();
|
|
81
|
+
if (!normalizedIdentifier) return false;
|
|
82
|
+
if (item.artifact.slug.toLowerCase() === normalizedIdentifier) return true;
|
|
83
|
+
if (item.artifact.name.toLowerCase() === normalizedIdentifier) return true;
|
|
84
|
+
if (item.artifactVersion.frontmatterName.toLowerCase() === normalizedIdentifier) return true;
|
|
85
|
+
return item.artifactVersion.id.toLowerCase() === normalizedIdentifier;
|
|
134
86
|
};
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
config,
|
|
139
|
-
skillSlug,
|
|
140
|
-
preferenceScope,
|
|
141
|
-
installed,
|
|
142
|
-
ignored
|
|
143
|
-
}) => {
|
|
144
|
-
const workspaceResolution = await resolveWorkspace({
|
|
145
|
-
config,
|
|
146
|
-
directory
|
|
147
|
-
});
|
|
148
|
-
const response = await fetchPublishedSkillsGraphQl({
|
|
149
|
-
worktree,
|
|
150
|
-
config,
|
|
151
|
-
query: SET_PUBLISHED_SKILL_PREFERENCE_MUTATION,
|
|
152
|
-
variables: {
|
|
153
|
-
input: {
|
|
154
|
-
...toDeliveryInput(workspaceResolution),
|
|
155
|
-
skillSlug,
|
|
156
|
-
preferenceScope: toBackendPreferenceScope(preferenceScope),
|
|
157
|
-
installed,
|
|
158
|
-
ignored
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
signal: AbortSignal.timeout(PRESENCE_EVENT_TIMEOUT_MS)
|
|
162
|
-
});
|
|
163
|
-
if (!response.ok) {
|
|
164
|
-
throw new Error(response.result.message);
|
|
165
|
-
}
|
|
166
|
-
const preferences = response.data.setPublishedSkillPreference;
|
|
167
|
-
publishedSkillPreferenceCacheVersion += 1;
|
|
87
|
+
const selectWizardArtifacts = (items, identifiers) => {
|
|
88
|
+
const selectedItems = items.filter(item => identifiers.some(identifier => matchesWizardArtifactIdentifier(item, identifier)));
|
|
89
|
+
const missingIdentifiers = identifiers.filter(identifier => !selectedItems.some(item => matchesWizardArtifactIdentifier(item, identifier)));
|
|
168
90
|
return {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
ignoredSkillSlugs: preferences.ignoredSkills.map(item => item.skill.slug),
|
|
172
|
-
installedGlobalSkillSlugs: [],
|
|
173
|
-
installedWorkspaceSkillSlugs: []
|
|
91
|
+
selectedItems,
|
|
92
|
+
missingIdentifiers
|
|
174
93
|
};
|
|
175
94
|
};
|
|
176
|
-
export const setPublishedSkillIgnored = async ({
|
|
177
|
-
worktree,
|
|
178
|
-
directory,
|
|
179
|
-
skillSlug,
|
|
180
|
-
ignored,
|
|
181
|
-
preferenceScope
|
|
182
|
-
}) => {
|
|
183
|
-
const config = await resolveConfig(worktree);
|
|
184
|
-
const normalizedSkillSlug = toIgnoredSkillSlug(skillSlug);
|
|
185
|
-
if (!normalizedSkillSlug) {
|
|
186
|
-
throw new Error('Cannot toggle an empty published skill slug.');
|
|
187
|
-
}
|
|
188
|
-
return setPublishedSkillPreference({
|
|
189
|
-
worktree,
|
|
190
|
-
directory,
|
|
191
|
-
config,
|
|
192
|
-
skillSlug: normalizedSkillSlug,
|
|
193
|
-
preferenceScope: preferenceScope ?? 'project',
|
|
194
|
-
ignored
|
|
195
|
-
});
|
|
196
|
-
};
|
|
197
|
-
export const setPublishedSkillInstalled = async ({
|
|
198
|
-
worktree,
|
|
199
|
-
directory,
|
|
200
|
-
skillSlug,
|
|
201
|
-
installed,
|
|
202
|
-
preferenceScope
|
|
203
|
-
}) => {
|
|
204
|
-
const config = await resolveConfig(worktree);
|
|
205
|
-
const normalizedSkillSlug = toIgnoredSkillSlug(skillSlug);
|
|
206
|
-
if (!normalizedSkillSlug) {
|
|
207
|
-
throw new Error('Cannot toggle an empty published skill slug.');
|
|
208
|
-
}
|
|
209
|
-
return setPublishedSkillPreference({
|
|
210
|
-
worktree,
|
|
211
|
-
directory,
|
|
212
|
-
config,
|
|
213
|
-
skillSlug: normalizedSkillSlug,
|
|
214
|
-
preferenceScope,
|
|
215
|
-
installed
|
|
216
|
-
});
|
|
217
|
-
};
|
|
218
|
-
const getDetailCacheKey = (catalogCacheKey, skillVersionId) => {
|
|
219
|
-
return JSON.stringify([catalogCacheKey, skillVersionId]);
|
|
220
|
-
};
|
|
221
|
-
const getDetailInflightKey = (catalogCacheKey, skillVersionId, purpose) => {
|
|
222
|
-
return JSON.stringify([catalogCacheKey, skillVersionId, purpose]);
|
|
223
|
-
};
|
|
224
95
|
export const OpencodeWizardSkillsPlugin = async input => {
|
|
225
96
|
const {
|
|
226
97
|
tool
|
|
@@ -561,20 +432,6 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
561
432
|
args,
|
|
562
433
|
context
|
|
563
434
|
}) => {
|
|
564
|
-
const authState = await resolveStoredAuthState(input.worktree, config);
|
|
565
|
-
if (!authState || authState.role !== 'ADMIN' && authState.role !== 'EDITOR') {
|
|
566
|
-
return {
|
|
567
|
-
output: JSON.stringify({
|
|
568
|
-
pluginId: PLUGIN_ID,
|
|
569
|
-
status: 'forbidden',
|
|
570
|
-
message: 'This tool requires ADMIN or EDITOR role. Your current session does not have the required permission.'
|
|
571
|
-
}, null, 2),
|
|
572
|
-
metadata: {
|
|
573
|
-
status: 'forbidden',
|
|
574
|
-
role: authState?.role ?? 'none'
|
|
575
|
-
}
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
435
|
const requestedDirectory = normalizeDirectoryArg(context.directory, args.directory);
|
|
579
436
|
const requestedSkills = parseRequestedSkillArgs(args);
|
|
580
437
|
const fetchActionDirectoryPath = normalizeRepositoryPath(workspacePath, requestedDirectory);
|
|
@@ -632,7 +489,11 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
632
489
|
const selection = selectPublishedSkills(filteredPublishedSkillsResult.fetchResult.payload, requestedSkills);
|
|
633
490
|
const isSingleRequest = requestedSkills.length === 1;
|
|
634
491
|
if (requestedSkills.length === 0) {
|
|
635
|
-
const
|
|
492
|
+
const authState = await resolveStoredAuthState(input.worktree, config);
|
|
493
|
+
const catalog = {
|
|
494
|
+
...toPublishedSkillCatalog(filteredPublishedSkillsResult.fetchResult.payload),
|
|
495
|
+
availableTools: resolveAvailableTools(authState?.role ?? null)
|
|
496
|
+
};
|
|
636
497
|
context.metadata({
|
|
637
498
|
title: `opencode-wizard published skills catalog: ${catalog.publishedSkillCount} active`,
|
|
638
499
|
metadata: {
|
|
@@ -789,24 +650,241 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
789
650
|
}
|
|
790
651
|
};
|
|
791
652
|
};
|
|
792
|
-
const
|
|
653
|
+
const executeWizardArtifactCatalogTool = async ({
|
|
793
654
|
args,
|
|
794
655
|
context
|
|
795
656
|
}) => {
|
|
796
|
-
const
|
|
797
|
-
|
|
657
|
+
const requestedDirectory = normalizeDirectoryArg(context.directory, args.directory);
|
|
658
|
+
const directoryPath = normalizeRepositoryPath(workspacePath, requestedDirectory);
|
|
659
|
+
const artifactKind = toWizardArtifactKind(args.artifactKind);
|
|
660
|
+
if (!artifactKind) {
|
|
661
|
+
return buildUnsupportedWizardArtifactOutput({
|
|
662
|
+
artifactKind: args.artifactKind ?? '',
|
|
663
|
+
directoryPath
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
if (artifactKind === 'DESIGN_DOC') {
|
|
667
|
+
const workspaceResolution = await resolveWorkspace({
|
|
668
|
+
config,
|
|
669
|
+
directory: requestedDirectory
|
|
670
|
+
});
|
|
671
|
+
const fetchResult = await fetchWizardArtifactsCatalog(input.worktree, config, workspaceResolution, artifactKind, context.abort, clearPublishedSkillState);
|
|
672
|
+
if (!fetchResult.ok) {
|
|
673
|
+
return {
|
|
674
|
+
output: JSON.stringify({
|
|
675
|
+
pluginId: PLUGIN_ID,
|
|
676
|
+
runtimeMode: 'tool_fetch_only',
|
|
677
|
+
status: fetchResult.status,
|
|
678
|
+
artifactKind,
|
|
679
|
+
requestedDirectoryPath: directoryPath,
|
|
680
|
+
workspaceResolution: toWorkspaceResolutionOutput(workspaceResolution),
|
|
681
|
+
message: fetchResult.message,
|
|
682
|
+
fetchedAt: fetchResult.fetchedAt,
|
|
683
|
+
source: fetchResult.source
|
|
684
|
+
}, null, 2),
|
|
685
|
+
metadata: {
|
|
686
|
+
status: fetchResult.status,
|
|
687
|
+
artifactKind,
|
|
688
|
+
directoryPath,
|
|
689
|
+
source: fetchResult.source
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
const authState = await resolveStoredAuthState(input.worktree, config);
|
|
694
|
+
const catalog = toWizardArtifactCatalog(fetchResult.payload, {
|
|
695
|
+
pluginId: PLUGIN_ID,
|
|
696
|
+
availableTools: resolveAvailableTools(authState?.role ?? null)
|
|
697
|
+
});
|
|
798
698
|
return {
|
|
799
699
|
output: JSON.stringify({
|
|
700
|
+
...catalog,
|
|
701
|
+
status: 'ready',
|
|
702
|
+
requestedDirectoryPath: directoryPath,
|
|
703
|
+
workspaceResolution: toWorkspaceResolutionOutput(workspaceResolution),
|
|
704
|
+
fetchedAt: fetchResult.fetchedAt,
|
|
705
|
+
source: fetchResult.source,
|
|
706
|
+
message: 'DESIGN_DOC catalog discovery only. Full DESIGN.md bodies/files require opencode_wizard_artifact_fetch with artifactKind DESIGN_DOC.'
|
|
707
|
+
}, null, 2),
|
|
708
|
+
metadata: {
|
|
709
|
+
status: 'ready',
|
|
710
|
+
artifactKind,
|
|
711
|
+
artifactCount: catalog.artifactCount.toString(),
|
|
712
|
+
...toWorkspaceResolutionMetadata(workspaceResolution),
|
|
713
|
+
source: fetchResult.source
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
const result = await executePublishedSkillsFetchTool({
|
|
718
|
+
args: {
|
|
719
|
+
directory: args.directory,
|
|
720
|
+
refresh: args.refresh
|
|
721
|
+
},
|
|
722
|
+
context
|
|
723
|
+
});
|
|
724
|
+
return withWizardArtifactEnvelope(result, artifactKind);
|
|
725
|
+
};
|
|
726
|
+
const executeWizardArtifactFetchTool = async ({
|
|
727
|
+
args,
|
|
728
|
+
context
|
|
729
|
+
}) => {
|
|
730
|
+
const requestedDirectory = normalizeDirectoryArg(context.directory, args.directory);
|
|
731
|
+
const directoryPath = normalizeRepositoryPath(workspacePath, requestedDirectory);
|
|
732
|
+
const artifactKind = toWizardArtifactKind(args.artifactKind);
|
|
733
|
+
if (!artifactKind) {
|
|
734
|
+
return buildUnsupportedWizardArtifactOutput({
|
|
735
|
+
artifactKind: args.artifactKind ?? '',
|
|
736
|
+
directoryPath
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
if (artifactKind === 'DESIGN_DOC') {
|
|
740
|
+
const requestedArtifacts = parseRequestedSkillArgs({
|
|
741
|
+
skill: args.artifact,
|
|
742
|
+
skills: args.artifacts
|
|
743
|
+
});
|
|
744
|
+
const workspaceResolution = await resolveWorkspace({
|
|
745
|
+
config,
|
|
746
|
+
directory: requestedDirectory
|
|
747
|
+
});
|
|
748
|
+
const fetchResult = await fetchWizardArtifactsCatalog(input.worktree, config, workspaceResolution, artifactKind, context.abort, clearPublishedSkillState);
|
|
749
|
+
if (!fetchResult.ok) {
|
|
750
|
+
return {
|
|
751
|
+
output: JSON.stringify({
|
|
752
|
+
pluginId: PLUGIN_ID,
|
|
753
|
+
runtimeMode: 'tool_fetch_only',
|
|
754
|
+
status: fetchResult.status,
|
|
755
|
+
artifactKind,
|
|
756
|
+
requestedDirectoryPath: directoryPath,
|
|
757
|
+
workspaceResolution: toWorkspaceResolutionOutput(workspaceResolution),
|
|
758
|
+
message: fetchResult.message,
|
|
759
|
+
fetchedAt: fetchResult.fetchedAt,
|
|
760
|
+
source: fetchResult.source
|
|
761
|
+
}, null, 2),
|
|
762
|
+
metadata: {
|
|
763
|
+
status: fetchResult.status,
|
|
764
|
+
artifactKind,
|
|
765
|
+
directoryPath,
|
|
766
|
+
source: fetchResult.source
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
const selection = selectWizardArtifacts(fetchResult.payload.artifacts, requestedArtifacts);
|
|
771
|
+
if (requestedArtifacts.length === 0) {
|
|
772
|
+
const authState = await resolveStoredAuthState(input.worktree, config);
|
|
773
|
+
const catalog = toWizardArtifactCatalog(fetchResult.payload, {
|
|
800
774
|
pluginId: PLUGIN_ID,
|
|
801
|
-
|
|
802
|
-
|
|
775
|
+
availableTools: resolveAvailableTools(authState?.role ?? null)
|
|
776
|
+
});
|
|
777
|
+
return {
|
|
778
|
+
output: JSON.stringify({
|
|
779
|
+
...catalog,
|
|
780
|
+
status: 'ready',
|
|
781
|
+
requestedDirectoryPath: directoryPath,
|
|
782
|
+
workspaceResolution: toWorkspaceResolutionOutput(workspaceResolution),
|
|
783
|
+
fetchedAt: fetchResult.fetchedAt,
|
|
784
|
+
source: fetchResult.source,
|
|
785
|
+
message: 'Provide artifact or artifacts to fetch DESIGN.md body/files.'
|
|
786
|
+
}, null, 2),
|
|
787
|
+
metadata: {
|
|
788
|
+
status: 'ready',
|
|
789
|
+
artifactKind,
|
|
790
|
+
...toWorkspaceResolutionMetadata(workspaceResolution)
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
if (selection.selectedItems.length === 0 && requestedArtifacts.length === 1) {
|
|
795
|
+
return {
|
|
796
|
+
output: JSON.stringify({
|
|
797
|
+
pluginId: PLUGIN_ID,
|
|
798
|
+
runtimeMode: 'tool_fetch_only',
|
|
799
|
+
status: 'not_found',
|
|
800
|
+
artifactKind,
|
|
801
|
+
requestedArtifact: requestedArtifacts[0],
|
|
802
|
+
availableArtifacts: fetchResult.payload.artifacts.map(toWizardArtifactSummary),
|
|
803
|
+
requestedDirectoryPath: directoryPath,
|
|
804
|
+
workspaceResolution: toWorkspaceResolutionOutput(workspaceResolution)
|
|
805
|
+
}, null, 2),
|
|
806
|
+
metadata: {
|
|
807
|
+
status: 'not_found',
|
|
808
|
+
artifactKind,
|
|
809
|
+
...toWorkspaceResolutionMetadata(workspaceResolution)
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
const detailResults = await Promise.all(selection.selectedItems.map(item => fetchWizardArtifactDetail({
|
|
814
|
+
worktree: input.worktree,
|
|
815
|
+
config,
|
|
816
|
+
resolution: workspaceResolution,
|
|
817
|
+
artifactKind,
|
|
818
|
+
artifactVersionId: item.artifactVersion.id,
|
|
819
|
+
signal: context.abort,
|
|
820
|
+
onAuthStateChanged: clearPublishedSkillState,
|
|
821
|
+
purpose: 'TOOL_FETCH'
|
|
822
|
+
})));
|
|
823
|
+
const failedDetail = detailResults.find(result => !result.ok);
|
|
824
|
+
if (failedDetail && !failedDetail.ok) {
|
|
825
|
+
return {
|
|
826
|
+
output: JSON.stringify({
|
|
827
|
+
pluginId: PLUGIN_ID,
|
|
828
|
+
runtimeMode: 'tool_fetch_only',
|
|
829
|
+
status: failedDetail.result.status,
|
|
830
|
+
artifactKind,
|
|
831
|
+
requestedDirectoryPath: directoryPath,
|
|
832
|
+
workspaceResolution: toWorkspaceResolutionOutput(workspaceResolution),
|
|
833
|
+
message: failedDetail.result.message,
|
|
834
|
+
fetchedAt: failedDetail.result.fetchedAt,
|
|
835
|
+
source: failedDetail.result.source
|
|
836
|
+
}, null, 2),
|
|
837
|
+
metadata: {
|
|
838
|
+
status: failedDetail.result.status,
|
|
839
|
+
artifactKind,
|
|
840
|
+
...toWorkspaceResolutionMetadata(workspaceResolution)
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
const details = detailResults.map((result, index) => {
|
|
845
|
+
if (!result.ok) throw new Error('Wizard artifact detail result unexpectedly missing after success guard.');
|
|
846
|
+
return toWizardArtifactDetail({
|
|
847
|
+
...selection.selectedItems[index],
|
|
848
|
+
artifactVersion: result.artifact
|
|
849
|
+
});
|
|
850
|
+
});
|
|
851
|
+
return {
|
|
852
|
+
output: JSON.stringify({
|
|
853
|
+
pluginId: PLUGIN_ID,
|
|
854
|
+
runtimeMode: 'tool_fetch_only',
|
|
855
|
+
artifactKind,
|
|
856
|
+
requestedDirectoryPath: directoryPath,
|
|
857
|
+
workspaceResolution: toWorkspaceResolutionOutput(workspaceResolution),
|
|
858
|
+
workspace: fetchResult.payload.workspace,
|
|
859
|
+
fetchedAt: fetchResult.fetchedAt,
|
|
860
|
+
source: fetchResult.source,
|
|
861
|
+
requestedArtifacts,
|
|
862
|
+
missingArtifacts: selection.missingIdentifiers,
|
|
863
|
+
artifacts: details
|
|
803
864
|
}, null, 2),
|
|
804
865
|
metadata: {
|
|
805
|
-
status: '
|
|
806
|
-
|
|
866
|
+
status: selection.missingIdentifiers.length > 0 ? 'partial' : 'ready',
|
|
867
|
+
artifactKind,
|
|
868
|
+
matchedCount: details.length.toString(),
|
|
869
|
+
...toWorkspaceResolutionMetadata(workspaceResolution)
|
|
807
870
|
}
|
|
808
871
|
};
|
|
809
872
|
}
|
|
873
|
+
const result = await executePublishedSkillsFetchTool({
|
|
874
|
+
args: {
|
|
875
|
+
skill: args.artifact,
|
|
876
|
+
skills: args.artifacts,
|
|
877
|
+
directory: args.directory,
|
|
878
|
+
refresh: args.refresh
|
|
879
|
+
},
|
|
880
|
+
context
|
|
881
|
+
});
|
|
882
|
+
return withWizardArtifactEnvelope(result, artifactKind);
|
|
883
|
+
};
|
|
884
|
+
const executeStatusTool = async ({
|
|
885
|
+
args,
|
|
886
|
+
context
|
|
887
|
+
}) => {
|
|
810
888
|
const requestedDirectory = normalizeDirectoryArg(context.directory, args.directory);
|
|
811
889
|
let snapshot = await resolvePluginStatusSnapshot({
|
|
812
890
|
worktree: input.worktree,
|
|
@@ -844,20 +922,6 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
844
922
|
args,
|
|
845
923
|
context
|
|
846
924
|
}) => {
|
|
847
|
-
const authState = await resolveStoredAuthState(input.worktree, config);
|
|
848
|
-
if (!authState || authState.role !== 'ADMIN' && authState.role !== 'EDITOR') {
|
|
849
|
-
return {
|
|
850
|
-
output: JSON.stringify({
|
|
851
|
-
pluginId: PLUGIN_ID,
|
|
852
|
-
status: 'forbidden',
|
|
853
|
-
message: 'This tool requires ADMIN or EDITOR role. Your current session does not have the required permission.'
|
|
854
|
-
}, null, 2),
|
|
855
|
-
metadata: {
|
|
856
|
-
status: 'forbidden',
|
|
857
|
-
role: authState?.role ?? 'none'
|
|
858
|
-
}
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
925
|
const requestedDirectory = normalizeDirectoryArg(context.directory, args.directory);
|
|
862
926
|
const directoryPath = normalizeRepositoryPath(workspacePath, requestedDirectory);
|
|
863
927
|
lastInteractiveDirectoryPath = directoryPath;
|
|
@@ -882,7 +946,13 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
882
946
|
signal: context.abort
|
|
883
947
|
});
|
|
884
948
|
if (!catalogResult.fetchResult.ok) {
|
|
885
|
-
|
|
949
|
+
await emitPreferenceOutcome('PREFERENCE_FAILED');
|
|
950
|
+
return toFetchFailureOutput({
|
|
951
|
+
worktree: input.worktree,
|
|
952
|
+
config,
|
|
953
|
+
publishedSkillsResult: catalogResult,
|
|
954
|
+
loginBootstrapSnapshot: loginBootstrap.snapshot
|
|
955
|
+
});
|
|
886
956
|
}
|
|
887
957
|
const selectableCatalogSkills = catalogResult.fetchResult.payload.catalogSkills.map(item => ({
|
|
888
958
|
...item,
|
|
@@ -941,8 +1011,132 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
941
1011
|
};
|
|
942
1012
|
} catch (error) {
|
|
943
1013
|
await emitPreferenceOutcome('PREFERENCE_FAILED');
|
|
944
|
-
|
|
1014
|
+
const message = error instanceof Error ? error.message : 'Unknown published skill preference failure.';
|
|
1015
|
+
const metadata = {
|
|
1016
|
+
status: 'preference_failed',
|
|
1017
|
+
directoryPath
|
|
1018
|
+
};
|
|
1019
|
+
context.metadata({
|
|
1020
|
+
title: 'opencode-wizard published skill preference failed',
|
|
1021
|
+
metadata
|
|
1022
|
+
});
|
|
1023
|
+
return {
|
|
1024
|
+
output: JSON.stringify({
|
|
1025
|
+
pluginId: PLUGIN_ID,
|
|
1026
|
+
status: 'preference_failed',
|
|
1027
|
+
requestedIdentifier: requestedSkill,
|
|
1028
|
+
requestedDirectoryPath: directoryPath,
|
|
1029
|
+
message,
|
|
1030
|
+
guidance: 'The preference tool remains available. Check opencode_wizard_status for safe auth/catalog state, rerun fetch/status to bootstrap auth, and retry after catalog status is ready.'
|
|
1031
|
+
}, null, 2),
|
|
1032
|
+
metadata
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
};
|
|
1036
|
+
const executeWizardArtifactPreferenceTool = async ({
|
|
1037
|
+
args,
|
|
1038
|
+
context
|
|
1039
|
+
}) => {
|
|
1040
|
+
const requestedDirectory = normalizeDirectoryArg(context.directory, args.directory);
|
|
1041
|
+
const directoryPath = normalizeRepositoryPath(workspacePath, requestedDirectory);
|
|
1042
|
+
const artifactKind = toWizardArtifactKind(args.artifactKind);
|
|
1043
|
+
if (!artifactKind) {
|
|
1044
|
+
return buildUnsupportedWizardArtifactOutput({
|
|
1045
|
+
artifactKind: args.artifactKind ?? '',
|
|
1046
|
+
directoryPath
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
if (artifactKind === 'DESIGN_DOC') {
|
|
1050
|
+
try {
|
|
1051
|
+
const action = toPublishedSkillPreferenceAction(args.action);
|
|
1052
|
+
const workspaceResolution = await resolveWorkspace({
|
|
1053
|
+
config,
|
|
1054
|
+
directory: requestedDirectory
|
|
1055
|
+
});
|
|
1056
|
+
const response = await fetchPublishedSkillsGraphQl({
|
|
1057
|
+
worktree: input.worktree,
|
|
1058
|
+
config,
|
|
1059
|
+
query: SET_WIZARD_ARTIFACT_PREFERENCE_MUTATION,
|
|
1060
|
+
variables: {
|
|
1061
|
+
input: {
|
|
1062
|
+
workspaceSlug: workspaceResolution.workspaceSlug,
|
|
1063
|
+
repositoryUrl: workspaceResolution.repositoryUrl,
|
|
1064
|
+
directoryPath: workspaceResolution.directoryPath,
|
|
1065
|
+
artifactKind,
|
|
1066
|
+
artifactSlug: args.artifact.trim(),
|
|
1067
|
+
preferenceScope: toPublishedSkillPreferenceScope(args.preferenceScope, 'project') === 'global' ? 'GLOBAL' : 'WORKSPACE',
|
|
1068
|
+
installed: action === 'install' ? true : action === 'uninstall' ? false : undefined,
|
|
1069
|
+
ignored: action === 'ignore' ? true : action === 'unignore' ? false : undefined
|
|
1070
|
+
}
|
|
1071
|
+
},
|
|
1072
|
+
signal: context.abort,
|
|
1073
|
+
onAuthStateChanged: clearPublishedSkillState
|
|
1074
|
+
});
|
|
1075
|
+
if (!response.ok) {
|
|
1076
|
+
return {
|
|
1077
|
+
output: JSON.stringify({
|
|
1078
|
+
pluginId: PLUGIN_ID,
|
|
1079
|
+
status: response.result.status,
|
|
1080
|
+
artifactKind,
|
|
1081
|
+
requestedIdentifier: args.artifact,
|
|
1082
|
+
requestedDirectoryPath: directoryPath,
|
|
1083
|
+
message: response.result.message,
|
|
1084
|
+
fetchedAt: response.result.fetchedAt,
|
|
1085
|
+
source: response.result.source
|
|
1086
|
+
}, null, 2),
|
|
1087
|
+
metadata: {
|
|
1088
|
+
status: response.result.status,
|
|
1089
|
+
artifactKind,
|
|
1090
|
+
directoryPath
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
return {
|
|
1095
|
+
output: JSON.stringify({
|
|
1096
|
+
pluginId: PLUGIN_ID,
|
|
1097
|
+
status: 'updated',
|
|
1098
|
+
artifactKind,
|
|
1099
|
+
requestedIdentifier: args.artifact,
|
|
1100
|
+
action,
|
|
1101
|
+
requestedDirectoryPath: directoryPath,
|
|
1102
|
+
preferenceState: response.data.setWizardArtifactPreference,
|
|
1103
|
+
message: 'Wizard artifact preference updated through the generic backend API.'
|
|
1104
|
+
}, null, 2),
|
|
1105
|
+
metadata: {
|
|
1106
|
+
status: 'updated',
|
|
1107
|
+
artifactKind,
|
|
1108
|
+
directoryPath,
|
|
1109
|
+
action
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
} catch (error) {
|
|
1113
|
+
return {
|
|
1114
|
+
output: JSON.stringify({
|
|
1115
|
+
pluginId: PLUGIN_ID,
|
|
1116
|
+
status: 'preference_failed',
|
|
1117
|
+
artifactKind,
|
|
1118
|
+
requestedIdentifier: args.artifact,
|
|
1119
|
+
requestedDirectoryPath: directoryPath,
|
|
1120
|
+
message: error instanceof Error ? error.message : 'Unknown wizard artifact preference failure.'
|
|
1121
|
+
}, null, 2),
|
|
1122
|
+
metadata: {
|
|
1123
|
+
status: 'preference_failed',
|
|
1124
|
+
artifactKind,
|
|
1125
|
+
directoryPath
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
945
1129
|
}
|
|
1130
|
+
const result = await executePublishedSkillPreferenceTool({
|
|
1131
|
+
args: {
|
|
1132
|
+
skill: args.artifact,
|
|
1133
|
+
action: args.action,
|
|
1134
|
+
preferenceScope: args.preferenceScope,
|
|
1135
|
+
directory: args.directory
|
|
1136
|
+
},
|
|
1137
|
+
context
|
|
1138
|
+
});
|
|
1139
|
+
return withWizardArtifactEnvelope(result, artifactKind);
|
|
946
1140
|
};
|
|
947
1141
|
const executeEditorPublishSkillTool = async ({
|
|
948
1142
|
args,
|
|
@@ -1039,7 +1233,6 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
1039
1233
|
};
|
|
1040
1234
|
};
|
|
1041
1235
|
const role = initialAuthState?.role ?? null;
|
|
1042
|
-
const hasSharedToolAccess = role === 'ADMIN' || role === 'EDITOR';
|
|
1043
1236
|
const isEditor = role === 'EDITOR';
|
|
1044
1237
|
const {
|
|
1045
1238
|
sharedTools,
|
|
@@ -1049,10 +1242,22 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
1049
1242
|
args,
|
|
1050
1243
|
context
|
|
1051
1244
|
}),
|
|
1245
|
+
fetchWizardArtifactCatalog: (args, context) => executeWizardArtifactCatalogTool({
|
|
1246
|
+
args,
|
|
1247
|
+
context
|
|
1248
|
+
}),
|
|
1249
|
+
fetchWizardArtifacts: (args, context) => executeWizardArtifactFetchTool({
|
|
1250
|
+
args,
|
|
1251
|
+
context
|
|
1252
|
+
}),
|
|
1052
1253
|
updatePublishedSkillPreference: (args, context) => executePublishedSkillPreferenceTool({
|
|
1053
1254
|
args,
|
|
1054
1255
|
context
|
|
1055
1256
|
}),
|
|
1257
|
+
updateWizardArtifactPreference: (args, context) => executeWizardArtifactPreferenceTool({
|
|
1258
|
+
args,
|
|
1259
|
+
context
|
|
1260
|
+
}),
|
|
1056
1261
|
getStatus: (args, context) => executeStatusTool({
|
|
1057
1262
|
args,
|
|
1058
1263
|
context
|
|
@@ -1063,7 +1268,7 @@ export const OpencodeWizardSkillsPlugin = async input => {
|
|
|
1063
1268
|
})
|
|
1064
1269
|
});
|
|
1065
1270
|
return {
|
|
1066
|
-
tool:
|
|
1271
|
+
tool: {
|
|
1067
1272
|
...sharedTools,
|
|
1068
1273
|
...(isEditor ? editorOnlyTools : {})
|
|
1069
1274
|
},
|