@hiai-gg/hiai-opencode 0.1.5 → 0.1.6

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.
Files changed (67) hide show
  1. package/.env.example +14 -8
  2. package/AGENTS.md +14 -5
  3. package/LICENSE.md +0 -1
  4. package/README.md +42 -23
  5. package/assets/cli/hiai-opencode.mjs +590 -7
  6. package/assets/mcp/mempalace.mjs +159 -25
  7. package/config/hiai-opencode.schema.json +13 -2
  8. package/dist/agents/dynamic-agent-core-sections.d.ts +4 -1
  9. package/dist/agents/dynamic-agent-prompt-builder.d.ts +1 -1
  10. package/dist/config/platform-schema.d.ts +2 -6
  11. package/dist/config/schema/commands.d.ts +1 -0
  12. package/dist/config/schema/oh-my-opencode-config.d.ts +1 -3
  13. package/dist/config/types.d.ts +1 -3
  14. package/dist/features/builtin-commands/templates/doctor.d.ts +1 -0
  15. package/dist/features/builtin-commands/types.d.ts +1 -1
  16. package/dist/features/builtin-skills/skills/hiai-opencode-setup.d.ts +2 -0
  17. package/dist/features/builtin-skills/skills/index.d.ts +1 -0
  18. package/dist/index.js +348 -1424
  19. package/dist/shared/mcp-static-export.d.ts +22 -0
  20. package/dist/tools/ast-grep/constants.d.ts +1 -1
  21. package/dist/tools/ast-grep/environment-check.d.ts +1 -5
  22. package/dist/tools/ast-grep/language-support.d.ts +0 -1
  23. package/dist/tools/ast-grep/types.d.ts +1 -2
  24. package/hiai-opencode.json +4 -2
  25. package/package.json +6 -4
  26. package/src/agents/bob/default.ts +6 -1
  27. package/src/agents/bob/gpt-pro.ts +1 -0
  28. package/src/agents/bob.ts +1 -0
  29. package/src/agents/coder/gpt-codex.ts +1 -0
  30. package/src/agents/coder/gpt-pro.ts +1 -0
  31. package/src/agents/coder/gpt.ts +1 -0
  32. package/src/agents/dynamic-agent-core-sections.ts +36 -0
  33. package/src/agents/dynamic-agent-prompt-builder.ts +1 -0
  34. package/src/config/defaults.ts +14 -1
  35. package/src/config/model-slots-and-export.test.ts +55 -0
  36. package/src/config/platform-schema.ts +1 -3
  37. package/src/config/schema/commands.ts +1 -0
  38. package/src/config/schema/oh-my-opencode-config.ts +0 -3
  39. package/src/config/types.ts +1 -3
  40. package/src/features/builtin-commands/commands.ts +7 -0
  41. package/src/features/builtin-commands/templates/doctor.ts +43 -0
  42. package/src/features/builtin-commands/types.ts +1 -1
  43. package/src/features/builtin-skills/skills/hiai-opencode-setup.ts +69 -0
  44. package/src/features/builtin-skills/skills/index.ts +1 -0
  45. package/src/features/builtin-skills/skills.ts +10 -1
  46. package/src/index.ts +3 -75
  47. package/src/shared/mcp-static-export.ts +121 -0
  48. package/src/tools/ast-grep/constants.ts +1 -1
  49. package/src/tools/ast-grep/environment-check.ts +2 -32
  50. package/src/tools/ast-grep/language-support.ts +0 -3
  51. package/src/tools/ast-grep/types.ts +1 -2
  52. package/src/tools/skill-mcp/tools.test.ts +44 -0
  53. package/dist/ast-grep-napi.win32-x64-msvc-67c0y8nc.node +0 -0
  54. package/dist/config/loader.test.d.ts +0 -1
  55. package/dist/config/models.d.ts +0 -13
  56. package/dist/internals/plugins/websearch-cited/google.d.ts +0 -38
  57. package/dist/internals/plugins/websearch-cited/index.d.ts +0 -17
  58. package/dist/internals/plugins/websearch-cited/openai.d.ts +0 -9
  59. package/dist/internals/plugins/websearch-cited/openrouter.d.ts +0 -2
  60. package/dist/internals/plugins/websearch-cited/types.d.ts +0 -5
  61. package/src/internals/plugins/websearch-cited/LICENSE +0 -214
  62. package/src/internals/plugins/websearch-cited/codex_prompt.txt +0 -79
  63. package/src/internals/plugins/websearch-cited/google.ts +0 -749
  64. package/src/internals/plugins/websearch-cited/index.ts +0 -306
  65. package/src/internals/plugins/websearch-cited/openai.ts +0 -407
  66. package/src/internals/plugins/websearch-cited/openrouter.ts +0 -190
  67. package/src/internals/plugins/websearch-cited/types.ts +0 -7
package/dist/index.js CHANGED
@@ -32523,1270 +32523,6 @@ var require_dist2 = __commonJS((exports) => {
32523
32523
  exports.visitAsync = visit2.visitAsync;
32524
32524
  });
32525
32525
 
