om-memory-system 3.0.0 → 3.1.0-next.3

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 (52) hide show
  1. package/{LICENSE → LICENSE.md} +4 -2
  2. package/README.md +108 -532
  3. package/dist/adapters/opencode/auto-capture-summary.js +23 -17
  4. package/dist/adapters/opencode/import-command.d.ts +42 -0
  5. package/dist/adapters/opencode/import-command.js +154 -0
  6. package/dist/adapters/pi/extension.js +4 -5
  7. package/dist/adapters/pi/import-command.d.ts +7 -19
  8. package/dist/adapters/pi/import-command.js +46 -161
  9. package/dist/adapters/pi/live-model.d.ts +17 -0
  10. package/dist/adapters/pi/live-model.js +86 -0
  11. package/dist/adapters/pi/profile.d.ts +7 -17
  12. package/dist/adapters/pi/profile.js +28 -66
  13. package/dist/adapters/pi/provider.d.ts +8 -1
  14. package/dist/adapters/pi/provider.js +34 -6
  15. package/dist/cli/index.d.ts +7 -0
  16. package/dist/cli/index.js +94 -0
  17. package/dist/config.d.ts +7 -20
  18. package/dist/config.js +24 -61
  19. package/dist/core/extraction.d.ts +2 -2
  20. package/dist/core/extraction.js +8 -3
  21. package/dist/core/profile-analysis.d.ts +16 -0
  22. package/dist/core/profile-analysis.js +47 -0
  23. package/dist/importer/import-args.d.ts +44 -0
  24. package/dist/importer/import-args.js +219 -0
  25. package/dist/importer/importer.d.ts +33 -9
  26. package/dist/importer/importer.js +153 -89
  27. package/dist/importer/ledger.d.ts +9 -2
  28. package/dist/importer/ledger.js +16 -2
  29. package/dist/importer/model-selection.d.ts +16 -0
  30. package/dist/importer/model-selection.js +84 -0
  31. package/dist/importer/opencode-import.d.ts +28 -0
  32. package/dist/importer/opencode-import.js +87 -0
  33. package/dist/importer/opencode-project.d.ts +11 -0
  34. package/dist/importer/opencode-project.js +38 -0
  35. package/dist/importer/opencode-reader.d.ts +19 -0
  36. package/dist/importer/opencode-reader.js +214 -0
  37. package/dist/importer/profile-import.d.ts +29 -0
  38. package/dist/importer/profile-import.js +111 -0
  39. package/dist/importer/run-import.d.ts +49 -0
  40. package/dist/importer/run-import.js +123 -0
  41. package/dist/index.d.ts +4 -0
  42. package/dist/index.js +37 -1
  43. package/dist/services/ai/internal-capture-sessions.d.ts +2 -0
  44. package/dist/services/ai/internal-capture-sessions.js +7 -0
  45. package/dist/services/ai/live-model-choice.d.ts +52 -0
  46. package/dist/services/ai/live-model-choice.js +83 -0
  47. package/dist/services/ai/profile-llm-client.js +7 -5
  48. package/dist/services/user-memory-learning.js +9 -8
  49. package/dist/services/user-profile/ai-cleanup.js +8 -5
  50. package/dist/services/user-profile/user-profile-manager.js +11 -10
  51. package/dist/v2/adapter.js +27 -0
  52. package/package.json +5 -2
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Which model live capture and profile learning call, the same rule on both
3
+ * hosts. Pure functions of the config: callers pass CONFIG, so this module
4
+ * has no dependency on config loading.
5
+ */
6
+ export interface AutoCaptureProviderRuntimeConfig {
7
+ opencodeProvider?: string;
8
+ opencodeModel?: string;
9
+ memoryProvider?: string;
10
+ memoryModel?: string;
11
+ memoryApiUrl?: string;
12
+ memoryApiKey?: string;
13
+ }
14
+ /**
15
+ * Where live capture and profile learning send model calls, the same on both hosts:
16
+ * - `opencode`: the configured host model (`opencodeModel`/`piModel`; "inherit" = session model)
17
+ * - `manual`: no host model, so the external API (`memoryModel`/`memoryApiUrl`/`memoryApiKey`)
18
+ * - `session`: neither is set, so the session's own model through the host's sign-in
19
+ */
20
+ export type AutoCaptureProviderStatus = {
21
+ ready: true;
22
+ mode: "opencode" | "manual" | "session";
23
+ issues: [];
24
+ } | {
25
+ ready: false;
26
+ issues: string[];
27
+ };
28
+ export declare function getAutoCaptureProviderStatus(config: AutoCaptureProviderRuntimeConfig): AutoCaptureProviderStatus;
29
+ /** OpenCode host model for live calls, or null when the external API should be used. */
30
+ export declare function resolveOpencodeHostModel(config: AutoCaptureProviderRuntimeConfig): {
31
+ providerID: string;
32
+ modelID: string;
33
+ } | null;
34
+ export type PiLiveModelChoice = {
35
+ kind: "pi";
36
+ provider: string;
37
+ model: string;
38
+ } | {
39
+ kind: "session";
40
+ } | {
41
+ kind: "manual";
42
+ } | {
43
+ kind: "unready";
44
+ issues: string[];
45
+ };
46
+ /** Pi's live model, by the same rule as OpenCode's (`piProvider`/`piModel` in place of `opencode*`). */
47
+ export declare function resolvePiLiveModel(config: AutoCaptureProviderRuntimeConfig & {
48
+ piProvider?: string;
49
+ piModel?: string;
50
+ }): PiLiveModelChoice;
51
+ /** True when the external API is fully configured and can serve as a fallback. */
52
+ export declare function isExternalModelReady(config: AutoCaptureProviderRuntimeConfig): boolean;
@@ -0,0 +1,83 @@
1
+ import { isPlaceholderApiKey } from "./api-key-placeholder.js";
2
+ function hasValue(value) {
3
+ return typeof value === "string" && value.trim().length > 0;
4
+ }
5
+ /** External-API readiness alone; any memory* value set counts as intent to use it. */
6
+ function getManualProviderStatus(config) {
7
+ const hasMemoryModel = hasValue(config.memoryModel);
8
+ const hasMemoryApiUrl = hasValue(config.memoryApiUrl);
9
+ const hasMemoryApiKey = hasValue(config.memoryApiKey);
10
+ const hasPlaceholderMemoryApiKey = isPlaceholderApiKey(config.memoryApiKey);
11
+ const attempted = hasMemoryModel || hasMemoryApiUrl || hasMemoryApiKey;
12
+ const issues = [];
13
+ // The orcarouter provider presets its endpoint and default model, so only
14
+ // an API key is required for the manual fallback path.
15
+ if (config.memoryProvider === "orcarouter") {
16
+ if (!hasMemoryApiKey)
17
+ issues.push("memoryApiKey is not configured");
18
+ if (hasPlaceholderMemoryApiKey)
19
+ issues.push("memoryApiKey contains a placeholder value");
20
+ return { attempted, ready: hasMemoryApiKey && !hasPlaceholderMemoryApiKey, issues };
21
+ }
22
+ if (!hasMemoryModel)
23
+ issues.push("memoryModel is not configured");
24
+ if (!hasMemoryApiUrl)
25
+ issues.push("memoryApiUrl is not configured");
26
+ if (!hasMemoryApiKey)
27
+ issues.push("memoryApiKey is not configured");
28
+ if (hasPlaceholderMemoryApiKey)
29
+ issues.push("memoryApiKey contains a placeholder value");
30
+ return {
31
+ attempted,
32
+ ready: hasMemoryModel && hasMemoryApiUrl && hasMemoryApiKey && !hasPlaceholderMemoryApiKey,
33
+ issues,
34
+ };
35
+ }
36
+ export function getAutoCaptureProviderStatus(config) {
37
+ const hasOpencodeProvider = hasValue(config.opencodeProvider);
38
+ const hasOpencodeModel = hasValue(config.opencodeModel);
39
+ if (hasOpencodeProvider && hasOpencodeModel) {
40
+ return { ready: true, mode: "opencode", issues: [] };
41
+ }
42
+ const manual = getManualProviderStatus(config);
43
+ if (manual.ready)
44
+ return { ready: true, mode: "manual", issues: [] };
45
+ // Nothing configured at all: follow the session's model, as Pi always has.
46
+ if (!manual.attempted)
47
+ return { ready: true, mode: "session", issues: [] };
48
+ // A half-configured external API is a mistake worth reporting, not a silent switch.
49
+ const issues = [];
50
+ if (!hasOpencodeProvider)
51
+ issues.push("opencodeProvider is not configured");
52
+ if (!hasOpencodeModel)
53
+ issues.push("opencodeModel is not configured");
54
+ return { ready: false, issues: [...issues, ...manual.issues] };
55
+ }
56
+ /** OpenCode host model for live calls, or null when the external API should be used. */
57
+ export function resolveOpencodeHostModel(config) {
58
+ const status = getAutoCaptureProviderStatus(config);
59
+ if (!status.ready || status.mode === "manual")
60
+ return null;
61
+ if (status.mode === "opencode") {
62
+ return { providerID: config.opencodeProvider.trim(), modelID: config.opencodeModel.trim() };
63
+ }
64
+ return { providerID: config.opencodeProvider?.trim() ?? "", modelID: "inherit" };
65
+ }
66
+ /** Pi's live model, by the same rule as OpenCode's (`piProvider`/`piModel` in place of `opencode*`). */
67
+ export function resolvePiLiveModel(config) {
68
+ if (hasValue(config.piProvider) && hasValue(config.piModel)) {
69
+ return config.piModel.trim() === "inherit"
70
+ ? { kind: "session" }
71
+ : { kind: "pi", provider: config.piProvider.trim(), model: config.piModel.trim() };
72
+ }
73
+ const manual = getManualProviderStatus(config);
74
+ if (manual.ready)
75
+ return { kind: "manual" };
76
+ if (!manual.attempted)
77
+ return { kind: "session" };
78
+ return { kind: "unready", issues: manual.issues };
79
+ }
80
+ /** True when the external API is fully configured and can serve as a fallback. */
81
+ export function isExternalModelReady(config) {
82
+ return getManualProviderStatus(config).ready;
83
+ }
@@ -1,18 +1,20 @@
1
1
  import { CONFIG } from "../../config.js";
