opencode-manifold 0.4.16 → 0.4.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -12,6 +12,45 @@ var __dirname2 = dirname(fileURLToPath(import.meta.url));
12
12
  var require2 = createRequire2(import.meta.url);
13
13
  var bundledTemplatesDir = join(__dirname2, "..", "src", "templates");
14
14
  var globalTemplatesDir = join(homedir(), ".config", "opencode", "manifold");
15
+ async function getPluginVersion() {
16
+ try {
17
+ const packageJson = require2(join(__dirname2, "..", "package.json"));
18
+ return packageJson.version || "unknown";
19
+ } catch {
20
+ return "unknown";
21
+ }
22
+ }
23
+ async function dirHasContent(dirPath) {
24
+ if (!existsSync(dirPath))
25
+ return false;
26
+ try {
27
+ const entries = await readdir(dirPath);
28
+ return entries.length > 0;
29
+ } catch {
30
+ return false;
31
+ }
32
+ }
33
+ async function copyMissingFiles(src, dest) {
34
+ if (!existsSync(src))
35
+ return [];
36
+ await mkdir(dest, { recursive: true });
37
+ const copied = [];
38
+ const entries = await readdir(src, { withFileTypes: true });
39
+ for (const entry of entries) {
40
+ const srcPath = join(src, entry.name);
41
+ const destPath = join(dest, entry.name);
42
+ if (entry.isDirectory()) {
43
+ const subCopied = await copyMissingFiles(srcPath, destPath);
44
+ if (subCopied.length > 0) {
45
+ copied.push(entry.name);
46
+ }
47
+ } else if (!existsSync(destPath)) {
48
+ await writeFile(destPath, await readFile(srcPath));
49
+ copied.push(entry.name);
50
+ }
51
+ }
52
+ return copied;
53
+ }
15
54
  async function copyFiles(src, dest) {
16
55
  if (!existsSync(src))
17
56
  return [];
@@ -65,6 +104,49 @@ async function ensureGlobalTemplates(ctx) {
65
104
  }
66
105
  });
67
106
  }
107
+ async function initProject(directory, client) {
108
+ const initialized = [];
109
+ await client.app.log({
110
+ body: {
111
+ service: "opencode-manifold",
112
+ level: "info",
113
+ message: `Running /manifold-init in ${directory}`
114
+ }
115
+ });
116
+ if (!await dirHasContent(globalTemplatesDir)) {
117
+ await client.app.log({
118
+ body: {
119
+ service: "opencode-manifold",
120
+ level: "error",
121
+ message: `Global templates not found at ${globalTemplatesDir}. Plugin may not have loaded correctly.`
122
+ }
123
+ });
124
+ return initialized;
125
+ }
126
+ const agentsCopied = await copyMissingFiles(join(globalTemplatesDir, "agents"), join(directory, ".opencode", "agents"));
127
+ if (agentsCopied.length > 0) {
128
+ initialized.push(`agents (${agentsCopied.join(", ")})`);
129
+ }
130
+ const skillsCopied = await copyMissingFiles(join(globalTemplatesDir, "skills"), join(directory, ".opencode", "skills"));
131
+ if (skillsCopied.length > 0) {
132
+ initialized.push(`skills (${skillsCopied.join(", ")})`);
133
+ }
134
+ const manifoldCopied = await copyMissingFiles(join(globalTemplatesDir, "manifold"), join(directory, "Manifold"));
135
+ if (manifoldCopied.length > 0) {
136
+ initialized.push(`Manifold/ (${manifoldCopied.join(", ")})`);
137
+ }
138
+ const version = await getPluginVersion();
139
+ await writeFile(join(directory, "Manifold", "VERSION"), version + `
140
+ `);
141
+ await client.app.log({
142
+ body: {
143
+ service: "opencode-manifold",
144
+ level: "info",
145
+ message: `/manifold-init complete: ${initialized.join(", ") || "already initialized"}`
146
+ }
147
+ });
148
+ return initialized;
149
+ }
68
150
 