32526
- // src/internals/plugins/websearch-cited/google.ts
32527
- function buildGeminiUrl(model) {
32528
- const encoded = encodeURIComponent(model);
32529
- return `${GEMINI_API_BASE}/models/${encoded}:generateContent`;
32530
- }
32531
- async function runGeminiWebSearch(options) {
32532
- const response = await fetch(buildGeminiUrl(options.model), {
32533
- method: "POST",
32534
- headers: {
32535
- "Content-Type": "application/json",
32536
- "x-goog-api-key": options.apiKey,
32537
- "User-Agent": CODE_ASSIST_HEADERS["User-Agent"],
32538
- "X-Goog-Api-Client": CODE_ASSIST_HEADERS["X-Goog-Api-Client"],
32539
- "Client-Metadata": CODE_ASSIST_HEADERS["Client-Metadata"]
32540
- },
32541
- body: JSON.stringify({
32542
- contents: [
32543
- {
32544
- role: "user",
32545
- parts: [{ text: options.query }]
32546
- }
32547
- ],
32548
- tools: [{ googleSearch: {} }]
32549
- }),
32550
- signal: options.abortSignal
32551
- });
32552
- if (!response.ok) {
32553
- const message = await readErrorMessage(response);
32554
- throw new Error(message ?? `Request failed with status ${response.status}`);
32555
- }
32556
- return await response.json();
32557
- }
32558
- function formatWebSearchResponse(response, query) {
32559
- const responseText = extractResponseText(response);
32560
- if (!responseText || !responseText.trim()) {
32561
- return `No search results or information found for query: "${query}"`;
32562
- }
32563
- const metadata = extractGroundingMetadata(response);
32564
- const sources = metadata?.groundingChunks;
32565
- const hasSources = Boolean(sources && sources.length > 0);
32566
- let modifiedText = responseText;
32567
- if (hasSources && metadata) {
32568
- const insertions = buildCitationInsertions(metadata);
32569
- if (insertions.length > 0) {
32570
- modifiedText = insertMarkersByUtf8Index(modifiedText, insertions);
32571
- }
32572
- }
32573
- if (hasSources && sources) {
32574
- const sourceLines = sources.map((source, index) => {
32575
- const title = source.web?.title || "Untitled";
32576
- const uri = source.web?.uri || "No URI";
32577
- return `[${index + 1}] ${title} (${uri})`;
32578
- });
32579
- modifiedText += `
32580
-
32581
- Sources:
32582
- ${sourceLines.join(`
32583
- `)}`;
32584
- }
32585
- return modifiedText;
32586
- }
32587
- function extractResponseText(response) {
32588
- const parts = response.candidates?.[0]?.content?.parts;
32589
- if (!parts || parts.length === 0) {
32590
- return;
32591
- }
32592
- let combined = "";
32593
- for (const part of parts) {
32594
- if (part.thought) {
32595
- continue;
32596
- }
32597
- if (typeof part.text === "string") {
32598
- combined += part.text;
32599
- }
32600
- }
32601
- return combined || undefined;
32602
- }
32603
- function extractGroundingMetadata(response) {
32604
- return response.candidates?.[0]?.groundingMetadata;
32605
- }
32606
- function buildCitationInsertions(metadata) {
32607
- const supports = metadata?.groundingSupports;
32608
- if (!supports || supports.length === 0) {
32609
- return [];
32610
- }
32611
- const insertions = [];
32612
- for (const support of supports) {
32613
- const segment = support.segment;
32614
- const indices = support.groundingChunkIndices;
32615
- if (!segment || segment.endIndex == null || !indices || indices.length === 0) {
32616
- continue;
32617
- }
32618
- const uniqueSorted = Array.from(new Set(indices)).sort((a, b) => a - b);
32619
- const marker = uniqueSorted.map((idx) => `[${idx + 1}]`).join("");
32620
- insertions.push({
32621
- index: segment.endIndex,
32622
- marker
32623
- });
32624
- }
32625
- insertions.sort((a, b) => b.index - a.index);
32626
- return insertions;
32627
- }
32628
- function insertMarkersByUtf8Index(text, insertions) {
32629
- if (insertions.length === 0) {
32630
- return text;
32631
- }
32632
- const encoder = new TextEncoder;
32633
- const responseBytes = encoder.encode(text);
32634
- const parts = [];
32635
- let lastIndex = responseBytes.length;
32636
- for (const insertion of insertions) {
32637
- const position = Math.min(insertion.index, lastIndex);
32638
- parts.unshift(responseBytes.subarray(position, lastIndex));
32639
- parts.unshift(encoder.encode(insertion.marker));
32640
- lastIndex = position;
32641
- }
32642
- parts.unshift(responseBytes.subarray(0, lastIndex));
32643
- const totalLength = parts.reduce((sum, part) => sum + part.length, 0);
32644
- const finalBytes = new Uint8Array(totalLength);
32645
- let offset = 0;
32646
- for (const part of parts) {
32647
- finalBytes.set(part, offset);
32648
- offset += part.length;
32649
- }
32650
- return new TextDecoder().decode(finalBytes);
32651
- }
32652
-
32653
- class GeminiApiKeyClient {
32654
- apiKey;
32655
- model;
32656
- constructor(apiKey, model) {
32657
- const normalizedKey = apiKey.trim();
32658
- const normalizedModel = model.trim();
32659
- if (!normalizedKey || !normalizedModel) {
32660
- throw new Error("Invalid Google API configuration");
32661
- }
32662
- this.apiKey = normalizedKey;
32663
- this.model = normalizedModel;
32664
- }
32665
- async search(query, abortSignal) {
32666
- const normalizedQuery = query.trim();
32667
- const response = await runGeminiWebSearch({
32668
- apiKey: this.apiKey,
32669
- model: this.model,
32670
- query: normalizedQuery,
32671
- abortSignal
32672
- });
32673
- return formatWebSearchResponse(response, normalizedQuery);
32674
- }
32675
- }
32676
- function parseRefresh(refresh) {
32677
- const normalized = refresh.trim();
32678
- if (!normalized) {
32679
- return { refreshToken: "" };
32680
- }
32681
- const [token, project, managed] = normalized.split("|");
32682
- const refreshToken = token?.trim() ?? "";
32683
- const projectId = project?.trim() ?? "";
32684
- const managedProjectId = managed?.trim() ?? "";
32685
- return {
32686
- refreshToken,
32687
- projectId: projectId || undefined,
32688
- managedProjectId: managedProjectId || undefined
32689
- };
32690
- }
32691
- function getCachedAccess(refreshToken) {
32692
- const cached3 = tokenCache.get(refreshToken);
32693
- if (!cached3) {
32694
- return;
32695
- }
32696
- if (cached3.expiresAt <= Date.now() + REFRESH_BUFFER_MS) {
32697
- tokenCache.delete(refreshToken);
32698
- return;
32699
- }
32700
- return cached3;
32701
- }
32702
- function cacheToken(refreshToken, accessToken, expiresAt) {
32703
- if (!refreshToken || !accessToken) {
32704
- return;
32705
- }
32706
- if (typeof expiresAt === "number" && Number.isFinite(expiresAt)) {
32707
- tokenCache.set(refreshToken, { accessToken, expiresAt });
32708
- }
32709
- }
32710
- async function requestToken(refreshToken) {
32711
- const requestTime = Date.now();
32712
- const response = await fetch(OAUTH_TOKEN_ENDPOINT, {
32713
- method: "POST",
32714
- headers: {
32715
- "Content-Type": "application/x-www-form-urlencoded"
32716
- },
32717
- body: new URLSearchParams({
32718
- grant_type: "refresh_token",
32719
- refresh_token: refreshToken,
32720
- client_id: ANTIGRAVITY_CLIENT_ID,
32721
- client_secret: ANTIGRAVITY_CLIENT_SECRET
32722
- })
32723
- });
32724
- if (!response.ok) {
32725
- const message = await readErrorMessage(response);
32726
- throw new Error(message ?? `Request failed with status ${response.status}`);
32727
- }
32728
- const payload = await response.json();
32729
- if (!payload.access_token) {
32730
- throw new Error("Token refresh response missing access_token");
32731
- }
32732
- const expiresIn = typeof payload.expires_in === "number" && Number.isFinite(payload.expires_in) ? payload.expires_in : 3600;
32733
- const expiresAt = expiresIn > 0 ? requestTime + expiresIn * 1000 : requestTime;
32734
- return {
32735
- accessToken: payload.access_token,
32736
- expiresAt
32737
- };
32738
- }
32739
- async function refreshAccessToken(refreshToken) {
32740
- const result = await requestToken(refreshToken);
32741
- cacheToken(refreshToken, result.accessToken, result.expiresAt);
32742
- return result;
32743
- }
32744
- function buildMetadata(projectId) {
32745
- const metadata = {
32746
- ideType: "IDE_UNSPECIFIED",
32747
- platform: "PLATFORM_UNSPECIFIED",
32748
- pluginType: "GEMINI"
32749
- };
32750
- if (projectId) {
32751
- metadata.duetProject = projectId;
32752
- }
32753
- return metadata;
32754
- }
32755
- async function loadManagedProject(accessToken, projectId, abortSignal) {
32756
- const loadHeaders = {
32757
- "Content-Type": "application/json",
32758
- Authorization: `Bearer ${accessToken}`,
32759
- "User-Agent": "google-api-nodejs-client/9.15.1",
32760
- "X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
32761
- "Client-Metadata": CODE_ASSIST_HEADERS["Client-Metadata"]
32762
- };
32763
- const requestBody = {
32764
- metadata: buildMetadata(projectId)
32765
- };
32766
- const loadEndpoints = Array.from(new Set([...CODE_ASSIST_LOAD_ENDPOINTS, ...CODE_ASSIST_GENERATE_ENDPOINTS]));
32767
- for (const baseEndpoint of loadEndpoints) {
32768
- try {
32769
- const response = await fetch(`${baseEndpoint}${GEMINI_CODE_ASSIST_LOAD_PATH}`, {
32770
- method: "POST",
32771
- headers: loadHeaders,
32772
- body: JSON.stringify(requestBody),
32773
- signal: abortSignal
32774
- });
32775
- if (!response.ok) {
32776
- continue;
32777
- }
32778
- return await response.json();
32779
- } catch {}
32780
- }
32781
- return null;
32782
- }
32783
- function extractManagedProjectId(payload) {
32784
- if (!payload) {
32785
- return;
32786
- }
32787
- const project = payload.cloudaicompanionProject;
32788
- if (typeof project === "string" && project.trim() !== "") {
32789
- return project;
32790
- }
32791
- if (project && typeof project === "object" && project.id) {
32792
- const id = project.id;
32793
- if (typeof id === "string" && id.trim() !== "") {
32794
- return id;
32795
- }
32796
- }
32797
- return;
32798
- }
32799
- async function resolveProjectId(accessToken, refreshToken, refreshParts, abortSignal) {
32800
- if (refreshParts.managedProjectId) {
32801
- return refreshParts.managedProjectId;
32802
- }
32803
- const cached3 = projectCache.get(refreshToken);
32804
- if (cached3) {
32805
- return cached3;
32806
- }
32807
- const fallbackProjectId = ANTIGRAVITY_DEFAULT_PROJECT_ID;
32808
- const desiredProjectId = refreshParts.projectId ?? fallbackProjectId;
32809
- const loadPayload = await loadManagedProject(accessToken, desiredProjectId, abortSignal);
32810
- const resolvedManagedProjectId = extractManagedProjectId(loadPayload);
32811
- if (resolvedManagedProjectId) {
32812
- projectCache.set(refreshToken, resolvedManagedProjectId);
32813
- return resolvedManagedProjectId;
32814
- }
32815
- if (refreshParts.projectId) {
32816
- return refreshParts.projectId;
32817
- }
32818
- return fallbackProjectId;
32819
- }
32820
- function parseExpires(expires) {
32821
- if (typeof expires === "number" && Number.isFinite(expires)) {
32822
- return expires;
32823
- }
32824
- return;
32825
- }
32826
- function accessTokenExpired(accessToken, expiresAt) {
32827
- if (!accessToken || typeof expiresAt !== "number") {
32828
- return true;
32829
- }
32830
- return expiresAt <= Date.now() + REFRESH_BUFFER_MS;
32831
- }
32832
- async function requestGenerateContent(accessToken, projectId, model, query, abortSignal) {
32833
- const requestPayload = {
32834
- contents: [
32835
- {
32836
- role: "user",
32837
- parts: [{ text: query }]
32838
- }
32839
- ],
32840
- tools: [{ googleSearch: {} }]
32841
- };
32842
- const body = JSON.stringify({
32843
- project: projectId,
32844
- model,
32845
- request: requestPayload,
32846
- requestType: "agent",
32847
- userAgent: "antigravity",
32848
- requestId: `agent-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`
32849
- });
32850
- const headers = {
32851
- "Content-Type": "application/json",
32852
- Authorization: `Bearer ${accessToken}`,
32853
- "User-Agent": CODE_ASSIST_HEADERS["User-Agent"],
32854
- "X-Goog-Api-Client": CODE_ASSIST_HEADERS["X-Goog-Api-Client"],
32855
- "Client-Metadata": CODE_ASSIST_HEADERS["Client-Metadata"]
32856
- };
32857
- let lastError;
32858
- for (const baseUrl of CODE_ASSIST_GENERATE_ENDPOINTS) {
32859
- const response = await fetch(`${baseUrl}${GEMINI_CODE_ASSIST_GENERATE_PATH}`, {
32860
- method: "POST",
32861
- headers,
32862
- body,
32863
- signal: abortSignal
32864
- });
32865
- if (!response.ok) {
32866
- const message = await readErrorMessage(response);
32867
- if (response.status === 401 || response.status === 403) {
32868
- return { ok: false, status: response.status, message };
32869
- }
32870
- lastError = { status: response.status, message };
32871
- continue;
32872
- }
32873
- const text = await response.text();
32874
- if (!text) {
32875
- throw new Error("Empty response from Google Code Assist");
32876
- }
32877
- let parsed;
32878
- try {
32879
- parsed = JSON.parse(text);
32880
- } catch {
32881
- throw new Error("Invalid JSON response from Google Code Assist");
32882
- }
32883
- const effectiveResponse = extractGenerateContentResponse(parsed);
32884
- if (!effectiveResponse) {
32885
- throw new Error("Google Code Assist response did not include a valid response payload");
32886
- }
32887
- return { ok: true, body: effectiveResponse };
32888
- }
32889
- if (lastError) {
32890
- return { ok: false, status: lastError.status, message: lastError.message };
32891
- }
32892
- return {
32893
- ok: false,
32894
- status: 502,
32895
- message: "Request failed for all Google Code Assist endpoints."
32896
- };
32897
- }
32898
- function createGeminiOAuthWebSearchClient(authDetails, model) {
32899
- const refreshParts = parseRefresh(authDetails.refresh ?? "");
32900
- const refreshToken = refreshParts.refreshToken;
32901
- if (!refreshToken) {
32902
- throw new Error("Missing Google OAuth refresh token");
32903
- }
32904
- const initialAccess = authDetails.access?.trim() ?? "";
32905
- const initialExpires = parseExpires(authDetails.expires);
32906
- return {
32907
- async search(query, abortSignal) {
32908
- const normalizedQuery = query.trim();
32909
- const cached3 = getCachedAccess(refreshToken);
32910
- let accessToken = cached3?.accessToken ?? initialAccess;
32911
- let expiresAt = cached3?.expiresAt ?? initialExpires;
32912
- let refreshedThisRequest = false;
32913
- if (accessTokenExpired(accessToken, expiresAt)) {
32914
- const refreshed2 = await refreshAccessToken(refreshToken);
32915
- accessToken = refreshed2.accessToken;
32916
- expiresAt = refreshed2.expiresAt;
32917
- refreshedThisRequest = true;
32918
- }
32919
- if (!accessToken) {
32920
- throw new Error("Missing Google OAuth access token");
32921
- }
32922
- if (typeof expiresAt === "number") {
32923
- cacheToken(refreshToken, accessToken, expiresAt);
32924
- }
32925
- const effectiveProjectId = await resolveProjectId(accessToken, refreshToken, refreshParts, abortSignal);
32926
- const firstAttempt = await requestGenerateContent(accessToken, effectiveProjectId, model, normalizedQuery, abortSignal);
32927
- if (firstAttempt.ok) {
32928
- return formatWebSearchResponse(firstAttempt.body, normalizedQuery);
32929
- }
32930
- const shouldRetry = (firstAttempt.status === 401 || firstAttempt.status === 403) && !refreshedThisRequest;
32931
- if (!shouldRetry) {
32932
- throw new Error(firstAttempt.message ?? `Request failed with status ${firstAttempt.status}`);
32933
- }
32934
- tokenCache.delete(refreshToken);
32935
- const refreshed = await refreshAccessToken(refreshToken);
32936
- accessToken = refreshed.accessToken;
32937
- expiresAt = refreshed.expiresAt;
32938
- refreshedThisRequest = true;
32939
- cacheToken(refreshToken, accessToken, expiresAt);
32940
- const retry = await requestGenerateContent(accessToken, effectiveProjectId, model, normalizedQuery, abortSignal);
32941
- if (retry.ok) {
32942
- return formatWebSearchResponse(retry.body, normalizedQuery);
32943
- }
32944
- throw new Error(retry.message ?? `Request failed with status ${retry.status}`);
32945
- }
32946
- };
32947
- }
32948
- function extractGenerateContentResponse(payload) {
32949
- const candidateObject = (() => {
32950
- if (Array.isArray(payload)) {
32951
- for (const item of payload) {
32952
- if (item && typeof item === "object") {
32953
- return item;
32954
- }
32955
- }
32956
- return;
32957
- }
32958
- if (payload && typeof payload === "object") {
32959
- return payload;
32960
- }
32961
- return;
32962
- })();
32963
- if (!candidateObject) {
32964
- return;
32965
- }
32966
- const withResponse = candidateObject;
32967
- if (withResponse.response && typeof withResponse.response === "object") {
32968
- return withResponse.response;
32969
- }
32970
- if (withResponse.candidates) {
32971
- return candidateObject;
32972
- }
32973
- return;
32974
- }
32975
- async function readErrorMessage(response) {
32976
- try {
32977
- const text = await response.text();
32978
- const trimmed = text.trim();
32979
- return trimmed === "" ? undefined : trimmed;
32980
- } catch {
32981
- return;
32982
- }
32983
- }
32984
- function createGeminiWebSearchClient(config4) {
32985
- return new GeminiApiKeyClient(config4.apiKey, config4.model);
32986
- }
32987
- function createWebSearchClientForGoogle(authDetails, model) {
32988
- if (authDetails.type === "api") {
32989
- const apiKey = extractApiKey(authDetails);
32990
- if (!apiKey) {
32991
- throw new Error("Missing Google API key");
32992
- }
32993
- return createGeminiWebSearchClient({
32994
- mode: "api",
32995
- apiKey,
32996
- model
32997
- });
32998
- }
32999
- if (authDetails.type === "oauth") {
33000
- const oauthAuth = authDetails;
33001
- return createGeminiOAuthWebSearchClient(oauthAuth, model);
33002
- }
33003
- throw new Error("Unsupported auth type for Google web search");
33004
- }
33005
- function extractApiKey(authDetails) {
33006
- if (!authDetails || authDetails.type !== "api") {
33007
- return;
33008
- }
33009
- const normalized = authDetails.key.trim();
33010
- return normalized === "" ? undefined : normalized;
33011
- }
33012
- function createGoogleWebsearchClient(model) {
33013
- const normalizedModel = model.trim();
33014
- if (!normalizedModel) {
33015
- throw new Error("Invalid Google web search model");
33016
- }
33017
- return {
33018
- async search(query, abortSignal, getAuth) {
33019
- const normalizedQuery = query.trim();
33020
- if (!normalizedQuery) {
33021
- throw new Error("Query must not be empty");
33022
- }
33023
- const auth2 = await getAuth();
33024
- if (!auth2) {
33025
- throw new Error('Missing auth for provider "google"');
33026
- }
33027
- const client3 = createWebSearchClientForGoogle(auth2, normalizedModel);
33028
- return client3.search(normalizedQuery, abortSignal);
33029
- }
33030
- };
33031
- }
33032
- var GEMINI_API_BASE = "https://generativelanguage.googleapis.com/v1beta", ANTIGRAVITY_ENDPOINT_DAILY = "https://daily-cloudcode-pa.sandbox.googleapis.com", ANTIGRAVITY_ENDPOINT_AUTOPUSH = "https://autopush-cloudcode-pa.sandbox.googleapis.com", ANTIGRAVITY_ENDPOINT_PROD = "https://cloudcode-pa.googleapis.com", GEMINI_CODE_ASSIST_GENERATE_PATH = "/v1internal:generateContent", GEMINI_CODE_ASSIST_LOAD_PATH = "/v1internal:loadCodeAssist", CODE_ASSIST_GENERATE_ENDPOINTS, CODE_ASSIST_LOAD_ENDPOINTS, ANTIGRAVITY_DEFAULT_PROJECT_ID = "rising-fact-p41fc", OAUTH_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token", ANTIGRAVITY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com", ANTIGRAVITY_CLIENT_SECRET = "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", REFRESH_BUFFER_MS = 60000, CODE_ASSIST_HEADERS, tokenCache, projectCache;
33033
- var init_google = __esm(() => {
33034
- CODE_ASSIST_GENERATE_ENDPOINTS = [
33035
- ANTIGRAVITY_ENDPOINT_DAILY,
33036
- ANTIGRAVITY_ENDPOINT_AUTOPUSH,
33037
- ANTIGRAVITY_ENDPOINT_PROD
33038
- ];
33039
- CODE_ASSIST_LOAD_ENDPOINTS = [
33040
- ANTIGRAVITY_ENDPOINT_PROD,
33041
- ANTIGRAVITY_ENDPOINT_DAILY,
33042
- ANTIGRAVITY_ENDPOINT_AUTOPUSH
33043
- ];
33044
- CODE_ASSIST_HEADERS = {
33045
- "User-Agent": "antigravity/1.11.5 windows/amd64",
33046
- "X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
33047
- "Client-Metadata": '{"ideType":"IDE_UNSPECIFIED","platform":"PLATFORM_UNSPECIFIED","pluginType":"GEMINI"}'
33048
- };
33049
- tokenCache = new Map;
33050
- projectCache = new Map;
33051
- });
33052
-
33053
- // src/internals/plugins/websearch-cited/codex_prompt.txt
33054
- var codex_prompt_default = `You are OpenCode, the best coding agent on the planet.
33055
-
33056
- You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
33057
-
33058
- ## Editing constraints
33059
- - Default to ASCII when editing or creating files. Only introduce non-ASCII or other Unicode characters when there is a clear justification and the file already uses them.
33060
- - Only add comments if they are necessary to make a non-obvious block easier to understand.
33061
- - Try to use apply_patch for single file edits, but it is fine to explore other options to make the edit if it does not work well. Do not use apply_patch for changes that are auto-generated (i.e. generating package.json or running a lint or format command like gofmt) or when scripting is more efficient (such as search and replacing a string across a codebase).
33062
-
33063
- ## Tool usage
33064
- - Prefer specialized tools over shell for file operations:
33065
- - Use Read to view files, Edit to modify files, and Write only when needed.
33066
- - Use Glob to find files by name and Grep to search file contents.
33067
- - Use Bash for terminal operations (git, bun, builds, tests, running scripts).
33068
- - Run tool calls in parallel when neither call needs the other\u2019s output; otherwise run sequentially.
33069
-
33070
- ## Git and workspace hygiene
33071
- - You may be in a dirty git worktree.
33072
- * NEVER revert existing changes you did not make unless explicitly requested, since these changes were made by the user.
33073
- * If asked to make a commit or code edits and there are unrelated changes to your work or changes that you didn't make in those files, don't revert those changes.
33074
- * If the changes are in files you've touched recently, you should read carefully and understand how you can work with the changes rather than reverting them.
33075
- * If the changes are in unrelated files, just ignore them and don't revert them.
33076
- - Do not amend commits unless explicitly requested.
33077
- - **NEVER** use destructive commands like \`git reset --hard\` or \`git checkout --\` unless specifically requested or approved by the user.
33078
-
33079
- ## Frontend tasks
33080
- When doing frontend design tasks, avoid collapsing into bland, generic layouts.
33081
- Aim for interfaces that feel intentional and deliberate.
33082
- - Typography: Use expressive, purposeful fonts and avoid default stacks (Inter, Roboto, Arial, system).
33083
- - Color & Look: Choose a clear visual direction; define CSS variables; avoid purple-on-white defaults. No purple bias or dark mode bias.
33084
- - Motion: Use a few meaningful animations (page-load, staggered reveals) instead of generic micro-motions.
33085
- - Background: Don't rely on flat, single-color backgrounds; use gradients, shapes, or subtle patterns to build atmosphere.
33086
- - Overall: Avoid boilerplate layouts and interchangeable UI patterns. Vary themes, type families, and visual languages across outputs.
33087
- - Ensure the page loads properly on both desktop and mobile.
33088
-
33089
- Exception: If working within an existing website or design system, preserve the established patterns, structure, and visual language.
33090
-
33091
- ## Presenting your work and final message
33092
-
33093
- You are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.
33094
-
33095
- - Default: be very concise; friendly coding teammate tone.
33096
- - Default: do the work without asking questions. Treat short tasks as sufficient direction; infer missing details by reading the codebase and following existing conventions.
33097
- - Questions: only ask when you are truly blocked after checking relevant context AND you cannot safely pick a reasonable default. This usually means one of:
33098
- * The request is ambiguous in a way that materially changes the result and you cannot disambiguate by reading the repo.
33099
- * The action is destructive/irreversible, touches production, or changes billing/security posture.
33100
- * You need a secret/credential/value that cannot be inferred (API key, account id, etc.).
33101
- - If you must ask: do all non-blocked work first, then ask exactly one targeted question, include your recommended default, and state what would change based on the answer.
33102
- - Never ask permission questions like "Should I proceed?" or "Do you want me to run tests?"; proceed with the most reasonable option and mention what you did.
33103
- - For substantial work, summarize clearly; follow final\u2011answer formatting.
33104
- - Skip heavy formatting for simple confirmations.
33105
- - Don't dump large files you've written; reference paths only.
33106
- - No "save/copy this file" - User is on the same machine.
33107
- - Offer logical next steps (tests, commits, build) briefly; add verify steps if you couldn't do something.
33108
- - For code changes:
33109
- * Lead with a quick explanation of the change, and then give more details on the context covering where and why a change was made. Do not start this explanation with "summary", just jump right in.
33110
- * If there are natural next steps the user may want to take, suggest them at the end of your response. Do not make suggestions if there are no natural next steps.
33111
- * When suggesting multiple options, use numeric lists for the suggestions so the user can quickly respond with a single number.
33112
- - The user does not command execution outputs. When asked to show the output of a command (e.g. \`git show\`), relay the important details in your answer or summarize the key lines so the user understands the result.
33113
-
33114
- ## Final answer structure and style guidelines
33115
-
33116
- - Plain text; CLI handles styling. Use structure only when it helps scannability.
33117
- - Headers: optional; short Title Case (1-3 words) wrapped in **\u2026**; no blank line before the first bullet; add only if they truly help.
33118
- - Bullets: use - ; merge related points; keep to one line when possible; 4\u20136 per list ordered by importance; keep phrasing consistent.
33119
- - Monospace: backticks for commands/paths/env vars/code ids and inline examples; use for literal keyword bullets; never combine with **.
33120
- - Code samples or multi-line snippets should be wrapped in fenced code blocks; include an info string as often as possible.
33121
- - Structure: group related bullets; order sections general \u2192 specific \u2192 supporting; for subsections, start with a bolded keyword bullet, then items; match complexity to the task.
33122
- - Tone: collaborative, concise, factual; present tense, active voice; self\u2011contained; no "above/below"; parallel wording.
33123
- - Don'ts: no nested bullets/hierarchies; no ANSI codes; don't cram unrelated keywords; keep keyword lists short\u2014wrap/reformat if long; avoid naming formatting styles in answers.
33124
- - Adaptation: code explanations \u2192 precise, structured with code refs; simple tasks \u2192 lead with outcome; big changes \u2192 logical walkthrough + rationale + next actions; casual one-offs \u2192 plain sentences, no headers/bullets.
33125
- - File References: When referencing files in your response follow the below rules:
33126
- * Use inline code to make file paths clickable.
33127
- * Each reference should have a stand alone path. Even if it's the same file.
33128
- * Accepted: absolute, workspace\u2011relative, a/ or b/ diff prefixes, or bare filename/suffix.
33129
- * Optionally include line/column (1\u2011based): :line[:column] or #Lline[Ccolumn] (column defaults to 1).
33130
- * Do not use URIs like file://, vscode://, or https://.
33131
- * Do not provide range of lines
33132
- * Examples: src/app.ts, src/app.ts:42, b/server/index.js#L10, C:\\repo\\project\\main.rs:12:5
33133
- `;
33134
- var init_codex_prompt = () => {};
33135
-
33136
- // src/internals/plugins/websearch-cited/openai.ts
33137
- function buildWebSearchUserPrompt(query) {
33138
- const normalized = query.trim();
33139
- return `perform web search on "${normalized}". Return results with inline citations (**only** source index like [1], no URL in the answer) and end with a Sources list of URLs.`;
33140
- }
33141
- function getAccessToken(auth2) {
33142
- if (auth2.type === "oauth") {
33143
- const access = auth2.access.trim();
33144
- if (!access) {
33145
- throw new Error("Missing OpenAI OAuth access token");
33146
- }
33147
- return access;
33148
- }
33149
- if (auth2.type === "api") {
33150
- const key = auth2.key.trim();
33151
- if (!key) {
33152
- throw new Error("Missing OpenAI API key");
33153
- }
33154
- return key;
33155
- }
33156
- const token = auth2.token.trim();
33157
- if (!token) {
33158
- throw new Error("Missing OpenAI token");
33159
- }
33160
- return token;
33161
- }
33162
- function extractChatGPTAccountId(auth2) {
33163
- if (auth2.type !== "oauth") {
33164
- return;
33165
- }
33166
- const access = auth2.access.trim();
33167
- if (!access) {
33168
- return;
33169
- }
33170
- const parts = access.split(".");
33171
- if (parts.length !== 3) {
33172
- return;
33173
- }
33174
- try {
33175
- const payload = parts[1];
33176
- if (!payload) {
33177
- return;
33178
- }
33179
- const decoded = Buffer.from(payload, "base64").toString("utf8");
33180
- const parsed = JSON.parse(decoded);
33181
- if (!parsed || typeof parsed !== "object") {
33182
- return;
33183
- }
33184
- const root = parsed;
33185
- const claim = root["https://api.openai.com/auth"];
33186
- if (!claim || typeof claim !== "object") {
33187
- return;
33188
- }
33189
- const accountId = claim.chatgpt_account_id;
33190
- if (typeof accountId !== "string") {
33191
- return;
33192
- }
33193
- const trimmed = accountId.trim();
33194
- return trimmed === "" ? undefined : trimmed;
33195
- } catch {
33196
- return;
33197
- }
33198
- }
33199
- async function runOpenAIWebSearch(options) {
33200
- const normalizedModel = options.model.trim();
33201
- if (!normalizedModel) {
33202
- throw new Error("Invalid OpenAI web search model");
33203
- }
33204
- const normalizedQuery = options.query.trim();
33205
- if (!normalizedQuery) {
33206
- throw new Error("Query must not be empty");
33207
- }
33208
- const accessToken = getAccessToken(options.auth);
33209
- const isOAuth = options.auth.type === "oauth";
33210
- const body = {
33211
- model: normalizedModel,
33212
- instructions: "",
33213
- input: [
33214
- {
33215
- role: "user",
33216
- content: [
33217
- {
33218
- type: "input_text",
33219
- text: buildWebSearchUserPrompt(normalizedQuery)
33220
- }
33221
- ]
33222
- }
33223
- ],
33224
- tools: [{ type: "web_search" }],
33225
- include: ["web_search_call.action.sources"]
33226
- };
33227
- if (options.reasoningEffort || options.reasoningSummary) {
33228
- body.reasoning = {
33229
- effort: options.reasoningEffort,
33230
- summary: options.reasoningSummary
33231
- };
33232
- }
33233
- body.store = false;
33234
- if (options.textVerbosity) {
33235
- body.text = {
33236
- verbosity: options.textVerbosity
33237
- };
33238
- }
33239
- if (Array.isArray(options.include) && options.include.length > 0) {
33240
- const filtered = options.include.filter((value) => typeof value === "string" && value.trim() !== "");
33241
- if (filtered.length > 0) {
33242
- body.include = filtered;
33243
- }
33244
- }
33245
- body.stream = true;
33246
- body.tool_choice = "auto";
33247
- body.parallel_tool_calls = true;
33248
- if (isOAuth) {
33249
- body.instructions = codex_prompt_default;
33250
- } else {
33251
- body.instructions = "You are an AI assistant answering a single web search query for the user.";
33252
- }
33253
- const url3 = isOAuth ? "https://chatgpt.com/backend-api/codex/responses" : "https://api.openai.com/v1/responses";
33254
- const headers = {
33255
- Authorization: `Bearer ${accessToken}`,
33256
- "Content-Type": "application/json",
33257
- "OpenAI-Beta": "responses=experimental"
33258
- };
33259
- if (isOAuth) {
33260
- const accountId = extractChatGPTAccountId(options.auth);
33261
- if (accountId) {
33262
- headers["chatgpt-account-id"] = accountId;
33263
- }
33264
- headers.originator = "codex_cli_rs";
33265
- }
33266
- const response = await fetch(url3, {
33267
- method: "POST",
33268
- headers,
33269
- body: JSON.stringify(body),
33270
- signal: options.abortSignal
33271
- });
33272
- if (!response.ok) {
33273
- const message = await buildErrorDetails(response, url3, body);
33274
- throw new Error(message);
33275
- }
33276
- const payload = await readOpenAIResponsePayload(response);
33277
- const text = extractOpenAIText(payload);
33278
- if (!text || !text.trim()) {
33279
- return `Web search completed for "${normalizedQuery}", but no results were returned.`;
33280
- }
33281
- return text;
33282
- }
33283
- function createOpenAIWebsearchClient(model, config4) {
33284
- const normalizedModel = model.trim();
33285
- if (!normalizedModel) {
33286
- throw new Error("Invalid OpenAI web search model");
33287
- }
33288
- return {
33289
- async search(query, abortSignal, getAuth) {
33290
- const normalizedQuery = query.trim();
33291
- if (!normalizedQuery) {
33292
- throw new Error("Query must not be empty");
33293
- }
33294
- const auth2 = await getAuth();
33295
- if (!auth2) {
33296
- throw new Error('Missing auth for provider "openai"');
33297
- }
33298
- return runOpenAIWebSearch({
33299
- model: normalizedModel,
33300
- query: normalizedQuery,
33301
- abortSignal,
33302
- auth: auth2,
33303
- reasoningEffort: config4.reasoningEffort,
33304
- reasoningSummary: config4.reasoningSummary,
33305
- textVerbosity: config4.textVerbosity,
33306
- store: config4.store,
33307
- include: config4.include
33308
- });
33309
- }
33310
- };
33311
- }
33312
- function extractOpenAIText(payload) {
33313
- if (!payload || typeof payload !== "object") {
33314
- return;
33315
- }
33316
- const root = payload;
33317
- const output = root.output;
33318
- if (!Array.isArray(output) || output.length === 0) {
33319
- return;
33320
- }
33321
- let combined = "";
33322
- for (const item of output) {
33323
- if (!item || typeof item !== "object") {
33324
- continue;
33325
- }
33326
- const content = item.content;
33327
- if (!Array.isArray(content)) {
33328
- continue;
33329
- }
33330
- for (const part of content) {
33331
- if (!part || typeof part !== "object") {
33332
- continue;
33333
- }
33334
- const kind = part.type;
33335
- if (kind !== "output_text") {
33336
- continue;
33337
- }
33338
- const textField = part.text;
33339
- if (typeof textField === "string") {
33340
- combined += textField;
33341
- } else if (textField && typeof textField === "object") {
33342
- const obj = textField;
33343
- if (typeof obj.value === "string") {
33344
- combined += obj.value;
33345
- }
33346
- }
33347
- }
33348
- }
33349
- return combined || undefined;
33350
- }
33351
- async function buildErrorDetails(response, url3, body) {
33352
- const parts = [];
33353
- parts.push(`status=${response.status}`);
33354
- parts.push(`url=${url3}`);
33355
- const safeBody = { ...body };
33356
- if (typeof safeBody.instructions === "string") {
33357
- const value = safeBody.instructions;
33358
- const maxLength = 512;
33359
- if (value.length > maxLength) {
33360
- const headLength = 256;
33361
- const tailLength = 128;
33362
- const head = value.slice(0, headLength);
33363
- const tail = value.slice(-tailLength);
33364
- const omitted = value.length - headLength - tailLength;
33365
- safeBody.instructions = `${head} ... [${omitted} chars truncated] ... ${tail}`;
33366
- }
33367
- }
33368
- parts.push(`requestBody=${JSON.stringify(safeBody)}`);
33369
- let rawText;
33370
- try {
33371
- rawText = await response.text();
33372
- } catch {}
33373
- if (rawText) {
33374
- let parsedMessage;
33375
- try {
33376
- const parsed = JSON.parse(rawText);
33377
- const message = parsed.error?.message;
33378
- if (typeof message === "string" && message.trim() !== "") {
33379
- parsedMessage = message.trim();
33380
- }
33381
- } catch {}
33382
- if (parsedMessage) {
33383
- parts.unshift(`error=${parsedMessage}`);
33384
- }
33385
- parts.push(`responseBody=${rawText}`);
33386
- }
33387
- return parts.join(" | ");
33388
- }
33389
- async function readOpenAIResponsePayload(response) {
33390
- const text = await response.text();
33391
- const trimmed = text.trim();
33392
- if (trimmed === "") {
33393
- return {};
33394
- }
33395
- if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
33396
- try {
33397
- const parsed = JSON.parse(trimmed);
33398
- return parsed;
33399
- } catch {}
33400
- }
33401
- const extracted = extractOpenAIResponseFromSse(text);
33402
- if (extracted !== undefined) {
33403
- return extracted;
33404
- }
33405
- throw new Error("Failed to parse JSON");
33406
- }
33407
- function extractOpenAIResponseFromSse(sseText) {
33408
- const lines = sseText.split(`
33409
- `);
33410
- for (const line of lines) {
33411
- if (!line.startsWith("data: ")) {
33412
- continue;
33413
- }
33414
- const payload = line.slice(6).trim();
33415
- if (!payload || payload === "[DONE]") {
33416
- continue;
33417
- }
33418
- try {
33419
- const parsed = JSON.parse(payload);
33420
- const kind = parsed.type ?? "";
33421
- if (kind === "response.done" || kind === "response.completed") {
33422
- return parsed.response;
33423
- }
33424
- } catch {}
33425
- }
33426
- return;
33427
- }
33428
- var init_openai = __esm(() => {
33429
- init_codex_prompt();
33430
- });
33431
-
33432
- // src/internals/plugins/websearch-cited/openrouter.ts
33433
- function buildWebSearchUserPrompt2(query) {
33434
- const normalized = query.trim();
33435
- return `perform web search on "${normalized}". Return results with inline citations (**only** source index like [1], no URL in the answer) and end with a Sources list of URLs.`;
33436
- }
33437
- function getApiKey(auth2) {
33438
- if (auth2.type !== "api") {
33439
- throw new Error("OpenRouter only supports API key authentication");
33440
- }
33441
- const key = auth2.key.trim();
33442
- if (!key) {
33443
- throw new Error("Missing OpenRouter API key");
33444
- }
33445
- return key;
33446
- }
33447
- function extractOutputText(payload) {
33448
- if (!payload || typeof payload !== "object") {
33449
- return;
33450
- }
33451
- const root = payload;
33452
- const direct = root.output_text;
33453
- if (typeof direct === "string" && direct.trim() !== "") {
33454
- return direct;
33455
- }
33456
- const output = root.output;
33457
- if (!Array.isArray(output) || output.length === 0) {
33458
- return;
33459
- }
33460
- let combined = "";
33461
- for (const item of output) {
33462
- if (item.type !== "message") {
33463
- continue;
33464
- }
33465
- const content = item.content;
33466
- if (!Array.isArray(content)) {
33467
- continue;
33468
- }
33469
- for (const part of content) {
33470
- if (part.type !== "output_text") {
33471
- continue;
33472
- }
33473
- const text = part.text;
33474
- if (typeof text === "string") {
33475
- combined += text;
33476
- }
33477
- }
33478
- }
33479
- return combined || undefined;
33480
- }
33481
- async function runOpenRouterWebSearch(options) {
33482
- const normalizedModel = options.model.trim();
33483
- if (!normalizedModel) {
33484
- throw new Error("Invalid OpenRouter web search model");
33485
- }
33486
- const normalizedQuery = options.query.trim();
33487
- if (!normalizedQuery) {
33488
- throw new Error("Query must not be empty");
33489
- }
33490
- const apiKey = getApiKey(options.auth);
33491
- const body = {
33492
- model: normalizedModel,
33493
- input: buildWebSearchUserPrompt2(normalizedQuery),
33494
- plugins: [
33495
- {
33496
- id: "web",
33497
- search_prompt: buildWebSearchUserPrompt2(normalizedQuery)
33498
- }
33499
- ],
33500
- store: false,
33501
- stream: false
33502
- };
33503
- const response = await fetch(OPENROUTER_RESPONSES_ENDPOINT, {
33504
- method: "POST",
33505
- headers: {
33506
- Authorization: `Bearer ${apiKey}`,
33507
- "Content-Type": "application/json"
33508
- },
33509
- body: JSON.stringify(body),
33510
- signal: options.abortSignal
33511
- });
33512
- if (!response.ok) {
33513
- const text = await response.text().catch(() => "");
33514
- const details = text.trim() !== "" ? ` | responseBody=${text}` : "";
33515
- throw new Error(`status=${response.status} | url=${OPENROUTER_RESPONSES_ENDPOINT} | requestBody=${JSON.stringify(body)}${details}`);
33516
- }
33517
- const payload = await response.json();
33518
- const outputText = extractOutputText(payload);
33519
- if (!outputText || !outputText.trim()) {
33520
- return `Web search completed for "${normalizedQuery}", but no results were returned.`;
33521
- }
33522
- return outputText;
33523
- }
33524
- function createOpenRouterWebsearchClient(model) {
33525
- const normalizedModel = model.trim();
33526
- if (!normalizedModel) {
33527
- throw new Error("Invalid OpenRouter web search model");
33528
- }
33529
- return {
33530
- async search(query, abortSignal, getAuth) {
33531
- const normalizedQuery = query.trim();
33532
- if (!normalizedQuery) {
33533
- throw new Error("Query must not be empty");
33534
- }
33535
- const auth2 = await getAuth();
33536
- if (!auth2) {
33537
- throw new Error('Missing auth for provider "openrouter"');
33538
- }
33539
- return runOpenRouterWebSearch({
33540
- model: normalizedModel,
33541
- query: normalizedQuery,
33542
- abortSignal,
33543
- auth: auth2
33544
- });
33545
- }
33546
- };
33547
- }
33548
- var OPENROUTER_RESPONSES_ENDPOINT = "https://openrouter.ai/api/v1/responses";
33549
-
33550
- // src/internals/plugins/websearch-cited/index.ts
33551
- var exports_websearch_cited = {};
33552
- __export(exports_websearch_cited, {
33553
- resolveGetAuth: () => resolveGetAuth,
33554
- registerGetAuth: () => registerGetAuth,
33555
- default: () => websearch_cited_default,
33556
- WebsearchCitedOpenAIPlugin: () => WebsearchCitedOpenAIPlugin,
33557
- WebsearchCitedGooglePlugin: () => WebsearchCitedGooglePlugin,
33558
- OPENROUTER_PROVIDER_ID: () => OPENROUTER_PROVIDER_ID,
33559
- OPENAI_PROVIDER_ID: () => OPENAI_PROVIDER_ID,
33560
- GOOGLE_PROVIDER_ID: () => GOOGLE_PROVIDER_ID
33561
- });
33562
- function isRecord17(value) {
33563
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
33564
- }
33565
- function registerGetAuth(providerID, getAuth) {
33566
- authRegistry.set(providerID, getAuth);
33567
- }
33568
- function resolveGetAuth(providerID) {
33569
- return authRegistry.get(providerID);
33570
- }
33571
- function findFirstWebsearchCitedConfig(config4) {
33572
- const providers = config4.provider;
33573
- if (!providers || typeof providers !== "object") {
33574
- return {};
33575
- }
33576
- let firstError;
33577
- for (const [providerID, providerConfig] of Object.entries(providers)) {
33578
- if (!providerConfig || typeof providerConfig !== "object") {
33579
- continue;
33580
- }
33581
- const options = providerConfig.options;
33582
- if (!isRecord17(options)) {
33583
- continue;
33584
- }
33585
- if (!("websearch_cited" in options)) {
33586
- continue;
33587
- }
33588
- const cited = options.websearch_cited;
33589
- if (!isRecord17(cited)) {
33590
- firstError ??= `Invalid websearch_cited configuration for provider "${providerID}".`;
33591
- continue;
33592
- }
33593
- const candidate = cited.model;
33594
- if (typeof candidate !== "string" || candidate.trim() === "") {
33595
- firstError ??= `Missing websearch_cited model for provider "${providerID}".`;
33596
- continue;
33597
- }
33598
- if (providerID !== GOOGLE_PROVIDER_ID && providerID !== OPENAI_PROVIDER_ID && providerID !== OPENROUTER_PROVIDER_ID) {
33599
- firstError ??= `Unsupported provider "${providerID}" for websearch_cited.`;
33600
- continue;
33601
- }
33602
- return {
33603
- selected: {
33604
- providerID,
33605
- model: candidate.trim()
33606
- }
33607
- };
33608
- }
33609
- return firstError ? { error: firstError } : {};
33610
- }
33611
- function parseOpenAIOptions(providerConfig, model) {
33612
- if (!isRecord17(providerConfig)) {
33613
- return {};
33614
- }
33615
- const providerRecord = providerConfig;
33616
- const rawOptions = providerRecord.options;
33617
- const baseOptions = isRecord17(rawOptions) ? rawOptions : undefined;
33618
- let modelOptions;
33619
- const rawModels = providerRecord.models;
33620
- if (model && isRecord17(rawModels)) {
33621
- const modelsRecord = rawModels;
33622
- const entry = modelsRecord[model];
33623
- if (isRecord17(entry)) {
33624
- const entryOptions = entry.options;
33625
- if (isRecord17(entryOptions)) {
33626
- modelOptions = entryOptions;
33627
- }
33628
- }
33629
- }
33630
- const merged = {
33631
- ...baseOptions ?? {},
33632
- ...modelOptions ?? {}
33633
- };
33634
- const result = {};
33635
- const reasoningEffort = merged.reasoningEffort;
33636
- if (typeof reasoningEffort === "string" && reasoningEffort.trim() !== "") {
33637
- result.reasoningEffort = reasoningEffort.trim();
33638
- }
33639
- const reasoningSummary = merged.reasoningSummary;
33640
- if (typeof reasoningSummary === "string" && reasoningSummary.trim() !== "") {
33641
- result.reasoningSummary = reasoningSummary.trim();
33642
- }
33643
- const textVerbosity = merged.textVerbosity;
33644
- if (typeof textVerbosity === "string" && textVerbosity.trim() !== "") {
33645
- result.textVerbosity = textVerbosity.trim();
33646
- }
33647
- const store2 = merged.store;
33648
- if (typeof store2 === "boolean") {
33649
- result.store = store2;
33650
- }
33651
- const include = merged.include;
33652
- if (Array.isArray(include)) {
33653
- const filtered = include.filter((value) => typeof value === "string" && value.trim() !== "");
33654
- if (filtered.length > 0) {
33655
- result.include = filtered;
33656
- }
33657
- }
33658
- return result;
33659
- }
33660
- var GOOGLE_PROVIDER_ID = "google", OPENAI_PROVIDER_ID = "openai", OPENROUTER_PROVIDER_ID = "openrouter", CITED_SEARCH_TOOL_DESCRIPTION = "Performs a Gemini-style grounded web search: returns a concise digest with inline citations and a Sources list of URLs. NOTE: for LLM rate limits, DO NOT parallel this tool > 5", WEBSEARCH_ARGS, WEBSEARCH_ALLOWED_KEYS, WEBSEARCH_ALLOWED_KEYS_DESCRIPTION, authRegistry, WebsearchCitedPlugin = (_ctx, fallback) => {
33661
- let selectedProvider = fallback?.providerID;
33662
- let selectedModel = fallback?.model;
33663
- let openaiConfig = {};
33664
- let configError;
33665
- return Promise.resolve({
33666
- auth: {
33667
- provider: OPENROUTER_PROVIDER_ID,
33668
- loader(getAuth) {
33669
- registerGetAuth(OPENROUTER_PROVIDER_ID, getAuth);
33670
- return Promise.resolve({});
33671
- },
33672
- methods: [
33673
- {
33674
- type: "api",
33675
- label: "OpenRouter API key"
33676
- }
33677
- ]
33678
- },
33679
- config: (config4) => {
33680
- const { selected, error: error92 } = findFirstWebsearchCitedConfig(config4);
33681
- selectedProvider = undefined;
33682
- selectedModel = undefined;
33683
- openaiConfig = {};
33684
- configError = error92;
33685
- if (selected) {
33686
- selectedProvider = selected.providerID;
33687
- selectedModel = selected.model;
33688
- if (selectedProvider === OPENAI_PROVIDER_ID) {
33689
- const openaiProvider = config4.provider?.openai;
33690
- openaiConfig = parseOpenAIOptions(openaiProvider, selectedModel);
33691
- }
33692
- } else if (!error92 && fallback) {
33693
- selectedProvider = fallback.providerID;
33694
- selectedModel = fallback.model;
33695
- }
33696
- return Promise.resolve();
33697
- },
33698
- tool: {
33699
- websearch_cited: tool({
33700
- description: CITED_SEARCH_TOOL_DESCRIPTION,
33701
- args: WEBSEARCH_ARGS,
33702
- async execute(args, context) {
33703
- const argKeys = Object.keys(args ?? {});
33704
- const extraKeys = argKeys.filter((key) => !WEBSEARCH_ALLOWED_KEYS.has(key));
33705
- if (extraKeys.length > 0) {
33706
- throw new Error(`Unknown argument(s): ${extraKeys.join(", ")}, only ${WEBSEARCH_ALLOWED_KEYS_DESCRIPTION} supported.`);
33707
- }
33708
- const query = args.query?.trim();
33709
- if (!query) {
33710
- throw new Error("The 'query' parameter cannot be empty.");
33711
- }
33712
- if (configError) {
33713
- throw new Error(configError);
33714
- }
33715
- if (!selectedProvider || !selectedModel) {
33716
- throw new Error("Missing web search model configuration.");
33717
- }
33718
- if (selectedProvider === OPENAI_PROVIDER_ID) {
33719
- const getAuth2 = resolveGetAuth(OPENAI_PROVIDER_ID);
33720
- if (!getAuth2) {
33721
- throw new Error('Missing auth for provider "openai". Authenticate via `opencode auth login`.');
33722
- }
33723
- const client4 = createOpenAIWebsearchClient(selectedModel, openaiConfig);
33724
- return client4.search(query, context.abort, getAuth2);
33725
- }
33726
- if (selectedProvider === OPENROUTER_PROVIDER_ID) {
33727
- const getAuth2 = resolveGetAuth(OPENROUTER_PROVIDER_ID);
33728
- if (!getAuth2) {
33729
- throw new Error('Missing auth for provider "openrouter". Authenticate via `opencode auth login`.');
33730
- }
33731
- const client4 = createOpenRouterWebsearchClient(selectedModel);
33732
- return client4.search(query, context.abort, getAuth2);
33733
- }
33734
- const getAuth = resolveGetAuth(GOOGLE_PROVIDER_ID);
33735
- if (!getAuth) {
33736
- throw new Error('Missing auth for provider "google". Authenticate via `opencode auth login`.');
33737
- }
33738
- const client3 = createGoogleWebsearchClient(selectedModel);
33739
- return client3.search(query, context.abort, getAuth);
33740
- }
33741
- })
33742
- }
33743
- });
33744
- }, WebsearchCitedGooglePlugin = () => {
33745
- return Promise.resolve({
33746
- auth: {
33747
- provider: GOOGLE_PROVIDER_ID,
33748
- loader(getAuth) {
33749
- registerGetAuth(GOOGLE_PROVIDER_ID, getAuth);
33750
- return Promise.resolve({});
33751
- },
33752
- methods: [
33753
- {
33754
- type: "api",
33755
- label: "Google API key"
33756
- }
33757
- ]
33758
- }
33759
- });
33760
- }, WebsearchCitedOpenAIPlugin = () => {
33761
- return Promise.resolve({
33762
- auth: {
33763
- provider: OPENAI_PROVIDER_ID,
33764
- loader(getAuth) {
33765
- registerGetAuth(OPENAI_PROVIDER_ID, getAuth);
33766
- return Promise.resolve({});
33767
- },
33768
- methods: [
33769
- {
33770
- type: "api",
33771
- label: "OpenAI API key"
33772
- }
33773
- ]
33774
- }
33775
- });
33776
- }, websearch_cited_default;
33777
- var init_websearch_cited = __esm(() => {
33778
- init_dist();
33779
- init_google();
33780
- init_openai();
33781
- WEBSEARCH_ARGS = {
33782
- query: tool.schema.string().describe("The natural language web search query.")
33783
- };
33784
- WEBSEARCH_ALLOWED_KEYS = new Set(Object.keys(WEBSEARCH_ARGS));
33785
- WEBSEARCH_ALLOWED_KEYS_DESCRIPTION = Array.from(WEBSEARCH_ALLOWED_KEYS).map((key) => `'${key}'`).join(", ");
33786
- authRegistry = new Map;
33787
- websearch_cited_default = WebsearchCitedPlugin;
33788
- });
33789
-
33790
32526
  // node_modules/bun-pty/src/interfaces.ts