2
+ import { resolveOpencodeHostModel } from "./live-model-choice.js";
2
3
  import { loadOpencodeProvider } from "./opencode-provider-loader.js";
3
4
  let _cachedClient = null;
4
5
  let _cachedProvider = null;
5
6
  let _cachedModel = null;
6
7
  export async function getOpenCodeClient() {
7
- const provider = CONFIG.opencodeProvider;
8
- const model = CONFIG.opencodeModel;
9
- if (!provider || !model) {
10
- throw new Error("omms: opencodeProvider and opencodeModel must be configured");
8
+ const hostModel = resolveOpencodeHostModel(CONFIG);
9
+ if (!hostModel) {
10
+ throw new Error("omms: live calls use the external API; no OpenCode model is configured");
11
11
  }
12
+ const { isProviderConnected, getV2Client, resolveOpencodeModelRef } = await loadOpencodeProvider();
13
+ // "inherit" checks the provider of the model it resolves to.
14
+ const { providerID: provider, modelID: model } = resolveOpencodeModelRef(hostModel);
12
15
  if (_cachedClient && _cachedProvider === provider && _cachedModel === model) {
13
16
  return _cachedClient;
14
17
  }
15
- const { isProviderConnected, getV2Client } = await loadOpencodeProvider();
16
18
  if (!isProviderConnected(provider)) {
17
19
  throw new Error(`opencode provider '${provider}' is not connected. Check your opencode provider configuration.`);
18
20
  }
@@ -1,6 +1,7 @@
1
1
  import { getTags } from "./tags.js";
2
2
  import { log } from "./logger.js";
3
3
  import { CONFIG } from "../config.js";
4
+ import { resolveOpencodeHostModel } from "./ai/live-model-choice.js";
4
5
  import { userPromptManager } from "./user-prompt/user-prompt-manager.js";
5
6
  import { userProfileManager } from "./user-profile/user-profile-manager.js";
6
7
  import { sortProfileItems } from "../utils/profile.js";
@@ -491,14 +492,14 @@ async function applyValidations(profileData, llmResult, profileId, prefKeys) {
491
492
  async function analyzeUserProfile(context, existingProfile) {
492
493
  log("user-profile-learning: analyze called", { hasProfile: !!existingProfile });
493
494
  let opencodeProviderError;
494
- if (CONFIG.opencodeProvider && CONFIG.opencodeModel) {
495
+ if (resolveOpencodeHostModel(CONFIG)) {
495
496
  log("user-profile-learning: trying opencode provider");
496
497
  try {
497
498
  const { generateStructuredOutput } = await loadOpencodeProvider();
498
499
  const { getOpenCodeClient } = await import("./ai/profile-llm-client.js");
499
500
  log("user-profile-learning: opencode provider diag", {
500
- provider: CONFIG.opencodeProvider,
501
- model: CONFIG.opencodeModel,
501
+ provider: resolveOpencodeHostModel(CONFIG)?.providerID,
502
+ model: resolveOpencodeHostModel(CONFIG)?.modelID,
502
503
  });
503
504
  const v2Client = await getOpenCodeClient();
504
505
  const systemPrompt = `You are a user behavior analyst for a coding assistant.
@@ -516,8 +517,8 @@ Use the update_user_profile tool to save the ${existingProfile ? "updated" : "ne
516
517
  const result = await Promise.race([
517
518
  generateStructuredOutput({
518
519
  client: v2Client,
519
- providerID: CONFIG.opencodeProvider,
520
- modelID: CONFIG.opencodeModel,
520
+ providerID: resolveOpencodeHostModel(CONFIG).providerID,
521
+ modelID: resolveOpencodeHostModel(CONFIG).modelID,
521
522
  systemPrompt,
522
523
  userPrompt: context,
523
524
  schema,
@@ -591,7 +592,7 @@ Identify sequences where earlier prompts led to later ones (e.g. "learned X →
591
592
  { "paths": [{ "topic": "string", "chain": ["step1", "step2", "step3"], "description": "one sentence summary" }] }
592
593
  If no clear chains, return { "paths": [] }.`;
593
594
  let result = null;
594
- if (CONFIG.opencodeProvider && CONFIG.opencodeModel) {
595
+ if (resolveOpencodeHostModel(CONFIG)) {
595
596
  try {
596
597
  const { z } = await import("zod");
597
598
  const { generateStructuredOutput } = await loadOpencodeProvider();
@@ -607,8 +608,8 @@ If no clear chains, return { "paths": [] }.`;
607
608
  result = (await Promise.race([
608
609
  generateStructuredOutput({
609
610
  client: v2Client,
610
- providerID: CONFIG.opencodeProvider,
611
- modelID: CONFIG.opencodeModel,
611
+ providerID: resolveOpencodeHostModel(CONFIG).providerID,
612
+ modelID: resolveOpencodeHostModel(CONFIG).modelID,
612
613
  systemPrompt,
613
614
  userPrompt,
614
615
  schema: z.object({
@@ -1,4 +1,5 @@
1
1
  import { CONFIG } from "../../config.js";
2
+ import { resolveOpencodeHostModel } from "../ai/live-model-choice.js";
2
3
  import { log } from "../logger.js";
3
4
  import { loadOpencodeProvider } from "../ai/opencode-provider-loader.js";
4
5
  import { EXTERNAL_PROFILE_CLEANUP_TIMEOUT_MS, OPENCODE_PROFILE_CLEANUP_TIMEOUT_MS, } from "../request-timeouts.js";
@@ -151,7 +152,7 @@ async function callAICleanup(prompt) {
151
152
  // Use opencode internal session when opencodeProvider is configured (same pattern as auto-capture).
152
153
  // When the client is available, surface OpenCode errors instead of masking them as
153
154
  // "No AI provider configured" via a silent fallback (#177).
154
- if (CONFIG.opencodeProvider && CONFIG.opencodeModel) {
155
+ if (resolveOpencodeHostModel(CONFIG)) {
155
156
  const { getV2Client } = await loadOpencodeProvider();
156
157
  const v2Client = getV2Client();
157
158
  if (v2Client) {
@@ -234,6 +235,11 @@ function raceWithTimeout(promise, ms, message) {
234
235
  }
235
236
  async function callViaOpencodeWithClient(v2Client, prompt) {
236
237
  const t0 = Date.now();
238
+ const hostModel = resolveOpencodeHostModel(CONFIG) ?? { providerID: "", modelID: "inherit" };
239
+ // Only "inherit" (or no model configured) needs resolving to the session's recent model.
240
+ const model = hostModel.modelID === "inherit"
241
+ ? (await loadOpencodeProvider()).resolveOpencodeModelRef(hostModel)
242
+ : hostModel;
237
243
  const systemPrompt = "You are a user profile cleanup assistant. Merge duplicate entries and return only JSON without markdown wrapping.";
238
244
  const created = (await raceWithTimeout(v2Client.session.create({
239
245
  title: "omms profile cleanup",
@@ -253,10 +259,7 @@ async function callViaOpencodeWithClient(v2Client, prompt) {
253
259
  const TIMEOUT_MS = OPENCODE_PROFILE_CLEANUP_TIMEOUT_MS;
254
260
  const promptResult = await raceWithTimeout(v2Client.session.prompt({
255
261
  sessionID,
256
- model: {
257
- providerID: CONFIG.opencodeProvider || "bs-aigw",
258
- modelID: CONFIG.opencodeModel || "deepseek-v4-flash",
259
- },
262
+ model,
260
263
  system: systemPrompt,
261
264
  parts: [{ type: "text", text: prompt }],
262
265
  // `noReply` suppresses assistant generation; cleanup needs the JSON reply (#177).
@@ -2,6 +2,7 @@ import { join } from "node:path";
2
2
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { tursoConnectionManager } from "../turso/connection-manager.js";
4
4
  import { CONFIG } from "../../config.js";
5
+ import { resolveOpencodeHostModel } from "../ai/live-model-choice.js";
5
6
  import { safeArray } from "./profile-utils.js";
6
7
  import { EmbeddingService } from "../embedding.js";
7
8
  import { log } from "../logger.js";
@@ -1178,7 +1179,7 @@ A: "${descA}"
1178
1179
  B: "${descB}"
1179
1180
 
1180
1181
  Answer JSON only: { "duplicate": true|false, "reason": "one sentence explanation" }`;
1181
- if (CONFIG.opencodeProvider && CONFIG.opencodeModel) {
1182
+ if (resolveOpencodeHostModel(CONFIG)) {
1182
1183
  try {
1183
1184
  const { z } = await import("zod");
1184
1185
  const { generateStructuredOutput } = await loadOpencodeProvider();
@@ -1194,8 +1195,8 @@ Answer JSON only: { "duplicate": true|false, "reason": "one sentence explanation
1194
1195
  const result = await Promise.race([
1195
1196
  generateStructuredOutput({
1196
1197
  client: v2Client,
1197
- providerID: CONFIG.opencodeProvider,
1198
- modelID: CONFIG.opencodeModel,
1198
+ providerID: resolveOpencodeHostModel(CONFIG).providerID,
1199
+ modelID: resolveOpencodeHostModel(CONFIG).modelID,
1199
1200
  systemPrompt: "You are a semantic duplicate detector. Output valid JSON.",
1200
1201
  userPrompt: prompt,
1201
1202
  schema: z.object({ duplicate: z.boolean(), reason: z.string() }),
@@ -1365,7 +1366,7 @@ A: "${descA}"
1365
1366
  B: "${descB}"
1366
1367
 
1367
1368
  Answer JSON only: { "conflict": true|false, "reason": "one sentence explanation" }`;
1368
- if (CONFIG.opencodeProvider && CONFIG.opencodeModel) {
1369
+ if (resolveOpencodeHostModel(CONFIG)) {
1369
1370
  try {
1370
1371
  const { z } = await import("zod");
1371
1372
  const { generateStructuredOutput } = await loadOpencodeProvider();
@@ -1381,8 +1382,8 @@ Answer JSON only: { "conflict": true|false, "reason": "one sentence explanation"
1381
1382
  const result = await Promise.race([
1382
1383
  generateStructuredOutput({
1383
1384
  client: v2Client,
1384
- providerID: CONFIG.opencodeProvider,
1385
- modelID: CONFIG.opencodeModel,
1385
+ providerID: resolveOpencodeHostModel(CONFIG).providerID,
1386
+ modelID: resolveOpencodeHostModel(CONFIG).modelID,
1386
1387
  systemPrompt: "You are a preference contradiction detector. Output valid JSON.",
1387
1388
  userPrompt: prompt,
1388
1389
  schema: z.object({ conflict: z.boolean(), reason: z.string() }),
@@ -1481,7 +1482,7 @@ ${evidenceList}
1481
1482
 
1482
1483
  Generate a concise, abstract description of the user's general behavioral tendency.`;
1483
1484
  let newDescription = null;
1484
- if (CONFIG.opencodeProvider && CONFIG.opencodeModel) {
1485
+ if (resolveOpencodeHostModel(CONFIG)) {
1485
1486
  try {
1486
1487
  newDescription = await this.callOpencodeProvider(systemPrompt, userPrompt);
1487
1488
  }
@@ -1602,7 +1603,7 @@ Generate a concise, abstract description of the user's general behavioral tenden
1602
1603
  }
1603
1604
  catch (e) {
1604
1605
  log("profile description evolution: native provider not connected", {
1605
- provider: CONFIG.opencodeProvider,
1606
+ provider: resolveOpencodeHostModel(CONFIG)?.providerID,
1606
1607
  error: String(e),
1607
1608
  });
1608
1609
  return null;
@@ -1612,8 +1613,8 @@ Generate a concise, abstract description of the user's general behavioral tenden
1612
1613
  const result = await Promise.race([
1613
1614
  generateStructuredOutput({
1614
1615
  client: v2Client,
1615
- providerID: CONFIG.opencodeProvider,
1616
- modelID: CONFIG.opencodeModel,
1616
+ providerID: resolveOpencodeHostModel(CONFIG).providerID,
1617
+ modelID: resolveOpencodeHostModel(CONFIG).modelID,
1617
1618
  systemPrompt,
1618
1619
  userPrompt,
1619
1620
  schema,
@@ -1,3 +1,4 @@
1
+ import { OPENCODE_IMPORT_COMMAND, OPENCODE_IMPORT_DESCRIPTION, runOpencodeImportCommand, } from "../adapters/opencode/import-command.js";
1
2
  import { log } from "../services/logger.js";
2
3
  import { eventBelongsToLocation, legacyToolResult, toLegacyEvent } from "./legacy-client.js";
3
4
  const memoryInput = {
@@ -127,6 +128,32 @@ export async function registerV2Adapter(ctx, legacy, memory) {
127
128
  async ask() { },
128
129
  })),
129
130
  }));
131
+ // Same command as V1 and Pi. V2 runs it natively, so no model turn relays the report.
132
+ await ctx.command?.transform?.((editor) => editor.add({
133
+ name: OPENCODE_IMPORT_COMMAND,
134
+ description: OPENCODE_IMPORT_DESCRIPTION,
135
+ execute: async (invocation) => {
136
+ const sessionID = invocation.sessionID;
137
+ const report = await runOpencodeImportCommand({
138
+ argsText: invocation.prompt?.text ?? "",
139
+ directory: ctx.location.directory,
140
+ sessionModel: async () => {
141
+ try {
142
+ const session = await ctx.session.get({ sessionID });
143
+ const model = session?.model ?? session?.data?.model;
144
+ return model?.providerID && model?.id
145
+ ? { providerID: String(model.providerID), modelID: String(model.id) }
146
+ : null;
147
+ }
148
+ catch (error) {
149
+ log("v2 import: could not read the session model", { error: String(error) });
150
+ return null;
151
+ }
152
+ },
153
+ });
154
+ await ctx.session.synthetic({ sessionID, text: report, resume: false });
155
+ },
156
+ }));
130
157
  await ctx.session.hook("prompt", async (event) => {
131
158
  const sessionID = event.sessionID;
132
159
  const text = event.prompt.text ?? "";
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "om-memory-system",
3
- "version": "3.0.0",
3
+ "version": "3.1.0-next.3",
4
4
  "description": "Opinionated modular memory system for coding agents (OpenCode + Pi)",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",
7
7
  "types": "dist/plugin.d.ts",
8
+ "bin": {
9
+ "om-memory-system": "dist/cli/index.js"
10
+ },
8
11
  "exports": {
9
12
  ".": {
10
13
  "types": "./dist/plugin.d.ts",
@@ -28,7 +31,7 @@
28
31
  }
29
32
  },
30
33
  "scripts": {
31
- "build": "rm -rf dist && bunx tsc && bun run web:build",
34
+ "build": "rm -rf dist && bunx tsc && chmod +x dist/cli/index.js && bun run web:build",
32
35
  "web:dev": "cd web && bun run dev",
33
36
  "web:build": "cd web && bun run build",
34
37
  "dev": "tsc --watch",