69
151
  // src/tools/dispatch-task.ts
70
152
  import { tool } from "@opencode-ai/plugin";
@@ -4148,6 +4230,26 @@ var ManifoldPlugin = async (ctx) => {
4148
4230
  return {
4149
4231
  tool: {
4150
4232
  dispatchTask: dispatchTaskTool
4233
+ },
4234
+ "command.execute.before": async (input, output) => {
4235
+ if (input.command === "manifold-init") {
4236
+ const initialized = await initProject(ctx.directory, ctx.client);
4237
+ if (initialized.length > 0) {
4238
+ output.parts = [
4239
+ {
4240
+ type: "text",
4241
+ text: `Manifold initialized: ${initialized.join(", ")}`
4242
+ }
4243
+ ];
4244
+ } else {
4245
+ output.parts = [
4246
+ {
4247
+ type: "text",
4248
+ text: "All Manifold files already present. To reset a specific component, delete the corresponding file(s) from `.opencode/agents/`, `.opencode/skills/`, or `Manifold/`, then run `/manifold-init` again."
4249
+ }
4250
+ ];
4251
+ }
4252
+ }
4151
4253
  }
4152
4254
  };
4153
4255
  };
package/dist/tui.js CHANGED
@@ -5,7 +5,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
5
5
  import { existsSync } from "fs";
6
6
  import { join } from "path";
7
7
  import { homedir } from "os";
8
- import { readFile, writeFile, readdir, mkdir } from "fs/promises";
8
+ import { readFile, writeFile, readdir } from "fs/promises";
9
9
 
10
10
  // node_modules/js-yaml/dist/js-yaml.mjs
11
11
  /*! js-yaml 4.1.1 https://github.com/nodeca/js-yaml @license MIT */
@@ -2770,7 +2770,8 @@ var tui = async (api) => {
2770
2770
  category: "Manifold",
2771
2771
  slash: {
2772
2772
  name: "manifold-models"
2773
- }
2773
+ },
2774
+ onSelect: () => handleManifoldModels(api)
2774
2775
  },
2775
2776
  {
2776
2777
  title: "Manifold Update",
@@ -2779,164 +2780,97 @@ var tui = async (api) => {
2779
2780
  category: "Manifold",
2780
2781
  slash: {
2781
2782
  name: "manifold-update"
2782
- }
2783
+ },
2784
+ onSelect: () => handleManifoldUpdate(api)
2783
2785
  }
2784
2786
  ]);
2785
- api.event.on("tui.command.execute", async (event) => {
2786
- if (event.properties.command === "manifold-init") {
2787
- await handleManifoldInit(api);
2788
- } else if (event.properties.command === "manifold-models") {
2789
- await handleManifoldModels(api);
2790
- } else if (event.properties.command === "manifold-update") {
2791
- await handleManifoldUpdate(api);
2792
- }
2793
- });
2794
2787
  };