33791
32527
  class EventEmitter {
33792
32528
  listeners = [];
@@ -33811,8 +32547,8 @@ class EventEmitter {
33811
32547
  // node_modules/bun-pty/src/terminal.ts
33812
32548
  import { dlopen, FFIType, ptr } from "bun:ffi";
33813
32549
  import { Buffer as Buffer2 } from "buffer";
33814
- import { join as join104, dirname as dirname31, basename as basename16 } from "path";
33815
- import { existsSync as existsSync92 } from "fs";
32550
+ import { join as join105, dirname as dirname32, basename as basename16 } from "path";
32551
+ import { existsSync as existsSync93 } from "fs";
33816
32552
  function shQuote(s) {
33817
32553
  if (s.length === 0)
33818
32554
  return "''";
@@ -33820,7 +32556,7 @@ function shQuote(s) {
33820
32556
  }
33821
32557
  function resolveLibPath() {
33822
32558
  const env = process.env.BUN_PTY_LIB;
33823
- if (env && existsSync92(env))
32559
+ if (env && existsSync93(env))
33824
32560
  return env;
33825
32561
  try {
33826
32562
  const embeddedPath = __require(`../rust-pty/target/release/${process.platform === "win32" ? "rust_pty.dll" : process.platform === "darwin" ? process.arch === "arm64" ? "librust_pty_arm64.dylib" : "librust_pty.dylib" : process.arch === "arm64" ? "librust_pty_arm64.so" : "librust_pty.so"}`);
@@ -33831,22 +32567,22 @@ function resolveLibPath() {
33831
32567
  const arch = process.arch;
33832
32568
  const filenames = platform2 === "darwin" ? arch === "arm64" ? ["librust_pty_arm64.dylib", "librust_pty.dylib"] : ["librust_pty.dylib"] : platform2 === "win32" ? ["rust_pty.dll"] : arch === "arm64" ? ["librust_pty_arm64.so", "librust_pty.so"] : ["librust_pty.so"];
33833
32569
  const base = Bun.fileURLToPath(import.meta.url);
33834
- const fileDir = dirname31(base);
32570
+ const fileDir = dirname32(base);
33835
32571
  const dirName = basename16(fileDir);
33836
- const here = dirName === "src" || dirName === "dist" ? dirname31(fileDir) : fileDir;
32572
+ const here = dirName === "src" || dirName === "dist" ? dirname32(fileDir) : fileDir;
33837
32573
  const basePaths = [
33838
- join104(here, "rust-pty", "target", "release"),
33839
- join104(here, "..", "bun-pty", "rust-pty", "target", "release"),
33840
- join104(process.cwd(), "node_modules", "bun-pty", "rust-pty", "target", "release")
32574
+ join105(here, "rust-pty", "target", "release"),
32575
+ join105(here, "..", "bun-pty", "rust-pty", "target", "release"),
32576
+ join105(process.cwd(), "node_modules", "bun-pty", "rust-pty", "target", "release")
33841
32577
  ];
33842
32578
  const fallbackPaths = [];
33843
32579
  for (const basePath of basePaths) {
33844
32580
  for (const filename of filenames) {
33845
- fallbackPaths.push(join104(basePath, filename));
32581
+ fallbackPaths.push(join105(basePath, filename));
33846
32582
  }
33847
32583
  }
33848
32584
  for (const path10 of fallbackPaths) {
33849
- if (existsSync92(path10))
32585
+ if (existsSync93(path10))
33850
32586
  return path10;
33851
32587
  }
33852
32588
  throw new Error(`librust_pty shared library not found.
@@ -35063,8 +33799,8 @@ var init_plugin = __esm(() => {
35063
33799
  });
35064
33800
 
35065
33801
  // src/index.ts
35066
- import { existsSync as existsSync93 } from "fs";
35067
- import { join as join105 } from "path";
33802
+ import { existsSync as existsSync94 } from "fs";
33803
+ import { join as join106 } from "path";
35068
33804
 
35069
33805
  // src/hooks/todo-continuation-enforcer/index.ts
35070
33806
  init_logger();
@@ -113721,6 +112457,73 @@ This file is clean. Here's why:
113721
112457
  **Conclusion**: This code appears to be human-written or well-reviewed AI code. No changes needed.
113722
112458
  \`\`\``
113723
112459
  };
112460
+ // src/features/builtin-skills/skills/hiai-opencode-setup.ts
112461
+ var hiaiOpencodeSetupSkill = {
112462
+ name: "hiai-opencode-setup",
112463
+ description: "Use when install/setup/onboarding or MCP debug mentions: install, setup, bootstrap, doctor, mcp-status, MCP not found, mcp list empty, MemPalace, RAG, firecrawl, stitch, sequential-thinking, playwright, DCP, agents, skills, or LSP.",
112464
+ template: `# hiai-opencode Setup And Runtime Operations
112465
+
112466
+ Use this skill for hiai-opencode installation, diagnostics, and integration repair.
112467
+
112468
+ ## Architecture
112469
+
112470
+ - hiai-opencode is an OpenCode plugin, not a standalone app.
112471
+ - MCP servers are external upstream tools launched by hiai-opencode wiring.
112472
+ - The user-facing config is \`hiai-opencode.json\` or \`.opencode/hiai-opencode.json\`.
112473
+ - Model provider credentials belong to OpenCode Connect. Do not ask for \`OPENROUTER_API_KEY\`, \`OPENAI_API_KEY\`, or \`ANTHROPIC_API_KEY\` for normal model usage.
112474
+ - Service credentials are separate: \`FIRECRAWL_API_KEY\`, \`STITCH_AI_API_KEY\`, \`CONTEXT7_API_KEY\`.
112475
+
112476
+ ## First Diagnostic Commands
112477
+
112478
+ \`\`\`bash
112479
+ hiai-opencode doctor
112480
+ hiai-opencode mcp-status
112481
+ opencode debug config
112482
+ \`\`\`
112483
+
112484
+ If \`opencode mcp list\` is empty but doctor/mcp-status sees servers, explain that OpenCode's list may read only static \`.mcp.json\`. Refresh static visibility:
112485
+
112486
+ \`\`\`bash
112487
+ hiai-opencode export-mcp .mcp.json
112488
+ opencode mcp list --print-logs --log-level INFO
112489
+ \`\`\`
112490
+
112491
+ ## Plugin vs MCP
112492
+
112493
+ Install OpenCode plugins with:
112494
+
112495
+ \`\`\`bash
112496
+ opencode plugin @hiai-gg/hiai-opencode@latest --global
112497
+ opencode plugin @tarquinen/opencode-dcp@latest --global
112498
+ \`\`\`
112499
+
112500
+ Do not add MCP packages to the OpenCode plugin array. MCP packages are launched through \`hiai-opencode.json\` and helper scripts.
112501
+
112502
+ ## MCP Runtime Notes
112503
+
112504
+ - \`playwright\`: node/npx; browser binaries may need \`HIAI_PLAYWRIGHT_INSTALL_BROWSERS=1\`; Linux system deps may require admin rights.
112505
+ - \`sequential-thinking\`: node/npx; use for complex planning, revision, and branching.
112506
+ - \`firecrawl\`: requires \`FIRECRAWL_API_KEY\`.
112507
+ - \`mempalace\`: prefers \`uv\`; otherwise Python 3.9+ with \`mempalace\`. Use \`mempalace_status\` first, search before answering memory questions, and never invent memories.
112508
+ - \`rag\`: requires \`OPENCODE_RAG_URL\` or a reachable default \`http://localhost:9002/tools/search\`.
112509
+ - \`stitch\`: requires \`STITCH_AI_API_KEY\`.
112510
+ - \`context7\`: remote docs/search; key optional but recommended for limits.
112511
+
112512
+ ## Calling MCP
112513
+
112514
+ - Use native MCP tools if OpenCode exposes them.
112515
+ - Use \`skill_mcp\` for skill-embedded MCP or enabled hiai-opencode MCP.
112516
+ - If \`skill_mcp\` says a server is not found, check whether the skill is loaded, whether the MCP is enabled in \`hiai-opencode.json\`, and whether \`.mcp.json\` needs export.
112517
+
112518
+ ## Safety Rules
112519
+
112520
+ - Report missing keys by env var name only. Never print key values.
112521
+ - Prefer project-local or user-level installs. Do not use sudo/admin rights unless the user explicitly asks.
112522
+ - Do not edit unrelated OpenCode/Claude/Agents global skill folders unless the user opts in.
112523
+ - Keep DCP separate: it is an optional OpenCode plugin, not part of the hiai-opencode package.
112524
+ `,
112525
+ allowedTools: ["Bash(*)", "Read(*)", "Edit(*)", "Glob(*)", "Grep(*)", "skill_mcp(*)"]
112526
+ };
113724
112527
  // src/features/builtin-skills/skills.ts
113725
112528
  function createBuiltinSkills(options = {}) {
113726
112529
  const { browserProvider = "playwright", disabledSkills } = options;
@@ -113732,7 +112535,15 @@ function createBuiltinSkills(options = {}) {
113732
112535
  } else {
113733
112536
  browserSkill = playwrightSkill;
113734
112537
  }
113735
- const skills = [browserSkill, frontendUiUxSkill, gitMasterSkill, devBrowserSkill, reviewWorkSkill, aiSlopRemoverSkill];
112538
+ const skills = [
112539
+ browserSkill,
112540
+ hiaiOpencodeSetupSkill,
112541
+ frontendUiUxSkill,
112542
+ gitMasterSkill,
112543
+ devBrowserSkill,
112544
+ reviewWorkSkill,
112545
+ aiSlopRemoverSkill
112546
+ ];
113736
112547
  if (!disabledSkills) {
113737
112548
  return skills;
113738
112549
  }
@@ -114059,7 +112870,8 @@ var BuiltinCommandNameSchema = exports_external.enum([
114059
112870
  "start-work",
114060
112871
  "stop-continuation",
114061
112872
  "remove-ai-slops",
114062
- "mcp-status"
112873
+ "mcp-status",
112874
+ "doctor"
114063
112875
  ]);
114064
112876
  // src/config/schema/comment-checker.ts
114065
112877
  var CommentCheckerConfigSchema = exports_external.object({
@@ -114337,9 +113149,6 @@ var WebsearchConfigSchema = exports_external.object({
114337
113149
 
114338
113150
  // src/config/schema/oh-my-opencode-config.ts
114339
113151
  var AuthConfigSchema = exports_external.object({
114340
- googleSearch: exports_external.string().optional(),
114341
- openai: exports_external.string().optional(),
114342
- openrouter: exports_external.string().optional(),
114343
113152
  stitch: exports_external.string().optional(),
114344
113153
  firecrawl: exports_external.string().optional(),
114345
113154
  context7: exports_external.string().optional()
@@ -116092,6 +114901,51 @@ Rules:
116092
114901
  - Do not run package installs unless the user explicitly asks.
116093
114902
  `;
116094
114903
 
114904
+ // src/features/builtin-commands/templates/doctor.ts
114905
+ var DOCTOR_TEMPLATE = `# Hiai OpenCode Doctor Command
114906
+
114907
+ ## Purpose
114908
+
114909
+ Use /doctor to run the hiai-opencode install/runtime diagnostic and report actionable setup issues.
114910
+
114911
+ ## Execute
114912
+
114913
+ Run:
114914
+
114915
+ \`\`\`bash
114916
+ hiai-opencode doctor
114917
+ \`\`\`
114918
+
114919
+ If the binary is not on PATH, try the package-local fallback:
114920
+
114921
+ \`\`\`bash
114922
+ node ./node_modules/@hiai-gg/hiai-opencode/assets/cli/hiai-opencode.mjs doctor
114923
+ \`\`\`
114924
+
114925
+ ## Report
114926
+
114927
+ Summarize:
114928
+
114929
+ - config path
114930
+ - enabled and disabled MCP servers
114931
+ - missing env vars by name only
114932
+ - static \`.mcp.json\` freshness and whether it is managed by hiai-opencode
114933
+ - OpenCode Connect visibility for configured model providers
114934
+ - OpenCode plugin registration sanity (including \`plugin: ["list"]\` misconfiguration warning)
114935
+ - skill materialization status from skill registry
114936
+ - agent count and naming summary
114937
+ - LSP runtime availability
114938
+ - MemPalace python source and selected interpreter (env/config/auto)
114939
+ - MCP tool probes (real connect + tools/list) for stdio and basic endpoint probes for remote MCP
114940
+
114941
+ Rules:
114942
+
114943
+ - Do not print API key values.
114944
+ - Do not ask for model provider env vars such as OPENROUTER_API_KEY or OPENAI_API_KEY; normal model auth belongs to OpenCode Connect.
114945
+ - If \`opencode mcp list\` is empty but doctor/mcp-status sees servers, explain the runtime-vs-static config distinction and run \`hiai-opencode export-mcp .mcp.json\` if the user wants static visibility.
114946
+ - Do not run package installs unless the user explicitly asks.
114947
+ `;
114948
+
116095
114949
  // src/features/builtin-commands/commands.ts
116096
114950
  function resolveStartWorkAgent(options) {
116097
114951
  if (options?.useRegisteredAgents) {
@@ -116200,6 +115054,12 @@ $ARGUMENTS
116200
115054
  description: "(builtin) Show hiai-opencode MCP server status, missing keys, and local runtime availability",
116201
115055
  template: `<command-instruction>
116202
115056
  ${MCP_STATUS_TEMPLATE}
115057
+ </command-instruction>`
115058
+ },
115059
+ doctor: {
115060
+ description: "(builtin) Run hiai-opencode install/runtime diagnostics and explain setup issues",
115061
+ template: `<command-instruction>
115062
+ ${DOCTOR_TEMPLATE}
116203
115063
  </command-instruction>`
116204
115064
  }
116205
115065
  };
@@ -120606,7 +119466,8 @@ var McpServerConfigSchema = exports_external.object({
120606
119466
  headers: exports_external.record(exports_external.string(), exports_external.string()).optional(),
120607
119467
  command: exports_external.array(exports_external.string()).optional(),
120608
119468
  timeout: exports_external.number().optional(),
120609
- environment: exports_external.record(exports_external.string(), exports_external.string()).optional()
119469
+ environment: exports_external.record(exports_external.string(), exports_external.string()).optional(),
119470
+ pythonPath: exports_external.string().optional()
120610
119471
  });
120611
119472
  var LspServerConfigSchema = exports_external.object({
120612
119473
  enabled: exports_external.boolean().optional(),
@@ -120654,9 +119515,6 @@ var PermissionsConfigSchema = exports_external.object({
120654
119515
  ["*"]: exports_external.record(exports_external.string(), exports_external.enum(["allow", "deny"])).optional()
120655
119516
  });
120656
119517
  var AuthKeysSchema = exports_external.object({
120657
- googleSearch: exports_external.string().optional(),
120658
- openai: exports_external.string().optional(),
120659
- openrouter: exports_external.string().optional(),
120660
119518
  stitch: exports_external.string().optional(),
120661
119519
  firecrawl: exports_external.string().optional(),
120662
119520
  context7: exports_external.string().optional()
@@ -120980,7 +119838,16 @@ function deriveMcp(config2) {
120980
119838
  const userMcp = config2.mcp ?? {};
120981
119839
  return Object.fromEntries(Object.entries(defaults).map(([name, entry]) => {
120982
119840
  const override = userMcp[name] ?? {};
120983
- return [name, { ...entry, ...override }];
119841
+ const merged = { ...entry, ...override };
119842
+ if (name === "mempalace" && typeof merged.pythonPath === "string" && merged.pythonPath.trim()) {
119843
+ merged.environment = {
119844
+ ...entry.environment ?? {},
119845
+ ...merged.environment ?? {},
119846
+ MEMPALACE_PYTHON: merged.pythonPath.trim()
119847
+ };
119848
+ }
119849
+ delete merged.pythonPath;
119850
+ return [name, merged];
120984
119851
  }));
120985
119852
  }
120986
119853
  function deriveLsp(config2) {
@@ -146772,7 +145639,7 @@ ${keyTriggers.join(`
146772
145639
  `)}
146773
145640
  - **"Look into" + "create PR"** \u2192 Not just research. Full implementation cycle expected.`;
146774
145641
  }
146775
- function buildToolSelectionTable(agents, tools = [], _skills = []) {
145642
+ function buildToolSelectionTable(agents, tools = [], _skills = [], options = {}) {
146776
145643
  const rows = ["### Tool & Agent Selection:", ""];
146777
145644
  if (tools.length > 0) {
146778
145645
  rows.push(`- ${getToolsPromptDisplay(tools)} - **FREE** - Not Complex, Scope Clear, No Implicit Assumptions`);
@@ -146785,9 +145652,43 @@ function buildToolSelectionTable(agents, tools = [], _skills = []) {
146785
145652
  }
146786
145653
  rows.push("");
146787
145654
  rows.push("**Default flow**: researcher (background) + tools \u2192 strategist (if required) \u2192 critic (high-risk gate)");
145655
+ if (options.includeIntegrationPrimer) {
145656
+ rows.push("");
145657
+ rows.push(buildHiaiIntegrationPrimerSection());
145658
+ }
146788
145659
  return rows.join(`
146789
145660
  `);
146790
145661
  }
145662
+ function buildHiaiIntegrationPrimerSection() {
145663
+ return `<hiai-opencode-integration-primer>
145664
+ ## hiai-opencode Architecture And Integration Rules
145665
+
145666
+ This workspace may use hiai-opencode, an OpenCode plugin that wires agents, skills, MCP launchers, LSP, diagnostics, and compatibility helpers around external upstream tools.
145667
+
145668
+ Core rules:
145669
+ - OpenCode plugins are not MCP servers. Keep \`@hiai-gg/hiai-opencode\` and optional \`@tarquinen/opencode-dcp\` in the OpenCode plugin list; never add MCP packages such as \`firecrawl-mcp\`, \`@playwright/mcp\`, or \`@modelcontextprotocol/server-sequential-thinking\` to the plugin list.
145670
+ - User-facing config lives in \`hiai-opencode.json\` or \`.opencode/hiai-opencode.json\`: 10 model slots, MCP enable flags, LSP enable flags, service auth placeholders, and skill discovery switches.
145671
+ - Model provider credentials are configured through OpenCode Connect. Do not ask for \`OPENROUTER_API_KEY\`, \`OPENAI_API_KEY\`, or \`ANTHROPIC_API_KEY\` for normal model usage.
145672
+ - Service keys are separate: \`FIRECRAWL_API_KEY\`, \`STITCH_AI_API_KEY\`, \`CONTEXT7_API_KEY\`, and \`OPENCODE_RAG_URL\` when those services are enabled.
145673
+
145674
+ MCP usage:
145675
+ - Run \`hiai-opencode doctor\` or \`hiai-opencode mcp-status\` for effective runtime MCP status.
145676
+ - \`opencode mcp list\` often reads only static \`.mcp.json\`; plugin runtime MCP may work even when that list is empty.
145677
+ - The plugin auto-exports \`.mcp.json\` when missing. Run \`hiai-opencode export-mcp .mcp.json\` to refresh static visibility.
145678
+ - \`skill_mcp\` can call skill-embedded MCP and enabled hiai-opencode MCP. If an MCP is "not found", check whether the skill was loaded, whether \`hiai-opencode.json\` enables it, and whether static export is needed.
145679
+
145680
+ Memory and retrieval:
145681
+ - MemPalace MCP is external. If enabled, use \`mempalace_status\` first, search before answering about remembered people/projects/past decisions, and write diary entries when appropriate. Never invent memories.
145682
+ - RAG MCP is external/local. Use it for project knowledge search when \`OPENCODE_RAG_URL\` is configured or the default \`http://localhost:9002/tools/search\` is reachable.
145683
+ - Sequential Thinking MCP is for complex planning/revision/branching, not for trivial edits.
145684
+ - Firecrawl/Stitch/Context7 are external web/docs/research services; missing service keys should be reported by env var name only.
145685
+
145686
+ Installation/debugging:
145687
+ - Use \`/doctor\` or \`hiai-opencode doctor\` before changing config.
145688
+ - Prefer user-level or project-local installs. Do not use sudo/admin rights unless explicitly requested.
145689
+ - If DCP is requested, install it separately with \`opencode plugin @tarquinen/opencode-dcp@latest --global\`.
145690
+ </hiai-opencode-integration-primer>`;
145691
+ }
146791
145692
  function buildResearcherSection(agents) {
146792
145693
  const researcherAgent = agents.find((agent) => agent.name === "researcher");
146793
145694
  if (!researcherAgent) {
@@ -147160,7 +146061,7 @@ When asking for clarification:
147160
146061
  }
147161
146062
  function buildGptProBobPrompt(_model, availableAgents, availableTools = [], availableSkills = [], availableCategories = [], useTaskSystem = false) {
147162
146063
  const keyTriggers = buildKeyTriggersSection(availableAgents, availableSkills);
147163
- const toolSelection = buildToolSelectionTable(availableAgents, availableTools, availableSkills);
146064
+ const toolSelection = buildToolSelectionTable(availableAgents, availableTools, availableSkills, { includeIntegrationPrimer: true });
147164
146065
  const researcherSection = buildResearcherSection(availableAgents);
147165
146066
  const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, availableSkills);
147166
146067
  const delegationTable = buildDelegationTable(availableAgents);
@@ -147636,7 +146537,7 @@ function buildIntentGate(role) {
147636
146537
  var MODE = "primary";
147637
146538
  function buildDynamicBobPrompt(model, availableAgents, availableTools = [], availableSkills = [], availableCategories = [], useTaskSystem = false) {
147638
146539
  const keyTriggers = buildKeyTriggersSection(availableAgents, availableSkills);
147639
- const toolSelection = buildToolSelectionTable(availableAgents, availableTools, availableSkills);
146540
+ const toolSelection = buildToolSelectionTable(availableAgents, availableTools, availableSkills, { includeIntegrationPrimer: true });
147640
146541
  const researcherSection = buildResearcherSection(availableAgents);
147641
146542
  const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, availableSkills);
147642
146543
  const delegationTable = buildDelegationTable(availableAgents);
@@ -149466,7 +148367,7 @@ var guardPromptMetadata = {
149466
148367
  // src/agents/coder/gpt.ts
149467
148368
  function buildCoderPrompt(availableAgents = [], availableTools = [], availableSkills = [], availableCategories = [], useTaskSystem = false) {
149468
148369
  const keyTriggers = buildKeyTriggersSection(availableAgents, availableSkills);
149469
- const toolSelection = buildToolSelectionTable(availableAgents, availableTools, availableSkills);
148370
+ const toolSelection = buildToolSelectionTable(availableAgents, availableTools, availableSkills, { includeIntegrationPrimer: true });
149470
148371
  const researcherSection = buildResearcherSection(availableAgents);
149471
148372
  const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, availableSkills);
149472
148373
  const delegationTable = buildDelegationTable(availableAgents);
@@ -149685,7 +148586,7 @@ function canonicalizeCriticSection(section) {
149685
148586
  }
149686
148587
  function buildCoderPrompt2(availableAgents = [], availableTools = [], availableSkills = [], availableCategories = [], useTaskSystem = false) {
149687
148588
  const keyTriggers = buildKeyTriggersSection(availableAgents, availableSkills);
149688
- const toolSelection = buildToolSelectionTable(availableAgents, availableTools, availableSkills);
148589
+ const toolSelection = buildToolSelectionTable(availableAgents, availableTools, availableSkills, { includeIntegrationPrimer: true });
149689
148590
  const researcherSection = buildResearcherSection(availableAgents);
149690
148591
  const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, availableSkills);
149691
148592
  const delegationTable = buildDelegationTable(availableAgents);
@@ -150010,7 +148911,7 @@ createCoderAgent.mode = MODE5;
150010
148911
  // src/agents/coder/gpt-pro.ts
150011
148912
  function buildCoderPrompt3(availableAgents = [], availableTools = [], availableSkills = [], availableCategories = [], useTaskSystem = false) {
150012
148913
  const keyTriggers = buildKeyTriggersSection(availableAgents, availableSkills);
150013
- const toolSelection = buildToolSelectionTable(availableAgents, availableTools, availableSkills);
148914
+ const toolSelection = buildToolSelectionTable(availableAgents, availableTools, availableSkills, { includeIntegrationPrimer: true });
150014
148915
  const researcherSection = buildResearcherSection(availableAgents);
150015
148916
  const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, availableSkills);
150016
148917
  const delegationTable = buildDelegationTable(availableAgents);
@@ -157512,15 +156413,98 @@ function createFirstMessageVariantGate() {
157512
156413
  // src/index.ts
157513
156414
  init_plugin_identity();
157514
156415
 
156416
+ // src/shared/mcp-static-export.ts
156417
+ import { existsSync as existsSync89, mkdirSync as mkdirSync18, readFileSync as readFileSync60, writeFileSync as writeFileSync21 } from "fs";
156418
+ import { dirname as dirname30, join as join98 } from "path";
156419
+ init_logger();
156420
+ var MCP_EXPORT_MARKER = "hiai-opencode";
156421
+ function toStaticMcpServer(config4) {
156422
+ if (config4.enabled === false)
156423
+ return null;
156424
+ if (config4.type === "remote") {
156425
+ const headers = config4.headers ? Object.fromEntries(Object.entries(config4.headers).map(([key, value]) => [key, resolveEnvVars(value)])) : undefined;
156426
+ return {
156427
+ type: "http",
156428
+ url: config4.url,
156429
+ ...headers ? { headers } : {}
156430
+ };
156431
+ }
156432
+ const [command, ...args] = config4.command ?? [];
156433
+ if (!command)
156434
+ return null;
156435
+ const env = config4.environment ? Object.fromEntries(Object.entries(config4.environment).map(([key, value]) => [key, resolveEnvVars(value)])) : undefined;
156436
+ return {
156437
+ command,
156438
+ ...args.length > 0 ? { args } : {},
156439
+ ...env ? { env } : {}
156440
+ };
156441
+ }
156442
+ function buildStaticMcpJson(config4) {
156443
+ const mcpServers = {};
156444
+ for (const [name, serverConfig] of Object.entries(config4.mcp ?? {})) {
156445
+ const converted = toStaticMcpServer(serverConfig);
156446
+ if (converted) {
156447
+ mcpServers[name] = converted;
156448
+ }
156449
+ }
156450
+ return {
156451
+ _meta: {
156452
+ generatedBy: MCP_EXPORT_MARKER,
156453
+ version: 1,
156454
+ generatedAt: new Date().toISOString()
156455
+ },
156456
+ mcpServers
156457
+ };
156458
+ }
156459
+ function isManagedStaticMcpFile(path9) {
156460
+ if (!existsSync89(path9))
156461
+ return false;
156462
+ try {
156463
+ const raw = readFileSync60(path9, "utf-8");
156464
+ const parsed = JSON.parse(raw);
156465
+ return parsed?._meta?.generatedBy === MCP_EXPORT_MARKER;
156466
+ } catch {
156467
+ return false;
156468
+ }
156469
+ }
156470
+ function autoExportStaticMcpJson(directory, config4) {
156471
+ const mode = process.env.HIAI_OPENCODE_AUTO_EXPORT_MCP?.trim().toLowerCase() || "if-missing";
156472
+ if (mode === "0" || mode === "false" || mode === "no" || mode === "off") {
156473
+ return;
156474
+ }
156475
+ const outputPath = process.env.HIAI_OPENCODE_MCP_EXPORT_PATH?.trim() || join98(directory, ".mcp.json");
156476
+ if (mode === "if-missing" && existsSync89(outputPath)) {
156477
+ return;
156478
+ }
156479
+ const isForceMode = mode === "force";
156480
+ if (mode === "always" && existsSync89(outputPath) && !isManagedStaticMcpFile(outputPath) && !isForceMode) {
156481
+ console.warn(`[hiai-opencode] WARNING: refusing to overwrite non-managed static MCP config at ${outputPath}. ` + "Set HIAI_OPENCODE_AUTO_EXPORT_MCP=force to override.");
156482
+ return;
156483
+ }
156484
+ try {
156485
+ mkdirSync18(dirname30(outputPath), { recursive: true });
156486
+ const payload = buildStaticMcpJson(config4);
156487
+ writeFileSync21(outputPath, `${JSON.stringify(payload, null, 2)}
156488
+ `);
156489
+ log("[hiai-opencode] exported static MCP config", {
156490
+ outputPath,
156491
+ servers: Object.keys(payload.mcpServers)
156492
+ });
156493
+ } catch (error92) {
156494
+ const message = error92 instanceof Error ? error92.message : String(error92);
156495
+ console.warn(`[hiai-opencode] WARNING: failed to export static MCP config to ${outputPath}: ${message}`);
156496
+ }
156497
+ }
156498
+
157515
156499
  // src/shared/startup-diagnostics.ts
157516
- import { existsSync as existsSync89, readFileSync as readFileSync60 } from "fs";
157517
- import { join as join98 } from "path";
156500
+ import { existsSync as existsSync90, readFileSync as readFileSync61 } from "fs";
156501
+ import { join as join99 } from "path";
157518
156502
  init_plugin_identity();
157519
156503
  function readPlugins(configPath) {
157520
- if (!existsSync89(configPath))
156504
+ if (!existsSync90(configPath))
157521
156505
  return [];
157522
156506
  try {
157523
- const content = readFileSync60(configPath, "utf-8");
156507
+ const content = readFileSync61(configPath, "utf-8");
157524
156508
  const parsed = parseJsoncSafe(content);
157525
156509
  return (parsed.data?.plugin ?? []).map((entry) => typeof entry === "string" ? entry : Array.isArray(entry) ? entry[0] : "").filter((entry) => typeof entry === "string" && entry.trim().length > 0);
157526
156510
  } catch {
@@ -157530,8 +156514,8 @@ function readPlugins(configPath) {
157530
156514
  function warnIfListPluginEntry(directory) {
157531
156515
  const globalPaths = getOpenCodeConfigPaths({ binary: "opencode", version: null });
157532
156516
  const candidates = [
157533
- join98(directory, ".opencode", "opencode.json"),
157534
- join98(directory, ".opencode", "opencode.jsonc"),
156517
+ join99(directory, ".opencode", "opencode.json"),
156518
+ join99(directory, ".opencode", "opencode.jsonc"),
157535
156519
  globalPaths.configJson,
157536
156520
  globalPaths.configJsonc
157537
156521
  ];
@@ -157884,8 +156868,8 @@ function resolveResultReferences(text, sessionID) {
157884
156868
  }
157885
156869
 
157886
156870
  // src/internals/plugins/subtask2/utils/config.ts
157887
- import { mkdirSync as mkdirSync18 } from "fs";
157888
- import { dirname as dirname30, join as join99 } from "path";
156871
+ import { mkdirSync as mkdirSync19 } from "fs";
156872
+ import { dirname as dirname31, join as join100 } from "path";
157889
156873
 
157890
156874
  // src/internals/plugins/subtask2/utils/prompts.ts
157891
156875
  var DEFAULT_RETURN_PROMPT = "Review, challenge and verify the task tool output above against the codebase. Then validate or revise it, before continuing with the next logical step.";
@@ -157951,15 +156935,15 @@ async function getAutoWorkflowPrompt() {
157951
156935
  var AUTO_WORKFLOW_PROMPT = AUTO_WORKFLOW_PROMPT_TEMPLATE.replace(README_PLACEHOLDER, "[Use getAutoWorkflowPrompt() to get full documentation]");
157952
156936
 
157953
156937
  // src/internals/plugins/subtask2/utils/config.ts
157954
- var CONFIG_PATH = join99(getOpenCodeConfigDir({ binary: "opencode" }), "subtask2.jsonc");
156938
+ var CONFIG_PATH = join100(getOpenCodeConfigDir({ binary: "opencode" }), "subtask2.jsonc");
157955
156939
  var cachedReadmeContent = null;
157956
156940
  async function loadReadmeContent() {
157957
156941
  if (cachedReadmeContent !== null) {
157958
156942
  return cachedReadmeContent;
157959
156943
  }
157960
156944
  try {
157961
- const pluginRoot = join99(dirname30(import.meta.path), "..", "..");
157962
- const readmePath = join99(pluginRoot, "README.md");
156945
+ const pluginRoot = join100(dirname31(import.meta.path), "..", "..");
156946
+ const readmePath = join100(pluginRoot, "README.md");
157963
156947
  const file3 = Bun.file(readmePath);
157964
156948
  if (await file3.exists()) {
157965
156949
  cachedReadmeContent = await file3.text();
@@ -157994,7 +156978,7 @@ async function loadConfig2() {
157994
156978
  }
157995
156979
  }
157996
156980
  } catch {}
157997
- mkdirSync18(dirname30(CONFIG_PATH), { recursive: true });
156981
+ mkdirSync19(dirname31(CONFIG_PATH), { recursive: true });
157998
156982
  await Bun.write(CONFIG_PATH, `{
157999
156983
  // Replace OpenCode's generic "Summarize..." prompt when no return is specified
158000
156984
  "replace_generic": true,
@@ -158066,7 +157050,7 @@ function clearLog() {
158066
157050
  }
158067
157051
 
158068
157052
  // src/internals/plugins/subtask2/commands/manifest.ts
158069
- import { join as join101 } from "path";
157053
+ import { join as join102 } from "path";
158070
157054
 
158071
157055
  // src/internals/plugins/subtask2/parsing/frontmatter.ts
158072
157056
  var import_yaml = __toESM(require_dist2(), 1);
@@ -158322,8 +157306,8 @@ function parseAutoWorkflowOutput(text) {
158322
157306
  async function buildManifest() {
158323
157307
  const manifest = {};
158324
157308
  const dirs = [
158325
- join101(getOpenCodeConfigDir({ binary: "opencode" }), "command"),
158326
- join101(Bun.env.PWD ?? ".", ".opencode", "command")
157309
+ join102(getOpenCodeConfigDir({ binary: "opencode" }), "command"),
157310
+ join102(Bun.env.PWD ?? ".", ".opencode", "command")
158327
157311
  ];
158328
157312
  for (const dir of dirs) {
158329
157313
  try {
@@ -158486,11 +157470,11 @@ async function resolveTurnReferences(text, sessionID) {
158486
157470
  }
158487
157471
 
158488
157472
  // src/internals/plugins/subtask2/commands/loader.ts
158489
- import { join as join102 } from "path";
157473
+ import { join as join103 } from "path";
158490
157474
  async function loadCommandFile(name) {
158491
157475
  const dirs = [
158492
- join102(getOpenCodeConfigDir({ binary: "opencode" }), "command"),
158493
- join102(Bun.env.PWD ?? ".", ".opencode", "command")
157476
+ join103(getOpenCodeConfigDir({ binary: "opencode" }), "command"),
157477
+ join103(Bun.env.PWD ?? ".", ".opencode", "command")
158494
157478
  ];
158495
157479
  for (const dir of dirs) {
158496
157480
  const directPath = `${dir}/${name}.md`;
@@ -159764,20 +158748,17 @@ var createPlugin = async (ctx) => {
159764
158748
  };
159765
158749
  };
159766
158750
 
159767
- // src/index.ts
159768
- init_websearch_cited();
159769
-
159770
158751
  // src/features/builtin-skills/materialize.ts
159771
158752
  import {
159772
158753
  cpSync,
159773
- existsSync as existsSync91,
159774
- mkdirSync as mkdirSync20,
158754
+ existsSync as existsSync92,
158755
+ mkdirSync as mkdirSync21,
159775
158756
  readdirSync as readdirSync26,
159776
- readFileSync as readFileSync61,
158757
+ readFileSync as readFileSync62,
159777
158758
  rmSync as rmSync4,
159778
- writeFileSync as writeFileSync22
158759
+ writeFileSync as writeFileSync23
159779
158760
  } from "fs";
159780
- import { join as join103 } from "path";
158761
+ import { join as join104 } from "path";
159781
158762
  var GENERATED_MARKER = "<!-- Generated by hiai-opencode builtin skill materializer. -->";
159782
158763
  var MANAGED_SKILL_METADATA = ".hiai-skill.json";
159783
158764
  var LEGACY_MANAGED_DIR_MARKER = ".hiai-plugin-skill";
@@ -159804,10 +158785,10 @@ function buildSkillFrontmatter(skill2) {
159804
158785
  `);
159805
158786
  }
159806
158787
  function shouldWriteSkillFile(skillFilePath) {
159807
- if (!existsSync91(skillFilePath))
158788
+ if (!existsSync92(skillFilePath))
159808
158789
  return true;
159809
158790
  try {
159810
- const existing = readFileSync61(skillFilePath, "utf-8");
158791
+ const existing = readFileSync62(skillFilePath, "utf-8");
159811
158792
  return existing.includes(GENERATED_MARKER);
159812
158793
  } catch {
159813
158794
  return false;
@@ -159815,13 +158796,13 @@ function shouldWriteSkillFile(skillFilePath) {
159815
158796
  }
159816
158797
  function getSkillLayout() {
159817
158798
  const configDir = getOpenCodeConfigDir({ binary: "opencode" });
159818
- const assembledSkillsDir = join103(configDir, "skills");
159819
- const sourceRootDir = join103(configDir, ".hiai", "skills");
159820
- const builtinSourceDir = join103(sourceRootDir, "builtin");
159821
- const pluginSourceDir = join103(sourceRootDir, "plugin");
159822
- mkdirSync20(assembledSkillsDir, { recursive: true });
159823
- mkdirSync20(builtinSourceDir, { recursive: true });
159824
- mkdirSync20(pluginSourceDir, { recursive: true });
158799
+ const assembledSkillsDir = join104(configDir, "skills");
158800
+ const sourceRootDir = join104(configDir, ".hiai", "skills");
158801
+ const builtinSourceDir = join104(sourceRootDir, "builtin");
158802
+ const pluginSourceDir = join104(sourceRootDir, "plugin");
158803
+ mkdirSync21(assembledSkillsDir, { recursive: true });
158804
+ mkdirSync21(builtinSourceDir, { recursive: true });
158805
+ mkdirSync21(pluginSourceDir, { recursive: true });
159825
158806
  return {
159826
158807
  configDir,
159827
158808
  assembledSkillsDir,
@@ -159831,31 +158812,31 @@ function getSkillLayout() {
159831
158812
  };
159832
158813
  }
159833
158814
  function writeManagedSkillMetadata(targetDir, metadata) {
159834
- writeFileSync22(join103(targetDir, MANAGED_SKILL_METADATA), `${JSON.stringify(metadata, null, 2)}
158815
+ writeFileSync23(join104(targetDir, MANAGED_SKILL_METADATA), `${JSON.stringify(metadata, null, 2)}
159835
158816
  `, "utf-8");
159836
158817
  }
159837
158818
  function readManagedSkillMetadata(targetDir) {
159838
- const metadataPath = join103(targetDir, MANAGED_SKILL_METADATA);
159839
- if (!existsSync91(metadataPath)) {
158819
+ const metadataPath = join104(targetDir, MANAGED_SKILL_METADATA);
158820
+ if (!existsSync92(metadataPath)) {
159840
158821
  return null;
159841
158822
  }
159842
158823
  try {
159843
- return JSON.parse(readFileSync61(metadataPath, "utf-8"));
158824
+ return JSON.parse(readFileSync62(metadataPath, "utf-8"));
159844
158825
  } catch {
159845
158826
  return null;
159846
158827
  }
159847
158828
  }
159848
158829
  function isLegacyManagedSkillDir(targetDir, origin) {
159849
- if (origin === "plugin" && existsSync91(join103(targetDir, LEGACY_MANAGED_DIR_MARKER))) {
158830
+ if (origin === "plugin" && existsSync92(join104(targetDir, LEGACY_MANAGED_DIR_MARKER))) {
159850
158831
  return true;
159851
158832
  }
159852
158833
  if (origin === "builtin") {
159853
- const skillFilePath = join103(targetDir, "SKILL.md");
159854
- if (!existsSync91(skillFilePath)) {
158834
+ const skillFilePath = join104(targetDir, "SKILL.md");
158835
+ if (!existsSync92(skillFilePath)) {
159855
158836
  return false;
159856
158837
  }
159857
158838
  try {
159858
- return readFileSync61(skillFilePath, "utf-8").includes(GENERATED_MARKER);
158839
+ return readFileSync62(skillFilePath, "utf-8").includes(GENERATED_MARKER);
159859
158840
  } catch {
159860
158841
  return false;
159861
158842
  }
@@ -159863,7 +158844,7 @@ function isLegacyManagedSkillDir(targetDir, origin) {
159863
158844
  return false;
159864
158845
  }
159865
158846
  function shouldSyncManagedSkillDir(targetDir, origin) {
159866
- if (!existsSync91(targetDir))
158847
+ if (!existsSync92(targetDir))
159867
158848
  return true;
159868
158849
  const metadata = readManagedSkillMetadata(targetDir);
159869
158850
  if (metadata?.generatedBy === "hiai-opencode" && metadata.origin === origin) {
@@ -159879,7 +158860,7 @@ function shouldCleanupManagedSkillDir(targetDir, origin) {
159879
158860
  return isLegacyManagedSkillDir(targetDir, origin);
159880
158861
  }
159881
158862
  function cleanupRemovedManagedSkillDirs(rootDir, origin, expectedSkillNames) {
159882
- if (!existsSync91(rootDir)) {
158863
+ if (!existsSync92(rootDir)) {
159883
158864
  return;
159884
158865
  }
159885
158866
  for (const entry of readdirSync26(rootDir, { withFileTypes: true })) {
@@ -159887,17 +158868,17 @@ function cleanupRemovedManagedSkillDirs(rootDir, origin, expectedSkillNames) {
159887
158868
  continue;
159888
158869
  if (expectedSkillNames.has(entry.name))
159889
158870
  continue;
159890
- const targetDir = join103(rootDir, entry.name);
158871
+ const targetDir = join104(rootDir, entry.name);
159891
158872
  if (shouldCleanupManagedSkillDir(targetDir, origin)) {
159892
158873
  rmSync4(targetDir, { recursive: true, force: true });
159893
158874
  }
159894
158875
  }
159895
158876
  }
159896
158877
  function writeSkillRegistry(configDir, entries) {
159897
- const registryDir = join103(configDir, ".hiai");
159898
- const assembledSkillsDir = join103(configDir, "skills");
159899
- mkdirSync20(registryDir, { recursive: true });
159900
- mkdirSync20(assembledSkillsDir, { recursive: true });
158878
+ const registryDir = join104(configDir, ".hiai");
158879
+ const assembledSkillsDir = join104(configDir, "skills");
158880
+ mkdirSync21(registryDir, { recursive: true });
158881
+ mkdirSync21(assembledSkillsDir, { recursive: true });
159901
158882
  const sortedEntries = [...entries].sort((left, right) => left.name.localeCompare(right.name));
159902
158883
  const payload = {
159903
158884
  generatedBy: "hiai-opencode",
@@ -159909,12 +158890,12 @@ function writeSkillRegistry(configDir, entries) {
159909
158890
  },
159910
158891
  layout: {
159911
158892
  assembledSkillsDir,
159912
- builtinSourceDir: join103(registryDir, "skills", "builtin"),
159913
- pluginSourceDir: join103(registryDir, "skills", "plugin")
158893
+ builtinSourceDir: join104(registryDir, "skills", "builtin"),
158894
+ pluginSourceDir: join104(registryDir, "skills", "plugin")
159914
158895
  },
159915
158896
  entries: sortedEntries
159916
158897
  };
159917
- writeFileSync22(join103(registryDir, "skill-registry.json"), `${JSON.stringify(payload, null, 2)}
158898
+ writeFileSync23(join104(registryDir, "skill-registry.json"), `${JSON.stringify(payload, null, 2)}
159918
158899
  `, "utf-8");
159919
158900
  const readme = [
159920
158901
  "# Hiai Skill Layout",
@@ -159929,24 +158910,24 @@ function writeSkillRegistry(configDir, entries) {
159929
158910
  ""
159930
158911
  ].join(`
159931
158912
  `);
159932
- writeFileSync22(join103(assembledSkillsDir, "README.md"), readme, "utf-8");
158913
+ writeFileSync23(join104(assembledSkillsDir, "README.md"), readme, "utf-8");
159933
158914
  }
159934
158915
  function materializeBuiltinSkills(skills) {
159935
158916
  const { assembledSkillsDir, builtinSourceDir } = getSkillLayout();
159936
158917
  const expectedSkillNames = new Set(skills.map((skill2) => skill2.name));
159937
158918
  for (const skill2 of skills) {
159938
- const sourceSkillDir = join103(builtinSourceDir, skill2.name);
159939
- const sourceSkillFilePath = join103(sourceSkillDir, "SKILL.md");
159940
- const assembledSkillDir = join103(assembledSkillsDir, skill2.name);
159941
- const assembledSkillFilePath = join103(assembledSkillDir, "SKILL.md");
158919
+ const sourceSkillDir = join104(builtinSourceDir, skill2.name);
158920
+ const sourceSkillFilePath = join104(sourceSkillDir, "SKILL.md");
158921
+ const assembledSkillDir = join104(assembledSkillsDir, skill2.name);
158922
+ const assembledSkillFilePath = join104(assembledSkillDir, "SKILL.md");
159942
158923
  const content = buildSkillFrontmatter(skill2);
159943
- mkdirSync20(sourceSkillDir, { recursive: true });
159944
- mkdirSync20(assembledSkillDir, { recursive: true });
158924
+ mkdirSync21(sourceSkillDir, { recursive: true });
158925
+ mkdirSync21(assembledSkillDir, { recursive: true });
159945
158926
  if (!shouldWriteSkillFile(sourceSkillFilePath) || !shouldWriteSkillFile(assembledSkillFilePath)) {
159946
158927
  continue;
159947
158928
  }
159948
- writeFileSync22(sourceSkillFilePath, content, "utf-8");
159949
- writeFileSync22(assembledSkillFilePath, content, "utf-8");
158929
+ writeFileSync23(sourceSkillFilePath, content, "utf-8");
158930
+ writeFileSync23(assembledSkillFilePath, content, "utf-8");
159950
158931
  writeManagedSkillMetadata(sourceSkillDir, {
159951
158932
  name: skill2.name,
159952
158933
  origin: "builtin",
@@ -159966,8 +158947,8 @@ function materializeBuiltinSkills(skills) {
159966
158947
  cleanupRemovedManagedSkillDirs(assembledSkillsDir, "builtin", expectedSkillNames);
159967
158948
  }
159968
158949
  function materializePluginSkillDirectories(pluginRootDir) {
159969
- const sourceSkillsDir = join103(pluginRootDir, "skills");
159970
- if (!existsSync91(sourceSkillsDir)) {
158950
+ const sourceSkillsDir = join104(pluginRootDir, "skills");
158951
+ if (!existsSync92(sourceSkillsDir)) {
159971
158952
  return;
159972
158953
  }
159973
158954
  const { assembledSkillsDir, pluginSourceDir, configDir } = getSkillLayout();
@@ -159979,9 +158960,9 @@ function materializePluginSkillDirectories(pluginRootDir) {
159979
158960
  if (entry.name === "tmp")
159980
158961
  continue;
159981
158962
  expectedSkillNames.add(entry.name);
159982
- const sourceDir = join103(sourceSkillsDir, entry.name);
159983
- const mirroredSourceDir = join103(pluginSourceDir, entry.name);
159984
- const assembledDir = join103(assembledSkillsDir, entry.name);
158963
+ const sourceDir = join104(sourceSkillsDir, entry.name);
158964
+ const mirroredSourceDir = join104(pluginSourceDir, entry.name);
158965
+ const assembledDir = join104(assembledSkillsDir, entry.name);
159985
158966
  if (!shouldSyncManagedSkillDir(mirroredSourceDir, "plugin") || !shouldSyncManagedSkillDir(assembledDir, "plugin")) {
159986
158967
  continue;
159987
158968
  }
@@ -160015,7 +158996,7 @@ function materializePluginSkillDirectories(pluginRootDir) {
160015
158996
  for (const builtinEntry of readdirSync26(assembledSkillsDir, { withFileTypes: true })) {
160016
158997
  if (!builtinEntry.isDirectory())
160017
158998
  continue;
160018
- const assembledDir = join103(assembledSkillsDir, builtinEntry.name);
158999
+ const assembledDir = join104(assembledSkillsDir, builtinEntry.name);
160019
159000
  const metadata = readManagedSkillMetadata(assembledDir);
160020
159001
  if (metadata?.generatedBy !== "hiai-opencode" || metadata.origin !== "builtin") {
160021
159002
  continue;
@@ -160032,45 +159013,17 @@ function materializePluginSkillDirectories(pluginRootDir) {
160032
159013
 
160033
159014
  // src/index.ts
160034
159015
  var activePluginDispose = null;
160035
- function createWebsearchFallback(config4) {
160036
- const model = config4.agents?.researcher?.model?.trim();
160037
- if (!model) {
160038
- return;
160039
- }
160040
- if (model.startsWith("openrouter/")) {
160041
- return {
160042
- providerID: OPENROUTER_PROVIDER_ID,
160043
- model: model.slice("openrouter/".length)
160044
- };
160045
- }
160046
- if (model.startsWith("openai/")) {
160047
- return {
160048
- providerID: OPENAI_PROVIDER_ID,
160049
- model: model.slice("openai/".length)
160050
- };
160051
- }
160052
- if (model.startsWith("google/")) {
160053
- return {
160054
- providerID: GOOGLE_PROVIDER_ID,
160055
- model: model.slice("google/".length)
160056
- };
160057
- }
160058
- return {
160059
- providerID: OPENROUTER_PROVIDER_ID,
160060
- model
160061
- };
160062
- }
160063
159016
  function configureBundledBunPtyLibrary() {
160064
159017
  if (process.env.BUN_PTY_LIB?.trim()) {
160065
159018
  return;
160066
159019
  }
160067
159020
  const libraryName = process.platform === "win32" ? "rust_pty.dll" : process.platform === "darwin" ? process.arch === "arm64" ? "librust_pty_arm64.dylib" : "librust_pty.dylib" : process.arch === "arm64" ? "librust_pty_arm64.so" : "librust_pty.so";
160068
159021
  const candidates = [
160069
- join105(import.meta.dirname, "..", "node_modules", "bun-pty", "rust-pty", "target", "release", libraryName),
160070
- join105(import.meta.dirname, "..", "..", "bun-pty", "rust-pty", "target", "release", libraryName),
160071
- join105(import.meta.dirname, "..", "..", "..", "bun-pty", "rust-pty", "target", "release", libraryName)
159022
+ join106(import.meta.dirname, "..", "node_modules", "bun-pty", "rust-pty", "target", "release", libraryName),
159023
+ join106(import.meta.dirname, "..", "..", "bun-pty", "rust-pty", "target", "release", libraryName),
159024
+ join106(import.meta.dirname, "..", "..", "..", "bun-pty", "rust-pty", "target", "release", libraryName)
160072
159025
  ];
160073
- const resolved = candidates.find((candidate) => existsSync93(candidate));
159026
+ const resolved = candidates.find((candidate) => existsSync94(candidate));
160074
159027
  if (resolved) {
160075
159028
  process.env.BUN_PTY_LIB = resolved;
160076
159029
  }
@@ -160092,11 +159045,12 @@ var HiaiOpenCodePlugin = async (ctx) => {
160092
159045
  pluginConfig: pluginConfig2,
160093
159046
  platformConfig: internalConfig
160094
159047
  });
159048
+ autoExportStaticMcpJson(ctx.directory, internalConfig);
160095
159049
  materializeBuiltinSkills(createBuiltinSkills({
160096
159050
  browserProvider: pluginConfig2.browser_automation_engine?.provider ?? "playwright",
160097
159051
  disabledSkills: new Set(pluginConfig2.disabled_skills ?? [])
160098
159052
  }));
160099
- materializePluginSkillDirectories(join105(import.meta.dirname, ".."));
159053
+ materializePluginSkillDirectories(join106(import.meta.dirname, ".."));
160100
159054
  const { initializeModelRequirements: initializeModelRequirements2 } = await Promise.resolve().then(() => (init_model_requirements(), exports_model_requirements));
160101
159055
  initializeModelRequirements2(internalConfig);
160102
159056
  const { initializeModelHeuristics: initializeModelHeuristics2 } = await Promise.resolve().then(() => (init_model_capability_heuristics(), exports_model_capability_heuristics));
@@ -160157,16 +159111,12 @@ var HiaiOpenCodePlugin = async (ctx) => {
160157
159111
  } catch (err) {
160158
159112
  console.error("[hiai-opencode] PTYPlugin failed to load:", err);
160159
159113
  }
160160
- const websearchResult = await websearch_cited_default(ctx, createWebsearchFallback(internalConfig));
160161
- const websearchGoogleResult = await WebsearchCitedGooglePlugin(ctx);
160162
- const websearchOpenAIResult = await WebsearchCitedOpenAIPlugin(ctx);
160163
159114
  const combinedResult = {
160164
159115
  name: PLUGIN_NAME,
160165
159116
  ...pluginInterface,
160166
159117
  tool: {
160167
159118
  ...pluginInterface.tool,
160168
- ...ptyResult.tool,
160169
- ...websearchResult.tool
159119
+ ...ptyResult.tool
160170
159120
  },
160171
159121
  "command.execute.before": async (input, output) => {
160172
159122
  await pluginInterface["command.execute.before"]?.(input, output);
@@ -160187,38 +159137,12 @@ var HiaiOpenCodePlugin = async (ctx) => {
160187
159137
  config: async (input) => {
160188
159138
  await pluginInterface.config?.(input);
160189
159139
  await subtask2Result.config?.(input);
160190
- await websearchResult.config?.(input);
160191
159140
  },
160192
159141
  event: async (input) => {
160193
159142
  await pluginInterface.event?.(input);
160194
159143
  await subtask2Result.event?.(input);
160195
159144
  await ptyResult.event?.(input);
160196
159145
  },
160197
- auth: {
160198
- provider: "hiai-opencode",
160199
- methods: [
160200
- { type: "api", label: "Google Search API key" }
160201
- ],
160202
- loader: async (getAuth) => {
160203
- const authData = await getAuth();
160204
- const { registerGetAuth: registerGetAuth2, GOOGLE_PROVIDER_ID: GOOGLE_PROVIDER_ID2, OPENAI_PROVIDER_ID: OPENAI_PROVIDER_ID2, OPENROUTER_PROVIDER_ID: OPENROUTER_PROVIDER_ID2 } = await Promise.resolve().then(() => (init_websearch_cited(), exports_websearch_cited));
160205
- const getConfiguredKey = (configKey) => {
160206
- if (configKey)
160207
- return resolveEnvVars(configKey);
160208
- return;
160209
- };
160210
- const googleKey = authData["Google Search API key"] || getConfiguredKey(internalConfig.auth?.googleSearch);
160211
- const openaiKey = getConfiguredKey(internalConfig.auth?.openai);
160212
- const openRouterKey = getConfiguredKey(internalConfig.auth?.openrouter);
160213
- if (googleKey)
160214
- registerGetAuth2(GOOGLE_PROVIDER_ID2, () => Promise.resolve({ type: "api", key: googleKey }));
160215
- if (openaiKey)
160216
- registerGetAuth2(OPENAI_PROVIDER_ID2, () => Promise.resolve({ type: "api", key: openaiKey }));
160217
- if (openRouterKey)
160218
- registerGetAuth2(OPENROUTER_PROVIDER_ID2, () => Promise.resolve({ type: "api", key: openRouterKey }));
160219
- return {};
160220
- }
160221
- },
160222
159146
  "experimental.session.compacting": async (_input, output) => {
160223
159147
  await hooks.compactionContextInjector?.capture(_input.sessionID);
160224
159148
  await hooks.compactionTodoPreserver?.capture(_input.sessionID);