@aexol/opencode-wizard 0.1.15 → 0.1.16
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 +2 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.js +19 -0
- package/dist/config.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.js +9 -36
- package/dist/server.js.map +1 -1
- package/dist/storage.d.ts +5 -0
- package/dist/storage.js +43 -0
- package/dist/storage.js.map +1 -0
- package/dist/tui.js +127 -64
- package/dist/tui.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -21,6 +21,7 @@ Useful commands:
|
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
23
|
npm run typecheck
|
|
24
|
+
npm run test
|
|
24
25
|
npm run build
|
|
25
26
|
npm run release:check
|
|
26
27
|
```
|
|
@@ -45,7 +46,7 @@ Use `skills.urls` only for public registries that are intentionally cacheable by
|
|
|
45
46
|
|
|
46
47
|
## Catalog discovery and auth bootstrap
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
Catalog discovery uses the backend-issued plugin session token stored at `~/.config/opencode/opencode-wizard.json` (`auth` field); the plugin does not persist or send Microsoft/Entra tokens to GraphQL. If no valid plugin session exists, no-arg `opencode_wizard_published_skills_fetch`, explicit `opencode_wizard_status`, TUI status, and chat/system-context startup may start the browser Entra PKCE flow and exchange the callback for a fresh backend-issued plugin session.
|
|
49
50
|
|
|
50
51
|
Call `opencode_wizard_published_skills_fetch` without `skill` or `skills` to manually bootstrap plugin login if needed and return catalog-only discovery output for the current directory scope.
|
|
51
52
|
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const PUBLISHED_BACKEND_ORIGIN = "https://opencode-wizard.aexol.work";
|
|
2
|
+
export declare const normalizeBackendOrigin: (value: string | undefined) => string | null;
|
|
3
|
+
export declare const resolveBackendOriginFromValues: ({ environmentBackendOrigin, localBackendOrigin, }: {
|
|
4
|
+
environmentBackendOrigin?: string;
|
|
5
|
+
localBackendOrigin?: string;
|
|
6
|
+
}) => string;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const PUBLISHED_BACKEND_ORIGIN = 'https://opencode-wizard.aexol.work';
|
|
2
|
+
export const normalizeBackendOrigin = value => {
|
|
3
|
+
if (!value) return null;
|
|
4
|
+
try {
|
|
5
|
+
const normalizedUrl = new URL(value);
|
|
6
|
+
return normalizedUrl.origin;
|
|
7
|
+
} catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
export const resolveBackendOriginFromValues = ({
|
|
12
|
+
environmentBackendOrigin,
|
|
13
|
+
localBackendOrigin
|
|
14
|
+
}) => {
|
|
15
|
+
const configuredBackendOrigin = normalizeBackendOrigin(environmentBackendOrigin) ?? normalizeBackendOrigin(localBackendOrigin);
|
|
16
|
+
if (configuredBackendOrigin) return configuredBackendOrigin;
|
|
17
|
+
return PUBLISHED_BACKEND_ORIGIN;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["PUBLISHED_BACKEND_ORIGIN","normalizeBackendOrigin","value","normalizedUrl","URL","origin","resolveBackendOriginFromValues","environmentBackendOrigin","localBackendOrigin","configuredBackendOrigin"],"sources":["../src/config.ts"],"sourcesContent":["export const PUBLISHED_BACKEND_ORIGIN = 'https://opencode-wizard.aexol.work';\n\nexport const normalizeBackendOrigin = (value: string | undefined): string | null => {\n if (!value) return null;\n\n try {\n const normalizedUrl = new URL(value);\n return normalizedUrl.origin;\n } catch {\n return null;\n }\n};\n\nexport const resolveBackendOriginFromValues = ({\n environmentBackendOrigin,\n localBackendOrigin,\n}: {\n environmentBackendOrigin?: string;\n localBackendOrigin?: string;\n}): string => {\n const configuredBackendOrigin =\n normalizeBackendOrigin(environmentBackendOrigin) ?? normalizeBackendOrigin(localBackendOrigin);\n\n if (configuredBackendOrigin) return configuredBackendOrigin;\n\n return PUBLISHED_BACKEND_ORIGIN;\n};\n"],"mappings":"AAAA,OAAO,MAAMA,wBAAwB,GAAG,oCAAoC;AAE5E,OAAO,MAAMC,sBAAsB,GAAIC,KAAyB,IAAoB;EAClF,IAAI,CAACA,KAAK,EAAE,OAAO,IAAI;EAEvB,IAAI;IACF,MAAMC,aAAa,GAAG,IAAIC,GAAG,CAACF,KAAK,CAAC;IACpC,OAAOC,aAAa,CAACE,MAAM;EAC7B,CAAC,CAAC,MAAM;IACN,OAAO,IAAI;EACb;AACF,CAAC;AAED,OAAO,MAAMC,8BAA8B,GAAGA,CAAC;EAC7CC,wBAAwB;EACxBC;AAIF,CAAC,KAAa;EACZ,MAAMC,uBAAuB,GAC3BR,sBAAsB,CAACM,wBAAwB,CAAC,IAAIN,sBAAsB,CAACO,kBAAkB,CAAC;EAEhG,IAAIC,uBAAuB,EAAE,OAAOA,uBAAuB;EAE3D,OAAOT,wBAAwB;AACjC,CAAC","ignoreList":[]}
|
package/dist/server.d.ts
CHANGED
|
@@ -185,6 +185,11 @@ export type NativeSkillsUrlCompatibility = {
|
|
|
185
185
|
guidance: string;
|
|
186
186
|
};
|
|
187
187
|
export declare const NATIVE_SKILLS_URL_COMPATIBILITY: NativeSkillsUrlCompatibility;
|
|
188
|
+
type PublishedSkillsResult = {
|
|
189
|
+
directoryPath: string;
|
|
190
|
+
workspaceResolution: WorkspaceResolution;
|
|
191
|
+
fetchResult: FetchResult;
|
|
192
|
+
};
|
|
188
193
|
type PublishedSkillsIgnoreState = {
|
|
189
194
|
scopeKey: string;
|
|
190
195
|
userKey: string;
|
|
@@ -238,6 +243,7 @@ export declare const selectPublishedSkills: <TItem extends PublishedSkillCatalog
|
|
|
238
243
|
selectedItems: TItem[];
|
|
239
244
|
missingIdentifiers: string[];
|
|
240
245
|
};
|
|
246
|
+
export declare const buildSystemNote: (result: PublishedSkillsResult, config: ResolvedConfig, details: PublishedSkillDetail[]) => string | null;
|
|
241
247
|
declare const toWorkspaceResolutionOutput: (resolution: WorkspaceResolution) => {
|
|
242
248
|
requestedDirectory: string;
|
|
243
249
|
repositoryRoot: string;
|
package/dist/server.js
CHANGED
|
@@ -6,6 +6,8 @@ import crypto from 'node:crypto';
|
|
|
6
6
|
import { execFile } from 'node:child_process';
|
|
7
7
|
import { promisify } from 'node:util';
|
|
8
8
|
import { URL, fileURLToPath } from 'node:url';
|
|
9
|
+
import { resolveBackendOriginFromValues } from './config.js';
|
|
10
|
+
import { deleteFileIfExists, readJsonFile, writePrivateJsonFile } from './storage.js';
|
|
9
11
|
const execFileAsync = promisify(execFile);
|
|
10
12
|
const MODULE_FILE_PATH = fileURLToPath(import.meta.url);
|
|
11
13
|
const PACKAGE_ROOT_PATH = path.resolve(path.dirname(MODULE_FILE_PATH), '..');
|
|
@@ -14,7 +16,6 @@ const CACHE_TTL_MS = 30_000;
|
|
|
14
16
|
const ROOT_SKILL_SEED_PATH = '.opencode/skills';
|
|
15
17
|
const GLOBAL_CONFIG_PATH = path.join(os.homedir(), '.config', 'opencode', 'opencode-wizard.json');
|
|
16
18
|
const LEGACY_AUTH_STATE_PATH = 'plugin/opencode-wizard/.generated/auth-state.json';
|
|
17
|
-
const PUBLISHED_BACKEND_ORIGIN = 'https://opencode-wizard.aexol.work';
|
|
18
19
|
const OIDC_ISSUER = 'https://login.microsoftonline.com/86f4caf4-0d6f-4682-9a06-ea57f3e4e76c/v2.0';
|
|
19
20
|
const OIDC_CLIENT_ID = 'da963901-2375-442b-9e99-14e59f43eda2';
|
|
20
21
|
const OIDC_CALLBACK_ORIGIN = 'http://localhost:24953';
|
|
@@ -307,20 +308,12 @@ const readLocalEnvValues = async startDirectory => {
|
|
|
307
308
|
return new Map();
|
|
308
309
|
}
|
|
309
310
|
};
|
|
310
|
-
const normalizeBackendOrigin = value => {
|
|
311
|
-
if (!value) return null;
|
|
312
|
-
try {
|
|
313
|
-
const normalizedUrl = new URL(value);
|
|
314
|
-
return normalizedUrl.origin;
|
|
315
|
-
} catch {
|
|
316
|
-
return null;
|
|
317
|
-
}
|
|
318
|
-
};
|
|
319
311
|
const resolveBackendOrigin = async worktree => {
|
|
320
312
|
const envValues = await readLocalEnvValues(worktree);
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
313
|
+
return resolveBackendOriginFromValues({
|
|
314
|
+
environmentBackendOrigin: process.env.OPENCODE_WIZARD_BACKEND_ORIGIN,
|
|
315
|
+
localBackendOrigin: envValues.get('OPENCODE_WIZARD_BACKEND_ORIGIN')
|
|
316
|
+
});
|
|
324
317
|
};
|
|
325
318
|
const toWorkspaceSlug = value => {
|
|
326
319
|
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9-]+/gu, '-').replace(/^-+|-+$/gu, '');
|
|
@@ -436,26 +429,6 @@ const formatSkillLabel = item => {
|
|
|
436
429
|
return item.skill.name;
|
|
437
430
|
};
|
|
438
431
|
const toFrontmatterString = value => JSON.stringify(value);
|
|
439
|
-
const readJsonFile = async filePath => {
|
|
440
|
-
try {
|
|
441
|
-
const raw = await fs.readFile(filePath, 'utf8');
|
|
442
|
-
const parsed = JSON.parse(raw);
|
|
443
|
-
return parsed;
|
|
444
|
-
} catch {
|
|
445
|
-
return null;
|
|
446
|
-
}
|
|
447
|
-
};
|
|
448
|
-
const writeJsonFile = async (filePath, value) => {
|
|
449
|
-
await fs.mkdir(path.dirname(filePath), {
|
|
450
|
-
recursive: true
|
|
451
|
-
});
|
|
452
|
-
await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
453
|
-
};
|
|
454
|
-
const deleteFileIfExists = async filePath => {
|
|
455
|
-
await fs.rm(filePath, {
|
|
456
|
-
force: true
|
|
457
|
-
});
|
|
458
|
-
};
|
|
459
432
|
const isRecord = value => {
|
|
460
433
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
461
434
|
};
|
|
@@ -472,7 +445,7 @@ const readGlobalConfig = async configFile => {
|
|
|
472
445
|
return {};
|
|
473
446
|
};
|
|
474
447
|
const writeGlobalConfig = async (configFile, config) => {
|
|
475
|
-
await
|
|
448
|
+
await writePrivateJsonFile(configFile, config);
|
|
476
449
|
};
|
|
477
450
|
const withoutLegacyPublishedSkillPreferences = config => {
|
|
478
451
|
const {
|
|
@@ -833,7 +806,7 @@ const buildSkillDetailSnippetLine = detail => {
|
|
|
833
806
|
const body = detail.markdownBody || detail.renderedContent || detail.markdownDocument;
|
|
834
807
|
return `- ${detail.artifactName || detail.skillName}: ${truncateText(body, 700)}`;
|
|
835
808
|
};
|
|
836
|
-
const buildSystemNote = (result, config, details) => {
|
|
809
|
+
export const buildSystemNote = (result, config, details) => {
|
|
837
810
|
if (!result.fetchResult.ok) return null;
|
|
838
811
|
const catalog = toPublishedSkillCatalog(result.fetchResult.payload);
|
|
839
812
|
const skillNames = catalog.skills.map(skill => skill.artifactName || skill.skillName || skill.skillSlug);
|
|
@@ -844,7 +817,7 @@ const buildSystemNote = (result, config, details) => {
|
|
|
844
817
|
const projectSkills = catalog.skills.filter(skill => skill.contextKind === 'project').slice(0, 5).map(buildSkillCatalogLine);
|
|
845
818
|
const detailLines = details.slice(0, SYSTEM_NOTE_DETAIL_LIMIT).map(buildSkillDetailSnippetLine);
|
|
846
819
|
const detailBlock = detailLines.length > 0 ? ` Loaded body snippets (capped):\n${truncateText(detailLines.join('\n'), SYSTEM_NOTE_DETAIL_CHAR_LIMIT)}` : '';
|
|
847
|
-
return [result.fetchResult.payload.workspace ? `opencode-wizard published
|
|
820
|
+
return [result.fetchResult.payload.workspace ? `Prefer opencode-wizard backend-published fetched skill bodies for scoped/private wizard skills in workspace ${result.fetchResult.payload.workspace.slug}.` : 'Prefer opencode-wizard backend-published global fetched skill bodies; workspace-specific skills are unavailable because the workspace was not found.', `Current directory: ${result.directoryPath}.`, `Published skills for this scope: ${renderedSkillNames}${renderedCountSuffix}; counts: ${catalog.assignmentCounts.global} global, ${catalog.assignmentCounts.project} project, ${catalog.assignmentCounts.other} other.`, 'Use catalog whenToUse guidance to decide applicability; when it matches the task, fetch full bodies with opencode_wizard_published_skills_fetch and prefer those fetched bodies for current scoped/private wizard guidance.', 'GLOBAL_CONTEXT skills are active context skills and are not project-installable; PROJECT_INSTALLABLE skills can be assigned globally or to project/workspace scopes; assignment rows decide which skills are active here.', globalSkills.length > 0 ? `Global context skills:\n${globalSkills.join('\n')}` : 'Global context skills: none.', projectSkills.length > 0 ? `Project-scoped active skills:\n${projectSkills.join('\n')}` : 'Project-scoped active skills: none.', detailBlock, 'Local/native sources can still complement wizard skills: .opencode/skills is source seed content, skills.urls is a public/static complement, and backend-published fetched bodies are preferred for private/scoped wizard guidance.', `Root source seed path remains seed/source content: ${config.rootSkillSeedPath}/**.`].filter(line => line.length > 0).join(' ');
|
|
848
821
|
};
|
|
849
822
|
const toWorkspaceResolutionOutput = resolution => ({
|
|
850
823
|
requestedDirectory: resolution.requestedDirectory,
|