2795
- async function copyMissingFiles(src, dest) {
2796
- if (!existsSync(src))
2797
- return [];
2798
- await mkdir(dest, { recursive: true });
2799
- const copied = [];
2800
- const entries = await readdir(src, { withFileTypes: true });
2801
- for (const entry of entries) {
2802
- const srcPath = join(src, entry.name);
2803
- const destPath = join(dest, entry.name);
2804
- if (entry.isDirectory()) {
2805
- const subCopied = await copyMissingFiles(srcPath, destPath);
2806
- if (subCopied.length > 0) {
2807
- copied.push(entry.name);
2808
- }
2809
- } else if (!existsSync(destPath)) {
2810
- await writeFile(destPath, await readFile(srcPath));
2811
- copied.push(entry.name);
2812
- }
2813
- }
2814
- return copied;
2815
- }
2816
- async function handleManifoldInit(api) {
2817
- const directory = api.state.path.directory;
2818
- const globalTemplatesDir = join(homedir(), ".config", "opencode", "manifold");
2819
- if (!existsSync(globalTemplatesDir)) {
2820
- api.ui.toast({
2821
- variant: "error",
2822
- message: "Global templates not found. Plugin may not be installed correctly."
2823
- });
2824
- return;
2825
- }
2826
- const agentsDir = join(directory, ".opencode", "agents");
2827
- const skillsDir = join(directory, ".opencode", "skills");
2828
- const manifoldDir = join(directory, "Manifold");
2829
- await mkdir(agentsDir, { recursive: true });
2830
- await mkdir(skillsDir, { recursive: true });
2831
- await mkdir(manifoldDir, { recursive: true });
2832
- const agentsCopied = await copyMissingFiles(join(globalTemplatesDir, "agents"), agentsDir);
2833
- const skillsCopied = await copyMissingFiles(join(globalTemplatesDir, "skills"), skillsDir);
2834
- const manifoldCopied = await copyMissingFiles(join(globalTemplatesDir, "manifold"), manifoldDir);
2835
- const initialized = [];
2836
- if (agentsCopied.length > 0) {
2837
- initialized.push(`agents (${agentsCopied.join(", ")})`);
2838
- }
2839
- if (skillsCopied.length > 0) {
2840
- initialized.push(`skills (${skillsCopied.join(", ")})`);
2841
- }
2842
- if (manifoldCopied.length > 0) {
2843
- initialized.push(`Manifold/ (${manifoldCopied.join(", ")})`);
2844
- }
2845
- const message = initialized.length > 0 ? `Manifold initialized: ${initialized.join(", ")}` : "All Manifold files already present.";
2846
- api.ui.toast({
2847
- variant: "success",
2848
- message
2849
- });
2850
- }
2851
- async function handleManifoldModels(api) {
2788
+ function handleManifoldModels(api) {
2852
2789
  const directory = api.state.path.directory;
2853
- const agents = await getManifoldAgents(directory);
2854
- if (agents.length === 0) {
2855
- api.ui.toast({
2856
- variant: "error",
2857
- message: "No Manifold agents found. Run /manifold-init first."
2858
- });
2859
- return;
2860
- }
2861
- const providers = api.state.provider;
2862
- const models = [];
2863
- for (const provider of providers) {
2864
- if (provider.models) {
2865
- for (const [modelId, model] of Object.entries(provider.models)) {
2866
- models.push({
2867
- id: `${provider.id}/${modelId}`,
2868
- name: model.name || modelId,
2869
- providerID: provider.id
2870
- });
2790
+ getManifoldAgents(directory).then((agents) => {
2791
+ if (agents.length === 0) {
2792
+ api.ui.toast({
2793
+ variant: "error",
2794
+ message: "No Manifold agents found. Run /manifold-init first."
2795
+ });
2796
+ return;
2797
+ }
2798
+ const providers = api.state.provider;
2799
+ const models = [];
2800
+ for (const provider of providers) {
2801
+ if (provider.models) {
2802
+ for (const [modelId, model] of Object.entries(provider.models)) {
2803
+ models.push({
2804
+ id: `${provider.id}/${modelId}`,
2805
+ name: model.name || modelId,
2806
+ providerID: provider.id
2807
+ });
2808
+ }
2871
2809
  }
2872
2810
  }
2873
- }
2874
- if (models.length === 0) {
2875
- api.ui.toast({
2876
- variant: "error",
2877
- message: "No models available. Configure providers first."
2878
- });
2879
- return;
2880
- }
2881
- models.sort((a, b) => a.name.localeCompare(b.name));
2882
- const agentOptions = agents.map((agent) => ({
2883
- title: `${agent} (sub-agent)`,
2884
- value: agent,
2885
- description: `Configure model for ${agent}`
2886
- }));
2887
- api.ui.dialog.setSize("medium");
2888
- return new Promise((resolve) => {
2889
- api.ui.ui.DialogSelect({
2811
+ if (models.length === 0) {
2812
+ api.ui.toast({
2813
+ variant: "error",
2814
+ message: "No models available. Configure providers first."
2815
+ });
2816
+ return;
2817
+ }
2818
+ models.sort((a, b) => a.name.localeCompare(b.name));
2819
+ const agentOptions = agents.map((agent) => ({
2820
+ title: `${agent} (sub-agent)`,
2821
+ value: agent,
2822
+ description: `Configure model for ${agent}`
2823
+ }));
2824
+ api.ui.dialog.replace(() => api.ui.DialogSelect({
2890
2825
  title: "Select Manifold Sub-Agent",
2891
2826
  options: agentOptions,
2892
- onSelect: async (option) => {
2827
+ onSelect: (option) => {
2893
2828
  const selectedAgent = option.value;
2894
- const currentModel = await readAgentFile(selectedAgent, directory).then((f) => f.frontmatter.model || "not set").catch(() => "not set");
2895
- const modelOptions = models.map((model) => ({
2896
- title: `${model.name}`,
2897
- value: model.id,
2898
- description: model.id,
2899
- footer: model.id === currentModel ? "✓ Current" : undefined
2900
- }));
2901
- api.ui.ui.DialogSelect({
2902
- title: `Select Model for ${selectedAgent}`,
2903
- options: modelOptions,
2904
- current: currentModel !== "not set" ? currentModel : undefined,
2905
- onSelect: async (modelOption) => {
2906
- const selectedModelId = modelOption.value;
2907
- await updateAgentModel(selectedAgent, selectedModelId, directory);
2908
- api.ui.toast({
2909
- variant: "success",
2910
- message: `Set ${selectedAgent} to ${selectedModelId}`
2911
- });
2912
- resolve();
2913
- }
2829
+ readAgentFile(selectedAgent, directory).then(({ frontmatter }) => {
2830
+ const currentModel = frontmatter.model || "not set";
2831
+ const modelOptions = models.map((model) => ({
2832
+ title: `${model.name}`,
2833
+ value: model.id,
2834
+ description: model.id,
2835
+ footer: model.id === currentModel ? "✓ Current" : undefined
2836
+ }));
2837
+ api.ui.dialog.replace(() => api.ui.DialogSelect({
2838
+ title: `Select Model for ${selectedAgent}`,
2839
+ options: modelOptions,
2840
+ current: currentModel !== "not set" ? currentModel : undefined,
2841
+ onSelect: (modelOption) => {
2842
+ const selectedModelId = modelOption.value;
2843
+ updateAgentModel(selectedAgent, selectedModelId, directory).then(() => {
2844
+ api.ui.toast({
2845
+ variant: "success",
2846
+ message: `Set ${selectedAgent} to ${selectedModelId}`
2847
+ });
2848
+ api.ui.dialog.clear();
2849
+ });
2850
+ }
2851
+ }));
2852
+ }).catch(() => {
2853
+ api.ui.toast({
2854
+ variant: "error",
2855
+ message: `Error reading agent file for ${selectedAgent}`
2856
+ });
2914
2857
  });
2915
2858
  }
2916
- });
2859
+ }));
2917
2860
  });
2918
2861
  }
2919
- async function handleManifoldUpdate(api) {
2862
+ function handleManifoldUpdate(api) {
2920
2863
  const directory = api.state.path.directory;
2921
- const { rm } = await import("fs/promises");
2922
2864
  const settingsPath = join(directory, "Manifold", "settings.json");
2923
- let settings = {};
2924
- if (existsSync(settingsPath)) {
2925
- const content = await readFile(settingsPath, "utf-8");
2926
- try {
2927
- settings = JSON.parse(content);
2928
- } catch {}
2929
- }
2930
- const globalConfigDir = join(homedir(), ".config", "opencode", "manifold");
2931
- const configuredPaths = settings.updateCachePaths || [];
2932
- const pathsToClear = [...new Set([...configuredPaths, globalConfigDir])];
2933
- const resolvedPaths = pathsToClear.map((p) => {
2934
- const expanded = p.startsWith("~") ? join(homedir(), p.slice(1)) : p;
2935
- return expanded;
2936
- });
2937
- api.ui.dialog.setSize("large");
2938
- return new Promise((resolve) => {
2939
- api.ui.ui.DialogSelect({
2865
+ const proceed = (settings) => {
2866
+ const globalConfigDir = join(homedir(), ".config", "opencode", "manifold");
2867
+ const configuredPaths = settings.updateCachePaths || [];
2868
+ const pathsToClear = [...new Set([...configuredPaths, globalConfigDir])];
2869
+ const resolvedPaths = pathsToClear.map((p) => {
2870
+ const expanded = p.startsWith("~") ? join(homedir(), p.slice(1)) : p;
2871
+ return expanded;
2872
+ });
2873
+ api.ui.dialog.replace(() => api.ui.DialogSelect({
2940
2874
  title: "Manifold Plugin Update",
2941
2875
  options: [
2942
2876
  {
@@ -2952,31 +2886,43 @@ async function handleManifoldUpdate(api) {
2952
2886
  description: "Do not clear cache"
2953
2887
  }
2954
2888
  ],
2955
- onSelect: async (option) => {
2889
+ onSelect: (option) => {
2956
2890
  if (option.value === "cancel") {
2957
- resolve();
2891
+ api.ui.dialog.clear();
2958
2892
  return;
2959
2893
  }
2960
- const cleared = [];
2961
- const blocked = [];
2962
- for (const pathStr of resolvedPaths) {
2963
- try {
2894
+ import("fs/promises").then(({ rm }) => {
2895
+ const cleared = [];
2896
+ const promises = resolvedPaths.map((pathStr) => {
2964
2897
  if (existsSync(pathStr)) {
2965
- await rm(pathStr, { recursive: true, force: true });
2966
- cleared.push(pathStr);
2898
+ return rm(pathStr, { recursive: true, force: true }).then(() => {
2899
+ cleared.push(pathStr);
2900
+ }).catch(() => {});
2967
2901
  }
2968
- } catch (error) {
2969
- blocked.push({ path: pathStr, reason: `${error}` });
2970
- }
2971
- }
2972
- api.ui.toast({
2973
- variant: cleared.length > 0 ? "success" : "error",
2974
- message: cleared.length > 0 ? `Cache cleared. Restart opencode to update.` : `No paths cleared or update blocked`
2902
+ return Promise.resolve();
2903
+ });
2904
+ Promise.all(promises).then(() => {
2905
+ api.ui.toast({
2906
+ variant: cleared.length > 0 ? "success" : "error",
2907
+ message: cleared.length > 0 ? `Cache cleared. Restart opencode to update.` : `No paths cleared or update blocked`
2908
+ });
2909
+ api.ui.dialog.clear();
2910
+ });
2975
2911
  });
2976
- resolve();
2977
2912
  }
2978
- });
2979
- });
2913
+ }));
2914
+ };
2915
+ if (existsSync(settingsPath)) {
2916
+ readFile(settingsPath, "utf-8").then((content) => {
2917
+ try {
2918
+ proceed(JSON.parse(content));
2919
+ } catch {
2920
+ proceed({});
2921
+ }
2922
+ }).catch(() => proceed({}));
2923
+ } else {
2924
+ proceed({});
2925
+ }
2980
2926
  }
2981
2927
  export {
2982
2928
  tui
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-manifold",
3
- "version": "0.4.16",
3
+ "version": "0.4.17",
4
4
  "description": "Multi-agent development system for opencode with persistent knowledge",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -1,3 +0,0 @@
1
- ---
2
- description: Set the model for a Manifold sub-agent (clerk, senior-dev, junior-dev, debug)
3
- ---
@@ -1,3 +0,0 @@
1
- ---
2
- description: Clear opencode-manifold plugin cache and prompt for restart
3
- ---